API Reference

Clusters

Register and manage target Kubernetes clusters

Clusters API

Register remote Kubernetes clusters as deployment targets for multi-cluster GitOps. All endpoints require a valid session and the noted permission.

Endpoints

List Clusters

GET /api/v1/clusters

Requires cluster:read. Each cluster is enriched with a live probe of its API server (version, provider, node count, resource usage, deployment health), and the probe result refreshes the stored status and last_seen.

Query paramDescription
shallow1 skips live probing and returns stored data only — cheap existence checks

Response — 200 OK

{
  "clusters": [
    {
      "id": "9f1c2a4e-...",
      "name": "staging",
      "server_url": "https://staging.example.com:6443",
      "namespace": "default,team-a",
      "status": "connected",
      "version": "v1.29.1",
      "provider": "eks",
      "region": "us-east-1",
      "node_count": 6,
      "cpu_usage": 34,
      "memory_usage": 61,
      "deployment_count": 12,
      "healthy_deployments": 11,
      "last_seen": "2026-09-20T14:00:00Z",
      "created_at": "2026-01-12T11:00:00Z"
    }
  ]
}

status is connected, disconnected, or unknown (never probed). cpu_usage/memory_usage are whole-percent utilisation across nodes.


Register Cluster

POST /api/v1/clusters

Requires cluster:register. Request bodies are capped at 1 MiB.

Request Body

{
  "name": "staging",
  "server_url": "https://staging.example.com:6443",
  "kubeconfig": "apiVersion: v1\nkind: Config\n...",
  "namespaces": ["default", "team-a"]
}
FieldTypeRequiredDescription
namestringYesUnique name — DNS-1123 label (lowercase, digits, dashes; max 63 chars)
server_urlstringYesAPI server URL — must be https://
kubeconfigstringNoRaw kubeconfig YAML. Omit for in-cluster (the server's own service account)
namespacesstring[]NoScope the cluster to these namespaces; empty means cluster-wide

The kubeconfig is validated at registration: unparseable configs and configs using exec or auth-provider credential plugins are rejected with 400. Accepted kubeconfigs are sealed with AES-256-GCM before storage.

Response — 201 Created

{
  "cluster": {
    "id": "9f1c2a4e-...",
    "name": "staging",
    "server_url": "https://staging.example.com:6443",
    "namespace": "default,team-a",
    "status": "unknown",
    "created_at": "2026-09-20T14:00:00Z"
  }
}

Registration does not probe the cluster — the first GET /clusters does. Use POST /clusters/test to verify connectivity before registering.

Errors

StatusDescription
400Missing/invalid name, non-HTTPS server_url, invalid namespace, or rejected kubeconfig
401No valid session
403Missing cluster:register permission
409Cluster name already registered
413Request body over 1 MiB

Test Cluster Connection

POST /api/v1/clusters/test

Requires cluster:register. Dry-run connectivity check against supplied credentials — nothing is stored. Always returns 200; the ok field carries the result so clients can render it inline.

Request Body

{
  "server_url": "https://staging.example.com:6443",
  "kubeconfig": "apiVersion: v1\nkind: Config\n..."
}
FieldTypeRequiredDescription
server_urlstringNoOverrides the kubeconfig's cluster endpoint; must be https:// when set
kubeconfigstringNoRaw kubeconfig YAML. Omit to test the server's own in-cluster credentials

Response — 200 OK

{
  "ok": true,
  "version": "v1.29.1",
  "namespaces": ["default", "kube-system", "team-a"]
}
{
  "ok": false,
  "error": "cannot reach API server: context deadline exceeded"
}

namespaces lists what the credentials can see — use it to populate a namespace picker. If the credentials can't list namespaces the test still succeeds but namespaces is omitted.

Security

  • Credentials sealed at rest — kubeconfigs are encrypted with AES-256-GCM (enc:v1: format) keyed by WARDN_CREDENTIALS_KEY before hitting Postgres
  • No executable credentials — kubeconfigs using exec or auth-provider plugins are rejected; the server never runs commands from user-supplied config
  • HTTPS only — server_url must use TLS; plaintext endpoints are refused
  • Least privilege — use service accounts with only the permissions Wardn needs
  • Rotation — rotate WARDN_CREDENTIALS_KEY by re-sealing stored kubeconfigs; rotate cluster credentials via re-registration
GET  /api/v1/clusters/{name}/namespaces              # live namespace list (cluster:read)
POST /api/v1/clusters/{name}/shell                   # spawn a shell pod (cluster:shell)

The shell pod's ServiceAccount (view vs admin) is picked from the caller's effective permissions — a read-only user gets a shell that physically cannot write.

On this page