SDK et CLI du Navin Manager
Ce guide montre comment appeler l'API du Navin Manager (navin-manager), qui installe et exploite l'infrastructure de
la plateforme : hôtes, clusters, services, haute disponibilité, sauvegardes, restaurations et reprise après
sinistre. Il couvre le client du manager de chaque SDK et la ligne de commande enterprise-ai manager. L'API est
décrite par navin-manager/docs/api/manager-openapi.json, écrit
depuis le serveur par tools/manager/openapi.py.
English version: manager.md
Vue d'ensemble
Les clients du manager sont générés depuis le document OpenAPI par tools/manager/gen_sdks.py : une méthode par
opération sous /api/v1, regroupée par tag (269 opérations dans 31 groupes), plus des alias du contrat comme
hosts.preflight, services.rollback, ha.test_failover, backups.create_plan, restores.execute et
dr.request_failover. Un test de couverture dans chaque SDK échoue dès qu'une opération du document n'a pas de
méthode.
| SDK | Point d'entrée | Table générée | Test de couverture |
|---|---|---|---|
| Python | enterprise_ai.manager.ManagerClient, AsyncManagerClient | sdks/python/src/enterprise_ai/manager/_generated.py | sdks/python/tests/test_manager.py |
| TypeScript | ManagerClient de @enterprise-ai/sdk | sdks/typescript/packages/sdk/src/manager/generated/operations.ts | sdks/typescript/packages/sdk/test/manager.test.ts |
| Go | paquet manager du module du SDK Go | sdks/go/manager/operations_gen.go | sdks/go/manager/manager_test.go |
| Java | io.enterpriseai.manager.ManagerClient | sdks/java/enterprise-ai-java/src/main/java/io/enterpriseai/manager/ManagerGroups.java | la suite de tests Java |
Le client du manager n'est pas le sous-client platform_manager du SDK du Control Plane : celui-ci atteint l'espace
Platform Manager de la console par le Control Plane, pour un platform_admin.
Prérequis et compatibilité
Le client du manager est livré dans chaque SDK et a les mêmes prérequis : Python 3.10+, Node.js 18+, Go 1.26+,
Java 17+ (vue d'ensemble des SDK). Il cible l'API v1 du manager (/api/v1). Installez le SDK comme
décrit dans son guide ; aucun paquet supplémentaire n'est nécessaire.
Authentification et délégation
- Créez un jeton d'API du manager. Dans la console du manager, Profil > Jetons d'API, créez un jeton
avec la plus petite liste de permissions. Le jeton a la forme
nmt_<12 hex>_<secret>; chaque SDK refuse un autre format avant tout appel. - Rangez le jeton dans un fichier lisible par son seul propriétaire (mode 0600) et donnez son chemin
(
NM_MANAGER_TOKEN_FILE,token_file,tokenFile), ou passez-le depuis un coffre de secrets. Aucun SDK et aucune commande de la CLI ne l'affiche. - Agissez pour un utilisateur quand un service appelle au nom d'une personne. L'option
on_behalf_of(onBehalfOfen TypeScript et en Java,WithOnBehalfOfen Go) envoieX-NM-On-Behalf-Of: <utilisateur>. Le manager ne l'accepte que d'un jeton dont la liste explicite de permissions contientauth:delegate: la requête porte alors l'intersection des permissions du jeton et de l'utilisateur, et chaque événement d'audit enregistre l'acteur (le jeton de service) ainsi quedetails.on_behalf_ofetdetails.delegation. Tout autre appelant reçoit HTTP 403on_behalf_of_forbidden; un utilisateur inconnu ou désactivé reçoit 403on_behalf_of_unknown.
Les identifiants du Control Plane (EAI_*) ne sont jamais envoyés au manager : les clients du manager ne lisent que
leurs propres réglages.
Configuration du client
| Réglage | Python (ManagerConfig) | TypeScript (ManagerClientOptions) | Go (manager.With...) | Java (ManagerClient.Builder) | Variable d'environnement |
|---|---|---|---|---|---|
| URL du manager (https, ou http sur localhost) | base_url: str | baseUrl: string | WithBaseURL(string) | baseUrl(String) | NM_MANAGER_URL |
| Jeton | token: str | None | token: string | () => string | Promise<string> | WithToken(string) | token(String) | NM_MANAGER_TOKEN |
| Fichier du jeton | token_file: str | None | aucun | aucun | tokenFile(Path) | NM_MANAGER_TOKEN_FILE |
| Autorités de certification (PEM) | ca_bundle: str | None | confiance de l'environnement (NODE_EXTRA_CA_CERTS) | WithCABundle([]byte) | caBundle(Path) | NM_MANAGER_CA_BUNDLE |
| Délégation | on_behalf_of: str | None | onBehalfOf: string | WithOnBehalfOf(string) | onBehalfOf(String) | NM_MANAGER_ON_BEHALF_OF |
Langue (en, fr) | locale: str | None | locale | WithLocale(string) | locale(Locale) | NM_MANAGER_LOCALE |
| Délai | timeout: float = 30.0 | timeoutMs (60000 par défaut) | WithHTTPClient(...) | timeout(Duration) (30 s), connectTimeout(Duration) (10 s) | aucune |
| Reprises | retries: int = 2 | retries (2 par défaut) | WithRetries(int) (2), WithRetryBaseDelay (250 ms) | retries(int) (2), retryBaseDelay(Duration) (250 ms) | aucune |
| En-têtes supplémentaires | headers: dict[str, str] | headers | WithHeader(name, value) | header(name, value) | aucune |
| Client HTTP | http_client= | fetch | WithHTTPClient(*http.Client) | httpClient(HttpClient) | aucune |
Les variables d'environnement sont lues par ManagerConfig.from_env() (Python, utilisé aussi quand aucun config
n'est donné) et par ManagerClient.Builder.fromEnv(System.getenv()) ou ManagerClient.fromEnv() (Java).
TypeScript et Go prennent les valeurs depuis votre code.
Démarrage rapide et groupes de ressources
Python
from enterprise_ai.manager import ManagerClient
with ManagerClient("<manager-url>", token=TOKEN, ca_bundle=None, http_client=HTTP) as nm:
check = nm.hosts.preflight({"cluster_id": "c1", "target": {"host": "10.0.0.5"}, "private_key": "ref",
"fqdn": "<host-fqdn>", "expected_ip": "10.0.0.5"})
op = nm.services.action("c1", "kafka", {"action": "restart"})
status = nm.operations.get_operation(op["operation_id"])
for item in nm.operations.list_operations("c1", limit=50):
print(item["id"])
as_alice = nm.on_behalf_of("alice")
as_alice.restores.plan({"artifact_id": "a1", "environment": "staging"})
Ce qui se passe : le client vérifie le format du jeton, ouvre un pool de connexions, lance le contrôle préalable
d'un nouvel hôte, redémarre un service, lit l'opération qui en résulte, parcourt chaque page des opérations du
cluster, puis planifie une restauration au nom d'alice. AsyncManagerClient a les mêmes méthodes, chacune
renvoyant un awaitable. nm.operation("<operationId>") renvoie la méthode liée de n'importe quel identifiant
d'opération du document.
TypeScript
import { ManagerClient, collect, paginate } from "@enterprise-ai/sdk";
const nm = new ManagerClient({ baseUrl: "<manager-url>", token, onBehalfOf: "alice" });
const op = await nm.services.action("c1", "kafka", { action: "restart" });
const ops = await collect(paginate((q) => nm.operations.listOperations("c1", q), { limit: 50 }));
Go
nm, err := manager.New(manager.WithBaseURL("<manager-url>"), manager.WithToken(token),
manager.WithCABundle(pem), manager.WithOnBehalfOf("alice"))
raw, err := nm.Services.Action(ctx, "c1", "kafka", map[string]any{"action": "restart"})
Java
ManagerClient nm = ManagerClient.builder()
.baseUrl("<manager-url>")
.tokenFile(Path.of("/run/secrets/manager.token"))
.caBundle(Path.of("/etc/pki/manager-ca.pem"))
.build();
JsonNode op = nm.services().action("c1", "kafka", Map.of("action", "restart"));
for (JsonNode item : nm.operations().listOperations("c1").autoPaging()) {
System.out.println(item.path("id").asText());
}
ManagerClient asAlice = nm.onBehalfOf("alice");
Groupes de ressources
Chaque groupe existe dans les quatre SDK (Python nm.hosts, TypeScript nm.hosts, Go nm.Hosts, Java
nm.hosts()). Le nombre est celui des opérations du document dans le groupe.
| Groupe | Opérations | Groupe | Opérations | Groupe | Opérations |
|---|---|---|---|---|---|
alerts | 10 | configs | 12 | node_security | 22 |
audit | 3 | connections | 7 | operations | 7 |
auth | 2 | consoles | 4 | pki | 3 |
auth_config | 11 | hosts | 13 | services | 22 |
availability | 6 | hub | 5 | settings | 2 |
backup | 31 | kubernetes | 19 | stack | 3 |
blueprint | 3 | lifecycle | 29 | system | 3 |
clusters | 10 | mcp | 12 | telemetry | 1 |
me | 3 | mcp_runtime | 4 | users | 10 |
meta | 1 | mirror | 2 | vault | 7 |
network | 2 |
Les groupes du contrat backups, restores, ha et dr ne contiennent que des alias des opérations ci-dessus
(par exemple restores.plan, ha.test_failover). Chaque méthode, avec ses paramètres et sa route, est dans la
référence générée : Python,
TypeScript, Go,
Java.
Erreurs
Les erreurs gardent l'enveloppe du manager {"error": {"code", "message", "message_i18n", "details"}} et sont les
erreurs typées de chaque SDK : sous-classes de EaiError en Python (messages contient message_i18n), sous-classes
de EaiError en TypeScript (messageI18n), *eai.APIError en Go (MessageI18n), sous-classes de EaiException en
Java. Les codes propres au manager comprennent on_behalf_of_forbidden, on_behalf_of_unknown (403) et les codes
d'approbation, de verrou et de conflit des opérations (409, 423, 428).
Reprises et délais
Le manager ne dédoublonne pas les appels POST par Idempotency-Key, donc les clients ne reprennent jamais une
opération qui a pu s'exécuter :
| SDK | Appels repris | Échecs repris |
|---|---|---|
| Python | GET, PUT, DELETE ; toute méthode sur HTTP 429 | erreurs réseau, 429, 500, 502, 503, 504 |
| TypeScript | GET, PUT, DELETE (les autres méthodes utilisent noRetry) | erreurs réseau, délais dépassés, 429, 5xx |
| Go | GET, PUT, DELETE | erreurs réseau, 408, 429, 502, 503, 504 |
| Java | GET, PUT, DELETE | erreurs d'entrée-sortie, 408, 429, 502, 503, 504 |
Pagination et opérations longues
Les opérations de liste prennent limit et cursor et renvoient la page {items, next_cursor} de chaque SDK :
Page en Python, Page<Doc> en TypeScript (parcourue avec paginate et collect), json.RawMessage en Go (avec
eai.WithLimit et eai.WithCursor), Page<JsonNode> en Java (autoPaging()). Les actions sur les services, les
hôtes et les sauvegardes répondent par une opération : lisez-la avec operations.get_operation(id) (getOperation,
GetOperation), et ses tâches et journaux avec les autres méthodes du groupe operations.
Opérations exemptées
Les parcours navigateur et MFA ne sont pas enveloppés : ils exigent le cookie de session et le jeton CSRF d'une
personne, jamais un jeton de service. La liste avec ses raisons est EXEMPT (Python), MANAGER_EXEMPT
(TypeScript), manager.Exempt (Go) et ManagerOperations.EXEMPT (Java) : auth/login, auth/logout,
auth/setup, auth/password/change, auth/oidc/{provider_id}/start et callback, auth/mfa/verify,
auth/mfa/enroll/start et confirm, auth/mfa/setup, auth/mfa/confirm, DELETE auth/mfa, POST me/tokens (un
jeton ne peut pas créer de jetons) et PATCH me (profil modifié depuis la session).
Tests
Chaque client du manager accepte une couche HTTP injectée, exactement comme le client du Control Plane : Python
http_client=httpx.Client(transport=httpx.MockTransport(handler)), TypeScript fetch, Go WithHTTPClient avec un
serveur httptest, Java httpClient ou un serveur local. Les exemples de cette page sont exécutés par
sdks/python/tests/test_manager.py contre un manager simulé.
Référence de la CLI
Configuration, de la plus prioritaire à la moins prioritaire : options (--manager-url, --ca-bundle,
--token-file, --on-behalf-of, --manager-config), variables d'environnement (NM_MANAGER_URL,
NM_MANAGER_CA_BUNDLE, NM_MANAGER_TOKEN_FILE ou NM_MANAGER_TOKEN, NM_MANAGER_ON_BEHALF_OF,
NM_MANAGER_CONFIG), puis le fichier JSON ~/.config/enterprise-ai/manager.json avec les clés url, ca_bundle,
token_file, on_behalf_of, locale. Une clé token dans ce fichier est refusée (code 7). --json affiche la
réponse JSON brute ; --body FICHIER (JSON ou YAML, - pour l'entrée standard) donne le corps de la requête, les
options s'y ajoutent. L'aide fonctionne hors ligne : enterprise-ai manager --help --lang fr.
| Commande | Route du manager |
|---|---|
host add --body F [--cluster C] | POST /api/v1/hosts/bootstrap/plan (plan de l'ajout, rien n'est exécuté) |
host preflight --body F | POST /api/v1/hosts/preflight |
host bootstrap --body F | POST /api/v1/hosts/bootstrap (clé d'hôte stricte, SSH temporaire) |
host enroll --cluster C --fqdn N --fingerprint SHA256 [--ttl S] | POST /api/v1/hosts/enroll |
host list [--cluster C] | GET /api/v1/hosts |
host show H | GET /api/v1/hosts/{host_id} |
host revoke H [--reason R] | POST /api/v1/hosts/{host_id}/revoke |
host diagnose H | GET /api/v1/agents/nodes/{host_id}, GET /api/v1/hosts/{host_id}/inventory, GET /api/v1/hosts/{host_id}/preflight |
cluster import C --api-server URL --kubeconfig F [--namespace N] | POST /api/v1/clusters/{cluster_id}/kubernetes |
cluster list | GET /api/v1/clusters |
cluster nodes C | GET /api/v1/clusters/{cluster_id}/hosts |
cluster pools C | GET /api/v1/clusters/{cluster_id}/topology, hôtes regroupés par baie (domaine de panne) ; le manager n'a pas de route de pool de nœuds |
service install C S [--body F] [--no-start] | POST /api/v1/clusters/{cluster_id}/services |
service configure C S --body F [--note N] | PUT /api/v1/clusters/{cluster_id}/services/{service}/config |
service start|stop|restart C S [--approval A] | POST /api/v1/clusters/{cluster_id}/services/{service}/actions |
service upgrade C S [--approval A] [--canary-host H] | POST /api/v1/clusters/{cluster_id}/services/{service}/upgrade |
service rollback C UPGRADE [--approval A] | POST /api/v1/clusters/{cluster_id}/upgrades/{upgrade_id}/rollback |
service status C S | GET /api/v1/clusters/{cluster_id}/services/{service} |
ha create --body F | POST /api/v1/ha-groups |
ha status G | GET /api/v1/ha-groups/{group_id} |
ha test-failover G | POST /api/v1/ha-groups/{group_id}/test-failover |
backup plan --body F | POST /api/v1/backup-plans |
backup run P --location L [--kind K] | POST /api/v1/backup-plans/{plan_id}/backups |
backup verify B | POST /api/v1/backups/{backup_id}/verify |
backup list | GET /api/v1/backups |
restore plan --artifact A --environment E | POST /api/v1/restores/plan |
restore approve P | POST /api/v1/restores/{plan_id}/approve (séparation des tâches imposée par le manager) |
restore execute P | POST /api/v1/restores/{plan_id}/execute |
restore verify P | GET /api/v1/restores/{plan_id} (état, approbateurs, vérification) |
dr plan --body F | POST /api/v1/dr-plans |
dr exercise --plan P | POST /api/v1/dr-exercises |
dr failover-request --plan P | POST /api/v1/dr-failovers/request (demande seulement : approbation requise) |
Codes de sortie : 0 succès ; 1 entrée invalide (contrôle local, HTTP 400 ou 422) ; 2 erreur d'utilisation ; 3 authentification ou permission refusée (HTTP 401 ou 403) ; 4 introuvable (HTTP 404) ; 5 conflit, verrou ou approbation requise (HTTP 409, 423 ou 428) ; 6 erreur du serveur ou du réseau ; 7 erreur de configuration.
Exemples
Ces lignes sont exécutées par sdks/python/tests/test_manager.py contre un manager simulé.
export NM_MANAGER_URL=<manager-url> NM_MANAGER_TOKEN_FILE=~/.config/enterprise-ai/manager.token
enterprise-ai manager host preflight --cluster c1 --body host.json --json
enterprise-ai manager host bootstrap --cluster c1 --body host.json
enterprise-ai manager host enroll --cluster c1 --fqdn <host-fqdn> --fingerprint SHA256:abc
enterprise-ai manager host list --cluster c1
enterprise-ai manager host diagnose h1 --json
enterprise-ai manager host revoke h1 --reason "key compromised"
enterprise-ai manager cluster import c1 --api-server <api-server-url> --kubeconfig kubeconfig
enterprise-ai manager cluster pools c1 --json
enterprise-ai manager service install c1 kafka
enterprise-ai manager service configure c1 kafka --body kafka.json --note "retention 7 days"
enterprise-ai manager service restart c1 kafka
enterprise-ai manager service upgrade c1 kafka --approval ap1
enterprise-ai manager service rollback c1 up1 --approval ap2
enterprise-ai manager service status c1 kafka --json
enterprise-ai manager ha create --body ha.json
enterprise-ai manager ha test-failover g1
enterprise-ai manager backup plan --body backup-plan.json
enterprise-ai manager backup run p1 --location s3://backups/pg/2026-10-30
enterprise-ai manager backup verify b1
enterprise-ai manager backup list --json
enterprise-ai manager restore plan --artifact a1 --environment staging
enterprise-ai manager restore approve r1 --on-behalf-of alice
enterprise-ai manager restore execute r1
enterprise-ai manager restore verify r1 --json
enterprise-ai manager dr plan --body dr-plan.json
enterprise-ai manager dr exercise --plan d1
enterprise-ai manager dr failover-request --plan d1
Référence
- Référence générée des clients du manager : Python, TypeScript, Go, Java.
- Guide de la CLI et référence de la CLI.
- Vue d'ensemble des SDK.