Documentation
¶
Overview ¶
Package apicontract is the durable enforcement core of the Culvert OpenAPI program. It is deliberately decoupled from package main: it operates on a plain []Route slice (supplied by the caller) plus two on-disk artifacts — the OpenAPI contract and the route-classification manifest — so its logic is unit-testable in isolation with fixtures and reused by the live-router gate in package main.
The three enforcement surfaces:
LoadSpec — parse + validate the OpenAPI document (Gate 1). StyleLint — organizational lint rules over the contract (Gate 2). CheckCoverage — bijective route⇄manifest⇄spec coverage (Gate 3). CheckExemptions — no expired route exemption survives (Gate 3, time axis).
Everything is offline and Go-native (getkin/kin-openapi); no network, no Node, no external binary — it runs inside `go test`.
Index ¶
- Constants
- func BundleJSON(specPath string) ([]byte, error)
- func CheckCoverage(routes []Route, spec *Spec, c *Classification) []string
- func CheckExemptions(c *Classification, now time.Time) []string
- func Ext(op *openapi3.Operation, key string) string
- func RenderDocsHTML(specPath string, allowedVis ...string) ([]byte, error)
- func RenderInventory(manifestPath string) ([]byte, error)
- func StyleLint(spec *Spec) []string
- type ClassRow
- type Classification
- type Exemption
- type Operation
- type Route
- type Spec
Constants ¶
const MaxExemptionHorizonDays = 270
MaxExemptionHorizonDays caps how far in the future an exemption may be set, so a route cannot dodge documentation indefinitely by pinning a far-future date.
Variables ¶
This section is empty.
Functions ¶
func BundleJSON ¶
BundleJSON converts the YAML contract to canonical, deterministic JSON.
Determinism is load-bearing (Gate 8): the YAML is decoded into a generic map and re-encoded with encoding/json, which sorts object keys lexicographically, so the same input always yields byte-identical output regardless of authoring order. No external tool, no network — pure Go, air-gap safe.
func CheckCoverage ¶
func CheckCoverage(routes []Route, spec *Spec, c *Classification) []string
CheckCoverage enforces the bijection route ⇄ manifest and the coverage manifest ⇄ spec. It returns a sorted list of human-readable violations (empty == pass).
- every live route+method has exactly one manifest row (no unclassified route)
- every manifest row maps to a live route+method (no stale row)
- every documented row appears as a spec operation
- every spec operation maps to a documented row (no phantom operation)
- row/exemption/documented consistency
The time axis (expired exemptions) is CheckExemptions.
func CheckExemptions ¶
func CheckExemptions(c *Classification, now time.Time) []string
CheckExemptions fails when any exemption has expired OR is set beyond the maximum horizon relative to `now` (Gate 3, time axis). Expiry is YYYY-MM-DD and the route stays valid THROUGH the expiry date (the +24h makes the date inclusive); it fails the day after.
func RenderDocsHTML ¶
RenderDocsHTML renders the offline, self-contained HTML documentation for the contract at specPath. When allowedVis is non-empty, only operations whose x-culvert-visibility is in that set are included (the public build filter).
func RenderInventory ¶ added in v1.0.142
RenderInventory produces the deterministic API-inventory markdown from the classification manifest. Totals and per-domain counts are computed from the manifest rows; nothing is hand-entered.
Types ¶
type ClassRow ¶
type ClassRow struct {
Route string `yaml:"route"`
Method string `yaml:"method"`
Handler string `yaml:"handler"`
Domain string `yaml:"domain"`
Visibility string `yaml:"visibility"`
MinRole string `yaml:"min_role"`
Mutating bool `yaml:"mutating"`
AuditExpected bool `yaml:"audit_expected"`
DangerLevel string `yaml:"danger_level"`
Documented bool `yaml:"documented"`
OpenAPIPath string `yaml:"openapi_path"` // optional; defaults to Route
Exemption *Exemption `yaml:"exemption"`
}
ClassRow is one classification manifest entry.
type Classification ¶
type Classification struct {
Version int `yaml:"version"`
BaselineExemptionExpiry string `yaml:"baseline_exemption_expiry"`
Rows []ClassRow `yaml:"routes"`
}
Classification is the parsed route-classification manifest.
func LoadClassification ¶
func LoadClassification(path string) (*Classification, error)
LoadClassification parses the manifest (goccy/go-yaml resolves the YAML anchor used for the shared baseline expiry).
type Exemption ¶
type Exemption struct {
Owner string `yaml:"owner"`
Reason string `yaml:"reason"`
SecurityClass string `yaml:"security_class"`
Expires string `yaml:"expires"`
}
Exemption records why a route is not (yet) in the contract.
type Route ¶
type Route struct {
Path string
Method string // "GET" | "POST" | ... | "*" (MethodAny)
Handler string
Domain string
Public bool
MinRole string
Muta bool
Audit bool
}
Route is a single registered admin-API route+method, as known to the live router. package main adapts its uiRoutes table into this shape.
type Spec ¶
Spec wraps a validated OpenAPI document.
func LoadSpec ¶
LoadSpec loads and validates the OpenAPI document (Gate 1: syntax, $ref resolution, schema correctness, duplicate operationId, security-scheme refs).
func (*Spec) ValidateJSONRequest ¶
ValidateJSONRequest validates a request body against the operation's application/json request schema (Gate 4).
func (*Spec) ValidateJSONResponse ¶
ValidateJSONResponse validates a real handler's JSON response body against the operation's response schema for `status` (Gate 5). A missing operation or status is a hard error — the contract must describe what the handler returns.