0012: The JSON API is defined by an OpenAPI document¶
Date: 2026-10-07 · Status: accepted
Context¶
The design lists the /api/v1 endpoints but does not say how they are specified. The console handlers, the CLI and
the tests each had their own copy of the request and response shapes, written by hand. Nothing checked that they
matched, and input checks were spread over the handlers.
Decision¶
- One source of truth.
api/openapi/v1.yaml(OpenAPI 3.0) describes every/api/v1operation: parameters, request bodies, responses and errors. It also holds each operation's minimum role inx-keeper-role. The server serves the document atGET /api/v1/openapi.yaml. - Generated code.
go generate ./internal/apiv1(oapi-codegen, pinned as a Go tool ingo.mod) buildsinternal/apiv1: the DTOs, a typed client and the embedded document. - The console handlers return these DTOs.
- The CLI uses the generated client.
- Storage formats (manifest, inventory, tags, previews), audit events and principals are not copied. The document
describes them and maps them to the existing Go types with
x-go-type. make lint(hack/check-generated.sh) fails when the generated code is out of date.- Request validation. Every API request passes through kin-openapi's
openapi3filterafter authentication and before its handler. A request that does not match the document gets 400 with the failing parameter or body field. An unauthenticated request still gets 401. - Request bodies reject unknown fields (
additionalProperties: false). - Names, durations, enums and limits are checked by their patterns and bounds.
- Roles. Routes are registered from a table. Each route takes its role from the document and fails start-up if it is missing, so routes and documented roles cannot drift.
- Response validation in tests. With
KEEPER_API_VALIDATE_RESPONSES=true(orOptions.ValidateResponses), every response is checked against the document, and a mismatch becomes a 500. The unit, CLI and UI tests and the e2e harness turn it on. Production does not, because it buffers each response./events(SSE) is not checked.
Consequences¶
An API change starts in api/openapi/v1.yaml, then make generate. Third-party clients can be generated from the
served document. Validation adds a route lookup and a schema check per request, which is well inside the console's
latency budget.