Skip to content

HTTP API

All the HTTP routes kubelatch serves, who can call them, and what they do. The web interface uses this same API; you can call it with curl using a session cookie.

Who can call

Access What's needed
Anonymous Nothing.
Session The kubelatch_session cookie of a person with an enabled account. Without it, 401.
Admin Session of a person with the administrator role. Without the role, 403.
Bearer klt_ Authorization: Bearer klt_…, a kubelatch credential.
Actions OIDC token The id_token of a GitHub Actions job, in the body.
GitHub signature X-Hub-Signature-256 header with the HMAC of the body and GITHUB_WEBHOOK_SECRET.

/api conventions

  • JSON bodies up to 64 KiB. Unknown fields and extra data are rejected (400); a larger body is 413.
  • Every response carries Cache-Control: no-store.
  • Errors are {"error": "<message in Spanish>"} (see Errors).
  • An unknown route under /api/ is 404; a known route with another method is 405 with an Allow header.
  • Modifying requests (POST, PATCH, DELETE) sent by a browser from another origin are rejected with 403. curl and other non-browser clients pass.

Health

Method Route Access What it does
GET /healthz Anonymous 200 ok if the process is alive.
GET /readyz Anonymous 200 ok if Postgres responds within 2 s; otherwise, 503 database unavailable.

They only accept GET and HEAD; any other method is 405.

Proxy

Method Route Access What it does
Any /clusters/<id>/<Kubernetes API route> Bearer klt_ Forwards the request to cluster <id>'s API server, acting as the credential's owner (impersonation). /clusters/<id> and /clusters/<id>/ are equivalent to /. Ignores cookies. See The journey of a request.

Proxy errors are Kubernetes Status objects, so kubectl shows them well (Proxy errors). /clusters without a trailing slash responds 404.

CI and GitHub (outside /api)

These two routes use no session, cookies or origin protection: authentication travels in the request itself. They only accept POST (405 with Allow for anything else).

Method Route Access What it does
POST /v1/ci/github-actions/token Actions OIDC token Exchanges {"token": "<id_token>"} for a bot credential according to the matching trust rule. Responds 201 with credential, token, kubeconfig, clusters and expires_at, once only. Limit: 60 requests per minute per IP. See Credentials for GitHub Actions.
POST /v1/github/webhook GitHub signature Receives the GitHub App's events. organization/member_removed instantly disables the linked account; installation/deleted or suspend are logged; the rest are accepted and ignored. Requires Content-Type: application/json and a body up to 1 MiB. Without GITHUB_WEBHOOK_SECRET it responds 404.

Session and own account

Method Route Access What it does
POST /api/auth/login Anonymous Password login: {"login", "password"}. Opens a session and returns the account.
POST /api/auth/logout Session Closes all of the person's sessions. 204.
POST /api/auth/link Anonymous Looks up an invitation or linking link without consuming it: {"token": "kli_…"} → login, display_name, purpose, expires_at, github_required.
POST /api/auth/link/complete Anonymous Completes a link by setting the password: {"token", "password"}. Opens a session.
GET /api/me Session Your own account: login, name, email, admin, whether it has and can use a password, break-glass status, and GitHub link.
POST /api/me/password Session Changes your own password: {"current_password", "new_password"}. Closes the other sessions.

GitHub login

Method Route Access What it does
GET /api/auth/methods Anonymous {"github": <boolean>, "org": "<org>"}: what the login page offers.
GET /api/auth/github/login Anonymous Sets the state cookie and redirects (302) to GitHub to authorize the App.
GET /api/auth/github/callback Anonymous with state cookie Return from GitHub. Redirects to / with a session, to /mi-cuenta?github=linked after linking from Mi cuenta (My account), or to /login?error=<code> (/mi-cuenta?error=<code>) if it fails.
POST /api/auth/github/link Anonymous Starts completing an invitation or linking link with GitHub: {"token": "kli_…"} → {"url"} to navigate to.
POST /api/me/github/link Session Starts linking your own account, if not already linked (409 if it already is) → {"url"}.
GET /api/github/status Admin Configuration (configured, org, webhook, require_org_2fa) and the result of the last member sync (last_sync).

Without GitHub configured, /api/auth/github/login, /api/auth/github/callback, /api/auth/github/link and /api/me/github/link respond 404.

Users and bots

All require admin.

Method Route What it does
GET /api/subjects Lists people and bots.
POST /api/subjects Creates a person ({"login", "display_name", "email", "is_admin"}) and returns its invite_link once only; or a bot ({"kind": "bot", "login", "display_name"}).
PATCH /api/subjects/{id} Changes a person's display_name, email or is_admin.
POST /api/subjects/{id}/reset-link Generates a reset or linking link (24 h) and returns it once as reset_link. Revokes pending ones and instantly closes that person's sessions. 409 if the account is disabled.
POST /api/subjects/{id}/disable Disables a person or bot: closes sessions and revokes credentials.
POST /api/subjects/{id}/enable Re-enables them. Revoked credentials stay revoked.
POST /api/subjects/{id}/logout Closes all of a person's sessions.
DELETE /api/subjects/{id}/github Unlinks a person's GitHub account and closes their sessions.

Clusters

All require admin.

Method Route What it does
GET /api/clusters Lists clusters with status, last reconciliation and last error.
POST /api/clusters Registers a cluster: {"id", "name"}. The id is the proxy URL's slug.
GET /api/clusters/{id} A cluster.
DELETE /api/clusters/{id} Cleans up the RBAC kubelatch manages in the cluster and deletes the cluster, its permissions and the credentials restricted to it. Returns rbac_cleanup with status (ok, error or skipped), error, bindings_deleted, roles_deleted and lock_held.
GET /api/clusters/{id}/bootstrap.yaml Downloads the bootstrap manifest to apply in the cluster.
POST /api/clusters/{id}/tokens Pastes the bootstrap JSON: {"server", "ca", "proxyToken", "reconcilerToken"}. Validates, encrypts, and kicks off the first reconciliation.
GET /api/clusters/{id}/namespaces The cluster's namespaces with their PSA level (psa_enforce).
POST /api/clusters/{id}/reconcile Reconciles now, synchronously. 502 with the error if the cluster side fails; 409 if another pass is in progress or tokens are missing.

Permissions

Method Route Access What it does
GET /api/grants Session Lists permissions. Filters: subject, cluster, include_revoked=true. Without the admin role, only your own (403 if you ask for another subject).
POST /api/grants Admin Grants: {"subject_id", "cluster_id", "tier", "scope", "expires_at", "note"}.
DELETE /api/grants/{id} Admin Revokes a permission.
GET /api/tiers Session Catalog of tiers, protected namespaces, and whether PSA is required.

Credentials

Method Route Access What it does
GET /api/credentials Session Inventory: an admin sees all of them; a person, their own.
POST /api/credentials Session Issues a credential: {"ttl", "name", "note", "cluster_id", "subject_id"}. ttl is required (7d, 12h). Without cluster_id, it's valid in every cluster where the subject has permissions. Only an admin issues one for another subject. Returns token and kubeconfig once only.
POST /api/credentials/{id}/revoke Session (owner or admin) Revokes a credential. Optional body {"reason"}.

Audit

Method Route Access What it does
GET /api/audit Session Requests to the proxy, 50 at a time. Filters: subject, cluster, namespace, verb, resource, credential, from and to (RFC 3339), include_discovery=true, page (1 to 10000). Without the admin role, only your own.
GET /api/audit/{id} Session One request by its Audit-ID. Someone else's responds 404 if you're not an admin.
GET /api/control-events Admin Control plane actions and login attempts, 50 at a time. Filter: page.

By default /api/audit hides rows with no resource (API discovery and authentication failures); include_discovery=true shows them. Both listings return events, page, page_size and has_more.

CI trust rules

All require admin.

Method Route What it does
GET /api/ci/trust-rules Lists the rules plus the audience, issuer, token lifetime, and exchange route a workflow should use.
POST /api/ci/trust-rules Creates a rule: {"name", "bot_subject_id", "repository_owner_id", "repository_id", "ref", "environment"}. Empty ref and environment match any.
DELETE /api/ci/trust-rules/{id} Deletes a rule. Does not revoke credentials already issued.

Web interface

Any other route serves the embedded SPA: the file if it exists, or index.html for interface routes (/login, /cuenta, /mi-cuenta, /admin/…). A missing file under /assets/ is 404. It only accepts GET and HEAD.