Documentation
¶
Overview ¶
GraphQL config validators, split out of config_validate.go to keep each file focused (QG-086 / QG-050 file-size gate). Pure move — no behavior change; same package, same call sites in validateConfig / validateConfigGraphQL.
Password/secret hashing and wildcard-param validators, split out of config_validate.go to keep each file focused (QG-086 / QG-050 file-size gate). Pure move — no behavior change; same package, same call sites in validateConfig.
Package generator turns an apic JSON/YAML config into a complete Go server tree, its OpenAPI document, and its Go/TypeScript/Rust/Zig/Python clients.
Test-suite budget ¶
This package's tests are the largest in the repository, and they run on the push path: the org pre-push hook executes a bare `go test ./...`, which carries Go's default 10-minute per-package timeout. Three rules keep the suite inside that budget; break any of them and the hook starts timing out again rather than reporting a real failure.
Tests that generate a tree and then compile, vet or boot it must call t.Parallel(). They are independent — each works in its own t.TempDir() — and they are where essentially all of the wall time lives. A test that mutates process state (t.Setenv, t.Chdir, directly or through a helper such as writeTestConfig) must NOT be parallel; keep it sequential instead of reaching for a lock.
External compilers are throttled, not fanned out. Every cargo invocation, and every zig build reached through runZigBuild / runZigBuildOut / runZigBuildStdout, goes through runToolchainBuild (or runToolchainBuildStdout), which bounds how many run at once; an unbounded fan-out saturates the machine and gets compilers reaped mid-build. Their build caches are shared for the lifetime of the test binary by toolchainCacheDir — a cargo target directory keyed on the generated crate's content digest, and one zig global cache — so the same dependency graph is compiled once per run rather than once per test. Route any new `zig build` call site through one of those helpers rather than invoking `zig` directly with a bare os.Environ() — a residual handful of zig_client_*_test.go gates still call runCmd directly and bypass both the semaphore and the cache; fold those into the same helpers when touching them next.
The Rust and Zig build gates are opt-in, behind APIC_POLYGLOT_TOOLCHAIN_TESTS. They cost more than the rest of the package combined, and no pipeline ever ran them: nothing in .gitlab-ci.yml installs cargo or zig, so requireToolchain already skipped them everywhere but on developer machines. Run them deliberately with
APIC_POLYGLOT_TOOLCHAIN_TESTS=1 go test -count=1 ./internal/generator/
and export the variable on any job that does install those toolchains. The Python and TypeScript gates are NOT behind it — they are cheap, and an image could plausibly carry python3 or tsc.
Index ¶
- Constants
- Variables
- func DecodeSchemaFragment(path string, raw []byte) (map[string]SchemaDef, error)
- func DetectModuleForOutput(outDir string) (string, error)
- func DetectModulePath(dir string) (string, error)
- func EmitOpenAPIArtifacts(ctx context.Context, outDir string, clean bool, artifacts []OpenAPIArtifact) (err error)
- func EmitRuntime(outDir, module string) (err error)
- func EnsureGoModFloor(startDir string) (changed bool, detail string, err error)
- func Generate(ctx context.Context, cfg []byte, outDir string) error
- func GenerateWithOptions(ctx context.Context, cfg []byte, outDir, module string, ...) error
- func GenerateWithRESTInterface(ctx context.Context, cfg []byte, outDir, module string, ...) error
- func IsCompositeExpr(s string) bool
- func NestedRefsOf(prop SchemaProp) []string
- func NewMCPServer() *mcpServer
- func ParseAuthExpr(s string) ([][]string, error)
- func ParseSchemaRef(ref string) (relPath, name string, err error)
- func ReadConfigWithSchemaRoot(configPath, schemaRoot string) (result []byte, err error)
- func ReadSchemaFragment(reader io.Reader) ([]byte, error)
- func ResolveSchemaRefs(cfg []byte, baseDir string) ([]byte, error)
- func ResolveSchemaRefsInRoot(cfg []byte, baseDir, rootPath string) (result []byte, err error)
- func ResolveSchemaRefsWithLoader(cfg []byte, baseDir string, load FragmentLoader) ([]byte, error)
- func SpecHashOfJSON(raw []byte) (string, error)
- type APIData
- type APIOperation
- type APIRoute
- type AuthWiring
- type AuthzContract
- type CACPIVContract
- type CRLContract
- type ClientData
- type ClientGenConfig
- type ClientGraphQLData
- type ClientGraphQLOperation
- type ClientGraphQLSubscription
- type ClientMCPTool
- type ClientRoute
- type ClientSchema
- type ClientSchemaField
- type ClientWebSocket
- type Config
- type ExposureConfig
- type FileField
- type FormScalarField
- type FragmentLoader
- type FuzzAuthRoute
- type FuzzAuthSeed
- type FuzzData
- type FuzzRoute
- type GQLArg
- type GQLHandlersData
- type GQLProxyData
- type GQLProxyField
- type GQLResolverField
- type GQLResolversData
- type GQLSchemaCustomScalar
- type GQLSchemaData
- type GQLSchemaEnum
- type GQLSchemaEnumValue
- type GQLSchemaField
- type GQLSchemaInputField
- type GQLSchemaInputType
- type GQLSchemaType
- type GQLSubscriptionField
- type GenerationPlan
- type GraphQLConfig
- type GraphQLCustomScalar
- type GraphQLEnum
- type GraphQLEnumValue
- type GraphQLField
- type GraphQLFuzzData
- type GraphQLInputField
- type GraphQLInputType
- type GraphQLSubField
- type MCPConfigSection
- type MCPData
- type MCPFuzzData
- type MCPFuzzTool
- type MCPTool
- type MCPToolConfig
- type MTLSContract
- type MTLSRuntimeRoute
- type NestedPattern
- type NestedValidationNode
- type OCSPContract
- type OIDCContract
- type OIDCGrantSchemas
- type OIDCRefreshTokenPolicy
- type OpenAPIArtifact
- type OwnershipConfig
- type PackagePaths
- type QueryScalarField
- type ReactQueryData
- type ReactQueryResource
- type ReactQueryRoute
- type ReactUIData
- type SchemaDef
- type SchemaProp
- type SecurityContract
- type ServerData
- type SharedConfig
- type TestRequestExample
- type TestsData
- type TypeField
- type TypeSchema
- type TypeValidation
- type TypedPathParam
- type TypesData
- type UIAuth
- type UIEndpoint
- type UIGraphQLOp
- type UIMcpTool
- type UISchema
- type UISchemaField
- type UIWSEndpoint
- type WSAuthPolicy
- type WSData
- type WSEndpoint
- type WSFuzzData
- type WSFuzzEndpoint
- type WebauthnContract
- type WebauthnUserHandleDocs
- type WebhookContract
- type WebsocketEndpoint
Constants ¶
const DefaultRESTInterfaceName = "ServerInterface"
DefaultRESTInterfaceName is the historical name of the generated business-logic REST contract interface. It is the default for the generate --rest-interface-name flag; using it reproduces byte-identical output for every ordinary (non-self-host) generation. Only the repo-root self-host mirror overrides it (to "GeneratedServerInterface") so the business interface does not collide with the oapi-codegen ServerInterface that already lives in the root api/ package. DefaultRESTInterfaceName is the identifier used for the generated business-logic REST contract interface unless a caller overrides it.
const MaxFragmentFileBytes = 4 << 20
MaxFragmentFileBytes caps source bytes read from a referenced fragment.
const MaxSchemaRefLen = 512
MaxSchemaRefLen caps one schema reference before parsing or reporting it.
const RequiredGoVersion = "1.27.1"
RequiredGoVersion is the Go floor for generated code.
Every emitted server, client, and runtime package imports encoding/json/v2, which ships only in Go 1.27+ (before that it needed GOEXPERIMENT=jsonv2).
The `go` directive is also the canonical minimum toolchain request under GOTOOLCHAIN=auto. A same-version `toolchain` directive is redundant and makes an otherwise dependency-free module require normalization before a readonly build, so the floor writer deliberately does not add one.
const RequiredRustVersion = "1.88"
RequiredRustVersion is the Rust floor declared by the generated crate, in both `rust-version` (Cargo.toml) and `channel` (rust-toolchain.toml).
It is the maximum `rust-version` across the crate's resolved dependency graph — determined from `cargo metadata` over the pinned dependency set, not chosen — so a consumer below it cannot build the emitted crate at all. Raising a pinned dependency in client_rust_cargo.toml.tmpl may raise this; re-derive it rather than guessing.
const RequiredToolchain = "go" + RequiredGoVersion
RequiredToolchain is the toolchain name equivalent of RequiredGoVersion. Existing strictly newer toolchain hints remain meaningful and are preserved; an equal or older hint is redundant once the go directive reaches the floor.
const RequiredZigVersion = "0.16.0"
RequiredZigVersion is the Zig floor declared by the generated client, in both `minimum_zig_version` (build.zig.zon) and the emitted README. The client uses std.Io.net and std.Io.Writer.Allocating, which were introduced or restructured in 0.16.
const SpecHashHeaderName = "X-Apic-Spec-Hash"
SpecHashHeaderName is the HTTP response header the generated server uses to advertise the spec hash of the config it was generated from (GAP-0121 #259). A client generated from the same config carries the identical value in its own APISpecHash / API_SPEC_HASH / api_spec_hash constant, so a mismatch is a reliable signal that client and server were built from different revisions.
const SpecHashSpecField = "x-apic-spec-hash"
SpecHashSpecField is the member name carrying the spec hash in the public /api-spec.json document. The "x-" prefix follows the OpenAPI specification extension convention so the field cannot collide with a config member.
const TLSCertReloadCheckName = "tls_cert_reload"
TLSCertReloadCheckName is the health.checks name that opts a generated server into the built-in TLS certificate-reload check (SEC-0082, #382). It is the SINGLE source of that string: prepareServerData matches the config against it and the template interpolates it into the emitted const, so the generator, the emitted code and the tests cannot drift (QG-139, #398).
Variables ¶
var CommonInitialisms = map[string]bool{ "ABAC": true, "ACK": true, "API": true, "ASCII": true, "CPU": true, "CSS": true, "CSV": true, "DB": true, "DNS": true, "EOF": true, "GUID": true, "HMAC": true, "HTML": true, "HTTP": true, "HTTPS": true, "ID": true, "IO": true, "IP": true, "JSON": true, "JWT": true, "LHS": true, "MCP": true, "MD5": true, "OAUTH": true, "OIDC": true, "OS": true, "PDF": true, "QPS": true, "RAM": true, "RBAC": true, "RHS": true, "RPC": true, "SHA1": true, "SHA256": true, "SLA": true, "SMTP": true, "SQL": true, "SSE": true, "SSH": true, "TCP": true, "TLS": true, "TTL": true, "UDP": true, "UI": true, "UID": true, "URI": true, "URL": true, "URN": true, "UTF8": true, "UUID": true, "VM": true, "WS": true, "WSS": true, "XML": true, "XSRF": true, "XSS": true, "YAML": true, }
CommonInitialisms is the set of identifier segments that must render in fully-uppercase form when emitted as Go identifiers, mirroring the well-known list in https://github.com/golang/lint/blob/master/lint.go (the source for `revive`'s var-naming rule). The map is exported so downstream tests can pin the membership; callers must treat it as read-only (Go does not enforce immutability for map literals).
The keys are the *normalized* uppercase form of the initialism. Lookup is performed against strings.ToUpper(segment) so any case-mix in the source path (e.g. "Id", "id", "ID", "iD") collapses to the canonical uppercase rendering. Extending this list is a public-API change for any downstream service that relies on the current rendering of a previously-unrecognized segment; treat additions accordingly.
The list mirrors revive's default plus a handful of project-specific additions (MCP, WS, WSS, JWT, OIDC, RBAC, ABAC) that recur in apic-consumer configs. Closes GAP-0077.
var ErrInvalidConfig = errors.New("apic: invalid config")
ErrInvalidConfig identifies errors caused by invalid generator configuration.
var ( // ErrSchemaRef identifies malformed cross-file schema references. ErrSchemaRef = errors.New("invalid schema $ref") )
var ErrUnrepresentableSchema = errors.New("apic: unrepresentable schema construct")
ErrUnrepresentableSchema identifies schema constructs that the Go type emitter cannot represent. Generation fails rather than dropping a field.
Functions ¶
func DecodeSchemaFragment ¶ added in v0.19.0
DecodeSchemaFragment strictly decodes the schemas block from normalized JSON.
func DetectModuleForOutput ¶ added in v0.19.0
DetectModuleForOutput preserves the local CLI rule: an absolute output first selects its enclosing module, then falls back to the invocation directory.
func DetectModulePath ¶
DetectModulePath reads the nearest go.mod using ParseLax so newer directives remain readable while malformed or unreadable declarations fail closed.
func EmitOpenAPIArtifacts ¶ added in v0.19.0
func EmitOpenAPIArtifacts(ctx context.Context, outDir string, clean bool, artifacts []OpenAPIArtifact) (err error)
EmitOpenAPIArtifacts emits already-rendered raw files through one captured local output capability, without an APIC config or module discovery/update.
func EmitRuntime ¶
EmitRuntime writes the embedded apic runtime (pkg/ tree) to outDir with imports rewritten under module, producing a shared library that any generation can reference via "shared": {"runtime": "<module>[/<out>]"}. ENG-1424 shared-runtime dedup: one runtime copy for N services instead of one vendored copy per generation.
func EnsureGoModFloor ¶
EnsureGoModFloor raises the `go` directive of the module that will compile the generated tree to at least RequiredGoVersion, which makes GOTOOLCHAIN=auto select a compiler new enough for encoding/json/v2. It drops an equal or stale toolchain hint while preserving a meaningful newer hint.
startDir is the generator's output directory. Generated output is deliberately not a standalone module (Generate removes any stale go.mod it finds), so the search walks up from startDir to the nearest enclosing go.mod and edits that file in place.
It never downgrades: a meaningful newer toolchain hint is preserved. A module already at or above the floor is byte-for-byte untouched when no equal/stale hint needs canonicalization. A tree with no enclosing go.mod is reported as (false, "", nil) — that is the legitimate "generate first, go mod init later" flow, not an error. A go.mod whose `go` directive cannot be parsed is an error: the floor is a correctness guarantee, so an uninterpretable version is failed closed rather than silently overwritten.
The returned detail is a space-separated summary of what moved, suitable for logging, e.g. `go=1.24->1.27.1`.
func GenerateWithOptions ¶
func GenerateWithOptions(ctx context.Context, cfg []byte, outDir, module string, withTests, withFuzz bool) error
GenerateWithOptions generates cfg into outDir using module and the requested test and fuzz outputs.
func GenerateWithRESTInterface ¶
func GenerateWithRESTInterface(ctx context.Context, cfg []byte, outDir, module string, withTests, withFuzz bool, restInterfaceName string) error
GenerateWithRESTInterface validates and prepares the config and output capability before crossing the filesystem mutation boundary.
func IsCompositeExpr ¶
IsCompositeExpr reports whether s uses any boolean operator or grouping.
func NestedRefsOf ¶ added in v0.19.0
func NestedRefsOf(prop SchemaProp) []string
NestedRefsOf returns schema names referenced at structural property sites.
func NewMCPServer ¶
func NewMCPServer() *mcpServer
NewMCPServer returns the config-manipulation tool implementation backing the `apic mcp` subcommand. The concrete type stays unexported so the tool set is only reachable through the generated MCP tool interface.
func ParseAuthExpr ¶
ParseAuthExpr parses a boolean auth expression (`&&` binds tighter than `||`, with parentheses) into disjunctive normal form: a slice of deduped AND-groups OR'd together. A bare single token returns [][]string{{token}}.
func ParseSchemaRef ¶ added in v0.19.0
ParseSchemaRef splits an exact "<relative-path>#/schemas/<Name>" reference. Parent components are syntax, not authority; filesystem adapters separately decide whether the normalized target remains inside their owned root.
func ReadConfigWithSchemaRoot ¶ added in v0.19.0
ReadConfigWithSchemaRoot reads and resolves one local APIC config through a single directory capability. An empty schemaRoot selects the config's own directory. A nonempty root is explicit wider fragment authority; it never grants output or module-write authority.
func ReadSchemaFragment ¶ added in v0.19.0
ReadSchemaFragment reads and normalizes one bounded JSON or YAML fragment.
func ResolveSchemaRefs ¶
ResolveSchemaRefs resolves imports with the config directory as the exact default fragment root. A parent reference that leaves that directory fails.
func ResolveSchemaRefsInRoot ¶
ResolveSchemaRefsInRoot resolves imports through one explicit directory capability. The root is opened lazily, so no-ref input is returned byte-for- byte without requiring the base or root to exist.
func ResolveSchemaRefsWithLoader ¶ added in v0.19.0
func ResolveSchemaRefsWithLoader(cfg []byte, baseDir string, load FragmentLoader) ([]byte, error)
ResolveSchemaRefsWithLoader resolves references through load. Configurations without imports pass through byte-identically without invoking or requiring the loader. Equivalent normalized logical paths are loaded once per call.
func SpecHashOfJSON ¶
SpecHashOfJSON returns the lowercase hex SHA-256 of the canonical encoding of raw. Two configs that differ only in member ordering or whitespace hash identically; any change to a schema, route, or other config value changes the hash.
Types ¶
type APIData ¶
type APIData struct {
Paths PackagePaths
// RoleHierarchyLit is the Go expression the generated package assigns to
// its own immutable `_roleHierarchy` variable: either
// securex.MustRoleHierarchy([]string{...}) built from security.roles, or
// securex.DefaultRoleHierarchy() when the config declares none.
//
// ENG-4634: this used to be an emitted init() calling the process-wide
// securex.SetRoleHierarchy, which meant two generated servers in one
// binary could not hold different role vocabularies -- the last init to
// run re-ranked everyone's roles. The vocabulary is now owned by the
// generated package that enforces it.
RoleHierarchyLit string
// RestInterfaceName is the Go identifier emitted for the generated
// business-logic REST contract (the `type <name> interface` declaration,
// the RegisterGeneratedAPI srv param, and NewCatchAllServer's return type).
// Defaults to "ServerInterface" (DefaultRESTInterfaceName) so ordinary
// generation is byte-identical to historical output. The self-host mirror
// at the repo root sets it to "GeneratedServerInterface" via the
// generate --rest-interface-name flag to avoid colliding with the
// oapi-codegen-emitted ServerInterface that lives in the same `api`
// package (GAP-0087/QG-061/QG-062/PERF-0050/SEC-0048).
RestInterfaceName string
Routes []APIRoute
// ResponseBufferLimit is the effective server.response_buffer_bytes
// value (configx.ServerConfig.EffectiveResponseBufferBytes -- 0 resolves
// to configx.DefaultResponseBufferBytes, 64 KiB) baked into the emitted
// _responseBufferLimit constant that gates writeJSONResponse's pooled
// Content-Length-vs-chunked framing decision. PERF-0074 (#247).
ResponseBufferLimit int64
NeedsStrings bool // true when multipart isAllowedType needs strings.*
NeedsIO bool // unused after json/v2 migration; kept for struct compat
NeedsMultipart bool // true when any route uses multipart body mode
// NeedsDefaultJSONMaxBody is true when at least one JSON-parsing route
// resolves no positive body limit (route maxBodyBytes, else
// server.limits.max_body_bytes) and therefore caps its body with the
// emitted _defaultJSONMaxBody constant (CWE-770, SEC-0051 follow-up).
NeedsDefaultJSONMaxBody bool
// NeedsStrconv is true when at least one route binds a non-string
// scalar query field (A-Q2) and therefore uses strconv.ParseInt/Bool/
// ParseFloat in the per-route handler.
NeedsStrconv bool
// NeedsMath is true when at least one route binds a float64 typed path
// param or query scalar field (Kind == "float64") and therefore emits
// a math.IsNaN/math.IsInf guard right after strconv.ParseFloat (R4-3):
// ParseFloat accepts "NaN"/"nan" and returns (NaN, nil) -- it is a
// valid float64 literal, not a parse error -- and every IEEE-754
// comparison against NaN is false, so an unguarded NaN would silently
// satisfy (i.e. bypass) any minimum/maximum bound the schema declares.
// Scoped separately from NeedsStrconv so a config with only
// int64/bool typed params/fields does not pay for an unused "math"
// import.
NeedsMath bool
HasOIDCContracts bool
// HasAnyJWT is true when at least one HTTP route in the rendered
// template declares auth: "jwt". The generator emits a boot-time
// guard in RegisterGeneratedAPI that panics with
// securex.ErrAuthVerifierRequired if opts.AuthJWT is nil in that
// case, so a forgetful consumer cannot ship a binary whose JWT
// routes silently 401 every request. GAP-0076.
HasAnyJWT bool
// HasAnyAPIKey mirrors HasAnyJWT for auth: "api_key" routes.
// GAP-0076.
HasAnyAPIKey bool
// HasAnyWebhook is true when at least one route declares
// auth="webhook". RegisterGeneratedAPI panics at boot if
// opts.AuthWebhook is nil under this flag. GEN-WEBHOOK.
HasAnyWebhook bool
// HasAnyMTLS is true when at least one route declares auth="mtls"
// (regardless of supported_issuers). RegisterGeneratedAPI panics at
// boot with securex.ErrAuthMTLSRequired if opts.AuthMTLS is nil under
// this flag: securex.VerifyMTLS never enforces issuer/identity policy,
// so a nil hook fails OPEN. Mirrors HasAnyJWT/HasAnyWebhook.
HasAnyMTLS bool
// HasAnyMTLSRuntime is true when at least one route's mtls block
// declares crl/ocsp/cac_piv/principal_mapping (L-51). RegisterGeneratedAPI
// uses this to fail closed on a nil opts.MTLSRuntimes instead of
// silently treating a forgotten wiring the same as "nothing configured".
HasAnyMTLSRuntime bool
// HasAnyComposite is true when at least one route declares a composite
// auth expression (e.g. "mtls && jwt"). The generated handler emits
// securex.EvalComposite for such routes; a later phase uses this flag to
// gate the composite-evaluation imports/helpers.
HasAnyComposite bool
// HasAnyCookie is true when at least one HTTP route resolves to
// auth: "cookie". Cookie routes lift the session JWT from the
// HttpOnly cookie into the Authorization header and reuse
// opts.AuthJWT, so the boot guard for AuthJWT also fires for cookie.
HasAnyCookie bool
// CookieName is the session cookie name for auth:"cookie" routes;
// from security.auth.cookie_name, default "session".
CookieName string
// CSRF fields (F2). Populated from security.csrf when CSRFEnabled.
CSRFEnabled bool // security.csrf.enabled
CSRFEndpointPath string // e.g. "/csrf"
CSRFCookieName string // CSRF token cookie, e.g. "csrf_token"
CSRFHeaderName string // e.g. "X-CSRF-Token"
CSRFSecureCookies bool // Secure attr on the CSRF cookie
CSRFSameSiteConst string // rendered Go const e.g. "http.SameSiteStrictMode"
// HasAnyCSRFEnforced is true when at least one route sets CSRFEnforced.
// Gates the "crypto/subtle" import (used only in the enforce block).
HasAnyCSRFEnforced bool
// HasAnyOwnership is true when at least one route declares an
// object-level ownership gate (SEC-0027 / OWASP API1:2023 BOLA). Like
// HasAnyCSRFEnforced it gates the "crypto/subtle" import, since the
// ownership comparison is constant-time.
HasAnyOwnership bool
// CSRFMintRateLimit bounds requests/minute to the GET <endpoint_path>
// issuance route, via the same securex.NewBucket helper the per-route
// rateLimit buckets use (SEC-0060/0061). From
// security.csrf.mint_rate_limit; Normalize() defaults it to 60.
CSRFMintRateLimit int
// WebAuthn fields (Task 4.1, GENWA-C4/W1). When HasAnyWebauthn is
// true the emitted api/handlers_gen.go declares
// APIOptions.WebAuthnCredentialStore + APIOptions.WebAuthnSessionStore
// and constructs a package-level *webauthnx.Server from these
// flattened global config values + the AAGUID allow-list. Empty
// values flow through to webauthnx.Config which applies its own
// defaults; the AAGUID list is decoded at runtime via
// webauthnx.HexDecodeAAGUID so the generated code stays string-
// shaped.
HasAnyWebauthn bool
// HasAnyWebauthnOptions is true when any webauthn route's response
// schema declares an "options" property — the only place the
// generated handlers reference jsontext.Value. Gating the
// "encoding/json/jsontext" import on this flag (instead of
// HasAnyWebauthn) keeps minimal hardened configs that drop options
// from the resp shape compiling. WA-FINAL.
HasAnyWebauthnOptions bool
WebauthnRPID string
WebauthnRPDisplayName string
WebauthnOrigins []string
WebauthnAttestationPreference string
WebauthnUserVerification string
WebauthnRequireResidentKey bool
WebauthnAAGUIDAllowList []string
// SafeInjectedPathParamKeysBySchema (R2-10) is the Safe-set accumulation
// extendTypedPathParamsForInjectedFields computed while building Routes
// above, keyed by requestSchema name. Not used by api.go.tmpl itself
// (Routes[i].TypedPathParams already carries the per-route result) --
// exposed here purely so prepareMCPData (templates_mcp.go) can reuse the
// EXACT same computation for the G6 MCP dispatch binding without a
// second, differently-timed call to prepareAPIData: calling
// safeInjectedPathParamKeysBySchema again on the (already-extended)
// Routes would silently see every injected token as "already bound" via
// hasTypedPathParamToken and compute an empty Safe set. Capturing the
// return value from the ONE call inside this function sidesteps that
// footgun entirely.
SafeInjectedPathParamKeysBySchema map[string]map[string]bool
}
APIData is the top-level data for api.go.tmpl.
func (APIData) RESTIfaceName ¶
RESTIfaceName returns the business-logic REST interface name api.go.tmpl emits, defaulting to DefaultRESTInterfaceName ("ServerInterface") when RestInterfaceName is empty. The template calls this method (not the raw field) so a hand-built APIData literal — or any caller that omits the field — always emits a valid, non-empty Go type identifier.
type APIOperation ¶
type APIOperation struct {
Method string `json:"method"`
Path string `json:"path"`
Name string `json:"name"`
RequestSchema string `json:"requestSchema"`
ResponseSchema string `json:"responseSchema"`
Auth string `json:"auth"`
// Description is an optional human-readable description of the operation.
// MCP tools that mirror this operation (and declare no description of
// their own) inherit it as the tools/list descriptor description.
Description string `json:"description,omitempty"`
// ResponseContentType overrides the 200-response media type advertised
// in the generated OpenAPI document for this route. Empty defaults to
// "application/json". Set to e.g. "application/octet-stream" for routes
// that stream binary downloads (PDF/XLSX/ZIP) so the published spec and
// generated SDK clients do not mistype the response as JSON. When set to
// a non-JSON type the emitter advertises a binary string schema
// ({type:string, format:binary}) rather than a $ref. Closes
// GENERATOR_BUGS.md L-9.
ResponseContentType string `json:"responseContentType,omitempty"`
// JWTAlg is the JWS algorithm to use when Auth == "jwt". Recognized
// values: "HS256" (legacy), "RS256", "ES256", "PS256". Empty defaults
// to the legacy HS256 path. When SecurityConfig.FIPS is true the
// validator rejects HS256 because the FIPS-validated JWT signing path
// must use a hardware-backed RSA/ECDSA key (Plan 04 HSM).
JWTAlg string `json:"jwt_alg,omitempty"`
RateLimit int `json:"rateLimit"`
MaxBodyBytes *int `json:"maxBodyBytes"`
FormEncoded bool `json:"form_encoded"`
RequiredRoles []string `json:"required_roles"`
RequiredScopes []string `json:"required_scopes"`
RequiredAttributes map[string]string `json:"required_attributes"`
WildcardParams []string `json:"wildcard_params"`
// Streaming opts the endpoint into the streaming-response handler
// contract. When true the generated handler hands the
// http.ResponseWriter to the user handler with signature
// func(http.ResponseWriter, *http.Request, *ReqType) error -- the
// user handler owns the entire 200 response body (status line,
// Content-Type, body, flushing). The generated wrapper still runs
// every pre-handler concern (auth/role/scope checks, rate limiting,
// request parsing/validation, body limits) and emits the standard
// JSON error envelope for any failure that occurs *before* the user
// handler is called. See internal/generator/templates/api.go.tmpl for the
// full contract.
Streaming bool `json:"streaming,omitempty"`
// CSRF overrides the inherited CSRF policy for this route: "required"
// forces enforcement, "disabled" opts out. Empty inherits (auto-enforced
// when the resolved auth mode is "cookie" and security.csrf.enabled).
CSRF string `json:"csrf,omitempty"`
// Ownership declares object-level authorization (BOLA / OWASP
// API1:2023, SEC-0027) for this route: the authenticated caller's
// verifier-attested subject must equal the routed path parameter named
// by Ownership.Param. Nil (the default) leaves the route ungated by
// object-level authorization -- role/scope/attribute checks, when
// declared, still apply. See docs/CONFIGURATION.md "Object-level
// ownership (BOLA)".
Ownership *OwnershipConfig `json:"ownership,omitempty"`
SecurityContract
OIDC *OIDCContract `json:"oidc,omitempty"`
Webauthn *WebauthnContract `json:"webauthn,omitempty"`
MTLS *MTLSContract `json:"mtls,omitempty"`
Webhook *WebhookContract `json:"webhook,omitempty"`
}
APIOperation models one HTTP endpoint.
type APIRoute ¶
type APIRoute struct {
Method string
Path string
MethodName string
BktName string // bucket variable name
ReqType string
RespType string
ReqSchema string
RespSchema string
Auth string // "", "api_key", "jwt", "cookie", "mtls", "webhook"
// AuthDNF is the disjunctive-normal-form of a composite auth expression
// (OR of AND-groups). Empty for single-mode routes (use Auth). When set,
// IsComposite is true and the handler emits securex.EvalComposite.
AuthDNF [][]string
IsComposite bool
// CompositeNeedsMTLSPolicy is true when a composite route's DNF references
// any cert-based mode (mtls/cac/piv), so the handler emits the per-route
// _mtlsPolicy literal that securex.GateMTLS (used for mtls/cac/piv) consumes.
CompositeNeedsMTLSPolicy bool
// CSRFEnforced is true when the per-route handler must emit the CSRF
// double-submit capture/enforce blocks on this route's unsafe methods
// (F2). Set in prepareAPIData from data.CSRFEnabled, the route's
// resolved auth, and the route's csrf override.
CSRFEnforced bool
Surface string
Sensitivity string
Profile string
RateLimit int
RateLit string // either a literal int or "opts.GlobalRate"
MaxBody int
// MaxBodyLit is the Go expression emitted as the http.MaxBytesReader cap
// for this route: the explicit positive limit (route maxBodyBytes, else
// server.limits.max_body_bytes) as a numeric literal, or the
// _defaultJSONMaxBody fallback constant for a JSON-parsing route with no
// configured limit (CWE-770, SEC-0051 follow-up). Empty means no body cap
// is emitted (query-mode routes with no configured limit — they never
// parse a request body).
MaxBodyLit string
// Per-route mTLS contract values, populated when Auth == "mtls". The
// generator emits these into a securex.MTLSPolicy literal that the
// runtime VerifyMTLS gate consumes.
MTLSRequired bool
MTLSEKUValidation bool
MTLSSupportedIssuersLit string // pre-rendered Go literal e.g. `[]string{"piv","cac"}`
// APPSEC-15: per-route ca_bundle_path emitted into RouteInfo so
// catch-all handlers can wire their own mtlsx.TrustStore. Empty
// when the operation declares no MTLS block.
MTLSCABundlePath string
// MTLSRuntimeKeyLit is the quoted "METHOD /path" key (matching
// mtlsRuntimeKey / MTLSRuntimeRoute.Key) the mtls/composite auth
// branches use to look up this route's boot-constructed
// securex.MTLSRuntime from opts.MTLSRuntimes (L-51). Populated for
// every route: a missing map key returns the zero MTLSRuntime{}, which
// VerifyMTLS treats as "no CRL/OCSP/CAC-PIV enforcement", identical to
// pre-L-51 behavior.
MTLSRuntimeKeyLit string
// NeedsMTLSRuntime is true when this route's mtls block declares
// crl/ocsp/cac_piv/principal_mapping, i.e. the boot wiring constructs a
// securex.MTLSRuntime under MTLSRuntimeKeyLit. RegisterGeneratedAPI
// fails closed when opts.MTLSRuntimes is present but has no entry for
// such a route (S1, #337): a missing key would otherwise yield the zero
// runtime and silently skip the enforcement the route declares.
NeedsMTLSRuntime bool
// Streaming flips the generated handler to the streaming-response
// contract: the user handler signature becomes
// func(http.ResponseWriter, *http.Request, *ReqType) error and the
// wrapper does NOT marshal a response value -- the handler owns the
// 200 body. Pre-handler concerns (auth/authz, rate limiting, request
// parsing/validation) are unchanged.
Streaming bool
// ResponseContentType is the route's declared 200 media type
// (APIOperation.ResponseContentType). Empty defaults to application/json.
// Non-JSON values drive the client to decode blob()/arrayBuffer() instead
// of JSON. L-32b.
ResponseContentType string
// Body parsing mode
BodyMode string // "query", "form", "json"
HasTyped bool // true when request uses typed structs
HasTypedResp bool // true when response uses typed structs
// For query mode: sorted list of string-typed property names from schema.
// Non-string scalar fields (integer/number/boolean) are tracked
// separately on QueryScalarFields so the handler template can emit the
// correct strconv.Parse* with a 400 on parse error. Closes A-Q2 from
// GENERATOR_BUGS.md — the pre-A-Q2 generator filtered query binding to
// string-only, silently dropping fields like OIDCAuthorizeRequest.MaxAge.
QueryFields []string
QueryScalarFields []QueryScalarField
// For form mode: sorted list of string-typed property names
FormFields []string
// For form and multipart modes: non-string scalar properties
// (integer/number/boolean) plus nullable strings, each parsed with the
// matching strconv.Parse* and 400'd on a malformed value. SONNY-801 --
// before this the two modes bound string properties only, so every
// other scalar silently arrived as its zero value.
FormScalarFields []FormScalarField
// For multipart mode: file upload fields with validation metadata
FileFields []FileField
HasAuthzPolicy bool
RequiredRolesLit string
RequiredScopesLit string
RequiredAttrsLit string
// APPSEC-Gen-F-006: pre-rendered Go literal for the per-route
// WebAuthn ceremony policy (RouteInfo.Webauthn). "nil" when the
// operation declares no webauthn block; otherwise an
// `&api.WebauthnInfo{...}` literal mirroring the WebauthnContract.
WebauthnLit string
// OwnershipParam is the path parameter carrying the resource-owner id
// for this route's object-level ownership gate (SEC-0027 / OWASP
// API1:2023 BOLA), from APIOperation.Ownership.Param. Empty when the
// route declares no ownership block, which suppresses the gate entirely.
OwnershipParam string
PathParams []string // path parameter names extracted from {name} segments
PathParamsLit string // pre-rendered Go literal for path params
TypedPathParams []TypedPathParam // path params matched to request schema fields (token + schema key)
MuxPattern string // Go 1.22 mux pattern e.g. "GET /users/{id}"
ExposeClient bool
ExposeReact bool
HasOIDCAuthorizeValidation bool
HasOIDCTokenValidation bool
HasOIDCUserinfoClaims bool
OIDCInteractiveAuth string
OIDCAuthCodeSchema string
OIDCRefreshSchema string
OIDCPasswordSchema string
OIDCPasswordGrantEnabled bool
OIDCClientAuth string
OIDCRefreshMode string
OIDCRotationRequired bool
OIDCRequireStore bool
OIDCRequireRevocationStore bool
OIDCUnsafeStatelessJWT bool
OIDCScopeClaimsLit string
// Webhook fields (Task 8). The generator renders WebhookPolicyLit
// directly into the per-route handler when HasWebhook is true;
// WebhookSecretRef names the SecurityConfig.Webhooks entry the
// runtime resolves at boot.
HasWebhook bool
WebhookSecretRef string
WebhookPolicyLit string
// WebAuthn fields (Task 3.1, GENWA-C1). Per-endpoint
// WebauthnContract values are flattened so the template can
// render them into the per-route handler when emission lands
// in Phase 4 (Task 4.2). Empty/zero values mean "use the
// global security.webauthn default".
HasWebauthn bool
WebauthnCeremony string // "register" or "authenticate"
WebauthnPhase string // "begin" or "complete"
WebauthnAttestation string // "" | "none" | "direct" | "enterprise"
WebauthnAuthenticatorAttachment string // "" | "platform" | "cross-platform"
WebauthnUserVerification string // "" | "required" | "preferred" | "discouraged"
WebauthnDiscoverable bool
// WebauthnReqHasDisplayName is true when the resolved request
// schema for a webauthn_register_begin route exposes a
// "display_name" property. The default profile schema declares it
// (security_contracts.go), but user-defined request schemas (see
// configs/fedramp-baseline.json + configs/webauthn-passkey.json)
// can omit it. api.go.tmpl gates the req.DisplayName read on this
// flag so generated code compiles for either shape. WA-FINAL.
WebauthnReqHasDisplayName bool
// WebauthnReqHasUserID mirrors the above for the "user_id" field on
// webauthn_register_begin AND webauthn_authenticate_begin request
// schemas. The same user-defined-schema flexibility motivates
// gating; both the default profile schemas and every shipped
// config carry the field today, but emitting unconditional access
// would crash for a future minimal config that drops it.
WebauthnReqHasUserID bool
// WA-FINAL: the ceremony handler bodies in api.go.tmpl read
// req.SessionID + req.Credential on the *complete* phase requests
// and write resp.SessionID + resp.Options on the *begin* phase
// responses and resp.CredentialID on register-complete. Real
// configs (configs/webauthn-passkey-hardened.json) ship a minimal
// register-complete-resp shape ({received: bool}) instead, so the
// template must gate every field reference on the resolved
// schema's property set; otherwise the emitted code will not
// compile against the consumer's typed struct.
WebauthnReqHasSessionID bool
WebauthnReqHasCredential bool
WebauthnRespHasSessionID bool
WebauthnRespHasOptions bool
WebauthnRespHasCredentialID bool
}
APIRoute represents one HTTP route in the generated API layer.
type AuthWiring ¶
type AuthWiring struct {
HasAnyJWT bool
HasAnyAPIKey bool
HasAnyMTLS bool
HasAnyMTLSRuntime bool
// MTLSRuntimeKeyLits lists the quoted "METHOD /path" keys of every
// HTTP-exposed route that needs a boot-constructed securex.MTLSRuntime,
// in config order. RegisterGeneratedAPI fails closed on a map that is
// present but missing one of these keys (S1, #337), so the harnesses
// emit a map literal carrying every key with a zero (no-enforcement)
// runtime rather than an empty map.
MTLSRuntimeKeyLits []string
HasAnyCookie bool
HasWebauthn bool
// WebauthnNeedsAttestationRoots is true when a webauthn_* route exists AND
// security.webauthn.aaguid_allow_list is non-empty: webauthnx.NewServer
// then refuses a nil AttestationRoots pool (R10-2), so the harnesses must
// supply one. They emit an EMPTY x509.NewCertPool() -- present, so the
// presence check passes; empty, so it verifies nothing -- which is the
// deliberate permissive-harness opt-out (QG-113, #339). Production never
// does this: server_lib.go.tmpl loads attestation_roots_path for real.
WebauthnNeedsAttestationRoots bool
HasAnyWebhook bool
CSRFEnabled bool
CookieName string
// Conditional import lines (tab-prefixed, quoted) emitted only when the
// corresponding wiring is referenced. Empty when paths.Module == "".
TypesImport string
SecurexImport string
WebauthnxImport string
// CsrfxImport is the csrfx import line the test/fuzz harnesses need to
// build the permissive CSRF signer when security.csrf is enabled.
CsrfxImport string
}
AuthWiring carries the booleans + cookie name + conditional import lines that the generated test and fuzz harnesses need to wire APIOptions defensively so RegisterGeneratedAPI's bootstrap-fail guard (GAP-0076) does not panic at boot. It is computed once by scanAuthWiring and consumed by both prepareTestsData and prepareFuzzData so the two emitters never drift.
type AuthzContract ¶
type AuthzContract struct {
Roles []string `json:"roles,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Attributes map[string]string `json:"attributes,omitempty"`
}
AuthzContract mirrors the legacy route-level policy fields but keeps them in the new security contract so policy intent travels with the operation.
type CACPIVContract ¶
type CACPIVContract struct {
RequirePerson bool `json:"require_person,omitempty"`
RequiredPolicyOIDs []string `json:"required_policy_oids,omitempty"`
RejectUnknown bool `json:"reject_unknown_classification,omitempty"`
}
CACPIVContract is the per-operation CAC/PIV policy (Plan 03). Set on MTLSContract.CACPIV when an endpoint should reject non-PE or non-policy-compliant certificates.
type CRLContract ¶
type CRLContract struct {
Endpoints []string `json:"endpoints,omitempty"`
TTLMS int `json:"ttl_ms,omitempty"`
AllowSoftFail bool `json:"allow_soft_fail,omitempty"`
}
CRLContract configures CRL revocation checks for a route.
type ClientData ¶
type ClientData struct {
Paths PackagePaths
Schemas []ClientSchema
Routes []ClientRoute
WebSockets []ClientWebSocket
GraphQL *ClientGraphQLData
NeedsMultipart bool
NeedsWSJSON bool
NeedsWSTypes bool
NeedsUntypedRESTResp bool
CSRFEnabled bool
CSRFEndpointPath string
CSRFHeaderName string
CSRFCookieName string
// CorrelationHeader is the header name the generated Go client sends to
// carry an end-to-end correlation id on every REST, GraphQL, and
// WebSocket call. It mirrors the server's
// observability.tracing.correlation_header (default "X-Correlation-ID")
// and is emitted as the generated `defaultCorrelationHeader` constant.
CorrelationHeader string
APIPrefix string
// ReactQuery opts into emitting the @tanstack/react-query variant
// (gen/client/react-query/) in addition to the plain client. TSC-03/04/05/06/07.
ReactQuery bool
// ReactQueryData is the react-query-variant template view (key factory +
// per-route hook list). Populated only when ReactQuery is true.
ReactQueryData ReactQueryData
// Python/Rust/Zig opt into emitting the respective generated SDK client.
Python bool
Rust bool
Zig bool
// RustVersion / ZigVersion are the toolchain floors stamped into the
// emitted crates (Cargo.toml `rust-version` + rust-toolchain.toml
// `channel`; build.zig.zon `minimum_zig_version`). They mirror the go.mod
// `go`/`toolchain` pin the Go surface gets, so every generated artifact
// declares the compiler it actually needs. Sourced from
// RequiredRustVersion / RequiredZigVersion — never hardcoded in a template.
RustVersion string
ZigVersion string
// ZigNeedsWS is true when the Zig client must emit ws.zig.
// True when there are raw WebSocket endpoints OR GraphQL subscriptions
// (graphql.zig imports ws.zig for subscriptions). MCP uses HTTP POST, not WS.
ZigNeedsWS bool
// Fips is true when the server config has security.fips: true.
// When Fips && Zig, the generator emits the devnw/zig/fips dual-mode
// crypto seam files (crypto_backend.zig, std_shim.zig, policy.zig,
// fips_link.zig) into the client src/ and routes hmac.zig through the
// seam. Non-FIPS configs are byte-identical to today.
Fips bool
// MCPTools is the client-facing projection of all MCP-exposed tools, sorted
// by name. Language plans (MCP phase) iterate this slice.
MCPTools []ClientMCPTool
// MCPAuthMode is the auth mode string for the MCP WebSocket endpoint,
// derived from mcpAuthMode(c). Empty/"none" when no MCP tools are present.
// Used by the Python (and other) MCP client templates instead of hardcoded "public".
MCPAuthMode string
// MCPHasWebSocket and MCPHasHTTP gate emission of the transport-specific
// polyglot MCP client modules on mcp.transport (GAP-0113). The generated
// MCP clients are NOT transport-agnostic: the Python client rides the
// WebSocket transport (server_lib.go.tmpl mounts genmcp.HandleMCPWS at
// /mcp/ws only when "websocket" is in mcp.transport), while the Rust and
// Zig clients POST JSON-RPC over HTTP to /mcp (mounted only when "http"
// is present). Without this gate, a config that requests client.python
// but sets mcp.transport to, say, ["stdio"] would still emit a Python
// MCP client that opens a WebSocket handshake against an endpoint the
// server never mounts — broken at runtime. Both are false when there are
// no client-exposed MCP tools (MCPTools is empty), so a config with no
// MCP surface never trips either gate. Computed via healthMCPTransports
// (config_validate_health.go) — the SAME helper the server-mount
// collision validators use — so client gating always mirrors exactly
// what the generated server actually mounts.
MCPHasWebSocket bool
MCPHasHTTP bool
// SpecHash is the deterministic spec-identity hash of the resolved config
// this generation was produced from (GAP-0121 #259). Every client language
// embeds it verbatim -- Go `APISpecHash`, TypeScript/Python/Rust
// `API_SPEC_HASH`, Zig `api_spec_hash` -- and the generated server
// advertises the same value as the X-Apic-Spec-Hash response header, so a
// client can detect that it was built against a different spec revision
// than the server it is talking to.
SpecHash string
}
ClientData is the top-level data for generated Go and React clients.
type ClientGenConfig ¶
type ClientGenConfig struct {
// ReactQuery opts into emitting the @tanstack/react-query variant
// (gen/client/react-query/) IN ADDITION to the default dependency-free
// plain-hooks client. Default false keeps the default output dep-free.
// TSC-03/04/05/06/07/15.
ReactQuery bool `json:"react_query,omitempty"`
// ReactUI opts into emitting the dependency-free React service explorer
// (gen/client/react-ui/) IN ADDITION to the default React client. The
// explorer lists exactly the React-exposed surface. Default false.
ReactUI bool `json:"react_ui,omitempty"`
// Python opts into emitting the stdlib-only async Python client
// (gen/client/python/). Default false. See docs/superpowers/plans/2026-06-25-polyglot-clients-SPEC.md.
Python bool `json:"python,omitempty"`
// Rust opts into emitting the async (tokio/reqwest) Rust client
// (gen/client/rust/). Default false.
Rust bool `json:"rust,omitempty"`
// Zig opts into emitting the std-only synchronous Zig client
// (gen/client/zig/). Default false.
Zig bool `json:"zig,omitempty"`
}
ClientGenConfig holds generate-time-only knobs for the emitted SDK clients. It is a top-level config block ("client": {...}); because the generator decodes the config with json.RejectUnknownMembers(true), this must be a declared field on Config or generation fails on an unknown "client" key.
type ClientGraphQLData ¶
type ClientGraphQLData struct {
Path string
WSPath string
Queries []ClientGraphQLOperation
Mutations []ClientGraphQLOperation
Subscriptions []ClientGraphQLSubscription
NeedsTypes bool
// NeedsSubscriptionClient is true when at least one subscription has
// ExposeClient set, i.e. client_go_graphql.go.tmpl actually emits a
// GraphQLSubscribe<Field> method. That method is the sole user of the
// strconv/time imports (subscription-ID generation); a subscriptions-only
// config whose sole subscription's backing WS route sets
// exposure.client_sdk:false (e.g. configs/graphql-subscriptions-only.json)
// has .Subscriptions non-empty but emits no such method, so gating
// strconv/time on .Subscriptions alone would leave them unused
// (GQL-IMPORTS-CLIENT).
NeedsSubscriptionClient bool
}
ClientGraphQLData groups generated GraphQL client operations.
type ClientGraphQLOperation ¶
type ClientGraphQLOperation struct {
Field string
MethodName string
Auth string
Endpoint string // backing REST endpoint key, e.g. "POST /v1/foo"
ReqSchema string
RespSchema string
RespType string
TSReqType string
TSRespType string
HasRequest bool
HasResp bool
QueryDoc string
Args []GQLArg
ExposeClient bool
ExposeReact bool
}
ClientGraphQLOperation represents a query or mutation wrapper.
type ClientGraphQLSubscription ¶
type ClientGraphQLSubscription struct {
Field string
MethodName string
Auth string
MessageSchema string
MessageType string
TSMessageType string
QueryDoc string
HasMessage bool
ExposeClient bool
ExposeReact bool
}
ClientGraphQLSubscription represents a generated GraphQL subscription wrapper.
type ClientMCPTool ¶
type ClientMCPTool struct {
Name string
Title string
Description string
ParamsSchema string
ResultSchema string
MethodName string // snake_case method identifier, e.g. "send_message"
HasParams bool
HasResult bool
}
ClientMCPTool is a client-facing projection of one exposed MCP tool.
type ClientRoute ¶
type ClientRoute struct {
Route APIRoute
HasRequest bool
HasResp bool
GoReqType string
GoRespType string // Go client response type: []byte for RespIsBinary, else Route.RespType
TSPathParamsLit string // TS literal: array of [urlToken, schemaKey] pairs
GoPathParamsLit string // Go literal: [][2]string of {urlToken, schemaKey} pairs
ZigPathParamsLit string // Zig literal: array of [2][]const u8 pairs
PathParams [][2]string // ordered {urlToken, schemaKey} pairs (language-neutral)
TSReqType string
TSRespType string
ZigReqType string // Zig request type (schema struct or std.json.Value)
ZigRespType string // Zig response type (schema struct or std.json.Value)
ExposeClient bool
ExposeReact bool
ResponseKind string // "json" | "blob" | "arrayBuffer"
RespIsBinary bool // route declares a non-JSON response content-type
ResourcePrefix string // resource identity for post-mutation cache invalidation
}
ClientRoute wraps one generated REST route.
type ClientSchema ¶
type ClientSchema struct {
Name string
Fields []ClientSchemaField
HasFields bool // true when Fields is non-empty (Zig jsonStringify self-use gate)
FormatUsesSelf bool // true when >=1 field is non-secret (Zig format self-use gate)
// HasNestedSchemas is true when >=1 field declares a NestedSchema, gating
// this schema's entry in the TS client's schemaNestedSchemas table (L-59)
// so a schema with no nested fields contributes no empty object literal.
// ExtraFields never counts: an injected path param is always a plain
// string (see injectPathParamFields), so it has no nested schema.
HasNestedSchemas bool
// ExtraFields holds path-param fields (ENG-1640/F2) that a route needs at
// runtime but this schema's own Properties never declared. Populated by
// injectPathParamFields AFTER prepareClientSchemas, by cross-referencing
// every route whose ReqSchema is this schema's Name.
//
// Deliberately kept SEPARATE from Fields rather than appended to it:
// Fields is shared verbatim by client_python_models.py.tmpl,
// client_rust_models.rs.tmpl, and client_zig_models.zig.tmpl (types.go.tmpl
// is NOT in this list -- the Go server/client types are a wholly separate
// pipeline; see injectTypesPathParamFields), each of which reads per-field
// RustType/ZigType/ZigName that a naively-injected field wouldn't
// populate. injectPathParamFields DOES populate RustType (a path param is
// always a plain string) and WireName -- the two fields
// client_rust_models.rs.tmpl and client_python_models.py.tmpl actually
// need -- so client_react.ts.tmpl (and the react-query variant), the Rust
// models template, and the Python models template all range over
// ExtraFields in addition to Fields (F2). client_zig_models.zig.tmpl does
// NOT: the generated Zig struct is never the Zig REST call's parameter
// type (that's always std.json.Value; see zigReqTypeForRoute), so an
// omitted path-param field there is inert, not a defect.
ExtraFields []ClientSchemaField
}
ClientSchema is a TypeScript-facing schema mirror.
type ClientSchemaField ¶
type ClientSchemaField struct {
Name string
WireName string
ZigName string // safe Zig identifier (may differ from WireName for keywords/collisions)
TSType string
RustType string // idiomatic Rust type for this field (Option<...> when optional)
ZigType string // idiomatic Zig type for this field (optional-ness applied by template)
PyType string // idiomatic Python annotation for this field (| None applied by template)
// PyNestedKind/PyNestedModel describe how the Python client's from_wire
// rebuilds this field's value into a generated dataclass: "ref", "list" or
// "map" plus the model name, or both empty when the field declares no
// model. See pyNestedSpec (python_types.go).
PyNestedKind string
PyNestedModel string
// ZigIsMap is true when ZigType is a std.json.ArrayHashMap. The generated
// Zig format() method must print such a field's entry count rather than
// hand the hash map to `{any}`, which would reflect over its internals.
ZigIsMap bool
Optional bool
Secret bool // schema property marked "secret": true — redact in client formatting
// NestedSchema names the DECLARED schema this field's value resolves to
// (directly, or through any depth of array nesting), or "" when the field
// has no declared schema — a scalar, or free-form/additionalProperties
// object data. The TS client's camelCase<->wire converters key off this to
// descend into a nested schema with THAT schema's own wire-key map (L-59);
// an empty value is what keeps free-form nested DATA keys verbatim (G-08).
NestedSchema string
}
ClientSchemaField represents one generated TypeScript field.
type ClientWebSocket ¶
type ClientWebSocket struct {
Endpoint WSEndpoint
Proto string
Auth string
MessageSchema string
MessageType string
TSMessageType string
GoProtocolsLit string
ProtocolsLit string
PyProtocolsLit string
ZigProtocolsLit string
HasMessageType bool
ExposeClient bool
ExposeReact bool
}
ClientWebSocket wraps one generated raw WebSocket endpoint.
type Config ¶
type Config struct {
configx.RuntimeConfig
API []APIOperation `json:"api"`
Websocket []WebsocketEndpoint `json:"websocket"`
MCP MCPConfigSection `json:"mcp"`
Schemas map[string]SchemaDef `json:"schemas"`
GraphQL *GraphQLConfig `json:"graphql,omitempty"`
Client *ClientGenConfig `json:"client,omitempty"`
// contains filtered or unexported fields
}
Config models the input JSON config.
func (*Config) SharedRuntimePrefix ¶ added in v0.19.0
SharedRuntimePrefix returns the shared runtime import prefix, or "" when this generation vendors its own pkg/ copy (the default, historical mode).
func (*Config) SpecHash ¶
SpecHash returns the config's spec-identity hash, computing it on first use and caching the result. Generation computes (and error-checks) the hash once up front via ensureSpecHash; this accessor exists so every template-data assembler — including unit tests that build a Config by hand and call prepareClientData directly — sees the same value without threading it through half a dozen signatures.
It returns "" only when the config cannot be marshalled at all, which GenerateWithRESTInterface rejects before any file is written.
type ExposureConfig ¶
type ExposureConfig struct {
HTTP *bool `json:"http,omitempty"`
WS *bool `json:"ws,omitempty"`
MCP *bool `json:"mcp,omitempty"`
ClientSDK *bool `json:"client_sdk,omitempty"`
ReactSDK *bool `json:"react_sdk,omitempty"`
}
ExposureConfig controls which generated surfaces receive an operation.
type FileField ¶
type FileField struct {
Name string // JSON/form field name (e.g. "avatar")
GoName string // Go struct field name (e.g. "Avatar")
MaxSize int64 // max bytes per file (0 = use endpoint maxBody)
AllowedTypes []string // MIME type whitelist (nil = any)
AllowedTypesLit string // pre-rendered Go literal e.g. `[]string{"image/png", "image/jpeg"}`
MaxFiles int // max files (1 = single, >1 = multi)
Multi bool // true when MaxFiles > 1
Required bool // true when field name is in schema required[]
}
FileField describes one file upload field in a multipart endpoint.
type FormScalarField ¶ added in v0.19.3
type FormScalarField struct {
Name string // form field name (e.g. "chunk_index")
GoName string // Go struct field name (e.g. "ChunkIndex")
// Kind is one of "int64", "float64", "bool", or "string". It selects the
// strconv.Parse* the template emits; "string" (a nullable string
// property, whose Go field is *string) needs no parsing, only the
// nil-guarded address-of assignment -- the same carve-out
// QueryScalarField.Kind makes for GENERATOR_BUGS.md L-37.
Kind string
// Required is true when the schema lists this property in required[].
// It is honored only for the non-"string" kinds: a non-pointer int64/
// float64/bool field cannot distinguish "absent" from the zero value
// after binding, so presence has to be enforced at bind time. Required
// STRING form fields keep their existing contract (bound to "" and left
// to Valid()/the OIDC request validators, which answer with the
// protocol-specific error the spec mandates rather than a generic 400).
Required bool
// Nullable is true when the property is `"nullable": true`, i.e. the Go
// struct field is a pointer; the template then assigns the address of
// the parsed local instead of the value.
Nullable bool
// BodyMode is the owning route's body mode -- "form" or "multipart".
// It selects the BODY-ONLY accessor the template emits
// (r.PostFormValue vs _multipartFormValue), which differ because
// r.MultipartForm.Value is the only handle on the parsed multipart
// parts alone. It is carried per field rather than read from the route
// so the one shared bindFormScalarFields define serves both branches.
BodyMode string
}
FormScalarField describes one non-string scalar property bound from a form-encoded (application/x-www-form-urlencoded) or multipart/form-data request body: integer, number, boolean, or a nullable string. The generated handler parses it with the matching strconv.Parse* and answers 400 on a malformed value or an absent REQUIRED non-string field.
SONNY-801: the pre-fix generator bound ONLY `type: "string"` properties (FormFields -> req.X = r.FormValue("x")) plus `type: "file"` properties (FileFields). Every other scalar had no binding emitted at all, so a required `"chunkIndex": {"type":"integer","minimum":0,"maximum":8191}` reached the handler as 0 -- and Valid() passed, because 0 satisfies `minimum: 0`. The route was silently unusable (observed in the Sonny consumer's gen/sync/api/handlers_gen.go multipart upload handler). This is the form/multipart twin of QueryScalarField, which closed the same gap for query-bound routes (A-Q2).
type FragmentLoader ¶ added in v0.19.0
FragmentLoader loads schema definitions from a normalized logical path. The caller owns the path coordinate system and all filesystem authority.
type FuzzAuthRoute ¶
type FuzzAuthRoute struct {
FuncName string
Method string // http.Method<Verb> selector
RawMethod string // upper-cased HTTP verb, for the comment
Path string
// AuthDesc is the resolved auth expression (e.g. "mtls && jwt"), for the
// func-name comment only.
AuthDesc string
// Seeds are (authz, body) literal pairs fed to f.Add(authz, body). Each is
// already a Go string literal (strconv.Quote). Deterministic, deduplicated.
Seeds []FuzzAuthSeed
}
FuzzAuthRoute is one REST auth-enforcement fuzz target. It fuzzes two inputs — the Authorization header and the request body — against a server whose auth verifiers ALWAYS reject. The invariant is that no fuzzed input yields a 2xx (which would mean a path bypasses the generated auth gate) and none yields a 500 (a recovered panic/defect in the auth path). The func name carries an "_Auth" suffix so it never collides with the body-only FuzzRoute target on the same method/path.
type FuzzAuthSeed ¶
FuzzAuthSeed is one f.Add(authz, body) pair of pre-quoted Go string literals.
type FuzzData ¶
type FuzzData struct {
// AuthWiring carries the defensive APIOptions wiring booleans + cookie
// name + conditional import lines (shared with prepareTestsData via
// scanAuthWiring so the two emitters never drift).
AuthWiring
HasRoutes bool
Routes []FuzzRoute
// HasAuthRoutes is true when at least one HTTP route resolved to a
// non-public auth mode. It gates emission of newFuzzServerRejectingAuth,
// the errFuzzAuthReject sentinel, the "errors" import, and the AuthRoutes
// targets so a public-only config does not get an unused builder/import.
HasAuthRoutes bool
// AuthRoutes are the auth-enforcement (no-2xx-under-rejecting-verifier)
// fuzz targets, one per non-public HTTP route.
AuthRoutes []FuzzAuthRoute
}
FuzzData is the top-level data for tests_fuzz_api.go.tmpl.
type FuzzRoute ¶
type FuzzRoute struct {
FuncName string
// Method is the http.Method<Verb> selector (e.g. "MethodPost") so the
// template emits http.MethodPost rather than a bare string.
Method string
// RawMethod is the upper-cased HTTP verb (e.g. "POST"), used only for the
// func-name comment.
RawMethod string
Path string
// Seeds are Go string literals (already quoted via strconv.Quote) ready
// to drop into f.Add(<lit>). Deterministic, deduplicated.
Seeds []string
}
FuzzRoute is one REST fuzz target: a Go func name, the HTTP method in its http.Method<Verb> form, the concrete request path (path params materialized so the request matches the registered route pattern and reaches the handler), and the precomputed corpus of Go string literals fed to f.Add.
type GQLHandlersData ¶
type GQLHandlersData struct {
Paths PackagePaths
GQLPath string
AllowIntrospection bool
}
GQLHandlersData is the top-level data for gql_handlers.go.tmpl.
type GQLProxyData ¶
type GQLProxyData struct {
Paths PackagePaths
Queries []GQLProxyField
Mutations []GQLProxyField
HasCompositeMTLS bool // true when any field declares a composite mtls/cac/piv auth mode
// HasCompositeMTLSRuntime mirrors GQLResolversData.HasCompositeMTLSRuntime
// (SEC-0078): gates NewProxyResolvers' fail-closed guard on a missing
// WithProxyMTLSRuntimes map.
HasCompositeMTLSRuntime bool
// CompositeMTLSRuntimeKeyLits mirrors GQLResolversData.CompositeMTLSRuntimeKeyLits
// (S1, #337): NewProxyResolvers panics on a map missing any of these keys.
CompositeMTLSRuntimeKeyLits []string
// CookieName mirrors GQLResolversData.CookieName (GAP-0091): the configured
// session cookie name emitted as gqlCookieName for composite cookie gates.
CookieName string
}
GQLProxyData is the top-level data for gql_proxy.go.tmpl.
func (GQLProxyData) HasCompositeCookie ¶
func (d GQLProxyData) HasCompositeCookie() bool
HasCompositeCookie reports whether any proxy query or mutation declares a composite auth expression containing a "cookie" leaf.
type GQLProxyField ¶
type GQLProxyField struct {
Field string
Method string
Path string
TypeExpr string
// Auth + Required*Lit carry the backing REST endpoint's authorization
// policy so the proxy resolver enforces the SAME per-field gate as the
// in-process resolver (N-1, closes the proxy fail-open).
Auth string
RequiredRolesLit string
RequiredScopesLit string
RequiredAttrsLit string
// Composite auth fields — mirrors REST RouteData (templates_api.go).
IsComposite bool
AuthDNF [][]string
CompositeNeedsMTLSPolicy bool
MTLSRequired bool
MTLSEKUValidation bool
MTLSSupportedIssuersLit string
// MTLSRuntimeKeyLit / NeedsMTLSRuntime mirror GQLResolverField (SEC-0078).
MTLSRuntimeKeyLit string
NeedsMTLSRuntime bool
}
GQLProxyField represents a proxy query or mutation.
type GQLResolverField ¶
type GQLResolverField struct {
Field string
Endpoint string
TypeExpr string
MethodName string
ReqSchema string
RespSchema string
HasTyped bool
Auth string
HasAuthzPolicy bool
RequiredRolesLit string
RequiredScopesLit string
RequiredAttrsLit string
Args []GQLArg
// RateLimit / HasRateLimit carry the backing REST route's per-route
// rateLimit. When HasRateLimit is true the resolver template emits a
// package-scoped token bucket (rate=burst=RateLimit) and a fail-closed
// check after auth/RBAC and before dispatch, mirroring the REST per-route
// bucket the in-process call would otherwise bypass (SEC-0040).
RateLimit int
HasRateLimit bool
// Composite auth fields — mirrors REST RouteData (templates_api.go).
IsComposite bool
AuthDNF [][]string
CompositeNeedsMTLSPolicy bool
MTLSRequired bool
MTLSEKUValidation bool
MTLSSupportedIssuersLit string
// MTLSRuntimeKeyLit is the quoted "METHOD /path" key of the backing REST
// route (mtlsRuntimeKey, identical to APIRoute.MTLSRuntimeKeyLit) that the
// composite mtls/cac/piv gate uses to look up the boot-constructed
// securex.MTLSRuntime -- so a GraphQL field enforces the same CRL/OCSP/
// CAC-PIV/principal policy as the REST route it dispatches to (SEC-0078).
MTLSRuntimeKeyLit string
// NeedsMTLSRuntime is true when the backing route's mtls block declares
// crl/ocsp/cac_piv/principal_mapping (needsRuntime).
NeedsMTLSRuntime bool
}
GQLResolverField represents a query or mutation field.
type GQLResolversData ¶
type GQLResolversData struct {
Paths PackagePaths
// RoleHierarchyLit is the Go expression the generated gql package assigns
// to its own immutable `_roleHierarchy` (see APIData.RoleHierarchyLit).
// The GraphQL RBAC gate resolves required_roles against it instead of the
// package-wide securex.RoleFromString, so a service with a custom
// security.roles vocabulary enforces its own names (ENG-4634).
RoleHierarchyLit string
// RestInterfaceName is the Go identifier of the generated business-logic
// REST contract interface that NewInProcessResolvers accepts as its first
// parameter (apic.<name>). Defaults to "ServerInterface"; the repo-root
// self-host mirror sets it to "GeneratedServerInterface" to match the
// renamed interface in api/handlers_gen.go (collision-avoidance, GAP-0087).
RestInterfaceName string
// AnyTypedField is true when at least one query or mutation binds a typed
// request struct (HasTyped). It gates emission of the shared bindArgs
// helper + its bytes/sync imports so a schema with no typed resolver args
// does not emit an unused helper/import (PERF-0059), AND the genTypes
// import (GQL-IMPORTS-CLIENT) -- both are consumed by the exact same
// `{{if .HasTyped}}` per-field branch, so they share this one flag.
AnyTypedField bool
APIPkg string
WSPkg string
Queries []GQLResolverField
Mutations []GQLResolverField
Subscriptions []GQLSubscriptionField
HasCompositeMTLS bool // true when any field declares a composite mtls/cac/piv auth mode
// HasCompositeMTLSRuntime is true when at least one composite mtls/cac/piv
// field is backed by a REST route whose mtls block declares
// crl/ocsp/cac_piv/principal_mapping (SEC-0078). It gates the fail-closed
// guard in NewInProcessResolvers that panics (securex.ErrMTLSRuntimeRequired)
// when the boot-constructed runtime map was not supplied via
// WithMTLSRuntimes -- the GraphQL analogue of RegisterGeneratedAPI's
// HasAnyMTLSRuntime guard.
HasCompositeMTLSRuntime bool
// CompositeMTLSRuntimeKeyLits lists (sorted, deduped) the quoted
// "METHOD /path" runtime-map keys of every runtime-bearing route a
// composite mtls/cac/piv query/mutation dispatches to. NewInProcessResolvers
// checks each key is present in the supplied map and panics
// (securex.ErrMTLSRuntimeRequired, naming the key) otherwise (S1, #337):
// a present map missing a key would yield the zero runtime and silently
// skip the route's CRL/OCSP/CAC-PIV policy.
CompositeMTLSRuntimeKeyLits []string
// CookieName is the configured session cookie name (security.auth.cookie_name,
// defaulted). It is emitted as the package constant gqlCookieName so composite
// cookie gates honor the configured name instead of a hardcoded literal
// (GAP-0091). Only emitted when HasCompositeCookie is true.
CookieName string
}
GQLResolversData is the top-level data for gql_resolvers.go.tmpl.
func (GQLResolversData) HasCompositeCookie ¶
func (d GQLResolversData) HasCompositeCookie() bool
HasCompositeCookie reports whether any query or mutation declares a composite auth expression containing a "cookie" leaf, which gates emission of the gqlCookieName package constant.
func (GQLResolversData) RESTIfaceName ¶
func (d GQLResolversData) RESTIfaceName() string
RESTIfaceName returns the business-logic REST interface name the gql_resolvers template references (apic.<name> as NewInProcessResolvers' first param), defaulting to DefaultRESTInterfaceName ("ServerInterface") when RestInterfaceName is empty so any caller that omits the field still emits a valid Go type identifier.
type GQLSchemaCustomScalar ¶
type GQLSchemaCustomScalar struct {
Name string
VarName string // "s" + Name
CoercerID string // "datetime" | "uuid" | "json"
}
GQLSchemaCustomScalar is one declared GraphQL custom scalar emitted into the schema. CoercerID picks the entry in gqlx.BuiltinScalarCoercers.
type GQLSchemaData ¶
type GQLSchemaData struct {
Paths PackagePaths
Schemas []GQLSchemaType
Enums []GQLSchemaEnum
InputTypes []GQLSchemaInputType
CustomScalars []GQLSchemaCustomScalar
}
GQLSchemaData is the top-level data for gql_schema.go.tmpl.
type GQLSchemaEnum ¶
type GQLSchemaEnum struct {
Name string
VarName string // "e" + Name
Values []GQLSchemaEnumValue
}
GQLSchemaEnum is one declared GraphQL enum emitted into the schema.
type GQLSchemaEnumValue ¶
GQLSchemaEnumValue is one variant within a GQLSchemaEnum.
type GQLSchemaField ¶
type GQLSchemaField struct {
Name string // quoted field name
TypeExpr string // gqlx type expression
}
GQLSchemaField represents one field in a GraphQL object type.
type GQLSchemaInputField ¶
GQLSchemaInputField is one field on a GQLSchemaInputType. TypeExpr is the literal Go expression that constructs the field's gqlx type (e.g. "gqlx.TypeString", "gqlx.NonNullOf(gqlx.TypeID)").
type GQLSchemaInputType ¶
type GQLSchemaInputType struct {
Name string
VarName string // "i" + Name
Fields []GQLSchemaInputField
}
GQLSchemaInputType is one declared GraphQL input object emitted into the schema.
type GQLSchemaType ¶
type GQLSchemaType struct {
Name string
VarName string // "t" + Name
Fields []GQLSchemaField
}
GQLSchemaType represents one GraphQL object type.
type GQLSubscriptionField ¶
type GQLSubscriptionField struct {
Field string
Websocket string
TypeExpr string
MethodName string
Auth string
MessageSchema string
// RequiredRolesLit/ScopesLit/AttrsLit carry the backing WebSocket
// endpoint's authz policy into the subscription resolver so a role/scope/
// attribute-gated subscription enforces it (APPSEC: previously the
// subscription branch hard-coded nil, silently skipping authz).
RequiredRolesLit string
RequiredScopesLit string
RequiredAttrsLit string
// MaxFrame/MaxMessage size the in-process wsx.Conn pair the subscription
// resolver bridges through (net.Pipe): the backing WebSocket route's
// max_frame_bytes/max_message_bytes when set, else 65536
// (PERF-0111/PERF-0116 N-10 — previously hardcoded to 65536 regardless
// of the route's configured limits).
MaxFrame int
MaxMessage int
}
GQLSubscriptionField represents a subscription field.
type GenerationPlan ¶ added in v0.19.0
type GenerationPlan struct {
// contains filtered or unexported fields
}
GenerationPlan owns the captured output and enclosing-module directory handles and records the validated emitter's operations without mutation. It is single-use: Emit closes it on every outcome; callers must Close an abandoned plan.
func PrepareWithOptions ¶ added in v0.19.0
func PrepareWithOptions(cfg []byte, outDir, module string, withTests, withFuzz bool) (*GenerationPlan, error)
PrepareWithOptions performs config, module, interface, representability, and output-discovery validation and records generated bytes without creating, cleaning, or writing output. The returned plan owns handles until Emit or Close.
func (*GenerationPlan) Close ¶ added in v0.19.0
func (p *GenerationPlan) Close() error
Close abandons a prepared plan without mutation. It is idempotent; Emit also closes the plan on every outcome. An abandoned plan must be explicitly closed.
func (*GenerationPlan) Emit ¶ added in v0.19.0
func (p *GenerationPlan) Emit(ctx context.Context) error
Emit materializes a prepared plan exactly once.
func (*GenerationPlan) EmitWithOpenAPI ¶ added in v0.19.0
func (p *GenerationPlan) EmitWithOpenAPI(ctx context.Context, clean bool, artifacts []OpenAPIArtifact) (err error)
EmitWithOpenAPI emits a prepared config and already-rendered raw artifacts through the same selected output capability. Clean clears its contents after preparation, preserving the selected directory's identity and mode.
type GraphQLConfig ¶
type GraphQLConfig struct {
Path string `json:"path"`
WSPath string `json:"ws_path"`
MaxDepth int `json:"max_depth"`
MaxComplexity int `json:"max_complexity"`
MaxBatchSize int `json:"max_batch_size"`
MaxAliases int `json:"max_aliases"`
TimeoutMS int `json:"timeout_ms"`
Queries []GraphQLField `json:"queries"`
Mutations []GraphQLField `json:"mutations"`
Subscriptions []GraphQLSubField `json:"subscriptions"`
// AllowIntrospection mirrors gqlx.HandlerOptions.AllowIntrospection.
// Default false keeps the historical "schema sealed" posture.
AllowIntrospection bool `json:"allow_introspection,omitempty"`
// Enums declares named GraphQL enums that can be referenced as field
// types, input types, or argument types in the generated schema. All
// fields are additive and optional; zero-value (nil slice) preserves
// the previous behavior.
Enums []GraphQLEnum `json:"enums,omitempty"`
// InputTypes declares named GraphQL input objects.
InputTypes []GraphQLInputType `json:"input_types,omitempty"`
// CustomScalars binds named GraphQL scalars to the built-in coercers
// shipped in gqlx (datetime, uuid, json).
CustomScalars []GraphQLCustomScalar `json:"custom_scalars,omitempty"`
}
GraphQLConfig holds the graphql section of the config.
type GraphQLCustomScalar ¶
type GraphQLCustomScalar struct {
Name string `json:"name"`
Coercer string `json:"coercer"`
Description string `json:"description,omitempty"`
}
GraphQLCustomScalar binds a named GraphQL scalar to a built-in coercer id (datetime|uuid|json). Future custom coercers can be added by extending the registry in pkg/gqlx/builtins.go.
type GraphQLEnum ¶
type GraphQLEnum struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Values []GraphQLEnumValue `json:"values"`
}
GraphQLEnum models one GraphQL enum type declared in the config.
type GraphQLEnumValue ¶
type GraphQLEnumValue struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Deprecation string `json:"deprecation,omitempty"`
}
GraphQLEnumValue models one variant within a GraphQLEnum.
type GraphQLField ¶
type GraphQLField struct {
Field string `json:"field"`
Endpoint string `json:"endpoint"`
Description string `json:"description"`
}
GraphQLField maps a GraphQL field to a REST endpoint.
type GraphQLFuzzData ¶
type GraphQLFuzzData struct {
// Emit gates whether the gql fuzz file is written at all. The caller also
// guards on c.GraphQL != nil; Emit additionally requires a resolvable
// handler path so the fuzz request targets the mounted route.
Emit bool
// Path is the mounted GraphQL HTTP path (POST), e.g. "/graphql".
Path string
// Limit knobs mirror the config's GraphQL section so the generated
// GQLOptions match the real server's enforcement thresholds.
MaxDepth int
MaxComplexity int
MaxBatchSize int
MaxAliases int
TimeoutMS int
// Import lines (tab-prefixed, quoted) for the api, ws, and securex
// packages the handler wiring references. Empty in import-less mode
// (paths.Module == "").
APIImport string
WSImport string
SecurexImport string
// Seeds are Go string literals (strconv.Quote) of raw POST body strings:
// JSON envelopes carrying valid/over-limit/garbage GraphQL documents plus
// an oversized batch array and raw non-JSON edges. Deterministic, deduped.
Seeds []string
// HasCompositeMTLSRuntime mirrors GQLResolversData.HasCompositeMTLSRuntime
// (SEC-0078): when true NewInProcessResolvers requires WithMTLSRuntimes, so
// the harness supplies a defensive map exactly as the REST fuzz harness
// sets opts.MTLSRuntimes.
HasCompositeMTLSRuntime bool
// CompositeMTLSRuntimeKeyLits mirrors GQLResolversData.CompositeMTLSRuntimeKeyLits
// (S1, #337): NewInProcessResolvers fails closed on a map missing any of
// these keys, so the harness map literal carries every one of them with
// a zero (no-enforcement) runtime.
CompositeMTLSRuntimeKeyLits []string
}
GraphQLFuzzData is the top-level data for tests_fuzz_gql.go.tmpl. The emitted target drives the generated GraphQL HTTP handler (RegisterGeneratedGQL over NewInProcessResolvers) end-to-end, fuzzing the raw POST body, and asserts the handler never returns 500 — over-limit/garbage documents must be rejected with a 4xx or a 200-with-errors envelope, never a crash.
type GraphQLInputField ¶
type GraphQLInputField struct {
Name string `json:"name"`
Type string `json:"type"`
Required bool `json:"required,omitempty"`
Default any `json:"default,omitempty"`
}
GraphQLInputField models one field on a GraphQLInputType. Type accepts a GraphQL type reference string: "String", "Int", "EnumName", "InputTypeName", "[Type!]", "Type!", etc. Required and Default are independent of the Type modifier so callers can express "optional but non-null when present" defaulting cleanly.
type GraphQLInputType ¶
type GraphQLInputType struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Fields []GraphQLInputField `json:"fields"`
}
GraphQLInputType models one GraphQL input object type declared in the config.
type GraphQLSubField ¶
type GraphQLSubField struct {
Field string `json:"field"`
Websocket string `json:"websocket"`
Description string `json:"description"`
}
GraphQLSubField maps a GraphQL subscription to a WebSocket path.
type MCPConfigSection ¶
type MCPConfigSection struct {
configx.MCPConfig
Tools []MCPToolConfig `json:"tools"`
// MaxBody caps the JSON-RPC request body that the generated /mcp HTTP
// handler will accept. Mirrors the per-route maxBodyBytes on regular
// HTTP operations. Zero (or unset) selects a 1 MiB default at codegen
// time. APPSEC-1.
MaxBody int `json:"max_body_bytes,omitempty"`
// Rate is the token-bucket fill rate (tokens/second) used by the
// emitted mcpx.WithToolBuckets call to construct the per-tool rate
// limiter. Zero selects the historical default of 100. Operators
// who need a tighter ceiling on sensitive MCP surfaces should set
// this explicitly; the runtime's hard-coded default predates the
// config surface. GEN-2026-05-29-06.
Rate float64 `json:"rate,omitempty"`
// Burst is the maximum token-bucket depth (the surge above which a
// caller is rate-limited). Zero selects the historical default of
// 20. Like Rate, expose as a config knob so the operator can tune
// per deployment rather than patching the generator output.
// GEN-2026-05-29-06.
Burst float64 `json:"burst,omitempty"`
// JWTAlg is the JWS algorithm the server-wide JWT verifier is expected
// to use when MCP transport auth is JWT (security.auth.jwt=true).
// MCP has no per-tool auth mode -- authSurface (server_lib.go.tmpl)
// gates the ENTIRE /mcp surface on security.auth.jwt (else api_key),
// exactly like every other JWT-gated surface -- so this is a
// transport-level field, not a per-tool one. Same recognized values
// as an operation's jwt_alg: "HS256" (legacy), "RS256", "RS384",
// "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512",
// "EdDSA". Empty defaults to the legacy HS256 path. When
// SecurityConfig.FIPS is true and the MCP surface is JWT-gated, it
// must be an asymmetric alg, exactly like APIOperation.JWTAlg.
JWTAlg string `json:"jwt_alg,omitempty"`
}
MCPConfigSection models the MCP generator block.
type MCPData ¶
type MCPData struct {
Paths PackagePaths
Tools []MCPTool
HasToolRole bool // true when at least one tool declares authz.roles
// HasToolOwnership is true when at least one tool declares `ownership`
// (SEC-0027 / OWASP API1:2023 BOLA). Gates both the mcpToolOwnership
// table and the mcpx.WithToolOwnership install in NewEngine.
HasToolOwnership bool
// NeedsTypesImport is true when at least one tool references a generated
// type (i.e. has a non-empty paramsSchema or resultSchema that resolves to
// a types.X reference in the emitted code). When false the types import is
// omitted from the generated file to avoid "imported and not used" build
// errors for schema-less MCP configs (GQL-IMPORTS-MCP).
NeedsTypesImport bool
// HasAnyTypedTool is true when at least one tool has a typed
// paramsSchema (MCPTool.HasTyped) -- the per-tool block guarded by
// {{if .HasTyped}} is the only site in mcp.go.tmpl that references
// "log/slog" (the mcp_hash_error LogAuditAttrs call, PERF-0108/#287).
// Gating the import on this narrower flag rather than NeedsTypesImport
// avoids an "imported and not used" build failure for a config whose
// tools are all untyped (jsontext.Value params) but whose result
// schemas still resolve to a types.X (which sets NeedsTypesImport
// without ever reaching the HasTyped branch).
HasAnyTypedTool bool
// Rate and Burst are the token-bucket parameters wired into the
// emitted mcpx.WithToolBuckets call. Defaults preserve the historic
// 100 rps / 20 burst when MCPConfigSection leaves them unset.
// GEN-2026-05-29-06.
Rate float64
Burst float64
// ServerName and ServerVersion feed the emitted mcpx.WithServerInfo
// call (MCP initialize serverInfo). ServerName empty suppresses the
// call. There is no per-config server-name field today, so the
// generator sources these from the OpenAPI info block constants.
ServerName string
ServerVersion string
}
MCPData is the top-level data for mcp.go.tmpl.
type MCPFuzzData ¶
type MCPFuzzData struct {
HasTools bool
Tools []MCPFuzzTool
}
MCPFuzzData is the top-level data for tests_fuzz_mcp.go.tmpl.
type MCPFuzzTool ¶
type MCPFuzzTool struct {
FuncName string
ToolName string // the string key in the NewTools(...) map, e.g. "echo"
// Seeds are Go string literals (already quoted via strconv.Quote) ready
// to drop into f.Add(<lit>). Deterministic, deduplicated.
Seeds []string
}
MCPFuzzTool is one MCP fuzz target: a Go func name, the tool name string (for the map lookup), and the corpusgen-derived seed corpus.
type MCPTool ¶
type MCPTool struct {
Name string
MethodName string
ParamsType string
ResultType string
HasTyped bool // true when params/result are typed (not jsontext.Value)
ParamsSchema string
RequiredRoles []string // per-tool authz.roles; empty means no role gate
// RolesLit is the elided-form Go literal for RequiredRoles (e.g.
// {"admin"}, not []string{"admin"}) -- the sole use site, mcp.go.tmpl's
// mcpToolRoles table, embeds it as a map[string][]string{...} value,
// where gofmt -s requires dropping the redundant []string prefix
// (QS-05; see renderStringSliceElemLiteral).
RolesLit string
// Ownership is the tools/call argument name carrying the resource-owner
// id for this tool (MCPToolConfig.Ownership). Empty means no
// object-level gate. validateMCPToolOwnership has already proven the
// name is an argument of the emitted inputSchema, that the surface is
// JWT-gated (so a subject is attested), and that the tool is not marked
// public.
Ownership string
// APPSEC-Gen-F-009: per-tool body cap enforced at the
// jsontext.Value level so every transport (HTTP, WS, stdio)
// inherits the same defense. Zero falls back to 1 MiB.
MaxBody int
// Title and Description feed the tools/list ToolDescriptor. Empty
// fields are omitted from the emitted literal.
Title string
Description string
// InputSchemaJSON is the marshaled JSON Schema object (union of the
// tool's body, path, and query parameters) emitted as a backtick
// literal in the ToolDescriptor.InputSchema field. Always a valid
// JSON object (at minimum `{"type":"object","properties":{}}`).
InputSchemaJSON string
// PathParamBinds maps flat MCP argument tokens to the request-schema
// fields the G6 dispatch must bind before validation. Empty for
// non-mirrored tools or tools whose source op has no path params that
// land in the request schema. Only string-typed path fields are bound
// (the request field for a path param is a Go string).
PathParamBinds []TypedPathParam
}
MCPTool represents one MCP tool.
type MCPToolConfig ¶
type MCPToolConfig struct {
Name string `json:"name"`
ParamsSchema string `json:"paramsSchema"`
ResultSchema string `json:"resultSchema"`
Operation string `json:"operation,omitempty"`
// Title is the optional human-readable display name surfaced in the MCP
// tools/list descriptor (ToolDescriptor.Title). Empty omits the field.
Title string `json:"title,omitempty"`
// Description is the optional MCP tools/list description
// (ToolDescriptor.Description). When a tool mirrors an operation
// (Operation != "") and leaves this empty, normalizeMCPTool inherits the
// source operation's Description.
Description string `json:"description,omitempty"`
// MaxBody caps the per-tool jsontext.Value payload size. Inherits
// from MCPConfigSection.MaxBody when zero; zero MaxBody falls back
// to 1 MiB at codegen. APPSEC-Gen-F-009.
MaxBody int `json:"max_body_bytes,omitempty"`
// Ownership names the tools/call argument carrying the resource-owner
// id for this tool (SEC-0027 / OWASP API1:2023 BOLA, MCP surface).
// When set, the generated NewEngine passes mcpx.WithToolOwnership so the engine allows a
// dispatch only when the caller's context carries an authenticated
// subject (stamped by the server-wide auth verifier via
// mcpx.ContextWithSubject) AND the named argument equals it. Empty (the
// default) leaves the tool ungated by object-level authorization.
//
// MCP has no per-tool auth mode (SEC-0049) -- transport auth is
// server-wide -- so an ownership-gated tool requires
// security.auth.jwt (the only surface gate that attests a subject) and
// cannot be marked authn:"public". The argument name must appear in the
// tool's resolved input schema. Declaring ownership satisfies the
// SEC-0049 default-deny requirement on its own: it is a real
// authorization gate, not an absence of one.
Ownership string `json:"ownership,omitempty"`
SecurityContract
}
MCPToolConfig models one MCP tool.
type MTLSContract ¶
type MTLSContract struct {
ClientAuth string `json:"client_auth,omitempty"`
// CABundlePath is the operator-supplied PEM CA bundle that the
// runtime mtlsx.TrustStore loads to seed tls.Config.ClientCAs. Empty
// when the bundle is supplied via api.WithMTLS at server-construction
// time rather than per-operation config.
CABundlePath string `json:"ca_bundle_path,omitempty"`
SupportedIssuers []string `json:"supported_issuers,omitempty"`
EKUValidation bool `json:"eku_validation,omitempty"`
// CRL configures CRL-based revocation checking (L-51). The generator
// constructs a mtlsx.CRLChecker ONCE at server boot (server_lib.go.tmpl)
// from this contract and threads it through
// securex.MTLSPolicy.Runtime.CRL on the REST surface; VerifyMTLS
// enforces it fail-closed (allow_soft_fail:false denies on an
// unreachable/unparseable CRL, allow_soft_fail:true warns and
// continues). Also enforced on the GraphQL surface (SEC-0078): the
// composite mtls/cac/piv gate in gql_resolvers.go.tmpl / gql_proxy.go.tmpl
// reads the same per-route Runtime via gengql.WithMTLSRuntimes, keyed by
// the backing route's "METHOD /path". See docs/GENERATOR_BUGS.md L-51.
CRL *CRLContract `json:"crl,omitempty"`
// OCSP configures OCSP-based revocation checking. See the CRL doc
// comment above: same construction/enforcement story on both surfaces.
OCSP *OCSPContract `json:"ocsp,omitempty"`
// PrincipalMapping selects which verified-certificate field the
// generated handler resolves as the caller's identity. Accepted values:
// "subject_cn" (default), "upn", "san_email", "san_dns_first", "edipi".
// Enforced via cacpiv.Adapter.Verify (L-51): the generator constructs
// one cacpiv.Adapter per route at boot and VerifyMTLS calls
// Adapter.Verify after CRL/OCSP checks pass, failing closed
// (cacpiv.ErrMappingEmpty) when the selected field is empty on the
// certificate -- a configured mapping is a security control, so a
// certificate lacking that field must not silently authenticate with no
// bound identity. Enforced on the REST and GraphQL surfaces alike (see
// the CRL doc comment above).
PrincipalMapping string `json:"principal_mapping,omitempty"`
// CACPIV is the optional DOD CAC / federal PIV policy layered on top of
// the base mTLS verification: require_person rejects non-person
// (CardAuth/NPE/unknown) certificates, required_policy_oids requires at
// least one named certificate policy OID, reject_unknown_classification
// rejects certificates cacpiv.Classify cannot categorize. Enforced via
// the same cacpiv.Adapter.Verify call as PrincipalMapping above (L-51),
// on the REST and GraphQL surfaces alike (see the CRL doc comment above).
CACPIV *CACPIVContract `json:"cac_piv,omitempty"`
}
MTLSContract captures per-operation mutual-TLS settings used by mTLS-bound authorization endpoints (PIV/CAC/custom-PKI deployments) and certificate registration flows.
type MTLSRuntimeRoute ¶
type MTLSRuntimeRoute struct {
// Key is "METHOD /path" (method uppercased), matching the same string
// api.go.tmpl renders into MTLSPolicy lookups against
// opts.MTLSRuntimes. Method+Path uniqueness is already enforced by
// validateConfigAPI's duplicate-route check.
Key string
// CRLConfigLit is a Go literal for mtlsx.CRLConfig{...}, or "" when the
// route declares no mtls.crl block.
CRLConfigLit string
// OCSPConfigLit mirrors CRLConfigLit for mtlsx.OCSPConfig.
OCSPConfigLit string
// NeedsCertPolicy is true when the route needs a cacpiv.Adapter (either
// a cac_piv block or a non-empty principal_mapping).
NeedsCertPolicy bool
// CACPIVOptsLit is a Go literal for []cacpiv.Option{...} (never empty
// string when NeedsCertPolicy is true; may render as
// "[]cacpiv.Option{}" when cac_piv itself is unset but
// principal_mapping is).
CACPIVOptsLit string
// HasPolicyOIDs is true when CACPIVOptsLit references
// asn1.ObjectIdentifier literals (i.e. cac_piv.required_policy_oids is
// non-empty), so the caller knows whether the encoding/asn1 import is
// needed.
HasPolicyOIDs bool
// PrincipalMappingLit is the quoted Go string literal passed to
// cacpiv.Adapter.Verify as the mapping argument. Only meaningful when
// NeedsCertPolicy is true; "" (unquoted Go empty string, i.e. the
// literal `""`) defaults to "subject_cn" at runtime
// (cacpiv.PrincipalValue).
PrincipalMappingLit string
}
MTLSRuntimeRoute carries the generation-time literals server_lib.go.tmpl needs to construct one route's securex.MTLSRuntime exactly ONCE at boot (L-51): CRL/OCSP revocation checkers hold response caches and make network calls, and the CAC/PIV policy verifier must be shared across every request for the route, so none of this is safe to rebuild per-request.
type NestedPattern ¶
NestedPattern is a depth-qualified regexp declaration used by recursive nested-array validation. Keeping declarations flat makes their ordering deterministic and avoids template-side tree traversal outside Valid.
type NestedValidationNode ¶
type NestedValidationNode struct {
Depth int // entry depth relative to the containing schema helper
Map bool // true for a privately decoded typed map, never an opaque JSON object
Active bool
Type string
Ref string
Expr string
JSONName string
MinItems *int64
MaxItems *int64
MinLength *int64
MaxLength *int64
Pattern string
ReVar string
MinOp string
MinValue string
MaxOp string
MaxValue string
EnumValues []string
DateTime bool
Child *NestedValidationNode
}
NestedValidationNode is one recursively nested array-item schema used by generated Valid methods. Expr is a generator-owned Go identifier/expression, never config text. Array nodes validate cardinality then visit Child; scalar nodes apply the same facets supported by top-level and immediate array items.
type OCSPContract ¶
type OCSPContract struct {
Endpoints []string `json:"endpoints,omitempty"`
TTLMS int `json:"ttl_ms,omitempty"`
AllowSoftFail bool `json:"allow_soft_fail,omitempty"`
}
OCSPContract configures OCSP revocation checks for a route.
type OIDCContract ¶
type OIDCContract struct {
PKCERequired bool `json:"pkce_required,omitempty"`
InteractiveAuthentication string `json:"interactive_authentication,omitempty"`
GrantSchemas OIDCGrantSchemas `json:"grant_schemas,omitempty"`
ClientAuth string `json:"client_auth,omitempty"`
PasswordGrantEnabled bool `json:"password_grant_enabled,omitempty"`
RefreshTokens OIDCRefreshTokenPolicy `json:"refresh_tokens,omitempty"`
ScopeClaims map[string][]string `json:"scope_claims,omitempty"`
}
OIDCContract captures profile-specific invariants that the generator can validate before it emits handlers or clients.
type OIDCGrantSchemas ¶
type OIDCGrantSchemas struct {
AuthorizationCode string `json:"authorization_code,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
Password string `json:"password,omitempty"`
}
OIDCGrantSchemas names the per-grant schemas used for token endpoint validation and contract documentation.
type OIDCRefreshTokenPolicy ¶
type OIDCRefreshTokenPolicy struct {
Mode string `json:"mode,omitempty"`
RotationRequired bool `json:"rotation_required,omitempty"`
RequireStore bool `json:"require_store,omitempty"`
RequireRevocationStore bool `json:"require_revocation_store,omitempty"`
UnsafeAllowStatelessJWT bool `json:"unsafe_allow_stateless_jwt,omitempty"`
}
OIDCRefreshTokenPolicy records refresh-token lifecycle expectations.
type OpenAPIArtifact ¶ added in v0.19.0
OpenAPIArtifact is one already-rendered raw OpenAPI Go file. Name is a basename ending in .gen.go; emission is restricted to openapi/ with mode 0644. This value grants no authority to load raw inputs or update a module.
type OwnershipConfig ¶
type OwnershipConfig struct {
// Param is the name of the path parameter carrying the resource-owner
// id -- e.g. "userId" for a route path "/v1/users/{userId}". Required,
// and validated to be present in the route path.
Param string `json:"param"`
}
OwnershipConfig declares the object-level ownership gate for one REST route (SEC-0027 / OWASP API1:2023 BOLA). The generated handler compares the verifier-attested subject on the request context (securex.ClaimsFromContext, stashed by APIOptions.AuthJWT) against the routed path parameter, in constant time, after RBAC and before the request is dispatched. It never re-parses the bearer token, so a caller cannot forge the subject.
Fail-closed by construction: validation requires Param to name a path parameter that actually appears in the route path and requires every OR-group of the route's auth expression to carry an identity-bearing mode (jwt or cookie); at runtime an absent claims context, an empty subject, an empty path value, or any mismatch is a generic 403 with a server-side audit record.
type PackagePaths ¶
type PackagePaths struct {
Module string
CleanOut string
PkgPrefix string
APIPkg string
WSPkg string
MCPPkg string
GQLPkg string
TypesPkg string
ServerPkg string
HTTPXPkg string
WSXPkg string
MCPXPkg string
GQLXPkg string
ConfigPkg string
SecurePkg string
MTLSXPkg string
CACPIVPkg string
OBSPkg string
HealthPkg string
HTTPPkg string
GenPkg string
}
PackagePaths holds the computed import paths used by all templates.
type QueryScalarField ¶
type QueryScalarField struct {
Name string // JSON query key (e.g. "max_age")
GoName string // Go struct field name (e.g. "MaxAge")
// Kind is one of "int", "int64", "bool", "float64", "string". Selects
// which strconv.Parse* the template emits for the numeric/bool kinds; the
// template uses Kind directly in case labels, so values must match
// strconv.Parse* function names (modulo case). "string" (GENERATOR_BUGS.md
// L-37 — a nullable string query field) needs no parsing, just the same
// nil-guarded address-of assignment as the other kinds.
Kind string
}
QueryScalarField describes one non-string scalar query parameter (integer, number, boolean). The generator emits a `strconv.Parse*` for each, with a 400 response on parse error. Closes A-Q2 from GENERATOR_BUGS.md — the pre-fix generator filtered the query binding to string-only schema properties, silently dropping fields like `OIDCAuthorizeRequest.MaxAge int64` so a spec-compliant `?...&max_age=300` reached the handler as `MaxAge=0`.
type ReactQueryData ¶
type ReactQueryData struct {
Resources []ReactQueryResource
Routes []ReactQueryRoute
// Schemas is the deduplicated, sorted list of schema type names actually
// referenced by the emitted useQuery<T>/useMutation<T,V> hooks. The
// template uses this instead of the full ClientData.Schemas list to avoid
// importing types (e.g. WebSocket message schemas) that are declared in the
// react client but never referenced in any hook.
Schemas []string
}
ReactQueryData is the whole react-query-variant view: per-resource key factory groups + the flat per-route hook list.
type ReactQueryResource ¶
type ReactQueryResource struct {
ID string // JS object key, e.g. "items"
Members []ReactQueryRoute // query routes contributing keyed members (may be empty)
}
ReactQueryResource groups query routes under one resource for the key factory.
type ReactQueryRoute ¶
type ReactQueryRoute struct {
Route ClientRoute
ResourceID string // JS-safe resource identifier, e.g. "items" (from /items)
KeyMember string // per-route key fn member, e.g. "getItems" (lowerFirst MethodName)
IsQuery bool // GET/HEAD -> useQuery; else (POST/PUT/PATCH/DELETE) useMutation
}
ReactQueryRoute is one route's react-query-variant metadata: a stable camelCase key-factory member name + the resource it belongs to.
type ReactUIData ¶
type ReactUIData struct {
APIPrefix string
// SpecHash is the resolved config's spec-identity hash, re-exported by the
// explorer's metadata module so an embedding app can surface (or compare)
// the spec revision the UI was generated from. GAP-0121 (#259).
SpecHash string
REST []UIEndpoint
WebSockets []UIWSEndpoint
GraphQL []UIGraphQLOp
MCP []UIMcpTool
Schemas []UISchema
HasGraphQL bool
HasWS bool
HasMCP bool
// MCPAuthMode describes which auth modes the generated MCP HTTP endpoint
// accepts. It is derived from the resolved security.auth config that the
// generated authSurface uses. Values: "jwt", "api_key", "jwt+api_key",
// "none". Used by McpPanel to show an honest auth note and disable the
// Invoke button when only api_key/HMAC auth is available (which requires
// nonce+timestamp signing that cannot be performed from a browser).
MCPAuthMode string
// OIDCIssuer is the JWT/OIDC issuer URL from security.auth.jwt_issuer.
// Non-empty when the config declares a service-wide JWT issuer.
// This is a public, non-secret value (same as the OIDC discovery document
// issuer). Used by ServiceExplorer to show a service-level OIDC note.
// Empty when no issuer is configured.
OIDCIssuer string
// HasOIDC is true when any route uses OIDC-profile auth (jwt/cookie-auth
// routes) AND a service-wide jwt_issuer is set. Drives the explorer note.
HasOIDC bool
}
ReactUIData is the top-level view model for the generated React service explorer (gen/client/react-ui/metadata.ts).
type SchemaDef ¶
type SchemaDef struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Required []string `json:"required"`
Properties map[string]SchemaProp `json:"properties"`
// Ref imports this schema from another spec file instead of defining it
// inline: "$ref": "<relative-path>#/schemas/<Name>". The referenced file
// may be a full apic config or a fragment containing only a "schemas"
// block; the map key MUST equal <Name> (no renaming). Resolution happens
// before validation via ResolveSchemaRefs (the CLI and MCP surfaces do
// this automatically); an unresolved Ref fails validation closed.
// Mutually exclusive with every inline field on this struct. ENG-1424.
Ref string `json:"$ref,omitempty"`
}
SchemaDef describes a JSON object schema with required properties.
type SchemaProp ¶
type SchemaProp struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Nullable bool `json:"nullable,omitempty"`
MinLength int `json:"minLength"`
MaxLength *int `json:"maxLength"`
// Pattern is an optional RE2 (Go regexp) the generated Valid()
// method enforces on a string property via regexp.MustCompile +
// MatchString. It is compile-validated at config load
// (validateSchemaPatterns) so a malformed regex fails generation,
// never at request time. Also emitted as the OpenAPI `pattern`
// facet so published specs and SDK clients carry the constraint.
// Closes GENERATOR_BUGS.md L-8.
Pattern string `json:"pattern,omitempty"`
// Format is the JSON-Schema `format` annotation. A string property accepts
// "date-time"; an integer property accepts "int64", matching its generated
// Go field type. Integer format defaults to int64 when omitted. The config
// validator (validateSchemaFormats) rejects type/format mismatches and
// unknown values so a typo cannot silently weaken the published contract.
// For "date-time" the generated Valid() parses the field with
// time.Parse(time.RFC3339, ...). The Go field remains an RFC3339-typed
// string rather than time.Time, so that string-format support is additive.
Format string `json:"format,omitempty"`
// Secret marks this field as sensitive: it is redacted in logs/audit output
// (via the generated LogValue), must not appear in any response schema, and
// — when Hash is set — is one-way hashed before any handler observes it.
Secret bool `json:"secret,omitempty"`
// Hash, when non-empty, requires this string field to be one-way hashed
// before the request reaches the handler. Recognized values:
// "argon2id", "bcrypt", "scrypt", "pbkdf2", "pbkdf2-sha256", "pbkdf2-sha512",
// or "default" to inherit security.password_hash.default. Implies Secret.
Hash string `json:"hash,omitempty"`
MinItems *int `json:"minItems"`
MaxItems *int `json:"maxItems"`
Minimum *float64 `json:"minimum"`
Maximum *float64 `json:"maximum"`
ExclusiveMinimum bool `json:"exclusiveMinimum"`
ExclusiveMaximum bool `json:"exclusiveMaximum"`
Enum []any `json:"enum"`
Items *SchemaProp `json:"items"`
Properties map[string]SchemaProp `json:"properties"`
Required []string `json:"required"`
Ref string `json:"$ref"`
// MaxSize is the maximum size, in bytes, of a single uploaded file for a
// `type:"file"` property. Only meaningful on a file property; ignored
// otherwise. Zero (the default) means "no per-file bound declared here —
// fall back to the route's effective body cap" (route `maxBodyBytes`,
// else `server.limits.max_body_bytes`): the generated handler always
// enforces the body cap via http.MaxBytesReader regardless of MaxSize,
// so a file is never accepted larger than that cap even when MaxSize is
// unset. When MaxSize IS set, config validation's G-07 rule
// (validateFileFieldsFitBodyCap) rejects the route at generate time if
// MaxSize * MaxFiles exceeds the effective body cap — that combination
// would make the advertised MaxSize an unreachable lie, since
// MaxBytesReader would always reject the request first.
MaxSize int64 `json:"maxSize"`
// AllowedTypes is a MIME-type allow-list for a `type:"file"` property.
// Empty (the default) allows any content type. Matching is an exact,
// case-insensitive comparison against the multipart part's Content-Type
// header with any `;`-delimited parameter (e.g. `; charset=...`) stripped
// first — not a prefix or wildcard match, so `"image/*"` does not match
// `"image/png"` and each accepted type must be listed explicitly (e.g.
// `["image/png", "image/jpeg"]`). A part whose Content-Type is not in the
// list is rejected with HTTP 400.
AllowedTypes []string `json:"allowedTypes"`
// MaxFiles is the maximum number of files accepted for a `type:"file"`
// property. Zero or one (one is the default) means a single-file field,
// generating a `*gen.FileUpload` struct field; any value greater than one
// makes it a multi-file field generating `[]*gen.FileUpload`, and the
// generated handler rejects the request with HTTP 400 if more than
// MaxFiles parts are posted under that field name.
MaxFiles int `json:"maxFiles"`
// AdditionalProperties models a `type:"object"` property as a
// `map[string]T` field (L-26 defect 1). The value schema may be a scalar
// (`{"type":"string"}` -> map[string]string), a `$ref`
// (`{"$ref":"T"}` -> map[string]T), or untyped/object
// (`{}`/`{"type":"object"}` -> map[string]jsontext.Value). When nil, an
// object property remains a raw jsontext.Value field as before.
AdditionalProperties *SchemaProp `json:"additionalProperties,omitempty"`
}
SchemaProp models a JSON Schema property subset used by the generator.
Nullable indicates the property may be absent from the wire OR explicitly set to a clearing zero value. Scalars use pointers so callers can distinguish "preserve current value" (nil) from "set/clear to zero" (non-nil pointer to a zero value). Collections retain their slice/map type and use omitempty, so nil means absent while a non-nil empty collection remains an explicit clear. This is the schema-side hook used by PATCH-style requests where partial updates must be expressible without ambiguity.
type SecurityContract ¶
type SecurityContract struct {
Surface string `json:"surface,omitempty"`
Exposure ExposureConfig `json:"exposure,omitempty"`
Sensitivity string `json:"sensitivity,omitempty"`
Authn string `json:"authn,omitempty"`
Authz AuthzContract `json:"authz,omitempty"`
DevOnly bool `json:"dev_only,omitempty"`
InternalOnly bool `json:"internal_only,omitempty"`
Profile string `json:"profile,omitempty"`
}
SecurityContract captures generator-owned security posture for a surfaced operation. Fields are flattened into route/tool JSON for ergonomic configs.
type ServerData ¶
type ServerData struct {
Paths PackagePaths
// RestInterfaceName is the Go identifier of the generated business-logic
// REST contract interface, referenced by the emitted server package
// (Options.Impl, WithImpl, and the Serve default conversion). Defaults to
// "ServerInterface"; the repo-root self-host mirror sets it to
// "GeneratedServerInterface" so it matches the renamed interface emitted
// into api/handlers_gen.go (collision-avoidance, GAP-0087).
RestInterfaceName string
// SpecHash is the deterministic spec-identity hash of the resolved config
// (GAP-0121 #259), emitted as the server package's APISpecHash constant
// and advertised as the X-Apic-Spec-Hash response header when
// server.spec_hash_header is enabled (the default).
SpecHash string
HasGraphQL bool
GQLTimeout int
GQLMaxQueryBytes int
GQLMaxBatchSize int
GQLMaxDepth int
GQLMaxComplexity int
GQLMaxAliases int
GQLWSPath string
GQLMaxSubscriptions int
// MCPMaxBody is the per-request body cap (in bytes) the generated /mcp
// HTTP handler enforces via http.MaxBytesReader. Defaults to 1 MiB. A
// value of 0 disables the cap (operator opt-out for unlimited bodies).
// APPSEC-1.
MCPMaxBody int
// HasMCPTools is true when the spec exposes at least one MCP tool, i.e.
// when the emitted genmcp.NewTools map is non-empty (anyMCPToolExposed).
// It gates the ENTIRE emitted MCP block: the *mcpx.Engine construction,
// the /mcp and /mcp/ws mounts, the stdio loop, the mcpImpl local and the
// mcpx import.
//
// mcpx.NewEngine fails closed on an empty tool set (ErrNoTools, "mcpx:
// engine has no tools; pass WithTools") -- deliberately, because an
// engine with no tools can serve neither tools/list nor tools/call and is
// almost always a wiring mistake. A spec that declares mcp.tools but
// exposes none of them (every entry exposure.mcp:false) is NOT that
// mistake, and before this gate it generated and compiled cleanly and
// then failed at boot with "mcp engine: mcpx: engine has no tools"
// (make boot-verify: BOOT-FAIL graphql-subscriptions-only). Gating here
// keeps the mcpx refusal meaningful for callers who DO ask for MCP
// instead of weakening it for everyone.
HasMCPTools bool
// HasAnyMTLSWithIssuers is true when the spec declares at least one
// auth: "mtls" route with a non-empty supported_issuers list. The
// generator emits a boot-time guard requiring opts.AuthMTLS to be
// wired in that case so the issuer-label policy cannot silently
// no-op. APPSEC-5.
HasAnyMTLSWithIssuers bool
// HasAnyMTLS is true when the spec declares ANY auth: "mtls" route,
// regardless of whether it lists supported_issuers. securex.VerifyMTLS
// deliberately never enforces SupportedIssuers (only opts.AuthMTLS
// enforces issuer/identity policy), so a nil AuthMTLS hook fails OPEN
// for ALL mtls routes — not just the issuer-bound ones. The generator
// uses this flag to (a) emit the RegisterGeneratedAPI nil-AuthMTLS
// fail-closed guard, (b) broaden the New constructor refuse-to-start
// guard, and (c) install a secure default verifier in the generated
// entrypoint. HasAnyMTLSWithIssuers stays as-is to pick the default
// verifier (IssuerCNVerifier vs RequireClientCert).
HasAnyMTLS bool
// HasAnyMTLSRuntime is true when at least one route's mtls block
// declares crl/ocsp/cac_piv/principal_mapping (L-51): Serve constructs
// one securex.MTLSRuntime per such route ONCE at boot (never per
// request -- mtlsx.CRLChecker/OCSPChecker hold response caches and make
// network calls) and threads the result through
// APIOptions.MTLSRuntimes. Gates the mtlsx import when
// HasAnyMTLSWithIssuers is false (a route can have crl/ocsp with no
// supported_issuers).
HasAnyMTLSRuntime bool
// HasAnyMTLSCertPolicy is true when at least one MTLSRuntimeRoutes entry
// needs a cacpiv.Adapter (cac_piv block present OR principal_mapping
// set): gates the cacpiv import.
HasAnyMTLSCertPolicy bool
// HasAnyMTLSPolicyOIDs is true when at least one route's cac_piv block
// lists required_policy_oids: gates the encoding/asn1 import.
HasAnyMTLSPolicyOIDs bool
// MTLSRuntimeRoutes carries the boot-construction literals for every
// route that needs a securex.MTLSRuntime (L-51). See MTLSRuntimeRoute.
MTLSRuntimeRoutes []MTLSRuntimeRoute
// HasWebAuthn is true when the spec configures the WebAuthn surface
// (security.webauthn set, or any route declares a webauthn ceremony
// profile/block). The generated server package imports webauthnx,
// defaults the credential/session stores to in-memory implementations
// when the caller does not supply durable ones, and threads both into
// APIOptions -- without this RegisterGeneratedAPI panics at boot
// (webauthnx.ErrWebAuthnStoresRequired).
HasWebAuthn bool
// HealthDeclaresTLSCertReload is true when health is enabled and
// health.checks declares a check named "tls_cert_reload" (SEC-0082,
// #382). The generated Serve then binds that check to the listener's
// fipsx.CertReloader itself (the operator declares it, never registers
// it), and refuses to boot when the listener has no file-backed
// reloader to check.
HealthDeclaresTLSCertReload bool
// TLSCertReloadCheckName is TLSCertReloadCheckName, interpolated into the
// template so the name lives in exactly one place (QG-139, #398) --
// the same single-source pattern RustVersion/ZigVersion use for the
// polyglot toolchain floors.
TLSCertReloadCheckName string
// CompressionExcludePaths is the sorted, deduplicated list of route
// paths that must never be gzipped, regardless of EnableCompression.
// Populated from API routes flagged sensitivity:"sensitive" or with
// profile in {oidc_token, oidc_userinfo}. Rendered into
// httpx.Config.CompressionExcludePaths as a belt-and-suspenders
// BREACH (CWE-310) mitigation on top of the runtime
// Cache-Control: no-store/private skip in pkg/httpx/compress.go.
// Closes A-NEW-1 generator-wiring follow-up.
CompressionExcludePaths []string
// RequiresAsymmetricJWT is true when the spec declares any route with
// auth: "jwt" and an asymmetric jwt_alg (RS256/ES256/PS256 family) OR
// when security.fips is true and any jwt route exists. In those cases
// the built-in HS256 gate cannot correctly verify the tokens the
// operator issues, so the generated server emits a boot-time
// fail-closed guard requiring an operator-supplied WithAuthJWT verifier.
// Symmetric configs (HS256 / empty jwt_alg) are unaffected. APPSEC.
RequiresAsymmetricJWT bool
// CSRFEnabled is true when security.csrf.enabled. The generated server
// resolves the CSRF signing key fail-closed at boot, builds a
// *csrfx.Signer, installs a default session-id extractor (claims
// Subject), warns when secure_cookies is on with a non-TLS listener, and
// threads the signer + names into APIOptions. F2.
CSRFEnabled bool
}
ServerData is the top-level data for server.go.tmpl.
func (ServerData) RESTIfaceName ¶
func (d ServerData) RESTIfaceName() string
RESTIfaceName returns the business-logic REST interface name the server_lib template references (apic.<name> in Options.Impl / WithImpl / Serve), defaulting to DefaultRESTInterfaceName ("ServerInterface") when RestInterfaceName is empty so any caller that omits the field still emits a valid Go type identifier.
type SharedConfig ¶
type SharedConfig struct {
// (a tree produced by `apic runtime --out <dir>`, or devnw.dev/apic
// itself to import the runtime as a normal module dependency). When set,
// this generation does NOT vendor its own <out>/pkg/ copy; every
// generated import of the runtime resolves to <Runtime>/pkg/<sub>.
// All generations sharing one binary or module should point at the same
// prefix so runtime types are identical across services.
Runtime string `json:"runtime,omitempty"`
}
SharedConfig opts a generation into consuming shared libraries instead of vendoring per-generation copies (ENG-1424 / shared-runtime dedup).
type TestRequestExample ¶
TestRequestExample describes one generated request used by generated_behavior_test.go.
type TestsData ¶
type TestsData struct {
// AuthWiring embeds the defensive APIOptions wiring booleans + cookie
// name + conditional import lines (shared with FuzzData via
// scanAuthWiring so the two emitters never drift). Embedded-struct
// promotion means all fields (HasAnyJWT, TypesImport, etc.) resolve
// directly in templates as .HasAnyJWT, .TypesImport, etc.
AuthWiring
HasRoutes bool
HasValidation bool // true when validRoute has a typed request schema with validators
FirstRequest TestRequestExample
FirstRouteMethod string // "GET", "POST", etc.
FirstPayloadValid string
FirstRoutePath string
FirstRouteAuth string // "", "api_key", "jwt"
FirstRouteMultipart bool // true when first route uses multipart body mode
// FirstSkipReason / ValidSkipReason name the schema facet whose sample
// the generator could not derive (ENG-4622). When set, the emitted test
// calls t.Skip with the reason instead of asserting against a request
// body the generated Valid() would reject.
FirstSkipReason string
ValidRequestValid TestRequestExample
ValidRequestInvalid TestRequestExample
ValidRouteMethod string // "GET", "POST", etc.
ValidMethodName string
ValidReqType string
ValidRespType string
ValidRoutePath string
ValidRouteAuth string // "", "api_key", "jwt"
ValidPayloadValid string
ValidPayloadInvalid string
ValidSkipReason string
HasTypedResp bool
ValidRespSchema string
}
TestsData is the top-level data for tests_api.go.tmpl.
type TypeField ¶
type TypeField struct {
GoName string
GoType string
JSONName string
IsFile bool // true for file upload fields (uses json:"-" tag)
Nullable bool // true when source schema set "nullable": true
// OptionalRef is true for a non-required singleton $ref. Such fields use
// pointers and omitempty so an absent nested object is not confused with a
// present object whose members have zero values.
OptionalRef bool
StoragePointer bool // true only for added indirection on a required by-value cycle edge
RedactInLog bool // true for direct or structurally reachable sensitive content
// WebauthnUserHandle records every WebAuthn ceremony/auth context in
// which this field is the request schema's "user_id" property. The
// emitter renders context-specific trust-boundary documentation instead
// of applying the registration-only JWT-subject warning to public
// authentication requests. Multiple flags may be true when routes reuse
// a request schema across ceremony contexts.
WebauthnUserHandle WebauthnUserHandleDocs
// DateTimeDoc is true when this field's source schema set
// `format:"date-time"`. The emitter renders a doc comment above the
// field noting it is an RFC3339 date-time string validated by Valid()
// (GAP-0080). The Go type stays string (not time.Time).
DateTimeDoc bool
// Secret is true when the source schema marked this field `secret:true`
// (directly or implied by `hash`). Transitive log redaction is tracked
// separately in RedactInLog and never expands HashSecrets traversal.
Secret bool
// HashAlgo is the resolved password-hashing algorithm for this field
// (e.g. "argon2id", "pbkdf2-sha256"). Empty when the field is not hashed.
// `hash:"default"` is resolved to a concrete algorithm at generation time
// (resolvePasswordHashDefault) before reaching the template, so the emitted
// hashx.Algorithm("...") literal is always concrete. A non-empty HashAlgo on
// a string field drives one HashSecrets() statement per field.
HashAlgo string
}
TypeField represents one struct field.
Nullable signals the source schema marked the property as nullable. Scalar fields encode that state with a pointer (for example, "*string"); collection fields retain their slice/map type so nil means absent and a non-nil empty value remains an explicit clear. The emitted JSON tag uses ",omitempty" so nil nullable fields are omitted from the wire. Generated Valid() checks for nullable scalar fields nil-guard the dereference so an absent field does not trigger validation.
type TypeSchema ¶
type TypeSchema struct {
Name string
Fields []TypeField
Validations []TypeValidation
// HasSecret is true when any field structurally reaches sensitive content,
// including named references and opaque descendants. The template emits a
// non-recursive, whole-field-redacting LogValue() slog.LogValuer method.
HasSecret bool
}
TypeSchema represents one generated struct + its Valid() method.
type TypeValidation ¶
type TypeValidation struct {
Ref string // declared target type for a typed child validation
Kind string // "minlength", "maxlength", "enum", "min_int", "max_int", "min_num", "max_num", "minitems", "maxitems", "pattern", "format_datetime", "item_constraints", "item_valid", "nested_array"
GoName string
JSONName string
Op string // comparison operator: "<", "<=", ">", ">="
IntVal int64 // for int comparisons and string length
NumVal string // for float comparisons (formatted with %v)
EnumValues []string // quoted enum values
Required bool // true when the field is in the schema's "required" array
Nullable bool // true when the field is pointer-typed
// Pattern is the raw RE2 regex for Kind=="pattern" (L-8). ReVar is
// the deterministic package-level variable name the template emits
// the compiled regexp under (e.g. "reDocReqDocumentID"), referenced
// by the MatchString guard in Valid().
Pattern string
ReVar string
// L-10 array-element (items.*) constraints. Populated only for
// Kind=="item_constraints"; the template emits a per-element loop
// applying these to each slice element, reusing the scalar error
// vocabulary (too short / too long / pattern mismatch / invalid
// enum value). ItemReVar names the package-level compiled regexp
// emitted for ItemPattern (same scheme as ReVar).
ItemPattern string
ItemReVar string
ItemMinLength *int64
ItemMaxLength *int64
ItemMinOp string
ItemMinValue string
ItemMaxOp string
ItemMaxValue string
ItemEnumValues []string
// L-45 nested arrays retain their public []jsontext.Value field type for
// compatibility, but Valid decodes each raw inner array and walks this
// recursive constraint tree. The decode type is derived from the supported
// array/scalar/$ref items schema, so malformed supported element shapes fail
// before handler dispatch.
NestedDecodeType string
NestedDecodeVar string
NestedRawVar string
Nested *NestedValidationNode
}
TypeValidation represents one validation check in a Valid() method.
When Nullable is true the emitted check guards a nil pointer dereference (e.g. `if v.X != nil && len(*v.X) > N`). For non-pointer fields the dereference is omitted so existing code paths are untouched.
type TypedPathParam ¶
type TypedPathParam struct {
Token string // path token as it appears in the route pattern, e.g. "profileId"
SchemaKey string // schema property key (json tag), e.g. "profile_id"
// Kind mirrors QueryScalarField.Kind: one of "int64", "float64", "bool",
// or "string" (the default/fallback). Selects which strconv.Parse* (if
// any) api.go.tmpl emits before assigning r.PathValue(Token) into
// req.<goFieldName(SchemaKey)>. Populated from the matched schema
// property's declared "type" (GENERATOR_BUGS.md L-54: previously always
// assigned the raw string PathValue() regardless of the destination
// field's actual Go type -- req.ID = r.PathValue("id") against an int64
// struct field failed to compile). A path param injected by
// extendTypedPathParamsForInjectedFields below (no declaring schema
// property) is always "string", matching injectTypesPathParamFields'
// GoType: "string" for those fields.
Kind string
}
TypedPathParam pairs a route path token (e.g. "profileId") with the corresponding schema property key (e.g. "profile_id"). The token is used as the argument to r.PathValue(); the schema key drives goFieldName() and the query-string fallback. Matching is done by normalizing both to lowercase with underscores stripped so camelCase tokens align with snake_case JSON tags.
type TypesData ¶
type TypesData struct {
Schemas []TypeSchema
NestedPatterns []NestedPattern
NeedsJSON bool // true when any field uses jsontext.Value (needs encoding/json/jsontext import)
NeedsJSONV2 bool // true when nested raw array elements must be decoded for recursive validation
NeedsErrors bool // true when any schema has a validation that calls errors.New directly (i.e. any Kind except "item_valid", which delegates to the element's Valid())
NeedsFileUpload bool // true when any field uses gen.FileUpload
NeedsRegexp bool // true when any field uses a pattern validation (regexp import) — L-8
NeedsTime bool // true when any field uses a date-time format validation (time import) — GAP-0080
NeedsValidationState bool // true when singleton-connected schemas share bounded traversal state
NeedsSort bool // true when selected typed maps require deterministic key traversal
GenPkg string // import path for gen package (for FileUpload)
// NeedsContext is true once any struct is emitted (every struct gets a
// HashSecrets(ctx context.Context) method), so the template imports context.
NeedsContext bool
// NeedsSlog is true when any schema has a secret field (LogValue uses slog).
NeedsSlog bool
// NeedsHashx is true when any field is hash-marked (HashSecrets calls hashx).
NeedsHashx bool
// HashxPkg is the import path for the hashx package, set when NeedsHashx.
HashxPkg string
// PepperEnv, when non-empty, is the environment variable name holding the
// server-side password pepper (security.password_hash.pepper_env). When set
// the template emits a package-level `var passwordPepper = []byte(os.Getenv(
// "<PepperEnv>"))` and threads hashx.WithPepper(passwordPepper) into every
// generated hashx.Hash call so the pepper applies uniformly across surfaces.
PepperEnv string
}
TypesData is the top-level data for types.go.tmpl.
type UIAuth ¶
type UIAuth struct {
Mode string
Composite string
DNF [][]string
Roles []string
Scopes []string
Attributes map[string]string
CSRF string
MTLSRequired bool
MTLSEKU bool
MTLSIssuers []string
Webauthn string
Webhook bool
// WebhookSignatureHeader is the HTTP header carrying the HMAC signature
// (e.g. "X-Signature" or "X-Hub-Signature-256"). Never empty when Webhook
// is true; defaults to "X-Signature" when the config omits the field.
// Safe to display: header names are public configuration, not secrets.
WebhookSignatureHeader string
// WebhookSecretEnv is the name of the environment variable that holds the
// HMAC secret (e.g. "APIC_WEBHOOK_STRIPE_SECRET"). Only the ENV VAR NAME
// is projected — the secret VALUE is never stored or emitted. Safe to show.
WebhookSecretEnv string
RateLimit int
Sensitivity string
Surface string
}
UIAuth carries the complete auth policy for one endpoint.
type UIEndpoint ¶
type UIEndpoint struct {
Method string
Path string
MethodName string
Description string
Auth UIAuth
RequestSchema string
ResponseSchema string
PathParams []string
ResponseKind string
}
UIEndpoint describes one React-exposed REST route.
type UIGraphQLOp ¶
type UIGraphQLOp struct {
Field string
MethodName string
Kind string // "query" | "mutation" | "subscription"
Auth UIAuth
ReqSchema string
RespSchema string
}
UIGraphQLOp describes one React-exposed GraphQL operation (query or mutation).
type UIMcpTool ¶
type UIMcpTool struct {
Name string
Title string
Description string
ParamsSchema string
ResultSchema string
}
UIMcpTool describes one MCP-exposed tool.
type UISchema ¶
type UISchema struct {
Name string
Fields []UISchemaField
}
UISchema is a UI-facing mirror of a schema definition.
type UISchemaField ¶
type UISchemaField struct {
Name string
Type string
Required bool
Secret bool
Hash string
Example string // always "" for secret/hash fields
}
UISchemaField is one field in a UISchema.
type UIWSEndpoint ¶
type UIWSEndpoint struct {
Path string
Proto string
MethodName string // title-cased; client method is "connect" + MethodName
Auth UIAuth
MessageSchema string
}
UIWSEndpoint describes one React-exposed WebSocket endpoint.
type WSAuthPolicy ¶
type WSAuthPolicy struct {
AllowAuthorizationHeader *bool `json:"allow_authorization_header,omitempty"`
AllowSubprotocolBearer *bool `json:"allow_subprotocol_bearer,omitempty"`
AllowSubprotocolSignature *bool `json:"allow_subprotocol_signature,omitempty"`
AllowQueryBearer *bool `json:"allow_query_bearer,omitempty"`
AllowQuerySignature *bool `json:"allow_query_signature,omitempty"`
}
WSAuthPolicy controls which websocket auth transports the generated server will accept for a route.
type WSData ¶
type WSData struct {
Paths PackagePaths
NeedsSecure bool
Endpoints []WSEndpoint
// HasAnyJWT is true when at least one WebSocket endpoint declares
// auth: "jwt". GAP-0076 bootstrap-fail guard fires at registration
// time when this is true and opts.AuthJWT is nil.
HasAnyJWT bool
// HasAnyAPIKey mirrors HasAnyJWT for auth: "api_key" endpoints.
HasAnyAPIKey bool
// NeedsTypesImport is true when at least one endpoint has a declared
// messageSchema eligible for validation (G-04), so the types package
// import is only emitted when actually referenced (mirrors MCPData's
// NeedsTypesImport, GQL-IMPORTS-MCP).
NeedsTypesImport bool
// ValidatedSchemas is the deduplicated, sorted list of messageSchema
// names that need a WsValidate<Schema> helper emitted. Deduplicated
// across endpoints because two WS routes may declare the same
// messageSchema (e.g. the same event type exposed on two paths) --
// emitting one func per ENDPOINT would produce duplicate Go function
// declarations when they share a schema name.
ValidatedSchemas []string
}
WSData is the top-level data for ws.go.tmpl.
type WSEndpoint ¶
type WSEndpoint struct {
Path string
MethodName string
Proto string
MessageSchema string
Auth string
Surface string
Sensitivity string
PingInterval int
MaxFrame int
MaxMessage int
Protocols []string
Origins []string
AllowAnyOrigin bool
AllowEmptyOrigin bool
SameOriginOnly bool
AuthPolicyLit string
ExposeClient bool
ExposeReact bool
// GAP-0075 connection-cap fields. Zero means "use default";
// negative disables the knob (matches the wsx.Limiter contract).
MaxConnections int
MaxConnectionsPerIP int
UpgradeRatePerMin int
// HasMessageValidation (G-04) is true when this endpoint declares a
// messageSchema eligible for a Read<MethodName> validated-read wrapper:
// MessageSchema is set, Proto is not "binary" (a binary frame is not
// JSON and has no schema to decode against), and the module path is
// known (mirrors MCPData's hasModule gate -- types.X only resolves once
// a real module path is available). When false, ws.go.tmpl emits no
// wrapper for this endpoint even if MessageSchema is set.
HasMessageValidation bool
}
WSEndpoint represents one WebSocket endpoint.
type WSFuzzData ¶
type WSFuzzData struct {
HasEndpoints bool
// TypesImport is the tab-indented, quoted import line for the types
// package (e.g. "\t\"devnw.dev/apic/types\""). Empty when no WS
// endpoint in the config carries a messageSchema.
TypesImport string
Endpoints []WSFuzzEndpoint
}
WSFuzzData is the top-level data for tests_fuzz_ws.go.tmpl.
type WSFuzzEndpoint ¶
type WSFuzzEndpoint struct {
FuncName string
Path string
SchemaType string // "types.<SchemaName>" as a Go type expression
// Seeds are Go string literals (already quoted via strconv.Quote) ready
// to drop into f.Add(<lit>). Deterministic, deduplicated.
Seeds []string
}
WSFuzzEndpoint is one WS fuzz target: a Go func name, the sanitized path used in the func name, the types.* Go type for the message schema (e.g. "types.EchoMsg"), and the corpusgen-derived seed corpus.
type WebauthnContract ¶
type WebauthnContract struct {
Ceremony string `json:"ceremony,omitempty"`
Phase string `json:"phase,omitempty"`
Attestation string `json:"attestation,omitempty"`
AuthenticatorAttachment string `json:"authenticator_attachment,omitempty"`
UserVerification string `json:"user_verification,omitempty"`
Discoverable bool `json:"discoverable,omitempty"`
}
WebauthnContract captures per-operation WebAuthn ceremony metadata that the generator can validate before it emits handlers or clients. The field set mirrors the FIDO2 / WebAuthn Level 3 surface that the OIDC reference server (devnw.dev/oidc) drives through registration and authentication endpoints.
type WebauthnUserHandleDocs ¶
type WebauthnUserHandleDocs struct {
Registration bool
PublicAuthentication bool
JWTAuthentication bool
CookieAuthentication bool
CompositeJWTAuthentication bool
NonJWTAuthentication bool
UnclassifiedCustomRoute bool
}
WebauthnUserHandleDocs identifies the generated-handler semantics for a request schema's user_id field. It is deliberately a set of flags rather than an enum because more than one WebAuthn route may reuse the same schema.
type WebhookContract ¶
type WebhookContract struct {
// SecretRef names the entry in SecurityConfig.Webhooks that the
// runtime resolves at boot to find the env var holding the HMAC
// secret. Required. Must be a valid Go identifier (letters,
// digits, underscores; not leading-digit). The validator rejects
// any other shape.
SecretRef string `json:"secret_ref"`
// Algs is the HMAC algorithm allowlist. Defaults to ["sha256"]
// when empty. Recognised values: "sha256", "sha512". Other values
// are rejected by validateWebhookContracts.
Algs []string `json:"algs,omitempty"`
// WindowSeconds bounds the |now - timestamp| skew. Zero defaults
// to 300 (5 minutes) per industry-standard webhook windows
// (Stripe, GitHub, PagerDuty).
WindowSeconds int `json:"window_seconds,omitempty"`
// MaxBodyBytes caps the body the verifier will hash. Zero defaults
// to the route's overall MaxBodyBytes (or 1 MiB if neither is set).
MaxBodyBytes int `json:"max_body_bytes,omitempty"`
// SignatureHeader, TimestampHeader, IDHeader override the default
// header names ("X-Signature", "X-Timestamp", "X-Webhook-Id").
// Provider-specific schemes (Stripe, GitHub) use these overrides;
// generic deployments leave them empty.
SignatureHeader string `json:"signature_header,omitempty"`
TimestampHeader string `json:"timestamp_header,omitempty"`
IDHeader string `json:"id_header,omitempty"`
}
WebhookContract captures per-operation inbound-webhook verification settings. When set on an APIOperation with Auth == "webhook", the generator emits a per-route VerifyWebhook call that runs BEFORE the user handler — the user only sees a request that has already been authenticated, time-windowed, replay-protected, and body-capped.
type WebsocketEndpoint ¶
type WebsocketEndpoint struct {
Path string `json:"path"`
// Name is the optional per-route override for the generated
// WSServerInterface method name. When empty the method name derives
// from the terminal path segment (titleFirst(safeName(Path))), which
// is generic and can collide when another route's segment later
// matches — forcing a rename that breaks the existing hand-written
// handler on regen. Set Name to pin a stable method. Validated as a
// Go identifier and required globally unique (no two WS routes may
// map to the same method). Mirrors APIOperation.Name. GENERATOR_BUGS.md L-31.
Name string `json:"name,omitempty"`
Proto string `json:"proto"`
MessageSchema string `json:"messageSchema"`
Auth string `json:"auth"`
// JWTAlg is the JWS algorithm to use when Auth resolves to "jwt".
// Recognized values: "HS256" (legacy), "RS256", "RS384", "RS512",
// "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "EdDSA". Empty
// defaults to the legacy HS256 path. When SecurityConfig.FIPS is true
// the validator rejects HS256 (and every symmetric alg) because the
// FIPS-validated JWT signing path must use a hardware-backed
// RSA/ECDSA key (Plan 04 HSM). Mirrors APIOperation.JWTAlg -- a
// WebSocket route previously had no way to declare an asymmetric alg,
// so any jwt route was unconditionally forbidden under FIPS
// (SEC-0068/#322 fix, round 2).
JWTAlg string `json:"jwt_alg,omitempty"`
PingInterval int `json:"ping_interval_ms"`
Protocols []string `json:"protocols"`
Origins []string `json:"origins"`
MaxFrameBytes int `json:"max_frame_bytes"`
MaxMessageBytes int `json:"max_message_bytes"`
// AllowAnyOrigin opts the endpoint into accepting any Origin header
// value (including unrelated public origins). Use only for endpoints
// that are genuinely origin-agnostic -- server-to-server hops, CLI
// tooling, public realtime feeds. APPSEC-2.
AllowAnyOrigin bool `json:"allow_any_origin,omitempty"`
// AllowEmptyOrigin opts the endpoint into accepting requests with no
// Origin header. Use for non-browser callers (CLI / native clients).
// APPSEC-2.
AllowEmptyOrigin bool `json:"allow_empty_origin,omitempty"`
// SameOriginOnly opts the endpoint into the CSWSH defense-in-depth
// pattern: the wsx layer synthesises a same-origin entry at request
// time from r.TLS + r.Host and treats it as part of the effective
// allowlist, additive with `origins`. The SPA on the API origin
// always works; cross-origin attackers are rejected even if they
// hold a stolen bearer token. Cross-origin SPA deployments (CDN
// front-end on a separate host) still work by listing the trusted
// origins in `origins`. APPSEC-2 / geode-ui SEC issue 265.
SameOriginOnly bool `json:"same_origin_only,omitempty"`
// MaxConnections is the per-pod concurrent-WS cap for this
// endpoint. Zero defaults to wsx.DefaultMaxConnections (1024);
// negative disables the knob. GAP-0075.
MaxConnections int `json:"max_connections,omitempty"`
// MaxConnectionsPerIP caps concurrent WS connections from a single
// remote IP. Zero defaults to wsx.DefaultMaxConnectionsPerIP (4);
// negative disables. GAP-0075.
MaxConnectionsPerIP int `json:"max_connections_per_ip,omitempty"`
// UpgradeRatePerMin caps NEW upgrades from a single remote IP per
// rolling minute. Zero defaults to wsx.DefaultUpgradeRatePerMin
// (60); negative disables. GAP-0075.
UpgradeRatePerMin int `json:"upgrade_rate_per_min,omitempty"`
SecurityContract
AuthPolicy *WSAuthPolicy `json:"auth_policy,omitempty"`
}
WebsocketEndpoint models one websocket surface.
Source Files
¶
- auth_resolve.go
- authexpr_parse.go
- client_templates.go
- client_templates_gql.go
- client_templates_ui.go
- config_model.go
- config_source.go
- config_validate.go
- config_validate_collisions.go
- config_validate_controlplane.go
- config_validate_fips.go
- config_validate_graphql.go
- config_validate_health.go
- config_validate_mtls.go
- config_validate_ownership.go
- config_validate_password.go
- config_validate_routes.go
- config_validate_schema.go
- doc.go
- generate.go
- generate_clients.go
- generate_clients_python.go
- generate_clients_rust.go
- generate_clients_zig.go
- generate_emit.go
- generate_fuzz.go
- generate_graphql.go
- generate_module.go
- generate_openapi.go
- generate_output.go
- generate_plan.go
- generate_sample.go
- generate_tests.go
- gomod_floor.go
- mcp_impl.go
- mcp_inputschema.go
- mtls_runtime.go
- naming.go
- python_types.go
- runtime_emit.go
- runtime_format.go
- rust_types.go
- schema_fragment.go
- schema_imports.go
- schema_pointer.go
- schema_shapes.go
- security_contracts.go
- security_contracts_oidc.go
- sortedkeys.go
- spechash.go
- templates.go
- templates_api.go
- templates_gql.go
- templates_mcp.go
- templates_types.go
- templates_ws.go
- warnings.go
- zig_helpers.go