Skip to main content

REST API and agent protocol

The binding reference of every route, body and error code is section 7 (REST) and section 8 (agent) of navin-manager/CONTRACT.md. The running server also publishes its OpenAPI description at /api/v1/openapi.json.

Authentication​

ClientHow
BrowserPOST /api/v1/auth/login sets the cookie nm_session and returns csrf_token. Send it in the header X-CSRF-Token with every POST, PUT, PATCH and DELETE.
AutomationAuthorization: Bearer nmt_<id>_<secret>: an API token created in Profile, API tokens (POST /api/v1/me/tokens). No CSRF header.
TOKEN=nmt_0123456789ab_...
curl -s -H "Authorization: Bearer $TOKEN" https://<manager-host>:8460/api/v1/clusters

The language of the messages follows the cookie nm_lang (en or fr), then the header Accept-Language.

Errors and lists​

Every error has the same shape, with the message in both languages:

{"error": {"code": "cluster_not_empty", "message": "...", "message_i18n": {"en": "...", "fr": "..."},
"details": {}}}
StatusTypical codes
401unauthenticated, session_expired, token_invalid, invalid_credentials, agent_signature_invalid
403permission_denied (details give the permission), csrf_invalid, account_disabled, password_expired
404not_found, service_unknown, operation_not_found
409cluster_not_empty, host_has_components, service_in_use, connection_in_use, artifact_missing, sandbox_only_service, last_admin
422validation_error, config_invalid (details give the fields), password_policy
423account_locked
429too_many_attempts (header Retry-After)

Lists take ?limit= (50 by default, 500 at most) and ?cursor= and return {"items": [...], "next_cursor": ...}. A secret is never returned: it reads {"secret": true, "set": true}; sending that value back keeps the stored secret.

Main routes​

AreaRoutes
Session/auth/providers, /auth/setup, /auth/login, /auth/mfa/*, /auth/password/change, /auth/session, /auth/logout, /auth/oidc/{id}/start and /callback, /me, /me/tokens
Administration/users, /roles, /auth-config/ldap, /auth-config/oidc, /auth-config/policy, /audit, /audit/verify, /audit/export, /settings, /connections
Catalog/stack, /stack/services/{name}, /mirror, /mirror/reload
Clusters and hosts/clusters, /clusters/{id}/summary, /topology, /bootstrap-tokens, /hosts/ssh-bootstrap, /hosts, /hosts/{id}, /hosts/{id}/metrics, /preflight, /facts/refresh, /parameters, /decommission
Services/clusters/{id}/services, /services/validate, /services/{name}, /actions, /commands/{command}, /components, /logs, /client-config
Configurations/clusters/{id}/services/{name}/config, /config/versions, /config/diff, /config/versions/{v}/restore, /config/groups, /config/preview, /config/apply, /clusters/{id}/blueprint
Operations/clusters/{id}/operations, /operations/running, /operations/{id}, /operations/{id}/tasks/{task}, /logs, /abort, /retry
Alerts/clusters/{id}/alerts, /alerts/{id}, /alerts/{id}/ack, /alert-definitions, /notification-targets
Monitoring/system/health, /clusters/{id}/sd/prometheus, /pki/ca, /clusters/{id}/certificates; outside /api/v1: /healthz, /readyz, /metrics

All paths above are below /api/v1.

Following an operation​

Every action returns {"operation_id": "..."}. Poll GET /api/v1/operations/{id} for the stages, tasks and progress, and GET /api/v1/operations/{id}/tasks/{task}/logs?after_seq=<n> for the log lines of a task (next_seq is the value of the next call, done turns true when the task is finished).

OP=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"action": "rolling_restart", "batch_size": 1}' \
https://<manager-host>:8460/api/v1/clusters/$CLUSTER/services/opa/actions | jq -r .operation_id)
curl -s -H "Authorization: Bearer $TOKEN" https://<manager-host>:8460/api/v1/operations/$OP | jq .status

Webhook notifications​

A webhook target receives a JSON POST with X-NM-Signature: t=<unix>,v1=<hex>. Check it on the receiving side: v1 must equal HMAC-SHA256(secret, "<t>.<raw body>") and t must be less than 5 minutes old.

Agent protocol​

The agent calls the manager, never the other way round:

  1. POST /agent/v1/register on port 8460 with the bootstrap token, the facts and a certificate request; the response holds the host id, the agent secret, the certificate and the manager CA, whose sha256 fingerprint the agent compares with --ca-sha256.
  2. On port 8461, with the client certificate: POST /agent/v1/heartbeat every 15 seconds (metrics, component states, facts when asked), GET /agent/v1/tasks?wait=25 (long poll), POST /agent/v1/tasks/{id}/logs and POST /agent/v1/tasks/{id}/result, POST /agent/v1/renew before the certificate expires.
  3. Each request is signed: Authorization: NM-HMAC <host_id>:<unix ts>:<hex> where the HMAC-SHA256 covers the method, the path with its query, the timestamp and the sha256 of the body. A timestamp more than 300 seconds away is refused.

The task types and their payloads are listed in section 9 of the contract.