This is the central schema repository for the Meshery platform. Schemas here drive Go struct generation, TypeScript type generation, and RTK Query client generation. Mistakes in schema design propagate into generated code across multiple downstream repos (meshery/meshery, layer5io/meshery-cloud).
make build # generate Go structs + TypeScript types + RTK clients
npm run build # build TypeScript distribution (dist/)Generated artifacts (models/, typescript/generated/) are committed by automation on master. The TypeScript distribution in dist/ is produced by the npm build/publish workflow and is not committed to this repo. Do not edit generated artifacts by hand, and do not manually commit regenerated output in normal PRs unless the change explicitly requires it.
This is the most critical design rule in this repo. Every agent or contributor MUST follow it.
The YAML file for an entity represents the full server-side object as returned in API responses. It is NOT a request body schema.
Required properties of every entity .yaml:
additionalProperties: falseat the top level- All server-generated fields defined in
properties:id,created_at,updated_at,deleted_at - Server-generated fields that are always present belong in
required
# CORRECT: keychain.yaml
type: object
additionalProperties: false
required:
- id
- name
- owner
- created_at
- updated_at
properties:
id:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
name:
type: string
owner:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
created_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/created_at
updated_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/updated_at
deleted_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/nullTimeFor any entity that has POST or PUT operations, define a {Construct}Payload schema in api.yml that:
- Contains only client-settable fields — no
created_at,updated_at,deleted_at - Makes
idoptional withjson:"id,omitempty"for upsert patterns - Is the schema referenced by all
requestBodyentries forPOST/PUT
# CORRECT: in api.yml
components:
schemas:
KeychainPayload:
type: object
description: Payload for creating or updating a keychain.
required:
- name
properties:
id:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
description: Existing keychain ID for updates; omit on create.
x-oapi-codegen-extra-tags:
json: "id,omitempty"
name:
type: string
owner:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
x-oapi-codegen-extra-tags:
json: "owner,omitempty"# WRONG — forces clients to supply server-generated fields
post:
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Keychain"
# CORRECT — separate payload type for writes
post:
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/KeychainPayload"When uncertain, model new schemas on these:
schemas/constructs/v1beta1/connection/—connection.yaml+ConnectionPayloadinapi.ymlschemas/constructs/v1beta1/key/—key.yaml+KeyPayloadinapi.ymlschemas/constructs/v1beta1/team/—team.yaml+teamPayload/teamUpdatePayloadinapi.ymlschemas/constructs/v1beta1/environment/—environment.yaml+environmentPayloadinapi.yml
Before opening a PR, verify:
-
<construct>.yamlhasadditionalProperties: false -
<construct>.yamllists all server-generated fields inpropertiesand appropriate ones inrequired -
api.ymldefines{Construct}Payloadwith only client-settable fields - All
POST/PUTrequestBodyentries reference{Construct}Payload -
GETresponses reference the full{Construct}entity schema
- Property names:
camelCase(schemaVersion,displayName) - ID-suffix fields:
lowerCamelCase+Id(modelId,registrantId) - Enums: lowercase words (
enabled,ignored,duplicate) - Object names: singular nouns (
model,component,design) components/schemasnames: PascalCase nouns (Model,Component,KeychainPayload)- Files/folders: lowercase (
api.yml,keychain.yaml,templates/keychain_template.json) - Endpoint paths:
/apiprefix, kebab-case, plural nouns (/api/workspaces,/api/environments) - Path params: camelCase with
Idsuffix ({subscriptionId},{connectionId},{orgId}— NOT{orgID}, NOT{org_id}) operationId: lower camelCase verbNoun (createKeychain,updateEnvironment— NOTCreateKeychain, NOTUpdateEnvironment)
Every element in the API has exactly one correct casing. The table below is the single authoritative reference:
| Element | Casing | Example | Counter-example |
|---|---|---|---|
| Schema property names (non-DB) | camelCase | schemaVersion, displayName |
schema_versionSchemaVersion |
| ID-suffix properties | camelCase + Id |
modelId, registrantId |
modelIDmodel_id |
| DB-mirrored fields | snake_case | created_at, updated_at, user_id |
createdAt |
| Enum values | lowercase | enabled, ignored |
EnabledENABLED |
components/schemas names |
PascalCase | ModelDefinition, KeychainPayload |
modelDefinition |
| File and folder names | lowercase | api.yml, keychain.yaml |
Keychain.yaml |
| Path segments | kebab-case, plural nouns | /api/role-holders |
/api/roleHolders |
| Path parameters | camelCase + Id |
{orgId}, {workspaceId} |
{orgID}{org_id} |
operationId |
lower camelCase verbNoun | getAllRoles, createWorkspace |
GetAllRolesget_all_roles |
| Go type names | PascalCase (generated) | Connection, KeychainPayload |
— |
| Go field names | PascalCase (generated) | CreatedAt, UpdatedAt |
— |
| TypeScript type names | PascalCase (generated) | Connection, KeychainPayload |
— |
snake_case is only for DB-mirrored fields — created_at, updated_at, deleted_at, user_id, and similar fields that map directly to a database column named with underscores. All other names follow the rules above.
These rules govern how endpoints are structured. They are enforced in part by make validate-schemas.
| Use case | Method | Example |
|---|---|---|
| Create a resource | POST |
POST /api/workspaces → 201 |
| Upsert a resource | POST |
POST /api/keys → 200 |
| Update an existing resource | PUT or PATCH |
PUT /api/workspaces/{workspaceId} → 200 |
| Non-CRUD action on a resource | POST to a sub-resource path |
POST /api/invitations/{invitationId}/accept |
| Bulk delete | POST to a /delete sub-resource |
POST /api/designs/delete → 200 |
| Single delete | DELETE |
DELETE /api/keys/{keyId} → 204 |
Do NOT use DELETE with a request body for bulk operations. REST semantics do not define a request body for DELETE; many HTTP clients and proxies strip it silently. Use a POST /api/{resources}/delete sub-resource instead:
# WRONG — DELETE with a request body
delete:
operationId: deletePatterns
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatternIds'
# CORRECT — POST sub-resource for bulk delete
post:
operationId: deletePatterns
summary: Bulk delete patterns by ID
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatternIds'
responses:
"200":
description: Patterns deleted| Code | Meaning | When to use |
|---|---|---|
| 200 | OK | Request succeeded; body contains the result (queries, upserts, actions) |
| 201 | Created | A new resource was created; body contains the new resource |
| 202 | Accepted | Request received; operation will complete asynchronously |
| 204 | No Content | Request succeeded; no response body (e.g., a single-resource DELETE) |
Use 201 (not 200) for POST endpoints that exclusively create a new resource. Use 200 for upsert operations where the resource may already exist.
Endpoints are grouped into logical categories under /api:
| Category prefix | Domain |
|---|---|
/api/identity/ |
Users, orgs, roles, teams, invitations |
/api/integrations/ |
Connections, environments, credentials |
/api/content/ |
Designs, views, components, models |
/api/entitlement/ |
Plans, subscriptions, features |
/api/auth/ |
Tokens, keychains, keys |
New endpoints must be placed in the appropriate category. Path segments must be kebab-case plural nouns matching the resource name.
schemas/constructs/v1beta1/<construct>/
api.yml # OpenAPI spec: endpoints + all schema definitions
<construct>.yaml # Entity (response) schema
templates/
<construct>_template.json # Example instance
<construct>_template.yamlAuto-generated Go structs (models/<version>/<construct>/<construct>.go) are committed by the artifact-generation workflow on master. Do not edit them by hand; the manually written helpers below are the files contributors should maintain directly:
models/v1beta1/<construct>/
<construct>.go # Auto-generated — DO NOT edit
<construct>_helper.go # Manual — add SQL driver, Entity interface, TableName(), etc.Always add // This is not autogenerated. at the top of helper files.
Use x-generate-db-helpers: true on a schema component to auto-generate Scan/Value SQL driver methods for types stored as JSON blobs in a single DB column. Do NOT use this for types mapped to full DB tables.
Control which bundled output includes an API path:
x-internal: ["cloud"]— cloud-only (_openapi_build/cloud_openapi.yml)x-internal: ["meshery"]— Meshery-only (_openapi_build/meshery_openapi.yml)- Omit
x-internal— included in both bundled outputs
See The Dual-Schema Pattern above for the canonical entity/payload rules and reference examples used throughout this repo.
When manually implementing sql.Scanner and driver.Valuer for map-like types:
core.Map marshals nil maps to the JSON string "null" instead of SQL NULL. Prefer the same behavior for new or updated helpers unless the column is explicitly nullable and the nil-vs-empty distinction is required and documented.
// CORRECT — matches core.Map pattern
func (m MapObject) Value() (driver.Value, error) {
b, err := json.Marshal(m)
if err != nil {
return nil, err
}
return string(b), nil
}
// WRONG — writes SQL NULL, inconsistent with core.Map
func (m MapObject) Value() (driver.Value, error) {
if m == nil {
return nil, nil // <- do not do this
}
...
}When src is nil (SQL NULL), new or updated Scan implementations should explicitly zero the receiver. Some legacy helpers return early, but clearing the receiver avoids stale data if the same struct is reused across rows.
// CORRECT
case nil:
*m = nil
return nil
// WRONG — leaves stale data
case nil:
return nil- ❌ Hand-editing generated Go code in
models/directory - ❌ Hand-editing generated TypeScript code in
typescript/generated/directory - ❌ Hand-editing built files in
dist/directory - ❌ Using deprecated
core.jsonreferences - ❌ Adding redundant
x-oapi-codegen-extra-tagswhen using schema references - ❌ Forgetting to update template files in the
templates/subdirectory with default values - ❌ Not testing the build process after schema changes
- ❌ Placing template files outside the
templates/subdirectory - ❌ Using
.d.tsextension in TypeScript import paths - ❌ Assuming schema property names are PascalCase (check actual generated
.d.tsfiles) - ❌ Adding
x-generate-db-helperson individual properties — it must be at the schema component level - ❌ Using
x-generate-db-helperson amorphous types without a fixed schema — usex-go-type: "core.Map"instead - ❌ Using the full entity schema as a
POST/PUTrequestBody— always use a separate*Payloadschema - ❌ Omitting
additionalProperties: falsefrom entity<construct>.yamlfiles - ❌ Adding new
Value()implementations that return(nil, nil)unless SQL NULL behavior is explicitly required and documented - ❌ In new
Scan()implementations, returning without zeroing the receiver whensrcis nil - ❌ Using PascalCase for new
operationIdvalues — always lower camelCase (getPatterns, notGetPatterns) - ❌ Using SCREAMING_CASE path parameters (
{orgID},{roleID}) — always camelCase withIdsuffix ({orgId},{roleId}) - ❌ Using
DELETEwith a request body for bulk operations — usePOST /api/{resources}/deleteinstead - ❌ Returning 200 from a
POSTthat exclusively creates a new resource — use 201
- Modified only schema JSON/YAML files (not generated code)
- Updated corresponding template files in
templates/subdirectory with default values - Used non-deprecated
v1alpha1/core/api.ymlreferences - If adding new schemas, referenced them from
api.yml(the construct index file) - Removed redundant tags when using schema references
- If a schema type is stored as a JSON blob in a DB column AND has a dedicated schema definition, used
x-generate-db-helpers: trueat the schema component level (not per-property) - Ran
make buildsuccessfully - Ran
go test ./...successfully - Ran
npm run buildsuccessfully - Verified only schema JSON/YAML files are in the commit
- If updating
typescript/index.ts, verified import paths are correct - (New entity)
<construct>.yamlhasadditionalProperties: false - (New entity)
<construct>.yamlincludes all server-generated fields inpropertiesandrequired - (New entity with writes)
api.ymldefines a{Construct}Payloadwith only client-settable fields - (New entity with writes) All
POST/PUTrequestBodyentries reference{Construct}Payload, not{Construct} - (New SQL driver)
Value()always marshals — never returns(nil, nil) - (New SQL driver) Prefer
Scan()implementations that set*m = nilwhensrcis nil; some legacy drivers may still return early - (New endpoint)
operationIdis lower camelCase verbNoun - (New endpoint) Path parameters are camelCase with
Idsuffix (e.g.,{workspaceId}, not{workspaceID}) - (New endpoint) No
DELETEoperation has arequestBody— bulk deletes usePOST .../delete - (New
POSTfor creation only) Response code is 201, not 200
If you're unsure about any schema modification:
- Check existing schemas for patterns (e.g.,
environment.yaml,connection.yaml) - Look at
schemas/constructs/v1alpha1/core/api.ymlfor available core schema definitions - Examine any construct's
api.ymlto see how subschemas are referenced and endpoints are defined - Check generated
.d.tsfiles for actual type/property names - Review this document for guidelines
- Test your changes with
make buildbefore committing