{
 "components": {
  "schemas": {
   "AccessBody": {
    "description": "Adresse interne publiée pour une console et sa protection ; les deux à nul reviennent à l'adresse découverte.",
    "properties": {
     "protection": {
      "anyOf": [
       {
        "enum": [
         "native",
         "gateway"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Protection",
      "description": "Protection de la console : `native` (sa propre connexion) ou `gateway` (une passerelle d'authentification devant elle)."
     },
     "published_url": {
      "anyOf": [
       {
        "maxLength": 2048,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Published Url",
      "description": "Adresse HTTPS de la console sur le réseau interne, par exemple `https://<kafka-ui-host>`."
     }
    },
    "title": "AccessBody",
    "type": "object"
   },
   "AckIn": {
    "properties": {
     "note": {
      "default": "",
      "maxLength": 2000,
      "title": "Note",
      "type": "string",
      "description": "Note conservée avec l'acquittement, 2000 caractères au plus."
     }
    },
    "title": "AckIn",
    "type": "object",
    "description": "Acquittement d'une alerte."
   },
   "ActionBody": {
    "properties": {
     "action": {
      "enum": [
       "start",
       "stop",
       "restart",
       "rolling_restart",
       "service_check",
       "smoke_test",
       "reinstall",
       "configure"
      ],
      "title": "Action",
      "type": "string",
      "description": "Action : `start`, `stop`, `restart`, `rolling_restart`, `service_check`, `smoke_test`, `reinstall` ou `configure` (pousser la configuration courante)."
     },
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Approbation accordée pour cette action sous `banking-secure` ; omettez-la pour utiliser l'approbation qui vous a été accordée."
     },
     "batch_size": {
      "default": 1,
      "maximum": 100.0,
      "minimum": 1.0,
      "title": "Batch Size",
      "type": "integer",
      "description": "Machines traitées en même temps par un redémarrage progressif, de 1 à 100. 1 par défaut."
     },
     "components": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Components",
      "description": "Composants concernés ; nul : tous les composants."
     },
     "hosts": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Hosts",
      "description": "Identifiants des machines concernées ; nul : toutes les machines du service."
     },
     "tolerate_failures": {
      "default": 0,
      "maximum": 100.0,
      "minimum": 0.0,
      "title": "Tolerate Failures",
      "type": "integer",
      "description": "Nombre de machines en échec acceptées avant l'arrêt de l'opération, de 0 à 100. 0 par défaut."
     }
    },
    "required": [
     "action"
    ],
    "title": "ActionBody",
    "type": "object",
    "description": "Une action sur un service."
   },
   "ActionIn": {
    "additionalProperties": false,
    "properties": {
     "action": {
      "enum": [
       "start",
       "stop",
       "restart",
       "rolling_restart",
       "scale",
       "maintenance"
      ],
      "title": "Action",
      "type": "string",
      "description": "`start`, `stop`, `restart`, `rolling_restart`, `scale` ou `maintenance`."
     },
     "autoscaling": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/AutoscalingIn"
       },
       {
        "type": "null"
       }
      ],
      "description": "Pour `scale` : mise à l'échelle automatique au lieu de réplicas fixes."
     },
     "enabled": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Enabled",
      "description": "Pour `maintenance` : vrai pour y entrer, faux pour en sortir."
     },
     "replicas": {
      "anyOf": [
       {
        "maximum": 1000.0,
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Replicas",
      "description": "Pour `scale` : nombre fixe de réplicas, de 0 à 1000."
     }
    },
    "required": [
     "action"
    ],
    "title": "ActionIn",
    "type": "object",
    "description": "Une action sur un composant Kubernetes."
   },
   "AgentIn": {
    "additionalProperties": false,
    "properties": {
     "gateway_url": {
      "maxLength": 512,
      "minLength": 9,
      "title": "Gateway Url",
      "type": "string",
      "description": "Adresse HTTPS de la passerelle des agents du Manager, par exemple `<manager-url>:8461`."
     },
     "offline_after_s": {
      "default": 90,
      "maximum": 3600.0,
      "minimum": 10.0,
      "title": "Offline After S",
      "type": "integer",
      "description": "Secondes sans rapport au bout desquelles l'agent est affiché hors ligne, de 10 à 3600. 90 par défaut."
     },
     "tenant_id": {
      "default": "default",
      "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
      "title": "Tenant Id",
      "type": "string",
      "description": "Locataire de l'agent. `default` par défaut."
     }
    },
    "required": [
     "gateway_url"
    ],
    "title": "AgentIn",
    "type": "object",
    "description": "Identité du Navin Cluster Agent à émettre."
   },
   "ApprovalBody": {
    "properties": {
     "action": {
      "enum": [
       "service.stop",
       "service.restart",
       "service.rolling_restart",
       "service.reinstall",
       "service.uninstall",
       "service.upgrade",
       "service.rollback",
       "service.restore",
       "backup.restore",
       "config.apply"
      ],
      "title": "Action",
      "type": "string",
      "description": "Action : `service.stop`, `service.restart`, `service.rolling_restart`, `service.reinstall`, `service.uninstall`, `service.upgrade`, `service.rollback`, `service.restore`, `backup.restore` ou `config.apply`."
     },
     "reason": {
      "default": "",
      "maxLength": 2000,
      "title": "Reason",
      "type": "string",
      "description": "Raison de l'action. Inscrite dans le journal d'audit avec l'acteur et l'heure."
     },
     "service": {
      "default": "",
      "maxLength": 80,
      "title": "Service",
      "type": "string",
      "description": "Service visé par l'action, 80 caractères au plus."
     }
    },
    "required": [
     "action"
    ],
    "title": "ApprovalBody",
    "type": "object",
    "description": "Une demande d'approbation pour une action perturbatrice."
   },
   "ApprovalOnly": {
    "properties": {
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Identifiant de la demande approuvée (validation à quatre yeux) qui autorise cette action."
     }
    },
    "title": "ApprovalOnly",
    "type": "object",
    "description": "Approbation facultative d'une action perturbatrice."
   },
   "ArtifactIn": {
    "properties": {
     "kind": {
      "default": "archive",
      "title": "Kind",
      "type": "string",
      "description": "Type d'artefact, par exemple `archive` (par défaut) ou `snapshot`."
     },
     "location": {
      "title": "Location",
      "type": "string",
      "description": "Chemin du fichier de sauvegarde chiffré, lisible par le Manager (cible `filesystem`)."
     },
     "verification_inventory": {
      "additionalProperties": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Verification Inventory",
      "type": "object",
      "description": "Pour un `pg_dump` (format custom) seulement : lignes et SHA-256 de chaque table de la base sauvegardée, `{\"schema.table\": {\"rows\": 50, \"sha256\": \"...\"}}`, calculés lors de l'export. Une restauration n'est exécutée que pour un artefact doté de cet inventaire, et vérifiée par rapport à lui."
     },
     "versions": {
      "additionalProperties": {
       "type": "string"
      },
      "default": {},
      "title": "Versions",
      "type": "object",
      "description": "Versions des composants sauvegardés. Un pg_dump exige la clé `postgres`, par exemple `{\"postgres\": \"16.4\"}` ; utilisées par le contrôle de compatibilité de la restauration."
     }
    },
    "required": [
     "location"
    ],
    "title": "ArtifactIn",
    "type": "object",
    "description": "Un artefact de sauvegarde à enregistrer."
   },
   "AutoscalingIn": {
    "additionalProperties": false,
    "properties": {
     "engine": {
      "default": "Auto",
      "enum": [
       "Auto",
       "HPA",
       "KEDA"
      ],
      "title": "Engine",
      "type": "string",
      "description": "`Auto` (par défaut), `HPA` ou `KEDA`."
     },
     "max_replicas": {
      "maximum": 1000.0,
      "minimum": 1.0,
      "title": "Max Replicas",
      "type": "integer",
      "description": "Nombre maximal de réplicas, de 1 à 1000, pas inférieur à `min_replicas`."
     },
     "min_replicas": {
      "maximum": 1000.0,
      "minimum": 1.0,
      "title": "Min Replicas",
      "type": "integer",
      "description": "Nombre minimal de réplicas, de 1 à 1000."
     },
     "target": {
      "anyOf": [
       {
        "maxLength": 253,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Target",
      "description": "Cible de la mise à l'échelle automatique, transmise à l'opérateur quand elle est fournie, 253 caractères au plus."
     },
     "target_cpu_utilization_percentage": {
      "maximum": 1000.0,
      "minimum": 1.0,
      "title": "Target Cpu Utilization Percentage",
      "type": "integer",
      "description": "Utilisation CPU visée en pourcentage des demandes, de 1 à 1000."
     }
    },
    "required": [
     "min_replicas",
     "max_replicas",
     "target_cpu_utilization_percentage"
    ],
    "title": "AutoscalingIn",
    "type": "object",
    "description": "Mise à l'échelle automatique d'un composant Kubernetes."
   },
   "BackupBody": {
    "properties": {
     "services": {
      "items": {
       "type": "string"
      },
      "maxItems": 100,
      "title": "Services",
      "type": "array",
      "description": "Services à sauvegarder, 100 au plus ; vide : tous les services sauvegardables."
     }
    },
    "title": "BackupBody",
    "type": "object",
    "description": "Services d'une sauvegarde coordonnée."
   },
   "BootstrapIn": {
    "additionalProperties": false,
    "properties": {
     "certificate": {
      "anyOf": [
       {
        "maxLength": 20000,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Certificate",
      "description": "Certificat utilisateur OpenSSH de la clé, signé par une autorité approuvée, à la place d'une clé autorisée."
     },
     "cluster_id": {
      "title": "Cluster Id",
      "type": "string",
      "description": "Cluster que la machine rejoint."
     },
     "fqdn": {
      "anyOf": [
       {
        "maxLength": 253,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fqdn",
      "description": "FQDN transmis à l'enregistrement de l'agent ; facultatif."
     },
     "jumps": {
      "items": {
       "$ref": "#/components/schemas/EndpointIn"
      },
      "maxItems": 4,
      "title": "Jumps",
      "type": "array",
      "description": "Machines de rebond à traverser, dans l'ordre, 4 au plus."
     },
     "private_key": {
      "maxLength": 20000,
      "minLength": 20,
      "title": "Private Key",
      "type": "string",
      "description": "Clé SSH privée de l'opérateur (OpenSSH ou PEM). Gardée en mémoire pour l'exécution uniquement, jamais conservée."
     },
     "ssh_user": {
      "anyOf": [
       {
        "maxLength": 32,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Ssh User",
      "description": "Compte SSH ; par défaut : le compte d'amorçage de la politique SSH."
     },
     "target": {
      "$ref": "#/components/schemas/EndpointIn",
      "description": "La machine à amorcer."
     },
     "use_sudo": {
      "default": true,
      "title": "Use Sudo",
      "type": "boolean",
      "description": "Exécuter les étapes privilégiées avec la liste sudo autorisée. Vrai par défaut."
     }
    },
    "required": [
     "cluster_id",
     "target",
     "private_key"
    ],
    "title": "BootstrapIn",
    "type": "object",
    "description": "Accès SSH pour l'amorçage d'un agent de nœud. Un champ de mot de passe est refusé."
   },
   "BreakGlassIn": {
    "additionalProperties": false,
    "properties": {
     "principal": {
      "default": "navin-breakglass",
      "maxLength": 32,
      "minLength": 1,
      "title": "Principal",
      "type": "string",
      "description": "Compte Unix du certificat. `navin-breakglass` par défaut."
     },
     "public_key": {
      "maxLength": 2000,
      "minLength": 20,
      "title": "Public Key",
      "type": "string",
      "description": "Clé publique OpenSSH du demandeur, celle que le certificat signera."
     },
     "reason": {
      "maxLength": 1000,
      "minLength": 10,
      "title": "Reason",
      "type": "string",
      "description": "Raison de l'accès d'urgence, de 10 à 1000 caractères."
     },
     "ttl_s": {
      "default": 1800,
      "maximum": 14400.0,
      "minimum": 60.0,
      "title": "Ttl S",
      "type": "integer",
      "description": "Validité du certificat en secondes, de 60 à 14400 (4 heures). 1800 par défaut."
     }
    },
    "required": [
     "reason",
     "public_key"
    ],
    "title": "BreakGlassIn",
    "type": "object",
    "description": "Une demande d'accès SSH d'urgence."
   },
   "ChecksumsIn": {
    "additionalProperties": false,
    "description": "Lignes de sommes de contrôle de la version.",
    "properties": {
     "text": {
      "maxLength": 65536,
      "minLength": 1,
      "title": "Text",
      "type": "string",
      "description": "Lignes `<sha256>  <fichier>` des fichiers SHA256SUMS et .sha256 de la version, collées ensemble (64 Kio au plus) ; les lignes nommant un fichier inconnu sont ignorées."
     }
    },
    "required": [
     "text"
    ],
    "title": "ChecksumsIn",
    "type": "object"
   },
   "ClientCertificate": {
    "additionalProperties": false,
    "description": "Certificat client pour le TLS mutuel, en PEM ; un objet vide le retire lors d'une modification.",
    "properties": {
     "client_cert": {
      "anyOf": [
       {
        "maxLength": 65536,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Client Cert",
      "description": "Certificat client, encodé en PEM."
     },
     "client_key": {
      "anyOf": [
       {
        "maxLength": 65536,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Client Key",
      "description": "Clé privée du certificat client, encodée en PEM. En écriture seule, scellée au repos."
     }
    },
    "title": "ClientCertificate",
    "type": "object"
   },
   "ClusterActionBody": {
    "properties": {
     "action": {
      "enum": [
       "start",
       "stop",
       "restart",
       "rolling_restart",
       "restart_stale",
       "service_check"
      ],
      "title": "Action",
      "type": "string",
      "description": "Action : `start`, `stop`, `restart` (arrêt de tous les services dans l'ordre inverse, puis démarrage dans l'ordre), `rolling_restart`, `restart_stale` (seulement les instances dont la configuration a changé) ou `service_check`."
     },
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Approbation accordée pour le service `*` sous `banking-secure` ; omettez-la pour utiliser l'approbation qui vous a été accordée."
     },
     "batch_size": {
      "default": 1,
      "maximum": 100.0,
      "minimum": 1.0,
      "title": "Batch Size",
      "type": "integer",
      "description": "Machines redémarrées en même temps par `rolling_restart` et `restart_stale`, de 1 à 100. 1 par défaut."
     },
     "dry_run": {
      "default": false,
      "title": "Dry Run",
      "type": "boolean",
      "description": "Renvoyer le plan (services dans l'ordre, instances écartées, étapes) sans créer l'opération. Faux par défaut."
     },
     "hosts": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 1000,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Hosts",
      "description": "Identifiants des machines concernées, 1000 au plus ; nul : toutes les machines."
     },
     "services": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 200,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Services",
      "description": "Services concernés, 200 au plus ; nul : tous les services gérés du cluster."
     },
     "tolerate_failures": {
      "default": 0,
      "maximum": 100.0,
      "minimum": 0.0,
      "title": "Tolerate Failures",
      "type": "integer",
      "description": "Machines en échec acceptées dans un lot avant l'arrêt de l'opération, de 0 à 100. 0 par défaut."
     }
    },
    "required": [
     "action"
    ],
    "title": "ClusterActionBody",
    "type": "object",
    "description": "Une action sur chaque service géré d'un cluster, dans l'ordre des dépendances."
   },
   "ClusterIn": {
    "properties": {
     "description": {
      "default": "",
      "title": "Description",
      "type": "string",
      "description": "Texte libre qui décrit le cluster. Vide par défaut."
     },
     "mode": {
      "default": "production",
      "enum": [
       "production",
       "preprod",
       "test",
       "development",
       "sandbox"
      ],
      "title": "Mode",
      "type": "string",
      "description": "Type d'environnement : `production` (par défaut), `preprod`, `test`, `development` ou `sandbox`. Non modifiable ensuite."
     },
     "name": {
      "maxLength": 80,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom unique du cluster, 1 à 80 caractères."
     },
     "profile": {
      "anyOf": [
       {
        "enum": [
         "development",
         "standard",
         "banking-secure",
         "air-gapped",
         "hybrid"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Profile",
      "description": "Profil d'installation : `development`, `standard`, `banking-secure`, `air-gapped` ou `hybrid`. Par défaut : `standard` pour `production` et `preprod`, sinon `development`."
     }
    },
    "required": [
     "name"
    ],
    "title": "ClusterIn",
    "type": "object",
    "description": "Un nouveau cluster."
   },
   "ClusterPatch": {
    "properties": {
     "description": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Description",
      "description": "Nouvelle description."
     },
     "name": {
      "anyOf": [
       {
        "maxLength": 80,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name",
      "description": "Nouveau nom unique, 1 à 80 caractères."
     },
     "profile": {
      "anyOf": [
       {
        "enum": [
         "development",
         "standard",
         "banking-secure",
         "air-gapped",
         "hybrid"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Profile",
      "description": "Nouveau profil d'installation : `development`, `standard`, `banking-secure`, `air-gapped` ou `hybrid`."
     }
    },
    "title": "ClusterPatch",
    "type": "object",
    "description": "Modification d'un cluster ; un champ omis ou nul est conservé."
   },
   "CommandBody": {
    "properties": {
     "host_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Host Id",
      "description": "Machine sur laquelle exécuter la commande ; nul : la machine choisie par la définition de la commande."
     },
     "params": {
      "additionalProperties": true,
      "title": "Params",
      "type": "object",
      "description": "Valeurs des paramètres déclarés par la commande, contrôlées selon leurs types."
     }
    },
    "title": "CommandBody",
    "type": "object",
    "description": "Paramètres d'une commande personnalisée."
   },
   "CompleteIn": {
    "properties": {
     "evidence": {
      "additionalProperties": true,
      "title": "Evidence",
      "type": "object",
      "description": "Preuves des mesures (journaux, horodatages, références de captures...)."
     },
     "ok": {
      "title": "Ok",
      "type": "boolean",
      "description": "Indique si l'exercice a réussi."
     },
     "rpo_observed_seconds": {
      "minimum": 0.0,
      "title": "Rpo Observed Seconds",
      "type": "number",
      "description": "Perte de données observée, en secondes (0 ou plus)."
     },
     "rto_observed_seconds": {
      "minimum": 0.0,
      "title": "Rto Observed Seconds",
      "type": "number",
      "description": "Durée de rétablissement du service observée, en secondes (0 ou plus)."
     }
    },
    "required": [
     "rpo_observed_seconds",
     "rto_observed_seconds",
     "ok",
     "evidence"
    ],
    "title": "CompleteIn",
    "type": "object",
    "description": "Mesures d'un exercice ou d'une bascule clôturés."
   },
   "ComponentBody": {
    "properties": {
     "component": {
      "title": "Component",
      "type": "string",
      "description": "Nom du composant."
     },
     "ha_acknowledged": {
      "default": false,
      "title": "Ha Acknowledged",
      "type": "boolean",
      "description": "Accepter une topologie en dessous des exigences de haute disponibilité. Faux par défaut."
     },
     "hosts": {
      "items": {
       "type": "string"
      },
      "maxItems": 500,
      "minItems": 1,
      "title": "Hosts",
      "type": "array",
      "description": "Identifiants des machines où le placer, de 1 à 500."
     }
    },
    "required": [
     "component",
     "hosts"
    ],
    "title": "ComponentBody",
    "type": "object",
    "description": "Instances d'un composant à ajouter."
   },
   "ComponentIn": {
    "additionalProperties": false,
    "properties": {
     "autoscaling": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/AutoscalingIn"
       },
       {
        "type": "null"
       }
      ],
      "description": "Mise à l'échelle automatique au lieu de réplicas fixes."
     },
     "desired_state": {
      "default": "started",
      "enum": [
       "started",
       "stopped"
      ],
      "title": "Desired State",
      "type": "string",
      "description": "`started` (par défaut) ou `stopped`."
     },
     "maintenance": {
      "default": false,
      "title": "Maintenance",
      "type": "boolean",
      "description": "Le créer en maintenance. Faux par défaut."
     },
     "name": {
      "anyOf": [
       {
        "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name",
      "description": "Nom de la ressource (libellé DNS) ; par défaut : le nom du service."
     },
     "pdb": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/PdbIn"
       },
       {
        "type": "null"
       }
      ],
      "description": "Budget d'interruption des pods."
     },
     "replicas": {
      "anyOf": [
       {
        "maximum": 1000.0,
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Replicas",
      "description": "Nombre fixe de réplicas, de 0 à 1000 ; jamais inférieur au `min_available` du budget."
     },
     "secret_refs": {
      "items": {
       "$ref": "#/components/schemas/SecretRefIn"
      },
      "maxItems": 32,
      "title": "Secret Refs",
      "type": "array",
      "description": "Valeurs prises dans des Secrets Kubernetes, 32 au plus."
     },
     "service": {
      "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
      "title": "Service",
      "type": "string",
      "description": "Nom du service du catalogue."
     },
     "source": {
      "$ref": "#/components/schemas/Source",
      "description": "Provenance du chart."
     },
     "values": {
      "additionalProperties": true,
      "title": "Values",
      "type": "object",
      "description": "Valeurs du chart ; ne doivent contenir aucun secret."
     },
     "version": {
      "anyOf": [
       {
        "maxLength": 64,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Version",
      "description": "Version du service ; par défaut : la version du catalogue."
     }
    },
    "required": [
     "service",
     "source"
    ],
    "title": "ComponentIn",
    "type": "object",
    "description": "Un service du catalogue à exécuter sur Kubernetes."
   },
   "ComponentPatch": {
    "additionalProperties": false,
    "properties": {
     "pdb": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/PdbIn"
       },
       {
        "type": "null"
       }
      ],
      "description": "Nouveau budget d'interruption des pods."
     },
     "remove_pdb": {
      "default": false,
      "title": "Remove Pdb",
      "type": "boolean",
      "description": "Retirer le budget d'interruption. Faux par défaut."
     },
     "secret_refs": {
      "anyOf": [
       {
        "items": {
         "$ref": "#/components/schemas/SecretRefIn"
        },
        "maxItems": 32,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Secret Refs",
      "description": "Nouvelles références de secrets, qui remplacent les actuelles."
     },
     "source": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/Source"
       },
       {
        "type": "null"
       }
      ],
      "description": "Nouvelle source du chart."
     },
     "values": {
      "anyOf": [
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Values",
      "description": "Nouvelles valeurs du chart, qui remplacent les actuelles ; aucun secret."
     },
     "version": {
      "anyOf": [
       {
        "maxLength": 64,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Version",
      "description": "Nouvelle version du service."
     }
    },
    "title": "ComponentPatch",
    "type": "object",
    "description": "Modification d'un composant Kubernetes ; un champ omis est conservé."
   },
   "ConfigBody": {
    "properties": {
     "note": {
      "default": "",
      "maxLength": 2000,
      "title": "Note",
      "type": "string",
      "description": "Raison du changement de configuration, conservée avec la version, 2000 caractères au plus."
     },
     "properties": {
      "additionalProperties": true,
      "title": "Properties",
      "type": "object",
      "description": "Paramètres à modifier sous la forme `{nom: valeur}` ; les autres sont conservés. Un secret renvoyé sous la forme `{\"secret\": true, \"set\": true}` est conservé, `null` supprime une valeur."
     }
    },
    "required": [
     "properties"
    ],
    "title": "ConfigBody",
    "type": "object",
    "description": "Un changement de la configuration d'un service."
   },
   "ConnectAuth": {
    "additionalProperties": false,
    "description": "Méthode d'authentification parmi celles que propose le descripteur (`connect.auth`) et ses champs ; les champs secrets ne sont jamais renvoyés. Sans méthode, celle enregistrée est conservée lors d'une modification, sinon `none` quand elle est proposée.",
    "properties": {
     "fields": {
      "additionalProperties": {
       "type": "string"
      },
      "maxProperties": 16,
      "title": "Fields",
      "type": "object",
      "description": "Champs de la méthode, par exemple `username` et `password`, 16 au plus. Un champ secret laissé vide conserve la valeur enregistrée lors d'une modification."
     },
     "method": {
      "anyOf": [
       {
        "maxLength": 40,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Method",
      "description": "Nom de la méthode, par exemple `basic`, `token` ou `none`."
     }
    },
    "title": "ConnectAuth",
    "type": "object"
   },
   "ConnectBody": {
    "description": "Configurer et connecter : un service existant enregistré selon la section `connect` de son descripteur.",
    "properties": {
     "auth": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/ConnectAuth"
       },
       {
        "type": "null"
       }
      ],
      "description": "Méthode d'authentification et ses champs."
     },
     "ca_pem": {
      "anyOf": [
       {
        "maxLength": 65536,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Ca Pem",
      "description": "Autorité de certification (PEM) qui a signé le certificat du service."
     },
     "config": {
      "additionalProperties": true,
      "maxProperties": 100,
      "title": "Config",
      "type": "object",
      "description": "Valeurs des clés de configuration du descripteur de connexion, 100 au plus."
     },
     "credentials_ref": {
      "anyOf": [
       {
        "maxLength": 512,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Credentials Ref",
      "description": "Référence à des identifiants conservés ailleurs, sous forme d'URI comme `vault://kv/navin/postgres`."
     },
     "endpoint": {
      "maxLength": 2048,
      "minLength": 1,
      "title": "Endpoint",
      "type": "string",
      "description": "Adresse du service, par exemple `https://<search-host>:9200`."
     },
     "mtls": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/ClientCertificate"
       },
       {
        "type": "null"
       }
      ],
      "description": "Certificat client pour le TLS mutuel."
     },
     "name": {
      "maxLength": 64,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom du service du catalogue à connecter, par exemple `opensearch`."
     },
     "tls_verify": {
      "default": true,
      "title": "Tls Verify",
      "type": "boolean",
      "description": "Vérifier le certificat TLS du service. Vrai par défaut ; obligatoire en production."
     }
    },
    "required": [
     "name",
     "endpoint"
    ],
    "title": "ConnectBody",
    "type": "object"
   },
   "ConnectionCreate": {
    "additionalProperties": false,
    "properties": {
     "description": {
      "default": "",
      "maxLength": 1000,
      "title": "Description",
      "type": "string",
      "description": "Texte libre, 1000 caractères au plus."
     },
     "name": {
      "maxLength": 100,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom unique, de 1 à 100 caractères."
     },
     "params": {
      "additionalProperties": true,
      "title": "Params",
      "type": "object",
      "description": "Paramètres du type, par exemple `host`, `port`, `database`, `username`, `sslmode` pour `postgresql`."
     },
     "secret": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Secret",
      "description": "Secret de la connexion (mot de passe, jeton ou clé). En écriture seule, scellé au repos."
     },
     "type": {
      "pattern": "^(clickhouse|elasticsearch|http|kafka|ldap|mysql|nats|oidc|opensearch|postgresql|qdrant|redis|s3|smtp|syslog)$",
      "title": "Type",
      "type": "string",
      "description": "Type : `clickhouse`, `elasticsearch`, `http`, `kafka`, `ldap`, `mysql`, `nats`, `oidc`, `opensearch`, `postgresql`, `qdrant`, `redis`, `s3`, `smtp` ou `syslog`."
     }
    },
    "required": [
     "name",
     "type"
    ],
    "title": "ConnectionCreate",
    "type": "object",
    "description": "Une nouvelle connexion externe."
   },
   "ConnectionValidate": {
    "additionalProperties": false,
    "properties": {
     "params": {
      "additionalProperties": true,
      "title": "Params",
      "type": "object",
      "description": "Paramètres du type."
     },
     "secret": {
      "anyOf": [
       {
        "maxLength": 4096,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Secret",
      "description": "Secret à utiliser pour le test, 4096 caractères au plus. Jamais conservé."
     },
     "type": {
      "pattern": "^(clickhouse|elasticsearch|http|kafka|ldap|mysql|nats|oidc|opensearch|postgresql|qdrant|redis|s3|smtp|syslog)$",
      "title": "Type",
      "type": "string",
      "description": "Type de la connexion, comme pour une création."
     }
    },
    "required": [
     "type"
    ],
    "title": "ConnectionValidate",
    "type": "object",
    "description": "Une connexion à tester sans l'enregistrer."
   },
   "ConsistencyIn": {
    "properties": {
     "reconcile": {
      "default": false,
      "title": "Reconcile",
      "type": "boolean",
      "description": "Marquer les lignes manquantes et mettre en quarantaine les objets orphelins. Faux par défaut (rapport seul)."
     },
     "registry": {
      "default": [],
      "items": {
       "type": "string"
      },
      "title": "Registry",
      "type": "array",
      "description": "Empreintes SHA-256 que le registre des artefacts déclare sauvegardées."
     },
     "target_id": {
      "title": "Target Id",
      "type": "string",
      "description": "Identifiant de la cible de sauvegarde."
     }
    },
    "required": [
     "target_id"
    ],
    "title": "ConsistencyIn",
    "type": "object",
    "description": "Un contrôle de cohérence d'une cible de sauvegarde `filesystem`."
   },
   "DecisionBody": {
    "properties": {
     "decision": {
      "enum": [
       "approve",
       "reject"
      ],
      "title": "Decision",
      "type": "string",
      "description": "`approve` ou `reject`."
     }
    },
    "required": [
     "decision"
    ],
    "title": "DecisionBody",
    "type": "object",
    "description": "Décision sur une demande d'approbation."
   },
   "DefinitionPatch": {
    "properties": {
     "critical": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "null"
       }
      ],
      "title": "Critical",
      "description": "Seuil de l'état `critical`, dans l'unité de la définition (pourcentage, secondes, jours...)."
     },
     "enabled": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Enabled",
      "description": "Indique si la définition est évaluée."
     },
     "interval_s": {
      "anyOf": [
       {
        "maximum": 86400.0,
        "minimum": 10.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Interval S",
      "description": "Intervalle d'évaluation en secondes, de 10 à 86400."
     },
     "warning": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "null"
       }
      ],
      "title": "Warning",
      "description": "Seuil de l'état `warning`, dans l'unité de la définition."
     }
    },
    "title": "DefinitionPatch",
    "type": "object",
    "description": "Modification d'une définition d'alerte ; un champ omis ou nul est conservé."
   },
   "DiscoverIn": {
    "additionalProperties": false,
    "description": "Le proxy à lire.",
    "properties": {
     "auth_mode": {
      "default": "user_key",
      "enum": [
       "user_key",
       "shared_key",
       "none"
      ],
      "title": "Auth Mode",
      "type": "string",
      "description": "`user_key`, `shared_key` ou `none`."
     },
     "base_url": {
      "default": "",
      "maxLength": 2048,
      "title": "Base Url",
      "type": "string",
      "description": "Adresse du proxy, avec ou sans `/v1`."
     },
     "ca_pem": {
      "default": "",
      "maxLength": 65536,
      "title": "Ca Pem",
      "type": "string",
      "description": "PEM d'une autorité interne que le Manager approuve pour cet appel."
     },
     "headers": {
      "additionalProperties": {
       "maxLength": 4096,
       "type": "string"
      },
      "maxProperties": 50,
      "title": "Headers",
      "type": "object",
      "description": "En-têtes HTTP supplémentaires."
     },
     "key": {
      "default": "",
      "maxLength": 8192,
      "title": "Key",
      "type": "string",
      "description": "Clé saisie pour ce seul appel, jamais conservée ni auditée."
     },
     "litellm_cluster_id": {
      "anyOf": [
       {
        "format": "uuid",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Litellm Cluster Id",
      "description": "Cluster d'un service LiteLLM installé par le Manager : il est alors lu avec sa clé maître et l'autorité du Manager, toutes deux gardées dans le serveur ; demande `litellm_service`."
     },
     "litellm_service": {
      "default": "",
      "maxLength": 100,
      "title": "Litellm Service",
      "type": "string",
      "description": "Nom du service LiteLLM installé ; demande `litellm_cluster_id`."
     },
     "shared_key_ref": {
      "anyOf": [
       {
        "format": "uuid",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Shared Key Ref",
      "description": "Entrée du coffre de la clé partagée."
     }
    },
    "title": "DiscoverIn",
    "type": "object"
   },
   "DrPlanIn": {
    "properties": {
     "mode": {
      "title": "Mode",
      "type": "string",
      "description": "Mode de reprise : `active_passive`, `warm`, `cold` ou `active_active_stateless`."
     },
     "name": {
      "maxLength": 100,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom du plan, de 1 à 100 caractères."
     },
     "primary_site": {
      "title": "Primary Site",
      "type": "string",
      "description": "Nom du site primaire ; créé s'il est inconnu."
     },
     "rpo_target_seconds": {
      "anyOf": [
       {
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rpo Target Seconds",
      "description": "Objectif de point de reprise (RPO) : perte de données maximale acceptée, en secondes."
     },
     "rto_target_seconds": {
      "anyOf": [
       {
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rto Target Seconds",
      "description": "Objectif de temps de reprise (RTO) : durée maximale acceptée pour rétablir le service, en secondes."
     },
     "secondary_site": {
      "title": "Secondary Site",
      "type": "string",
      "description": "Nom du site secondaire ; doit différer du primaire."
     },
     "sequence": {
      "default": [],
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Sequence",
      "type": "array",
      "description": "Étapes ordonnées de la bascule, sous forme d'objets libres."
     }
    },
    "required": [
     "name",
     "mode",
     "primary_site",
     "secondary_site"
    ],
    "title": "DrPlanIn",
    "type": "object",
    "description": "Un nouveau plan de reprise après sinistre."
   },
   "EndpointIn": {
    "additionalProperties": false,
    "properties": {
     "host": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Host",
      "type": "string",
      "description": "Nom ou adresse de la machine, par exemple `<node1-host>`."
     },
     "host_ca": {
      "default": "",
      "maxLength": 2000,
      "title": "Host Ca",
      "type": "string",
      "description": "Clé publique de l'autorité d'hôtes SSH qui signe la clé de la machine, à la place de `host_key`."
     },
     "host_key": {
      "default": "",
      "maxLength": 2000,
      "title": "Host Key",
      "type": "string",
      "description": "Clé publique SSH attendue de la machine (ligne known_hosts) ; la connexion est refusée si elle diffère."
     },
     "port": {
      "default": 22,
      "maximum": 65535.0,
      "minimum": 1.0,
      "title": "Port",
      "type": "integer",
      "description": "Port SSH, de 1 à 65535. 22 par défaut."
     },
     "user": {
      "default": "",
      "maxLength": 32,
      "title": "User",
      "type": "string",
      "description": "Compte sur ce point d'accès ; vide : l'utilisateur SSH de la requête."
     }
    },
    "required": [
     "host"
    ],
    "title": "EndpointIn",
    "type": "object",
    "description": "Un point d'accès SSH : la cible ou une machine de rebond."
   },
   "EnrollConfirmBody": {
    "additionalProperties": false,
    "properties": {
     "code": {
      "maxLength": 20,
      "minLength": 1,
      "title": "Code",
      "type": "string",
      "description": "Code courant (6 chiffres) du nouveau secret dans l'application d'authentification."
     },
     "mfa_token": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Mfa Token",
      "type": "string",
      "description": "Jeton de défi renvoyé par la connexion (`mfa_token`), valable 5 minutes."
     }
    },
    "required": [
     "mfa_token",
     "code"
    ],
    "title": "EnrollConfirmBody",
    "type": "object",
    "description": "Confirmation de l'enrôlement exigé à la connexion."
   },
   "EnrollStartBody": {
    "additionalProperties": false,
    "properties": {
     "mfa_token": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Mfa Token",
      "type": "string",
      "description": "Jeton de défi renvoyé par la connexion (`mfa_token`), valable 5 minutes."
     }
    },
    "required": [
     "mfa_token"
    ],
    "title": "EnrollStartBody",
    "type": "object",
    "description": "Début de l'enrôlement exigé à la connexion."
   },
   "EntryIn": {
    "additionalProperties": false,
    "description": "Un service du coffre : nom, URL, identifiant, mot de passe et clé d'API ou clé secrète (les quatre derniers facultatifs).",
    "properties": {
     "api_key": {
      "default": "",
      "maxLength": 8192,
      "title": "Api Key",
      "type": "string",
      "description": "Clé d'API ou clé secrète, 8192 caractères au plus. En écriture seule, scellée."
     },
     "login": {
      "default": "",
      "maxLength": 320,
      "title": "Login",
      "type": "string",
      "description": "Identifiant du compte, 320 caractères au plus."
     },
     "name": {
      "maxLength": 120,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom unique du service, de 1 à 120 caractères, conservé en clair."
     },
     "password": {
      "default": "",
      "maxLength": 4096,
      "title": "Password",
      "type": "string",
      "description": "Mot de passe du compte, 4096 caractères au plus. En écriture seule, scellé."
     },
     "url": {
      "default": "",
      "maxLength": 2048,
      "title": "Url",
      "type": "string",
      "description": "Adresse du service, par exemple `https://<grafana-host>`."
     }
    },
    "required": [
     "name"
    ],
    "title": "EntryIn",
    "type": "object"
   },
   "EntryPatch": {
    "additionalProperties": false,
    "description": "Champs à modifier ; un champ omis est conservé, un mot de passe ou une clé vide l'efface.",
    "properties": {
     "api_key": {
      "anyOf": [
       {
        "maxLength": 8192,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Api Key",
      "description": "Nouvelle clé d'API ou clé secrète ; une chaîne vide l'efface."
     },
     "login": {
      "anyOf": [
       {
        "maxLength": 320,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Login",
      "description": "Nouvel identifiant."
     },
     "name": {
      "anyOf": [
       {
        "maxLength": 120,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name",
      "description": "Nouveau nom unique."
     },
     "password": {
      "anyOf": [
       {
        "maxLength": 4096,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Password",
      "description": "Nouveau mot de passe ; une chaîne vide l'efface."
     },
     "url": {
      "anyOf": [
       {
        "maxLength": 2048,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Url",
      "description": "Nouvelle adresse."
     }
    },
    "title": "EntryPatch",
    "type": "object"
   },
   "EvidenceIn": {
    "properties": {
     "digest": {
      "anyOf": [
       {
        "maxLength": 100,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Digest",
      "description": "SHA-256 de la preuve (commandes, enregistrement), 100 caractères au plus."
     },
     "kind": {
      "enum": [
       "session_start",
       "command",
       "session_end",
       "recording"
      ],
      "title": "Kind",
      "type": "string",
      "description": "`session_start`, `command`, `session_end` ou `recording`."
     },
     "summary": {
      "default": "",
      "maxLength": 2000,
      "title": "Summary",
      "type": "string",
      "description": "Brève description, expurgée avant conservation, 2000 caractères au plus."
     }
    },
    "required": [
     "kind"
    ],
    "title": "EvidenceIn",
    "type": "object",
    "description": "Preuve d'une session d'accès d'urgence."
   },
   "ExerciseIn": {
    "properties": {
     "plan_id": {
      "title": "Plan Id",
      "type": "string",
      "description": "Identifiant du plan de reprise."
     }
    },
    "required": [
     "plan_id"
    ],
    "title": "ExerciseIn",
    "type": "object",
    "description": "Une demande d'exercice ou de bascule."
   },
   "FetchIn": {
    "additionalProperties": false,
    "description": "L'entrée du coffre qui porte le jeton GitHub d'une récupération.",
    "properties": {
     "vault_entry_id": {
      "format": "uuid",
      "title": "Vault Entry Id",
      "type": "string",
      "description": "Entrée du coffre dont la clé API est un jeton GitHub autorisé à lire la version ; le jeton ne quitte jamais le serveur."
     }
    },
    "required": [
     "vault_entry_id"
    ],
    "title": "FetchIn",
    "type": "object"
   },
   "GroupBody": {
    "properties": {
     "hosts": {
      "items": {
       "type": "string"
      },
      "maxItems": 1000,
      "title": "Hosts",
      "type": "array",
      "description": "Identifiants des machines du groupe, 1000 au plus ; chacune doit exécuter le service et n'appartenir à aucun autre groupe."
     },
     "name": {
      "maxLength": 120,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom unique du groupe dans le service, de 1 à 120 caractères."
     },
     "overrides": {
      "additionalProperties": true,
      "title": "Overrides",
      "type": "object",
      "description": "Paramètres surchargés sur ces machines sous la forme `{nom: valeur}`."
     }
    },
    "required": [
     "name"
    ],
    "title": "GroupBody",
    "type": "object",
    "description": "Un groupe de configuration : des valeurs surchargées sur certaines machines."
   },
   "GroupIn": {
    "properties": {
     "cluster_id": {
      "title": "Cluster Id",
      "type": "string",
      "description": "Cluster du groupe."
     },
     "component": {
      "maxLength": 80,
      "minLength": 1,
      "title": "Component",
      "type": "string",
      "description": "Nom du composant que le groupe maintient disponible, de 1 à 80 caractères."
     },
     "deployment_mode": {
      "default": "NODE_GROUP",
      "enum": [
       "SINGLE_HOST",
       "NODE_GROUP",
       "K8S_EXISTING",
       "K8S_MANAGED",
       "MULTI_CLUSTER",
       "EXTERNAL_MANAGED"
      ],
      "title": "Deployment Mode",
      "type": "string",
      "description": "Lieu d'exécution : `SINGLE_HOST`, `NODE_GROUP` (par défaut), `K8S_EXISTING`, `K8S_MANAGED`, `MULTI_CLUSTER` ou `EXTERNAL_MANAGED`."
     },
     "desired_replicas": {
      "maximum": 99.0,
      "minimum": 1.0,
      "title": "Desired Replicas",
      "type": "integer",
      "description": "Nombre de réplicas souhaités, de 1 à 99 ; au moins 2 pour `ha`, 3 pour `critical-ha`."
     },
     "failover_policy": {
      "$ref": "#/components/schemas/PolicyIn",
      "description": "Façon dont se fait une bascule."
     },
     "fault_domains": {
      "items": {
       "type": "string"
      },
      "title": "Fault Domains",
      "type": "array",
      "description": "Domaines de panne distincts (baies, zones) du groupe ; au moins 2 pour `ha`, 3 pour `critical-ha`."
     },
     "ha_mode": {
      "enum": [
       "single",
       "active_passive",
       "active_active",
       "quorum"
      ],
      "title": "Ha Mode",
      "type": "string",
      "description": "Mode de HA : `single`, `active_passive`, `active_active` ou `quorum`."
     },
     "members": {
      "items": {
       "$ref": "#/components/schemas/MemberIn"
      },
      "title": "Members",
      "type": "array",
      "description": "Membres du groupe."
     },
     "name": {
      "maxLength": 80,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom du groupe, de 1 à 80 caractères."
     },
     "ownership_mode": {
      "default": "PLATFORM_MANAGED",
      "enum": [
       "PLATFORM_MANAGED",
       "CLIENT_MANAGED"
      ],
      "title": "Ownership Mode",
      "type": "string",
      "description": "Qui l'exploite : `PLATFORM_MANAGED` (par défaut, le Manager agit dessus) ou `CLIENT_MANAGED` (le Manager observe seulement)."
     },
     "profile": {
      "enum": [
       "standalone",
       "ha",
       "critical-ha"
      ],
      "title": "Profile",
      "type": "string",
      "description": "Profil de disponibilité : `standalone`, `ha` ou `critical-ha`."
     },
     "rpo_target_seconds": {
      "anyOf": [
       {
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rpo Target Seconds",
      "description": "Objectif de point de reprise (RPO) : perte de données maximale acceptée, en secondes."
     },
     "rto_target_seconds": {
      "anyOf": [
       {
        "exclusiveMinimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rto Target Seconds",
      "description": "Objectif de temps de reprise (RTO) : durée maximale acceptée pour rétablir le service, en secondes (plus de 0)."
     }
    },
    "required": [
     "cluster_id",
     "name",
     "component",
     "ha_mode",
     "profile",
     "desired_replicas"
    ],
    "title": "GroupIn",
    "type": "object",
    "description": "Un nouveau groupe de haute disponibilité."
   },
   "HTTPValidationError": {
    "properties": {
     "detail": {
      "items": {
       "$ref": "#/components/schemas/ValidationError"
      },
      "title": "Detail",
      "type": "array",
      "description": "Éléments invalides de la requête, un par champ."
     }
    },
    "title": "HTTPValidationError",
    "type": "object",
    "description": "Forme d'erreur de validation déclarée par FastAPI. Le Manager répond en réalité à une requête invalide par `422` et l'enveloppe d'erreur de l'API, code `validation_error`, dont `details.fields` liste les champs invalides."
   },
   "HostActionBody": {
    "properties": {
     "action": {
      "enum": [
       "start",
       "stop",
       "restart",
       "configure",
       "reinstall"
      ],
      "title": "Action",
      "type": "string",
      "description": "Action : `start`, `stop`, `restart`, `configure` ou `reinstall`."
     },
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Approbation accordée pour cette action sous `banking-secure`."
     }
    },
    "required": [
     "action"
    ],
    "title": "HostActionBody",
    "type": "object",
    "description": "Une action sur une instance de composant."
   },
   "HostBulkBody": {
    "properties": {
     "action": {
      "enum": [
       "start",
       "stop",
       "restart",
       "restart_stale"
      ],
      "title": "Action",
      "type": "string",
      "description": "Action : `start`, `stop`, `restart` ou `restart_stale` (seulement les composants dont la configuration a changé)."
     },
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Approbation accordée pour le service `host:<host id>` sous `banking-secure` ; omettez-la pour utiliser l'approbation qui vous a été accordée."
     },
     "dry_run": {
      "default": false,
      "title": "Dry Run",
      "type": "boolean",
      "description": "Renvoyer le plan sans créer l'opération. Faux par défaut."
     }
    },
    "required": [
     "action"
    ],
    "title": "HostBulkBody",
    "type": "object",
    "description": "Une action sur chaque composant d'une machine, dans l'ordre des dépendances."
   },
   "HostPatch": {
    "properties": {
     "environment": {
      "anyOf": [
       {
        "maxLength": 40,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Environment",
      "description": "Plateforme de la machine : `auto` (détectée à partir des caractéristiques), `on_premises`, `aws`, `azure`, `gcp`, `ibm`, `ovh`, `openstack`, `oracle`..."
     },
     "labels": {
      "anyOf": [
       {
        "additionalProperties": {
         "type": "string"
        },
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Labels",
      "description": "Étiquettes de la machine sous forme de table de chaînes, qui remplacent les étiquettes actuelles, par exemple `{\"zone\": \"a\"}`."
     },
     "maintenance": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Maintenance",
      "description": "Vrai met la machine en maintenance (aucune alerte n'est levée pour elle), faux y met fin."
     },
     "rack": {
      "anyOf": [
       {
        "maxLength": 200,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rack",
      "description": "Baie ou domaine de panne de la machine, utilisé par les règles de placement, 200 caractères au plus."
     }
    },
    "title": "HostPatch",
    "type": "object",
    "description": "Modification d'une machine ; un champ omis ou nul est conservé."
   },
   "HostingIn": {
    "additionalProperties": false,
    "properties": {
     "cluster_id": {
      "description": "Cluster dont le service `mcp-host` héberge le serveur (UUID).",
      "pattern": "^[0-9a-fA-F-]{36}$",
      "title": "Cluster Id",
      "type": "string"
     },
     "limits": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/LimitsIn"
       },
       {
        "type": "null"
       }
      ],
      "description": "Limites du serveur hébergé ; une valeur absente prend la valeur par défaut du service `mcp-host`."
     }
    },
    "required": [
     "cluster_id"
    ],
    "title": "HostingIn",
    "type": "object",
    "description": "Hébergement d'un serveur MCP stdio."
   },
   "HubSettingsBody": {
    "additionalProperties": false,
    "properties": {
     "ca_pem": {
      "default": "",
      "maxLength": 65536,
      "title": "Ca Pem",
      "type": "string",
      "description": "Autorité de certification (PEM) du Hub quand elle n'est pas reconnue publiquement."
     },
     "customer": {
      "default": "",
      "maxLength": 100,
      "pattern": "^[a-z0-9-]*$",
      "title": "Customer",
      "type": "string",
      "description": "Identifiant du client sur le Hub : lettres minuscules, chiffres et `-`."
     },
     "enforce": {
      "default": false,
      "title": "Enforce",
      "type": "boolean",
      "description": "N'installer que des images dont l'empreinte est une cible approuvée et non révoquée. Faux par défaut."
     },
     "expected_hub": {
      "default": "",
      "maxLength": 500,
      "title": "Expected Hub",
      "type": "string"
     },
     "token": {
      "anyOf": [
       {
        "maxLength": 500,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Token",
      "description": "Jeton d'accès au Hub. En écriture seule, scellé ; nul conserve le jeton enregistré."
     },
     "trusted_keys_pem": {
      "default": "",
      "maxLength": 65536,
      "title": "Trusted Keys Pem",
      "type": "string",
      "description": "Clés publiques Ed25519 épinglées (PEM) autorisées à signer les documents."
     },
     "url": {
      "default": "",
      "maxLength": 500,
      "pattern": "^(|https://[^\\s]+)$",
      "title": "Url",
      "type": "string",
      "description": "Adresse HTTPS du Hub, par exemple `https://<hub-host>` ; vide pour un usage hors ligne."
     }
    },
    "title": "HubSettingsBody",
    "type": "object",
    "description": "Connexion au Navin Trusted Hub."
   },
   "InstallBody": {
    "additionalProperties": false,
    "properties": {
     "mirror_ca_pem": {
      "anyOf": [
       {
        "maxLength": 65536,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Mirror Ca Pem",
      "description": "Autorité de certification (PEM) du miroir."
     },
     "mirror_url": {
      "anyOf": [
       {
        "maxLength": 500,
        "pattern": "^(|https?://[^\\s]+)$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Mirror Url",
      "description": "Adresse du miroir de paquets, `http://` ou `https://` ; vide pour le miroir intégré."
     },
     "preferred_method": {
      "anyOf": [
       {
        "enum": [
         "podman",
         "binary"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Preferred Method",
      "description": "Méthode d'installation utilisée quand un service la propose et que la demande n'en indique aucune : `podman` ou `binary`."
     },
     "registry": {
      "anyOf": [
       {
        "maxLength": 300,
        "pattern": "^[^\\s]*$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Registry",
      "description": "Registre de conteneurs d'où les images sont tirées, par exemple `<harbor-host>/navin`."
     }
    },
    "title": "InstallBody",
    "type": "object",
    "description": "Valeurs par défaut d'installation du Manager."
   },
   "KeyIn": {
    "additionalProperties": false,
    "description": "Une nouvelle clé virtuelle pour un utilisateur du Manager ou un groupe du fournisseur d'identité, avec ses modèles et limites.",
    "properties": {
     "assignee_kind": {
      "default": "user",
      "enum": [
       "user",
       "group"
      ],
      "title": "Assignee Kind",
      "type": "string",
      "description": "`user` (un utilisateur du Manager, `user_id`) ou `group` (un nom de groupe, `group`)."
     },
     "budget_duration": {
      "anyOf": [
       {
        "pattern": "^[1-9][0-9]{0,3}(s|m|h|d|mo)$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Budget Duration",
      "description": "Période de remise à zéro du budget : un nombre puis `s`, `m`, `h`, `d` ou `mo` (`30d`, `1mo`) ; nul pour aucune remise à zéro."
     },
     "group": {
      "default": "",
      "maxLength": 200,
      "title": "Group",
      "type": "string",
      "description": "Nom du groupe du fournisseur d'identité à qui la clé est destinée (`assignee_kind` group), 200 caractères au plus."
     },
     "max_budget": {
      "anyOf": [
       {
        "maximum": 1000000000.0,
        "minimum": 0.0,
        "type": "number"
       },
       {
        "type": "null"
       }
      ],
      "title": "Max Budget",
      "description": "Budget de dépense de la clé dans l'unité du proxy ; nul pour aucun budget."
     },
     "models": {
      "items": {
       "maxLength": 200,
       "minLength": 1,
       "type": "string"
      },
      "maxItems": 200,
      "title": "Models",
      "type": "array",
      "description": "Identifiants des modèles que la clé peut utiliser, tous parmi ceux du profil ; vide pour tous les modèles du profil."
     },
     "rpm_limit": {
      "anyOf": [
       {
        "maximum": 1000000000.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rpm Limit",
      "description": "Requêtes par minute permises ; nul pour aucune limite."
     },
     "tpm_limit": {
      "anyOf": [
       {
        "maximum": 1000000000.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tpm Limit",
      "description": "Jetons par minute permis ; nul pour aucune limite."
     },
     "user_id": {
      "anyOf": [
       {
        "format": "uuid",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "User Id",
      "description": "Identifiant de l'utilisateur actif du Manager à qui la clé est destinée (`assignee_kind` user)."
     }
    },
    "title": "KeyIn",
    "type": "object"
   },
   "KeyLimitsIn": {
    "additionalProperties": false,
    "description": "Modèles et limites d'une clé virtuelle.",
    "properties": {
     "budget_duration": {
      "anyOf": [
       {
        "pattern": "^[1-9][0-9]{0,3}(s|m|h|d|mo)$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Budget Duration",
      "description": "Période de remise à zéro du budget : un nombre puis `s`, `m`, `h`, `d` ou `mo` (`30d`, `1mo`) ; nul pour aucune remise à zéro."
     },
     "max_budget": {
      "anyOf": [
       {
        "maximum": 1000000000.0,
        "minimum": 0.0,
        "type": "number"
       },
       {
        "type": "null"
       }
      ],
      "title": "Max Budget",
      "description": "Budget de dépense de la clé dans l'unité du proxy ; nul pour aucun budget."
     },
     "models": {
      "items": {
       "maxLength": 200,
       "minLength": 1,
       "type": "string"
      },
      "maxItems": 200,
      "title": "Models",
      "type": "array",
      "description": "Identifiants des modèles que la clé peut utiliser, tous parmi ceux du profil ; vide pour tous les modèles du profil."
     },
     "rpm_limit": {
      "anyOf": [
       {
        "maximum": 1000000000.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rpm Limit",
      "description": "Requêtes par minute permises ; nul pour aucune limite."
     },
     "tpm_limit": {
      "anyOf": [
       {
        "maximum": 1000000000.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tpm Limit",
      "description": "Jetons par minute permis ; nul pour aucune limite."
     }
    },
    "title": "KeyLimitsIn",
    "type": "object"
   },
   "LdapBody": {
    "additionalProperties": false,
    "properties": {
     "bind_dn": {
      "default": "",
      "maxLength": 500,
      "title": "Bind Dn",
      "type": "string",
      "description": "Nom distinctif du compte de service utilisé pour les recherches, par exemple `cn=navin,ou=services,dc=example,dc=internal`. Vide : liaison anonyme."
     },
     "bind_password": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Bind Password",
      "description": "Mot de passe du compte de liaison : une chaîne le définit, une chaîne vide l'efface, l'objet relu ou nul le conserve. Scellé au repos."
     },
     "ca_pem": {
      "default": "",
      "maxLength": 65536,
      "title": "Ca Pem",
      "type": "string",
      "description": "Autorité de certification (PEM) qui a signé le certificat de l'annuaire, pour `ldaps://` ou StartTLS."
     },
     "display_name_attribute": {
      "default": "cn",
      "maxLength": 100,
      "pattern": "^[A-Za-z0-9-]*$",
      "title": "Display Name Attribute",
      "type": "string",
      "description": "Attribut qui porte le nom affiché. `cn` par défaut."
     },
     "email_attribute": {
      "default": "mail",
      "maxLength": 100,
      "pattern": "^[A-Za-z0-9-]*$",
      "title": "Email Attribute",
      "type": "string",
      "description": "Attribut qui porte l'adresse e-mail. `mail` par défaut."
     },
     "enabled": {
      "default": false,
      "title": "Enabled",
      "type": "boolean",
      "description": "Indique si les utilisateurs peuvent se connecter par LDAP. Faux par défaut."
     },
     "group_base_dn": {
      "default": "",
      "maxLength": 500,
      "title": "Group Base Dn",
      "type": "string",
      "description": "DN de base de la recherche des groupes ; vide : les groupes ne sont pas lus."
     },
     "group_filter": {
      "default": "(objectClass=groupOfNames)",
      "maxLength": 500,
      "title": "Group Filter",
      "type": "string",
      "description": "Filtre LDAP qui sélectionne les groupes. `(objectClass=groupOfNames)` par défaut."
     },
     "group_name_attribute": {
      "default": "cn",
      "maxLength": 100,
      "pattern": "^[A-Za-z0-9-]*$",
      "title": "Group Name Attribute",
      "type": "string",
      "description": "Attribut qui porte le nom de groupe comparé aux correspondances. `cn` par défaut."
     },
     "name": {
      "default": "LDAP",
      "maxLength": 100,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom affiché sur la page de connexion. `LDAP` par défaut."
     },
     "role_mappings": {
      "items": {
       "$ref": "#/components/schemas/Mapping"
      },
      "maxItems": 200,
      "title": "Role Mappings",
      "type": "array",
      "description": "Correspondances entre groupes et rôles appliquées à chaque connexion, 200 au plus."
     },
     "start_tls": {
      "default": false,
      "title": "Start Tls",
      "type": "boolean",
      "description": "Chiffrer les connexions `ldap://` avec StartTLS (obligatoire pour une URL `ldap://`). Faux par défaut."
     },
     "urls": {
      "items": {
       "type": "string"
      },
      "maxItems": 10,
      "title": "Urls",
      "type": "array",
      "description": "Serveurs de l'annuaire, essayés dans l'ordre, par exemple `ldaps://<ldap-host>:636`. 10 au plus."
     },
     "user_base_dn": {
      "default": "",
      "maxLength": 500,
      "title": "User Base Dn",
      "type": "string",
      "description": "DN de base de la recherche des utilisateurs, par exemple `ou=people,dc=example,dc=internal`."
     },
     "user_filter": {
      "default": "(objectClass=person)",
      "maxLength": 500,
      "title": "User Filter",
      "type": "string",
      "description": "Filtre LDAP qui sélectionne les comptes utilisateurs. `(objectClass=person)` par défaut."
     },
     "username_attribute": {
      "default": "uid",
      "maxLength": 100,
      "minLength": 1,
      "pattern": "^[A-Za-z0-9-]+$",
      "title": "Username Attribute",
      "type": "string",
      "description": "Attribut comparé au nom d'utilisateur saisi à la connexion. `uid` par défaut."
     }
    },
    "title": "LdapBody",
    "type": "object",
    "description": "Réglages de l'annuaire LDAP."
   },
   "LdapTestBody": {
    "additionalProperties": false,
    "properties": {
     "password": {
      "anyOf": [
       {
        "maxLength": 1024,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Password",
      "description": "Mot de passe du compte de test. Jamais conservé."
     },
     "username": {
      "anyOf": [
       {
        "maxLength": 200,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Username",
      "description": "Nom d'utilisateur du compte de test."
     }
    },
    "title": "LdapTestBody",
    "type": "object",
    "description": "Compte de test d'un test LDAP ; sans lui, seule la liaison est contrôlée."
   },
   "LimitsIn": {
    "additionalProperties": false,
    "description": "Limites d'un serveur hébergé ; une valeur absente prend la valeur par défaut du service `mcp-host`.",
    "properties": {
     "idle_timeout_s": {
      "anyOf": [
       {
        "maximum": 86400.0,
        "minimum": 10.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Idle Timeout S",
      "description": "Délai d'inactivité du serveur hébergé en secondes, de 10 à 86400."
     },
     "max_concurrency": {
      "anyOf": [
       {
        "maximum": 256.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Max Concurrency",
      "description": "Requêtes servies en même temps, de 1 à 256."
     },
     "memory_mb": {
      "anyOf": [
       {
        "maximum": 65536.0,
        "minimum": 64.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memory Mb",
      "description": "Limite de mémoire du serveur hébergé en Mo, de 64 à 65536."
     },
     "request_timeout_s": {
      "anyOf": [
       {
        "maximum": 3600.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Request Timeout S",
      "description": "Durée maximale d'une requête en secondes, de 1 à 3600."
     }
    },
    "title": "LimitsIn",
    "type": "object"
   },
   "LockBody": {
    "properties": {
     "reason": {
      "maxLength": 500,
      "minLength": 1,
      "title": "Reason",
      "type": "string",
      "description": "Raison du verrouillage du cluster, de 1 à 500 caractères ; affichée dans la console et inscrite au journal d'audit."
     }
    },
    "required": [
     "reason"
    ],
    "title": "LockBody",
    "type": "object",
    "description": "Verrou de sécurité d'un cluster."
   },
   "LoginBody": {
    "additionalProperties": false,
    "properties": {
     "password": {
      "maxLength": 1024,
      "minLength": 1,
      "title": "Password",
      "type": "string",
      "description": "Mot de passe du compte (1 à 1024 caractères). Jamais écrit dans les journaux."
     },
     "provider": {
      "default": "local",
      "enum": [
       "local",
       "ldap"
      ],
      "title": "Provider",
      "type": "string",
      "description": "Origine du compte : `local` (par défaut) ou `ldap`."
     },
     "username": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Username",
      "type": "string",
      "description": "Nom d'utilisateur, insensible à la casse (1 à 200 caractères)."
     }
    },
    "required": [
     "username",
     "password"
    ],
    "title": "LoginBody",
    "type": "object",
    "description": "Identifiants d'une connexion."
   },
   "LogsBody": {
    "properties": {
     "component": {
      "title": "Component",
      "type": "string",
      "description": "Nom du composant."
     },
     "host_id": {
      "title": "Host Id",
      "type": "string",
      "description": "Identifiant de la machine qui exécute l'instance."
     },
     "lines": {
      "default": 200,
      "maximum": 5000.0,
      "minimum": 1.0,
      "title": "Lines",
      "type": "integer",
      "description": "Nombre de dernières lignes à lire, de 1 à 5000. 200 par défaut."
     }
    },
    "required": [
     "host_id",
     "component"
    ],
    "title": "LogsBody",
    "type": "object",
    "description": "Journaux à récupérer."
   },
   "ManagementIn": {
    "properties": {
     "management": {
      "enum": [
       "platform",
       "client"
      ],
      "title": "Management",
      "type": "string",
      "description": "`platform` ou `client`."
     }
    },
    "required": [
     "management"
    ],
    "title": "ManagementIn",
    "type": "object",
    "description": "Mode de gestion d'une machine."
   },
   "Mapping": {
    "properties": {
     "cluster_id": {
      "anyOf": [
       {
        "maxLength": 64,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Cluster Id",
      "description": "Cluster auquel le rôle est limité ; nul pour une attribution globale."
     },
     "group": {
      "maxLength": 500,
      "minLength": 1,
      "title": "Group",
      "type": "string",
      "description": "Nom du groupe (attribut de nom de groupe LDAP, ou valeur de la revendication de groupes OIDC)."
     },
     "role": {
      "maxLength": 50,
      "title": "Role",
      "type": "string",
      "description": "Rôle donné aux membres : `admin`, `security_admin`, `cluster_admin`, `operator`, `viewer` ou `auditor`."
     }
    },
    "required": [
     "group",
     "role"
    ],
    "title": "Mapping",
    "type": "object",
    "description": "Un groupe du fournisseur d'identité associé à un rôle."
   },
   "MePatch": {
    "additionalProperties": false,
    "properties": {
     "display_name": {
      "anyOf": [
       {
        "maxLength": 200,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Display Name",
      "description": "Nom affiché dans la console, 200 caractères au plus."
     },
     "email": {
      "anyOf": [
       {
        "maxLength": 200,
        "pattern": "^$|^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Email",
      "description": "Adresse e-mail, 200 caractères au plus ; une chaîne vide l'efface."
     },
     "language": {
      "anyOf": [
       {
        "enum": [
         "en",
         "fr"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Language",
      "description": "Langue de la console et des messages : `en` ou `fr`."
     }
    },
    "title": "MePatch",
    "type": "object",
    "description": "Modification du profil de l'appelant ; un champ omis ou nul est conservé."
   },
   "MemberIn": {
    "properties": {
     "fault_domain": {
      "default": "",
      "title": "Fault Domain",
      "type": "string",
      "description": "Domaine de panne du membre (baie, zone). Vide par défaut."
     },
     "host_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Host Id",
      "description": "Machine du membre dans le cluster ; nul pour un membre hors des machines (Kubernetes, externe)."
     },
     "name": {
      "maxLength": 120,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom du membre, de 1 à 120 caractères."
     },
     "role": {
      "default": "any",
      "enum": [
       "primary",
       "replica",
       "witness",
       "any"
      ],
      "title": "Role",
      "type": "string",
      "description": "Rôle : `primary`, `replica`, `witness` ou `any` (par défaut)."
     }
    },
    "required": [
     "name"
    ],
    "title": "MemberIn",
    "type": "object",
    "description": "Un membre d'un groupe de haute disponibilité."
   },
   "MfaConfirmBody": {
    "additionalProperties": false,
    "properties": {
     "code": {
      "maxLength": 20,
      "minLength": 1,
      "title": "Code",
      "type": "string",
      "description": "Code courant (6 chiffres) du secret en attente."
     }
    },
    "required": [
     "code"
    ],
    "title": "MfaConfirmBody",
    "type": "object",
    "description": "Confirmation d'un enrôlement en libre service."
   },
   "MfaDisableBody": {
    "additionalProperties": false,
    "properties": {
     "code": {
      "anyOf": [
       {
        "maxLength": 20,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Code",
      "description": "Code TOTP courant, exigé pour un compte LDAP ou OIDC."
     },
     "password": {
      "anyOf": [
       {
        "maxLength": 1024,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Password",
      "description": "Mot de passe du compte, exigé pour un compte local."
     }
    },
    "title": "MfaDisableBody",
    "type": "object",
    "description": "Preuve d'identité pour désactiver le second facteur."
   },
   "MfaVerifyBody": {
    "additionalProperties": false,
    "properties": {
     "code": {
      "anyOf": [
       {
        "maxLength": 20,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Code",
      "description": "Code TOTP courant (6 chiffres). Prioritaire sur `recovery_code`."
     },
     "mfa_token": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Mfa Token",
      "type": "string",
      "description": "Jeton de défi renvoyé par la connexion (`mfa_token`), valable 5 minutes et 5 essais."
     },
     "recovery_code": {
      "anyOf": [
       {
        "maxLength": 64,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Recovery Code",
      "description": "Un des codes de secours donnés à l'enrôlement ; chacun ne sert qu'une fois."
     }
    },
    "required": [
     "mfa_token"
    ],
    "title": "MfaVerifyBody",
    "type": "object",
    "description": "Second facteur d'une connexion : un code TOTP ou un code de secours."
   },
   "ModeBody": {
    "properties": {
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Approbation accordée pour un mode qui arrête le service sous `banking-secure`."
     },
     "drain_timeout_s": {
      "anyOf": [
       {
        "maximum": 3600.0,
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Drain Timeout S",
      "description": "Pour `drain` : secondes laissées aux transactions en cours avant l'arrêt forcé des processus, de 0 à 3600. 300 par défaut."
     },
     "mode": {
      "enum": [
       "drain",
       "scale_to_zero",
       "security_suspend",
       "emergency_stop",
       "disable",
       "quarantine",
       "maintenance",
       "enable"
      ],
      "title": "Mode",
      "type": "string",
      "description": "Mode : `drain`, `scale_to_zero`, `security_suspend`, `emergency_stop`, `disable`, `quarantine`, `maintenance` ou `enable`."
     },
     "reason": {
      "anyOf": [
       {
        "maxLength": 500,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reason",
      "description": "Raison du mode, 500 caractères au plus ; exigée pour lever une suspension de sécurité."
     }
    },
    "required": [
     "mode"
    ],
    "title": "ModeBody",
    "type": "object",
    "description": "Un mode d'exploitation à appliquer."
   },
   "ModelIn": {
    "additionalProperties": false,
    "description": "Un modèle de la passerelle : son model_name LiteLLM, un libellé et les limites facultatives.",
    "properties": {
     "context_window": {
      "anyOf": [
       {
        "maximum": 100000000.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Context Window",
      "description": "Fenêtre de contexte en jetons, positive."
     },
     "id": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Id",
      "type": "string",
      "description": "`model_name` LiteLLM, unique dans le profil."
     },
     "label": {
      "default": "",
      "maxLength": 120,
      "title": "Label",
      "type": "string",
      "description": "Nom affiché dans Navin ; l'identifiant s'il est vide."
     },
     "max_output_tokens": {
      "anyOf": [
       {
        "maximum": 100000000.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Max Output Tokens",
      "description": "Nombre maximal de jetons produits, positif."
     },
     "reasoning_effort": {
      "anyOf": [
       {
        "enum": [
         "none",
         "minimal",
         "low",
         "medium",
         "high"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reasoning Effort",
      "description": "`none`, `minimal`, `low`, `medium` ou `high`."
     },
     "vision": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Vision",
      "description": "Le modèle accepte des images."
     }
    },
    "required": [
     "id"
    ],
    "title": "ModelIn",
    "type": "object"
   },
   "OidcBody": {
    "additionalProperties": false,
    "properties": {
     "client_id": {
      "maxLength": 300,
      "minLength": 1,
      "title": "Client Id",
      "type": "string",
      "description": "Identifiant client déclaré chez le fournisseur."
     },
     "client_secret": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Client Secret",
      "description": "Secret client : une chaîne le définit, une chaîne vide l'efface, l'objet relu ou nul le conserve. Scellé au repos."
     },
     "enabled": {
      "default": true,
      "title": "Enabled",
      "type": "boolean",
      "description": "Indique si le fournisseur est proposé sur la page de connexion. Vrai par défaut."
     },
     "groups_claim": {
      "default": "groups",
      "maxLength": 100,
      "minLength": 1,
      "title": "Groups Claim",
      "type": "string",
      "description": "Revendication du jeton d'identité qui porte les groupes. `groups` par défaut."
     },
     "issuer": {
      "maxLength": 500,
      "minLength": 9,
      "pattern": "^https://[^\\s]+$",
      "title": "Issuer",
      "type": "string",
      "description": "URL HTTPS de l'émetteur, par exemple `https://<sso-host>/realms/navin`."
     },
     "name": {
      "maxLength": 100,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom unique affiché sur la page de connexion, 1 à 100 caractères."
     },
     "role_mappings": {
      "items": {
       "$ref": "#/components/schemas/Mapping"
      },
      "maxItems": 200,
      "title": "Role Mappings",
      "type": "array",
      "description": "Correspondances entre groupes et rôles appliquées à chaque connexion, 200 au plus."
     },
     "scopes": {
      "items": {
       "type": "string"
      },
      "maxItems": 20,
      "title": "Scopes",
      "type": "array",
      "description": "Portées demandées ; doivent comprendre `openid`. Par défaut `openid`, `profile`, `email`."
     },
     "username_claim": {
      "default": "preferred_username",
      "maxLength": 100,
      "minLength": 1,
      "title": "Username Claim",
      "type": "string",
      "description": "Revendication utilisée comme nom d'utilisateur. `preferred_username` par défaut."
     }
    },
    "required": [
     "name",
     "issuer",
     "client_id"
    ],
    "title": "OidcBody",
    "type": "object",
    "description": "Réglages d'un fournisseur OIDC."
   },
   "ParametersIn": {
    "properties": {
     "limits": {
      "additionalProperties": {
       "anyOf": [
        {
         "type": "integer"
        },
        {
         "type": "string"
        }
       ]
      },
      "title": "Limits",
      "type": "object",
      "description": "Limites de ressources pour tous les utilisateurs : clés `nofile`, `nproc`, `memlock` ; valeurs numériques ou chaînes telles que `unlimited`."
     },
     "sysctl": {
      "additionalProperties": {
       "anyOf": [
        {
         "type": "integer"
        },
        {
         "type": "string"
        }
       ]
      },
      "title": "Sysctl",
      "type": "object",
      "description": "Réglages sysctl, par exemple `{\"vm.max_map_count\": 262144}`. Clés : lettres minuscules, chiffres, `_`, `.` et `-`."
     }
    },
    "title": "ParametersIn",
    "type": "object",
    "description": "Paramètres noyau d'une machine."
   },
   "PasswordChangeBody": {
    "additionalProperties": false,
    "properties": {
     "current_password": {
      "anyOf": [
       {
        "maxLength": 1024,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Current Password",
      "description": "Mot de passe actuel, exigé quand on est connecté."
     },
     "new_password": {
      "maxLength": 1024,
      "minLength": 1,
      "title": "New Password",
      "type": "string",
      "description": "Nouveau mot de passe ; doit respecter la politique des mots de passe."
     },
     "password_change_token": {
      "anyOf": [
       {
        "maxLength": 200,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Password Change Token",
      "description": "Jeton issu de `details.password_change_token` d'une erreur de connexion `403 password_expired`, valable 5 minutes."
     }
    },
    "required": [
     "new_password"
    ],
    "title": "PasswordChangeBody",
    "type": "object",
    "description": "Changement d'un mot de passe, connecté ou avec un jeton de changement de mot de passe."
   },
   "PasswordReset": {
    "additionalProperties": false,
    "properties": {
     "must_change_password": {
      "default": true,
      "title": "Must Change Password",
      "type": "boolean",
      "description": "Si vrai (par défaut), l'utilisateur doit choisir un nouveau mot de passe à la prochaine connexion."
     },
     "new_password": {
      "maxLength": 1024,
      "minLength": 1,
      "title": "New Password",
      "type": "string",
      "description": "Nouveau mot de passe ; doit respecter la politique des mots de passe. En écriture seule."
     }
    },
    "required": [
     "new_password"
    ],
    "title": "PasswordReset",
    "type": "object",
    "description": "Mot de passe défini par un administrateur."
   },
   "PdbIn": {
    "additionalProperties": false,
    "properties": {
     "max_unavailable": {
      "anyOf": [
       {
        "maximum": 1000.0,
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "pattern": "^(100|[1-9]?[0-9])%$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Max Unavailable",
      "description": "Pods qui peuvent être indisponibles en même temps : un nombre (de 0 à 1000) ou un pourcentage tel que `25%`."
     },
     "min_available": {
      "anyOf": [
       {
        "maximum": 1000.0,
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "pattern": "^(100|[1-9]?[0-9])%$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Min Available",
      "description": "Pods qui doivent rester disponibles : un nombre (de 0 à 1000) ou un pourcentage tel que `50%`."
     }
    },
    "title": "PdbIn",
    "type": "object",
    "description": "Budget d'interruption des pods : exactement un parmi `min_available` et `max_unavailable`."
   },
   "PlanIn": {
    "properties": {
     "encryption_key_ref": {
      "minLength": 1,
      "title": "Encryption Key Ref",
      "type": "string",
      "description": "Référence de la clé qui chiffre les sauvegardes."
     },
     "immutability_required": {
      "default": false,
      "title": "Immutability Required",
      "type": "boolean",
      "description": "Exiger une cible immuable (WORM) avec verrouillage d'objet et rétention. Faux par défaut."
     },
     "interval_seconds": {
      "minimum": 60.0,
      "title": "Interval Seconds",
      "type": "integer",
      "description": "Délai entre deux sauvegardes en secondes, 60 ou plus."
     },
     "name": {
      "maxLength": 100,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom du plan, de 1 à 100 caractères."
     },
     "retention_days": {
      "minimum": 1.0,
      "title": "Retention Days",
      "type": "integer",
      "description": "Nombre de jours pendant lesquels une sauvegarde est conservée avant de pouvoir être supprimée, 1 ou plus."
     },
     "scope": {
      "additionalProperties": true,
      "title": "Scope",
      "type": "object",
      "description": "Ce qui est sauvegardé (clusters, services, jeux de données), sous forme d'objet."
     },
     "target_id": {
      "title": "Target Id",
      "type": "string",
      "description": "Identifiant de la cible de sauvegarde."
     },
     "window_seconds": {
      "default": 3600,
      "minimum": 0.0,
      "title": "Window Seconds",
      "type": "integer",
      "description": "Tolérance après l'intervalle avant qu'une sauvegarde soit signalée en retard, en secondes. 3600 par défaut."
     }
    },
    "required": [
     "name",
     "target_id",
     "scope",
     "interval_seconds",
     "encryption_key_ref",
     "retention_days"
    ],
    "title": "PlanIn",
    "type": "object",
    "description": "Un nouveau plan de sauvegarde."
   },
   "PolicyBody": {
    "additionalProperties": false,
    "properties": {
     "lockout_minutes": {
      "maximum": 1440.0,
      "minimum": 1.0,
      "title": "Lockout Minutes",
      "type": "integer",
      "description": "Durée d'un verrouillage en minutes, de 1 à 1440."
     },
     "lockout_threshold": {
      "maximum": 20.0,
      "minimum": 3.0,
      "title": "Lockout Threshold",
      "type": "integer",
      "description": "Nombre d'échecs de connexion qui verrouillent le compte, de 3 à 20."
     },
     "mfa_required_roles": {
      "items": {
       "type": "string"
      },
      "maxItems": 6,
      "title": "Mfa Required Roles",
      "type": "array",
      "description": "Rôles dont les titulaires doivent utiliser la MFA (enrôlement imposé à la connexion quand la MFA est activée)."
     },
     "password_history": {
      "maximum": 24.0,
      "minimum": 0.0,
      "title": "Password History",
      "type": "integer",
      "description": "Nombre d'anciens mots de passe qui ne peuvent pas être réutilisés, de 0 à 24."
     },
     "password_max_age_days": {
      "maximum": 3650.0,
      "minimum": 0.0,
      "title": "Password Max Age Days",
      "type": "integer",
      "description": "Nombre de jours après lequel un mot de passe local doit être changé, de 0 à 3650 ; 0 : jamais."
     },
     "password_min_length": {
      "maximum": 128.0,
      "minimum": 8.0,
      "title": "Password Min Length",
      "type": "integer",
      "description": "Longueur minimale d'un mot de passe, de 8 à 128 caractères."
     },
     "session_absolute_hours": {
      "maximum": 72.0,
      "minimum": 1.0,
      "title": "Session Absolute Hours",
      "type": "integer",
      "description": "Durée de vie maximale d'une session en heures, de 1 à 72."
     },
     "session_idle_minutes": {
      "maximum": 480.0,
      "minimum": 5.0,
      "title": "Session Idle Minutes",
      "type": "integer",
      "description": "Durée d'inactivité au bout de laquelle une session prend fin, de 5 à 480 minutes."
     }
    },
    "required": [
     "password_min_length",
     "password_history",
     "password_max_age_days",
     "lockout_threshold",
     "lockout_minutes",
     "session_idle_minutes",
     "session_absolute_hours",
     "mfa_required_roles"
    ],
    "title": "PolicyBody",
    "type": "object",
    "description": "Politique de sécurité de la console."
   },
   "PolicyIn": {
    "properties": {
     "automatic": {
      "default": false,
      "title": "Automatic",
      "type": "boolean",
      "description": "Basculer automatiquement quand le primaire est perdu. Faux par défaut (décision de l'opérateur)."
     },
     "fencing": {
      "default": "lease",
      "enum": [
       "none",
       "lease",
       "stonith",
       "promote_demote"
      ],
      "title": "Fencing",
      "type": "string",
      "description": "Façon d'isoler l'ancien primaire : `none`, `lease` (par défaut), `stonith` ou `promote_demote`."
     },
     "max_lag_seconds": {
      "anyOf": [
       {
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Max Lag Seconds",
      "description": "Retard de réplication maximal accepté pour promouvoir un réplica, en secondes."
     }
    },
    "title": "PolicyIn",
    "type": "object",
    "description": "Politique de bascule d'un groupe."
   },
   "PreflightBody": {
    "properties": {
     "hosts": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 1000,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Hosts",
      "description": "Identifiants des machines à contrôler, 1000 au plus ; nul : toutes les machines."
     },
     "services": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 200,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Services",
      "description": "Services à contrôler, 200 au plus ; nul : tous les services."
     }
    },
    "title": "PreflightBody",
    "type": "object",
    "description": "Périmètre de contrôles préalables d'installation."
   },
   "PreflightIn": {
    "additionalProperties": false,
    "properties": {
     "certificate": {
      "anyOf": [
       {
        "maxLength": 20000,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Certificate",
      "description": "Certificat utilisateur OpenSSH de la clé, signé par une autorité approuvée."
     },
     "cluster_id": {
      "title": "Cluster Id",
      "type": "string",
      "description": "Cluster que la machine rejoindrait."
     },
     "disk_paths": {
      "items": {
       "type": "string"
      },
      "maxItems": 10,
      "title": "Disk Paths",
      "type": "array",
      "description": "Chemins dont l'espace libre est contrôlé, 10 au plus. `/` par défaut."
     },
     "expected_ip": {
      "maxLength": 64,
      "minLength": 1,
      "title": "Expected Ip",
      "type": "string",
      "description": "Adresse IP vers laquelle le FQDN doit être résolu."
     },
     "fqdn": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Fqdn",
      "type": "string",
      "description": "FQDN de la machine, contrôlé en résolution directe et inverse."
     },
     "jumps": {
      "items": {
       "$ref": "#/components/schemas/EndpointIn"
      },
      "maxItems": 4,
      "title": "Jumps",
      "type": "array",
      "description": "Machines de rebond à traverser, 4 au plus."
     },
     "private_key": {
      "maxLength": 20000,
      "minLength": 20,
      "title": "Private Key",
      "type": "string",
      "description": "Clé SSH privée de l'opérateur. Gardée en mémoire pour l'exécution uniquement."
     },
     "required_ports": {
      "items": {
       "type": "integer"
      },
      "maxItems": 50,
      "title": "Required Ports",
      "type": "array",
      "description": "Ports TCP qui doivent être libres sur la machine (un port déjà en écoute est signalé), 50 au plus. `[8461]` par défaut."
     },
     "ssh_user": {
      "anyOf": [
       {
        "maxLength": 32,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Ssh User",
      "description": "Compte SSH ; par défaut : le compte d'amorçage de la politique SSH."
     },
     "target": {
      "$ref": "#/components/schemas/EndpointIn",
      "description": "La machine à contrôler."
     }
    },
    "required": [
     "cluster_id",
     "target",
     "private_key",
     "fqdn",
     "expected_ip"
    ],
    "title": "PreflightIn",
    "type": "object",
    "description": "Accès SSH et attentes des contrôles préalables d'une machine. Un champ de mot de passe est refusé."
   },
   "ProfileBody": {
    "properties": {
     "profile": {
      "enum": [
       "development",
       "standard",
       "banking-secure",
       "air-gapped",
       "hybrid"
      ],
      "title": "Profile",
      "type": "string",
      "description": "`development`, `standard`, `banking-secure`, `air-gapped` ou `hybrid`."
     }
    },
    "required": [
     "profile"
    ],
    "title": "ProfileBody",
    "type": "object",
    "description": "Profil d'installation d'un cluster."
   },
   "ProfileIn": {
    "additionalProperties": false,
    "description": "Un profil Navin SM.",
    "properties": {
     "auth_mode": {
      "default": "user_key",
      "enum": [
       "user_key",
       "shared_key",
       "none"
      ],
      "title": "Auth Mode",
      "type": "string",
      "description": "`user_key`, `shared_key` ou `none`."
     },
     "base_url": {
      "maxLength": 2048,
      "minLength": 1,
      "title": "Base Url",
      "type": "string",
      "description": "Adresse `http` ou `https` du proxy, sans `/chat/completions`."
     },
     "ca_bundle": {
      "default": "",
      "maxLength": 1024,
      "title": "Ca Bundle",
      "type": "string",
      "description": "Chemin d'un fichier PEM sur les postes, approuvé en plus des racines publiques ; refusé avec `use_manager_ca`."
     },
     "default_model": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Default Model",
      "type": "string",
      "description": "Un des identifiants de modèle."
     },
     "exclusive": {
      "default": true,
      "title": "Exclusive",
      "type": "boolean",
      "description": "Seule cette passerelle peut servir (vrai par défaut)."
     },
     "headers": {
      "additionalProperties": {
       "maxLength": 4096,
       "type": "string"
      },
      "maxProperties": 50,
      "title": "Headers",
      "type": "object",
      "description": "En-têtes HTTP supplémentaires ; les en-têtes d'identification sont refusés."
     },
     "litellm_cluster_id": {
      "anyOf": [
       {
        "format": "uuid",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Litellm Cluster Id",
      "description": "Cluster du service LiteLLM installé par le Manager auquel le profil est lié ; avec `litellm_service`, ou aucun des deux. Le lien donne sa clé maître à la découverte et permet au Manager d'émettre les clés virtuelles."
     },
     "litellm_service": {
      "default": "",
      "maxLength": 100,
      "title": "Litellm Service",
      "type": "string",
      "description": "Nom du service LiteLLM installé dans ce cluster (100 caractères au plus)."
     },
     "models": {
      "items": {
       "$ref": "#/components/schemas/ModelIn"
      },
      "maxItems": 500,
      "title": "Models",
      "type": "array",
      "description": "1 à 200 modèles."
     },
     "org_id": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Org Id",
      "type": "string",
      "description": "Identifiant de l'organisation : 1 à 64 lettres minuscules, chiffres et tirets, unique."
     },
     "org_name": {
      "maxLength": 120,
      "minLength": 1,
      "title": "Org Name",
      "type": "string",
      "description": "Nom de l'organisation affiché dans Navin, 120 caractères au plus (la limite de Navin)."
     },
     "policy": {
      "$ref": "#/components/schemas/SmPolicyIn",
      "description": "Politique gérée."
     },
     "routes": {
      "additionalProperties": {
       "maxLength": 200,
       "type": "string"
      },
      "maxProperties": 50,
      "title": "Routes",
      "type": "object",
      "description": "Rôles de tâche Navin vers identifiants de modèle."
     },
     "shared_key_ref": {
      "anyOf": [
       {
        "format": "uuid",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Shared Key Ref",
      "description": "Entrée du coffre qui contient la clé partagée ; obligatoire en `shared_key`, refusée sinon."
     },
     "support_contact": {
      "default": "",
      "maxLength": 200,
      "title": "Support Contact",
      "type": "string",
      "description": "Contact affiché aux utilisateurs quand la passerelle échoue, 200 caractères au plus."
     },
     "use_manager_ca": {
      "default": false,
      "title": "Use Manager Ca",
      "type": "boolean",
      "description": "Embarquer l'autorité du Manager dans le bundle de déploiement et faire pointer `gateway.caBundle` vers son chemin sur le système (`/etc/navin/navin-sm-ca.pem`, `/Library/Application Support/Navin/navin-sm-ca.pem`, `C:\\ProgramData\\Navin\\navin-sm-ca.pem`) ; l'export et la prévisualisation demandent alors `os`."
     }
    },
    "required": [
     "org_id",
     "org_name",
     "base_url",
     "default_model"
    ],
    "title": "ProfileIn",
    "type": "object"
   },
   "ProxyBody": {
    "additionalProperties": false,
    "description": "Proxy d'entreprise des machines gérées : URL http(s) sans identifiants, chaîne vide pour effacer.",
    "properties": {
     "http_proxy": {
      "anyOf": [
       {
        "maxLength": 500,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Http Proxy",
      "description": "Proxy du trafic HTTP, par exemple `http://<proxy-host>:3128`."
     },
     "https_proxy": {
      "anyOf": [
       {
        "maxLength": 500,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Https Proxy",
      "description": "Proxy du trafic HTTPS."
     },
     "no_proxy": {
      "anyOf": [
       {
        "maxLength": 2000,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "No Proxy",
      "description": "Hôtes, domaines et réseaux séparés par des virgules, joints sans le proxy, par exemple `<internal-domain>,10.0.0.0/8`."
     }
    },
    "title": "ProxyBody",
    "type": "object"
   },
   "ReconcileBody": {
    "properties": {
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Identifiant de la demande approuvée (validation à quatre yeux) qui autorise cette action."
     },
     "restart": {
      "default": false,
      "title": "Restart",
      "type": "boolean",
      "description": "Redémarrer l'instance après la réécriture de ses fichiers. Faux par défaut."
     }
    },
    "title": "ReconcileBody",
    "type": "object",
    "description": "Options d'une correction de dérive."
   },
   "RegisterIn": {
    "properties": {
     "agent_version": {
      "default": "",
      "maxLength": 40,
      "title": "Agent Version",
      "type": "string",
      "description": "Version de l'agent, 40 caractères au plus."
     },
     "bootstrap_token": {
      "maxLength": 200,
      "minLength": 20,
      "title": "Bootstrap Token",
      "type": "string",
      "description": "Jeton d'amorçage créé pour le cluster (20 à 200 caractères). En écriture seule, conservé sous forme d'empreinte."
     },
     "csr_pem": {
      "maxLength": 20000,
      "minLength": 100,
      "title": "Csr Pem",
      "type": "string",
      "description": "Demande de signature de certificat de la clé de l'agent, encodée en PEM. Seule sa clé publique est utilisée."
     },
     "facts": {
      "additionalProperties": true,
      "title": "Facts",
      "type": "object",
      "description": "Caractéristiques relevées sur la machine : système d'exploitation, architecture, processeurs, mémoire, disques, réseau..."
     },
     "fqdn": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Fqdn",
      "type": "string",
      "description": "Nom de domaine complet de la machine, par exemple `<db1-host>`. Identifie la machine dans son cluster."
     },
     "hostname": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Hostname",
      "type": "string",
      "description": "Nom court de la machine, 1 à 253 caractères."
     },
     "ip": {
      "default": "",
      "maxLength": 64,
      "title": "Ip",
      "type": "string",
      "description": "Adresse IP de la machine. Vide : l'adresse source de la requête est utilisée."
     },
     "service_driver": {
      "default": "systemd",
      "pattern": "^(systemd|process)$",
      "title": "Service Driver",
      "type": "string",
      "description": "Façon dont l'agent fait tourner les services : `systemd` (par défaut) ou `process` (simples processus, pour les conteneurs sans systemd)."
     }
    },
    "required": [
     "bootstrap_token",
     "hostname",
     "fqdn",
     "csr_pem"
    ],
    "title": "RegisterIn",
    "type": "object",
    "description": "Demande d'enregistrement d'un agent."
   },
   "RestorePlanIn": {
    "properties": {
     "artifact_id": {
      "title": "Artifact Id",
      "type": "string",
      "description": "Identifiant de l'artefact de sauvegarde à restaurer."
     },
     "config_snapshot": {
      "additionalProperties": true,
      "default": {},
      "title": "Config Snapshot",
      "type": "object",
      "description": "Configuration enregistrée avec le plan pour l'opérateur ; elle n'est jamais appliquée automatiquement."
     },
     "environment": {
      "pattern": "^(isolated|staging|production)$",
      "title": "Environment",
      "type": "string",
      "description": "Lieu de restauration : `isolated`, `staging` ou `production` ; doit être l'environnement déclaré pour `target_name`."
     },
     "target_name": {
      "default": "",
      "maxLength": 100,
      "title": "Target Name",
      "type": "string",
      "description": "Nom d'une destination de `GET /restore-targets`. Sans elle, le plan ne permet que l'essai à blanc et son exécution est refusée (`501 restore_executor_unavailable`)."
     },
     "target_versions": {
      "additionalProperties": {
       "type": "string"
      },
      "default": {},
      "title": "Target Versions",
      "type": "object",
      "description": "Versions des composants cibles, comparées aux versions sauvegardées (même version majeure exigée)."
     }
    },
    "required": [
     "artifact_id",
     "environment"
    ],
    "title": "RestorePlanIn",
    "type": "object",
    "description": "Une restauration à planifier."
   },
   "RestoreTestIn": {
    "properties": {
     "inject_corruption": {
      "default": false,
      "title": "Inject Corruption",
      "type": "boolean",
      "description": "Corrompre volontairement la copie pour prouver que le test le détecte. Faux par défaut."
     }
    },
    "title": "RestoreTestIn",
    "type": "object",
    "description": "Options d'un test de restauration."
   },
   "RetentionBody": {
    "additionalProperties": false,
    "properties": {
     "metrics_days": {
      "anyOf": [
       {
        "maximum": 3650.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Metrics Days",
      "description": "Jours de métriques des machines conservés, de 1 à 3650."
     },
     "operations_days": {
      "anyOf": [
       {
        "maximum": 3650.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Operations Days",
      "description": "Jours d'opérations terminées conservés, de 1 à 3650."
     },
     "task_logs_days": {
      "anyOf": [
       {
        "maximum": 3650.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Task Logs Days",
      "description": "Jours de journaux de tâches conservés, de 1 à 3650."
     }
    },
    "title": "RetentionBody",
    "type": "object",
    "description": "Durée de conservation des données du Manager."
   },
   "RoleItem": {
    "properties": {
     "cluster_id": {
      "anyOf": [
       {
        "maxLength": 64,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Cluster Id",
      "description": "Cluster auquel le rôle est limité ; nul ou absent pour une attribution globale. Interdit pour les rôles uniquement globaux."
     },
     "role": {
      "maxLength": 50,
      "title": "Role",
      "type": "string",
      "description": "Nom du rôle : `admin`, `security_admin`, `cluster_admin`, `operator`, `viewer` ou `auditor`."
     }
    },
    "required": [
     "role"
    ],
    "title": "RoleItem",
    "type": "object",
    "description": "Une attribution de rôle : un rôle, globalement ou sur un cluster."
   },
   "RolesBody": {
    "additionalProperties": false,
    "properties": {
     "roles": {
      "items": {
       "$ref": "#/components/schemas/RoleItem"
      },
      "maxItems": 50,
      "title": "Roles",
      "type": "array",
      "description": "Toutes les attributions manuelles de l'utilisateur, 50 au plus ; une liste vide les retire toutes."
     }
    },
    "required": [
     "roles"
    ],
    "title": "RolesBody",
    "type": "object",
    "description": "Attributions de rôle manuelles d'un utilisateur."
   },
   "RotateBody": {
    "description": "Nouveau mot de passe du compte de la console (généré s'il est omis) et façon dont le service redémarre pour l'utiliser.",
    "properties": {
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Approbation accordée pour l'application sous `banking-secure`."
     },
     "batch_size": {
      "default": 1,
      "maximum": 100.0,
      "minimum": 1.0,
      "title": "Batch Size",
      "type": "integer",
      "description": "Machines redémarrées en même temps par un redémarrage progressif, de 1 à 100. 1 par défaut."
     },
     "password": {
      "anyOf": [
       {
        "maxLength": 256,
        "minLength": 16,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Password",
      "description": "Nouveau mot de passe, de 16 à 256 caractères ; généré s'il est omis. En écriture seule."
     },
     "restart": {
      "default": "rolling",
      "enum": [
       "rolling",
       "all"
      ],
      "title": "Restart",
      "type": "string",
      "description": "Politique de redémarrage : `rolling` (par défaut) ou `all`."
     }
    },
    "title": "RotateBody",
    "type": "object"
   },
   "RotateIn": {
    "properties": {
     "service": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Service",
      "description": "Renouveler uniquement les certificats de ce service ; nul : tous les services du cluster."
     }
    },
    "title": "RotateIn",
    "type": "object",
    "description": "Périmètre d'un renouvellement de certificats."
   },
   "ScanBody": {
    "properties": {
     "hosts": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 1000,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Hosts",
      "description": "Identifiants des machines à contrôler, 1000 au plus ; nul : toutes les machines."
     }
    },
    "title": "ScanBody",
    "type": "object",
    "description": "Périmètre d'une recherche de dérive."
   },
   "SecretRefIn": {
    "additionalProperties": false,
    "properties": {
     "key": {
      "anyOf": [
       {
        "maxLength": 253,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Key",
      "description": "Clé dans le Secret ; facultative, transmise à l'opérateur quand elle est fournie."
     },
     "name": {
      "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
      "title": "Name",
      "type": "string",
      "description": "Nom de la référence (libellé DNS)."
     },
     "secret_name": {
      "maxLength": 253,
      "pattern": "^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$",
      "title": "Secret Name",
      "type": "string",
      "description": "Nom du Secret dans l'espace de noms géré."
     },
     "value_path": {
      "maxLength": 253,
      "pattern": "^[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*$",
      "title": "Value Path",
      "type": "string",
      "description": "Chemin pointé de la valeur du chart à remplir, par exemple `auth.password`."
     }
    },
    "required": [
     "name",
     "secret_name",
     "value_path"
    ],
    "title": "SecretRefIn",
    "type": "object",
    "description": "Une valeur de chart prise dans un Secret Kubernetes."
   },
   "ServerIn": {
    "additionalProperties": false,
    "description": "Un serveur MCP d'un projet de locataire : à partir d'un modèle (`preset` et `values`, `url` pour l'adresse interne quand l'URL du modèle n'est pas interne) ou personnalisé (`url` ou `command`).",
    "properties": {
     "allowed_tools": {
      "items": {
       "type": "string"
      },
      "maxItems": 500,
      "minItems": 1,
      "title": "Allowed Tools",
      "type": "array",
      "description": "Outils que le serveur peut exposer, de 1 à 500 noms. `[\"*\"]` par défaut (tous les outils)."
     },
     "args": {
      "items": {
       "type": "string"
      },
      "maxItems": 64,
      "title": "Args",
      "type": "array",
      "description": "Arguments de la commande stdio, 64 au plus."
     },
     "command": {
      "anyOf": [
       {
        "maxLength": 512,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Command",
      "description": "Commande d'un serveur stdio personnalisé, par exemple `mcp-server-git`. Aucun shell n'est utilisé."
     },
     "env": {
      "additionalProperties": {
       "type": "string"
      },
      "maxProperties": 64,
      "title": "Env",
      "type": "object",
      "description": "Environnement d'un serveur stdio personnalisé, 64 variables au plus ; scellé."
     },
     "headers": {
      "additionalProperties": {
       "type": "string"
      },
      "maxProperties": 32,
      "title": "Headers",
      "type": "object",
      "description": "En-têtes HTTP d'un serveur HTTP personnalisé, 32 au plus ; scellés."
     },
     "name": {
      "anyOf": [
       {
        "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name",
      "description": "Nom unique du serveur dans le projet (identifiant court) ; par défaut : le nom du modèle ; obligatoire pour un serveur personnalisé."
     },
     "preset": {
      "anyOf": [
       {
        "maxLength": 120,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Preset",
      "description": "Nom du modèle de la bibliothèque Navin."
     },
     "project": {
      "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
      "title": "Project",
      "type": "string",
      "description": "Identifiant court du projet."
     },
     "status": {
      "default": "active",
      "pattern": "^(active|disabled)$",
      "title": "Status",
      "type": "string",
      "description": "`active` (par défaut) ou `disabled`."
     },
     "tenant": {
      "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
      "title": "Tenant",
      "type": "string",
      "description": "Identifiant court du locataire."
     },
     "transport": {
      "anyOf": [
       {
        "pattern": "^(stdio|streamable_http|sse)$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Transport",
      "description": "`stdio`, `streamable_http` ou `sse` ; par défaut : `stdio` avec une commande, sinon `streamable_http`."
     },
     "url": {
      "anyOf": [
       {
        "maxLength": 2048,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Url",
      "description": "URL interne du serveur, par exemple `https://<mcp-host>/mcp` ; doit être une adresse privée ou un hôte interne autorisé."
     },
     "values": {
      "additionalProperties": {
       "type": "string"
      },
      "maxProperties": 64,
      "title": "Values",
      "type": "object",
      "description": "Valeurs des champs du modèle, 64 au plus ; les champs secrets sont scellés."
     }
    },
    "required": [
     "tenant",
     "project"
    ],
    "title": "ServerIn",
    "type": "object"
   },
   "ServerPatch": {
    "additionalProperties": false,
    "description": "Champs à modifier. `values` est fusionné avec les valeurs enregistrées (une chaîne vide oublie une valeur) ; `headers` et `env` remplacent ceux enregistrés (serveurs personnalisés uniquement).",
    "properties": {
     "allowed_tools": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 500,
        "minItems": 1,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Allowed Tools",
      "description": "Nouvelle liste d'outils autorisés, de 1 à 500 noms."
     },
     "args": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 64,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Args",
      "description": "Nouveaux arguments de la commande (serveurs personnalisés uniquement)."
     },
     "command": {
      "anyOf": [
       {
        "maxLength": 512,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Command",
      "description": "Nouvelle commande (serveurs personnalisés uniquement)."
     },
     "env": {
      "anyOf": [
       {
        "additionalProperties": {
         "type": "string"
        },
        "maxProperties": 64,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Env",
      "description": "Nouvel environnement, qui remplace celui enregistré (serveurs personnalisés uniquement)."
     },
     "headers": {
      "anyOf": [
       {
        "additionalProperties": {
         "type": "string"
        },
        "maxProperties": 32,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Headers",
      "description": "Nouveaux en-têtes HTTP, qui remplacent ceux enregistrés (serveurs personnalisés uniquement)."
     },
     "status": {
      "anyOf": [
       {
        "pattern": "^(active|disabled)$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Status",
      "description": "`active` ou `disabled` ; désactiver un serveur hébergé le retire."
     },
     "url": {
      "anyOf": [
       {
        "maxLength": 2048,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Url",
      "description": "Nouvelle URL interne (serveurs personnalisés uniquement)."
     },
     "values": {
      "anyOf": [
       {
        "additionalProperties": {
         "type": "string"
        },
        "maxProperties": 64,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Values",
      "description": "Valeurs de champs du modèle à fusionner ; une chaîne vide oublie une valeur."
     }
    },
    "title": "ServerPatch",
    "type": "object"
   },
   "ServicesBody": {
    "properties": {
     "auto_dependencies": {
      "default": true,
      "title": "Auto Dependencies",
      "type": "boolean",
      "description": "Ajouter les dépendances manquantes à l'installation. Vrai par défaut."
     },
     "ha_acknowledged": {
      "default": false,
      "title": "Ha Acknowledged",
      "type": "boolean",
      "description": "Accepter une topologie en dessous des exigences de haute disponibilité. Faux par défaut."
     },
     "service_check": {
      "default": true,
      "title": "Service Check",
      "type": "boolean",
      "description": "Exécuter le contrôle de service de chaque service à la fin. Vrai par défaut."
     },
     "services": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "maxItems": 100,
      "minItems": 1,
      "title": "Services",
      "type": "array",
      "description": "Services à installer, de 1 à 100. Chaque élément : `name` (obligatoire), `version`, `mode`, `method`, `managed`, `placements` (`{composant: [identifiants de machines]}`), `config` et `bindings` (vers d'autres services)."
     },
     "start": {
      "default": true,
      "title": "Start",
      "type": "boolean",
      "description": "Démarrer les services une fois installés. Vrai par défaut."
     }
    },
    "required": [
     "services"
    ],
    "title": "ServicesBody",
    "type": "object",
    "description": "Une demande d'installation."
   },
   "SettingsBody": {
    "additionalProperties": false,
    "properties": {
     "default_language": {
      "anyOf": [
       {
        "enum": [
         "en",
         "fr"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Default Language",
      "description": "Langue de la console pour les utilisateurs qui n'en ont pas choisi : `en` ou `fr`."
     },
     "install": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/InstallBody"
       },
       {
        "type": "null"
       }
      ],
      "description": "Valeurs par défaut d'installation."
     },
     "proxy": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/ProxyBody"
       },
       {
        "type": "null"
       }
      ],
      "description": "Proxy d'entreprise des machines gérées."
     },
     "retention": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/RetentionBody"
       },
       {
        "type": "null"
       }
      ],
      "description": "Conservation des données."
     },
     "site_name": {
      "anyOf": [
       {
        "maxLength": 100,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Site Name",
      "description": "Nom du site affiché dans la console, de 1 à 100 caractères."
     },
     "smtp": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/SmtpBody"
       },
       {
        "type": "null"
       }
      ],
      "description": "Serveur de courrier sortant."
     },
     "syslog": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/SyslogBody"
       },
       {
        "type": "null"
       }
      ],
      "description": "Serveur syslog distant."
     }
    },
    "title": "SettingsBody",
    "type": "object",
    "description": "Modification des réglages ; une section omise ou nulle est conservée."
   },
   "SetupBody": {
    "additionalProperties": false,
    "properties": {
     "display_name": {
      "anyOf": [
       {
        "maxLength": 200,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Display Name",
      "description": "Nom affiché dans la console ; le nom d'utilisateur s'il est vide."
     },
     "password": {
      "maxLength": 1024,
      "minLength": 1,
      "title": "Password",
      "type": "string",
      "description": "Mot de passe ; doit respecter la politique des mots de passe. En écriture seule."
     },
     "username": {
      "maxLength": 100,
      "minLength": 1,
      "pattern": "^[A-Za-z0-9._@-]+$",
      "title": "Username",
      "type": "string",
      "description": "Nom d'utilisateur : lettres, chiffres, `.`, `_`, `@` et `-`, 100 caractères au plus."
     }
    },
    "required": [
     "username",
     "password"
    ],
    "title": "SetupBody",
    "type": "object",
    "description": "Premier administrateur d'un nouveau Manager."
   },
   "SmPolicyIn": {
    "additionalProperties": false,
    "description": "Politique gérée du fichier.",
    "properties": {
     "allow": {
      "items": {
       "maxLength": 200,
       "minLength": 1,
       "type": "string"
      },
      "maxItems": 500,
      "title": "Allow",
      "type": "array",
      "description": "Outils permis (`permissions.allow`)."
     },
     "allow_managed_hooks_only": {
      "default": false,
      "title": "Allow Managed Hooks Only",
      "type": "boolean",
      "description": "Les hooks personnels, de projet et de plugin sont ignorés."
     },
     "allow_managed_plugins_only": {
      "default": false,
      "title": "Allow Managed Plugins Only",
      "type": "boolean",
      "description": "Seuls les plugins de `allowed_plugins` se chargent."
     },
     "allowed_plugins": {
      "items": {
       "maxLength": 200,
       "minLength": 1,
       "type": "string"
      },
      "maxItems": 500,
      "title": "Allowed Plugins",
      "type": "array",
      "description": "Plugins permis."
     },
     "deny": {
      "items": {
       "maxLength": 200,
       "minLength": 1,
       "type": "string"
      },
      "maxItems": 500,
      "title": "Deny",
      "type": "array",
      "description": "Outils refusés (`permissions.deny`)."
     },
     "disable_bypass_permissions": {
      "default": false,
      "title": "Disable Bypass Permissions",
      "type": "boolean",
      "description": "Le contournement des demandes de permission est refusé."
     },
     "env": {
      "additionalProperties": {
       "maxLength": 4096,
       "type": "string"
      },
      "maxProperties": 100,
      "title": "Env",
      "type": "object",
      "description": "Variables d'environnement imposées ; les noms ou valeurs portant un secret sont refusés."
     },
     "mcp_allowlist": {
      "anyOf": [
       {
        "items": {
         "maxLength": 200,
         "minLength": 1,
         "type": "string"
        },
        "maxItems": 500,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Mcp Allowlist",
      "description": "Noms des serveurs MCP permis ; nul pour aucune restriction."
     }
    },
    "title": "SmPolicyIn",
    "type": "object"
   },
   "SmtpBody": {
    "additionalProperties": false,
    "properties": {
     "from": {
      "anyOf": [
       {
        "maxLength": 300,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "From",
      "description": "Adresse de l'expéditeur, par exemple `<email-address>`."
     },
     "host": {
      "anyOf": [
       {
        "maxLength": 255,
        "pattern": "^[A-Za-z0-9._:-]*$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Host",
      "description": "Nom ou adresse du serveur SMTP, par exemple `<smtp-host>`."
     },
     "password": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Password",
      "description": "Mot de passe du compte SMTP : une chaîne le définit, l'objet relu le conserve. En écriture seule, scellé."
     },
     "port": {
      "anyOf": [
       {
        "maximum": 65535.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Port",
      "description": "Port du serveur SMTP, de 1 à 65535."
     },
     "starttls": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Starttls",
      "description": "Chiffrer la connexion avec STARTTLS."
     },
     "username": {
      "anyOf": [
       {
        "maxLength": 300,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Username",
      "description": "Compte du serveur SMTP."
     }
    },
    "title": "SmtpBody",
    "type": "object",
    "description": "Serveur de courrier sortant des notifications d'alertes."
   },
   "Source": {
    "additionalProperties": false,
    "properties": {
     "chart_path": {
      "anyOf": [
       {
        "maxLength": 253,
        "pattern": "^[A-Za-z0-9][A-Za-z0-9._/-]*$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Chart Path",
      "description": "Chemin d'un chart connu de l'opérateur, par exemple `charts/postgresql`."
     },
     "oci": {
      "anyOf": [
       {
        "maxLength": 512,
        "pattern": "^oci://[A-Za-z0-9.:/_-]+@sha256:[a-f0-9]{64}$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Oci",
      "description": "Référence OCI épinglée par empreinte, `oci://<harbor-host>/charts/postgresql@sha256:<64 hex>`."
     }
    },
    "title": "Source",
    "type": "object",
    "description": "Source d'un chart : exactement un parmi `chart_path` et `oci`."
   },
   "SshBootstrapIn": {
    "additionalProperties": false,
    "description": "Machines à connecter par SSH (méthode SSH d'ajout de nœuds) : le Manager vérifie l'identité de chaque machine, puis lance la même installation que la commande à coller.",
    "properties": {
     "certificate": {
      "anyOf": [
       {
        "maxLength": 20000,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Certificate",
      "description": "Certificat utilisateur OpenSSH de la clé privée (`ssh-ed25519-cert-v01...`), signé par une autorité approuvée dans la politique SSH du cluster. Facultatif."
     },
     "host_ca": {
      "default": "",
      "maxLength": 2000,
      "title": "Host Ca",
      "type": "string",
      "description": "Clé publique de l'autorité de certification des machines (`ssh-ed25519 AAAA...`) : remplace l'identité de chaque machine quand elles ont des certificats d'hôte."
     },
     "host_key_fingerprints": {
      "additionalProperties": {
       "type": "string"
      },
      "title": "Host Key Fingerprints",
      "type": "object",
      "description": "Première forme de la requête : clé SSH attendue de chaque machine nommée dans `hosts`, sous la forme `SHA256:<43 caractères base64>` (sortie de `ssh-keygen -lf`). Une machine sans empreinte correspondante est refusée."
     },
     "hosts": {
      "items": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "$ref": "#/components/schemas/SshMachineIn"
        }
       ]
      },
      "maxItems": 200,
      "minItems": 1,
      "title": "Hosts",
      "type": "array",
      "description": "Machines à connecter, de 1 à 200 : objets `{host, port, host_key}`, ou noms de machines avec `host_key_fingerprints` (première forme)."
     },
     "jumps": {
      "items": {
       "$ref": "#/components/schemas/SshMachineIn"
      },
      "maxItems": 4,
      "title": "Jumps",
      "type": "array",
      "description": "Bastions à traverser pour joindre les machines, 4 au plus, chacun vérifié par sa propre identité."
     },
     "passphrase": {
      "anyOf": [
       {
        "maxLength": 1024,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Passphrase",
      "description": "Phrase de passe de la clé privée quand elle est protégée. Utilisée par le Manager seulement pour déchiffrer la clé en mémoire, jamais envoyée à une machine ni conservée."
     },
     "private_key": {
      "maxLength": 20000,
      "minLength": 20,
      "title": "Private Key",
      "type": "string",
      "description": "Clé privée SSH de `ssh_user` (OpenSSH ou PEM) : Ed25519, ou RSA quand la politique SSH du cluster l'autorise. Gardée en mémoire pour les tâches uniquement, jamais conservée."
     },
     "ssh_port": {
      "default": 22,
      "maximum": 65535.0,
      "minimum": 1.0,
      "title": "Ssh Port",
      "type": "integer",
      "description": "Port SSH des machines sans port propre, de 1 à 65535. 22 par défaut."
     },
     "ssh_user": {
      "maxLength": 32,
      "minLength": 1,
      "title": "Ssh User",
      "type": "string",
      "description": "Compte SSH avec sudo sans mot de passe, un nom d'utilisateur Unix tel que `ec2-user` ou `ubuntu`. `root` est refusé."
     },
     "use_sudo": {
      "default": true,
      "title": "Use Sudo",
      "type": "boolean",
      "description": "Lancer l'installation avec `sudo -n`. Vrai par défaut."
     }
    },
    "required": [
     "hosts",
     "ssh_user",
     "private_key"
    ],
    "title": "SshBootstrapIn",
    "type": "object"
   },
   "SshMachineIn": {
    "additionalProperties": false,
    "description": "Une machine ou un bastion de la méthode SSH.",
    "properties": {
     "host": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Host",
      "type": "string",
      "description": "Nom ou adresse de la machine, par exemple `10.0.1.12` ou `<db1-host>`."
     },
     "host_key": {
      "default": "",
      "maxLength": 2000,
      "title": "Host Key",
      "type": "string",
      "description": "Identité de la machine : empreinte SHA256 de sa clé d'hôte (`SHA256:...`) ou sa ligne de clé publique (`ssh-ed25519 AAAA...`). La connexion s'arrête si la clé diffère."
     },
     "port": {
      "anyOf": [
       {
        "maximum": 65535.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Port",
      "description": "Port SSH de cette machine, de 1 à 65535. `ssh_port` par défaut (22 pour un bastion)."
     },
     "user": {
      "default": "",
      "maxLength": 32,
      "title": "User",
      "type": "string",
      "description": "Compte sur un bastion quand il diffère de `ssh_user`."
     }
    },
    "required": [
     "host"
    ],
    "title": "SshMachineIn",
    "type": "object"
   },
   "SshPolicyIn": {
    "additionalProperties": false,
    "properties": {
     "allow_rsa": {
      "default": false,
      "title": "Allow Rsa",
      "type": "boolean",
      "description": "Accepter les clés RSA (Ed25519 est toujours acceptée ; DSA et ECDSA jamais). Faux par défaut."
     },
     "approved_user_cas": {
      "items": {
       "type": "string"
      },
      "maxItems": 20,
      "title": "Approved User Cas",
      "type": "array",
      "description": "Clés publiques d'autorités d'utilisateurs OpenSSH dont les certificats sont acceptés, 20 au plus."
     },
     "bootstrap_user": {
      "default": "navin-bootstrap",
      "maxLength": 32,
      "minLength": 1,
      "title": "Bootstrap User",
      "type": "string",
      "description": "Compte par défaut de l'amorçage. `navin-bootstrap` par défaut."
     },
     "rotate_before_days": {
      "default": 30,
      "maximum": 365.0,
      "minimum": 1.0,
      "title": "Rotate Before Days",
      "type": "integer",
      "description": "Nombre de jours avant expiration où un certificat d'agent doit être renouvelé, de 1 à 365. 30 par défaut."
     },
     "rsa_min_bits": {
      "default": 3072,
      "maximum": 16384.0,
      "minimum": 3072.0,
      "title": "Rsa Min Bits",
      "type": "integer",
      "description": "Plus petite clé RSA acceptée, de 3072 à 16384 bits. 3072 par défaut."
     },
     "spiffe_trust_domain": {
      "anyOf": [
       {
        "maxLength": 200,
        "pattern": "^[a-z0-9.-]+$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Spiffe Trust Domain",
      "description": "Domaine de confiance SPIFFE des identités d'agents, par exemple `<trust-domain>` ; nul : aucun."
     }
    },
    "title": "SshPolicyIn",
    "type": "object",
    "description": "Politique SSH d'un cluster."
   },
   "SwitchIn": {
    "properties": {
     "to_site": {
      "minLength": 1,
      "title": "To Site",
      "type": "string",
      "description": "Nom du site vers lequel va le trafic."
     }
    },
    "required": [
     "to_site"
    ],
    "title": "SwitchIn",
    "type": "object",
    "description": "Cible d'une bascule de trafic."
   },
   "SyslogBody": {
    "additionalProperties": false,
    "properties": {
     "facility": {
      "anyOf": [
       {
        "enum": [
         "kern",
         "user",
         "daemon",
         "auth",
         "authpriv",
         "local0",
         "local1",
         "local2",
         "local3",
         "local4",
         "local5",
         "local6",
         "local7"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility",
      "description": "Facilité syslog : `kern`, `user`, `daemon`, `auth`, `authpriv` ou `local0` à `local7`."
     },
     "host": {
      "anyOf": [
       {
        "maxLength": 255,
        "pattern": "^[A-Za-z0-9._:-]*$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Host",
      "description": "Nom ou adresse du serveur syslog."
     },
     "port": {
      "anyOf": [
       {
        "maximum": 65535.0,
        "minimum": 1.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Port",
      "description": "Port du serveur syslog, de 1 à 65535."
     },
     "protocol": {
      "anyOf": [
       {
        "enum": [
         "udp",
         "tcp",
         "tls"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Protocol",
      "description": "Transport : `udp`, `tcp` ou `tls`."
     }
    },
    "title": "SyslogBody",
    "type": "object",
    "description": "Serveur syslog distant."
   },
   "TargetsIn": {
    "properties": {
     "rpo_target_seconds": {
      "anyOf": [
       {
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rpo Target Seconds",
      "description": "Objectif de point de reprise (RPO) : perte de données maximale acceptée, en secondes."
     },
     "rto_target_seconds": {
      "anyOf": [
       {
        "minimum": 0.0,
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rto Target Seconds",
      "description": "Objectif de temps de reprise (RTO) : durée maximale acceptée pour rétablir le service, en secondes."
     }
    },
    "title": "TargetsIn",
    "type": "object",
    "description": "Objectifs de reprise d'un plan ; nul efface un objectif."
   },
   "TokenBody": {
    "additionalProperties": false,
    "properties": {
     "expires_in_days": {
      "default": 90,
      "maximum": 365.0,
      "minimum": 1.0,
      "title": "Expires In Days",
      "type": "integer",
      "description": "Durée de vie du jeton en jours, de 1 à 365. 90 par défaut."
     },
     "name": {
      "maxLength": 100,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom qui indique l'usage du jeton, 1 à 100 caractères."
     },
     "permissions": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "maxItems": 32,
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Permissions",
      "description": "Permissions auxquelles le jeton est limité, par exemple `[\"cluster:read\", \"operations:read\"]` ; chacune doit être détenue par vous. Nul (par défaut) : toutes les permissions que vous détenez."
     }
    },
    "required": [
     "name"
    ],
    "title": "TokenBody",
    "type": "object",
    "description": "Un nouveau jeton d'API personnel."
   },
   "TokenIn": {
    "properties": {
     "expires_in_hours": {
      "default": 24,
      "maximum": 720.0,
      "minimum": 1.0,
      "title": "Expires In Hours",
      "type": "integer",
      "description": "Durée de vie du jeton en heures, de 1 à 720. 24 par défaut."
     },
     "max_uses": {
      "default": 50,
      "maximum": 10000.0,
      "minimum": 1.0,
      "title": "Max Uses",
      "type": "integer",
      "description": "Nombre d'enregistrements d'agents autorisés, de 1 à 10000. 50 par défaut."
     }
    },
    "title": "TokenIn",
    "type": "object",
    "description": "Limites d'un jeton d'amorçage."
   },
   "TrafficIn": {
    "properties": {
     "kind": {
      "title": "Kind",
      "type": "string",
      "description": "Type de bascule : `dns` ou `load_balancer`."
     },
     "name": {
      "maxLength": 200,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom de l'enregistrement ou de l'équilibreur, de 1 à 200 caractères, par exemple `<app-host>`."
     },
     "spec": {
      "additionalProperties": true,
      "title": "Spec",
      "type": "object",
      "description": "Spécification : `sites` (valeur par site) et `health_check` avec son `url`, tous deux obligatoires."
     }
    },
    "required": [
     "kind",
     "name",
     "spec"
    ],
    "title": "TrafficIn",
    "type": "object",
    "description": "Un enregistrement DNS ou un équilibreur de charge basculé par un plan de reprise."
   },
   "TrustIn": {
    "properties": {
     "reason": {
      "default": "",
      "maxLength": 500,
      "title": "Reason",
      "type": "string",
      "description": "Raison du changement d'état de confiance, 500 caractères au plus ; expurgée et auditée."
     }
    },
    "title": "TrustIn",
    "type": "object",
    "description": "Raison d'un changement d'état de confiance."
   },
   "UpdateIn": {
    "properties": {
     "health_timeout_s": {
      "default": 120,
      "maximum": 900.0,
      "minimum": 30.0,
      "title": "Health Timeout S",
      "type": "integer",
      "description": "Secondes dont dispose le nouvel agent pour se déclarer en bonne santé après la mise à jour, de 30 à 900. 120 par défaut."
     },
     "ttl_s": {
      "default": 900,
      "maximum": 3600.0,
      "minimum": 60.0,
      "title": "Ttl S",
      "type": "integer",
      "description": "Validité de la commande en secondes, de 60 à 3600. 900 par défaut."
     }
    },
    "title": "UpdateIn",
    "type": "object",
    "description": "Options d'une mise à jour d'agent."
   },
   "UpgradeBody": {
    "properties": {
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Identifiant de la demande approuvée (validation à quatre yeux) qui autorise cette action."
     },
     "auto_rollback": {
      "default": false,
      "title": "Auto Rollback",
      "type": "boolean",
      "description": "true : si la mise à niveau échoue, le Manager rejoue aussitôt la version précédente, scellée avant la mise à niveau, sans demander de nouvelle approbation (celle de la mise à niveau la couvre). Le verrou de sécurité, les modes et le profil du cluster décident toujours. Quand le retour arrière est impossible (migration irréversible, retour arrière non pris en charge par le catalogue, opération refusée), la mise à niveau reste en échec, la raison est auditée et le retour passe par la restauration de la sauvegarde prise avant la mise à niveau. Par défaut false : une mise à niveau en échec attend l'opérateur."
     },
     "canary_host": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Canary Host",
      "description": "Machine mise à niveau en premier (la canari) ; nul : la première machine du service."
     }
    },
    "title": "UpgradeBody",
    "type": "object",
    "description": "Options d'une mise à niveau."
   },
   "UserCreate": {
    "additionalProperties": false,
    "properties": {
     "display_name": {
      "default": "",
      "maxLength": 200,
      "title": "Display Name",
      "type": "string",
      "description": "Nom affiché dans la console ; le nom d'utilisateur s'il est vide."
     },
     "email": {
      "default": "",
      "maxLength": 200,
      "pattern": "^$|^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
      "title": "Email",
      "type": "string",
      "description": "Adresse e-mail de l'utilisateur ; peut être vide."
     },
     "language": {
      "default": "en",
      "enum": [
       "en",
       "fr"
      ],
      "title": "Language",
      "type": "string",
      "description": "Langue de la console : `en` (par défaut) ou `fr`."
     },
     "must_change_password": {
      "default": true,
      "title": "Must Change Password",
      "type": "boolean",
      "description": "Si vrai (par défaut), l'utilisateur doit choisir un nouveau mot de passe à la première connexion."
     },
     "password": {
      "maxLength": 1024,
      "minLength": 1,
      "title": "Password",
      "type": "string",
      "description": "Mot de passe initial ; doit respecter la politique des mots de passe. En écriture seule."
     },
     "roles": {
      "items": {
       "$ref": "#/components/schemas/RoleItem"
      },
      "maxItems": 50,
      "title": "Roles",
      "type": "array",
      "description": "Attributions de rôle initiales, 50 au plus."
     },
     "username": {
      "maxLength": 100,
      "minLength": 1,
      "pattern": "^[A-Za-z0-9._@-]+$",
      "title": "Username",
      "type": "string",
      "description": "Nom d'utilisateur unique, insensible à la casse : lettres, chiffres, `.`, `_`, `@` et `-`, 100 caractères au plus."
     }
    },
    "required": [
     "username",
     "password"
    ],
    "title": "UserCreate",
    "type": "object",
    "description": "Un nouvel utilisateur local."
   },
   "UserPatch": {
    "additionalProperties": false,
    "properties": {
     "disabled": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Disabled",
      "description": "Vrai désactive le compte (ses sessions sont révoquées), faux le réactive."
     },
     "display_name": {
      "anyOf": [
       {
        "maxLength": 200,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Display Name",
      "description": "Nom affiché dans la console, 200 caractères au plus."
     },
     "email": {
      "anyOf": [
       {
        "maxLength": 200,
        "pattern": "^$|^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Email",
      "description": "Adresse e-mail, 200 caractères au plus."
     },
     "language": {
      "anyOf": [
       {
        "enum": [
         "en",
         "fr"
        ],
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Language",
      "description": "Langue de la console : `en` ou `fr`."
     },
     "must_change_password": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Must Change Password",
      "description": "Si vrai, l'utilisateur doit choisir un nouveau mot de passe à la prochaine connexion."
     }
    },
    "title": "UserPatch",
    "type": "object",
    "description": "Modification d'un utilisateur ; un champ omis ou nul est conservé."
   },
   "ValidateBody": {
    "properties": {
     "blueprint": {
      "additionalProperties": true,
      "title": "Blueprint",
      "type": "object",
      "description": "Le document du blueprint, schéma `navin-manager/blueprint/v1`."
     },
     "host_mapping": {
      "anyOf": [
       {
        "additionalProperties": {
         "items": {
          "type": "string"
         },
         "type": "array"
        },
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Host Mapping",
      "description": "Machines du cluster cible pour chaque groupe de machines, sous la forme `{nom du groupe: [identifiants ou FQDN]}`. Nul : seule la forme est contrôlée."
     }
    },
    "required": [
     "blueprint"
    ],
    "title": "ValidateBody",
    "type": "object",
    "description": "Un blueprint à valider."
   },
   "ValidationError": {
    "properties": {
     "ctx": {
      "title": "Context",
      "type": "object",
      "description": "Contexte de la règle en échec, par exemple la borne dépassée."
     },
     "input": {
      "title": "Input",
      "description": "Valeur refusée."
     },
     "loc": {
      "items": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "integer"
        }
       ]
      },
      "title": "Location",
      "type": "array",
      "description": "Emplacement de l'élément : `body`, `query` ou `path`, puis les noms des champs."
     },
     "msg": {
      "title": "Message",
      "type": "string",
      "description": "Message lisible."
     },
     "type": {
      "title": "Error Type",
      "type": "string",
      "description": "Code de la règle en échec, par exemple `missing` ou `string_too_long`."
     }
    },
    "required": [
     "loc",
     "msg",
     "type"
    ],
    "title": "ValidationError",
    "type": "object",
    "description": "Un élément invalide d'une requête, tel que déclaré par FastAPI."
   },
   "VerifyBody": {
    "properties": {
     "document": {
      "additionalProperties": true,
      "title": "Document",
      "type": "object",
      "description": "Le document du rapport tel que renvoyé par l'API, signature comprise."
     }
    },
    "required": [
     "document"
    ],
    "title": "VerifyBody",
    "type": "object",
    "description": "Un rapport de recette signé à vérifier."
   },
   "navin_manager__agentapi__node__EnrollIn": {
    "properties": {
     "agent_version": {
      "default": "",
      "maxLength": 40,
      "title": "Agent Version",
      "type": "string"
     },
     "csr_pem": {
      "maxLength": 20000,
      "minLength": 100,
      "title": "Csr Pem",
      "type": "string"
     },
     "enrollment_token": {
      "maxLength": 200,
      "minLength": 20,
      "title": "Enrollment Token",
      "type": "string"
     },
     "facts": {
      "additionalProperties": true,
      "title": "Facts",
      "type": "object"
     },
     "fqdn": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Fqdn",
      "type": "string",
      "description": "Nom de domaine complet de la machine, par exemple `<db1-host>`."
     },
     "hostname": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Hostname",
      "type": "string"
     },
     "ip": {
      "default": "",
      "maxLength": 64,
      "title": "Ip",
      "type": "string"
     },
     "service_driver": {
      "default": "systemd",
      "pattern": "^(systemd|process)$",
      "title": "Service Driver",
      "type": "string"
     }
    },
    "required": [
     "enrollment_token",
     "hostname",
     "fqdn",
     "csr_pem"
    ],
    "title": "EnrollIn",
    "type": "object"
   },
   "navin_manager__api__alerts__TargetIn": {
    "properties": {
     "config": {
      "additionalProperties": true,
      "title": "Config",
      "type": "object",
      "description": "Réglages du type : `{\"to\": \"<email-address>\"}` pour `email`, `{\"url\": \"https://<hooks-host>/navin\"}` pour `webhook`."
     },
     "enabled": {
      "default": true,
      "title": "Enabled",
      "type": "boolean",
      "description": "Indique si la cible reçoit les notifications. Vrai par défaut."
     },
     "hmac_secret": {
      "title": "Hmac Secret",
      "description": "Secret qui signe les corps des webhooks (`X-NM-Signature`). En écriture seule, scellé au repos."
     },
     "kind": {
      "enum": [
       "email",
       "webhook",
       "syslog"
      ],
      "title": "Kind",
      "type": "string",
      "description": "Type de cible : `email`, `webhook` ou `syslog`."
     },
     "min_severity": {
      "default": "warning",
      "enum": [
       "warning",
       "critical"
      ],
      "title": "Min Severity",
      "type": "string",
      "description": "Sévérité minimale notifiée : `warning` (par défaut) ou `critical`. Les résolutions sont aussi notifiées."
     },
     "name": {
      "maxLength": 120,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom de la cible, de 1 à 120 caractères."
     },
     "params": {
      "anyOf": [
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Params"
     }
    },
    "required": [
     "name",
     "kind"
    ],
    "title": "TargetIn",
    "type": "object",
    "description": "Une nouvelle cible de notification."
   },
   "navin_manager__api__alerts__TargetPatch": {
    "properties": {
     "config": {
      "anyOf": [
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Config",
      "description": "Nouveaux réglages du type."
     },
     "enabled": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Enabled",
      "description": "Indique si la cible reçoit les notifications."
     },
     "hmac_secret": {
      "title": "Hmac Secret",
      "description": "Nouveau secret HMAC : une chaîne le définit, une chaîne vide le retire, nul ou l'objet relu le conserve."
     },
     "min_severity": {
      "enum": [
       "warning",
       "critical",
       null
      ],
      "title": "Min Severity",
      "description": "Sévérité minimale notifiée : `warning` ou `critical`."
     },
     "name": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name",
      "description": "Nouveau nom de la cible."
     },
     "params": {
      "anyOf": [
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Params"
     }
    },
    "title": "TargetPatch",
    "type": "object",
    "description": "Modification d'une cible de notification ; un champ omis ou nul est conservé."
   },
   "navin_manager__api__backup_recovery__TargetIn": {
    "properties": {
     "bucket": {
      "minLength": 1,
      "title": "Bucket",
      "type": "string",
      "description": "Compartiment ou conteneur ; pour `filesystem`, le répertoire racine."
     },
     "capabilities": {
      "additionalProperties": true,
      "default": {},
      "title": "Capabilities",
      "type": "object",
      "description": "Capacités de la cible, par exemple `object_lock` et `retention` vérifiées pour les sauvegardes immuables."
     },
     "endpoint": {
      "default": "",
      "title": "Endpoint",
      "type": "string",
      "description": "Adresse du service de stockage, par exemple `https://<s3-host>` ; vide pour `filesystem`."
     },
     "name": {
      "maxLength": 100,
      "minLength": 1,
      "title": "Name",
      "type": "string",
      "description": "Nom de la cible, de 1 à 100 caractères."
     },
     "provider": {
      "title": "Provider",
      "type": "string",
      "description": "Fournisseur : `s3`, `azure_blob`, `gcs`, `ibm_cos`, `seaweedfs` ou `filesystem`."
     },
     "purpose": {
      "default": "backup",
      "title": "Purpose",
      "type": "string",
      "description": "Usage de la cible. `backup` par défaut."
     },
     "secret_ref": {
      "default": "",
      "title": "Secret Ref",
      "type": "string",
      "description": "Référence des identifiants du stockage, jamais le secret lui-même."
     }
    },
    "required": [
     "name",
     "provider",
     "bucket"
    ],
    "title": "TargetIn",
    "type": "object",
    "description": "Une cible de sauvegarde."
   },
   "navin_manager__api__blueprint__ApplyBody": {
    "properties": {
     "blueprint": {
      "additionalProperties": true,
      "title": "Blueprint",
      "type": "object",
      "description": "Le document du blueprint, schéma `navin-manager/blueprint/v1`."
     },
     "ha_acknowledged": {
      "default": false,
      "title": "Ha Acknowledged",
      "type": "boolean",
      "description": "Accepter une topologie en dessous des exigences de haute disponibilité. Faux par défaut."
     },
     "host_mapping": {
      "additionalProperties": {
       "items": {
        "type": "string"
       },
       "type": "array"
      },
      "title": "Host Mapping",
      "type": "object",
      "description": "Machines du cluster cible pour chaque groupe de machines, sous la forme `{nom du groupe: [identifiants ou FQDN]}` ; chaque groupe doit être associé."
     },
     "service_check": {
      "default": true,
      "title": "Service Check",
      "type": "boolean",
      "description": "Exécuter les contrôles de service à la fin. Vrai par défaut."
     },
     "start": {
      "default": true,
      "title": "Start",
      "type": "boolean",
      "description": "Démarrer les services une fois installés. Vrai par défaut."
     }
    },
    "required": [
     "blueprint"
    ],
    "title": "ApplyBody",
    "type": "object",
    "description": "Un blueprint à appliquer."
   },
   "navin_manager__api__configs__ApplyBody": {
    "properties": {
     "approval_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Approval Id",
      "description": "Approbation accordée pour cette application sous `banking-secure`."
     },
     "batch_size": {
      "default": 1,
      "maximum": 100.0,
      "minimum": 1.0,
      "title": "Batch Size",
      "type": "integer",
      "description": "Machines redémarrées en même temps par un redémarrage progressif, de 1 à 100. 1 par défaut."
     },
     "restart": {
      "default": "rolling",
      "enum": [
       "rolling",
       "all",
       "none"
      ],
      "title": "Restart",
      "type": "string",
      "description": "Politique de redémarrage : `rolling` (par défaut, `batch_size` machines à la fois), `all` (toutes les instances en même temps) ou `none` (fichiers uniquement)."
     }
    },
    "title": "ApplyBody",
    "type": "object",
    "description": "Façon d'appliquer la configuration."
   },
   "navin_manager__api__connections__ConnectionPatch": {
    "additionalProperties": false,
    "properties": {
     "description": {
      "anyOf": [
       {
        "maxLength": 1000,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Description",
      "description": "Nouvelle description."
     },
     "name": {
      "anyOf": [
       {
        "maxLength": 100,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name",
      "description": "Nouveau nom unique."
     },
     "params": {
      "anyOf": [
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Params",
      "description": "Nouveaux paramètres du type, contrôlés à nouveau."
     },
     "secret": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Secret",
      "description": "Nouveau secret : une chaîne le définit, l'objet relu ou nul le conserve."
     }
    },
    "title": "ConnectionPatch",
    "type": "object",
    "description": "Modification d'une connexion ; un champ omis ou nul est conservé."
   },
   "navin_manager__api__k8s_agents__CommandIn": {
    "additionalProperties": false,
    "properties": {
     "idempotency_key": {
      "maxLength": 128,
      "minLength": 8,
      "title": "Idempotency Key",
      "type": "string",
      "description": "Clé qui fait renvoyer la même commande lors d'une nouvelle tentative, de 8 à 128 caractères ; générée si omise."
     },
     "operation": {
      "enum": [
       "managedcomponent.apply",
       "managedcomponent.delete",
       "inventory.report"
      ],
      "title": "Operation",
      "type": "string",
      "description": "`managedcomponent.apply`, `managedcomponent.delete` ou `inventory.report`."
     },
     "parameters": {
      "additionalProperties": true,
      "title": "Parameters",
      "type": "object",
      "description": "Paramètres de l'opération."
     },
     "trace_id": {
      "anyOf": [
       {
        "maxLength": 64,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Trace Id",
      "description": "Identifiant de trace reporté dans les reçus, 64 caractères au plus."
     },
     "ttl_s": {
      "default": 600,
      "maximum": 3600.0,
      "minimum": 30.0,
      "title": "Ttl S",
      "type": "integer",
      "description": "Validité de la commande en secondes, de 30 à 3600. 600 par défaut."
     }
    },
    "required": [
     "operation"
    ],
    "title": "CommandIn",
    "type": "object",
    "description": "Une commande signée pour l'agent de cluster."
   },
   "navin_manager__api__kubernetes__TargetIn": {
    "additionalProperties": false,
    "properties": {
     "api_server": {
      "maxLength": 512,
      "minLength": 8,
      "title": "Api Server",
      "type": "string",
      "description": "Adresse HTTPS du serveur d'API, par exemple `<api-server-url>` ; le kubeconfig doit pointer vers elle."
     },
     "kubeconfig": {
      "maxLength": 262144,
      "minLength": 1,
      "title": "Kubeconfig",
      "type": "string",
      "description": "Kubeconfig de l'identité d'import. En écriture seule, scellé au repos."
     },
     "namespace": {
      "default": "navin-platform",
      "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
      "title": "Namespace",
      "type": "string",
      "description": "Espace de noms géré. `navin-platform` par défaut."
     }
    },
    "required": [
     "api_server",
     "kubeconfig"
    ],
    "title": "TargetIn",
    "type": "object",
    "description": "Un cluster Kubernetes à enregistrer."
   },
   "navin_manager__api__kubernetes__TargetPatch": {
    "additionalProperties": false,
    "properties": {
     "api_server": {
      "anyOf": [
       {
        "maxLength": 512,
        "minLength": 8,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Api Server",
      "description": "Nouvelle adresse du serveur d'API."
     },
     "kubeconfig": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Kubeconfig",
      "description": "Nouveau kubeconfig ; nul ou l'objet relu conserve celui enregistré."
     },
     "namespace": {
      "anyOf": [
       {
        "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Namespace",
      "description": "Nouvel espace de noms géré ; refusé tant que des composants restent."
     }
    },
    "title": "TargetPatch",
    "type": "object",
    "description": "Modification d'une cible Kubernetes ; un champ omis est conservé."
   },
   "navin_manager__api__node_security__CommandIn": {
    "additionalProperties": false,
    "properties": {
     "idempotency_key": {
      "anyOf": [
       {
        "maxLength": 100,
        "minLength": 8,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Idempotency Key",
      "description": "Clé qui fait renvoyer la même commande lors d'une nouvelle tentative, de 8 à 100 caractères."
     },
     "operation": {
      "maxLength": 60,
      "minLength": 1,
      "title": "Operation",
      "type": "string",
      "description": "Opération : `facts.collect`, `health.check`, `logs.tail`, `files.digest`, `service.control`, `command.run` ou `agent.update`."
     },
     "parameters": {
      "additionalProperties": true,
      "title": "Parameters",
      "type": "object",
      "description": "Paramètres de l'opération."
     },
     "trace_id": {
      "anyOf": [
       {
        "maxLength": 100,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Trace Id",
      "description": "Identifiant de trace reporté dans le reçu, 100 caractères au plus."
     },
     "ttl_s": {
      "default": 300,
      "maximum": 3600.0,
      "minimum": 30.0,
      "title": "Ttl S",
      "type": "integer",
      "description": "Validité de la commande en secondes, de 30 à 3600. 300 par défaut."
     }
    },
    "required": [
     "operation"
    ],
    "title": "CommandIn",
    "type": "object",
    "description": "Une commande signée pour un agent de nœud."
   },
   "navin_manager__api__node_security__EnrollIn": {
    "additionalProperties": false,
    "properties": {
     "cluster_id": {
      "title": "Cluster Id",
      "type": "string"
     },
     "fqdn": {
      "maxLength": 253,
      "minLength": 1,
      "title": "Fqdn",
      "type": "string",
      "description": "Nom de domaine complet de la machine, par exemple `<db1-host>`."
     },
     "host_fingerprint": {
      "maxLength": 100,
      "minLength": 64,
      "title": "Host Fingerprint",
      "type": "string"
     },
     "ttl_s": {
      "default": 600,
      "maximum": 900.0,
      "minimum": 60.0,
      "title": "Ttl S",
      "type": "integer"
     }
    },
    "required": [
     "cluster_id",
     "fqdn",
     "host_fingerprint"
    ],
    "title": "EnrollIn",
    "type": "object"
   },
   "navin_manager__api__services__ConnectionPatch": {
    "additionalProperties": false,
    "description": "Modification d'un service connecté : une valeur omise conserve celle enregistrée, de même qu'un champ secret laissé vide.",
    "properties": {
     "auth": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/ConnectAuth"
       },
       {
        "type": "null"
       }
      ],
      "description": "Nouvelle méthode ou nouveaux champs d'authentification (rotation des identifiants)."
     },
     "ca_pem": {
      "anyOf": [
       {
        "maxLength": 65536,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Ca Pem",
      "description": "Nouvelle autorité de certification, en PEM."
     },
     "config": {
      "anyOf": [
       {
        "additionalProperties": true,
        "maxProperties": 100,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Config",
      "description": "Nouvelles valeurs des clés de configuration."
     },
     "credentials_ref": {
      "anyOf": [
       {
        "maxLength": 512,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Credentials Ref",
      "description": "Nouvelle référence à des identifiants externes (URI)."
     },
     "dry_run": {
      "default": false,
      "title": "Dry Run",
      "type": "boolean",
      "description": "Contrôler le changement sans l'enregistrer. Faux par défaut."
     },
     "endpoint": {
      "anyOf": [
       {
        "maxLength": 2048,
        "minLength": 1,
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Endpoint",
      "description": "Nouvelle adresse du service."
     },
     "mtls": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/ClientCertificate"
       },
       {
        "type": "null"
       }
      ],
      "description": "Nouveau certificat client ; un objet vide le retire."
     },
     "tls_verify": {
      "anyOf": [
       {
        "type": "boolean"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tls Verify",
      "description": "Vérifier le certificat TLS du service."
     }
    },
    "title": "ConnectionPatch",
    "type": "object"
   }
  }
 },
 "info": {
  "title": "API du Navin Manager",
  "version": "1.0.2",
  "description": "## Présentation\n\nLe Navin Manager est la console d'administration de la plateforme Navin. Son API REST est celle qu'utilise la\nconsole web : tout ce que vous faites dans la console, vous pouvez le faire avec cette API. Elle gère :\n\n- les **clusters** : un environnement de machines (ou un cluster Kubernetes) avec son mode (production, test,\n  sandbox...), son profil d'installation (standard, banking-secure, air-gapped...), ses réglages et ses services ;\n- les **machines** : enregistrées par leur agent Navin, avec leurs caractéristiques, leurs métriques, leurs\n  contrôles préalables et leurs paramètres ;\n- les **services** : services du catalogue (PostgreSQL, Keycloak, Kafka...) installés sur les machines, ou\n  services existants connectés au Manager, avec leurs composants, leurs versions de configuration, leur\n  configuration cliente et leurs tableaux de bord ;\n- les **opérations** : chaque changement (installation, redémarrage, mise à niveau, sauvegarde...) s'exécute sous\n  forme d'opération composée d'étapes et de tâches exécutées par les agents des machines ;\n- la **sécurité, la sauvegarde et le cycle de vie** : utilisateurs et rôles, fournisseurs d'identité, PKI, coffre,\n  sécurité des nœuds, sauvegardes, restaurations, reprise après sinistre, mises à niveau et approbations.\n\n## URL de base et versions\n\nTous les points d'accès sont servis sous `/api/v1` sur l'écoute console du Manager, par exemple\n`<manager-url>/api/v1/clusters`. La version fait partie du chemin : un changement qui casse un\nclient reçoit un nouveau préfixe de version ; des champs peuvent être ajoutés à une réponse de `/api/v1` à tout\nmoment, les clients doivent donc ignorer les champs qu'ils ne connaissent pas. Le document OpenAPI est servi sur\n`/api/v1/openapi.json`. Les corps sont en JSON (UTF-8) ; les dates sont en ISO 8601 avec fuseau horaire ; les\nidentifiants sont des UUID transmis sous forme de chaînes.\n\n## Authentification\n\nUne requête s'authentifie de l'une des deux façons suivantes :\n\n1. **Cookie de session** (la console web). `POST /api/v1/auth/login` avec un nom d'utilisateur et un mot de passe\n   (compte local ou LDAP) ouvre une session côté serveur conservée dans le cookie `nm_session` (HttpOnly, délais\n   d'inactivité et absolu) et renvoie un `csrf_token`. Toute requête non sûre (`POST`, `PUT`, `PATCH`, `DELETE`)\n   faite avec le cookie doit envoyer cette valeur dans l'en-tête `X-CSRF-Token`, sinon elle est refusée avec\n   `403 csrf_invalid`. La connexion OIDC commence par `GET /api/v1/auth/oidc/{provider_id}/start`.\n2. **Jeton d'API** (scripts, CI, intégrations). Créez un jeton personnel depuis votre profil dans la console, ou\n   avec `POST /api/v1/me/tokens` depuis une session de navigateur. Un jeton a la forme `nmt_<12 hex>_<secret>`,\n   n'est affiché qu'une fois, expire (90 jours par défaut, 365 au plus) et peut être limité à une partie de vos\n   permissions. Envoyez-le dans l'en-tête `Authorization` ; aucun en-tête CSRF n'est nécessaire :\n\n```bash\ncurl -H \"Authorization: Bearer <token>\" <manager-url>/api/v1/clusters\n```\n\n**Second facteur (MFA).** Quand le Manager fonctionne avec le TOTP activé, une connexion dont le compte a la MFA\n(ou dont le rôle l'exige) répond `{\"mfa_required\": true, \"mfa_token\": ...}` au lieu d'ouvrir une session ;\nterminez-la avec `POST /api/v1/auth/mfa/verify` (ou enrôlez-vous d'abord avec `/auth/mfa/enroll/start` et\n`/auth/mfa/enroll/confirm`). Les identifiants, la MFA et les jetons se gèrent uniquement depuis une session de\nnavigateur : un jeton d'API ne peut pas créer un autre jeton.\n\nDes identifiants absents ou invalides donnent `401` (`unauthenticated`, `session_expired`, `token_invalid`) ; des\néchecs répétés donnent `429 too_many_attempts` avec un en-tête `Retry-After`, puis `423 account_locked`.\n\n## Permissions\n\nL'accès repose sur des rôles. Un utilisateur détient des attributions de rôle, chacune globale ou limitée à un\ncluster : `admin`, `security_admin`, `cluster_admin`, `operator`, `viewer`, `auditor`. Un rôle accorde des\npermissions comme `cluster:read`, `services:install`, `services:operate`, `configs:edit`, `operations:abort`,\n`alerts:manage`, `audit:read`, `users:manage`, `backup:manage`, `restore:execute`, `vault:manage` ou\n`mcp:manage`. Certaines permissions sont uniquement globales (`users:manage`, `audit:read`, `settings:manage`,\n`pki:manage`...) : une attribution limitée à un cluster ne les accorde jamais. Chaque opération de cette référence\nindique la permission qu'elle vérifie ; pour un chemin de cluster, la permission peut être détenue globalement ou\nsur ce cluster. `GET /api/v1/auth/session` renvoie les permissions effectives de l'appelant. Un refus répond\n`403 permission_denied` avec la permission manquante dans `details` et est inscrit au journal d'audit.\n\n## Erreurs\n\nToutes les erreurs ont la même enveloppe, avec le message dans la langue de l'appelant (cookie `nm_lang`, puis\n`Accept-Language`) et dans les deux langues :\n\n```json\n{\n  \"error\": {\n    \"code\": \"permission_denied\",\n    \"message\": \"Vous n'avez pas la permission services:install.\",\n    \"message_i18n\": {\n      \"en\": \"You do not have the permission services:install.\",\n      \"fr\": \"Vous n'avez pas la permission services:install.\"\n    },\n    \"details\": {\"permission\": \"services:install\", \"cluster_id\": \"6f1c2b9e-0000-4000-8000-000000000001\"}\n  }\n}\n```\n\nTestez `code`, jamais le message. Codes courants :\n\n| HTTP | Code | Signification |\n|------|------|---------------|\n| 401 | `unauthenticated`, `session_expired`, `token_invalid` | Aucune session ni aucun jeton valide. |\n| 403 | `permission_denied`, `csrf_invalid`, `session_required` | Permission manquante, en-tête CSRF absent, ou l'appel exige une session de navigateur. |\n| 404 | `not_found`, `cluster_not_found`, `operation_not_found`... | La ressource n'existe pas ou n'est pas visible. |\n| 409 | `profile_blocked`, `approval_required`, `service_in_use`... | L'état courant ou le profil du cluster refuse l'action. |\n| 422 | `validation_error` | Le corps ou un paramètre est invalide ; `details.fields` associe à chaque champ son `code` et son `message`. |\n| 429 | `too_many_attempts` | Limite de débit ; attendez les secondes de `Retry-After`. |\n| 500 | `internal_error` | Erreur inattendue ; `details.incident` l'identifie dans les journaux du serveur. |\n\nChaque réponse porte un en-tête `X-Request-Id` (celui que vous avez envoyé, ou un nouveau) à citer quand vous\nsignalez un problème.\n\n## Pagination\n\nLes listes répondent `{\"items\": [...], \"next_cursor\": ...}`. Une liste paginée accepte `limit` (borné par le\nserveur) et `cursor` : passez le `next_cursor` d'une page dans `cursor` pour lire la suivante ; `next_cursor` vaut\n`null` sur la dernière page. Beaucoup de listes d'un cluster sont courtes et renvoient toujours tout, avec\n`next_cursor: null`.\n\n## Opérations asynchrones\n\nUn changement n'est pas effectué pendant la requête : l'API le valide, le planifie et répond aussitôt avec un\n`operation_id`. Par exemple, pour installer des services :\n\n1. `POST /api/v1/clusters/{cluster_id}/services/validate` contrôle la demande (erreurs, haute disponibilité,\n   dépendances manquantes) et montre le plan, sans rien modifier ;\n2. `POST /api/v1/clusters/{cluster_id}/services` crée l'opération et renvoie `{\"operation_id\": ...}` ;\n3. `GET /api/v1/operations/{operation_id}` renvoie son `status` (`pending`, `running`, `succeeded`, `failed`,\n   `aborted`), sa progression (`progress`) et ses étapes, chacune avec une tâche par machine ;\n4. `GET /api/v1/operations/{operation_id}/tasks/{task_id}/logs` lit la sortie d'une tâche au fil de l'eau\n   (`after_seq`) ;\n5. `POST /api/v1/operations/{operation_id}/abort` l'arrête, `POST /api/v1/operations/{operation_id}/retry` relance\n   une opération échouée ou interrompue.\n\nSous le profil `banking-secure`, une opération perturbatrice (désinstallation, restauration...) exige aussi\nl'approbation d'une seconde personne : demandez-la, faites-la approuver, puis transmettez son `approval_id` (ou\nlaissez le Manager retrouver l'approbation qui vous a été accordée) ; sans elle, l'API répond\n`409 approval_required`.\n\n## Audit\n\nChaque changement, chaque connexion et chaque refus est inscrit dans un journal d'audit infalsifiable (chaque\névénement est chaîné au précédent par une empreinte) avec l'acteur, la cible, le cluster, le résultat et l'adresse\ndu client. Les lecteurs disposant de `audit:read` le consultent avec `GET /api/v1/audit`, vérifient la chaîne avec\n`GET /api/v1/audit/verify` et l'exportent en NDJSON ou en CSV. Les secrets ne sont jamais écrits dans le journal\nd'audit.\n\n## Points d'accès des agents\n\nLes agents Navin dialoguent avec le Manager sur une écoute distincte protégée par TLS mutuel (battements de cœur,\nrécupération des tâches, résultats, paquets). Ce canal ne fait pas partie de cette API. Seul l'enregistrement d'un\nnouvel agent, `POST /agent/v1/register`, est documenté ici, sous **Agent**."
 },
 "openapi": "3.1.0",
 "paths": {
  "/agent/v1/enroll": {
   "post": {
    "description": "Enrollment (HOST-011): the token is consumed by the first CSR of the bound key, then the host registers like\nwith a bootstrap token (one use, one minute) and receives its mTLS identity.",
    "operationId": "enroll_agent_v1_enroll_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__agentapi__node__EnrollIn"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Enroll Agent V1 Enroll Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enroll",
    "tags": [
     "agent"
    ]
   }
  },
  "/agent/v1/register": {
   "post": {
    "operationId": "register_agent_v1_register_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RegisterIn"
       }
      }
     },
     "required": true,
     "description": "Jeton d'amorçage, noms, caractéristiques et CSR de l'agent."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Register Agent V1 Register Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enregistrer un agent avec un jeton d'amorçage",
    "tags": [
     "agent"
    ],
    "description": "Enregistre l'agent d'une machine dans le cluster du jeton d'amorçage et signe son certificat client.\n\nUne machine déjà connue avec le même FQDN dans le même cluster est enregistrée à nouveau : elle reçoit un\nnouveau secret et un nouveau certificat, ses certificats précédents sont révoqués. Le compteur d'utilisations\ndu jeton est incrémenté.\n\nLa réponse donne `host_id`, `cluster_id`, `agent_secret`, `certificate_pem`, `ca_pem` et son empreinte SHA-256\n`ca_sha256`, les clés publiques qui signent les commandes et les paquets (`command_key_pem`,\n`package_key_pem`), le `spiffe_id`, les URL de l'écoute des agents et du miroir, et les intervalles\nd'interrogation (`heartbeat_interval_s`, `poll_wait_s`).\n\nErreurs : `401 bootstrap_token_invalid` (inconnu, révoqué, expiré ou épuisé), `409 host_in_other_cluster` (le\nFQDN appartient à une machine d'un autre cluster qui n'est pas mise hors service), `422 csr_invalid`,\n`422 validation_error` (FQDN qui n'est pas un nom DNS valide). Audité en `host.register` ou `host.reregister`."
   }
  },
  "/api/v1/acceptance/public-key": {
   "get": {
    "operationId": "acceptance_key_api_v1_acceptance_public_key_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Acceptance Key Api V1 Acceptance Public Key Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la clé publique qui signe les rapports de recette",
    "tags": [
     "lifecycle"
    ],
    "description": "Renvoie `algorithm` (`Ed25519`), `key_id` et `public_key_pem`, pour vérifier un rapport hors ligne. Tout utilisateur connecté."
   }
  },
  "/api/v1/acceptance/verify": {
   "post": {
    "operationId": "verify_report_api_v1_acceptance_verify_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/VerifyBody"
       }
      }
     },
     "required": true,
     "description": "Le document de rapport signé."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Verify Report Api V1 Acceptance Verify Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Vérifier la signature d'un rapport de recette",
    "tags": [
     "lifecycle"
    ],
    "description": "Contrôle la signature Ed25519 d'un document de rapport avec la clé de ce Manager et renvoie `valid`, `trusted`, `key_id` et la raison `reason` d'un refus. Tout utilisateur connecté."
   }
  },
  "/api/v1/agent-templates": {
   "get": {
    "description": "Chaque gabarit d'agent du registre, trié par identifiant, avec `published` calculé d'après le catalogue chargé et les publications de ce Manager, `reason` (fr et en) quand il ne peut pas être déployé, `publication` (version, auteur, date) quand il a été publié ici, `publishable` et `publish_blocker`. Renvoie `items` et `count`. Tout utilisateur connecté.",
    "operationId": "agent_templates_api_v1_agent_templates_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Agent Templates Api V1 Agent Templates Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les gabarits d'agents",
    "tags": [
     "agent-templates"
    ]
   }
  },
  "/api/v1/agent-templates/{template_id}": {
   "get": {
    "description": "Un gabarit d'agent par son identifiant : nom et description (fr et en), éditeur, version, statut, service du catalogue, chemins de la documentation et des sources, composants, `published`, `reason`, `publication`, `publishable` et `publish_blocker`. Tout utilisateur connecté ; `404` pour un gabarit inconnu.",
    "operationId": "agent_template_api_v1_agent_templates__template_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "template_id",
      "required": true,
      "schema": {
       "title": "Template Id",
       "type": "string"
      },
      "description": "Identifiant du gabarit d'agent, égal au nom de son fichier descripteur."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Agent Template Api V1 Agent Templates  Template Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un gabarit d'agent",
    "tags": [
     "agent-templates"
    ]
   }
  },
  "/api/v1/agent-templates/{template_id}/publication": {
   "delete": {
    "description": "Supprime la définition de service et les gabarits publiés sur ce Manager ainsi que l'enregistrement de publication : le service quitte le catalogue et le gabarit attend de nouveau sa validation. Refusé tant qu'une instance du service est installée dans un cluster (`409 agent_template_in_use`, les instances sont dans `details.instances`). Renvoie le gabarit. Permission `agent_templates:publish` ; audité en `agent_template.unpublish`. Erreurs : `404 agent_template_unknown`, `404 agent_template_not_published`.",
    "operationId": "withdraw_agent_template_api_v1_agent_templates__template_id__publication_delete",
    "parameters": [
     {
      "in": "path",
      "name": "template_id",
      "required": true,
      "schema": {
       "title": "Template Id",
       "type": "string"
      },
      "description": "Identifiant du gabarit d'agent."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Withdraw Agent Template Api V1 Agent Templates  Template Id  Publication Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Retirer la publication d'un gabarit d'agent",
    "tags": [
     "agent-templates"
    ]
   }
  },
  "/api/v1/agent-templates/{template_id}/publish": {
   "post": {
    "description": "La validation du client : la définition de service et les gabarits de l'agent sont vérifiés par le chargeur du catalogue avec tout le catalogue (sur une copie de travail, rien n'est écrit si une vérification échoue), puis copiés sous `published_templates_dir` ; le service rejoint le catalogue de ce Manager, le gabarit répond `published: true` et son bouton Déployer ouvre l'assistant d'installation. Publier de nouveau remplace les fichiers par les sources actuelles. Renvoie le gabarit. Permission `agent_templates:publish` ; audité en `agent_template.publish`. Erreurs : `404 agent_template_unknown`, `409 agent_template_not_publishable` (prévu ou en développement), `409 agent_template_sources_missing`, `409 agent_template_already_published` (service livré avec le catalogue), `422 agent_template_invalid` (les problèmes trouvés sont dans `details.problems`).",
    "operationId": "publish_agent_template_api_v1_agent_templates__template_id__publish_post",
    "parameters": [
     {
      "in": "path",
      "name": "template_id",
      "required": true,
      "schema": {
       "title": "Template Id",
       "type": "string"
      },
      "description": "Identifiant du gabarit d'agent."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Publish Agent Template Api V1 Agent Templates  Template Id  Publish Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Valider et publier un gabarit d'agent",
    "tags": [
     "agent-templates"
    ]
   }
  },
  "/api/v1/agents/nodes": {
   "get": {
    "operationId": "list_nodes_api_v1_agents_nodes_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Nodes Api V1 Agents Nodes Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les agents de nœud",
    "tags": [
     "node-security"
    ],
    "description": "Machines non mises hors service de chaque cluster que l'appelant peut lire, avec leur état de confiance, leur mode de gestion, la version de leur agent, leur dernier battement de cœur, leur identité (numéro et expiration du certificat, identifiant SPIFFE, `rotation_due`), leurs métriques et leur plateforme."
   }
  },
  "/api/v1/agents/nodes/{host_id}": {
   "get": {
    "operationId": "node_detail_api_v1_agents_nodes__host_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Node Detail Api V1 Agents Nodes  Host Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un agent de nœud",
    "tags": [
     "node-security"
    ],
    "description": "La vue du nœud plus ses 10 derniers certificats `certificates` et ses `diagnostics` (caractéristiques, dernières métriques, 20 derniers reçus). Permission `cluster:read` ; `404 host_not_found`."
   }
  },
  "/api/v1/alert-definitions": {
   "get": {
    "operationId": "list_definitions_api_v1_alert_definitions_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Definitions Api V1 Alert Definitions Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les définitions d'alertes",
    "tags": [
     "alerts"
    ],
    "description": "Chaque définition avec son `name`, `scope`, `kind`, `label`, `description`, `enabled`, `interval_s`, ses seuils `warning` et `critical` et ses `params`. Permission `alerts:read`."
   }
  },
  "/api/v1/alert-definitions/{definition_id}": {
   "patch": {
    "operationId": "patch_definition_api_v1_alert_definitions__definition_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "definition_id",
      "required": true,
      "schema": {
       "title": "Definition Id",
       "type": "string"
      },
      "description": "Identifiant de la définition d'alerte."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DefinitionPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de la définition à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch Definition Api V1 Alert Definitions  Definition Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Ajuster une définition d'alerte",
    "tags": [
     "alerts"
    ],
    "description": "Active ou désactive une définition et modifie ses seuils ou son intervalle d'évaluation ; un champ omis est conservé. Renvoie la définition. Permission `alerts:manage` ; `404 alert_definition_not_found`. Audité en `alert_definition.update`."
   }
  },
  "/api/v1/alerts/{alert_id}": {
   "get": {
    "operationId": "get_alert_api_v1_alerts__alert_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "alert_id",
      "required": true,
      "schema": {
       "title": "Alert Id",
       "type": "string"
      },
      "description": "Identifiant de l'alerte."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Alert Api V1 Alerts  Alert Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une alerte",
    "tags": [
     "alerts"
    ],
    "description": "Renvoie une alerte avec l'historique `history` de ses changements d'état (les 200 derniers : `ts`, `from_state`, `to_state`, `value`, `text`). Permission `alerts:read` sur son cluster ; `404 alert_not_found`."
   }
  },
  "/api/v1/alerts/{alert_id}/ack": {
   "post": {
    "operationId": "ack_alert_api_v1_alerts__alert_id__ack_post",
    "parameters": [
     {
      "in": "path",
      "name": "alert_id",
      "required": true,
      "schema": {
       "title": "Alert Id",
       "type": "string"
      },
      "description": "Identifiant de l'alerte."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AckIn"
       }
      }
     },
     "required": true,
     "description": "Note facultative."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Ack Alert Api V1 Alerts  Alert Id  Ack Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Acquitter une alerte",
    "tags": [
     "alerts"
    ],
    "description": "Enregistre qui a acquitté l'alerte, quand, et la note ; l'alerte reste ouverte jusqu'à la disparition de sa condition. Renvoie l'alerte. Permission `alerts:manage` sur son cluster. Audité en `alert.ack`."
   }
  },
  "/api/v1/audit": {
   "get": {
    "operationId": "list_audit_api_v1_audit_get",
    "parameters": [
     {
      "in": "query",
      "name": "actor",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 200,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Actor"
      },
      "description": "Nom ou identifiant de l'acteur, correspondance exacte."
     },
     {
      "in": "query",
      "name": "action",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 200,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Action"
      },
      "description": "Action, correspondance exacte (`auth.login`) ou une famille terminée par `.*` (`service.*`)."
     },
     {
      "in": "query",
      "name": "target_type",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 100,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Target Type"
      },
      "description": "Type de la cible, par exemple `user`, `service`, `cluster` ou `operation`."
     },
     {
      "in": "query",
      "name": "cluster_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 64,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cluster Id"
      },
      "description": "Uniquement les événements de ce cluster."
     },
     {
      "in": "query",
      "name": "outcome",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "enum": [
          "success",
          "failure",
          "denied"
         ],
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Outcome"
      },
      "description": "Résultat : `success`, `failure` ou `denied`."
     },
     {
      "in": "query",
      "name": "from",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "format": "date-time",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "From"
      },
      "description": "Événements à partir de cette date et heure (ISO 8601), incluse."
     },
     {
      "in": "query",
      "name": "to",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "format": "date-time",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "To"
      },
      "description": "Événements antérieurs à cette date et heure (ISO 8601), exclue."
     },
     {
      "in": "query",
      "name": "limit",
      "required": false,
      "schema": {
       "default": 50,
       "maximum": 500,
       "minimum": 1,
       "title": "Limit",
       "type": "integer"
      },
      "description": "Nombre maximal d'événements dans la page, de 1 à 500. 50 par défaut."
     },
     {
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 30,
         "pattern": "^[0-9]+$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cursor"
      },
      "description": "Le `next_cursor` de la page précédente (un numéro de séquence). Omettez-le pour la première page."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Audit Api V1 Audit Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Rechercher dans le journal d'audit",
    "tags": [
     "audit"
    ],
    "description": "Événements qui correspondent à tous les filtres donnés, du plus récent au plus ancien : `seq`, `id`, `ts`, `actor_type`, `actor_id`, `actor_name`, `action`, `target_type`, `target_id`, `cluster_id`, `outcome`, `ip`, `details`, `prev_hash`, `hash`. Paginé avec `limit` et `cursor`. Permission `audit:read`."
   }
  },
  "/api/v1/audit/export": {
   "get": {
    "operationId": "export_api_v1_audit_export_get",
    "parameters": [
     {
      "in": "query",
      "name": "format",
      "required": false,
      "schema": {
       "default": "ndjson",
       "maxLength": 10,
       "title": "Format",
       "type": "string"
      },
      "description": "Format du fichier : `ndjson` (par défaut) ou `csv`."
     },
     {
      "in": "query",
      "name": "from",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "format": "date-time",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "From"
      },
      "description": "Événements à partir de cette date et heure (ISO 8601), incluse. Omettez-la pour partir du premier événement."
     },
     {
      "in": "query",
      "name": "to",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "format": "date-time",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "To"
      },
      "description": "Événements antérieurs à cette date et heure (ISO 8601), exclue. Omettez-la pour exporter jusqu'à maintenant."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exporter le journal d'audit",
    "tags": [
     "audit"
    ],
    "description": "Diffuse tous les événements de la période dans l'ordre des séquences sous forme de fichier à télécharger : `audit.ndjson` (un objet JSON par ligne) ou `audit.csv` (les cellules qu'un tableur lirait comme une formule sont préfixées d'une apostrophe). Permission `audit:read` ; `422 export_format_invalid` pour un autre format. Audité en `audit.export`."
   }
  },
  "/api/v1/audit/verify": {
   "get": {
    "operationId": "verify_api_v1_audit_verify_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Verify Api V1 Audit Verify Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Vérifier la chaîne d'empreintes du journal d'audit",
    "tags": [
     "audit"
    ],
    "description": "Recalcule l'empreinte de chaque événement et contrôle le lien avec le précédent. Renvoie `valid`, le nombre d'événements contrôlés `checked` et `first_broken_seq` (nul quand la chaîne est intacte). Permission `audit:read`. Audité en `audit.verify` (résultat `failure` quand la chaîne est rompue)."
   }
  },
  "/api/v1/auth-config/ldap": {
   "get": {
    "operationId": "get_ldap_api_v1_auth_config_ldap_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Ldap Api V1 Auth Config Ldap Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la configuration LDAP",
    "tags": [
     "auth-config"
    ],
    "description": "Renvoie les réglages LDAP avec leurs valeurs par défaut, le mot de passe de liaison sous la forme `{\"secret\": true, \"set\": ...}` et les correspondances de rôles. Permission `auth:manage`."
   },
   "put": {
    "operationId": "put_ldap_api_v1_auth_config_ldap_put",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LdapBody"
       }
      }
     },
     "required": true,
     "description": "Réglages LDAP complets."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Ldap Api V1 Auth Config Ldap Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enregistrer la configuration LDAP",
    "tags": [
     "auth-config"
    ],
    "description": "Remplace les réglages LDAP et les correspondances de rôles et les renvoie. Chaque URL doit être protégée par TLS (`ldaps://`, ou `ldap://` avec `start_tls`), au moins une URL est nécessaire quand l'annuaire est activé, les filtres doivent être équilibrés en parenthèses. Permission `auth:manage`. Erreurs : `422 ldap_config_invalid` (avec `details.problems`), `422 role_invalid`. Audité en `auth_config.ldap`."
   }
  },
  "/api/v1/auth-config/ldap/test": {
   "post": {
    "operationId": "test_ldap_api_v1_auth_config_ldap_test_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LdapTestBody"
       }
      }
     },
     "required": true,
     "description": "Compte de test facultatif."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Test Ldap Api V1 Auth Config Ldap Test Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Tester la configuration LDAP",
    "tags": [
     "auth-config"
    ],
    "description": "Se lie avec les réglages enregistrés et, quand un compte de test est fourni, recherche l'utilisateur, vérifie son mot de passe et lit ses groupes. Renvoie `ok`, `bind`, `user_found`, `groups` et `error_code`. Rien n'est modifié. Permission `auth:manage` ; `404 ldap_not_configured`. Audité en `auth_config.ldap_test`."
   }
  },
  "/api/v1/auth-config/oidc": {
   "get": {
    "operationId": "list_oidc_api_v1_auth_config_oidc_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Oidc Api V1 Auth Config Oidc Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les fournisseurs OIDC",
    "tags": [
     "auth-config"
    ],
    "description": "Tous les fournisseurs OIDC, activés ou non, avec leurs réglages et leurs correspondances ; les secrets clients ne sont jamais renvoyés. Permission `auth:manage`."
   },
   "post": {
    "operationId": "create_oidc_api_v1_auth_config_oidc_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OidcBody"
       }
      }
     },
     "required": true,
     "description": "Réglages du fournisseur."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Oidc Api V1 Auth Config Oidc Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Ajouter un fournisseur OIDC",
    "tags": [
     "auth-config"
    ],
    "description": "Crée un fournisseur et le renvoie (`201`). Les portées doivent comprendre `openid`. Permission `auth:manage`. Erreurs : `409 provider_name_taken`, `422 oidc_config_invalid`, `422 role_invalid`. Audité."
   }
  },
  "/api/v1/auth-config/oidc/{provider_id}": {
   "delete": {
    "operationId": "delete_oidc_api_v1_auth_config_oidc__provider_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "provider_id",
      "required": true,
      "schema": {
       "title": "Provider Id",
       "type": "string"
      },
      "description": "Identifiant du fournisseur OIDC."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer un fournisseur OIDC",
    "tags": [
     "auth-config"
    ],
    "description": "Supprime le fournisseur ; ses utilisateurs ne peuvent plus se connecter par son intermédiaire. Répond `204`. Permission `auth:manage` ; `404 oidc_provider_not_found`. Audité en `auth_config.oidc_delete`."
   },
   "get": {
    "operationId": "get_oidc_api_v1_auth_config_oidc__provider_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "provider_id",
      "required": true,
      "schema": {
       "title": "Provider Id",
       "type": "string"
      },
      "description": "Identifiant du fournisseur OIDC."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Oidc Api V1 Auth Config Oidc  Provider Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un fournisseur OIDC",
    "tags": [
     "auth-config"
    ],
    "description": "Renvoie un fournisseur avec ses réglages et ses correspondances. Permission `auth:manage` ; `404 oidc_provider_not_found`."
   },
   "put": {
    "operationId": "put_oidc_api_v1_auth_config_oidc__provider_id__put",
    "parameters": [
     {
      "in": "path",
      "name": "provider_id",
      "required": true,
      "schema": {
       "title": "Provider Id",
       "type": "string"
      },
      "description": "Identifiant du fournisseur OIDC."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OidcBody"
       }
      }
     },
     "required": true,
     "description": "Réglages complets du fournisseur."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Oidc Api V1 Auth Config Oidc  Provider Id  Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier un fournisseur OIDC",
    "tags": [
     "auth-config"
    ],
    "description": "Remplace les réglages et les correspondances du fournisseur et le renvoie ; le secret client est conservé sauf si un nouveau est envoyé. Permission `auth:manage`. Erreurs : `404 oidc_provider_not_found`, `409 provider_name_taken`, `422 oidc_config_invalid`. Audité."
   }
  },
  "/api/v1/auth-config/oidc/{provider_id}/test": {
   "post": {
    "operationId": "test_oidc_api_v1_auth_config_oidc__provider_id__test_post",
    "parameters": [
     {
      "in": "path",
      "name": "provider_id",
      "required": true,
      "schema": {
       "title": "Provider Id",
       "type": "string"
      },
      "description": "Identifiant du fournisseur OIDC."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Test Oidc Api V1 Auth Config Oidc  Provider Id  Test Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Tester un fournisseur OIDC",
    "tags": [
     "auth-config"
    ],
    "description": "Lit le document de découverte de l'émetteur et ses clés de signature. Renvoie `ok`, `issuer`, `jwks_keys` (nombre de clés) et `error_code`. Permission `auth:manage` ; `404 oidc_provider_not_found`. Audité en `auth_config.oidc_test`."
   }
  },
  "/api/v1/auth-config/policy": {
   "get": {
    "operationId": "get_policy_api_v1_auth_config_policy_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Policy Api V1 Auth Config Policy Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la politique de sécurité",
    "tags": [
     "auth-config"
    ],
    "description": "Renvoie la politique courante, valeurs par défaut comprises : longueur minimale de mot de passe 12, historique 5, pas d'âge maximal, verrouillage après 5 échecs pendant 15 minutes, sessions de 30 minutes d'inactivité et 12 heures au plus, MFA exigée pour `admin` et `security_admin`. Permission `auth:manage`."
   },
   "put": {
    "operationId": "put_policy_api_v1_auth_config_policy_put",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PolicyBody"
       }
      }
     },
     "required": true,
     "description": "Tous les champs de la politique."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Policy Api V1 Auth Config Policy Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enregistrer la politique de sécurité",
    "tags": [
     "auth-config"
    ],
    "description": "Remplace toute la politique et la renvoie. Elle s'applique au prochain changement de mot de passe, à la prochaine connexion et au prochain contrôle de session. Permission `auth:manage` ; `422 role_invalid` pour un rôle inconnu dans `mfa_required_roles`. Audité en `auth_config.policy`."
   }
  },
  "/api/v1/auth/login": {
   "post": {
    "operationId": "login_api_v1_auth_login_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LoginBody"
       }
      }
     },
     "required": true,
     "description": "Nom d'utilisateur, mot de passe et fournisseur (`local` ou `ldap`)."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Login Api V1 Auth Login Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Se connecter avec un nom d'utilisateur et un mot de passe",
    "tags": [
     "auth"
    ],
    "description": "Vérifie un compte local ou LDAP. Répond l'un des cas suivants :\n\n- la session (`user`, `permissions`, `csrf_token`, `expires_at`) avec le cookie `nm_session` posé ;\n- `{\"mfa_required\": true, \"mfa_token\": ..., \"enrollment_required\": ...}` quand le second facteur est nécessaire\n  (le jeton est valable 5 minutes et 5 essais) ;\n- une erreur : `401 invalid_credentials`, `423 account_locked` (`details.locked_until`), `403 account_disabled`,\n  `403 password_expired` (`details.password_change_token`, `details.reason` `change_required` ou `expired`),\n  `400 ldap_disabled`, `503 ldap_unavailable`, `429 too_many_attempts`.\n\nUne connexion LDAP crée ou met à jour l'utilisateur et remplace ses attributions de rôle LDAP par les groupes\nassociés. Audité en `auth.login`."
   }
  },
  "/api/v1/auth/logout": {
   "post": {
    "operationId": "logout_api_v1_auth_logout_post",
    "responses": {
     "204": {
      "description": "Successful Response"
     }
    },
    "summary": "Se déconnecter",
    "tags": [
     "auth"
    ],
    "description": "Révoque la session courante et efface le cookie de session. Répond `204`, y compris quand la session a déjà expiré. Audité en `auth.logout`."
   }
  },
  "/api/v1/auth/mfa": {
   "delete": {
    "description": "Désactive le second facteur TOTP de l'utilisateur connecté et supprime ses codes de secours. Un compte local\nconfirme avec son mot de passe, un compte LDAP ou OIDC avec un code TOTP courant. Session de navigateur\nuniquement. Répond `204`. Erreurs : `403 mfa_required_by_policy` (un rôle de l'utilisateur exige la MFA),\n`409 mfa_not_enabled`, `401 invalid_credentials`, `401 invalid_mfa_code`, `429 too_many_attempts`. Audité en\n`auth.mfa_disabled`.",
    "operationId": "mfa_disable_api_v1_auth_mfa_delete",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/MfaDisableBody"
       }
      }
     },
     "required": true,
     "description": "Mot de passe (compte local) ou code TOTP courant (compte LDAP ou OIDC)."
    },
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Désactiver mon second facteur",
    "tags": [
     "auth"
    ]
   }
  },
  "/api/v1/auth/mfa/confirm": {
   "post": {
    "description": "Seconde étape de l'enrôlement en libre service : vérifie un code du secret donné par `POST /auth/mfa/setup`,\nactive la MFA et renvoie dix codes de secours à usage unique (`recovery_codes`), affichés une seule fois.\nSession de navigateur uniquement. Erreurs : `409 mfa_disabled`, `409 mfa_already_enabled`,\n`409 mfa_enrollment_not_started`, `401 invalid_mfa_code`. Audité en `auth.mfa_enrolled`.",
    "operationId": "mfa_confirm_api_v1_auth_mfa_confirm_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/MfaConfirmBody"
       }
      }
     },
     "required": true,
     "description": "Code courant de l'application d'authentification."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Mfa Confirm Api V1 Auth Mfa Confirm Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Confirmer mon second facteur",
    "tags": [
     "auth"
    ]
   }
  },
  "/api/v1/auth/mfa/enroll/confirm": {
   "post": {
    "operationId": "mfa_enroll_confirm_api_v1_auth_mfa_enroll_confirm_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EnrollConfirmBody"
       }
      }
     },
     "required": true,
     "description": "Jeton de défi de la connexion et premier code du nouveau secret."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Mfa Enroll Confirm Api V1 Auth Mfa Enroll Confirm Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Confirmer l'enrôlement imposé à la connexion",
    "tags": [
     "auth"
    ],
    "description": "Seconde étape de l'enrôlement exigé à la connexion : vérifie le code du secret en attente, active la MFA, puis\nouvre la session comme une connexion et ajoute les codes de secours `recovery_codes` (affichés une fois) à la\nréponse. Erreurs : `401 mfa_token_invalid`, `409 mfa_already_enabled`, `409 mfa_enrollment_not_started`,\n`401 invalid_mfa_code`, `403 password_expired`. Audité en `auth.mfa_enrolled`."
   }
  },
  "/api/v1/auth/mfa/enroll/start": {
   "post": {
    "operationId": "mfa_enroll_start_api_v1_auth_mfa_enroll_start_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EnrollStartBody"
       }
      }
     },
     "required": true,
     "description": "Jeton de défi de la connexion."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Mfa Enroll Start Api V1 Auth Mfa Enroll Start Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Démarrer l'enrôlement imposé à la connexion",
    "tags": [
     "auth"
    ],
    "description": "Première étape de l'enrôlement exigé par la politique de sécurité, avec le `mfa_token` de la connexion. Génère un\nsecret TOTP et renvoie `secret` et `otpauth_uri` (à afficher en QR code). Erreurs : `401 mfa_token_invalid`,\n`409 mfa_already_enabled`. Audité en `auth.mfa_enroll_start`."
   }
  },
  "/api/v1/auth/mfa/setup": {
   "post": {
    "description": "Libre service, depuis une session de navigateur : génère un secret TOTP en attente et renvoie `secret` et\n`otpauth_uri`. Terminez avec `POST /auth/mfa/confirm`. Erreurs : `409 mfa_disabled` (TOTP désactivé sur ce\nManager), `409 mfa_already_enabled`, `403 session_required`. Audité en `auth.mfa_setup`.",
    "operationId": "mfa_setup_api_v1_auth_mfa_setup_post",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Mfa Setup Api V1 Auth Mfa Setup Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Commencer l'enrôlement de mon second facteur",
    "tags": [
     "auth"
    ]
   }
  },
  "/api/v1/auth/mfa/verify": {
   "post": {
    "operationId": "mfa_verify_api_v1_auth_mfa_verify_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/MfaVerifyBody"
       }
      }
     },
     "required": true,
     "description": "Jeton de défi de la connexion avec un code TOTP ou un code de secours."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Mfa Verify Api V1 Auth Mfa Verify Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Vérifier le second facteur d'une connexion",
    "tags": [
     "auth"
    ],
    "description": "Seconde étape d'une connexion : envoyez le `mfa_token` avec un `code` TOTP ou un `recovery_code` (chaque code de\nsecours ne sert qu'une fois ; un code TOTP ne peut pas être rejoué). En cas de succès, la session s'ouvre comme\npour une connexion. Un mauvais code compte comme un échec de connexion ; après 5 mauvais codes, le jeton est\nsupprimé. Erreurs : `401 mfa_token_invalid`, `401 invalid_mfa_code`, `423 account_locked`,\n`403 password_expired`, `429 too_many_attempts`. Audité en `auth.mfa`."
   }
  },
  "/api/v1/auth/oidc/{provider_id}/callback": {
   "get": {
    "operationId": "oidc_callback_api_v1_auth_oidc__provider_id__callback_get",
    "parameters": [
     {
      "in": "path",
      "name": "provider_id",
      "required": true,
      "schema": {
       "title": "Provider Id",
       "type": "string"
      },
      "description": "Identifiant du fournisseur OIDC."
     },
     {
      "in": "query",
      "name": "code",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Code"
      },
      "description": "Code d'autorisation renvoyé par le fournisseur."
     },
     {
      "in": "query",
      "name": "state",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "State"
      },
      "description": "Valeur d'état créée par l'étape de démarrage, comparée au cookie `nm_oidc_state`."
     },
     {
      "in": "query",
      "name": "error",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Error"
      },
      "description": "Erreur renvoyée par le fournisseur quand l'utilisateur a refusé ou que la demande a échoué."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Terminer une connexion OIDC (cible de redirection)",
    "tags": [
     "auth"
    ],
    "description": "Cible de redirection déclarée chez le fournisseur OIDC ; c'est le navigateur qui revient ici, jamais un script.\nLe Manager compare `state` au cookie posé à l'étape de démarrage, échange le code (PKCE), vérifie le jeton\nd'identité (émetteur, audience, nonce, signature), crée ou met à jour l'utilisateur, associe ses groupes à des\nrôles, puis répond `302` : vers la page demandée au démarrage avec le cookie de session, vers `/login?mfa=1`\nquand le second facteur est exigé, ou vers `/login?error=<code>` en cas d'échec (`oidc_state_invalid`,\n`oidc_denied`, `oidc_user_conflict`...). Audité en `auth.login` et `user.oidc_sync`."
   }
  },
  "/api/v1/auth/oidc/{provider_id}/start": {
   "get": {
    "operationId": "oidc_start_api_v1_auth_oidc__provider_id__start_get",
    "parameters": [
     {
      "in": "path",
      "name": "provider_id",
      "required": true,
      "schema": {
       "title": "Provider Id",
       "type": "string"
      },
      "description": "Identifiant du fournisseur OIDC, issu de `GET /auth/providers`."
     },
     {
      "in": "query",
      "name": "redirect",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 512,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Redirect"
      },
      "description": "Chemin de la console à ouvrir après la connexion, par exemple `/clusters`. 512 caractères au plus ; `/` par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Démarrer une connexion OIDC",
    "tags": [
     "auth"
    ],
    "description": "Ouvrez cette URL dans le navigateur : elle découvre le fournisseur, conserve un état, un nonce et un\nvérificateur PKCE valables 10 minutes, pose le cookie `nm_oidc_state` et répond `302` vers la page\nd'autorisation du fournisseur. Un fournisseur désactivé ou injoignable redirige vers `/login?error=<code>`. Un\n`redirect` qui sort de la console est remplacé par `/` et audité en `auth.oidc_redirect_refused`."
   }
  },
  "/api/v1/auth/password/change": {
   "post": {
    "operationId": "password_change_api_v1_auth_password_change_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PasswordChangeBody"
       }
      }
     },
     "required": true,
     "description": "Nouveau mot de passe avec le mot de passe actuel ou le jeton de changement de mot de passe."
    },
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Changer un mot de passe",
    "tags": [
     "auth"
    ],
    "description": "Deux usages :\n\n1. connecté avec un compte local : envoyez `current_password` et `new_password` ;\n2. à la connexion, après `403 password_expired` : envoyez le `password_change_token` de l'erreur et\n   `new_password` (aucune session nécessaire).\n\nLe nouveau mot de passe doit respecter la politique de sécurité (longueur, classes de caractères, différent du\nnom d'utilisateur et des derniers mots de passe) : sinon `422 password_policy` avec les règles enfreintes dans\n`details.rules`. En cas de succès, les autres sessions de l'utilisateur sont révoquées et l'API répond `204`.\nErreurs : `401 password_change_token_invalid`, `401 invalid_credentials`, `409 not_local_user`,\n`429 too_many_attempts`. Audité en `auth.password_change`."
   }
  },
  "/api/v1/auth/providers": {
   "get": {
    "operationId": "providers_api_v1_auth_providers_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Providers Api V1 Auth Providers Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les méthodes de connexion",
    "tags": [
     "auth"
    ],
    "description": "Point d'accès public utilisé par la page de connexion. Renvoie `local` (toujours vrai), `ldap` (`enabled`,\n`name`), `oidc` (les fournisseurs activés, `id` et `name`), `setup_required` (vrai tant qu'aucun utilisateur\nn'existe : appelez `POST /auth/setup`) et `mfa` (le second facteur TOTP est-il activé sur ce Manager)."
   }
  },
  "/api/v1/auth/session": {
   "get": {
    "operationId": "session_api_v1_auth_session_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Session Api V1 Auth Session Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la session courante",
    "tags": [
     "auth"
    ],
    "description": "Renvoie l'utilisateur authentifié (`user`), ses permissions effectives (`permissions.global` et\n`permissions.clusters` par identifiant de cluster), le `csrf_token` de la session (nul pour un jeton d'API) et\n`expires_at` (de la session ou du jeton). Tout appelant authentifié ; `401` sinon."
   }
  },
  "/api/v1/auth/setup": {
   "post": {
    "operationId": "setup_api_v1_auth_setup_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SetupBody"
       }
      }
     },
     "required": true,
     "description": "Nom d'utilisateur, mot de passe et nom affiché du premier administrateur."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Setup Api V1 Auth Setup Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer le premier administrateur",
    "tags": [
     "auth"
    ],
    "description": "Au premier démarrage uniquement, tant qu'aucun utilisateur n'existe : crée un compte local avec le rôle global\n`admin` et le connecte (mêmes réponses qu'une connexion). Erreurs : `409 setup_done` dès qu'un utilisateur\nexiste, `422 password_policy`, `429 too_many_attempts`. Audité en `auth.setup`."
   }
  },
  "/api/v1/backup-jobs": {
   "get": {
    "operationId": "list_jobs_api_v1_backup_jobs_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Jobs Api V1 Backup Jobs Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les tâches de sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Les 200 dernières tâches de sauvegarde, de la plus récente à la plus ancienne, avec le nom de leur plan, leur statut et leur erreur. Permission `backup:read`."
   }
  },
  "/api/v1/backup-plans": {
   "get": {
    "operationId": "list_plans_api_v1_backup_plans_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Plans Api V1 Backup Plans Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les plans de sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Chaque plan de sauvegarde, et `alerts` : les plans dont la dernière sauvegarde réussie est plus ancienne que l'intervalle plus la fenêtre, ou dont la dernière tâche a échoué. Permission `backup:read`."
   },
   "post": {
    "operationId": "create_plan_api_v1_backup_plans_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PlanIn"
       }
      }
     },
     "required": true,
     "description": "Cible, périmètre, calendrier, chiffrement et conservation du plan."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Plan Api V1 Backup Plans Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un plan de sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Crée un plan de sauvegarde sur une cible et le renvoie (`201`). Avec `immutability_required`, la cible doit avoir des capacités de verrouillage d'objet et de rétention vérifiées. Permission `backup:manage`. Erreurs : `404 backup_target_not_found`, `409 backup_worm_unsupported`. Audité en `backup.plan.create`."
   }
  },
  "/api/v1/backup-plans/{plan_id}/backups": {
   "post": {
    "operationId": "record_backup_api_v1_backup_plans__plan_id__backups_post",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de sauvegarde."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ArtifactIn"
       }
      }
     },
     "required": true,
     "description": "Emplacement, type et versions de l'artefact."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Record Backup Api V1 Backup Plans  Plan Id  Backups Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enregistrer une sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Enregistre une tâche et son artefact : le Manager calcule l'empreinte du fichier situé à `location`, conserve son empreinte, sa taille et ses versions, et fixe `expires_at` d'après la durée de conservation du plan. Quand le fichier est illisible, la tâche est enregistrée en `failed`. Renvoie la tâche avec son `artifact` (`201`). Permission `backup:manage` ; `404 backup_policy_not_found`. Audité en `backup.job`."
   }
  },
  "/api/v1/backup-targets": {
   "post": {
    "operationId": "create_target_api_v1_backup_targets_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__backup_recovery__TargetIn"
       }
      }
     },
     "required": true,
     "description": "Fournisseur, emplacement et capacités de la cible."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Target Api V1 Backup Targets Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Déclarer une cible de sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Crée une cible et la renvoie (`201`). Permission `backup:manage` ; `422 backup_provider_unsupported`. Audité en `backup.target.create`."
   }
  },
  "/api/v1/backups": {
   "get": {
    "operationId": "list_backups_api_v1_backups_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Backups Api V1 Backups Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les sauvegardes",
    "tags": [
     "backup"
    ],
    "description": "Les 200 derniers artefacts de sauvegarde, du plus récent au plus ancien : emplacement, SHA-256, taille, versions, expiration, plan et résultat de leur dernière vérification. Permission `backup:read`."
   }
  },
  "/api/v1/backups/consistency": {
   "post": {
    "description": "Compare les lignes de la base, les objets stockés et le registre des artefacts d'une cible `filesystem` (son `bucket` est le répertoire racine). Avec `reconcile`, les lignes dont l'objet a disparu sont marquées `missing` et les objets orphelins sont déplacés dans un répertoire de quarantaine, jamais supprimés. Permission `backup:manage`. Erreurs : `404 backup_target_not_found`, `422 backup_provider_unsupported` (autre que `filesystem`).",
    "operationId": "backup_consistency_api_v1_backups_consistency_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ConsistencyIn"
       }
      }
     },
     "required": true,
     "description": "Cible, empreintes du registre et indicateur de réconciliation."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Backup Consistency Api V1 Backups Consistency Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Contrôler la cohérence d'une cible de sauvegarde",
    "tags": [
     "backup"
    ]
   }
  },
  "/api/v1/backups/retention/enforce": {
   "post": {
    "operationId": "enforce_retention_api_v1_backups_retention_enforce_post",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Enforce Retention Api V1 Backups Retention Enforce Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Appliquer la conservation maintenant",
    "tags": [
     "backup"
    ],
    "description": "Retire les artefacts dont `expires_at` est dépassé et les marque `expired`. Renvoie `expired`. Permission `backup:manage`. Audité en `backup.expire`."
   }
  },
  "/api/v1/backups/{backup_id}": {
   "delete": {
    "operationId": "delete_backup_api_v1_backups__backup_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "backup_id",
      "required": true,
      "schema": {
       "title": "Backup Id",
       "type": "string"
      },
      "description": "Identifiant de l'artefact de sauvegarde."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Delete Backup Api V1 Backups  Backup Id  Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer une sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Supprime un artefact de sauvegarde dont la durée de conservation est écoulée. Permission `backup:manage`. Erreurs : `409 backup_retention_active` (avant `expires_at`), `404 backup_artifact_not_found`. Audité en `backup.delete`."
   }
  },
  "/api/v1/backups/{backup_id}/restore-tests": {
   "post": {
    "operationId": "run_restore_test_api_v1_backups__backup_id__restore_tests_post",
    "parameters": [
     {
      "in": "path",
      "name": "backup_id",
      "required": true,
      "schema": {
       "title": "Backup Id",
       "type": "string"
      },
      "description": "Identifiant de l'artefact de sauvegarde."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RestoreTestIn"
       }
      }
     },
     "required": true,
     "description": "Injection ou non d'une corruption (pour prouver que le test la détecte)."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Run Restore Test Api V1 Backups  Backup Id  Restore Tests Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lancer un test de restauration",
    "tags": [
     "backup"
    ],
    "description": "Copie l'artefact dans un répertoire isolé, injecte éventuellement une corruption, vérifie l'empreinte et le déchiffrement et signe le rapport (Ed25519). Un échec lève une alarme auditée. Renvoie le test (`201`). Permission `backup:manage`. Audité en `backup.restore_test`."
   }
  },
  "/api/v1/backups/{backup_id}/verifications": {
   "get": {
    "operationId": "list_verifications_api_v1_backups__backup_id__verifications_get",
    "parameters": [
     {
      "in": "path",
      "name": "backup_id",
      "required": true,
      "schema": {
       "title": "Backup Id",
       "type": "string"
      },
      "description": "Identifiant de l'artefact de sauvegarde."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Verifications Api V1 Backups  Backup Id  Verifications Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les vérifications d'une sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Chaque vérification de l'artefact, de la plus récente à la plus ancienne, avec son résultat `outcome` (`passed` ou `failed`) et ses détails (empreinte attendue et observée, taille, raison). Permission `backup:read`."
   }
  },
  "/api/v1/backups/{backup_id}/verify": {
   "post": {
    "operationId": "verify_backup_api_v1_backups__backup_id__verify_post",
    "parameters": [
     {
      "in": "path",
      "name": "backup_id",
      "required": true,
      "schema": {
       "title": "Backup Id",
       "type": "string"
      },
      "description": "Identifiant de l'artefact de sauvegarde."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Verify Backup Api V1 Backups  Backup Id  Verify Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Vérifier une sauvegarde",
    "tags": [
     "backup"
    ],
    "description": "Recalcule l'empreinte du fichier, compare l'empreinte et la taille à celles enregistrées et, si elles correspondent, le déchiffre avec sa clé. Renvoie la vérification avec son résultat `outcome` (`passed` ou `failed`). Permission `backup:manage` ; `404 backup_artifact_not_found`. Audité en `backup.verify`."
   }
  },
  "/api/v1/breakglass/{request_id}/approve": {
   "post": {
    "operationId": "breakglass_approve_api_v1_breakglass__request_id__approve_post",
    "parameters": [
     {
      "in": "path",
      "name": "request_id",
      "required": true,
      "schema": {
       "title": "Request Id",
       "type": "string"
      },
      "description": "Identifiant de la demande d'accès d'urgence."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Breakglass Approve Api V1 Breakglass  Request Id  Approve Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Approuver une demande d'accès d'urgence",
    "tags": [
     "node-security"
    ],
    "description": "Ajoute une approbation. Deux approbateurs distincts, tous deux différents du demandeur, sont nécessaires ; la seconde approbation signe un certificat utilisateur OpenSSH valable pendant la durée demandée. Permission `hosts:manage`. Erreurs : `403 breakglass_self_approval`, `409 breakglass_not_pending`, `409 breakglass_already_approved`, `404 breakglass_not_found`. Audité en `host.breakglass.approve`."
   }
  },
  "/api/v1/breakglass/{request_id}/evidence": {
   "post": {
    "operationId": "breakglass_evidence_api_v1_breakglass__request_id__evidence_post",
    "parameters": [
     {
      "in": "path",
      "name": "request_id",
      "required": true,
      "schema": {
       "title": "Request Id",
       "type": "string"
      },
      "description": "Identifiant de la demande d'accès d'urgence."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EvidenceIn"
       }
      }
     },
     "required": true,
     "description": "Type, empreinte et résumé de la preuve."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Breakglass Evidence Api V1 Breakglass  Request Id  Evidence Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Ajouter une preuve à une session d'accès d'urgence",
    "tags": [
     "node-security"
    ],
    "description": "Ajoute à la demande une preuve expurgée (début de session, empreinte de commande, fin de session ou enregistrement). Permission `hosts:manage` ; `404 breakglass_not_found`."
   }
  },
  "/api/v1/breakglass/{request_id}/revoke": {
   "post": {
    "operationId": "breakglass_revoke_api_v1_breakglass__request_id__revoke_post",
    "parameters": [
     {
      "in": "path",
      "name": "request_id",
      "required": true,
      "schema": {
       "title": "Request Id",
       "type": "string"
      },
      "description": "Identifiant de la demande d'accès d'urgence."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Breakglass Revoke Api V1 Breakglass  Request Id  Revoke Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révoquer un accès d'urgence avant son terme",
    "tags": [
     "node-security"
    ],
    "description": "Met fin à l'accès avant son terme : le certificat est ajouté à la liste de révocation de clés que chargent les machines. Permission `hosts:manage`. Erreurs : `404 breakglass_not_found`, `409 breakglass_not_active`."
   }
  },
  "/api/v1/catalog/high-availability": {
   "get": {
    "description": "Matrice de haute disponibilité de chaque service du catalogue, triée par catégorie puis par nom : ses modes (topologies) avec la cardinalité de chaque composant, et son bloc de haute disponibilité avec les contrôles de vivacité et de disponibilité. Renvoie `items` et `count`. Tout utilisateur connecté.",
    "operationId": "high_availability_api_v1_catalog_high_availability_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response High Availability Api V1 Catalog High Availability Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la matrice de haute disponibilité du catalogue",
    "tags": [
     "stack"
    ]
   }
  },
  "/api/v1/clusters": {
   "get": {
    "operationId": "list_clusters_api_v1_clusters_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Clusters Api V1 Clusters Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les clusters",
    "tags": [
     "clusters"
    ],
    "description": "Clusters que l'appelant peut lire (`cluster:read` globalement ou sur le cluster), triés par nom : `id`, `name`, `mode`, `description`, `stack_version`, `profile`, `kind` (`hosts` ou `kubernetes`), `created_at`, `updated_at`."
   },
   "post": {
    "operationId": "create_cluster_api_v1_clusters_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ClusterIn"
       }
      }
     },
     "required": true,
     "description": "Nom, mode, description et profil du cluster."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Cluster Api V1 Clusters Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un cluster",
    "tags": [
     "clusters"
    ],
    "description": "Crée un cluster vide sur la version courante du catalogue et le renvoie (`201`). Sans profil, les clusters `production` et `preprod` reçoivent `standard`, les autres modes `development`. Permission `clusters:create` ; `409 cluster_name_taken` (les noms ne tiennent pas compte de la casse). Audité en `cluster.create`."
   }
  },
  "/api/v1/clusters/{cluster_id}": {
   "delete": {
    "operationId": "delete_cluster_api_v1_clusters__cluster_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer un cluster vide",
    "tags": [
     "clusters"
    ],
    "description": "Supprime un cluster qui n'a aucun service et aucune machine en dehors des machines mises hors service. Répond `204`. Permission `cluster:manage`. Erreurs : `404 cluster_not_found`, `409 cluster_not_empty`. Audité en `cluster.delete`."
   },
   "get": {
    "operationId": "get_cluster_api_v1_clusters__cluster_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Cluster Api V1 Clusters  Cluster Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un cluster",
    "tags": [
     "clusters"
    ],
    "description": "Renvoie le cluster. Permission `cluster:read` ; `404 cluster_not_found`."
   },
   "patch": {
    "operationId": "patch_cluster_api_v1_clusters__cluster_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ClusterPatch"
       }
      }
     },
     "required": true,
     "description": "Champs du cluster à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch Cluster Api V1 Clusters  Cluster Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Renommer un cluster ou changer son profil",
    "tags": [
     "clusters"
    ],
    "description": "Modifie le nom, la description ou le profil ; un champ omis est conservé. Renvoie le cluster. Permission `cluster:manage`. Erreurs : `404 cluster_not_found`, `409 cluster_name_taken`. Audité en `cluster.update`."
   }
  },
  "/api/v1/clusters/{cluster_id}/acceptance-reports": {
   "get": {
    "operationId": "list_reports_api_v1_clusters__cluster_id__acceptance_reports_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response List Reports Api V1 Clusters  Cluster Id  Acceptance Reports Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les rapports de recette d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Les 100 dernières exécutions de recette du cluster, de la plus récente à la plus ancienne, avec leur statut, leur verdict et leur opération. Permission `cluster:read`."
   },
   "post": {
    "operationId": "create_report_api_v1_clusters__cluster_id__acceptance_reports_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Report Api V1 Clusters  Cluster Id  Acceptance Reports Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lancer une recette",
    "tags": [
     "lifecycle"
    ],
    "description": "Lance l'opération `acceptance.run` : contrôles préalables du cluster, vivacité de chaque instance en marche, disponibilité et test fonctionnel de chaque service géré (chaque étape tolère ses échecs pour que tous les contrôles s'exécutent), puis le rapport signé. Renvoie `operation_id` et `report_id`. Permission `services:install`. Audité en `acceptance.run`."
   }
  },
  "/api/v1/clusters/{cluster_id}/acceptance-reports/{report_id}": {
   "get": {
    "operationId": "get_report_api_v1_clusters__cluster_id__acceptance_reports__report_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "report_id",
      "required": true,
      "schema": {
       "title": "Report Id",
       "type": "string"
      },
      "description": "Identifiant du rapport de recette."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Report Api V1 Clusters  Cluster Id  Acceptance Reports  Report Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un rapport de recette signé",
    "tags": [
     "lifecycle"
    ],
    "description": "Renvoie le document signé : un élément par contrôle avec `PASS`, `FAIL` ou `BLOCKED` et sa preuve, le verdict (`FAIL` quand un élément a échoué, `BLOCKED` quand aucun n'a échoué et qu'un est bloqué, sinon `PASS`) et la signature. Permission `cluster:read` ; `404 acceptance_not_found` tant que l'exécution n'a pas réussi."
   }
  },
  "/api/v1/clusters/{cluster_id}/acceptance-reports/{report_id}/html": {
   "get": {
    "operationId": "report_html_api_v1_clusters__cluster_id__acceptance_reports__report_id__html_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "report_id",
      "required": true,
      "schema": {
       "title": "Report Id",
       "type": "string"
      },
      "description": "Identifiant du rapport de recette."
     },
     {
      "in": "query",
      "name": "lang",
      "required": false,
      "schema": {
       "default": "en",
       "enum": [
        "en",
        "fr"
       ],
       "title": "Lang",
       "type": "string"
      },
      "description": "Langue de la page : `en` (par défaut) ou `fr`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un rapport de recette en HTML",
    "tags": [
     "lifecycle"
    ],
    "description": "Produit le rapport sous forme de page HTML autonome dans la langue demandée, pour l'imprimer ou l'archiver. Permission `cluster:read` ; `404 acceptance_not_found`."
   }
  },
  "/api/v1/clusters/{cluster_id}/actions": {
   "post": {
    "operationId": "cluster_action_api_v1_clusters__cluster_id__actions_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ClusterActionBody"
       }
      }
     },
     "required": true,
     "description": "Action, périmètre, découpage par lots et simulation."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Cluster Action Api V1 Clusters  Cluster Id  Actions Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exécuter une action sur tous les services d'un cluster",
    "tags": [
     "services"
    ],
    "description": "Lance une seule opération qui exécute l'action sur chaque service géré du périmètre (tous les services par défaut, ou ceux de `services`, sur toutes les machines ou sur `hosts`), dans l'ordre des dépendances : un fournisseur démarre avant ses consommateurs et s'arrête après eux. `restart` arrête tous les services dans l'ordre inverse, puis démarre tous les services dans l'ordre avec les contrôles de santé ; `rolling_restart` redémarre chaque service par lots de `batch_size` machines ; `restart_stale` réécrit les fichiers et unités des instances dont la configuration a changé, puis les redémarre par lots ; `service_check` lance la vérification de chaque service. Les services connectés, les services en cours d'installation ou de désinstallation, un état administratif qui interdit l'action et les instances en maintenance, en cours de retrait ou sur une machine décommissionnée sont écartés et listés dans `skipped` avec la raison. Renvoie `services` dans l'ordre des dépendances, `skipped`, `steps` et `operation_id` ; avec `dry_run`, rien n'est créé et `operation_id` est nul. Permission `services:operate`. Erreurs : `409 bulk_nothing_to_do`, `404 cluster_service_not_found`, `409 profile_blocked`, `409 approval_required` (sous `banking-secure`, l'arrêt et les redémarrages demandent une approbation pour le service `*`). Audité en `cluster.<action>`."
   }
  },
  "/api/v1/clusters/{cluster_id}/alerts": {
   "get": {
    "description": "Alertes du cluster, de la dernière modifiée à la plus ancienne (500 au plus) : `definition`, `label`, `fingerprint`, machine, service, composant, `state`, `text`, `value`, première et dernière observation, acquittement et résolution. Alertes ouvertes uniquement, sauf avec `include_resolved`. Permission `alerts:read`.",
    "operationId": "list_alerts_api_v1_clusters__cluster_id__alerts_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "state",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "State"
      },
      "description": "Uniquement les alertes dans cet état : `warning`, `critical`, `unknown` ou `ok`."
     },
     {
      "in": "query",
      "name": "severity",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Severity"
      },
      "description": "Uniquement les alertes de cette sévérité (`warning` ou `critical`) ; comparée à l'état de l'alerte."
     },
     {
      "in": "query",
      "name": "include_resolved",
      "required": false,
      "schema": {
       "default": false,
       "title": "Include Resolved",
       "type": "boolean"
      },
      "description": "Renvoyer aussi les alertes résolues. Faux par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Alerts Api V1 Clusters  Cluster Id  Alerts Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les alertes d'un cluster",
    "tags": [
     "alerts"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/approvals": {
   "get": {
    "operationId": "list_approvals_api_v1_clusters__cluster_id__approvals_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response List Approvals Api V1 Clusters  Cluster Id  Approvals Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les approbations d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Les 200 dernières approbations, de la plus récente à la plus ancienne : `action`, `service`, `reason`, `status` (`pending`, `approved`, `rejected`, `expired` ou `used`), demandeur, décideur, l'opération qui l'a utilisée et les horaires. Les approbations échues sont d'abord marquées `expired`. Permission `cluster:read`."
   },
   "post": {
    "operationId": "request_approval_api_v1_clusters__cluster_id__approvals_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ApprovalBody"
       }
      }
     },
     "required": true,
     "description": "Action, service et raison."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Request Approval Api V1 Clusters  Cluster Id  Approvals Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Demander une approbation",
    "tags": [
     "lifecycle"
    ],
    "description": "Enregistre une demande en attente pour une action perturbatrice sur un service ; un autre utilisateur la tranche. Renvoie l'approbation (`201`). Permission `services:operate`. Audité en `approval.request`."
   }
  },
  "/api/v1/clusters/{cluster_id}/approvals/{approval_id}/decision": {
   "post": {
    "operationId": "decide_approval_api_v1_clusters__cluster_id__approvals__approval_id__decision_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "approval_id",
      "required": true,
      "schema": {
       "title": "Approval Id",
       "type": "string"
      },
      "description": "Identifiant de la demande d'approbation."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DecisionBody"
       }
      }
     },
     "required": true,
     "description": "La décision."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Decide Approval Api V1 Clusters  Cluster Id  Approvals  Approval Id  Decision Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Approuver ou rejeter une demande d'approbation",
    "tags": [
     "lifecycle"
    ],
    "description": "Tranche une demande en attente ; le demandeur ne peut pas trancher la sienne. Renvoie l'approbation. Permission `cluster:manage`. Erreurs : `404 approval_invalid`, `409 approval_not_pending`, `403 approval_self`. Audité en `approval.approve` ou `approval.reject`."
   }
  },
  "/api/v1/clusters/{cluster_id}/backups": {
   "get": {
    "operationId": "list_backups_api_v1_clusters__cluster_id__backups_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "service",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Service"
      },
      "description": "Uniquement les sauvegardes de ce service."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Backups Api V1 Clusters  Cluster Id  Backups Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les sauvegardes de services d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Renvoie `backupable` (les services installés dont le catalogue déclare une sauvegarde) et les 300 dernières sauvegardes avec leur service, machine, lot, statut, fichiers et empreintes. Permission `cluster:read`."
   },
   "post": {
    "operationId": "create_backup_api_v1_clusters__cluster_id__backups_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BackupBody"
       }
      }
     },
     "required": true,
     "description": "Services à sauvegarder."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Backup Api V1 Clusters  Cluster Id  Backups Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Prendre une sauvegarde coordonnée",
    "tags": [
     "lifecycle"
    ],
    "description": "Lance une opération qui sauvegarde les services fournis (par défaut tous les services sauvegardables) dans l'ordre des dépendances, avec un `set_id` commun ; le lot ne réussit que si chaque membre a réussi. Renvoie `operation_id`, `set_id` et `backup_ids`. Permission `services:operate`. Audité en `backup.create`."
   }
  },
  "/api/v1/clusters/{cluster_id}/backups/{backup_id}/restore": {
   "post": {
    "operationId": "restore_backup_api_v1_clusters__cluster_id__backups__backup_id__restore_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "backup_id",
      "required": true,
      "schema": {
       "title": "Backup Id",
       "type": "string"
      },
      "description": "Identifiant de la sauvegarde de service."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ApprovalOnly"
       }
      }
     },
     "required": true,
     "description": "Approbation facultative."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Restore Backup Api V1 Clusters  Cluster Id  Backups  Backup Id  Restore Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Restaurer une sauvegarde de service",
    "tags": [
     "lifecycle"
    ],
    "description": "Lance l'opération de restauration : arrêt des instances, contrôle des empreintes, étapes de restauration, démarrage des instances, contrôles de santé, étapes de vérification, contrôle de service et test fonctionnel. Renvoie `operation_id`. Permission `services:install` ; une approbation sous `banking-secure`. Audité en `backup.restore`."
   }
  },
  "/api/v1/clusters/{cluster_id}/blueprint": {
   "get": {
    "operationId": "export_blueprint_api_v1_clusters__cluster_id__blueprint_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Export Blueprint Api V1 Clusters  Cluster Id  Blueprint Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exporter le blueprint d'un cluster",
    "tags": [
     "blueprint"
    ],
    "description": "Renvoie le blueprint du cluster : ses services gérés avec leur configuration non secrète (les secrets ne sont jamais exportés) et les groupes de machines construits à partir des composants qui tournent ensemble ; `hosts` d'un groupe liste les machines de ce cluster à titre indicatif. Permission `cluster:manage`."
   }
  },
  "/api/v1/clusters/{cluster_id}/blueprint/apply": {
   "post": {
    "operationId": "apply_blueprint_api_v1_clusters__cluster_id__blueprint_apply_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__blueprint__ApplyBody"
       }
      }
     },
     "required": true,
     "description": "Blueprint, association des machines et options d'installation."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Apply Blueprint Api V1 Clusters  Cluster Id  Blueprint Apply Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Appliquer un blueprint à un cluster",
    "tags": [
     "blueprint"
    ],
    "description": "Transforme le blueprint et l'association des machines en demandes d'installation et crée une seule opération d'installation (type `blueprint.apply`), avec les mêmes contrôles qu'une installation de service. Renvoie `operation_id` et les contrôles `ha`. Permissions `cluster:manage` et `services:install`. Erreurs : `422 blueprint_invalid` (avec `details.errors`), et les erreurs d'une installation. Audité en `blueprint.apply`."
   }
  },
  "/api/v1/clusters/{cluster_id}/blueprint/validate": {
   "post": {
    "operationId": "validate_blueprint_api_v1_clusters__cluster_id__blueprint_validate_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ValidateBody"
       }
      }
     },
     "required": true,
     "description": "Blueprint et association facultative des machines."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Validate Blueprint Api V1 Clusters  Cluster Id  Blueprint Validate Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Valider un blueprint pour un cluster",
    "tags": [
     "blueprint"
    ],
    "description": "Contrôle la forme du blueprint et, quand un `host_mapping` est fourni, l'association et les demandes d'installation qui en résultent. Renvoie `ok` et `errors` (chacune avec `service`, `field`, `code`, `message_i18n`). Rien n'est modifié. Permission `cluster:manage`."
   }
  },
  "/api/v1/clusters/{cluster_id}/bootstrap-tokens": {
   "get": {
    "operationId": "list_tokens_api_v1_clusters__cluster_id__bootstrap_tokens_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Tokens Api V1 Clusters  Cluster Id  Bootstrap Tokens Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les jetons d'amorçage d'un cluster",
    "tags": [
     "clusters"
    ],
    "description": "Jetons du cluster, du plus récent au plus ancien, avec `prefix`, `expires_at`, `max_uses`, `uses`, `created_at` et `revoked_at` ; le secret n'est jamais renvoyé. Permission `hosts:manage`."
   },
   "post": {
    "operationId": "create_token_api_v1_clusters__cluster_id__bootstrap_tokens_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TokenIn"
       }
      }
     },
     "required": true,
     "description": "Durée de vie et nombre d'utilisations du jeton."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Token Api V1 Clusters  Cluster Id  Bootstrap Tokens Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un jeton d'amorçage pour de nouvelles machines",
    "tags": [
     "clusters"
    ],
    "description": "Crée un jeton qui permet aux agents de s'enregistrer dans ce cluster et renvoie `id`, `token` (affiché\nuniquement ici), `expires_at`, `register_command` (la commande `nm-agent register` avec l'URL du serveur, le\njeton et l'empreinte de l'autorité de certification) et `install_command` (un script qui installe l'agent depuis\nle miroir et l'enregistre). Répond `201`. Permission `hosts:manage`. Audité en `bootstrap_token.create`."
   }
  },
  "/api/v1/clusters/{cluster_id}/bootstrap-tokens/{token_id}": {
   "delete": {
    "operationId": "revoke_token_api_v1_clusters__cluster_id__bootstrap_tokens__token_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "token_id",
      "required": true,
      "schema": {
       "title": "Token Id",
       "type": "string"
      },
      "description": "Identifiant du jeton d'amorçage."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révoquer un jeton d'amorçage",
    "tags": [
     "clusters"
    ],
    "description": "Révoque le jeton : plus aucun nouvel agent ne peut s'enregistrer avec lui ; les machines déjà enregistrées ne sont pas affectées. Répond `204`. Permission `hosts:manage` ; `404 bootstrap_token_not_found` (inconnu ou déjà révoqué). Audité en `bootstrap_token.revoke`."
   }
  },
  "/api/v1/clusters/{cluster_id}/certificates": {
   "get": {
    "operationId": "list_certificates_api_v1_clusters__cluster_id__certificates_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Certificates Api V1 Clusters  Cluster Id  Certificates Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les certificats d'un cluster",
    "tags": [
     "pki"
    ],
    "description": "Certificats émis pour le cluster, du premier à expirer au dernier : `kind` (`agent` ou `service`), machine, service, composant, `serial`, `subject`, `sans`, `not_before`, `not_after`, `revoked_at`. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/certificates/rotate": {
   "post": {
    "operationId": "rotate_api_v1_clusters__cluster_id__certificates_rotate_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "approval_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Approval Id"
      }
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RotateIn"
       }
      }
     },
     "required": true,
     "description": "Service facultatif auquel limiter le renouvellement."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Rotate Api V1 Clusters  Cluster Id  Certificates Rotate Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Renouveler les certificats des services d'un cluster",
    "tags": [
     "pki"
    ],
    "description": "Émet un nouveau certificat pour chaque instance de composant (d'un service, ou de tous), puis lance une opération qui les installe sur les machines et redémarre les composants. Répond `202` avec `operation_id`. Permission `pki:manage`. Erreurs : `404 cluster_not_found`, `409 no_certificates_to_rotate`. Audité en `pki.rotate`."
   }
  },
  "/api/v1/clusters/{cluster_id}/consoles": {
   "get": {
    "operationId": "list_consoles_api_v1_clusters__cluster_id__consoles_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Consoles Api V1 Clusters  Cluster Id  Consoles Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les consoles des services d'un cluster",
    "tags": [
     "consoles"
    ],
    "description": "Chaque console du catalogue avec son état sur le cluster : installée ou non, santé, adresse interne, contrôles de conformité, possibilité pour l'appelant de l'ouvrir et, pour les administrateurs du coffre, l'entrée du coffre qui contient ses identifiants. Les services sans interface web sont listés à part avec leurs points d'accès HTTP. Permission `services:operate`. Audité en `consoles.list`."
   }
  },
  "/api/v1/clusters/{cluster_id}/consoles/{service}/access": {
   "put": {
    "operationId": "put_access_api_v1_clusters__cluster_id__consoles__service__access_put",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AccessBody"
       }
      }
     },
     "required": true,
     "description": "Adresse publiée et protection."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Access Api V1 Clusters  Cluster Id  Consoles  Service  Access Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Publier l'adresse interne d'une console",
    "tags": [
     "consoles"
    ],
    "description": "Définit l'adresse interne de la console (derrière la passerelle HTTPS de la banque) et sa protection ; les deux à nul reviennent à l'adresse découverte. Renvoie la console ; l'entrée du coffre du service est mise à jour avec la nouvelle adresse. Permission `cluster:manage` ; `422 console_url_invalid`. Audité en `console.access`."
   }
  },
  "/api/v1/clusters/{cluster_id}/consoles/{service}/credentials/rotate": {
   "post": {
    "operationId": "rotate_credentials_api_v1_clusters__cluster_id__consoles__service__credentials_rotate_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RotateBody"
       }
      }
     },
     "required": true,
     "description": "Nouveau mot de passe et politique de redémarrage."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Rotate Credentials Api V1 Clusters  Cluster Id  Consoles  Service  Credentials Rotate Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Faire tourner le mot de passe d'un compte de console",
    "tags": [
     "consoles"
    ],
    "description": "Définit un nouveau mot de passe (généré s'il est omis) pour le compte de la console lu depuis la configuration : crée une nouvelle version de configuration et lance l'opération d'application ; l'entrée du coffre suit quand l'application réussit. Répond `202` avec `operation_id` et `version`. Le mot de passe n'est jamais journalisé. Permissions `configs:edit` et `vault:manage`. Erreurs : `409 console_no_credential`, `409 console_rotation_manual`, `409 service_unmanaged`, `422 config_invalid`. Audité en `console.credentials.rotate`."
   }
  },
  "/api/v1/clusters/{cluster_id}/consoles/{service}/open": {
   "post": {
    "operationId": "open_console_api_v1_clusters__cluster_id__consoles__service__open_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Open Console Api V1 Clusters  Cluster Id  Consoles  Service  Open Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Ouvrir une console de service",
    "tags": [
     "consoles"
    ],
    "description": "Renvoie `url`, les contrôles `checks` et `compliant`. Exige `services:operate` et la permission de la console (`vault:manage` pour une console d'identités ou de secrets). Erreurs : `404 console_unknown`, `409 console_not_installed`, `409 console_no_url`, `409 console_not_compliant` (production ou préproduction). Audité en `console.open`."
   }
  },
  "/api/v1/clusters/{cluster_id}/drift": {
   "get": {
    "operationId": "drift_report_api_v1_clusters__cluster_id__drift_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "resolved",
      "required": false,
      "schema": {
       "default": false,
       "title": "Resolved",
       "type": "boolean"
      },
      "description": "Renvoyer aussi les constats résolus. Faux par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Drift Report Api V1 Clusters  Cluster Id  Drift Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire le rapport de dérive d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Constats des derniers contrôles : fichiers et unités gérés modifiés, supprimés ou remplacés en dehors du Manager, avec machine, service, composant et chemin. Les constats ouverts lèvent l'alerte `config_drift`. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/drift/scan": {
   "post": {
    "operationId": "drift_scan_api_v1_clusters__cluster_id__drift_scan_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ScanBody"
       }
      }
     },
     "required": true,
     "description": "Machines à contrôler."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Drift Scan Api V1 Clusters  Cluster Id  Drift Scan Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Rechercher les dérives du cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Lance une opération avec une tâche d'empreintes par machine sur les fichiers et unités écrits par le Manager ; les différences ouvrent des constats. Renvoie `operation_id`. Permission `services:operate`. Audité en `drift.scan`."
   }
  },
  "/api/v1/clusters/{cluster_id}/drift/{finding_id}/reconcile": {
   "post": {
    "description": "Réécrit les fichiers et unités de l'instance qui a dérivé à partir de la configuration du Manager (et la redémarre si demandé). Renvoie `operation_id`. Permission `configs:edit`. Erreurs : `404 drift_not_found`, `409 drift_not_reconcilable` (un fichier qui n'appartient à aucun service). Audité en `drift.reconcile`.",
    "operationId": "drift_reconcile_api_v1_clusters__cluster_id__drift__finding_id__reconcile_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "finding_id",
      "required": true,
      "schema": {
       "title": "Finding Id",
       "type": "string"
      },
      "description": "Identifiant du constat de dérive."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ReconcileBody"
       }
      }
     },
     "required": true,
     "description": "Option de redémarrage et approbation facultative."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Drift Reconcile Api V1 Clusters  Cluster Id  Drift  Finding Id  Reconcile Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Corriger un constat de dérive",
    "tags": [
     "lifecycle"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/hosts": {
   "get": {
    "operationId": "list_hosts_api_v1_clusters__cluster_id__hosts_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Hosts Api V1 Clusters  Cluster Id  Hosts Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les machines d'un cluster",
    "tags": [
     "hosts"
    ],
    "description": "Machines du cluster triées par nom d'hôte, avec leur état, leur système d'exploitation, leurs ressources, leurs étiquettes, leurs composants, leurs alertes ouvertes, la version de leur agent, l'expiration de leur certificat, leurs dernières métriques et leurs débits réseau. Permission `cluster:read` ; `404 cluster_not_found`."
   }
  },
  "/api/v1/clusters/{cluster_id}/hosts/ssh-bootstrap": {
   "post": {
    "description": "Lance une opération avec une tâche par machine : le Manager se connecte en SSH (la clé de la machine doit\ncorrespondre à l'empreinte fournie), installe l'agent et l'enregistre avec un jeton d'amorçage créé pour cette\nopération (valable 2 heures, une utilisation par machine). La clé privée est gardée en mémoire pour les tâches\nuniquement, jamais conservée ni journalisée. Une machine en échec n'arrête pas les autres. Répond `202` avec\n`operation_id`. Permission `hosts:manage`. Erreurs : `422 validation_error` (nom invalide ou empreinte\nmanquante), `404 cluster_not_found`. Audité en `host.ssh_bootstrap`.",
    "operationId": "ssh_bootstrap_api_v1_clusters__cluster_id__hosts_ssh_bootstrap_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SshBootstrapIn"
       }
      }
     },
     "required": true,
     "description": "Machines, compte SSH, clé privée et empreintes des clés des machines."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Ssh Bootstrap Api V1 Clusters  Cluster Id  Hosts Ssh Bootstrap Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Installer des agents par SSH",
    "tags": [
     "hosts"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes": {
   "delete": {
    "operationId": "unregister_api_v1_clusters__cluster_id__kubernetes_delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Désenregistrer un cluster Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Oublie le serveur d'API et le kubeconfig ; le cluster redevient un cluster de machines. Refusé tant que des composants restent (`409 k8s_components_remain`). Répond `204`. Permission `cluster:manage`. Audité en `k8s.cluster.unregister`."
   },
   "get": {
    "operationId": "get_target_api_v1_clusters__cluster_id__kubernetes_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Target Api V1 Clusters  Cluster Id  Kubernetes Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une cible Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Renvoie le serveur d'API, l'espace de noms, la version du serveur, le mode, la distribution et le nombre de nœuds découverts, l'heure de la découverte, la dernière erreur et le kubeconfig sous la forme `{\"secret\": true, \"set\": true}`. Permission `cluster:read`."
   },
   "patch": {
    "operationId": "patch_target_api_v1_clusters__cluster_id__kubernetes_patch",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__kubernetes__TargetPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de la cible à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch Target Api V1 Clusters  Cluster Id  Kubernetes Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier une cible Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Modifie le serveur d'API, le kubeconfig (renvoyez l'objet relu pour le conserver) ou l'espace de noms (refusé tant que des composants restent) ; le contrôle du kubeconfig s'exécute à nouveau. Permission `cluster:manage`. Erreurs : `422 validation_error`, `409 k8s_components_remain`. Audité en `k8s.cluster.update`."
   },
   "post": {
    "operationId": "register_api_v1_clusters__cluster_id__kubernetes_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__kubernetes__TargetIn"
       }
      }
     },
     "required": true,
     "description": "Serveur d'API, kubeconfig et espace de noms."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Register Api V1 Clusters  Cluster Id  Kubernetes Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enregistrer un cluster Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Contrôle le kubeconfig par rapport au serveur d'API fourni, appelle `/version`, scelle le kubeconfig et fait du cluster un cluster Kubernetes. Le cluster ne doit avoir aucune machine. Renvoie la cible (`201`). Permission `cluster:manage`. Erreurs : `409 k8s_already_registered`, `409 k8s_cluster_has_hosts`, erreurs du contrôle du kubeconfig et de l'API. Audité en `k8s.cluster.register`."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/agent": {
   "delete": {
    "operationId": "revoke_api_v1_clusters__cluster_id__kubernetes_agent_delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révoquer l'identité de l'agent de cluster",
    "tags": [
     "kubernetes"
    ],
    "description": "Révoque l'identité du Navin Cluster Agent. Répond `204`. Permission `cluster:manage` ; `404 k8s_agent_not_registered`. Audité en `k8s.agent.revoke`."
   },
   "get": {
    "operationId": "get_agent_api_v1_clusters__cluster_id__kubernetes_agent_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Agent Api V1 Clusters  Cluster Id  Kubernetes Agent Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire l'agent de cluster",
    "tags": [
     "kubernetes"
    ],
    "description": "État de l'identité du Navin Cluster Agent et son dernier rapport. Permission `cluster:read`."
   },
   "post": {
    "operationId": "issue_api_v1_clusters__cluster_id__kubernetes_agent_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AgentIn"
       }
      }
     },
     "required": true,
     "description": "URL de la passerelle, locataire et délai hors ligne de l'agent."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Issue Api V1 Clusters  Cluster Id  Kubernetes Agent Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Émettre l'identité de l'agent de cluster",
    "tags": [
     "kubernetes"
    ],
    "description": "Émet le certificat client et la configuration du Navin Cluster Agent : `certificate_pem`, `private_key_pem` (renvoyée une seule fois, jamais conservée), `ca_pem`, `manager_public_key_pem`, `config`, `fingerprint` et `not_after` (`201`). Permission `cluster:manage` ; `422 k8s_agent_gateway_url_invalid`. Audité en `k8s.agent.issue`."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/agent/commands": {
   "get": {
    "operationId": "list_commands_api_v1_clusters__cluster_id__kubernetes_agent_commands_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Commands Api V1 Clusters  Cluster Id  Kubernetes Agent Commands Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les commandes de l'agent de cluster",
    "tags": [
     "kubernetes"
    ],
    "description": "Les 200 dernières commandes mises en file pour l'agent de cluster, chacune avec ses reçus. Permission `cluster:read`."
   },
   "post": {
    "operationId": "queue_command_api_v1_clusters__cluster_id__kubernetes_agent_commands_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__k8s_agents__CommandIn"
       }
      }
     },
     "required": true,
     "description": "Opération, paramètres, clé d'idempotence et validité."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Queue Command Api V1 Clusters  Cluster Id  Kubernetes Agent Commands Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Mettre en file une commande signée pour l'agent de cluster",
    "tags": [
     "kubernetes"
    ],
    "description": "Enregistre la décision d'autorisation, puis met en file une commande signée (`managedcomponent.apply`, `managedcomponent.delete` ou `inventory.report`) que l'agent récupère. Répond `202` avec la commande. Permission `services:operate`. Audité en `k8s.agent.policy`."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/agent/manifests": {
   "get": {
    "operationId": "agent_manifests_api_v1_clusters__cluster_id__kubernetes_agent_manifests_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "image",
      "required": true,
      "schema": {
       "pattern": "^[A-Za-z0-9.:/_-]+(@sha256:[a-f0-9]{64})$",
       "title": "Image",
       "type": "string"
      },
      "description": "Image de l'agent épinglée par empreinte, par exemple `<registry-host>/navin/cluster-agent@sha256:<64 hex>`."
     },
     {
      "in": "query",
      "name": "replicas",
      "required": false,
      "schema": {
       "default": 2,
       "maximum": 5,
       "minimum": 2,
       "title": "Replicas",
       "type": "integer"
      },
      "description": "Réplicas du Deployment de l'agent (élection d'un leader), de 2 à 5. 2 par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Agent Manifests Api V1 Clusters  Cluster Id  Kubernetes Agent Manifests Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire les manifestes de l'agent de cluster",
    "tags": [
     "kubernetes"
    ],
    "description": "ServiceAccount, RBAC et Deployment de l'agent de cluster pour l'espace de noms géré, sous forme d'objets (`manifests`) et de YAML (`yaml`) prêt pour `kubectl apply`. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/components": {
   "get": {
    "description": "Liste en direct : chaque `ManagedComponent` de l'espace de noms avec son statut, qui est aussi inscrit dans le modèle du Manager. Permission `cluster:read`.",
    "operationId": "list_components_api_v1_clusters__cluster_id__kubernetes_components_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Components Api V1 Clusters  Cluster Id  Kubernetes Components Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les composants Kubernetes",
    "tags": [
     "kubernetes"
    ]
   },
   "post": {
    "operationId": "install_component_api_v1_clusters__cluster_id__kubernetes_components_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ComponentIn"
       }
      }
     },
     "required": true,
     "description": "Service, source, valeurs, réplicas et politiques du composant."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Install Component Api V1 Clusters  Cluster Id  Kubernetes Components Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Installer un service du catalogue sur Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Crée la ressource `ManagedComponent` d'un service du catalogue (chart depuis un chemin ou une référence OCI épinglée par empreinte) et renvoie son état (`201`). Les valeurs qui ressemblent à des secrets sont refusées. Permission `services:install`. Erreurs : `409 k8s_component_exists`, `422 k8s_values_secret`, `409 k8s_replicas_below_pdb`. Audité en `k8s.component.install`."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/components/{name}": {
   "delete": {
    "operationId": "remove_component_api_v1_clusters__cluster_id__kubernetes_components__name__delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "name",
      "required": true,
      "schema": {
       "maxLength": 63,
       "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
       "title": "Name",
       "type": "string"
      },
      "description": "Nom du composant (la ressource `ManagedComponent`)."
     }
    ],
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Remove Component Api V1 Clusters  Cluster Id  Kubernetes Components  Name  Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Retirer un composant Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Supprime la ressource `ManagedComponent` ; l'opérateur désinstalle le chart. Répond `202` avec `{\"name\", \"state\": \"removed\"}` une fois la ressource disparue, sinon le composant en cours de suppression. Permission `services:install`. Erreurs : `404 k8s_component_not_found`, `409 k8s_maintenance`. Audité en `k8s.component.remove`."
   },
   "get": {
    "operationId": "get_component_api_v1_clusters__cluster_id__kubernetes_components__name__get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "name",
      "required": true,
      "schema": {
       "maxLength": 63,
       "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
       "title": "Name",
       "type": "string"
      },
      "description": "Nom du composant."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Component Api V1 Clusters  Cluster Id  Kubernetes Components  Name  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un composant Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Lit la ressource et renvoie son état observé. Permission `cluster:read` ; `404 k8s_component_not_found` (l'enregistrement est supprimé quand la ressource a disparu)."
   },
   "patch": {
    "operationId": "configure_component_api_v1_clusters__cluster_id__kubernetes_components__name__patch",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "name",
      "required": true,
      "schema": {
       "maxLength": 63,
       "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
       "title": "Name",
       "type": "string"
      },
      "description": "Nom du composant."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ComponentPatch"
       }
      }
     },
     "required": true,
     "description": "Champs du composant à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Configure Component Api V1 Clusters  Cluster Id  Kubernetes Components  Name  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Configurer un composant Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Modifie la version, la source, les valeurs, les références de secrets ou le budget d'interruption du composant (application côté serveur). Les valeurs qui ressemblent à des secrets sont refusées : utilisez `secret_refs`. Permission `configs:edit`. Erreurs : `422 k8s_values_secret`, `409 k8s_maintenance`, `404 k8s_component_not_found`. Audité en `k8s.component.configure` (noms des champs uniquement)."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/components/{name}/actions": {
   "post": {
    "operationId": "component_action_api_v1_clusters__cluster_id__kubernetes_components__name__actions_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "name",
      "required": true,
      "schema": {
       "maxLength": 63,
       "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
       "title": "Name",
       "type": "string"
      },
      "description": "Nom du composant."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ActionIn"
       }
      }
     },
     "required": true,
     "description": "Action et ses arguments."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Component Action Api V1 Clusters  Cluster Id  Kubernetes Components  Name  Actions Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exécuter une action sur un composant Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Modifie l'état souhaité : `start`, `stop`, `restart` ou `rolling_restart` (un horodatage de redémarrage), `scale` (`replicas` fixes ou `autoscaling`, jamais sous le budget d'interruption) ou `maintenance` (`enabled`). Répond `202` avec `component` et `requested`. Permission `services:operate`. Erreurs : `409 k8s_maintenance`, `409 k8s_component_stopped`, `409 k8s_replicas_below_pdb`. Audité en `k8s.component.<action>`."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/discovery": {
   "get": {
    "operationId": "last_discovery_api_v1_clusters__cluster_id__kubernetes_discovery_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Last Discovery Api V1 Clusters  Cluster Id  Kubernetes Discovery Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la dernière découverte d'un cluster Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Renvoie l'inventaire de la dernière découverte. Permission `cluster:read` ; `404 k8s_not_discovered`."
   },
   "post": {
    "operationId": "run_discovery_api_v1_clusters__cluster_id__kubernetes_discovery_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Run Discovery Api V1 Clusters  Cluster Id  Kubernetes Discovery Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Découvrir un cluster Kubernetes",
    "tags": [
     "kubernetes"
    ],
    "description": "Lit, par des appels en lecture seule, la version, la distribution (fournie par le client ou gérée), les nœuds avec leur capacité et leurs GPU, les classes de stockage et d'ingress, CoreDNS et les modules de la plateforme (cert-manager, Cilium, Kyverno, Argo CD, KEDA...). Enregistre et renvoie l'inventaire. Permission `hosts:manage`. Audité en `k8s.discovery`."
   }
  },
  "/api/v1/clusters/{cluster_id}/kubernetes/import-rbac": {
   "get": {
    "operationId": "import_rbac_api_v1_clusters__cluster_id__kubernetes_import_rbac_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "namespace",
      "required": false,
      "schema": {
       "default": "navin-platform",
       "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
       "title": "Namespace",
       "type": "string"
      },
      "description": "Espace de noms géré. `navin-platform` par défaut."
     },
     {
      "in": "query",
      "name": "identity",
      "required": false,
      "schema": {
       "default": "navin-import",
       "pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$",
       "title": "Identity",
       "type": "string"
      },
      "description": "Nom du ServiceAccount de l'import. `navin-import` par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Import Rbac Api V1 Clusters  Cluster Id  Kubernetes Import Rbac Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire le RBAC de l'identité d'import",
    "tags": [
     "kubernetes"
    ],
    "description": "RBAC à moindre privilège de l'identité qu'utilise le Manager pour le cluster, sous forme d'objets et de YAML, avec le nom d'utilisateur obtenu : lecture de ce dont la découverte a besoin, écriture uniquement des ressources `platform.navin.ai` dans l'espace de noms géré ; aucun Secret, objet RBAC, charge de travail, exec ni usurpation d'identité. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/mcp-runtime": {
   "get": {
    "description": "Renvoie l'état de l'opérateur `operator` (avec `crd_installed`), les composants ToolHive `components`, le `preset`, les instances gérées `items` (filtrées par `tenant` et `project`), les objets ignorés `ignored`, les agrégats `aggregates` par locataire et par projet (toujours sur toutes les instances gérées) et `observed_at`. Permission `cluster:read`.",
    "operationId": "runtime_state_api_v1_clusters__cluster_id__mcp_runtime_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "tenant",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 63,
         "pattern": "^[A-Za-z0-9]([-A-Za-z0-9_.]{0,61}[A-Za-z0-9])?$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Tenant"
      },
      "description": "Uniquement les instances de ce locataire (valeur d'étiquette, 63 caractères au plus)."
     },
     {
      "in": "query",
      "name": "project",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 63,
         "pattern": "^[A-Za-z0-9]([-A-Za-z0-9_.]{0,61}[A-Za-z0-9])?$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Project"
      },
      "description": "Uniquement les instances de ce projet (valeur d'étiquette, 63 caractères au plus)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Runtime State Api V1 Clusters  Cluster Id  Mcp Runtime Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire l'état de l'environnement MCP",
    "tags": [
     "mcp-runtime"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/mcp-runtime/install": {
   "post": {
    "description": "Écrit les deux ressources `ManagedComponent` de ToolHive (CRD, puis opérateur) dans `toolhive-system`, épinglées par empreinte. Renvoie `namespace`, `version` et les composants écrits `components` (`201`). Permission `services:install` ; `409 mcp_runtime_installed`. Audité en `mcp_runtime.install`.",
    "operationId": "install_toolhive_api_v1_clusters__cluster_id__mcp_runtime_install_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Install Toolhive Api V1 Clusters  Cluster Id  Mcp Runtime Install Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Installer l'opérateur ToolHive",
    "tags": [
     "mcp-runtime"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/mcp-runtime/instances/{namespace}/{name}": {
   "delete": {
    "description": "Supprime un `MCPServer` géré (précondition sur l'uid) ; le contrôleur du Control Plane révoque ensuite ses identifiants et le clôt. Renvoie `namespace`, `name`, `instance_id` et `state` (`deleting`). Permission `services:operate`. Erreurs : `404 mcp_runtime_instance_not_found`, `502 mcp_runtime_forbidden` (l'identité du Manager n'a pas le droit). Audité en `mcp_runtime.instance.destroy`.",
    "operationId": "destroy_instance_api_v1_clusters__cluster_id__mcp_runtime_instances__namespace___name__delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "namespace",
      "required": true,
      "schema": {
       "title": "Namespace",
       "type": "string"
      },
      "description": "Espace de noms de projet de l'instance, `mcp-<12 hex>`."
     },
     {
      "in": "path",
      "name": "name",
      "required": true,
      "schema": {
       "title": "Name",
       "type": "string"
      },
      "description": "Nom de l'objet `MCPServer`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Destroy Instance Api V1 Clusters  Cluster Id  Mcp Runtime Instances  Namespace   Name  Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer une instance MCP gérée",
    "tags": [
     "mcp-runtime"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/mcp-runtime/instances/{namespace}/{name}/restart": {
   "post": {
    "description": "Redémarre un `MCPServer` géré sans changer sa spécification : un correctif de fusion de l'annotation `mcpserver.toolhive.stacklok.dev/restarted-at` vers une nouvelle heure RFC 3339 (redémarrage progressif par l'opérateur), avec l'uid en précondition. Renvoie `namespace`, `name`, `instance_id`, `state` (`restarting`) et `restarted_at`. Permission `services:operate` ; `404 mcp_runtime_instance_not_found`. Audité en `mcp_runtime.instance.restart`.",
    "operationId": "restart_instance_api_v1_clusters__cluster_id__mcp_runtime_instances__namespace___name__restart_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "namespace",
      "required": true,
      "schema": {
       "title": "Namespace",
       "type": "string"
      },
      "description": "Espace de noms de projet de l'instance, `mcp-<12 hex>`."
     },
     {
      "in": "path",
      "name": "name",
      "required": true,
      "schema": {
       "title": "Name",
       "type": "string"
      },
      "description": "Nom de l'objet `MCPServer`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Restart Instance Api V1 Clusters  Cluster Id  Mcp Runtime Instances  Namespace   Name  Restart Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Redémarrer une instance MCP gérée",
    "tags": [
     "mcp-runtime"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/network/flows": {
   "get": {
    "description": "Flux à ouvrir entre les machines, le Manager, les clients et les points d'accès externes du cluster. En JSON : `cluster_id`, `items`, `summary` et `generated_at` ; avec `format=csv`, une pièce jointe `network-flows-<cluster>.csv`. Permission `cluster:read`.",
    "operationId": "network_flows_api_v1_clusters__cluster_id__network_flows_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "format",
      "required": false,
      "schema": {
       "default": "json",
       "enum": [
        "json",
        "csv"
       ],
       "title": "Format",
       "type": "string"
      },
      "description": "Format de la réponse : `json` (par défaut) ou `csv`."
     },
     {
      "in": "query",
      "name": "service",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 100,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Service"
      },
      "description": "Uniquement les flux de ce service."
     },
     {
      "in": "query",
      "name": "host_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 64,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Host Id"
      },
      "description": "Uniquement les flux depuis ou vers cette machine."
     },
     {
      "in": "query",
      "name": "scope",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "enum": [
          "platform",
          "service",
          "intra_service",
          "egress"
         ],
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Scope"
      },
      "description": "Uniquement les flux de cette portée : `platform`, `service`, `intra_service` ou `egress`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       },
       "text/csv": {
        "schema": {
         "type": "string"
        }
       }
      },
      "description": "The flows; text/csv attachment with format=csv."
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la matrice des flux réseau d'un cluster",
    "tags": [
     "network"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/network/rules": {
   "get": {
    "description": "Commandes qui ouvrent les flux dans les groupes de sécurité d'un fournisseur cloud ou dans un pare-feu de machine. Avec `auto`, le fournisseur de chaque machine est utilisé (l'environnement choisi pour la machine, sinon la plateforme détectée). Rien n'est appliqué. Permission `cluster:read`.",
    "operationId": "network_rules_api_v1_clusters__cluster_id__network_rules_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "provider",
      "required": true,
      "schema": {
       "enum": [
        "auto",
        "aws",
        "azure",
        "gcp",
        "ibm",
        "openstack",
        "ovh",
        "firewalld",
        "ufw"
       ],
       "title": "Provider",
       "type": "string"
      },
      "description": "Cible des règles : `auto`, `aws`, `azure`, `gcp`, `ibm`, `openstack`, `ovh`, `firewalld` ou `ufw`."
     },
     {
      "in": "query",
      "name": "service",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 100,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Service"
      },
      "description": "Uniquement les flux de ce service."
     },
     {
      "in": "query",
      "name": "host_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 64,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Host Id"
      },
      "description": "Uniquement les flux depuis ou vers cette machine."
     },
     {
      "in": "query",
      "name": "scope",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "enum": [
          "platform",
          "service",
          "intra_service",
          "egress"
         ],
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Scope"
      },
      "description": "Uniquement les flux de cette portée : `platform`, `service`, `intra_service` ou `egress`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Network Rules Api V1 Clusters  Cluster Id  Network Rules Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire les règles de pare-feu qui ouvrent les flux",
    "tags": [
     "network"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/operations": {
   "get": {
    "operationId": "list_operations_api_v1_clusters__cluster_id__operations_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "status",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "pattern": "^(pending|running|succeeded|failed|aborted)$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Status"
      },
      "description": "Uniquement les opérations dans ce statut : `pending`, `running`, `succeeded`, `failed` ou `aborted`."
     },
     {
      "in": "query",
      "name": "limit",
      "required": false,
      "schema": {
       "default": 50,
       "maximum": 500,
       "minimum": 1,
       "title": "Limit",
       "type": "integer"
      },
      "description": "Nombre maximal d'opérations dans la page, de 1 à 500. 50 par défaut."
     },
     {
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cursor"
      },
      "description": "Le `next_cursor` de la page précédente (un identifiant d'opération). Omettez-le pour la première page."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Operations Api V1 Clusters  Cluster Id  Operations Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les opérations d'un cluster",
    "tags": [
     "operations"
    ],
    "description": "Opérations du cluster, de la plus récente à la plus ancienne, sans leurs étapes : `id`, `type`, `title_i18n`, `target`, `status`, `progress`, `error`, `created_by`, `created_at`, `started_at`, `finished_at`, `abort_requested`. Paginé avec `limit` et `cursor`. Permission `operations:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/preflight": {
   "post": {
    "operationId": "run_preflight_api_v1_clusters__cluster_id__preflight_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PreflightBody"
       }
      }
     },
     "required": true,
     "description": "Services et machines à contrôler."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Run Preflight Api V1 Clusters  Cluster Id  Preflight Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lancer les contrôles préalables d'installation d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Chaque contrôle qui doit réussir avant l'installation ou la mise à niveau d'un service, avec sa sévérité, sa preuve et un verdict bloquant : caractéristiques des machines, DNS, contrôles préalables du catalogue, pare-feu, capacité, versions, TLS et règles du profil. Un contrôle en `fail` bloque. Rien n'est modifié. Permission `services:install`."
   }
  },
  "/api/v1/clusters/{cluster_id}/profile": {
   "get": {
    "description": "Chaque règle du profil du cluster (ou de `profile`, pour comparer avant d'en choisir un) évaluée par rapport à l'état réel, chacune avec son `status` (`pass` ou `fail`), son libellé et sa raison ; le statut est bloqué quand une règle échoue. Permission `cluster:read`.",
    "operationId": "cluster_profile_api_v1_clusters__cluster_id__profile_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "query",
      "name": "profile",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Profile"
      },
      "description": "Profil à évaluer à la place du profil courant : `development`, `standard`, `banking-secure`, `air-gapped` ou `hybrid`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Cluster Profile Api V1 Clusters  Cluster Id  Profile Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Évaluer le profil d'un cluster",
    "tags": [
     "lifecycle"
    ]
   },
   "put": {
    "operationId": "set_profile_api_v1_clusters__cluster_id__profile_put",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ProfileBody"
       }
      }
     },
     "required": true,
     "description": "Le nouveau profil."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Set Profile Api V1 Clusters  Cluster Id  Profile Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Changer le profil d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Définit le profil d'installation et renvoie son évaluation. Permission `cluster:manage`. Audité en `cluster.profile` avec l'ancien et le nouveau profil."
   }
  },
  "/api/v1/clusters/{cluster_id}/sd/prometheus": {
   "get": {
    "operationId": "prometheus_sd_api_v1_clusters__cluster_id__sd_prometheus_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response Prometheus Sd Api V1 Clusters  Cluster Id  Sd Prometheus Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Découverte de services Prometheus d'un cluster",
    "tags": [
     "system"
    ],
    "description": "Cibles au format de découverte de services HTTP de Prometheus : un groupe par instance de composant qui expose des métriques, avec `targets` (`fqdn:port`) et les étiquettes `__metrics_path__`, `__scheme__`, `nm_cluster`, `nm_service`, `nm_component`, `nm_host`. Permission `cluster:read` ; `404 cluster_not_found`."
   }
  },
  "/api/v1/clusters/{cluster_id}/security-lock": {
   "delete": {
    "operationId": "unlock_cluster_api_v1_clusters__cluster_id__security_lock_delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Unlock Cluster Api V1 Clusters  Cluster Id  Security Lock Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lever le verrou de sécurité d'un cluster",
    "tags": [
     "services"
    ],
    "description": "Lève le verrou : les nouvelles opérations sont à nouveau acceptées. Renvoie `cluster_id` et `security_lock` (nul). Permission `pki:manage` (rôle de sécurité). Audité en `cluster.security_unlock`."
   },
   "post": {
    "operationId": "lock_cluster_api_v1_clusters__cluster_id__security_lock_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LockBody"
       }
      }
     },
     "required": true,
     "description": "Raison du verrouillage."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Lock Cluster Api V1 Clusters  Cluster Id  Security Lock Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Poser le verrou de sécurité d'un cluster",
    "tags": [
     "services"
    ],
    "description": "Verrouille le cluster après un incident de sécurité : toute nouvelle opération est refusée avec `423 cluster_security_locked`, sauf une suspension de sécurité ou un arrêt d'urgence, jusqu'à ce qu'un administrateur de la sécurité le lève. Renvoie `cluster_id` et `security_lock`. Permission `pki:manage`. Audité en `cluster.security_lock`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services": {
   "get": {
    "operationId": "list_services_api_v1_clusters__cluster_id__services_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response List Services Api V1 Clusters  Cluster Id  Services Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les services d'un cluster",
    "tags": [
     "services"
    ],
    "description": "Chaque service du cluster avec son `display_name`, `mode`, `method`, `managed`, `state`, `health`, `version`, ses composants et leurs machines, les machines dont la configuration est périmée, `config_version`, les alertes ouvertes, l'état administratif, `delivery` (`install` ou `connect`) et la connexion. Permission `cluster:read`."
   },
   "post": {
    "operationId": "install_services_api_v1_clusters__cluster_id__services_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ServicesBody"
       }
      }
     },
     "required": true,
     "description": "Services à installer avec leurs placements et leur configuration."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Install Services Api V1 Clusters  Cluster Id  Services Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Installer des services",
    "tags": [
     "services"
    ],
    "description": "Valide, contrôle et planifie l'installation d'un ou plusieurs services, puis crée l'opération. Renvoie\n`operation_id` et les contrôles `ha`.\n\n1. la demande est validée comme avec `/validate` ; toute erreur donne `422 services_invalid`, `config_invalid`\n   ou `topology_invalid` (ou `409 sandbox_only_service` dans un cluster de production) ;\n2. avec `auto_dependencies`, les dépendances manquantes sont ajoutées à l'installation ;\n3. les contrôles de haute disponibilité doivent réussir ou être acquittés (`ha_acknowledged`) ;\n4. le profil et le verrou de sécurité du cluster doivent l'autoriser ;\n5. l'opération installe les services dans l'ordre des dépendances, les configure, les démarre (`start`) et\n   exécute leur contrôle de service (`service_check`).\n\nPermission `services:install`. Audité en `service.install`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/connect": {
   "post": {
    "description": "Enregistre un de vos services existants selon la section `connect` de son descripteur du catalogue : aucune machine, jamais installé, démarré ni arrêté par le Manager. Le contrôle de santé et le contrôle authentifié de la méthode s'exécutent d'abord ; des identifiants refusés donnent `422 connect_invalid`. Dans un cluster de production ou de préproduction, HTTPS, un certificat vérifié et une méthode autorisée sont exigés. Les champs secrets sont scellés. Renvoie la synthèse du service (`201`). Permission `services:install`. Audité en `service.connect`.",
    "operationId": "connect_service_api_v1_clusters__cluster_id__services_connect_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ConnectBody"
       }
      }
     },
     "required": true,
     "description": "Adresse, authentification et réglages TLS du service existant."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Connect Service Api V1 Clusters  Cluster Id  Services Connect Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Connecter un service existant",
    "tags": [
     "services"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/services/connect/validate": {
   "post": {
    "description": "Essai à blanc d'une connexion : contrôle la demande selon le descripteur, puis exécute le contrôle de santé et le contrôle authentifié de la méthode. Renvoie le verdict avec les erreurs ; rien n'est enregistré. Permission `services:install`.",
    "operationId": "validate_connect_api_v1_clusters__cluster_id__services_connect_validate_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ConnectBody"
       }
      }
     },
     "required": true,
     "description": "Même corps qu'une connexion."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Validate Connect Api V1 Clusters  Cluster Id  Services Connect Validate Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Contrôler une connexion avant de l'enregistrer",
    "tags": [
     "services"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/services/validate": {
   "post": {
    "operationId": "validate_services_api_v1_clusters__cluster_id__services_validate_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ServicesBody"
       }
      }
     },
     "required": true,
     "description": "Même corps qu'une installation."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Validate Services Api V1 Clusters  Cluster Id  Services Validate Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Valider une installation sans l'exécuter",
    "tags": [
     "services"
    ],
    "description": "Contrôle une demande d'installation et la prévisualise sans rien modifier. Renvoie `ok`, `errors` (chacune avec `service`, `field`, `code`, `message_i18n`), `plan` (`order`, nombre d'étapes `stages`, `steps`), `ha` (contrôles de haute disponibilité) et `dependencies` (`items` et `added`). Permission `services:install`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}": {
   "delete": {
    "operationId": "uninstall_service_api_v1_clusters__cluster_id__services__service__delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "query",
      "name": "purge_data",
      "required": false,
      "schema": {
       "default": false,
       "title": "Purge Data",
       "type": "boolean"
      },
      "description": "Supprimer aussi les répertoires de données du service. Irréversible. Faux par défaut."
     },
     {
      "in": "query",
      "name": "approval_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Approval Id"
      },
      "description": "Identifiant de l'approbation accordée pour cette désinstallation (banking-secure). Omettez-le pour utiliser l'approbation qui vous a été accordée, s'il y en a une."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Uninstall Service Api V1 Clusters  Cluster Id  Services  Service  Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Désinstaller un service",
    "tags": [
     "services"
    ],
    "description": "Lance l'opération de désinstallation (arrêt, retrait des paquets et, avec `purge_data`, des données) et renvoie `operation_id`. Permission `services:install`. Erreurs : `409 service_in_use` (consommateurs listés dans `details.consumers`), `409 service_connected` (utilisez `DELETE .../connection`), `409 approval_required` sous `banking-secure`. Audité en `service.uninstall`."
   },
   "get": {
    "operationId": "get_service_api_v1_clusters__cluster_id__services__service__get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Service Api V1 Clusters  Cluster Id  Services  Service  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un service",
    "tags": [
     "services"
    ],
    "description": "La synthèse du service plus ses `host_components` (chaque instance avec son état, sa santé et sa version), ses liaisons `bindings` vers d'autres services et ses liens rapides `quick_links` (consoles web issues du catalogue). Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/actions": {
   "post": {
    "operationId": "service_action_api_v1_clusters__cluster_id__services__service__actions_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ActionBody"
       }
      }
     },
     "required": true,
     "description": "Action, périmètre et découpage par lots."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Service Action Api V1 Clusters  Cluster Id  Services  Service  Actions Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exécuter une action sur un service",
    "tags": [
     "services"
    ],
    "description": "Lance une opération qui exécute l'action sur les composants et machines choisis (tous par défaut) et renvoie `operation_id`. Un redémarrage progressif traite `batch_size` machines à la fois. `reinstall` exige aussi `services:install`. Permission `services:operate`. Erreurs : `409 service_unmanaged`, `409 service_check_missing`, `409 smoke_test_missing`, `404 host_component_not_found`, `409 approval_required`. Audité en `service.<action>`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/checks": {
   "get": {
    "operationId": "service_checks_api_v1_clusters__cluster_id__services__service__checks_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Service Checks Api V1 Clusters  Cluster Id  Services  Service  Checks Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire les contrôles d'un service",
    "tags": [
     "lifecycle"
    ],
    "description": "Vivacité de chaque instance (contrôles de santé de l'agent), disponibilité (contrôle de service) et test fonctionnel, chacun avec son statut, ses résultats, son erreur et son heure. Seul un test fonctionnel réussi prouve que le service fonctionne. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/client-config": {
   "get": {
    "operationId": "client_config_api_v1_clusters__cluster_id__services__service__client_config_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Client Config Api V1 Clusters  Cluster Id  Services  Service  Client Config Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la configuration cliente d'un service",
    "tags": [
     "services"
    ],
    "description": "Fichiers dont un client a besoin pour joindre le service (`path`, `content`, `mode`), produits à partir du catalogue avec les secrets masqués. `files` vide quand le service n'en a pas. Permission `configs:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/commands/{command}": {
   "post": {
    "operationId": "service_command_api_v1_clusters__cluster_id__services__service__commands__command__post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "path",
      "name": "command",
      "required": true,
      "schema": {
       "title": "Command",
       "type": "string"
      },
      "description": "Nom de la commande, tel que déclaré par le service du catalogue."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CommandBody"
       }
      }
     },
     "required": true,
     "description": "Paramètres de la commande et machine cible facultative."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Service Command Api V1 Clusters  Cluster Id  Services  Service  Commands  Command  Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exécuter une commande personnalisée d'un service",
    "tags": [
     "services"
    ],
    "description": "Lance une opération qui exécute une commande déclarée par le service du catalogue (par exemple un nettoyage ou un rééquilibrage) avec ses paramètres et renvoie `operation_id`. Permission `services:operate`. Erreurs : `409 service_unmanaged`, `404 command_unknown`, `422 command_params_invalid`, `404 host_component_not_found`. Audité en `service.command` (noms des paramètres uniquement)."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/components": {
   "post": {
    "operationId": "add_component_api_v1_clusters__cluster_id__services__service__components_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ComponentBody"
       }
      }
     },
     "required": true,
     "description": "Composant et machines cibles."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Add Component Api V1 Clusters  Cluster Id  Services  Service  Components Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Ajouter des instances de composant sur des machines",
    "tags": [
     "services"
    ],
    "description": "Lance une opération qui installe le composant sur les machines fournies (démarré sauf si le service est arrêté) et renvoie `operation_id` avec les contrôles `ha`. Permission `services:install`. Erreurs : `409 service_unmanaged`, `404 component_unknown`, `422 topology_invalid`, `409 ha_requirements_not_met` ou `ha_acknowledgement_required`. Audité en `service.component_add`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/components/{component}/hosts/{host_id}": {
   "delete": {
    "operationId": "remove_component_api_v1_clusters__cluster_id__services__service__components__component__hosts__host_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "path",
      "name": "component",
      "required": true,
      "schema": {
       "title": "Component",
       "type": "string"
      },
      "description": "Nom du composant, tel que déclaré par le service du catalogue, par exemple `server`."
     },
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     },
     {
      "in": "query",
      "name": "ha_acknowledged",
      "required": false,
      "schema": {
       "default": false,
       "title": "Ha Acknowledged",
       "type": "boolean"
      },
      "description": "Accepter que le changement laisse le service en dessous de ses exigences de haute disponibilité. Faux par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Remove Component Api V1 Clusters  Cluster Id  Services  Service  Components  Component  Hosts  Host Id  Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Retirer une instance de composant d'une machine",
    "tags": [
     "services"
    ],
    "description": "Lance une opération qui arrête et retire le composant de cette machine et renvoie `operation_id` avec les contrôles `ha`. Permission `services:install`. Erreurs : `404 host_component_not_found`, `422 topology_invalid` (la topologie enfreindrait les règles du service), `409 ha_requirements_not_met` ou `ha_acknowledgement_required`. Audité en `service.component_remove`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/components/{component}/hosts/{host_id}/actions": {
   "post": {
    "operationId": "host_component_action_api_v1_clusters__cluster_id__services__service__components__component__hosts__host_id__actions_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "path",
      "name": "component",
      "required": true,
      "schema": {
       "title": "Component",
       "type": "string"
      },
      "description": "Nom du composant."
     },
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/HostActionBody"
       }
      }
     },
     "required": true,
     "description": "Action à exécuter."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Component Action Api V1 Clusters  Cluster Id  Services  Service  Components  Component  Hosts  Host Id  Actions Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exécuter une action sur une instance de composant",
    "tags": [
     "services"
    ],
    "description": "Comme les actions de service, limité au composant sur une machine. Renvoie `operation_id`. Permission `services:operate` (`services:install` pour `reinstall`). Audité en `service.<action>`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config": {
   "get": {
    "operationId": "get_config_api_v1_clusters__cluster_id__services__service__config_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Config Api V1 Clusters  Cluster Id  Services  Service  Config Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la configuration d'un service",
    "tags": [
     "configs"
    ],
    "description": "Renvoie la `version` courante, les propriétés effectives `properties` (valeurs par défaut fusionnées, secrets masqués), les définitions des paramètres `params` et les `sections` du catalogue, les recommandations `recommendations` calculées pour les machines, les groupes de configuration `groups` et les `stale_hosts`. Permission `configs:read` ; `409 service_unmanaged` pour un service que le Manager ne gère pas."
   },
   "put": {
    "operationId": "put_config_api_v1_clusters__cluster_id__services__service__config_put",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ConfigBody"
       }
      }
     },
     "required": true,
     "description": "Propriétés modifiées et une note."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Config Api V1 Clusters  Cluster Id  Services  Service  Config Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enregistrer une nouvelle version de configuration",
    "tags": [
     "configs"
    ],
    "description": "Fusionne les propriétés envoyées avec les valeurs courantes, les valide selon les définitions des paramètres et enregistre une nouvelle version. Renvoie `version` et `stale_hosts`. Permission `configs:edit`. Erreurs : `422 config_invalid` (avec les champs), `409 service_unmanaged`. Audité en `config.update` (noms des paramètres uniquement)."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/apply": {
   "post": {
    "operationId": "apply_api_v1_clusters__cluster_id__services__service__config_apply_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__configs__ApplyBody"
       }
      }
     },
     "required": true,
     "description": "Politique de redémarrage de l'application."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Apply Api V1 Clusters  Cluster Id  Services  Service  Config Apply Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Appliquer la configuration aux machines",
    "tags": [
     "configs"
    ],
    "description": "Lance l'opération qui écrit la configuration courante sur les machines et redémarre les composants comme demandé (`rolling` par lots de `batch_size`, `all` ou `none`). Renvoie `operation_id`. Permission `configs:edit`. Sous `banking-secure`, une approbation est nécessaire (`409 approval_required`). Audité en `config.apply`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/diff": {
   "get": {
    "operationId": "diff_api_v1_clusters__cluster_id__services__service__config_diff_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "query",
      "name": "from",
      "required": true,
      "schema": {
       "minimum": 1,
       "title": "From",
       "type": "integer"
      },
      "description": "Numéro de la version la plus ancienne (1 ou plus)."
     },
     {
      "in": "query",
      "name": "to",
      "required": true,
      "schema": {
       "minimum": 1,
       "title": "To",
       "type": "integer"
      },
      "description": "Numéro de la version la plus récente (1 ou plus)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response Diff Api V1 Clusters  Cluster Id  Services  Service  Config Diff Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Comparer deux versions de configuration",
    "tags": [
     "configs"
    ],
    "description": "Liste les paramètres dont la valeur diffère entre les deux versions, chacun avec `param`, `secret`, `from` et `to` ; pour un secret, seul le fait qu'il soit défini est indiqué. Permission `configs:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/groups": {
   "get": {
    "operationId": "list_groups_api_v1_clusters__cluster_id__services__service__config_groups_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response List Groups Api V1 Clusters  Cluster Id  Services  Service  Config Groups Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les groupes de configuration d'un service",
    "tags": [
     "configs"
    ],
    "description": "Chaque groupe de configuration avec son `id`, son `name`, ses `hosts`, ses surcharges `overrides` (secrets masqués) et `updated_at`. Permission `configs:read`."
   },
   "post": {
    "operationId": "create_group_api_v1_clusters__cluster_id__services__service__config_groups_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GroupBody"
       }
      }
     },
     "required": true,
     "description": "Nom, machines et surcharges du groupe."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Group Api V1 Clusters  Cluster Id  Services  Service  Config Groups Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un groupe de configuration",
    "tags": [
     "configs"
    ],
    "description": "Crée un groupe de machines avec des valeurs qui surchargent la configuration du service sur ces machines. Une machine appartient à un groupe au plus. Permission `configs:edit`. Erreurs : `409 config_group_name_taken`, `409 config_group_host_conflict`, `422 config_group_host_invalid`, `422 config_invalid`. Audité en `config_group.create`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/groups/{group_id}": {
   "delete": {
    "operationId": "delete_group_api_v1_clusters__cluster_id__services__service__config_groups__group_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "path",
      "name": "group_id",
      "required": true,
      "schema": {
       "title": "Group Id",
       "type": "string"
      },
      "description": "Identifiant du groupe de configuration."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer un groupe de configuration",
    "tags": [
     "configs"
    ],
    "description": "Supprime le groupe ; ses machines reprennent la configuration du service à la prochaine application. Répond `204`. Permission `configs:edit` ; `404 config_group_not_found`. Audité en `config_group.delete`."
   },
   "put": {
    "operationId": "update_group_api_v1_clusters__cluster_id__services__service__config_groups__group_id__put",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "path",
      "name": "group_id",
      "required": true,
      "schema": {
       "title": "Group Id",
       "type": "string"
      },
      "description": "Identifiant du groupe de configuration."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GroupBody"
       }
      }
     },
     "required": true,
     "description": "Nom, machines et surcharges du groupe."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Update Group Api V1 Clusters  Cluster Id  Services  Service  Config Groups  Group Id  Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier un groupe de configuration",
    "tags": [
     "configs"
    ],
    "description": "Remplace le nom, les machines et les surcharges du groupe. Permission `configs:edit`. Erreurs : `404 config_group_not_found`, `409 config_group_name_taken`, `409 config_group_host_conflict`, `422 config_group_host_invalid`. Audité en `config_group.update`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/preview": {
   "get": {
    "operationId": "preview_api_v1_clusters__cluster_id__services__service__config_preview_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "query",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Machine de l'instance."
     },
     {
      "in": "query",
      "name": "component",
      "required": true,
      "schema": {
       "title": "Component",
       "type": "string"
      },
      "description": "Composant de l'instance."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Preview Api V1 Clusters  Cluster Id  Services  Service  Config Preview Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Prévisualiser les fichiers d'une instance de composant",
    "tags": [
     "configs"
    ],
    "description": "Produit les fichiers de configuration qu'une instance recevrait avec la version courante et ses groupes : `path`, `mode`, `owner`, `group`, `content`, secrets masqués. Rien n'est écrit. Permission `configs:read` ; `404 host_component_not_found`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/versions": {
   "get": {
    "operationId": "list_versions_api_v1_clusters__cluster_id__services__service__config_versions_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "query",
      "name": "limit",
      "required": false,
      "schema": {
       "default": 50,
       "maximum": 500,
       "minimum": 1,
       "title": "Limit",
       "type": "integer"
      },
      "description": "Nombre maximal de versions dans la page, de 1 à 500. 50 par défaut."
     },
     {
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "minimum": 1,
         "type": "integer"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cursor"
      },
      "description": "Le `next_cursor` de la page précédente : les versions inférieures à ce numéro sont renvoyées."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Versions Api V1 Clusters  Cluster Id  Services  Service  Config Versions Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les versions de configuration",
    "tags": [
     "configs"
    ],
    "description": "Versions de la configuration, de la plus récente à la plus ancienne : `version`, `note`, `restored_from`, `secret_keys`, `created_by`, `created_at`. Paginé avec `limit` et `cursor`. Permission `configs:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/versions/{version}": {
   "get": {
    "operationId": "get_version_api_v1_clusters__cluster_id__services__service__config_versions__version__get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "path",
      "name": "version",
      "required": true,
      "schema": {
       "title": "Version",
       "type": "integer"
      },
      "description": "Numéro de version, à partir de 1."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Version Api V1 Clusters  Cluster Id  Services  Service  Config Versions  Version  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une version de configuration",
    "tags": [
     "configs"
    ],
    "description": "Renvoie les valeurs d'une version (secrets masqués) avec sa note et son auteur. Permission `configs:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/config/versions/{version}/restore": {
   "post": {
    "operationId": "restore_api_v1_clusters__cluster_id__services__service__config_versions__version__restore_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "path",
      "name": "version",
      "required": true,
      "schema": {
       "title": "Version",
       "type": "integer"
      },
      "description": "Numéro de la version à restaurer."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Restore Api V1 Clusters  Cluster Id  Services  Service  Config Versions  Version  Restore Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Restaurer une version de configuration",
    "tags": [
     "configs"
    ],
    "description": "Enregistre les valeurs d'une ancienne version dans une nouvelle version (note `restore of version N`) et renvoie `version` et `stale_hosts` ; appliquez-la pour atteindre les machines. Permission `configs:edit`. Audité en `config.restore`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/connection": {
   "delete": {
    "description": "Oublie un service connecté et ses identifiants scellés ; le service lui-même n'est pas touché. Permission `services:install`. Erreurs : `409 service_not_connected`, `409 service_in_use` tant qu'un consommateur y est lié. Audité en `service.disconnect`.",
    "operationId": "disconnect_service_api_v1_clusters__cluster_id__services__service__connection_delete",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Disconnect Service Api V1 Clusters  Cluster Id  Services  Service  Connection Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Déconnecter un service connecté",
    "tags": [
     "services"
    ]
   },
   "patch": {
    "description": "Modifie l'adresse, les identifiants (rotation) ou les réglages TLS d'un service connecté ; une valeur omise conserve celle enregistrée. Avec `dry_run`, le changement est contrôlé comme une validation de connexion et n'est pas enregistré. Des identifiants refusés par le service ne sont jamais enregistrés (`422 connect_invalid`). Permission `services:install` ; `409 service_not_connected`. Audité en `service.connection.update` (noms des champs uniquement, jamais les valeurs).",
    "operationId": "update_connection_api_v1_clusters__cluster_id__services__service__connection_patch",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__services__ConnectionPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de la connexion à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Update Connection Api V1 Clusters  Cluster Id  Services  Service  Connection Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier un service connecté",
    "tags": [
     "services"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/connection/check": {
   "post": {
    "description": "Exécute le contrôle de santé et le contrôle authentifié d'un service connecté et inscrit le résultat dans son historique de supervision. Renvoie `service`, `health` et `health_details`. Permission `services:operate` ; `409 service_not_connected`.",
    "operationId": "check_connection_api_v1_clusters__cluster_id__services__service__connection_check_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Check Connection Api V1 Clusters  Cluster Id  Services  Service  Connection Check Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Contrôler un service connecté maintenant",
    "tags": [
     "services"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/dashboard": {
   "get": {
    "description": "Tableau de bord de supervision d'un service sur [`from`, `to`] (par défaut : la dernière heure) : instances, santé et derniers contrôles, disponibilité sur la période, métriques de ses machines, alertes, opérations, configuration et, pour un service connecté, sa connexion. Permission `cluster:read`.",
    "operationId": "service_dashboard_view_api_v1_clusters__cluster_id__services__service__dashboard_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     },
     {
      "in": "query",
      "name": "from",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "From"
      },
      "description": "Début de la fenêtre, ISO 8601. Par défaut : une heure avant `to`."
     },
     {
      "in": "query",
      "name": "to",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "To"
      },
      "description": "Fin de la fenêtre, ISO 8601. Par défaut : maintenant."
     },
     {
      "in": "query",
      "name": "step",
      "required": false,
      "schema": {
       "default": 60,
       "maximum": 86400,
       "minimum": 10,
       "title": "Step",
       "type": "integer"
      },
      "description": "Largeur d'une tranche de métriques en secondes, de 10 à 86400. 60 par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Service Dashboard View Api V1 Clusters  Cluster Id  Services  Service  Dashboard Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire le tableau de bord de supervision d'un service",
    "tags": [
     "services"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/logs": {
   "post": {
    "operationId": "service_logs_api_v1_clusters__cluster_id__services__service__logs_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LogsBody"
       }
      }
     },
     "required": true,
     "description": "Machine, composant et nombre de lignes."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Service Logs Api V1 Clusters  Cluster Id  Services  Service  Logs Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Récupérer les journaux d'une instance de composant",
    "tags": [
     "services"
    ],
    "description": "Lance une opération où l'agent de la machine lit les dernières lignes des journaux du composant. Renvoie `operation_id` et `task_id` : lisez les lignes avec `GET /operations/{operation_id}/tasks/{task_id}/logs`. Permission `services:operate`. Audité en `service.logs`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/mode": {
   "get": {
    "operationId": "get_mode_api_v1_clusters__cluster_id__services__service__mode_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Mode Api V1 Clusters  Cluster Id  Services  Service  Mode Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire l'état administratif d'un service",
    "tags": [
     "services"
    ],
    "description": "Renvoie l'état administratif (`enabled`, `disabled`, `maintenance`, `quarantined`, `suspended`), sa raison, l'autorisation de fonctionner (`run_authorized`) le verrou de sécurité du cluster et `modes`, l'effet documenté de chaque mode. Permission `cluster:read`."
   },
   "post": {
    "operationId": "set_mode_api_v1_clusters__cluster_id__services__service__mode_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ModeBody"
       }
      }
     },
     "required": true,
     "description": "Mode, raison et délai de vidange."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Set Mode Api V1 Clusters  Cluster Id  Services  Service  Mode Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Appliquer un mode d'exploitation à un service",
    "tags": [
     "services"
    ],
    "description": "Applique un mode et renvoie l'état administratif obtenu, son effet documenté `effect` sur les nouvelles\nrequêtes et les transactions en cours, l'`operation_id` quand le mode arrête des processus, et l'impact\ninformatif `ha` :\n\n- `drain` : nouvelles requêtes refusées, transactions terminées dans la limite de `drain_timeout_s` (300 par\n  défaut), processus arrêtés, état `maintenance` ;\n- `scale_to_zero` : processus arrêtés jusqu'à un démarrage, état `enabled` ;\n- `security_suspend` : transactions interrompues immédiatement, processus arrêtés, état `suspended`\n  (autorisation de fonctionner révoquée) ; exige `pki:manage` ;\n- `emergency_stop` : tous les composants arrêtés immédiatement, état `disabled` ;\n- `disable` : aucun changement de processus, état `disabled` (ce qui le ferait tourner est refusé) ;\n- `quarantine` : processus gelés, tout changement refusé sauf un arrêt ;\n- `maintenance` : état `maintenance`, rien n'est arrêté ;\n- `enable` : lève l'état, ne démarre jamais rien ; lever une suspension exige `pki:manage` et une raison\n  `reason`.\n\nPermission `services:operate`. Erreurs : `422 service_mode_reason_required`, `409 service_unmanaged`,\n`409 approval_required`. Audité en `service.mode.<mode>`."
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/sheet": {
   "get": {
    "description": "Chaque onglet de la fiche uniforme de composant, à partir de l'état observé : `overview`, `instances`, `config`, `dependencies`, `logs`, `metrics`, `security` (TLS, points d'accès, secrets définis ou non, ports, certificats, utilisateurs d'exécution, profil), `operations`, `upgrade`, `backups`, `audit` et `support`. Permission `cluster:read`.",
    "operationId": "component_sheet_api_v1_clusters__cluster_id__services__service__sheet_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Component Sheet Api V1 Clusters  Cluster Id  Services  Service  Sheet Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la fiche de composant d'un service",
    "tags": [
     "lifecycle"
    ]
   }
  },
  "/api/v1/clusters/{cluster_id}/services/{service}/upgrade": {
   "get": {
    "operationId": "upgrade_info_api_v1_clusters__cluster_id__services__service__upgrade_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Upgrade Info Api V1 Clusters  Cluster Id  Services  Service  Upgrade Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Vérifier si un service peut être mis à niveau",
    "tags": [
     "lifecycle"
    ],
    "description": "Renvoie `from` (version installée), `to` (version du catalogue), `allowed` avec les problèmes `problems`, `stateful`, `backup_before`, les `migrations`, la possibilité d'un retour arrière `rollback`, la matrice de mise à niveau et l'historique `history` des 50 dernières mises à niveau. Permission `cluster:read`."
   },
   "post": {
    "operationId": "start_upgrade_api_v1_clusters__cluster_id__services__service__upgrade_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "service",
      "required": true,
      "schema": {
       "title": "Service",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql` ou `keycloak`."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/UpgradeBody"
       }
      }
     },
     "required": true,
     "description": "Machine canari et approbation facultative."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Start Upgrade Api V1 Clusters  Cluster Id  Services  Service  Upgrade Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Mettre à niveau un service",
    "tags": [
     "lifecycle"
    ],
    "description": "Met à niveau le service vers la version du catalogue en une seule opération :\n\n1. compatibilité et contrôles préalables ;\n2. sauvegarde d'un service à état ;\n3. la machine canari : récupération, arrêt, crochets de migration, nouveaux fichiers et unités, démarrage,\n   contrôles de santé, test fonctionnel ;\n4. les autres machines une par une, puis le contrôle de service et le test fonctionnel, et la nouvelle version\n   enregistrée.\n\nLes tâches qui ramènent la version précédente sont scellées avant toute exécution. Renvoie `operation_id` et\n`upgrade_id`. Permission `services:install` ; une approbation sous `banking-secure`. Audité en\n`service.upgrade`."
   }
  },
  "/api/v1/clusters/{cluster_id}/ssh-policy": {
   "get": {
    "operationId": "get_ssh_policy_api_v1_clusters__cluster_id__ssh_policy_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Ssh Policy Api V1 Clusters  Cluster Id  Ssh Policy Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la politique SSH d'un cluster",
    "tags": [
     "node-security"
    ],
    "description": "Renvoie `allow_rsa`, `rsa_min_bits`, `approved_user_cas`, `bootstrap_user`, `spiffe_trust_domain` et `rotate_before_days`. Permission `cluster:read` ; `404 cluster_not_found`."
   },
   "put": {
    "operationId": "put_ssh_policy_api_v1_clusters__cluster_id__ssh_policy_put",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SshPolicyIn"
       }
      }
     },
     "required": true,
     "description": "La politique SSH."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Ssh Policy Api V1 Clusters  Cluster Id  Ssh Policy Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Définir la politique SSH d'un cluster",
    "tags": [
     "node-security"
    ],
    "description": "Remplace la politique SSH et la renvoie. Le compte d'amorçage et chaque clé d'autorité approuvée sont contrôlés d'abord. Permission `hosts:manage`. Erreurs : `422 ssh_policy_refused` (clé faible ou invalide, compte refusé), `404 cluster_not_found`. Audité en `cluster.ssh_policy`."
   }
  },
  "/api/v1/clusters/{cluster_id}/summary": {
   "get": {
    "operationId": "summary_api_v1_clusters__cluster_id__summary_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Summary Api V1 Clusters  Cluster Id  Summary Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la synthèse d'un cluster",
    "tags": [
     "clusters"
    ],
    "description": "Compteurs de la page d'accueil d'un cluster : machines par état, services par état et par santé, alertes ouvertes (`critical`, `warning`) et opérations en cours. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/support-bundles": {
   "get": {
    "operationId": "list_bundles_api_v1_clusters__cluster_id__support_bundles_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response List Bundles Api V1 Clusters  Cluster Id  Support Bundles Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les archives de support d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Les 50 dernières archives avec leur statut, leur taille, leur SHA-256, leur contenu, leurs masquages et leur opération. Permission `cluster:manage`."
   },
   "post": {
    "operationId": "create_bundle_api_v1_clusters__cluster_id__support_bundles_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Bundle Api V1 Clusters  Cluster Id  Support Bundles Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Construire une archive de support",
    "tags": [
     "lifecycle"
    ],
    "description": "Lance une opération qui rassemble les fins de journaux, le profil, les machines, les versions et états des services, la configuration produite, les alertes ouvertes, les constats de dérive et l'historique des opérations dans une archive tar.gz où chaque secret et chaque donnée personnelle est masqué. Renvoie `operation_id` et `bundle_id`. Permission `cluster:manage`. Audité en `support.bundle`."
   }
  },
  "/api/v1/clusters/{cluster_id}/support-bundles/{bundle_id}/download": {
   "get": {
    "operationId": "download_bundle_api_v1_clusters__cluster_id__support_bundles__bundle_id__download_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "bundle_id",
      "required": true,
      "schema": {
       "title": "Bundle Id",
       "type": "string"
      },
      "description": "Identifiant de l'archive de support."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Télécharger une archive de support",
    "tags": [
     "lifecycle"
    ],
    "description": "Renvoie l'archive `navin-support-<heure>.tar.gz` d'une archive réussie. Permission `cluster:manage` ; `404 bundle_not_found`. Audité en `support.bundle_download`."
   }
  },
  "/api/v1/clusters/{cluster_id}/topology": {
   "get": {
    "operationId": "topology_api_v1_clusters__cluster_id__topology_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Topology Api V1 Clusters  Cluster Id  Topology Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire la topologie d'un cluster",
    "tags": [
     "clusters"
    ],
    "description": "Machines (`id`, `hostname`, `state`, `rack`) et instances de composants (`service`, `component`, `host_id`, `state`, `health`) du cluster, pour représenter où tourne chaque composant. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/upgrades": {
   "get": {
    "operationId": "list_upgrades_api_v1_clusters__cluster_id__upgrades_get",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response List Upgrades Api V1 Clusters  Cluster Id  Upgrades Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les mises à niveau d'un cluster",
    "tags": [
     "lifecycle"
    ],
    "description": "Les 200 dernières mises à niveau de chaque service avec leurs versions, leur statut, leur sauvegarde et leurs opérations. Permission `cluster:read`."
   }
  },
  "/api/v1/clusters/{cluster_id}/upgrades/{upgrade_id}/restore": {
   "post": {
    "operationId": "restore_upgrade_api_v1_clusters__cluster_id__upgrades__upgrade_id__restore_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "upgrade_id",
      "required": true,
      "schema": {
       "title": "Upgrade Id",
       "type": "string"
      },
      "description": "Identifiant de la mise à niveau."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ApprovalOnly"
       }
      }
     },
     "required": true,
     "description": "Approbation facultative."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Restore Upgrade Api V1 Clusters  Cluster Id  Upgrades  Upgrade Id  Restore Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Restaurer la sauvegarde prise avant une mise à niveau",
    "tags": [
     "lifecycle"
    ],
    "description": "Lance l'opération qui ramène la version précédente et ses données depuis la sauvegarde prise avant la mise à niveau, quand un retour arrière n'est pas possible. Renvoie `operation_id`. Permission `services:install` ; `404 upgrade_not_found` ; une approbation sous `banking-secure`. Audité en `service.restore`."
   }
  },
  "/api/v1/clusters/{cluster_id}/upgrades/{upgrade_id}/rollback": {
   "post": {
    "operationId": "rollback_api_v1_clusters__cluster_id__upgrades__upgrade_id__rollback_post",
    "parameters": [
     {
      "in": "path",
      "name": "cluster_id",
      "required": true,
      "schema": {
       "title": "Cluster Id",
       "type": "string"
      },
      "description": "Identifiant du cluster (un environnement de machines ou un cluster Kubernetes géré par le Navin Manager)."
     },
     {
      "in": "path",
      "name": "upgrade_id",
      "required": true,
      "schema": {
       "title": "Upgrade Id",
       "type": "string"
      },
      "description": "Identifiant de la mise à niveau."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ApprovalOnly"
       }
      }
     },
     "required": true,
     "description": "Approbation facultative."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Rollback Api V1 Clusters  Cluster Id  Upgrades  Upgrade Id  Rollback Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Annuler une mise à niveau",
    "tags": [
     "lifecycle"
    ],
    "description": "Lance l'opération qui rejoue les tâches scellées de la version précédente et rétablit la version de configuration précédente. Refusé quand le descripteur le déclare impossible ou qu'une migration irréversible a eu lieu (utilisez alors la restauration). Renvoie `operation_id`. Permission `services:install` ; `404 upgrade_not_found`. Audité en `service.rollback`."
   }
  },
  "/api/v1/connections": {
   "get": {
    "operationId": "list_connections_api_v1_connections_get",
    "parameters": [
     {
      "in": "query",
      "name": "limit",
      "required": false,
      "schema": {
       "default": 50,
       "maximum": 500,
       "minimum": 1,
       "title": "Limit",
       "type": "integer"
      },
      "description": "Nombre maximal de connexions dans la page, de 1 à 500. 50 par défaut."
     },
     {
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 200,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cursor"
      },
      "description": "Le `next_cursor` de la page précédente (le dernier nom, en minuscules). Omettez-le pour la première page."
     },
     {
      "in": "query",
      "name": "type",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "pattern": "^(clickhouse|elasticsearch|http|kafka|ldap|mysql|nats|oidc|opensearch|postgresql|qdrant|redis|s3|smtp|syslog)$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Type"
      },
      "description": "Uniquement les connexions de ce type, par exemple `postgresql`, `s3` ou `kafka`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Connections Api V1 Connections Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les connexions externes",
    "tags": [
     "connections"
    ],
    "description": "Connexions triées par nom : `id`, `name`, `type`, `params`, `secret` (défini ou non), `description`, `last_test`, `created_at`, `updated_at`. Paginé avec `limit` et `cursor`. Permission `connections:read`."
   },
   "post": {
    "operationId": "create_connection_api_v1_connections_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ConnectionCreate"
       }
      }
     },
     "required": true,
     "description": "Nom, type, paramètres et secret de la connexion."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Connection Api V1 Connections Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer une connexion",
    "tags": [
     "connections"
    ],
    "description": "Enregistre une connexion et la renvoie (`201`). Permission `connections:manage`. Erreurs : `409 connection_name_taken`, `422 connection_params_invalid` (champs dans `details.fields`). Audité en `connection.create`."
   }
  },
  "/api/v1/connections/validate": {
   "post": {
    "description": "Essai à blanc : contrôle les paramètres du type, puis exécute le vrai test du protocole avec le secret fourni. Renvoie le résultat du test (`ok`, `error_code`, `latency_ms`...). Rien n'est enregistré ; le secret n'est jamais renvoyé ni audité. Permission `connections:manage`. Audité en `connection.validate`.",
    "operationId": "validate_connection_api_v1_connections_validate_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ConnectionValidate"
       }
      }
     },
     "required": true,
     "description": "Type, paramètres et secret à tester."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Validate Connection Api V1 Connections Validate Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Tester une connexion avant de l'enregistrer",
    "tags": [
     "connections"
    ]
   }
  },
  "/api/v1/connections/{connection_id}": {
   "delete": {
    "operationId": "delete_connection_api_v1_connections__connection_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "connection_id",
      "required": true,
      "schema": {
       "title": "Connection Id",
       "type": "string"
      },
      "description": "Identifiant de la connexion."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer une connexion",
    "tags": [
     "connections"
    ],
    "description": "Supprime une connexion à laquelle aucun service n'est lié. Répond `204`. Permission `connections:manage`. Erreurs : `404 connection_not_found`, `409 connection_in_use` (services dans `details.services`). Audité en `connection.delete`."
   },
   "get": {
    "operationId": "get_connection_api_v1_connections__connection_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "connection_id",
      "required": true,
      "schema": {
       "title": "Connection Id",
       "type": "string"
      },
      "description": "Identifiant de la connexion."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Connection Api V1 Connections  Connection Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une connexion",
    "tags": [
     "connections"
    ],
    "description": "Renvoie une connexion, son secret masqué. Permission `connections:read` ; `404 connection_not_found`."
   },
   "patch": {
    "operationId": "patch_connection_api_v1_connections__connection_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "connection_id",
      "required": true,
      "schema": {
       "title": "Connection Id",
       "type": "string"
      },
      "description": "Identifiant de la connexion."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__connections__ConnectionPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de la connexion à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch Connection Api V1 Connections  Connection Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier une connexion",
    "tags": [
     "connections"
    ],
    "description": "Modifie les champs fournis ; le type ne peut pas changer. Renvoie la connexion. Permission `connections:manage`. Erreurs : `404 connection_not_found`, `409 connection_name_taken`, `422 connection_params_invalid`. Audité en `connection.update`."
   }
  },
  "/api/v1/connections/{connection_id}/test": {
   "post": {
    "operationId": "test_connection_api_v1_connections__connection_id__test_post",
    "parameters": [
     {
      "in": "path",
      "name": "connection_id",
      "required": true,
      "schema": {
       "title": "Connection Id",
       "type": "string"
      },
      "description": "Identifiant de la connexion."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Test Connection Api V1 Connections  Connection Id  Test Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Tester une connexion enregistrée",
    "tags": [
     "connections"
    ],
    "description": "Exécute le test du protocole avec les paramètres et le secret enregistrés, conserve le résultat avec son heure dans `last_test` et le renvoie (`ok`, `error_code`, `latency_ms`...). Permission `connections:manage` ; `404 connection_not_found`. Audité en `connection.test`."
   }
  },
  "/api/v1/dr-exercises": {
   "get": {
    "operationId": "list_exercises_api_v1_dr_exercises_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Exercises Api V1 Dr Exercises Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les exercices et bascules de reprise",
    "tags": [
     "backup"
    ],
    "description": "Les 200 derniers exercices et demandes de bascule avec leur rapport : statut, demandeur, approbateur, RPO et RTO observés comparés aux objectifs, preuves. Permission `backup:read`."
   },
   "post": {
    "operationId": "request_exercise_api_v1_dr_exercises_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ExerciseIn"
       }
      }
     },
     "required": true,
     "description": "Le plan de reprise."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Request Exercise Api V1 Dr Exercises Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Demander un exercice de reprise",
    "tags": [
     "backup"
    ],
    "description": "Enregistre une demande d'exercice pour un plan (`201`) ; une autre personne doit l'approuver avant qu'il puisse être clôturé. Permission `dr:manage` ; `404 dr_plan_not_found`. Audité en `dr.exercise.request`."
   }
  },
  "/api/v1/dr-exercises/{exercise_id}/approve": {
   "post": {
    "operationId": "approve_exercise_api_v1_dr_exercises__exercise_id__approve_post",
    "parameters": [
     {
      "in": "path",
      "name": "exercise_id",
      "required": true,
      "schema": {
       "title": "Exercise Id",
       "type": "string"
      },
      "description": "Identifiant de l'exercice ou de la demande de bascule."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Approve Exercise Api V1 Dr Exercises  Exercise Id  Approve Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Approuver un exercice ou une bascule de reprise",
    "tags": [
     "backup"
    ],
    "description": "Approuve la demande ; l'approbateur ne doit pas être le demandeur. Permission `dr:manage`. Erreurs : `403 restore_self_approval`, `404 dr_exercise_not_found`. Audité en `dr.approve`."
   }
  },
  "/api/v1/dr-exercises/{exercise_id}/complete": {
   "post": {
    "operationId": "complete_exercise_api_v1_dr_exercises__exercise_id__complete_post",
    "parameters": [
     {
      "in": "path",
      "name": "exercise_id",
      "required": true,
      "schema": {
       "title": "Exercise Id",
       "type": "string"
      },
      "description": "Identifiant de l'exercice ou de la demande de bascule."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CompleteIn"
       }
      }
     },
     "required": true,
     "description": "Mesures observées, résultat et preuves."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Complete Exercise Api V1 Dr Exercises  Exercise Id  Complete Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Clôturer un exercice de reprise avec ses mesures",
    "tags": [
     "backup"
    ],
    "description": "Enregistre le RPO et le RTO observés, le résultat et les preuves d'un exercice ou d'une bascule approuvés. Permission `dr:manage` ; `409 dr_not_approved`. Audité en `dr.complete`."
   }
  },
  "/api/v1/dr-failovers/request": {
   "post": {
    "operationId": "request_failover_api_v1_dr_failovers_request_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ExerciseIn"
       }
      }
     },
     "required": true,
     "description": "Le plan de reprise."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Request Failover Api V1 Dr Failovers Request Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Demander une bascule de reprise",
    "tags": [
     "backup"
    ],
    "description": "Enregistre une demande de bascule réelle pour un plan (`201`) ; elle suit la même approbation et la même clôture qu'un exercice. Permission `dr:manage` ; `404 dr_plan_not_found`. Audité en `dr.failover.request`."
   }
  },
  "/api/v1/dr-plans": {
   "get": {
    "operationId": "list_dr_plans_api_v1_dr_plans_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Dr Plans Api V1 Dr Plans Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les plans de reprise",
    "tags": [
     "backup"
    ],
    "description": "Chaque plan de reprise après sinistre avec son mode, ses sites primaire et secondaire, ses objectifs de RPO et RTO et sa séquence. Permission `backup:read`."
   },
   "post": {
    "operationId": "create_dr_plan_api_v1_dr_plans_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DrPlanIn"
       }
      }
     },
     "required": true,
     "description": "Nom, mode, sites, objectifs et séquence du plan."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Dr Plan Api V1 Dr Plans Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un plan de reprise",
    "tags": [
     "backup"
    ],
    "description": "Crée un plan entre un site primaire et un site secondaire (créés s'ils sont inconnus) et le renvoie (`201`). Permission `dr:manage`. Erreurs : `422 dr_mode_invalid`, `422 dr_same_site`. Audité en `dr.plan.create`."
   }
  },
  "/api/v1/dr-plans/{plan_id}/switch-plan": {
   "post": {
    "operationId": "traffic_switch_plan_api_v1_dr_plans__plan_id__switch_plan_post",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de reprise."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SwitchIn"
       }
      }
     },
     "required": true,
     "description": "Site vers lequel basculer le trafic."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Traffic Switch Plan Api V1 Dr Plans  Plan Id  Switch Plan Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Planifier une bascule de trafic vers un site",
    "tags": [
     "backup"
    ],
    "description": "Exécute les contrôles de santé des bascules déclarées sur le site cible (vraies requêtes HTTP) et renvoie les changements DNS et d'équilibreur de charge dans l'ordre. Rien n'est appliqué : l'application exige l'API de votre DNS et de votre équilibreur de charge (statut `requires_client_provider`). Permission `dr:manage`."
   }
  },
  "/api/v1/dr-plans/{plan_id}/targets": {
   "put": {
    "operationId": "set_dr_targets_api_v1_dr_plans__plan_id__targets_put",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de reprise."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TargetsIn"
       }
      }
     },
     "required": true,
     "description": "Objectifs de RPO et RTO."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Set Dr Targets Api V1 Dr Plans  Plan Id  Targets Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Fixer les objectifs de RPO et RTO d'un plan de reprise",
    "tags": [
     "backup"
    ],
    "description": "Fixe vos propres objectifs de reprise (secondes, ou nul pour effacer) ; le produit ne les propose jamais. Permission `dr:manage` ; `404 dr_plan_not_found`. Audité en `dr.plan.targets`."
   }
  },
  "/api/v1/dr-plans/{plan_id}/traffic": {
   "get": {
    "operationId": "list_traffic_api_v1_dr_plans__plan_id__traffic_get",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de reprise."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Traffic Api V1 Dr Plans  Plan Id  Traffic Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les bascules de trafic d'un plan de reprise",
    "tags": [
     "backup"
    ],
    "description": "Enregistrements DNS et équilibreurs de charge déclarés pour le plan, avec leur valeur par site et leur contrôle de santé. Permission `backup:read`."
   },
   "post": {
    "operationId": "declare_traffic_api_v1_dr_plans__plan_id__traffic_post",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de reprise."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TrafficIn"
       }
      }
     },
     "required": true,
     "description": "Type, nom et spécification de la bascule."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Declare Traffic Api V1 Dr Plans  Plan Id  Traffic Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Déclarer une bascule de trafic d'un plan de reprise",
    "tags": [
     "backup"
    ],
    "description": "Déclare (ou remplace) un enregistrement DNS ou un équilibreur de charge du plan avec sa valeur par site et le contrôle de santé qui doit réussir avant une bascule (`201`). Permission `dr:manage`. Erreurs : `422 dr_traffic_invalid`, `404 dr_plan_not_found`. Audité en `dr.traffic.declare`."
   }
  },
  "/api/v1/dr-sites": {
   "get": {
    "operationId": "list_sites_api_v1_dr_sites_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Sites Api V1 Dr Sites Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les sites de reprise",
    "tags": [
     "backup"
    ],
    "description": "Sites connus des plans de reprise après sinistre, avec leur rôle (`primary` ou `secondary`). Les sites sont créés par les plans. Permission `backup:read`."
   }
  },
  "/api/v1/ha-groups": {
   "get": {
    "operationId": "list_groups_api_v1_ha_groups_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Groups Api V1 Ha Groups Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les groupes de haute disponibilité",
    "tags": [
     "availability"
    ],
    "description": "Groupes de chaque cluster que l'appelant peut lire (`cluster:read`), triés par nom, chacun avec ses membres, son état de HA et son dernier test."
   },
   "post": {
    "operationId": "create_group_api_v1_ha_groups_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GroupIn"
       }
      }
     },
     "required": true,
     "description": "Le groupe, sa politique et ses membres."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Group Api V1 Ha Groups Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un groupe de haute disponibilité",
    "tags": [
     "availability"
    ],
    "description": "Déclare un groupe et ses membres et le renvoie (`201`). Le profil doit être satisfait par `desired_replicas` et `fault_domains`, deux membres ne peuvent pas partager une machine. Permission `cluster:manage` sur le cluster. Erreurs : `422 ha_profile_unmet`, `422 ha_mode_unsupported`, `422 ha_members_same_host`, `404 cluster_not_found`, `404 host_not_found`. Audité en `ha_group.create`."
   }
  },
  "/api/v1/ha-groups/{group_id}": {
   "get": {
    "operationId": "get_group_api_v1_ha_groups__group_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "group_id",
      "required": true,
      "schema": {
       "title": "Group Id",
       "type": "string"
      },
      "description": "Identifiant du groupe de HA."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Group Api V1 Ha Groups  Group Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un groupe de haute disponibilité",
    "tags": [
     "availability"
    ],
    "description": "Renvoie le groupe avec ses membres, son état de HA et son dernier test. Permission `cluster:read` sur son cluster ; `404 ha_group_not_found`."
   }
  },
  "/api/v1/ha-groups/{group_id}/operations": {
   "get": {
    "operationId": "operations_api_v1_ha_groups__group_id__operations_get",
    "parameters": [
     {
      "in": "path",
      "name": "group_id",
      "required": true,
      "schema": {
       "title": "Group Id",
       "type": "string"
      },
      "description": "Identifiant du groupe de HA."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Operations Api V1 Ha Groups  Group Id  Operations Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire l'historique des bascules d'un groupe",
    "tags": [
     "availability"
    ],
    "description": "Renvoie `test_runs` (statut, jeton de fencing, RTO et RPO observés en secondes, preuves, horaires), `fencing_operations` (membre, méthode, vérification, jeton) et les 200 derniers `events`. Permission `cluster:read`."
   }
  },
  "/api/v1/ha-groups/{group_id}/test-failover": {
   "post": {
    "operationId": "test_failover_api_v1_ha_groups__group_id__test_failover_post",
    "parameters": [
     {
      "in": "path",
      "name": "group_id",
      "required": true,
      "schema": {
       "title": "Group Id",
       "type": "string"
      },
      "description": "Identifiant du groupe de HA."
     }
    ],
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Test Failover Api V1 Ha Groups  Group Id  Test Failover Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lancer un test de bascule",
    "tags": [
     "availability"
    ],
    "description": "Prend le bail de bascule du groupe, enregistre un test avec un nouveau jeton de fencing et, quand le groupe est lié à des composants installés, lance la bascule. Répond `202` avec `run_id`, `status`, `fencing_token`, `executed` et les opérations lancées `operations`. Permission `services:operate`. Erreurs : `409 ha_operation_locked` (une autre bascule détient le bail), `409 ha_client_managed_refused`. Audité en `ha_group.test_failover`."
   }
  },
  "/api/v1/ha-modes": {
   "get": {
    "operationId": "ha_modes_api_v1_ha_modes_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Ha Modes Api V1 Ha Modes Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les modes de déploiement et leur prise en charge",
    "tags": [
     "availability"
    ],
    "description": "Chaque mode de déploiement avec `supported`, `ha_possible` et la raison `reason` dans les deux langues. Tout utilisateur connecté."
   }
  },
  "/api/v1/hosts": {
   "get": {
    "operationId": "list_all_hosts_api_v1_hosts_get",
    "parameters": [
     {
      "in": "query",
      "name": "cluster_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cluster Id"
      },
      "description": "Uniquement les machines de ce cluster."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List All Hosts Api V1 Hosts Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les machines de tous les clusters",
    "tags": [
     "node-security"
    ],
    "description": "Machines de chaque cluster que l'appelant peut lire (ou d'un seul cluster), triées par FQDN, avec leur état de confiance, leur mode de gestion, leur identité, leurs métriques, leur inventaire et leur plateforme."
   }
  },
  "/api/v1/hosts/bootstrap": {
   "post": {
    "operationId": "host_bootstrap_api_v1_hosts_bootstrap_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BootstrapIn"
       }
      }
     },
     "required": true,
     "description": "Machine cible, machines de rebond, compte SSH, clé privée ou certificat."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Bootstrap Api V1 Hosts Bootstrap Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Amorcer un agent de nœud par SSH",
    "tags": [
     "node-security"
    ],
    "description": "Lance une opération qui installe et enregistre l'agent sur une machine :\n\n1. le binaire de l'agent du miroir est vérifié (SHA-256 et signature Ed25519) avant toute connexion ;\n2. les clés d'hôte viennent uniquement de la requête, avec vérification stricte, via les machines de rebond\n   éventuelles ;\n3. une clé Ed25519 temporaire est ajoutée pour l'exécution et utilisée par toutes les étapes suivantes ;\n4. les étapes de préparation, d'installation du binaire, d'installation de l'unité, d'activation et\n   d'enregistrement ne font rien quand elles sont déjà faites ;\n5. une étape en échec annule ce que l'exécution a créé ; à la fin, la clé temporaire et le répertoire de\n   préparation sont retirés.\n\nAvec `use_sudo`, seules les lignes exactes de la liste sudo autorisée s'exécutent. Répond `202` avec\n`operation_id` et le plan `plan`. Permission `hosts:manage`. Erreurs : `422 ssh_policy_refused`,\n`404 cluster_not_found`. Audité en `host.bootstrap`."
   }
  },
  "/api/v1/hosts/bootstrap/plan": {
   "post": {
    "operationId": "bootstrap_plan_api_v1_hosts_bootstrap_plan_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BootstrapIn"
       }
      }
     },
     "required": true,
     "description": "Même corps qu'un amorçage."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Bootstrap Plan Api V1 Hosts Bootstrap Plan Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Prévisualiser l'amorçage d'un agent de nœud",
    "tags": [
     "node-security"
    ],
    "description": "Contrôle l'identifiant par rapport à la politique SSH et renvoie le plan sans se connecter : utilisateur SSH, cible, rebonds, verdict sur l'identifiant, fichier sudoers et sa validation, liste autorisée, unité systemd et répertoire de préparation. Permission `hosts:manage` ; `422 ssh_policy_refused`."
   }
  },
  "/api/v1/hosts/enroll": {
   "post": {
    "operationId": "host_enroll_api_v1_hosts_enroll_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__node_security__EnrollIn"
       }
      }
     },
     "required": true,
     "description": "Cluster, FQDN et empreinte de clé de la machine."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Enroll Api V1 Hosts Enroll Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Émettre un jeton d'enrôlement pour une clé d'agent",
    "tags": [
     "node-security"
    ],
    "description": "Émet un jeton à usage unique pour une machine et une clé : `host_fingerprint` est le SHA-256 (hexadécimal) de la clé publique de l'agent (`nm-agent` l'affiche avant l'enrôlement). Le jeton expire au bout de 15 minutes au plus et est consommé par le premier enrôlement qui présente une CSR de cette clé exacte. Renvoie `id`, `token`, `expires_at`, `ttl_s`, `host_fingerprint` et `agent_url` (`201`). Permission `hosts:manage`. Audité en `host.enroll_token`."
   }
  },
  "/api/v1/hosts/preflight": {
   "post": {
    "operationId": "host_preflight_api_v1_hosts_preflight_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PreflightIn"
       }
      }
     },
     "required": true,
     "description": "Accès SSH, identité attendue, ports et disques à contrôler."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Preflight Api V1 Hosts Preflight Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lancer les contrôles préalables d'une machine avant son amorçage",
    "tags": [
     "node-security"
    ],
    "description": "Lit la machine en SSH et depuis le Manager (résolution DNS directe et inverse, joignabilité TCP, ports requis libres, disques, système d'exploitation) et renvoie les contrôles classés `pass`, `warning` ou `blocking`, les nombres `blocking` et `warnings`, une empreinte reproductible `digest` et le verdict sur l'identifiant. Rien n'est installé. Permission `hosts:manage`. Erreurs : `422 ssh_policy_refused`. Audité en `host.preflight`."
   }
  },
  "/api/v1/hosts/{host_id}": {
   "delete": {
    "operationId": "delete_host_api_v1_hosts__host_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer une machine",
    "tags": [
     "hosts"
    ],
    "description": "Retire la machine du Manager et révoque les certificats de son agent, sans contacter l'agent (utilisez la mise hors service pour nettoyer la machine). Répond `204`. Permission `hosts:manage`. Erreurs : `404 host_not_found`, `409 host_has_components`. Audité en `host.delete`."
   },
   "get": {
    "operationId": "get_host_api_v1_hosts__host_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Host Api V1 Hosts  Host Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une machine avec ses caractéristiques",
    "tags": [
     "hosts"
    ],
    "description": "Renvoie la machine avec toutes les caractéristiques remontées par son agent. Permission `cluster:read` sur son cluster ; `404 host_not_found`."
   },
   "patch": {
    "operationId": "patch_host_api_v1_hosts__host_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/HostPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de la machine à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch Host Api V1 Hosts  Host Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier la baie, les étiquettes ou la maintenance d'une machine",
    "tags": [
     "hosts"
    ],
    "description": "Modifie les champs fournis et renvoie la machine. Activer la maintenance fait passer la machine à l'état `maintenance`, la désactiver la ramène à `healthy`. Permission `hosts:manage` ; `422 host_environment_unknown`. Audité en `host.update`."
   }
  },
  "/api/v1/hosts/{host_id}/actions": {
   "post": {
    "operationId": "host_action_api_v1_hosts__host_id__actions_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/HostBulkBody"
       }
      }
     },
     "required": true,
     "description": "Action et simulation."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Action Api V1 Hosts  Host Id  Actions Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exécuter une action sur tous les composants d'une machine",
    "tags": [
     "services"
    ],
    "description": "Lance une seule opération qui démarre, arrête ou redémarre tous les composants placés sur la machine, ou seulement les composants dont la configuration a changé (`restart_stale`), service après service dans l'ordre des dépendances. Les instances en maintenance et les services dont l'état administratif interdit l'action sont écartés et listés dans `skipped` ; avec `dry_run`, rien n'est créé et `operation_id` est nul. Permission `services:operate` sur le cluster de la machine. Erreurs : `404 host_not_found`, `409 bulk_nothing_to_do`, `409 approval_required` (sous `banking-secure`, l'arrêt et les redémarrages demandent une approbation pour le service `host:<host id>`). Audité en `host.<action>`."
   }
  },
  "/api/v1/hosts/{host_id}/agent-update": {
   "post": {
    "description": "Vérifie le binaire signé de l'agent dans le miroir, puis émet une commande signée `agent.update` avec son empreinte et sa signature (clé d'idempotence dérivée de l'empreinte). Renvoie l'enveloppe de la commande (`201`). Permission `hosts:manage`. Erreurs : `422 agent_package_untrusted`, `409 host_not_trusted`, `409 host_client_managed`.",
    "operationId": "agent_update_api_v1_hosts__host_id__agent_update_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/UpdateIn"
       }
      }
     },
     "required": true,
     "description": "Validité de la commande et délai de santé du nouvel agent."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Agent Update Api V1 Hosts  Host Id  Agent Update Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Mettre à jour l'agent d'une machine",
    "tags": [
     "node-security"
    ]
   }
  },
  "/api/v1/hosts/{host_id}/breakglass": {
   "get": {
    "operationId": "breakglass_list_api_v1_hosts__host_id__breakglass_get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Breakglass List Api V1 Hosts  Host Id  Breakglass Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les demandes d'accès d'urgence d'une machine",
    "tags": [
     "node-security"
    ],
    "description": "Les 50 dernières demandes d'accès d'urgence de la machine avec leur statut, leurs approbateurs, leur validité et leurs preuves. Permission `cluster:read`."
   },
   "post": {
    "operationId": "breakglass_request_api_v1_hosts__host_id__breakglass_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BreakGlassIn"
       }
      }
     },
     "required": true,
     "description": "Raison, clé publique, compte et validité."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Breakglass Request Api V1 Hosts  Host Id  Breakglass Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Demander un accès SSH d'urgence",
    "tags": [
     "node-security"
    ],
    "description": "Enregistre une demande d'accès d'urgence pour la machine avec la raison, la clé publique du demandeur et la durée ; deux autres utilisateurs doivent l'approuver. Renvoie la demande (`201`). Permission `hosts:manage`. Erreurs : `422 breakglass_key_refused`, `409 host_not_trusted`, `409 host_client_managed`. Audité en `host.breakglass.request`."
   }
  },
  "/api/v1/hosts/{host_id}/commands": {
   "post": {
    "operationId": "issue_command_api_v1_hosts__host_id__commands_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__node_security__CommandIn"
       }
      }
     },
     "required": true,
     "description": "Opération, paramètres, validité et clé d'idempotence."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Issue Command Api V1 Hosts  Host Id  Commands Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Envoyer une commande signée à un agent",
    "tags": [
     "node-security"
    ],
    "description": "Émet une enveloppe de commande signée avec la clé Ed25519 du Manager pour l'agent de la machine (la même clé d'idempotence renvoie l'enveloppe enregistrée). Opérations autorisées : `facts.collect`, `health.check`, `logs.tail`, `files.digest`, `service.control`, `command.run`, `agent.update` ; les trois dernières sont refusées sur les machines gérées par le client. Renvoie l'enveloppe (`201`). Permission `hosts:manage`. Erreurs : `422 command_operation_unknown`, `409 host_not_trusted`, `409 host_client_managed`. Audité en `host.command.issue`."
   }
  },
  "/api/v1/hosts/{host_id}/components": {
   "get": {
    "operationId": "host_components_api_v1_hosts__host_id__components_get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Components Api V1 Hosts  Host Id  Components Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les composants d'une machine",
    "tags": [
     "hosts"
    ],
    "description": "Instances de composants placées sur la machine : `service`, `component`, `desired_state`, `state`, `health` et ses détails, `version`, `config_version`, `stale_config` (la configuration a changé depuis le dernier redémarrage), `maintenance`, `pid`, `since`, `updated_at`. Permission `cluster:read`."
   }
  },
  "/api/v1/hosts/{host_id}/decommission": {
   "post": {
    "operationId": "decommission_api_v1_hosts__host_id__decommission_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Decommission Api V1 Hosts  Host Id  Decommission Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Mettre une machine hors service",
    "tags": [
     "hosts"
    ],
    "description": "Lance une opération en deux étapes : l'agent exécute sa tâche de mise hors service, puis le Manager marque la machine `decommissioned` et révoque ses certificats. La machine ne doit plus avoir aucun composant. Répond `202` avec `operation_id`. Permission `hosts:manage` ; `409 host_has_components`. Audité en `host.decommission`."
   }
  },
  "/api/v1/hosts/{host_id}/facts/refresh": {
   "post": {
    "operationId": "refresh_facts_api_v1_hosts__host_id__facts_refresh_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Refresh Facts Api V1 Hosts  Host Id  Facts Refresh Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Relever à nouveau les caractéristiques d'une machine",
    "tags": [
     "hosts"
    ],
    "description": "Lance une opération où l'agent relève et remonte à nouveau ses caractéristiques (les contrôles préalables sont évalués dessus). Répond `202` avec `operation_id`. Permission `hosts:manage`. Audité en `host.facts_refresh`."
   }
  },
  "/api/v1/hosts/{host_id}/inventory": {
   "get": {
    "operationId": "host_inventory_api_v1_hosts__host_id__inventory_get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Inventory Api V1 Hosts  Host Id  Inventory Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire l'inventaire d'une machine",
    "tags": [
     "node-security"
    ],
    "description": "Renvoie `host_id`, l'inventaire `inventory` rapproché à partir des caractéristiques de l'agent (matériel, GPU, plateforme) et `updated_at`. Permission `cluster:read`."
   }
  },
  "/api/v1/hosts/{host_id}/management": {
   "put": {
    "operationId": "set_management_api_v1_hosts__host_id__management_put",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ManagementIn"
       }
      }
     },
     "required": true,
     "description": "Le mode de gestion."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Set Management Api V1 Hosts  Host Id  Management Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Définir qui gère une machine",
    "tags": [
     "node-security"
    ],
    "description": "Définit le mode de gestion : `platform` (le Manager agit sur la machine) ou `client` (les commandes dangereuses et l'accès d'urgence sont refusés). Renvoie le nœud. Permission `hosts:manage`. Audité en `host.management`."
   }
  },
  "/api/v1/hosts/{host_id}/metrics": {
   "get": {
    "operationId": "host_metrics_api_v1_hosts__host_id__metrics_get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     },
     {
      "in": "query",
      "name": "from",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "From"
      },
      "description": "Début de la fenêtre, ISO 8601. Par défaut : une heure avant `to`."
     },
     {
      "in": "query",
      "name": "to",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "To"
      },
      "description": "Fin de la fenêtre, ISO 8601. Par défaut : maintenant."
     },
     {
      "in": "query",
      "name": "step",
      "required": false,
      "schema": {
       "default": 60,
       "maximum": 86400,
       "minimum": 10,
       "title": "Step",
       "type": "integer"
      },
      "description": "Largeur d'une tranche en secondes, de 10 à 86400. 60 par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Metrics Api V1 Hosts  Host Id  Metrics Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire les métriques d'une machine",
    "tags": [
     "hosts"
    ],
    "description": "Séries temporelles de la machine sur [`from`, `to`] (par défaut : la dernière heure) par tranches de `step` secondes : `cpu_pct`, `mem_used_mb`, `load1`, `disk_pct`, `net_rx`, `net_tx`, chacune sous forme de liste de `[heure, valeur]`. Permission `cluster:read`."
   }
  },
  "/api/v1/hosts/{host_id}/parameters": {
   "get": {
    "operationId": "get_parameters_api_v1_hosts__host_id__parameters_get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Parameters Api V1 Hosts  Host Id  Parameters Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire les paramètres noyau d'une machine",
    "tags": [
     "hosts"
    ],
    "description": "Les réglages sysctl et les limites de ressources (`nofile`, `nproc`, `memlock`) de la machine, chacun avec sa valeur `current` (remontée par l'agent), sa valeur `desired` (définie par PUT) et sa valeur `recommended` (issue des services installés), ainsi que le pare-feu utilisé et les ports occupés. Permission `cluster:read`."
   },
   "put": {
    "operationId": "put_parameters_api_v1_hosts__host_id__parameters_put",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ParametersIn"
       }
      }
     },
     "required": true,
     "description": "Réglages sysctl et limites de ressources."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Parameters Api V1 Hosts  Host Id  Parameters Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Appliquer des paramètres noyau à une machine",
    "tags": [
     "hosts"
    ],
    "description": "Enregistre les réglages sysctl et les limites de ressources et lance une opération qui les écrit dans `/etc/sysctl.d/90-navin-manager.conf` et `/etc/security/limits.d/90-navin-manager.conf`. Répond `202` avec `operation_id`. Permission `hosts:manage`. Erreurs : `422 validation_error` (clé invalide ou requête vide), `409 host_rootless` (agent sans droits root). Audité en `host.parameters`."
   }
  },
  "/api/v1/hosts/{host_id}/preflight": {
   "get": {
    "operationId": "preflight_results_api_v1_hosts__host_id__preflight_get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response Preflight Results Api V1 Hosts  Host Id  Preflight Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire les résultats des contrôles préalables d'une machine",
    "tags": [
     "hosts"
    ],
    "description": "Résultat de chaque contrôle préalable de la machine (`check`, `status`, `message_i18n`, `details`, `checked_at`), évalué à partir de ses dernières caractéristiques. Permission `cluster:read`."
   },
   "post": {
    "operationId": "run_preflight_api_v1_hosts__host_id__preflight_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Run Preflight Api V1 Hosts  Host Id  Preflight Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lancer les contrôles préalables d'une machine",
    "tags": [
     "hosts"
    ],
    "description": "Lance une opération qui relève des caractéristiques à jour ; les contrôles préalables sont évalués à leur arrivée. Lisez les résultats avec `GET /hosts/{host_id}/preflight`. Répond `202` avec `operation_id`. Permission `hosts:manage`. Audité en `host.preflight`."
   }
  },
  "/api/v1/hosts/{host_id}/quarantine": {
   "post": {
    "operationId": "quarantine_host_api_v1_hosts__host_id__quarantine_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TrustIn"
       }
      }
     },
     "required": true,
     "description": "Raison du changement."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Quarantine Host Api V1 Hosts  Host Id  Quarantine Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Mettre une machine en quarantaine",
    "tags": [
     "node-security"
    ],
    "description": "Passe l'état de confiance à `quarantined` : le Manager cesse de faire confiance à l'agent pour de nouvelles commandes jusqu'à son rétablissement. Renvoie le nœud. Permission `hosts:manage`. Audité en `host.quarantine`."
   }
  },
  "/api/v1/hosts/{host_id}/receipts": {
   "get": {
    "operationId": "host_receipts_api_v1_hosts__host_id__receipts_get",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     },
     {
      "in": "query",
      "name": "limit",
      "required": false,
      "schema": {
       "default": 50,
       "maximum": 500,
       "minimum": 1,
       "title": "Limit",
       "type": "integer"
      },
      "description": "Nombre maximal de reçus, de 1 à 500. 50 par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Receipts Api V1 Hosts  Host Id  Receipts Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les reçus de commandes d'une machine",
    "tags": [
     "node-security"
    ],
    "description": "Reçus des commandes signées exécutées par l'agent, du dernier terminé au plus ancien : opération, début et fin, code de sortie, état observé, code d'erreur et preuves, expurgés. Permission `cluster:read`."
   }
  },
  "/api/v1/hosts/{host_id}/recover": {
   "post": {
    "operationId": "recover_host_api_v1_hosts__host_id__recover_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TrustIn"
       }
      }
     },
     "required": true,
     "description": "Raison du changement."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Recover Host Api V1 Hosts  Host Id  Recover Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Rétablir la confiance dans une machine en quarantaine",
    "tags": [
     "node-security"
    ],
    "description": "Repasse l'état de confiance à `trusted`. Une machine révoquée ne peut pas être rétablie : elle doit s'enrôler à nouveau (`409 agent_revoked`). Renvoie le nœud. Permission `hosts:manage`. Audité en `host.recover`."
   }
  },
  "/api/v1/hosts/{host_id}/revoke": {
   "post": {
    "operationId": "revoke_host_api_v1_hosts__host_id__revoke_post",
    "parameters": [
     {
      "in": "path",
      "name": "host_id",
      "required": true,
      "schema": {
       "title": "Host Id",
       "type": "string"
      },
      "description": "Identifiant de la machine, enregistrée par son agent Navin."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TrustIn"
       }
      }
     },
     "required": true,
     "description": "Raison du changement."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Revoke Host Api V1 Hosts  Host Id  Revoke Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révoquer l'agent d'une machine",
    "tags": [
     "node-security"
    ],
    "description": "Passe l'état de confiance à `revoked`, révoque les certificats de l'agent et fait expirer ses commandes en attente. Seul un nouvel enrôlement le rétablit. Renvoie le nœud. Permission `hosts:manage`. Audité en `host.revoke` avec le nombre de certificats révoqués."
   }
  },
  "/api/v1/hub": {
   "get": {
    "operationId": "get_status_api_v1_hub_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Status Api V1 Hub Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire l'état du Hub",
    "tags": [
     "hub"
    ],
    "description": "Renvoie `configured`, les réglages publics `settings` (le jeton n'est jamais renvoyé), l'état `state` du dernier document accepté et un résumé `packages_summary`. Permission `system:read`."
   }
  },
  "/api/v1/hub/import": {
   "post": {
    "operationId": "import_offline_api_v1_hub_import_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "additionalProperties": true,
        "title": "Document",
        "type": "object"
       }
      }
     },
     "required": true,
     "description": "Le document de distribution signé, en JSON."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Import Offline Api V1 Hub Import Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Importer un document de distribution hors ligne",
    "tags": [
     "hub"
    ],
    "description": "Vérifie et enregistre un document signé apporté sur support amovible (site isolé). Exige le client et les clés épinglées dans les réglages. Renvoie `metadata_version`, `targets`, `images_loaded` et `changes`. Permission `settings:manage` ; `422 hub_document_invalid` quand un contrôle échoue. Audité en `hub.import`."
   }
  },
  "/api/v1/hub/packages": {
   "get": {
    "operationId": "list_packages_api_v1_hub_packages_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Packages Api V1 Hub Packages Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les paquets approuvés",
    "tags": [
     "hub"
    ],
    "description": "Cibles du document accepté (projet, nom, version, image, révocation), chacune indiquant si c'est la dernière version approuvée et si elle est installée sur un cluster. Renvoie `items`. Permission `system:read`."
   }
  },
  "/api/v1/hub/settings": {
   "put": {
    "operationId": "put_settings_api_v1_hub_settings_put",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/HubSettingsBody"
       }
      }
     },
     "required": true,
     "description": "Adresse du Hub, client, clés et jeton."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Settings Api V1 Hub Settings Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Enregistrer les réglages du Hub",
    "tags": [
     "hub"
    ],
    "description": "Enregistre la connexion au Hub et renvoie l'état. Le jeton est scellé ; laissez-le nul pour conserver celui enregistré. Permission `settings:manage` ; `422 hub_document_invalid` quand `trusted_keys_pem` ne contient aucune clé publique Ed25519. Audité en `hub.settings` (identifiants des clés uniquement)."
   }
  },
  "/api/v1/hub/sync": {
   "post": {
    "operationId": "sync_api_v1_hub_sync_post",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Sync Api V1 Hub Sync Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Synchroniser avec le Hub",
    "tags": [
     "hub"
    ],
    "description": "Télécharge le document du client depuis le Hub, le vérifie et l'enregistre. Exige l'URL, le client et les clés épinglées dans les réglages. Renvoie `metadata_version`, `targets`, `images_loaded` et `changes`. Permission `settings:manage`. Audité en `hub.sync`."
   }
  },
  "/api/v1/mcp/hosted": {
   "get": {
    "description": "Serveurs hébergés d'un locataire (un projet ou tous) avec `url`, le jeton porteur `token`, `allowed_tools`, `status` et `hosted_at`, pour la plateforme Navin Enterprise. Permission `mcp:publish` (rôle admin uniquement). Audité en `mcp.hosted_list` (identifiants uniquement, jamais un jeton).",
    "operationId": "hosted_servers_api_v1_mcp_hosted_get",
    "parameters": [
     {
      "description": "Identifiant court du locataire.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet ; tous les projets du locataire s'il est absent.",
      "in": "query",
      "name": "project",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "description": "project slug (every project if absent)",
       "title": "Project"
      }
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Hosted Servers Api V1 Mcp Hosted Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les serveurs MCP hébergés avec leurs jetons",
    "tags": [
     "mcp"
    ]
   }
  },
  "/api/v1/mcp/hosting": {
   "get": {
    "description": "Par cluster : installation ou non de `mcp-host`, ses instances (FQDN, URL de base, état) et le nombre de serveurs que chacune héberge. Un serveur est placé sur une instance d'après une empreinte de son identifiant. Permission `mcp:read`.",
    "operationId": "hosting_infrastructure_api_v1_mcp_hosting_get",
    "parameters": [
     {
      "in": "query",
      "name": "cluster_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "pattern": "^[0-9a-fA-F-]{36}$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cluster Id"
      },
      "description": "Uniquement ce cluster (UUID)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Hosting Infrastructure Api V1 Mcp Hosting Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire l'infrastructure d'hébergement MCP",
    "tags": [
     "mcp"
    ]
   }
  },
  "/api/v1/mcp/presets": {
   "get": {
    "description": "Modèles de la bibliothèque Navin (nom, catégorie, transport, hébergement, champs à remplir), triés par catégorie et par nom. Permission `mcp:read` ; `503 mcp_presets_unavailable`.",
    "operationId": "list_presets_api_v1_mcp_presets_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Presets Api V1 Mcp Presets Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les modèles MCP",
    "tags": [
     "mcp"
    ]
   }
  },
  "/api/v1/mcp/servers": {
   "get": {
    "description": "Serveurs d'un projet d'un locataire (les deux obligatoires), sans aucune valeur secrète. Permission `mcp:read`.",
    "operationId": "list_servers_api_v1_mcp_servers_get",
    "parameters": [
     {
      "description": "Identifiant court du locataire.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Servers Api V1 Mcp Servers Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les serveurs MCP d'un projet",
    "tags": [
     "mcp"
    ]
   },
   "post": {
    "operationId": "create_server_api_v1_mcp_servers_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ServerIn"
       }
      }
     },
     "required": true,
     "description": "Locataire, projet et définition du serveur."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Server Api V1 Mcp Servers Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un serveur MCP",
    "tags": [
     "mcp"
    ],
    "description": "Crée un serveur à partir d'un modèle (ses champs obligatoires sont exigés) ou un serveur personnalisé, et le renvoie (`201`). Un modèle dont l'URL n'est pas interne exige `url` réglé sur l'adresse interne de son serveur. Permission `mcp:manage`. Erreurs : `422 mcp_fields_missing`, `422 mcp_fields_unknown`, `422 mcp_target_required`, `422 mcp_online_forbidden`, `404 mcp_preset_not_found`, `409 mcp_server_name_taken`. Audité en `mcp.create`."
   }
  },
  "/api/v1/mcp/servers/{server_id}": {
   "delete": {
    "operationId": "delete_server_api_v1_mcp_servers__server_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "server_id",
      "required": true,
      "schema": {
       "title": "Server Id",
       "type": "string"
      },
      "description": "Identifiant du serveur MCP."
     },
     {
      "description": "Identifiant court du locataire du serveur : lettres minuscules, chiffres et `-`, 63 caractères au plus.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet du serveur.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer un serveur MCP",
    "tags": [
     "mcp"
    ],
    "description": "Supprime le serveur ; un serveur hébergé est d'abord retiré de son hébergement. Répond `204`. Permission `mcp:manage` ; `404 mcp_server_not_found`. Audité en `mcp.delete`."
   },
   "get": {
    "operationId": "get_server_api_v1_mcp_servers__server_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "server_id",
      "required": true,
      "schema": {
       "title": "Server Id",
       "type": "string"
      },
      "description": "Identifiant du serveur MCP."
     },
     {
      "description": "Identifiant court du locataire du serveur.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet du serveur.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Server Api V1 Mcp Servers  Server Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un serveur MCP",
    "tags": [
     "mcp"
    ],
    "description": "Renvoie le serveur avec son transport, son URL ou sa commande, ses outils autorisés, les noms des champs configurés et secrets, son dernier test et son hébergement (état, cluster, URL ; jamais le jeton). Permission `mcp:read` ; `404 mcp_server_not_found`."
   },
   "patch": {
    "operationId": "update_server_api_v1_mcp_servers__server_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "server_id",
      "required": true,
      "schema": {
       "title": "Server Id",
       "type": "string"
      },
      "description": "Identifiant du serveur MCP."
     },
     {
      "description": "Identifiant court du locataire du serveur.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet du serveur.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ServerPatch"
       }
      }
     },
     "required": true,
     "description": "Champs du serveur à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Update Server Api V1 Mcp Servers  Server Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier un serveur MCP",
    "tags": [
     "mcp"
    ],
    "description": "Modifie les champs fournis et renvoie le serveur. `values` est fusionné avec les valeurs enregistrées ; `url`, `command`, `args`, `headers` et `env` ne s'appliquent qu'aux serveurs personnalisés (`422 mcp_custom_only`). Désactiver un serveur hébergé le retire de son hébergement. Permission `mcp:manage`. Erreurs : `422 mcp_fields_unknown`, `422 mcp_online_forbidden`. Audité en `mcp.update`."
   }
  },
  "/api/v1/mcp/servers/{server_id}/hosting": {
   "delete": {
    "description": "Retire le serveur de son hébergement : statut `removing`, puis l'opération `mcp.hosting.apply` sans lui (`none` à la fin). Répond `202` avec le serveur et `operation_id`. Permission `mcp:manage` ; `409 mcp_not_hosted`. Audité en `mcp.unhost`.",
    "operationId": "unhost_server_api_v1_mcp_servers__server_id__hosting_delete",
    "parameters": [
     {
      "in": "path",
      "name": "server_id",
      "required": true,
      "schema": {
       "title": "Server Id",
       "type": "string"
      },
      "description": "Identifiant du serveur MCP."
     },
     {
      "description": "Identifiant court du locataire du serveur.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet du serveur.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Unhost Server Api V1 Mcp Servers  Server Id  Hosting Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Arrêter l'hébergement d'un serveur MCP",
    "tags": [
     "mcp"
    ]
   },
   "post": {
    "description": "Héberge un serveur stdio sur le service `mcp-host` d'un cluster : un nouveau jeton porteur (scellé ; l'environnement d'exécution ne reçoit que son SHA-256), statut `deploying`, puis l'opération `mcp.hosting.apply` qui écrit `servers.json` sur chaque instance ; le statut devient `hosted` ou `failed`. Répond `202` avec le serveur et `operation_id`. Permission `mcp:manage`. Erreurs : `422 mcp_hosting_stdio_only`, `409 mcp_hosting_disabled`, `409 mcp_host_not_installed`. Audité en `mcp.host`.",
    "operationId": "host_server_api_v1_mcp_servers__server_id__hosting_post",
    "parameters": [
     {
      "in": "path",
      "name": "server_id",
      "required": true,
      "schema": {
       "title": "Server Id",
       "type": "string"
      },
      "description": "Identifiant du serveur MCP."
     },
     {
      "description": "Identifiant court du locataire du serveur.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet du serveur.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/HostingIn"
       }
      }
     },
     "required": true,
     "description": "Cluster et limites de l'hébergement."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Host Server Api V1 Mcp Servers  Server Id  Hosting Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Héberger un serveur MCP stdio sur un cluster",
    "tags": [
     "mcp"
    ]
   }
  },
  "/api/v1/mcp/servers/{server_id}/hosting/token": {
   "post": {
    "description": "Émet un nouveau jeton porteur et l'applique ; l'ancien jeton cesse de fonctionner une fois l'application faite. Répond `202`. Permission `mcp:manage`. Erreurs : `409 mcp_not_hosted`, `409 mcp_hosting_disabled`. Audité en `mcp.hosting_token`.",
    "operationId": "renew_token_api_v1_mcp_servers__server_id__hosting_token_post",
    "parameters": [
     {
      "in": "path",
      "name": "server_id",
      "required": true,
      "schema": {
       "title": "Server Id",
       "type": "string"
      },
      "description": "Identifiant du serveur MCP."
     },
     {
      "description": "Identifiant court du locataire du serveur.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet du serveur.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Renew Token Api V1 Mcp Servers  Server Id  Hosting Token Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Renouveler le jeton d'un serveur MCP hébergé",
    "tags": [
     "mcp"
    ]
   }
  },
  "/api/v1/mcp/servers/{server_id}/test": {
   "post": {
    "description": "Parle MCP au serveur (`initialize`, `notifications/initialized`, `tools/list`) en HTTP en flux, en SSE, ou sur l'entrée et la sortie standard de la commande quand elle est dans le PATH (sans shell, avec un délai) ; un serveur hébergé est testé à son URL d'hébergement avec son jeton. Renvoie et conserve dans `last_test` le résultat `{ok, tools, server_info, error, tested_at}` ; une erreur ne répète jamais une valeur secrète. Permission `mcp:manage` ; `422 mcp_online_forbidden`. Audité en `mcp.test`.",
    "operationId": "test_server_api_v1_mcp_servers__server_id__test_post",
    "parameters": [
     {
      "in": "path",
      "name": "server_id",
      "required": true,
      "schema": {
       "title": "Server Id",
       "type": "string"
      },
      "description": "Identifiant du serveur MCP."
     },
     {
      "description": "Identifiant court du locataire du serveur.",
      "in": "query",
      "name": "tenant",
      "required": true,
      "schema": {
       "description": "tenant slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Tenant",
       "type": "string"
      }
     },
     {
      "description": "Identifiant court du projet du serveur.",
      "in": "query",
      "name": "project",
      "required": true,
      "schema": {
       "description": "project slug",
       "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
       "title": "Project",
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Test Server Api V1 Mcp Servers  Server Id  Test Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Tester un serveur MCP",
    "tags": [
     "mcp"
    ]
   }
  },
  "/api/v1/me": {
   "get": {
    "operationId": "me_api_v1_me_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Me Api V1 Me Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire mon profil",
    "tags": [
     "me"
    ],
    "description": "Renvoie le compte de l'appelant : identifiant, nom d'utilisateur, nom affiché, e-mail, origine (`local`, `ldap`, `oidc`), attributions de rôle, état de la MFA, langue, verrouillage et dernière connexion."
   },
   "patch": {
    "operationId": "patch_me_api_v1_me_patch",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/MePatch"
       }
      }
     },
     "required": true,
     "description": "Champs du profil à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch Me Api V1 Me Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier mon profil",
    "tags": [
     "me"
    ],
    "description": "Modifie le nom affiché, l'e-mail ou la langue de l'appelant ; un champ omis est conservé. Session de navigateur uniquement. Renvoie le profil mis à jour. Audité en `user.profile_update` avec les noms des champs modifiés."
   }
  },
  "/api/v1/me/tokens": {
   "get": {
    "operationId": "list_tokens_api_v1_me_tokens_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Tokens Api V1 Me Tokens Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister mes jetons d'API",
    "tags": [
     "me"
    ],
    "description": "Liste tous les jetons de l'appelant, du plus récent au plus ancien, révoqués compris : `id`, `name`, `prefix`, `permissions` (nul : toutes les permissions de l'utilisateur), `expires_at`, `last_used_at`, `revoked_at`, `created_at`. Le secret n'est jamais renvoyé."
   },
   "post": {
    "operationId": "create_token_api_v1_me_tokens_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TokenBody"
       }
      }
     },
     "required": true,
     "description": "Nom, sous-ensemble facultatif de permissions et durée de vie du jeton."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Token Api V1 Me Tokens Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un jeton d'API personnel",
    "tags": [
     "me"
    ],
    "description": "Crée un jeton et renvoie `token` (le secret complet, affiché uniquement dans cette réponse), `id`, `prefix`,\n`name`, `permissions` et `expires_at`. Répond `201`. Session de navigateur uniquement\n(`403 session_required` avec un jeton). Erreurs : `422 permission_unknown` (permission inexistante),\n`403 token_permissions_exceed` (permission que vous ne détenez pas). Audité en `token.create`."
   }
  },
  "/api/v1/me/tokens/{token_id}": {
   "delete": {
    "operationId": "revoke_token_api_v1_me_tokens__token_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "token_id",
      "required": true,
      "schema": {
       "title": "Token Id",
       "type": "string"
      },
      "description": "Identifiant du jeton d'API, issu de `GET /me/tokens`."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révoquer un de mes jetons d'API",
    "tags": [
     "me"
    ],
    "description": "Révoque le jeton immédiatement (le révoquer à nouveau est sans effet). Session de navigateur uniquement. Répond `204` ; `404 token_not_found` quand le jeton ne vous appartient pas. Audité en `token.revoke`."
   }
  },
  "/api/v1/mirror": {
   "get": {
    "operationId": "status_api_v1_mirror_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Status Api V1 Mirror Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire l'état du miroir",
    "tags": [
     "mirror"
    ],
    "description": "Renvoie l'`url` et le registre `registry` du miroir, `manifest_loaded`, `loaded_at`, le nombre d'`artifacts`, d'`images` et de paquets `python` chargés, et `missing`, ce dont le catalogue a besoin et qui manque au miroir. Permission `system:read`."
   }
  },
  "/api/v1/mirror/reload": {
   "post": {
    "operationId": "reload_api_v1_mirror_reload_post",
    "parameters": [
     {
      "in": "query",
      "name": "verify",
      "required": false,
      "schema": {
       "default": false,
       "title": "Verify",
       "type": "boolean"
      },
      "description": "Contrôler aussi l'empreinte SHA-256 de chaque fichier par rapport au manifeste (plus lent). Faux par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Reload Api V1 Mirror Reload Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Recharger le manifeste du miroir",
    "tags": [
     "mirror"
    ],
    "description": "Relit le manifeste du miroir et remplace les artefacts enregistrés par son contenu ; chaque fichier doit exister. Renvoie ce qui a été chargé. Permission `settings:manage`. Erreurs : `409 mirror_manifest_missing`, `422 mirror_manifest_invalid`. Audité en `mirror.reload` (résultat `failure` avec le code d'erreur quand le manifeste est refusé)."
   }
  },
  "/api/v1/navin-sm/discover": {
   "post": {
    "description": "Appelle `GET <base>/v1/models` puis `GET <base>/model/info` du proxy (délai de 5 secondes, TLS 1.2 au moins, racines publiques et le `ca_pem` fourni, sans redirection) et renvoie `{models: [{id, context_window?, max_output_tokens?, vision?, reasoning?}], info_available, info_error}`. Avec `litellm_cluster_id` et `litellm_service`, le service LiteLLM installé par le Manager est lu avec sa clé maître et l'autorité du Manager, toutes deux gardées dans le serveur (un `base_url` vide désigne alors son adresse) ; sinon la clé de l'entrée du coffre ou la clé saisie pour ce seul appel. `/model/info` est facultatif (route d'administration qu'une clé virtuelle peut ne pas atteindre). Permission `settings:manage`, et `vault:manage` avec un `shared_key_ref`. Erreurs : `422 navin_sm_base_url_invalid`, `422 navin_sm_header_invalid`, `422 navin_sm_litellm_link_incomplete` (un seul des deux champs du lien), `422 navin_sm_litellm_source_not_found`, `409 navin_sm_master_key_missing`, `409 navin_sm_shared_key_missing`, `502 navin_sm_discover_failed` (détail `reason` : timeout, tls_failed, refused, unreachable, auth_failed, not_found, http_status, protocol_error). Audité en `navin_sm.discover`.",
    "operationId": "discover_models_api_v1_navin_sm_discover_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DiscoverIn"
       }
      }
     },
     "required": true,
     "description": "Le proxy à lire."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Discover Models Api V1 Navin Sm Discover Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Découvrir les modèles d'un proxy LiteLLM",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/distribution": {
   "get": {
    "description": "La version de Navin SM que nomme le catalogue (`service` : nom, version, dépôt et étiquette GitHub, chemins du fichier de réglages et de l'autorité par système), ses paquets avec leur SHA256 publié, leur état de stockage, leur taille et leur lien de téléchargement, le nombre stocké, les dix derniers téléchargements depuis GitHub et si l'un d'eux est en cours. Permission `settings:manage` ; `409 navin_sm_service_missing` sans le service `navin-sm` dans le catalogue.",
    "operationId": "distribution_api_v1_navin_sm_distribution_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Distribution Api V1 Navin Sm Distribution Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "État de la distribution Navin SM",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/distribution/checksums": {
   "post": {
    "description": "Enregistre le SHA256 publié des paquets à partir des lignes `<sha256>  <fichier>` des fichiers SHA256SUMS et .sha256 de la version, collées ensemble (sites hors ligne). Renvoie `{updated, unchanged, conflicts, ignored}` : les fichiers enregistrés ou déjà identiques, les paquets stockés dont le SHA256 changerait (signalés et laissés tels quels) et les noms hors de la version. Permission `settings:manage`. Erreurs : `409 navin_sm_service_missing`, `422 navin_sm_checksums_invalid` (aucune ligne exploitable). Audité en `navin_sm.package.checksums`.",
    "operationId": "load_checksums_api_v1_navin_sm_distribution_checksums_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ChecksumsIn"
       }
      }
     },
     "required": true,
     "description": "Les lignes de sommes de contrôle."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Load Checksums Api V1 Navin Sm Distribution Checksums Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Charger les sommes de contrôle publiées de la version",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/distribution/fetch": {
   "post": {
    "description": "Lance en arrière-plan la récupération de la version (`202`, l'import) : les fichiers de sommes d'abord, puis chaque paquet pas encore stocké, chacun vérifié contre son SHA256 publié. Le jeton GitHub est la clé API de l'entrée du coffre, lu dans le serveur et jamais renvoyé, journalisé ni audité. Permissions `settings:manage` et `vault:manage`. Erreurs : `409 navin_sm_service_missing`, `422 navin_sm_vault_entry_not_found`, `422 navin_sm_github_token_missing`, `409 navin_sm_import_running`. Audité en `navin_sm.package.fetch` (nom de l'entrée du coffre seulement). À suivre avec `GET /navin-sm/distribution/imports/{import_id}`.",
    "operationId": "fetch_release_api_v1_navin_sm_distribution_fetch_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/FetchIn"
       }
      }
     },
     "required": true,
     "description": "L'entrée du coffre qui porte le jeton GitHub."
    },
    "responses": {
     "202": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Fetch Release Api V1 Navin Sm Distribution Fetch Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Récupérer la version depuis GitHub",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/distribution/imports/{import_id}": {
   "get": {
    "description": "`{id, version, state, files, error, created_at, finished_at}` : `state` vaut `running`, `done` ou `failed` ; chaque fichier est `pending`, `downloading`, `stored`, `skipped` (déjà stocké) ou `failed` avec son `error` (jamais le jeton). Permission `settings:manage` ; `404 navin_sm_import_not_found`.",
    "operationId": "get_import_api_v1_navin_sm_distribution_imports__import_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "import_id",
      "required": true,
      "schema": {
       "title": "Import Id",
       "type": "string"
      },
      "description": "Identifiant de la récupération, renvoyé par `POST /navin-sm/distribution/fetch`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Import Api V1 Navin Sm Distribution Imports  Import Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "État d'une récupération",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/distribution/packages/{filename}": {
   "delete": {
    "description": "Supprime le fichier stocké ; son SHA256 publié reste enregistré. Répond `204`. Permission `settings:manage`. Erreurs : `404 navin_sm_package_unknown`, `404 navin_sm_package_missing`. Audité en `navin_sm.package.delete`.",
    "operationId": "delete_package_api_v1_navin_sm_distribution_packages__filename__delete",
    "parameters": [
     {
      "in": "path",
      "name": "filename",
      "required": true,
      "schema": {
       "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$",
       "title": "Filename",
       "type": "string"
      },
      "description": "Nom de fichier du paquet."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Retirer un paquet stocké",
    "tags": [
     "navin-sm"
    ]
   },
   "put": {
    "description": "Corps brut d'un paquet de la version, écrit en flux sous `<mirror_dir>/navin-sm/<version>/` et haché pendant l'écriture ; conservé (`201`, le paquet tel que la distribution le liste) seulement quand son SHA256 est celui publié (chargé au préalable) ou celui de l'en-tête `X-Checksum-Sha256`. Permission `settings:manage`. Erreurs : `404 navin_sm_package_unknown` (pas un paquet de la version), `422 navin_sm_package_checksum_unknown` (aucun SHA256 publié ni en-tête), `422 navin_sm_package_checksum_mismatch` (le fichier est supprimé), `422 navin_sm_package_too_large` (2 Gio), `409 navin_sm_disk_full`. Audité en `navin_sm.package.store`, refus compris.",
    "operationId": "upload_package_api_v1_navin_sm_distribution_packages__filename__put",
    "parameters": [
     {
      "in": "path",
      "name": "filename",
      "required": true,
      "schema": {
       "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$",
       "title": "Filename",
       "type": "string"
      },
      "description": "Nom de fichier du paquet, tel que le catalogue le liste pour cette version."
     },
     {
      "in": "header",
      "name": "X-Checksum-Sha256",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 64,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "X-Checksum-Sha256"
      },
      "description": "SHA256 attendu du fichier (64 caractères hexadécimaux) quand celui publié n'a pas été chargé ; il doit égaler celui publié quand les deux existent."
     }
    ],
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Upload Package Api V1 Navin Sm Distribution Packages  Filename  Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Déposer un paquet de la version",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/distribution/packages/{filename}/download": {
   "get": {
    "description": "Le paquet tel que stocké (`application/octet-stream`, `Cache-Control: no-store`), son SHA256 dans l'en-tête `X-Checksum-Sha256`. Permission `settings:manage`. Erreurs : `404 navin_sm_package_unknown`, `404 navin_sm_package_missing` (non stocké).",
    "operationId": "download_package_api_v1_navin_sm_distribution_packages__filename__download_get",
    "parameters": [
     {
      "in": "path",
      "name": "filename",
      "required": true,
      "schema": {
       "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$",
       "title": "Filename",
       "type": "string"
      },
      "description": "Nom de fichier du paquet."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/octet-stream": {
        "schema": {
         "format": "binary",
         "type": "string"
        }
       }
      },
      "description": "The package"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Télécharger un paquet stocké",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/litellm-sources": {
   "get": {
    "description": "Services LiteLLM installés par le Manager avec leur adresse et l'entrée du coffre de leur clé, pour préremplir la passerelle d'un profil ou le lier (`litellm_cluster_id`, `litellm_service`). L'entrée du coffre n'est donnée qu'à un détenteur de `vault:manage` ; un service installé encore sans entrée a `vault_entry_id` nul (`POST /vault/import` la crée). Permission `settings:manage`.",
    "operationId": "litellm_sources_api_v1_navin_sm_litellm_sources_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Litellm Sources Api V1 Navin Sm Litellm Sources Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les passerelles LiteLLM installées",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles": {
   "get": {
    "description": "Profils triés par nom d'organisation, avec le nom de l'entrée du coffre de leur clé partagée mais jamais la clé (`shared_key_set`). Permission `settings:manage`.",
    "operationId": "list_profiles_api_v1_navin_sm_profiles_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Profiles Api V1 Navin Sm Profiles Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les profils Navin SM",
    "tags": [
     "navin-sm"
    ]
   },
   "post": {
    "operationId": "create_profile_api_v1_navin_sm_profiles_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ProfileIn"
       }
      }
     },
     "required": true,
     "description": "Le profil."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Profile Api V1 Navin Sm Profiles Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un profil Navin SM",
    "tags": [
     "navin-sm"
    ],
    "description": "Crée le profil et le renvoie (`201`). Permission `settings:manage`, et `vault:manage` avec un `shared_key_ref`. Erreurs (422 sauf mention) : `navin_sm_org_id_invalid`, `navin_sm_value_empty`, `navin_sm_value_duplicate`, `navin_sm_base_url_invalid`, `navin_sm_base_url_chat`, `navin_sm_shared_key_ref_required`, `navin_sm_shared_key_ref_forbidden`, `navin_sm_vault_entry_not_found`, `navin_sm_header_invalid`, `navin_sm_header_secret`, `navin_sm_models_count`, `navin_sm_model_duplicate`, `navin_sm_default_model_unknown`, `navin_sm_route_invalid`, `navin_sm_route_unknown`, `navin_sm_env_invalid`, `navin_sm_env_secret`, `409 navin_sm_org_taken`. Audité en `navin_sm.create`."
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}": {
   "delete": {
    "operationId": "delete_profile_api_v1_navin_sm_profiles__profile_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer un profil Navin SM",
    "tags": [
     "navin-sm"
    ],
    "description": "Supprime le profil (l'entrée du coffre de sa clé est conservée). Répond `204`. Permission `settings:manage` ; `404 navin_sm_profile_not_found`. Audité en `navin_sm.delete`."
   },
   "get": {
    "operationId": "get_profile_api_v1_navin_sm_profiles__profile_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Profile Api V1 Navin Sm Profiles  Profile Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un profil Navin SM",
    "tags": [
     "navin-sm"
    ],
    "description": "Renvoie un profil sans aucune clé. Permission `settings:manage` ; `404 navin_sm_profile_not_found`."
   },
   "put": {
    "operationId": "update_profile_api_v1_navin_sm_profiles__profile_id__put",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ProfileIn"
       }
      }
     },
     "required": true,
     "description": "Le profil complet."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Update Profile Api V1 Navin Sm Profiles  Profile Id  Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Remplacer un profil Navin SM",
    "tags": [
     "navin-sm"
    ],
    "description": "Remplace tout le profil et le renvoie. Mêmes règles et erreurs que la création, plus `404 navin_sm_profile_not_found`. Audité en `navin_sm.update` (noms des champs modifiés)."
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/bundle": {
   "get": {
    "description": "Zip pour un système (`navin-sm-<org_id>-<os>.zip`, `Cache-Control: no-store`) : `managed-settings.json` (clé partagée comprise, audité en `navin_sm.export` avec le motif `bundle`), l'autorité du Manager quand le profil l'embarque (`use_manager_ca`), et les fichiers de déploiement du système : script shell et playbook Ansible sous Linux, script shell et notes MDM sous macOS, script PowerShell avec notes GPO et Intune sous Windows. Les paquets se téléchargent à part (`GET /navin-sm/distribution/packages/{filename}/download`). Permission `settings:manage`, et `vault:manage` en `shared_key`. Erreurs : `404 navin_sm_profile_not_found`, `409 navin_sm_service_missing` (aucun service `navin-sm` dans le catalogue), `409 navin_sm_shared_key_missing`.",
    "operationId": "profile_bundle_api_v1_navin_sm_profiles__profile_id__bundle_get",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "query",
      "name": "os",
      "required": true,
      "schema": {
       "pattern": "^(linux|macos|windows)$",
       "title": "Os",
       "type": "string"
      },
      "description": "Système du bundle : `linux`, `macos` ou `windows`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/zip": {
        "schema": {
         "format": "binary",
         "type": "string"
        }
       }
      },
      "description": "The deployment bundle"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Télécharger le bundle de déploiement d'un profil",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/export": {
   "get": {
    "description": "Renvoie le fichier en pièce jointe (`managed-settings.json`, `Cache-Control: no-store`), conforme au contrat. `gateway.apiKey` n'est présent qu'en `shared_key`, descellé du coffre pour cette seule réponse. Permission `settings:manage`, et `vault:manage` en `shared_key`. Erreurs : `404 navin_sm_profile_not_found`, `409 navin_sm_shared_key_missing` (entrée du coffre supprimée ou sans clé), `422 navin_sm_os_required` (profil avec `use_manager_ca` exporté sans `os`), `500 navin_sm_export_invalid`. Audité en `navin_sm.export` (jamais la clé).",
    "operationId": "export_profile_api_v1_navin_sm_profiles__profile_id__export_get",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "query",
      "name": "os",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "pattern": "^(linux|macos|windows)$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Os"
      },
      "description": "Système auquel le fichier est destiné (`linux`, `macos`, `windows`) ; obligatoire quand le profil embarque l'autorité du Manager (`use_manager_ca`), car il fixe le chemin de `gateway.caBundle`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      },
      "description": "managed-settings.json of the profile"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exporter le managed-settings.json d'un profil",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/keys": {
   "get": {
    "description": "Clés virtuelles LiteLLM émises pour le profil, actives et bloquées d'abord, puis révoquées : bénéficiaire, alias, état, modèles et limites, jamais une valeur de clé ni son empreinte. Permission `navin_sm:keys` ; `404 navin_sm_profile_not_found`.",
    "operationId": "list_keys_api_v1_navin_sm_profiles__profile_id__keys_get",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Keys Api V1 Navin Sm Profiles  Profile Id  Keys Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les clés virtuelles d'un profil",
    "tags": [
     "navin-sm"
    ]
   },
   "post": {
    "description": "Émet une clé auprès du service LiteLLM auquel le profil est lié (`POST /key/generate` avec la clé maître gardée dans le serveur) pour un utilisateur du Manager ou un groupe du fournisseur d'identité, et répond `201 {key, item}` : `key` est renvoyée ici et jamais plus, le Manager ne garde que l'alias et le sha256 que LiteLLM utilise comme identifiant. L'utilisateur LiteLLM est `navin-sm:<org_id>:<kind>:<bénéficiaire>`, l'alias `navin-sm.<org_id>.<bénéficiaire>.<aléa>`. Permission `navin_sm:keys`. Erreurs : `404 navin_sm_profile_not_found`, `404 navin_sm_user_not_found`, `422 navin_sm_assignee_required`, `422 navin_sm_key_models_unknown` (modèle hors du profil), `409 navin_sm_profile_not_linked`, `409 navin_sm_master_key_missing`, `409 navin_sm_key_alias_taken`, `502 navin_sm_proxy_refused` ou `navin_sm_proxy_failed`. Audité en `navin_sm.key.create` (identifiant et alias, jamais la clé).",
    "operationId": "create_key_api_v1_navin_sm_profiles__profile_id__keys_post",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/KeyIn"
       }
      }
     },
     "required": true,
     "description": "Bénéficiaire, modèles et limites de la nouvelle clé."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Key Api V1 Navin Sm Profiles  Profile Id  Keys Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Émettre une clé virtuelle",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/keys/revoke-all": {
   "post": {
    "description": "Supprime toutes les clés actives ou bloquées du profil au proxy en un appel (`POST /key/delete`) et les enregistre comme révoquées. Renvoie `{revoked, items}`. Permission `navin_sm:keys`. Erreurs : `404 navin_sm_profile_not_found`, `409 navin_sm_profile_not_linked`, `409 navin_sm_master_key_missing`, `502 navin_sm_proxy_refused` ou `navin_sm_proxy_failed`. Audité en `navin_sm.key.revoke_all` (nombre et identifiants des clés).",
    "operationId": "revoke_all_keys_api_v1_navin_sm_profiles__profile_id__keys_revoke_all_post",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Revoke All Keys Api V1 Navin Sm Profiles  Profile Id  Keys Revoke All Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révoquer toutes les clés d'un profil",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/keys/{key_id}": {
   "delete": {
    "description": "Supprime la clé au proxy (`POST /key/delete`, immédiat) et la conserve dans le Manager avec l'état `revoked` et `revoked_at` ; une clé que le proxy ne connaît plus est révoquée tout de même. Renvoie la clé. Permission `navin_sm:keys`. Erreurs : `404 navin_sm_key_not_found`, `409 navin_sm_key_revoked`, `409 navin_sm_profile_not_linked`, `409 navin_sm_master_key_missing`, `502 navin_sm_proxy_refused` ou `navin_sm_proxy_failed`. Audité en `navin_sm.key.revoke`.",
    "operationId": "revoke_key_api_v1_navin_sm_profiles__profile_id__keys__key_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "path",
      "name": "key_id",
      "required": true,
      "schema": {
       "title": "Key Id",
       "type": "string"
      },
      "description": "Identifiant de la clé dans le Manager."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Revoke Key Api V1 Navin Sm Profiles  Profile Id  Keys  Key Id  Delete",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révoquer une clé",
    "tags": [
     "navin-sm"
    ]
   },
   "put": {
    "description": "Applique les modèles et limites au proxy (`POST /key/update`), puis dans le Manager, et renvoie la clé. Permission `navin_sm:keys`. Erreurs : `404 navin_sm_profile_not_found`, `404 navin_sm_key_not_found`, `409 navin_sm_key_revoked`, `409 navin_sm_key_gone` (le proxy ne connaît plus la clé, alors enregistrée comme révoquée), `422 navin_sm_key_models_unknown`, `409 navin_sm_profile_not_linked`, `409 navin_sm_master_key_missing`, `502 navin_sm_proxy_refused` ou `navin_sm_proxy_failed`. Audité en `navin_sm.key.update`.",
    "operationId": "update_key_api_v1_navin_sm_profiles__profile_id__keys__key_id__put",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "path",
      "name": "key_id",
      "required": true,
      "schema": {
       "title": "Key Id",
       "type": "string"
      },
      "description": "Identifiant de la clé dans le Manager (jamais sa valeur)."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/KeyLimitsIn"
       }
      }
     },
     "required": true,
     "description": "Nouveaux modèles et limites."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Update Key Api V1 Navin Sm Profiles  Profile Id  Keys  Key Id  Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier les modèles et limites d'une clé",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/keys/{key_id}/block": {
   "post": {
    "description": "Bloque la clé au proxy (`POST /key/block`) : elle est refusée aussitôt et peut être débloquée. Renvoie la clé avec l'état `blocked`. Permission `navin_sm:keys`. Erreurs : `404 navin_sm_key_not_found`, `409 navin_sm_key_revoked`, `409 navin_sm_key_gone`, `409 navin_sm_profile_not_linked`, `409 navin_sm_master_key_missing`, `502 navin_sm_proxy_refused` ou `navin_sm_proxy_failed`. Audité en `navin_sm.key.block`.",
    "operationId": "block_key_api_v1_navin_sm_profiles__profile_id__keys__key_id__block_post",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "path",
      "name": "key_id",
      "required": true,
      "schema": {
       "title": "Key Id",
       "type": "string"
      },
      "description": "Identifiant de la clé dans le Manager."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Block Key Api V1 Navin Sm Profiles  Profile Id  Keys  Key Id  Block Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Bloquer une clé",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/keys/{key_id}/unblock": {
   "post": {
    "description": "Débloque une clé bloquée au proxy (`POST /key/unblock`) et la renvoie avec l'état `active`. Permission `navin_sm:keys`. Mêmes erreurs que le blocage. Audité en `navin_sm.key.unblock`.",
    "operationId": "unblock_key_api_v1_navin_sm_profiles__profile_id__keys__key_id__unblock_post",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "path",
      "name": "key_id",
      "required": true,
      "schema": {
       "title": "Key Id",
       "type": "string"
      },
      "description": "Identifiant de la clé dans le Manager."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Unblock Key Api V1 Navin Sm Profiles  Profile Id  Keys  Key Id  Unblock Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Débloquer une clé",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/preview": {
   "get": {
    "description": "Le fichier que produirait l'export, vérifié par le contrat, sans sa clé : une clé `shared_key` est remplacée par `<vault: nom de l'entrée>`. Renvoie `{os, settings_path, ca_path, document, problems, valid}` ; `problems` liste les règles que le document enfreint (entrée du coffre sans clé, profil avec `use_manager_ca` prévisualisé sans `os`). Permission `settings:manage` ; `404 navin_sm_profile_not_found`. Non audité (rien ne sort du serveur).",
    "operationId": "preview_profile_api_v1_navin_sm_profiles__profile_id__preview_get",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "query",
      "name": "os",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "pattern": "^(linux|macos|windows)$",
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Os"
      },
      "description": "Système auquel le fichier est destiné (`linux`, `macos`, `windows`) : fixe `settings_path` et, avec `use_manager_ca`, le chemin de `gateway.caBundle`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Preview Profile Api V1 Navin Sm Profiles  Profile Id  Preview Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Prévisualiser le managed-settings.json d'un profil",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/profiles/{profile_id}/usage": {
   "get": {
    "description": "Lit le proxy par le serveur (`GET /key/info` pour chaque clé vivante, `GET /spend/logs` sur la période) et renvoie `{available, reason, period, keys, models, totals, truncated, last_use}` : pour chaque clé son état vivant, sa dépense, son budget et sa date de remise à zéro, et sur la période ses appels, jetons, erreurs, modèles et dernier usage ; les mêmes chiffres par modèle et au total. `truncated` est vrai quand le proxy a plafonné les journaux à 10000 lignes (les chiffres de la période sont alors partiels). Quand le profil n'est pas lié, que la clé maître manque ou que le proxy est injoignable, `available` est faux avec `reason` et sans aucun chiffre : aucun zéro n'est inventé. Permission `navin_sm:keys` ; `404 navin_sm_profile_not_found`.",
    "operationId": "usage_api_v1_navin_sm_profiles__profile_id__usage_get",
    "parameters": [
     {
      "in": "path",
      "name": "profile_id",
      "required": true,
      "schema": {
       "title": "Profile Id",
       "type": "string"
      },
      "description": "Identifiant du profil."
     },
     {
      "in": "query",
      "name": "days",
      "required": false,
      "schema": {
       "default": 30,
       "maximum": 366,
       "minimum": 1,
       "title": "Days",
       "type": "integer"
      },
      "description": "Longueur de la période en jours, 1 à 366 (30 par défaut), finissant aujourd'hui."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Usage Api V1 Navin Sm Profiles  Profile Id  Usage Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Suivi des clés d'un profil",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/navin-sm/users": {
   "get": {
    "description": "Utilisateurs actifs du Manager (50 au plus, triés par nom) dont le nom, le nom affiché ou le courriel contient `q`, avec `id`, `username`, `display_name`, `email` et `source`. Permission `navin_sm:keys`.",
    "operationId": "assignable_users_api_v1_navin_sm_users_get",
    "parameters": [
     {
      "in": "query",
      "name": "q",
      "required": false,
      "schema": {
       "default": "",
       "maxLength": 100,
       "title": "Q",
       "type": "string"
      },
      "description": "Texte cherché dans le nom, le nom affiché et le courriel (100 caractères au plus)."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Assignable Users Api V1 Navin Sm Users Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les utilisateurs à qui attribuer une clé",
    "tags": [
     "navin-sm"
    ]
   }
  },
  "/api/v1/notification-targets": {
   "get": {
    "operationId": "list_targets_api_v1_notification_targets_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Targets Api V1 Notification Targets Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les cibles de notification",
    "tags": [
     "alerts"
    ],
    "description": "Chaque cible avec son `kind`, sa `config`, `min_severity`, `enabled` et la présence d'un secret HMAC (jamais sa valeur). Permission `alerts:manage`."
   },
   "post": {
    "operationId": "create_target_api_v1_notification_targets_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__alerts__TargetIn"
       }
      }
     },
     "required": true,
     "description": "Nom, type, configuration et sévérité minimale de la cible."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Target Api V1 Notification Targets Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Ajouter une cible de notification",
    "tags": [
     "alerts"
    ],
    "description": "Crée une cible et la renvoie (`201`). Une cible e-mail exige `config.to`, une cible webhook `config.url` (`http://` ou `https://`) ; une cible syslog utilise le serveur syslog des réglages. Permission `alerts:manage` ; `422 notification_target_invalid`. Audité en `notification_target.create`."
   }
  },
  "/api/v1/notification-targets/{target_id}": {
   "delete": {
    "operationId": "delete_target_api_v1_notification_targets__target_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "target_id",
      "required": true,
      "schema": {
       "title": "Target Id",
       "type": "string"
      },
      "description": "Identifiant de la cible de notification."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer une cible de notification",
    "tags": [
     "alerts"
    ],
    "description": "Supprime la cible. Répond `204`. Permission `alerts:manage` ; `404 notification_target_not_found`. Audité en `notification_target.delete`."
   },
   "patch": {
    "operationId": "patch_target_api_v1_notification_targets__target_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "target_id",
      "required": true,
      "schema": {
       "title": "Target Id",
       "type": "string"
      },
      "description": "Identifiant de la cible de notification."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/navin_manager__api__alerts__TargetPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de la cible à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch Target Api V1 Notification Targets  Target Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier une cible de notification",
    "tags": [
     "alerts"
    ],
    "description": "Modifie les champs fournis et renvoie la cible. Permission `alerts:manage`. Erreurs : `404 notification_target_not_found`, `422 notification_target_invalid`. Audité en `notification_target.update`."
   }
  },
  "/api/v1/notification-targets/{target_id}/test": {
   "post": {
    "operationId": "test_target_api_v1_notification_targets__target_id__test_post",
    "parameters": [
     {
      "in": "path",
      "name": "target_id",
      "required": true,
      "schema": {
       "title": "Target Id",
       "type": "string"
      },
      "description": "Identifiant de la cible de notification."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Test Target Api V1 Notification Targets  Target Id  Test Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Envoyer une notification de test",
    "tags": [
     "alerts"
    ],
    "description": "Envoie immédiatement un message de test (état `warning`, empreinte `test`) à la cible et renvoie `{\"ok\": true}`. Permission `alerts:manage`. Erreurs : `404 notification_target_not_found`, `502 notification_failed` (la cible a refusé ou est injoignable). Audité en `notification_target.test`."
   }
  },
  "/api/v1/operations/running": {
   "get": {
    "operationId": "running_api_v1_operations_running_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Running Api V1 Operations Running Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les opérations en cours",
    "tags": [
     "operations"
    ],
    "description": "Opérations en attente et en cours de chaque cluster où l'appelant détient `operations:read` (200 au plus, de la plus récente à la plus ancienne), avec leur nombre `count`. Utilisé par l'indicateur d'activité de la console."
   }
  },
  "/api/v1/operations/{operation_id}": {
   "get": {
    "operationId": "get_operation_api_v1_operations__operation_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "operation_id",
      "required": true,
      "schema": {
       "title": "Operation Id",
       "type": "string"
      },
      "description": "Identifiant de l'opération (une installation, un redémarrage, une sauvegarde...) dont les tâches s'exécutent sur les machines."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Operation Api V1 Operations  Operation Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une opération avec ses étapes et ses tâches",
    "tags": [
     "operations"
    ],
    "description": "Renvoie l'opération et ses étapes `stages` dans l'ordre ; chaque étape donne `seq`, `name_i18n`, `status`, `tolerate_failures` et ses tâches `tasks` (machine, type, service, composant, statut, tentatives, code de sortie, résumé, erreur, horaires). Permission `operations:read` sur son cluster ; `404 operation_not_found`."
   }
  },
  "/api/v1/operations/{operation_id}/abort": {
   "post": {
    "operationId": "abort_api_v1_operations__operation_id__abort_post",
    "parameters": [
     {
      "in": "path",
      "name": "operation_id",
      "required": true,
      "schema": {
       "title": "Operation Id",
       "type": "string"
      },
      "description": "Identifiant de l'opération (une installation, un redémarrage, une sauvegarde...) dont les tâches s'exécutent sur les machines."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Abort Api V1 Operations  Operation Id  Abort Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Interrompre une opération",
    "tags": [
     "operations"
    ],
    "description": "Demande l'arrêt d'une opération en attente ou en cours : les tâches non commencées sont annulées, celles en cours sont arrêtées par leurs agents. Renvoie `id`, `status` et `abort_requested`. Permission `operations:abort` ; `409 operation_not_running` quand elle est déjà terminée. Audité en `operation.abort`."
   }
  },
  "/api/v1/operations/{operation_id}/retry": {
   "post": {
    "operationId": "retry_api_v1_operations__operation_id__retry_post",
    "parameters": [
     {
      "in": "path",
      "name": "operation_id",
      "required": true,
      "schema": {
       "title": "Operation Id",
       "type": "string"
      },
      "description": "Identifiant de l'opération (une installation, un redémarrage, une sauvegarde...) dont les tâches s'exécutent sur les machines."
     },
     {
      "in": "query",
      "name": "approval_id",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Approval Id"
      },
      "description": "Approbation accordée pour la nouvelle opération sous `banking-secure`, utilisable une fois ; omettez-la pour utiliser l'approbation qui vous a été accordée."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Retry Api V1 Operations  Operation Id  Retry Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Relancer une opération échouée",
    "tags": [
     "operations"
    ],
    "description": "Crée une nouvelle opération à partir d'une opération `failed` ou `aborted` et renvoie son `operation_id`. La relance passe par les contrôles d'une nouvelle opération : un seul changement à la fois par service, état administratif du service, profil du cluster et, sous `banking-secure`, une nouvelle approbation pour une opération perturbatrice. Permission `operations:abort` ; `409 operation_not_retryable` dans tout autre statut. Audité en `operation.retry`."
   }
  },
  "/api/v1/operations/{operation_id}/tasks/{task_id}": {
   "get": {
    "operationId": "get_task_api_v1_operations__operation_id__tasks__task_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "operation_id",
      "required": true,
      "schema": {
       "title": "Operation Id",
       "type": "string"
      },
      "description": "Identifiant de l'opération (une installation, un redémarrage, une sauvegarde...) dont les tâches s'exécutent sur les machines."
     },
     {
      "in": "path",
      "name": "task_id",
      "required": true,
      "schema": {
       "title": "Task Id",
       "type": "string"
      },
      "description": "Identifiant de la tâche, issu des étapes de l'opération."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Task Api V1 Operations  Operation Id  Tasks  Task Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une tâche d'une opération",
    "tags": [
     "operations"
    ],
    "description": "Renvoie une tâche avec sa sortie complète `output`, `attempts` et `max_attempts`, `timeout_s`, `exit_code`, `duration_ms` et ses horaires. Permission `operations:read` ; `404 operation_not_found` ou `404 task_not_found`."
   }
  },
  "/api/v1/operations/{operation_id}/tasks/{task_id}/logs": {
   "get": {
    "operationId": "task_logs_api_v1_operations__operation_id__tasks__task_id__logs_get",
    "parameters": [
     {
      "in": "path",
      "name": "operation_id",
      "required": true,
      "schema": {
       "title": "Operation Id",
       "type": "string"
      },
      "description": "Identifiant de l'opération (une installation, un redémarrage, une sauvegarde...) dont les tâches s'exécutent sur les machines."
     },
     {
      "in": "path",
      "name": "task_id",
      "required": true,
      "schema": {
       "title": "Task Id",
       "type": "string"
      },
      "description": "Identifiant de la tâche."
     },
     {
      "in": "query",
      "name": "after_seq",
      "required": false,
      "schema": {
       "default": -1,
       "minimum": -1,
       "title": "After Seq",
       "type": "integer"
      },
      "description": "Renvoyer les lignes dont le numéro de séquence est supérieur à celui-ci. -1 par défaut (depuis la première ligne)."
     },
     {
      "in": "query",
      "name": "limit",
      "required": false,
      "schema": {
       "default": 1000,
       "maximum": 5000,
       "minimum": 1,
       "title": "Limit",
       "type": "integer"
      },
      "description": "Nombre maximal de lignes, de 1 à 5000. 1000 par défaut."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Task Logs Api V1 Operations  Operation Id  Tasks  Task Id  Logs Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire les lignes de journal d'une tâche",
    "tags": [
     "operations"
    ],
    "description": "Renvoie les lignes `lines` écrites après `after_seq` (chacune avec `seq`, `ts`, `stream` et `data`), `next_seq`\nà passer dans `after_seq` à l'appel suivant, et `done` : vrai une fois la tâche terminée et toutes les lignes\nlues. Pour suivre une tâche, rappelez-le avec `after_seq=next_seq` jusqu'à `done`. Permission `operations:read`."
   }
  },
  "/api/v1/pki/ca": {
   "get": {
    "operationId": "get_ca_api_v1_pki_ca_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Ca Api V1 Pki Ca Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire l'autorité de certification",
    "tags": [
     "pki"
    ],
    "description": "Renvoie le certificat de l'autorité du Manager (`ca_pem`), son expiration `not_after` et son empreinte `sha256`. Tout utilisateur connecté."
   }
  },
  "/api/v1/profiles": {
   "get": {
    "operationId": "list_profiles_api_v1_profiles_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response List Profiles Api V1 Profiles Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les profils d'installation",
    "tags": [
     "lifecycle"
    ],
    "description": "Chaque profil avec son libellé et sa description dans les deux langues, ses règles, l'exigence ou non d'approbations et la sévérité des règles réseau. Tout utilisateur connecté."
   }
  },
  "/api/v1/restore-targets": {
   "get": {
    "operationId": "list_restore_targets_api_v1_restore_targets_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Restore Targets Api V1 Restore Targets Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les destinations de restauration",
    "tags": [
     "backup"
    ],
    "description": "Les destinations de restauration déclarées par l'opérateur dans le réglage `restore_targets` : nom, environnement, type (`pg_dump`) et versions déclarées. Les chaînes de connexion et les noms des variables secrètes ne quittent jamais le serveur. Permission `restore:execute`."
   }
  },
  "/api/v1/restore-tests": {
   "get": {
    "operationId": "list_restore_tests_api_v1_restore_tests_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Restore Tests Api V1 Restore Tests Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les tests de restauration",
    "tags": [
     "backup"
    ],
    "description": "Les 200 derniers tests de restauration avec leur résultat et leur rapport signé. Permission `backup:read`."
   }
  },
  "/api/v1/restores": {
   "get": {
    "operationId": "list_restores_api_v1_restores_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Restores Api V1 Restores Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les plans de restauration",
    "tags": [
     "backup"
    ],
    "description": "Les 200 derniers plans de restauration avec leur statut et leur nombre d'approbations. Permission `backup:read`."
   }
  },
  "/api/v1/restores/plan": {
   "post": {
    "operationId": "plan_restore_api_v1_restores_plan_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RestorePlanIn"
       }
      }
     },
     "required": true,
     "description": "Artefact, environnement, destination et versions cibles."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Plan Restore Api V1 Restores Plan Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Planifier une restauration",
    "tags": [
     "backup"
    ],
    "description": "Crée un plan de restauration pour un artefact de sauvegarde : contrôle la compatibilité des versions sauvegardées avec les versions cibles (même version majeure) et liste les étapes (vérification de l'artefact, vérification de la destination, restauration, vérification du résultat). Avec `target_name`, l'artefact doit être un `pg_dump` disponible avec un inventaire `verification_inventory` et la destination une base PostgreSQL vide appartenant à un rôle restreint ; son identité est figée dans `target_snapshot`. Renvoie le plan (`201`). Permission `restore:execute`. Erreurs : `404 backup_artifact_not_found`, `501 restore_executor_unavailable` (destination inconnue), `409 restore_target_invalid`, `409 restore_target_not_empty`, `422 restore_inventory_required`. Audité en `restore.plan`."
   }
  },
  "/api/v1/restores/{plan_id}": {
   "get": {
    "operationId": "get_restore_api_v1_restores__plan_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de restauration."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Restore Api V1 Restores  Plan Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un plan de restauration",
    "tags": [
     "backup"
    ],
    "description": "Renvoie le plan avec sa compatibilité, ses étapes, ses approbateurs `approvers` et `required_approvals` (2). Permission `backup:read` ; `404 restore_plan_not_found`."
   }
  },
  "/api/v1/restores/{plan_id}/approve": {
   "post": {
    "operationId": "approve_restore_api_v1_restores__plan_id__approve_post",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de restauration."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Approve Restore Api V1 Restores  Plan Id  Approve Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Approuver un plan de restauration",
    "tags": [
     "backup"
    ],
    "description": "Ajoute l'approbation de l'appelant ; deux approbations par des personnes autres que le demandeur sont nécessaires. Permission `restore:execute`. Erreurs : `403 restore_self_approval`, `409 restore_not_approvable`. Audité en `restore.approve`."
   }
  },
  "/api/v1/restores/{plan_id}/dry-run": {
   "post": {
    "operationId": "dry_run_restore_api_v1_restores__plan_id__dry_run_post",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de restauration."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Dry Run Restore Api V1 Restores  Plan Id  Dry Run Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Essayer à blanc un plan de restauration",
    "tags": [
     "backup"
    ],
    "description": "Contrôle l'empreinte, le déchiffrement et la compatibilité sans écrire dans la destination, et renvoie les approbateurs enregistrés. Pour une destination nommée, contrôle aussi son identité, son vide et son rôle restreint (`checks.destination`, `checks.executor`). Une simulation réussie n'autorise pas l'exécution : deux approbations restent nécessaires. Permission `restore:execute`. Audité en `restore.dry_run`."
   }
  },
  "/api/v1/restores/{plan_id}/execute": {
   "post": {
    "operationId": "execute_restore_api_v1_restores__plan_id__execute_post",
    "parameters": [
     {
      "in": "path",
      "name": "plan_id",
      "required": true,
      "schema": {
       "title": "Plan Id",
       "type": "string"
      },
      "description": "Identifiant du plan de restauration."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Execute Restore Api V1 Restores  Plan Id  Execute Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Exécuter une restauration approuvée",
    "tags": [
     "backup"
    ],
    "description": "Exécute un plan approuvé avec destination : l'état `executing` et un jeton durable `execution_token` sont validés avant toute écriture. L'artefact est contrôlé à nouveau (taille, empreinte, déchiffrement), restauré par `pg_restore --single-transaction` avec le rôle restreint, puis ses lignes sont relues et comparées à l'inventaire (`result.inventory`, `rows`, `tables`). Succès : `executed`. Échec enregistré : `failed`, `rollback_performed: false`, HTTP `502 restore_execution_failed` ; le champ imbriqué `result.error` précise la cause, notamment `backup_corrupted`, `restore_target_busy` ou `restore_execution_timeout`. Une interruption du processus peut laisser `executing`, à inspecter manuellement. Aucun retour arrière ni rejeu automatique. Les refus avant exécution incluent `409 restore_not_approved`, `501 restore_executor_unavailable` et `409 restore_target_changed`. Permission `restore:execute`. Audité en `restore.execute.started` et `restore.execute`."
   }
  },
  "/api/v1/roles": {
   "get": {
    "operationId": "roles_api_v1_roles_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "additionalProperties": true,
          "type": "object"
         },
         "title": "Response Roles Api V1 Roles Get",
         "type": "array"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les rôles et leurs permissions",
    "tags": [
     "users"
    ],
    "description": "Chaque rôle avec son nom `name`, son libellé `label` dans les deux langues, ses permissions triées `permissions` et ses portées `scopes` (`global`, et `cluster` quand il peut être attribué sur un seul cluster). Permission `users:read`."
   }
  },
  "/api/v1/settings": {
   "get": {
    "description": "Renvoie `site_name`, `default_language`, `install`, `smtp`, `syslog`, `retention` et `proxy`, secrets masqués. Tout utilisateur connecté.",
    "operationId": "get_settings_view_api_v1_settings_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Settings View Api V1 Settings Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire les réglages",
    "tags": [
     "settings"
    ]
   },
   "put": {
    "operationId": "put_settings_api_v1_settings_put",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SettingsBody"
       }
      }
     },
     "required": true,
     "description": "Sections des réglages à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Settings Api V1 Settings Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier les réglages",
    "tags": [
     "settings"
    ],
    "description": "Modifie les sections et les champs fournis ; une section ou un champ omis est conservé. Renvoie les réglages. Permission `settings:manage`. Erreurs : `422 proxy_invalid` (URL de proxy avec identifiants ou hors http(s)), `422 validation_error`. Audité en `settings.update`."
   }
  },
  "/api/v1/stack": {
   "get": {
    "operationId": "stack_api_v1_stack_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Stack Api V1 Stack Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la synthèse du catalogue",
    "tags": [
     "stack"
    ],
    "description": "Nom, version et catégories de la pile et une synthèse de chaque service qu'elle propose (composants, modes, dépendances, licence). Tout utilisateur connecté."
   }
  },
  "/api/v1/stack/services/{name}": {
   "get": {
    "operationId": "service_api_v1_stack_services__name__get",
    "parameters": [
     {
      "in": "path",
      "name": "name",
      "required": true,
      "schema": {
       "title": "Name",
       "type": "string"
      },
      "description": "Nom du service du catalogue, par exemple `postgresql`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Service Api V1 Stack Services  Name  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un service du catalogue",
    "tags": [
     "stack"
    ],
    "description": "Description publique d'un service du catalogue : composants, modes, méthodes, mode de livraison, couche et haute disponibilité. Tout utilisateur connecté ; `404` pour un service inconnu."
   }
  },
  "/api/v1/system/health": {
   "get": {
    "operationId": "health_api_v1_system_health_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Health Api V1 System Health Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la santé du Manager",
    "tags": [
     "system"
    ],
    "description": "Renvoie `version`, `database` (`ok`, `latency_ms`), `engine` (`leader`, `last_tick_at`, `lag_s`, `queued_tasks`), `agents` (`online` : battement de cœur dans les 60 dernières secondes, `lost`, `total`) et `scheduler.jobs` (dernière exécution et dernière erreur de chaque tâche). Permission `system:read`."
   }
  },
  "/api/v1/system/supported-os": {
   "get": {
    "description": "Liste publiée des systèmes d'exploitation et des versions pris en charge par les agents et les services. Tout utilisateur connecté.",
    "operationId": "supported_os_api_v1_system_supported_os_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Supported Os Api V1 System Supported Os Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les systèmes d'exploitation pris en charge",
    "tags": [
     "system"
    ]
   }
  },
  "/api/v1/telemetry": {
   "get": {
    "description": "Renvoie `enabled`, `database`, `flush_seconds`, `retention_days`, `stats` (`buffered`, `written`, `dropped`, `failures`, `last_error`) et les tables exportées `tables`. Permission `cluster:read` sur au moins un cluster.",
    "operationId": "telemetry_api_v1_telemetry_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Telemetry Api V1 Telemetry Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire l'état de l'export de télémétrie",
    "tags": [
     "telemetry"
    ]
   }
  },
  "/api/v1/users": {
   "get": {
    "operationId": "list_users_api_v1_users_get",
    "parameters": [
     {
      "in": "query",
      "name": "limit",
      "required": false,
      "schema": {
       "default": 50,
       "maximum": 500,
       "minimum": 1,
       "title": "Limit",
       "type": "integer"
      },
      "description": "Nombre maximal d'utilisateurs dans la page, de 1 à 500. 50 par défaut."
     },
     {
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 200,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Cursor"
      },
      "description": "Le `next_cursor` de la page précédente (le dernier nom d'utilisateur, en minuscules). Omettez-le pour la première page."
     },
     {
      "in": "query",
      "name": "q",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "maxLength": 100,
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Q"
      },
      "description": "Texte recherché, sans tenir compte de la casse, dans le nom d'utilisateur, le nom affiché et l'e-mail."
     },
     {
      "in": "query",
      "name": "source",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "enum": [
          "local",
          "ldap",
          "oidc"
         ],
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Source"
      },
      "description": "Uniquement les utilisateurs de cette origine : `local`, `ldap` ou `oidc`."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Users Api V1 Users Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lister les utilisateurs",
    "tags": [
     "users"
    ],
    "description": "Utilisateurs triés par nom d'utilisateur, chacun avec ses attributions de rôle, l'état de sa MFA, son verrouillage et sa dernière connexion. Paginé avec `limit` et `cursor`. Permission `users:read`."
   },
   "post": {
    "operationId": "create_user_api_v1_users_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/UserCreate"
       }
      }
     },
     "required": true,
     "description": "Le nouveau compte."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create User Api V1 Users Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Créer un utilisateur local",
    "tags": [
     "users"
    ],
    "description": "Crée un compte local avec son mot de passe et ses attributions de rôle, et le renvoie (`201`). Permission `users:manage`. Erreurs : `409 username_taken`, `422 password_policy`, `422 role_invalid`, `422 role_global_only`, `422 binding_cluster_not_found`, `403 user_manage_denied` (accorder `admin` sans être administrateur complet). Audité en `user.create`."
   }
  },
  "/api/v1/users/{user_id}": {
   "delete": {
    "operationId": "delete_user_api_v1_users__user_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "user_id",
      "required": true,
      "schema": {
       "title": "User Id",
       "type": "string"
      },
      "description": "Identifiant du compte utilisateur de la console."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer un utilisateur",
    "tags": [
     "users"
    ],
    "description": "Supprime le compte. Répond `204`. Permission `users:manage`. Erreurs : `404 user_not_found`, `409 cannot_delete_self`, `409 last_admin`, `403 user_manage_denied`. Audité en `user.delete`."
   },
   "get": {
    "operationId": "get_user_api_v1_users__user_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "user_id",
      "required": true,
      "schema": {
       "title": "User Id",
       "type": "string"
      },
      "description": "Identifiant du compte utilisateur de la console."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get User Api V1 Users  User Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire un utilisateur",
    "tags": [
     "users"
    ],
    "description": "Renvoie le compte avec ses attributions de rôle (`role`, `cluster_id`, `source`). Permission `users:read` ; `404 user_not_found`."
   },
   "patch": {
    "operationId": "patch_user_api_v1_users__user_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "user_id",
      "required": true,
      "schema": {
       "title": "User Id",
       "type": "string"
      },
      "description": "Identifiant du compte utilisateur de la console."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/UserPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de l'utilisateur à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Patch User Api V1 Users  User Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier ou désactiver un utilisateur",
    "tags": [
     "users"
    ],
    "description": "Modifie les champs fournis, conserve les autres et renvoie l'utilisateur. Désactiver un compte révoque immédiatement ses sessions. Permission `users:manage`. Erreurs : `404 user_not_found`, `409 cannot_disable_self`, `409 last_admin`, `403 user_manage_denied`. Audité en `user.update`."
   }
  },
  "/api/v1/users/{user_id}/mfa/reset": {
   "post": {
    "operationId": "reset_mfa_api_v1_users__user_id__mfa_reset_post",
    "parameters": [
     {
      "in": "path",
      "name": "user_id",
      "required": true,
      "schema": {
       "title": "User Id",
       "type": "string"
      },
      "description": "Identifiant du compte utilisateur de la console."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Reset Mfa Api V1 Users  User Id  Mfa Reset Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Réinitialiser le second facteur d'un utilisateur",
    "tags": [
     "users"
    ],
    "description": "Désactive la MFA de l'utilisateur, supprime son secret et ses codes de secours et révoque ses sessions ; l'utilisateur s'enrôle à nouveau à la prochaine connexion quand son rôle exige la MFA. Renvoie l'utilisateur. Permission `users:manage`. Audité en `user.mfa_reset`."
   }
  },
  "/api/v1/users/{user_id}/password": {
   "post": {
    "operationId": "reset_password_api_v1_users__user_id__password_post",
    "parameters": [
     {
      "in": "path",
      "name": "user_id",
      "required": true,
      "schema": {
       "title": "User Id",
       "type": "string"
      },
      "description": "Identifiant du compte utilisateur de la console."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PasswordReset"
       }
      }
     },
     "required": true,
     "description": "Le nouveau mot de passe et l'obligation ou non de le changer à la prochaine connexion."
    },
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Définir le mot de passe d'un utilisateur local",
    "tags": [
     "users"
    ],
    "description": "Définit un nouveau mot de passe conforme à la politique, déverrouille le compte et révoque ses sessions. Répond `204`. Permission `users:manage`. Erreurs : `409 not_local_user`, `422 password_policy`, `404 user_not_found`, `403 user_manage_denied`. Audité en `user.password_reset`."
   }
  },
  "/api/v1/users/{user_id}/roles": {
   "put": {
    "description": "Remplace les attributions manuelles par la liste fournie ; les attributions synchronisées depuis LDAP ou OIDC sont conservées. Quand un nouveau rôle exige la MFA et que l'utilisateur n'en a pas, ses sessions sont révoquées. Renvoie l'utilisateur. Permission `users:manage`. Erreurs : `409 last_admin`, `422 role_invalid`, `422 role_global_only`, `422 binding_cluster_not_found`, `403 user_manage_denied`. Audité en `user.roles` avec les attributions avant et après.",
    "operationId": "put_roles_api_v1_users__user_id__roles_put",
    "parameters": [
     {
      "in": "path",
      "name": "user_id",
      "required": true,
      "schema": {
       "title": "User Id",
       "type": "string"
      },
      "description": "Identifiant du compte utilisateur de la console."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RolesBody"
       }
      }
     },
     "required": true,
     "description": "La liste complète des attributions manuelles."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Put Roles Api V1 Users  User Id  Roles Put",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Remplacer les attributions de rôle d'un utilisateur",
    "tags": [
     "users"
    ]
   }
  },
  "/api/v1/users/{user_id}/unlock": {
   "post": {
    "operationId": "unlock_user_api_v1_users__user_id__unlock_post",
    "parameters": [
     {
      "in": "path",
      "name": "user_id",
      "required": true,
      "schema": {
       "title": "User Id",
       "type": "string"
      },
      "description": "Identifiant du compte utilisateur de la console."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Unlock User Api V1 Users  User Id  Unlock Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Déverrouiller un utilisateur",
    "tags": [
     "users"
    ],
    "description": "Lève le verrouillage posé après des échecs de connexion répétés et remet le compteur d'échecs à zéro. Renvoie l'utilisateur. Permission `users:manage`. Audité en `user.unlock`."
   }
  },
  "/api/v1/vault/entries": {
   "get": {
    "description": "Services du coffre triés par nom, avec leur URL et leur identifiant mais sans leur mot de passe ni leur clé. Permission `vault:manage`.",
    "operationId": "list_entries_api_v1_vault_entries_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response List Entries Api V1 Vault Entries Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lister les entrées du coffre",
    "tags": [
     "vault"
    ]
   },
   "post": {
    "operationId": "create_entry_api_v1_vault_entries_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EntryIn"
       }
      }
     },
     "required": true,
     "description": "Le service et ses identifiants."
    },
    "responses": {
     "201": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Create Entry Api V1 Vault Entries Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Ajouter une entrée au coffre",
    "tags": [
     "vault"
    ],
    "description": "Crée une entrée manuelle et la renvoie (`201`). Permission `vault:manage`. Erreurs : `409 vault_name_taken`, `422 vault_name_required`. Audité en `vault.create`."
   }
  },
  "/api/v1/vault/entries/{entry_id}": {
   "delete": {
    "operationId": "delete_entry_api_v1_vault_entries__entry_id__delete",
    "parameters": [
     {
      "in": "path",
      "name": "entry_id",
      "required": true,
      "schema": {
       "title": "Entry Id",
       "type": "string"
      },
      "description": "Identifiant de l'entrée du coffre."
     }
    ],
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Supprimer une entrée du coffre",
    "tags": [
     "vault"
    ],
    "description": "Supprime l'entrée. Répond `204`. Permission `vault:manage` ; `404 vault_entry_not_found`. Audité en `vault.delete`."
   },
   "get": {
    "operationId": "get_entry_api_v1_vault_entries__entry_id__get",
    "parameters": [
     {
      "in": "path",
      "name": "entry_id",
      "required": true,
      "schema": {
       "title": "Entry Id",
       "type": "string"
      },
      "description": "Identifiant de l'entrée du coffre."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Get Entry Api V1 Vault Entries  Entry Id  Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Lire une entrée du coffre",
    "tags": [
     "vault"
    ],
    "description": "Renvoie la fiche d'une entrée sans son mot de passe ni sa clé. Permission `vault:manage` ; `404 vault_entry_not_found`."
   },
   "patch": {
    "operationId": "update_entry_api_v1_vault_entries__entry_id__patch",
    "parameters": [
     {
      "in": "path",
      "name": "entry_id",
      "required": true,
      "schema": {
       "title": "Entry Id",
       "type": "string"
      },
      "description": "Identifiant de l'entrée du coffre."
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EntryPatch"
       }
      }
     },
     "required": true,
     "description": "Champs de l'entrée à modifier."
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Update Entry Api V1 Vault Entries  Entry Id  Patch",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Modifier une entrée du coffre",
    "tags": [
     "vault"
    ],
    "description": "Modifie les champs fournis et renvoie l'entrée. Permission `vault:manage`. Erreurs : `404 vault_entry_not_found`, `409 vault_name_taken`, `422 vault_name_required`, `409 vault_entry_managed` (identifiants d'une console dont le Manager assure la rotation). Audité en `vault.update` (noms des champs uniquement)."
   }
  },
  "/api/v1/vault/entries/{entry_id}/reveal": {
   "post": {
    "description": "Renvoie en clair le mot de passe et la clé d'un service, pour les afficher et les copier dans la console. Permission `vault:manage` ; `404 vault_entry_not_found`. Audité en `vault.reveal` (nom du service uniquement).",
    "operationId": "reveal_entry_api_v1_vault_entries__entry_id__reveal_post",
    "parameters": [
     {
      "in": "path",
      "name": "entry_id",
      "required": true,
      "schema": {
       "title": "Entry Id",
       "type": "string"
      },
      "description": "Identifiant de l'entrée du coffre."
     }
    ],
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Reveal Entry Api V1 Vault Entries  Entry Id  Reveal Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Révéler le mot de passe et la clé d'une entrée",
    "tags": [
     "vault"
    ]
   }
  },
  "/api/v1/vault/import": {
   "post": {
    "description": "Crée ou rafraîchit les entrées des services installés de chaque cluster ; les champs modifiés à la main sont conservés. Renvoie les noms des entrées par résultat. Permission `vault:manage`. Audité en `vault.import`.",
    "operationId": "import_entries_api_v1_vault_import_post",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": {
          "items": {
           "type": "string"
          },
          "type": "array"
         },
         "title": "Response Import Entries Api V1 Vault Import Post",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Importer les identifiants des services installés",
    "tags": [
     "vault"
    ]
   }
  },
  "/api/v1/version": {
   "get": {
    "operationId": "version_api_v1_version_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "title": "Response Version Api V1 Version Get",
         "type": "object"
        }
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Lire la version du Manager",
    "tags": [
     "meta"
    ],
    "description": "Renvoie `name` (`navin-manager`), `version`, `environment` et la pile `stack` du catalogue (`name`, `version`, nombre de `services`). Public."
   }
  }
 },
 "tags": [
  {
   "name": "agent",
   "x-displayName": "Agent",
   "description": "Enregistrement d'un nouvel agent Navin. L'agent est le petit démon installé sur chaque machine pilotée par le\nManager : il remonte les caractéristiques et les métriques de la machine et exécute les tâches des opérations.\nTous ses autres appels (battements de cœur, récupération des tâches, résultats, paquets) passent par l'écoute des\nagents du Manager sous TLS mutuel et ne font pas partie de cette API.\n\nDéroulement type :\n\n1. un administrateur crée un jeton d'amorçage pour un cluster (voir **Clusters**) et le donne au script\n   d'installation de l'agent ;\n2. l'agent génère sa clé privée et une demande de signature de certificat (CSR), puis appelle\n   `POST /agent/v1/register` avec le jeton, ses noms et ses caractéristiques ;\n3. le Manager vérifie le jeton (un seul cluster, durée limitée, nombre d'utilisations limité), enregistre la\n   machine, signe un certificat client dont le sujet vient du serveur (FQDN, nom d'hôte, IP, identifiant SPIFFE) et\n   jamais de la CSR, puis le renvoie avec le secret de l'agent et l'autorité de certification ;\n4. l'agent se connecte ensuite à l'écoute des agents avec ce certificat.\n\nAucune permission d'utilisateur n'est nécessaire : le jeton d'amorçage tient lieu d'identifiant. Chaque\nenregistrement, accepté ou refusé, est audité (`host.register`, ou `host.reregister` pour une machine déjà connue)."
  },
  {
   "name": "agent-templates",
   "x-displayName": "Gabarits d'agents",
   "description": "Le registre des gabarits d'agents (Service Templates des produits Navin, par exemple l'Agent RAG Navin Enterprise) : chaque gabarit avec son nom, son éditeur, sa version, son statut et le service du catalogue qui l'installe. Un gabarit n'est `published` que si ce service existe dans le catalogue et que le gabarit a été publié, par la version livrée (statut published) ou sur ce Manager par un administrateur (`POST .../publish`, la validation du client : la définition de service rejoint le catalogue de ce Manager) ; sinon `reason` indique pourquoi il ne peut pas être déployé et `publishable` s'il peut être publié ici. La lecture est ouverte à tout utilisateur connecté, comme le catalogue de la pile ; publier et retirer demandent `agent_templates:publish`."
  },
  {
   "name": "alerts",
   "x-displayName": "Alertes et notifications",
   "description": "Alertes levées par le Manager à partir de ses définitions d'alertes (machine hors ligne, disque plein, service en\nmauvaise santé, certificat proche de l'expiration...), et cibles de notification qui les reçoivent.\n\nUne alerte est identifiée par une empreinte (définition et cible) et passe entre les états `ok`, `warning`,\n`critical` et `unknown` ; chaque changement est conservé dans son historique et notifie les cibles activées dont la\nsévérité minimale est atteinte. Une alerte résolue a un `resolved_at`. Les machines en maintenance ne lèvent\naucune alerte.\n\nDéroulement type :\n\n1. `GET /clusters/{cluster_id}/alerts` liste les alertes ouvertes ; `POST /alerts/{alert_id}/ack` en acquitte une\n   avec une note ;\n2. `PATCH /alert-definitions/{definition_id}` ajuste les seuils et l'intervalle d'une définition ;\n3. `POST /notification-targets` ajoute une cible e-mail, webhook ou syslog, `POST .../test` envoie un message de\n   test.\n\nLes webhooks portent un en-tête `X-NM-Signature: t=<heure unix>,v1=<HMAC-SHA256 hexadécimal de \"<t>.<corps>\">`\nquand la cible a un secret HMAC. Permissions : `alerts:read` pour lire, `alerts:manage` pour acquitter, ajuster et\ngérer les cibles."
  },
  {
   "name": "audit",
   "x-displayName": "Audit",
   "description": "Le journal d'audit du Manager : chaque changement, connexion, refus et lecture sensible, avec l'acteur\n(utilisateur, jeton d'API, agent ou système), l'action (par exemple `service.install`), la cible, le cluster, le\nrésultat (`success`, `failure`, `denied`), l'adresse du client et des détails. Les événements sont chaînés : chacun\nporte l'empreinte du précédent, si bien qu'un événement modifié ou supprimé rompt la chaîne.\n\nDéroulement type :\n\n1. `GET /audit` avec des filtres pour enquêter (du plus récent au plus ancien, paginé avec `cursor`) ;\n2. `GET /audit/verify` pour vérifier que la chaîne est intacte ;\n3. `GET /audit/export` pour transmettre les événements à un SIEM ou à un auditeur (NDJSON ou CSV).\n\nPermission `audit:read` (globale uniquement ; rôles `admin`, `security_admin`, `auditor`). Les vérifications et\nles exports sont eux-mêmes audités."
  },
  {
   "name": "auth",
   "x-displayName": "Authentification",
   "description": "Connexion à la console et déconnexion : fournisseurs d'identité, configuration initiale, connexion locale et LDAP,\nsecond facteur (TOTP), changement de mot de passe, session et connexion OIDC.\n\nDéroulement d'une connexion :\n\n1. `GET /auth/providers` liste les méthodes disponibles (locale, LDAP, fournisseurs OIDC) et indique si la\n   configuration initiale reste à faire ;\n2. `POST /auth/login` vérifie le nom d'utilisateur et le mot de passe (compte local ou liaison LDAP) ;\n3. quand le second facteur est exigé, la réponse est `{\"mfa_required\": true, \"mfa_token\": ...}` : envoyez un code\n   TOTP ou un code de secours à `POST /auth/mfa/verify`, ou enrôlez-vous d'abord avec `/auth/mfa/enroll/start` puis\n   `/auth/mfa/enroll/confirm` quand `enrollment_required` vaut vrai ;\n4. quand le mot de passe doit être changé, l'API répond `403 password_expired` avec un `password_change_token` dans\n   `details` : envoyez-le avec le nouveau mot de passe à `POST /auth/password/change`, puis reconnectez-vous ;\n5. sinon la session s'ouvre : le cookie `nm_session` est posé et la réponse donne l'utilisateur, ses permissions\n   effectives, le `csrf_token` à envoyer dans `X-CSRF-Token` sur les requêtes non sûres, et `expires_at`.\n\nContrôles d'une connexion, dans l'ordre : limites de débit (adresse, nom d'utilisateur), premier facteur,\nverrouillage, compte désactivé, second facteur, expiration du mot de passe. Un nom d'utilisateur inconnu prend le\nmême temps qu'un mauvais mot de passe. Des échecs répétés verrouillent le compte (`423 account_locked`) pendant la\ndurée de la politique de sécurité. Chaque résultat est audité (`auth.login`, `auth.mfa`, `auth.password_change`...).\nCes points d'accès n'exigent aucune permission ; ceux de la MFA en libre service exigent une session de navigateur\n(un jeton d'API est refusé avec `403 session_required`)."
  },
  {
   "name": "auth-config",
   "x-displayName": "Fournisseurs d'identité et politique de sécurité",
   "description": "Configuration de la connexion des utilisateurs : l'annuaire LDAP, les fournisseurs OIDC et la politique de\nsécurité (mots de passe, verrouillage, délais de session, rôles qui exigent la MFA).\n\nLes secrets (mot de passe de liaison LDAP, secret client OIDC) sont scellés au repos et relus sous la forme\n`{\"secret\": true, \"set\": true|false}`. À l'écriture, renvoyer cet objet (ou omettre le champ) conserve la valeur\nenregistrée, une chaîne la remplace, une chaîne vide l'efface.\n\nDéroulement type pour LDAP :\n\n1. `PUT /auth-config/ldap` avec les URL, le compte de liaison, les bases et filtres de recherche et les\n   correspondances entre groupes et rôles ;\n2. `POST /auth-config/ldap/test` avec un compte de test pour contrôler la liaison, la recherche de l'utilisateur et\n   les groupes trouvés ;\n3. passez `enabled` à vrai ; les utilisateurs se connectent alors avec `provider: \"ldap\"`.\n\nPour OIDC : `POST /auth-config/oidc` pour ajouter un fournisseur (émetteur, identifiant et secret client, portées,\nrevendications, correspondances), puis `POST /auth-config/oidc/{provider_id}/test` pour contrôler son document de\ndécouverte et ses clés. L'URI de redirection à déclarer chez le fournisseur est\n`<URL publique>/api/v1/auth/oidc/{provider_id}/callback`.\n\nPermission `auth:manage` (globale). Chaque changement est audité (`auth_config.*`)."
  },
  {
   "name": "availability",
   "x-displayName": "Groupes de haute disponibilité",
   "description": "Un groupe de haute disponibilité décrit comment un composant d'un cluster reste disponible : son mode de HA\n(`single`, `active_passive`, `active_active`, `quorum`), son profil (`standalone`, `ha` avec au moins 2 réplicas\ndans 2 domaines de panne, `critical-ha` avec au moins 3 dans 3), ses membres et leurs domaines de panne, sa\npolitique de bascule et ses objectifs de reprise (RTO, RPO). Le groupe indique son état de HA à partir des\nbattements de cœur de ses membres.\n\nLe mode de déploiement indique ce que le Manager peut prouver : `SINGLE_HOST` (aucune HA promise), `NODE_GROUP`\n(bascule prouvée sur des machines), `K8S_EXISTING` et `K8S_MANAGED` (manifestes validés hors ligne),\n`MULTI_CLUSTER`, `EXTERNAL_MANAGED`. Un groupe `CLIENT_MANAGED` ou `EXTERNAL_MANAGED` n'est jamais démarré,\narrêté, mis à niveau, basculé ni restauré par le Manager.\n\nDéroulement type :\n\n1. `GET /ha-modes` montre ce que prend en charge chaque mode de déploiement ;\n2. `POST /ha-groups` déclare un groupe avec ses membres ;\n3. `POST /ha-groups/{group_id}/test-failover` lance un test de bascule sous un bail avec un jeton de fencing ;\n4. `GET /ha-groups/{group_id}/operations` montre les tests avec le RTO et le RPO observés, les opérations de\n   fencing et les événements.\n\nPermissions : `cluster:read` pour lire, `cluster:manage` pour créer un groupe, `services:operate` pour tester une\nbascule. Les opérations critiques échouent de façon fermée : si l'écriture en base ou dans l'audit échoue,\nl'opération entière est refusée."
  },
  {
   "name": "backup",
   "x-displayName": "Sauvegarde, restauration et reprise après sinistre",
   "description": "Le gestionnaire de sauvegarde et de reprise : cibles et plans de sauvegarde, artefacts de sauvegarde avec leurs\nvérifications, conservation, tests de restauration, restaurations et reprise après sinistre (sites, plans,\nexercices, bascules, changements DNS et d'équilibreur de charge).\n\nDéroulement d'une sauvegarde :\n\n1. `POST /backup-targets` déclare où vont les sauvegardes (`s3`, `azure_blob`, `gcs`, `ibm_cos`, `seaweedfs` ou\n   `filesystem`) avec leurs capacités ;\n2. `POST /backup-plans` fixe ce qui est sauvegardé, la fréquence, la clé de chiffrement et la durée de\n   conservation (un plan immuable exige une cible avec verrouillage d'objet et rétention) ;\n3. chaque sauvegarde est enregistrée avec `POST /backup-plans/{plan_id}/backups` : le Manager calcule l'empreinte\n   du fichier (SHA-256) et fixe son expiration ; `POST /backups/{backup_id}/verify` contrôle l'empreinte et le\n   déchiffrement, et `POST /backups/{backup_id}/restore-tests` la restaure dans un répertoire isolé et signe le\n   rapport.\n\nDéroulement d'une restauration (quatre yeux) : `GET /restore-targets` liste les destinations déclarées par\nl'opérateur, `POST /restores/plan` contrôle la compatibilité et fige l'identité d'une destination PostgreSQL\nvide, puis deux approbations par des personnes autres que le demandeur (`POST /restores/{plan_id}/approve`), un\nessai à blanc `dry-run` facultatif, et `execute` : le plan passe `executing` avant toute écriture, l'export\n`pg_dump` au format custom est restauré en une transaction par un rôle restreint, puis les lignes restaurées sont\nrelues et comparées à l'inventaire enregistré avec la sauvegarde. Un échec est signalé `failed` ; rien n'est\nannulé automatiquement et un plan n'est jamais rejoué.\n\nReprise après sinistre : `POST /dr-plans` entre deux sites, puis des exercices ou des demandes de bascule\napprouvés par une autre personne et clôturés avec le RPO et le RTO réellement observés (rien n'est estimé). Les\nchangements DNS et d'équilibreur de charge sont déclarés et évalués, jamais appliqués par le Manager.\n\nPermissions (toutes globales) : `backup:read` pour lire, `backup:manage` pour les cibles, plans, sauvegardes et\ntests, `restore:execute` pour les restaurations, `dr:manage` pour la reprise après sinistre. Chaque changement est\naudité (`backup.*`, `restore.*`, `dr.*`)."
  },
  {
   "name": "blueprint",
   "x-displayName": "Blueprints",
   "description": "Un blueprint décrit les services d'un cluster sous une forme portable, pour reconstruire la même plateforme sur un\nautre cluster : les services avec leur mode, leur méthode, leur configuration non secrète et leurs liaisons, et des\ngroupes de machines (ensembles de composants qui tournent ensemble, avec leur nombre de machines). Sa forme :\n\n```json\n{\"schema\": \"navin-manager/blueprint/v1\", \"stack\": {\"name\": \"...\", \"version\": \"...\"},\n \"services\": [{\"name\": \"postgresql\", \"mode\": \"...\", \"method\": \"...\", \"managed\": true, \"config\": {}, \"bindings\": {}}],\n \"host_groups\": [{\"name\": \"host_group_1\", \"cardinality\": 3, \"components\": [{\"service\": \"postgresql\", \"component\": \"server\"}],\n                  \"hosts\": [\"<db1-host>\"]}]}\n```\n\nDéroulement type :\n\n1. `GET /clusters/{source}/blueprint` exporte un cluster (les services connectés sont laissés de côté : ce sont des\n   points d'accès propres à un client, pas une partie portable) ;\n2. `POST /clusters/{cible}/blueprint/validate` avec un `host_mapping` qui associe chaque groupe de machines à des\n   machines du cluster cible ;\n3. `POST /clusters/{cible}/blueprint/apply` installe le tout en une seule opération.\n\nPermission `cluster:manage` sur le cluster ; l'application exige aussi `services:install`."
  },
  {
   "name": "clusters",
   "x-displayName": "Clusters",
   "description": "Un cluster est un environnement géré par le Navin Manager : un ensemble de machines (ou un cluster Kubernetes) avec\nses services. Il a un mode `mode` (`production`, `preprod`, `test`, `development`, `sandbox`) et un profil\nd'installation `profile` qui fixe les règles que ses opérations doivent respecter :\n\n- `development` : seul le catalogue doit être valide ;\n- `standard` : TLS sur le Manager, certificats clients pour les agents, TLS sur les services, MFA pour les\n  administrateurs ;\n- `banking-secure` : les règles strictes, plus le miroir local seul, une copie distante de l'audit, aucun service\n  de bac à sable et l'approbation d'une seconde personne pour les opérations perturbatrices ;\n- `air-gapped` : règles strictes, miroir seul, aucun trafic sortant ;\n- `hybrid` : règles standard plus TLS vers les services externes.\n\nTant qu'une règle du profil échoue, les opérations qui installeraient ou démarreraient quelque chose sont refusées\navec `409 profile_blocked` (voir **Cycle de vie** pour l'évaluation d'un profil).\n\nDéroulement type :\n\n1. `POST /clusters` crée le cluster ;\n2. `POST /clusters/{cluster_id}/bootstrap-tokens` crée un jeton et renvoie les commandes qui installent et\n   enregistrent l'agent sur une machine (ou utilisez l'amorçage SSH de **Machines**) ;\n3. les machines apparaissent dans `GET /clusters/{cluster_id}/hosts` ; `GET /clusters/{cluster_id}/summary` et\n   `/topology` donnent la vue d'ensemble ;\n4. installez des services (voir **Services**).\n\nPermissions : `clusters:create` (globale) pour créer, `cluster:read` pour lire, `cluster:manage` pour modifier ou\nsupprimer, `hosts:manage` pour les jetons d'amorçage."
  },
  {
   "name": "configs",
   "x-displayName": "Configurations",
   "description": "La configuration d'un service géré : ses paramètres (déclarés par le catalogue avec leurs types, leurs valeurs\npar défaut et leurs sections), enregistrés en versions numérotées, avec des groupes de configuration qui\nsurchargent des valeurs sur certaines machines.\n\nEnregistrer une configuration ne touche pas les machines : cela crée une nouvelle version et signale dans\n`stale_hosts` les machines dont les fichiers produits changeraient. L'application pousse les fichiers et redémarre\nles composants.\n\nDéroulement type :\n\n1. `GET .../config` lit les valeurs courantes, les définitions des paramètres, les recommandations et les groupes ;\n2. `PUT .../config` enregistre les valeurs modifiées dans une nouvelle version (validée selon les définitions des\n   paramètres) ;\n3. `GET .../config/preview` montre les fichiers qu'une machine recevrait, `GET .../config/diff` compare deux\n   versions ;\n4. `POST .../config/apply` lance l'opération qui écrit les fichiers et redémarre les composants (progressivement\n   par défaut) ;\n5. `POST .../config/versions/{version}/restore` réenregistre une ancienne version comme la plus récente.\n\nLes paramètres secrets sont relus sous la forme `{\"secret\": true, \"set\": true|false}` ; renvoyer cet objet\nconserve la valeur enregistrée, `null` supprime une valeur. Permissions : `configs:read` pour lire, `configs:edit`\npour modifier et appliquer. Les changements sont audités (`config.*`, `config_group.*`)."
  },
  {
   "name": "connections",
   "x-displayName": "Connexions externes",
   "description": "Connexions vers des systèmes extérieurs aux clusters que les services peuvent utiliser : bases de données\n(PostgreSQL, MySQL, ClickHouse), moteurs de recherche (Elasticsearch, OpenSearch, Qdrant), messagerie (Kafka,\nNATS, Redis), stockage objet S3, annuaires LDAP et OIDC, SMTP, syslog et simples points d'accès HTTP. Un service\nse lie à une connexion par ses liaisons `bindings` (type `connection`).\n\nChaque connexion a un type `type`, des paramètres `params` contrôlés selon ce type, et un secret facultatif\n`secret` (mot de passe, jeton ou clé) scellé au repos et relu sous la forme\n`{\"secret\": true, \"set\": true|false, \"name\": ...}`.\n\nDéroulement type :\n\n1. `POST /connections/validate` contrôle les paramètres et exécute le vrai test du protocole avec le secret, sans\n   rien enregistrer ;\n2. `POST /connections` enregistre la connexion ;\n3. `POST /connections/{connection_id}/test` la teste à nouveau à tout moment et conserve le résultat dans\n   `last_test`.\n\nPermissions : `connections:read` pour lire, `connections:manage` pour modifier et tester (toutes deux globales).\nLes changements et les tests sont audités (`connection.*`) ; le secret n'est jamais renvoyé ni audité."
  },
  {
   "name": "consoles",
   "x-displayName": "Consoles des services",
   "description": "Les interfaces web locales des services installés (par exemple la console d'administration de Keycloak ou d'un\ncourtier de messages), ouvertes depuis le Manager.\n\nPour chaque console, le Manager connaît son adresse interne (publiée par un administrateur, sinon le premier lien\nrapide de la configuration installée) et évalue les contrôles qu'exige une banque : HTTPS, un nom DNS interne,\nune authentification et aucune ressource chargée depuis Internet. Sur un cluster de production ou de\npréproduction, une console qui échoue à un contrôle ne peut pas être ouverte. Les consoles des identités et des\nsecrets (Keycloak, OpenBao, OpenFGA) sont réservées aux administrateurs du coffre : les autres les\nvoient verrouillées, sans leur adresse.\n\nDéroulement type :\n\n1. `GET /clusters/{cluster_id}/consoles` liste les consoles avec leur état et leurs contrôles ;\n2. `PUT .../consoles/{service}/access` publie l'adresse interne derrière la passerelle HTTPS et sa protection ;\n3. `POST .../consoles/{service}/open` renvoie l'adresse à ouvrir (audité) ;\n4. `POST .../consoles/{service}/credentials/rotate` change le mot de passe du compte de la console.\n\nPermissions : `services:operate` pour lister et ouvrir, `cluster:manage` pour publier une adresse, `configs:edit`\net `vault:manage` pour faire tourner les identifiants."
  },
  {
   "name": "hosts",
   "x-displayName": "Machines",
   "description": "Les machines d'un cluster, enregistrées par leur agent Navin. Une machine a un état `state` (`registering`,\n`healthy`, `unhealthy`, `lost`, `maintenance`, `decommissioned`), des caractéristiques remontées par l'agent\n(système d'exploitation, processeurs, mémoire, disques, réseau), des métriques, des contrôles préalables, des\nétiquettes et des paramètres noyau, ainsi que les instances de composants qui y sont placées.\n\nDéroulement type :\n\n1. enregistrez les machines : avec un jeton d'amorçage du cluster (voir **Clusters**) et la commande\n   d'installation lancée sur chaque machine, ou avec `POST /clusters/{cluster_id}/hosts/ssh-bootstrap`, où le\n   Manager se connecte en SSH et lance l'enregistrement pour vous ;\n2. `POST /hosts/{host_id}/preflight` puis `GET /hosts/{host_id}/preflight` pour vérifier qu'une machine est prête\n   à recevoir les services ;\n3. `PATCH /hosts/{host_id}` définit la baie, les étiquettes, l'environnement ou le mode maintenance ;\n4. pour retirer une machine, déplacez ses composants, puis `POST /hosts/{host_id}/decommission`.\n\nPermissions : `cluster:read` pour lire, `hosts:manage` pour modifier, globalement ou sur le cluster de la machine.\nLes actions sur la machine elle-même (contrôles préalables, caractéristiques, paramètres, mise hors service,\namorçage SSH) lancent une opération et répondent `202` avec son `operation_id`."
  },
  {
   "name": "hub",
   "x-displayName": "Navin Trusted Hub",
   "description": "Client du Navin Trusted Hub : le Manager récupère (en ligne) ou importe (hors ligne) le document de distribution\nsigné du client, le vérifie, conserve ses cibles (paquets et images approuvés avec leurs empreintes) et alimente\nle miroir avec les images approuvées.\n\nAvant tout enregistrement, le document est vérifié : signature Ed25519 par une clé épinglée, identité du client\net du Hub, expiration, version jamais inférieure à celle enregistrée (et version égale uniquement avec le même\ncontenu) et empreintes SHA-256. Avec `enforce`, les installations n'utilisent que des images dont l'empreinte est\nune cible approuvée et non révoquée.\n\nDéroulement type :\n\n1. `PUT /hub/settings` avec l'URL du Hub, le client, les clés publiques épinglées et le jeton d'accès ;\n2. `POST /hub/sync` (en ligne) ou `POST /hub/import` avec le fichier du document (hors ligne) ;\n3. `GET /hub` et `GET /hub/packages` montrent l'état et les paquets approuvés.\n\nPermissions : `system:read` pour lire, `settings:manage` pour modifier, synchroniser ou importer. Les changements\nsont audités (`hub.*`)."
  },
  {
   "name": "kubernetes",
   "x-displayName": "Clusters Kubernetes",
   "description": "Un cluster du Manager peut être un cluster Kubernetes plutôt qu'un ensemble de machines. Le Manager lui parle\nuniquement par son serveur d'API (jamais en SSH) avec un kubeconfig scellé, et y exécute les services du catalogue\nsous forme de ressources `ManagedComponent` du groupe `platform.navin.ai` : il écrit l'état souhaité (application\ncôté serveur, gestionnaire de champs `navin-manager`), le Navin Platform Operator applique les charts, et toute\nréponse sur un composant est lue dans le statut de sa ressource. Le Navin Cluster Agent, déployé dans le cluster,\nreçoit des commandes signées et rend compte.\n\nDéroulement type :\n\n1. `GET .../kubernetes/import-rbac` donne le RBAC à moindre privilège de l'identité d'import (lecture des nœuds,\n   espaces de noms, CRD et classes ; écriture uniquement des ressources `platform.navin.ai` de l'espace de noms\n   géré) ;\n2. `POST /clusters/{cluster_id}/kubernetes` enregistre le serveur d'API et le kubeconfig de cette identité ;\n3. `POST .../kubernetes/discovery` lit la version, la distribution, les nœuds, les classes de stockage et\n   d'ingress et les modules complémentaires ;\n4. `POST .../kubernetes/components` installe un service du catalogue ; `PATCH` et `/actions` le configurent, le\n   démarrent, l'arrêtent, le redémarrent, le mettent à l'échelle ou en maintenance ;\n5. facultativement, `GET .../kubernetes/agent/manifests` et `POST .../kubernetes/agent` déploient l'agent de\n   cluster et émettent son identité, puis `POST .../agent/commands` met en file des commandes signées.\n\nLe kubeconfig est refusé quand il pointe vers un autre serveur d'API, utilise http en clair, ignore la\nvérification TLS, exécute des greffons exec ou auth provider, référence des fichiers, ou utilise des proxys ou\nl'usurpation d'identité. Permissions : `cluster:read` pour lire, `cluster:manage` pour enregistrer et gérer\nl'agent, `hosts:manage` pour la découverte, `services:install` pour installer et retirer des composants,\n`configs:edit` pour les configurer, `services:operate` pour les actions et les commandes d'agent. Chaque\nchangement est audité (`k8s.*`)."
  },
  {
   "name": "lifecycle",
   "x-displayName": "Cycle de vie",
   "description": "Le cycle de vie d'un cluster et de ses services après l'installation : profil d'installation et approbations,\ncontrôles préalables, contrôles de vivacité, de disponibilité et de fonctionnement, mises à niveau avec retour\narrière et restauration, sauvegardes et restaurations coordonnées, détection des dérives, archives de support\nexpurgées, rapports de recette signés et fiche uniforme de composant.\n\nDéroulements types :\n\n1. **Profil** : `GET /profiles` liste les profils, `GET /clusters/{cluster_id}/profile` évalue les règles du profil\n   du cluster par rapport à l'état réel (une règle en échec bloque les installations et les démarrages), `PUT` le\n   change.\n2. **Approbations** (`banking-secure`) : `POST .../approvals` en demande une pour une action perturbatrice, un\n   autre utilisateur la tranche avec `POST .../approvals/{approval_id}/decision`, puis l'action l'utilise\n   (transmettez son `approval_id` ou laissez le Manager la retrouver).\n3. **Mise à niveau** : `GET .../services/{service}/upgrade` indique si la version du catalogue est atteignable,\n   puis `POST` la lance : contrôles préalables, sauvegarde d'un service à état, machine canari, les autres\n   machines une par une, contrôles. `POST .../upgrades/{upgrade_id}/rollback` revient à la version précédente,\n   `.../restore` restaure la sauvegarde prise avant.\n4. **Sauvegarde** : `POST .../backups` prend une sauvegarde coordonnée de plusieurs services ;\n   `.../backups/{backup_id}/restore` en restaure une.\n5. **Dérive** : `POST .../drift/scan` compare les fichiers et unités des machines avec ce que le Manager a écrit,\n   `GET .../drift` liste les constats, `POST .../drift/{finding_id}/reconcile` les réécrit.\n6. **Recette** : `POST .../acceptance-reports` exécute tous les contrôles et produit un rapport signé en Ed25519,\n   lisible en JSON ou en HTML et vérifiable hors ligne.\n\nPermissions : `cluster:read` pour lire, `cluster:manage` pour le profil, les décisions d'approbation et les\narchives de support, `services:install` pour les contrôles préalables, les mises à niveau, les restaurations et la\nrecette, `services:operate` pour demander des approbations, prendre des sauvegardes et rechercher les dérives,\n`configs:edit` pour corriger une dérive."
  },
  {
   "name": "mcp",
   "x-displayName": "Serveurs MCP",
   "description": "Les serveurs MCP (Model Context Protocol) d'un projet de locataire : créés à partir d'un modèle de la bibliothèque\nNavin ou saisis par un administrateur, testés, et éventuellement hébergés sur le service `mcp-host` d'un cluster.\n\nRègles :\n\n- chaque route prend `tenant` et `project` ; un serveur d'un autre locataire ou d'un autre projet répond `404\n  mcp_server_not_found`, et aucune route ne liste les serveurs de tous les locataires ;\n- les champs secrets d'un modèle, les en-têtes HTTP et l'environnement d'un serveur personnalisé sont scellés avec\n  la clé maîtresse ; les réponses et les événements d'audit ne nomment que les champs configurés\n  (`configured_fields`, `secret_fields`, `has_secrets`), jamais une valeur ;\n- les utilisateurs n'atteignent jamais un serveur MCP sur Internet : une URL doit désigner une adresse IP privée\n  (RFC 1918, ULA IPv6) ou un hôte du réglage `mcp_internal_hosts` ; les adresses de bouclage, de lien local et de\n  métadonnées cloud sont toujours refusées (`422 mcp_online_forbidden`).\n\nDéroulement type :\n\n1. `GET /mcp/presets` liste les modèles et les champs à remplir ;\n2. `POST /mcp/servers` crée un serveur à partir d'un modèle (`preset` et `values`) ou un serveur personnalisé\n   (`url` pour HTTP en flux ou SSE, `command` pour stdio) ;\n3. `POST /mcp/servers/{server_id}/test` lui parle en MCP et liste ses outils ;\n4. pour un serveur stdio, `POST /mcp/servers/{server_id}/hosting` l'héberge sur un cluster : il reçoit une URL\n   `/t/{tenant}/p/{project}/s/{name}/mcp` sur l'instance et un jeton porteur.\n\nPermissions (globales) : `mcp:read` pour lire, `mcp:manage` pour modifier, tester et héberger, `mcp:publish` (rôle\nadmin uniquement) pour lire les serveurs hébergés avec leurs jetons. Les changements sont audités (`mcp.*`)."
  },
  {
   "name": "mcp-runtime",
   "x-displayName": "Environnement d'exécution MCP",
   "description": "L'environnement d'exécution MCP d'un cluster Kubernetes : le ToolHive Kubernetes Operator, installé à partir de\ncharts épinglés, et les instances éphémères de serveurs MCP qu'y crée le contrôleur de cycle de vie du Control\nPlane.\n\nSeuls les objets `MCPServer` étiquetés `app.kubernetes.io/managed-by=eai-mcp-runtime` dans un espace de noms de\nprojet `mcp-<12 hex>` étiqueté `eai.io/mcp-runtime=true` sont listés, supprimés ou redémarrés ; tout autre objet\nrépond `404 mcp_runtime_instance_not_found`, exactement comme un objet absent, et le refus est audité.\n\nDéroulement type : `POST .../mcp-runtime/install` une fois, puis `GET .../mcp-runtime` pour suivre l'opérateur et\nles instances ; redémarrez (`restart`) ou supprimez une instance au besoin. Permissions : `cluster:read` pour lire,\n`services:install` pour installer, `services:operate` pour redémarrer ou supprimer une instance."
  },
  {
   "name": "me",
   "x-displayName": "Mon profil",
   "description": "Le profil de l'utilisateur connecté et ses jetons d'API personnels.\n\nUn jeton d'API permet à un script d'appeler l'API en votre nom : `Authorization: Bearer nmt_<12 hex>_<secret>`. Il\nn'est affiché qu'une fois à sa création et conservé sous forme d'empreinte. Il peut être limité à une liste de\npermissions, chacune devant être détenue par vous (globalement ou sur un cluster) ; un jeton n'accorde jamais plus\nque ce que vous détenez au moment de l'appel, et il cesse de fonctionner quand votre compte est désactivé.\n\nDéroulement type :\n\n1. connectez-vous à la console (les jetons se créent et se révoquent uniquement depuis une session de navigateur :\n   un jeton ne peut pas en créer un autre) ;\n2. `POST /me/tokens` avec un nom, une liste facultative de permissions et une durée de vie, puis conservez le\n   `token` de la réponse ;\n3. `GET /me/tokens` montre le préfixe, l'expiration et la dernière utilisation de chaque jeton ;\n4. `DELETE /me/tokens/{token_id}` révoque un jeton immédiatement.\n\nAucune permission n'est nécessaire : chaque utilisateur authentifié gère son propre profil et ses jetons. Les\nchangements sont audités (`user.profile_update`, `token.create`, `token.revoke`)."
  },
  {
   "name": "meta",
   "x-displayName": "Version",
   "description": "Métadonnées publiques du Manager. Aucune authentification n'est nécessaire."
  },
  {
   "name": "mirror",
   "x-displayName": "Miroir local",
   "description": "Le miroir local contient les paquets, les images de conteneurs et les paquets Python que les agents installent,\nafin qu'un site sans accès Internet (ou sous les profils `banking-secure` et `air-gapped`) ne télécharge jamais\nrien de l'extérieur. Il est alimenté par un lot hors ligne décrit par un manifeste.\n\nDéroulement type : copiez le lot dans le répertoire du miroir, `POST /mirror/reload` (avec `verify=true` pour\ncontrôler les empreintes), puis `GET /mirror` pour voir ce qui est chargé et ce qui manque encore au catalogue.\n\nPermissions : `system:read` pour lire, `settings:manage` pour recharger."
  },
  {
   "name": "navin-sm",
   "x-displayName": "Navin SM",
   "description": "Navin SM est la version de Navin (poste de travail et CLI) qu'une organisation remet à son personnel, avec un\nfichier qui dirige Navin vers le proxy LiteLLM de l'organisation et ses modèles (Azure, Bedrock, vLLM... derrière\nLiteLLM), éventuellement de façon exclusive (aucun autre fournisseur permis). Un profil est ce fichier :\nl'organisation, la passerelle (adresse, authentification, en-têtes, autorité de certification, modèles, modèle\npar défaut, routes, mode exclusif) et la politique gérée que Navin applique (permissions, liste des serveurs MCP\npermis, plugins, hooks, contournement des demandes de permission, environnement imposé).\n\n`GET /navin-sm/profiles/{profile_id}/export` renvoie le `managed-settings.json` du contrat (passerelle\nd'organisation, version 1), vérifié par `catalog/schema/navin-sm-managed-settings.schema.json`. Le service\ninformatique le déploie dans `/etc/navin/managed-settings.json` (Linux),\n`/Library/Application Support/Navin/managed-settings.json` (macOS) ou `%ProgramData%\\Navin\\managed-settings.json`\n(Windows) ; un utilisateur peut aussi l'importer dans Navin, Réglages > Organisation > Importer (seuls\n`organization` et `gateway` s'appliquent alors).\n\nLa clé partagée d'une passerelle `shared_key` est une référence à une entrée du coffre (`shared_key_ref`), jamais\nconservée avec le profil : elle n'est descellée que pour appeler le proxy ou écrire le fichier exporté. L'utiliser\ndemande aussi `vault:manage`. Permission `settings:manage` (globale). Chaque changement, export et découverte est\naudité (`navin_sm.*`), refus compris, jamais avec une clé.\n\nUn profil lié à un service LiteLLM installé par le Manager (`litellm_cluster_id`, `litellm_service`) est\ndécouvert avec la clé maître du service, gardée dans le serveur, et émet les clés virtuelles LiteLLM du\npersonnel (`/navin-sm/profiles/{profile_id}/keys`, permission `navin_sm:keys`, audit `navin_sm.key.*`) : une clé\nest montrée une seule fois, le Manager ne garde que son alias et son sha256. Le suivi (`/usage`) lit `/key/info`\net `/spend/logs` du proxy par le serveur et le dit quand le proxy est injoignable. La distribution\n(`/navin-sm/distribution`, service du catalogue `navin-sm`, livraison `distribute`) conserve les paquets de la\nversion, chacun vérifié contre son SHA256 publié, et le bundle (`/bundle?os=`) réunit le fichier, l'autorité du\nManager et les scripts de déploiement d'un système."
  },
  {
   "name": "network",
   "x-displayName": "Flux réseau",
   "description": "Les flux réseau d'un cluster, calculés à partir de ce que connaît le Manager, pour l'équipe réseau qui les ouvre\ndans ses groupes de sécurité ou ses pare-feu de machines. Chaque flux appartient à une portée :\n\n- `platform` : les utilisateurs vers la console du Manager, chaque machine vers l'API des agents et l'écoute\n  d'enregistrement, le Manager vers les machines en TCP 22 pour l'amorçage SSH, les machines vers un miroir ou un\n  registre externe ;\n- `service` : les consommateurs d'une liaison vers leur fournisseur, Prometheus vers les points de métriques, les\n  clients vers les points d'accès publics ;\n- `intra_service` : entre les machines d'un même service (ports de transport, de pairs, de gossip, de\n  réplication) ;\n- `egress` : les consommateurs d'un service connecté ou d'une connexion externe vers son point d'accès, et le\n  Manager vers l'URL de santé d'un service connecté.\n\nDéroulement type : `GET .../network/flows` (JSON ou CSV) pour examiner la matrice, puis `GET .../network/rules`\npour les commandes qui l'ouvrent chez un fournisseur cloud ou sur un pare-feu de machine. Lecture seule ;\npermission `cluster:read`."
  },
  {
   "name": "node-security",
   "x-displayName": "Sécurité des nœuds",
   "description": "La sécurité des machines gérées et de leurs agents de nœud : inventaire, politique SSH, contrôles préalables et\namorçage SSH de l'agent, jetons d'enrôlement liés à la clé de l'agent, état de confiance (quarantaine, révocation,\nrétablissement), mode de gestion, commandes d'agent signées et leurs reçus, mise à jour de l'agent et accès\nd'urgence SSH (break glass).\n\nDéroulement d'un amorçage :\n\n1. `PUT /clusters/{cluster_id}/ssh-policy` fixe les clés acceptées (Ed25519, ou RSA d'au moins `rsa_min_bits`),\n   les autorités d'utilisateurs approuvées, le compte d'amorçage et la rotation des certificats ;\n2. `POST /hosts/preflight` lit la machine en SSH (vérification stricte de la clé d'hôte) et depuis le Manager\n   (DNS, TCP) et renvoie des contrôles classés `pass`, `warning` ou `blocking` ;\n3. `POST /hosts/bootstrap/plan` montre le plan (liste sudo autorisée, unité systemd), puis `POST /hosts/bootstrap`\n   l'exécute : le binaire signé de l'agent est vérifié, une clé temporaire est utilisée puis retirée à la fin, une\n   étape en échec est annulée ;\n4. ou `POST /hosts/enroll` émet un jeton à usage unique lié à l'empreinte de la clé de l'agent, pour un agent\n   installé par d'autres moyens.\n\nAu quotidien : `POST /hosts/{host_id}/commands` envoie une commande signée en Ed25519 (l'agent vérifie la\nsignature, la cible, l'expiration, le nonce et le périmètre) et `GET .../receipts` lit les résultats ;\n`quarantine`, `revoke` et `recover` changent l'état de confiance ; l'accès d'urgence donne un certificat OpenSSH\naprès deux approbations.\n\nPermissions : `cluster:read` sur le cluster de la machine pour lire, `hosts:manage` pour modifier. Les clés privées\nne sont acceptées que dans le corps de la requête et gardées en mémoire le temps de l'exécution, jamais conservées\nni journalisées ; un corps qui contient un champ de mot de passe est refusé. Chaque changement et chaque refus est\naudité."
  },
  {
   "name": "operations",
   "x-displayName": "Opérations",
   "description": "Une opération est l'unité de tout changement fait par le Manager : une installation, un redémarrage, une mise à\nniveau, une sauvegarde, l'amorçage d'une machine... Elle se compose d'étapes ordonnées ; chaque étape contient des\ntâches, en général une par machine, exécutées par les agents Navin. Statut d'une opération : `pending`, `running`,\n`succeeded`, `failed` ou `aborted` ; `progress` va de 0 à 100.\n\nDéroulement type :\n\n1. un point d'accès qui modifie quelque chose répond `{\"operation_id\": ...}` ;\n2. `GET /operations/{operation_id}` renvoie l'opération avec ses étapes et ses tâches ; interrogez-le jusqu'à un\n   statut final ;\n3. `GET /operations/{operation_id}/tasks/{task_id}/logs` lit la sortie d'une tâche, page après page avec\n   `after_seq` ;\n4. `POST /operations/{operation_id}/abort` arrête une opération en cours ; `POST /operations/{operation_id}/retry`\n   crée une nouvelle opération à partir d'une opération échouée ou interrompue.\n\nPermissions : `operations:read` pour lire, `operations:abort` pour interrompre ou relancer, globalement ou sur le\ncluster de l'opération."
  },
  {
   "name": "pki",
   "x-displayName": "PKI et certificats",
   "description": "L'autorité de certification du Manager et les certificats qu'elle émet : certificats clients des agents et\ncertificats TLS des composants des services.\n\nDéroulement type : `GET /pki/ca` pour obtenir l'autorité et épingler son empreinte SHA-256,\n`GET /clusters/{cluster_id}/certificates` pour voir ce qui expire, `POST /clusters/{cluster_id}/certificates/rotate`\npour émettre de nouveaux certificats de services et redémarrer les composants avec. Permissions : tout utilisateur\nconnecté lit l'autorité, `cluster:read` liste les certificats, `pki:manage` (globale) les renouvelle."
  },
  {
   "name": "services",
   "x-displayName": "Services",
   "description": "Les services d'un cluster : services du catalogue (PostgreSQL, Keycloak, Kafka...) installés et exploités par le\nManager sur les machines, ou services existants à vous, enregistrés avec **Configurer et connecter** (jamais\ninstallés, démarrés ni arrêtés par le Manager). Un service géré se compose de composants (par exemple `server`,\n`replica`) placés sur des machines ; chaque placement est une instance de composant avec son état et sa santé.\n\nDéroulement d'une installation :\n\n1. `POST /clusters/{cluster_id}/services/validate` avec les services, leurs placements et leur configuration :\n   renvoie les erreurs, les contrôles de haute disponibilité, les dépendances qui seraient ajoutées et le plan\n   (ordre, étapes, pas) ; rien n'est modifié ;\n2. `POST /clusters/{cluster_id}/services` avec le même corps : crée l'opération d'installation et renvoie son\n   `operation_id` ;\n3. suivez-la avec `GET /operations/{operation_id}` (voir **Opérations**) ;\n4. exploitez le service au quotidien avec `/actions` (démarrage, arrêt, redémarrage, redémarrage progressif,\n   contrôles), `/commands/{command}`, `/components` et `/mode`.\n\nContrôles appliqués à la création d'une opération : le profil du cluster (`409 profile_blocked`), l'état\nadministratif du service (`409 service_admin_state`), le verrou de sécurité du cluster\n(`423 cluster_security_locked`), la haute disponibilité (`409 ha_requirements_not_met` ou\n`ha_acknowledgement_required`, sauf acquittement) et, sous `banking-secure`, l'approbation d'une seconde personne\npour une action perturbatrice (`409 approval_required`).\n\nPermissions : `cluster:read` pour lire, `services:install` pour installer, connecter, désinstaller, modifier les\ncomposants ou réinstaller, `services:operate` pour les actions, commandes, modes et journaux, `configs:read` pour\nla configuration cliente, `pki:manage` pour la suspension de sécurité et le verrou de sécurité. Chaque changement\nest audité."
  },
  {
   "name": "settings",
   "x-displayName": "Réglages",
   "description": "Réglages généraux du Manager : nom du site, langue par défaut, valeurs par défaut d'installation (méthode\npréférée, registre, miroir), SMTP et syslog distant pour les notifications d'alertes (syslog sert aussi à la règle d'audit distant de\n`banking-secure`), conservation des données et\nproxy d'entreprise des machines gérées. Tout utilisateur connecté lit les valeurs non secrètes ;\n`settings:manage` (globale) les modifie. Le mot de passe SMTP est relu sous la forme\n`{\"secret\": true, \"set\": true|false}`. Les changements sont audités (`settings.update`)."
  },
  {
   "name": "stack",
   "x-displayName": "Catalogue de la pile",
   "description": "Le catalogue des services que le Manager peut installer (la pile) : chaque service avec ses versions, ses composants, ses méthodes d'installation, ses paramètres, ses dépendances et ses règles de haute disponibilité. Tout utilisateur connecté peut le lire ; aucune permission n'est nécessaire."
  },
  {
   "name": "system",
   "x-displayName": "Système",
   "description": "Supervision du Manager par lui-même : santé de la base de données, du moteur, des agents et des tâches planifiées, liste des systèmes d'exploitation pris en charge et découverte de services Prometheus d'un cluster. Permissions : `system:read` pour la santé, `cluster:read` pour la découverte de services ; les systèmes pris en charge sont ouverts à tout utilisateur connecté."
  },
  {
   "name": "telemetry",
   "x-displayName": "Télémétrie",
   "description": "État de l'export en temps réel des métriques et événements du Manager vers ClickHouse. La plateforme ne livre pas de serveur de tableaux de bord (ADR-0169) : les machines et les services se suivent dans le Manager lui-même et les tables exportées se lisent en SQL. L'export est désactivé tant qu'aucune URL ClickHouse n'est configurée. Tout principal qui lit au moins un cluster (`cluster:read`, globale ou sur un cluster)."
  },
  {
   "name": "users",
   "x-displayName": "Utilisateurs et rôles",
   "description": "Administration des comptes de la console et de leurs attributions de rôle.\n\nUn utilisateur est `local` (mot de passe conservé par le Manager), `ldap` ou `oidc` (créé à sa première\nconnexion). Une attribution de rôle est un rôle (`admin`, `security_admin`, `cluster_admin`, `operator`, `viewer`,\n`auditor`) donné globalement ou sur un cluster ; `admin`, `security_admin` et `auditor` sont uniquement globaux. Les\nattributions faites ici sont `manual` ; celles déduites des groupes LDAP ou OIDC sont renouvelées à chaque\nconnexion et conservées quand les attributions manuelles sont remplacées.\n\nDéroulement type :\n\n1. `GET /roles` liste les rôles avec leurs permissions et leurs portées ;\n2. `POST /users` crée un compte local avec ses premières attributions (par défaut, le mot de passe doit être\n   changé à la première connexion) ;\n3. `PUT /users/{user_id}/roles` remplace ses attributions manuelles ; `PATCH /users/{user_id}` le désactive ;\n4. `POST /users/{user_id}/unlock`, `/password` et `/mfa/reset` dépannent un utilisateur bloqué.\n\nGarde-fous : le dernier administrateur actif (utilisateur activé avec une attribution globale `admin`) ne peut\njamais être supprimé, désactivé ni rétrogradé (`409 last_admin`) ; seul un administrateur complet peut accorder\n`admin` ou agir sur un compte administrateur (`403 user_manage_denied`) ; vous ne pouvez ni vous désactiver ni vous\nsupprimer. Permissions : `users:read` pour lire, `users:manage` pour modifier. Chaque changement et chaque refus est\naudité (`user.*`)."
  },
  {
   "name": "vault",
   "x-displayName": "Coffre-fort",
   "description": "Le coffre-fort des administrateurs de la plateforme : les identifiants des services de la plateforme. Une entrée\nest un nom de service, son URL, un identifiant de connexion, un mot de passe et une clé d'API ou clé secrète. Seul\nle nom est conservé en clair ; URL, identifiant, mot de passe et clé forment un document scellé en AES-256-GCM avec\nla clé maîtresse. Les listes et les fiches ne portent jamais le mot de passe ni la clé (`has_password`,\n`has_api_key`) ; `POST .../reveal` les renvoie et est audité, l'audit ne gardant que le nom du service, jamais une\nvaleur.\n\nLes entrées des services installés (`managed`) sont créées et rafraîchies par le Manager après chaque installation\nou application de configuration réussie, avec les identifiants générés pour cette installation ;\n`POST /vault/import` rafraîchit les services installés auparavant. Un champ modifié à la main est conservé par le\nrafraîchissement. L'identifiant, le mot de passe et la clé d'une console dont le Manager fait tourner les\nidentifiants ne changent que par `POST /clusters/{cluster_id}/consoles/{service}/credentials/rotate` (voir\n**Consoles des services**).\n\nPermission `vault:manage` (globale). Chaque changement et chaque révélation est audité (`vault.*`)."
  }
 ],
 "x-tagGroups": [
  {
   "name": "Accès",
   "tags": [
    "auth",
    "me",
    "users",
    "auth-config"
   ]
  },
  {
   "name": "Clusters et machines",
   "tags": [
    "clusters",
    "hosts",
    "agent",
    "network",
    "settings"
   ]
  },
  {
   "name": "Services",
   "tags": [
    "services",
    "configs",
    "connections",
    "consoles",
    "blueprint",
    "hub",
    "stack"
   ]
  },
  {
   "name": "Opérations",
   "tags": [
    "operations",
    "alerts",
    "availability",
    "telemetry",
    "audit"
   ]
  },
  {
   "name": "Sécurité",
   "tags": [
    "pki",
    "vault",
    "node-security"
   ]
  },
  {
   "name": "Sauvegarde et cycle de vie",
   "tags": [
    "backup",
    "lifecycle",
    "mirror"
   ]
  },
  {
   "name": "Kubernetes et MCP",
   "tags": [
    "kubernetes",
    "mcp",
    "mcp-runtime"
   ]
  },
  {
   "name": "Système",
   "tags": [
    "system",
    "meta"
   ]
  },
  {
   "name": "Autres",
   "tags": [
    "agent-templates",
    "navin-sm"
   ]
  }
 ]
}
