Skip to content

API Overview

The dockmesh API is a plain REST + JSON interface served at /api/v1/. The same API powers the SvelteKit UI — everything you can do in the browser, you can do over HTTP.

https://dockmesh.example.com/api/v1/

Behind the scenes, Caddy (or your own reverse proxy) terminates TLS and proxies to the Go server. In dev, http://localhost:8080/api/v1/ works.

Three authentication options:

Exchange username+password (or OIDC callback) for a short-lived access token (15 min) and a refresh token (7 days). Send the access token as Authorization: Bearer <token> on every request.

Terminal window
# Login
curl -X POST https://dockmesh.example.com/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"••••"}'
# Response
{
"access_token": "eyJhbGci...",
"refresh_token": "eyJhbGci...",
"expires_in": 900
}
# Use it
curl https://dockmesh.example.com/api/v1/stacks \
-H 'Authorization: Bearer eyJhbGci...'

Refresh with POST /auth/refresh { "refresh_token": "..." } before expiry.

Settings → API tokens → New token creates a long-lived token scoped to a role. Use it exactly like a JWT — Authorization: Bearer <token> — but without expiry. Revoke from the UI when no longer needed.

Agents use their certificate on the /agent/ endpoint. Not for general API use.

All endpoints accept and return JSON. Set Content-Type: application/json on POST/PATCH/PUT.

List endpoints (/stacks, /containers, etc.) accept:

ParamDefaultDescription
page11-indexed page number
per_page501–200
sortvariese.g. name, -created_at (minus for desc)
filter[field]e.g. filter[host]=prod-01

Response envelope:

{
"data": [ /* items */ ],
"meta": { "total": 137, "page": 1, "per_page": 50 }
}

Errors use a consistent shape and appropriate HTTP status:

{
"error": {
"code": "stack.deploy_failed",
"message": "pull access denied for private/image",
"details": { "stack": "analytics", "host": "prod-01" }
}
}
StatusMeaning
400Validation error (bad input)
401No/invalid auth
403Authenticated but forbidden by RBAC
404Resource not found
409Conflict (e.g. stack already exists)
422Unprocessable (e.g. compose.yaml failed to parse)
500Server error — report with the request_id header

Every response carries an X-Request-Id header for log correlation.

Defaults:

  • Anonymous: 60 req/min
  • Authenticated: 600 req/min
  • Burst: 20

Override via DOCKMESH_RATE_LIMIT env vars. Hit the limit and you get 429 Too Many Requests with Retry-After header.

Several endpoints stream in real time over WebSocket:

EndpointStreams
/api/v1/containers/{id}/logs/streamLive container logs
/api/v1/containers/{id}/stats/streamLive CPU/mem/net/io
/api/v1/containers/{id}/execInteractive shell
/api/v1/hosts/{id}/events/streamDocker daemon events

Authenticate by setting Authorization header on the HTTP upgrade request (most clients support this).

The machine-readable OpenAPI 3.1 spec is served at three paths — all public (no Bearer token needed) so consumers can integrate without first getting credentials:

  • GET /api/v1/openapi.json — JSON, 5-minute cache, ready for openapi-typescript, oapi-codegen, Swagger Codegen, openapi-generator-cli, etc.
  • GET /api/v1/openapi.yaml — raw YAML — what spectral / redocly linters prefer
  • GET /api/v1/docs — rendered Swagger UI with “Try it out” wired up against the live server. Log in separately in Swagger UI’s Authorize button (paste any Bearer token you already have).

Point any generator at openapi.json:

Terminal window
# Generate a TypeScript client
npx openapi-typescript https://dockmesh.example.com/api/v1/openapi.json \
-o ./dockmesh-api.d.ts
# Or a Go client
oapi-codegen -generate client \
-o dockmesh_client.go -package dockmesh \
https://dockmesh.example.com/api/v1/openapi.json

The spec is hand-maintained at internal/api/openapi/openapi.yaml in the dockmesh repo. A drift test (TestOpenAPIDriftAgainstRoutes) fails CI if a route is registered without a matching spec entry (or vice versa), so the documentation and the running server never diverge — that’s the mechanical guarantee, not a process promise.