On this page
ADR-0007: REST with generated OpenAPI for browsers; gRPC for machine clients
Status: Accepted (plan decision D7). Implemented: the assembled OpenAPI document (docs/api/openapi.json, WP-40) and the gRPC transport (WP-41) exist (amended 2026-11-10).
Context
The brief requires a REST API for browsers. The portal is TypeScript and should not
hand-maintain response types. Machine clients (clctl, integrations) benefit from a
typed RPC interface.
Decision
- REST under
/api/v1with Axum; each domain router annotates its handlers with utoipa so that the composition can merge them into one OpenAPI 3.1 document.clctl openapiprints it; the committed copy isdocs/api/openapi.json; the portal generates its client types from it (make generate-web). - gRPC (Tonic) package
commonlayer.v1for machine clients, calling the same application services. Every RPC maps to an existing service method.
Consequences
- One authorization path regardless of transport, because both transports call the same services.
- The router-level test enumerates the OpenAPI document and requires every operation not
tagged
publicto answer 401 without credentials. Thepublictag is therefore load-bearing (see ADR-0023). - Until
docs/api/openapi.jsonexists, the portal uses a hand-written placeholder schema that covers onlyGET /api/v1/auth/session. - gRPC is not implemented yet; it will be documented in its own page under API once available.
Amendment, 2026-11-10
- The OpenAPI document is assembled by the composition and committed as
docs/api/openapi.json; CI fails when it is stale (make check-generated). The portal generates its client types from it; the placeholder schema is gone. - gRPC is implemented (WP-41): 12 services and 59 RPCs in
proto/commonlayer/v1/, served by the API process, documented in gRPC API. Four RPCs of the plan were dropped because API tokens can never hold the permissions they need or no service read existed;RegistryService.GetPullStatswas restored in WP-70b once a pull-statistics read existed. RPCs that only an interactive session may perform have no gRPC equivalent.