Problem
meshery/schemas generates types only. Every other dimension of the OpenAPI contract, path registration, path-parameter decoding, query-parameter binding, operation dispatch, and response shape enforcement, is re-implemented by hand in meshery/meshery and meshery/meshery-cloud. The result is structural, not incidental, drift:
- 63 path-parameter mismatches (e.g.,
{orgID} in consumer vs. {orgId} in spec)
- 217 ghost query parameters declared in the spec and never read by any handler
- 15 response-type mismatches where the handler returns a materially different Go type than the contract advertises
- Only 6 of ~135 audited endpoints are confirmed schema-driven in both consumer repos
None of these failures are visible to the compiler. Types-only generation is the root cause; it discards the exact portion of the contract where mismatches are most frequent and most invisible.
Requested change for meshery/schemas
Extend the make build pipeline to emit a strict server artifact per construct alongside the existing type files.
New generated files per construct
models/<version>/<construct>/
<construct>.go # EXISTING: request/response structs (unchanged)
server.go # NEW: ServerInterface + router wiring
params.go # NEW: one *Params struct per operationId
server.go: ServerInterface
One Go interface whose method set is in 1:1 correspondence with operationId entries in api.yml:
// Code generated by oapi-codegen. DO NOT EDIT.
type ServerInterface interface {
GetWorkspaces(w http.ResponseWriter, r *http.Request, params GetWorkspacesParams)
GetWorkspace(w http.ResponseWriter, r *http.Request, workspaceId openapi_types.UUID)
UpdateWorkspace(w http.ResponseWriter, r *http.Request, workspaceId openapi_types.UUID)
// ... one method per operationId
}
A consumer handler that omits any operationId will not compile. Renaming a path parameter in the spec produces an immediate compile error in every downstream repo.
params.go: typed parameter structs
One struct per operationId containing the exact set of query/path/header parameters declared in the spec:
type GetWorkspacesParams struct {
Page *int `form:"page,omitempty"`
PageSize *int `form:"page_size,omitempty" validate:"omitempty,min=1"`
Filter *string `form:"filter,omitempty"`
Order *string `form:"order,omitempty"`
Search *string `form:"search,omitempty"`
}
Ghost parameters become structurally impossible: the params struct is the parameter universe.
Strict response envelopes (strict-server mode)
type GetWorkspaceResponseObject interface {
VisitGetWorkspaceResponse(w http.ResponseWriter) error
}
type GetWorkspace200JSONResponse Workspace
type GetWorkspace404Response struct{}
A handler cannot return an undeclared status code or a mismatched content type.
Framework adapters
Generate thin adapter files (adapter_gorilla.go, adapter_echo.go) that register the spec's paths on the host router. Consumer repos stop hand-authoring router.HandleFunc(...) for migrated constructs.
spec.go: embedded document
Embed the bundled OpenAPI document and expose a Spec() accessor for /api/openapi.json. Runtime and spec become the same artifact.
Generator configuration
Use oapi-codegen in strict-server mode. Target: --generate std-http-server,strict-server,types. Keep existing --generate types output unchanged. Gate new output behind a build flag (GENERATE_SERVER=true) so the pipeline is opt-in per construct during rollout.
Phased rollout (scope for this repo)
| Phase |
Work in meshery/schemas |
| 0 |
Add strict-server generation target behind feature flag; pilot on key + keychain constructs |
| 1 |
Normalize all 63 path-parameter mismatches in api.yml to camelCase + Id (e.g., {orgId}, {connectionId}); promote Rule 4 from suppressed to blocking in CI by wiring --style-debt into schema-audit.yml (the rule logic already exists in validation/rules_naming.go) |
| 2–4 |
Enable generation for remaining constructs in drift-rank order: workspaces → connections → designs → environments → teams → organizations → events → roles → tokens |
Consumer-repo migration (wiring handlers to the generated interfaces) is tracked separately in meshery/meshery and meshery/meshery-cloud.
Acceptance criteria
Out of scope
- Handler business logic: handler bodies remain the contributor's responsibility
- Framework migration: Gorilla and Echo are preserved via generated adapters
References
Problem
meshery/schemasgenerates types only. Every other dimension of the OpenAPI contract, path registration, path-parameter decoding, query-parameter binding, operation dispatch, and response shape enforcement, is re-implemented by hand inmeshery/mesheryandmeshery/meshery-cloud. The result is structural, not incidental, drift:{orgID}in consumer vs.{orgId}in spec)None of these failures are visible to the compiler. Types-only generation is the root cause; it discards the exact portion of the contract where mismatches are most frequent and most invisible.
Requested change for
meshery/schemasExtend the
make buildpipeline to emit a strict server artifact per construct alongside the existing type files.New generated files per construct
server.go:ServerInterfaceOne Go interface whose method set is in 1:1 correspondence with
operationIdentries inapi.yml:A consumer handler that omits any
operationIdwill not compile. Renaming a path parameter in the spec produces an immediate compile error in every downstream repo.params.go: typed parameter structsOne struct per
operationIdcontaining the exact set of query/path/header parameters declared in the spec:Ghost parameters become structurally impossible: the params struct is the parameter universe.
Strict response envelopes (
strict-servermode)A handler cannot return an undeclared status code or a mismatched content type.
Framework adapters
Generate thin adapter files (
adapter_gorilla.go,adapter_echo.go) that register the spec'spathson the host router. Consumer repos stop hand-authoringrouter.HandleFunc(...)for migrated constructs.spec.go: embedded documentEmbed the bundled OpenAPI document and expose a
Spec()accessor for/api/openapi.json. Runtime and spec become the same artifact.Generator configuration
Use
oapi-codegeninstrict-servermode. Target:--generate std-http-server,strict-server,types. Keep existing--generate typesoutput unchanged. Gate new output behind a build flag (GENERATE_SERVER=true) so the pipeline is opt-in per construct during rollout.Phased rollout (scope for this repo)
meshery/schemasstrict-servergeneration target behind feature flag; pilot onkey+keychainconstructsapi.ymltocamelCase + Id(e.g.,{orgId},{connectionId}); promote Rule 4 from suppressed to blocking in CI by wiring--style-debtintoschema-audit.yml(the rule logic already exists invalidation/rules_naming.go)workspaces → connections → designs → environments → teams → organizations → events → roles → tokensConsumer-repo migration (wiring handlers to the generated interfaces) is tracked separately in
meshery/mesheryandmeshery/meshery-cloud.Acceptance criteria
make buildemitsserver.go,params.go, andspec.gofor pilot constructs (key,keychain)ServerInterfacehas one method peroperationIdin the construct'sapi.yml*Paramsstructs contain exactly the parameters declared in the spec (no extras, no omissions)adapter_gorilla.goandadapter_echo.goregister spec-defined routes without requiring hand-authoredHandleFunccallsspec.goembeds the bundled document and exportsSpec() []byteschema-audit.ymlrunsvalidate-schemas-strict(or passes--style-debt) on PRs so Rule 4 (IsBadPathParam) is blocking in CI, not suppressed. The rule and theSuggestPathParamfix suggestion already exist invalidation/rules_naming.go; only the CI wiring is missingmake buildremains green; existing type-only consumers are unaffected until they opt inOut of scope
References
make consumer-audit MESHERY_REPO=../meshery CLOUD_REPO=../meshery-cloudoapi-codegenstrict-server docs: https://github.com/oapi-codegen/oapi-codegen#strict-server-generation