Documentation
¶
Overview ¶
Package validation exposes in-process assertion counters that the celeris engine and middleware bump under the `validation` build tag.
Production binaries (built without the tag) compile against the no-op stubs in disabled.go, so the counters and the unix-socket endpoint are stripped at compile time — no allocations, no atomic adds, no goroutines. Validation builds (-tags=validation) compile against assertions.go and endpoint.go, exposing the counters as atomic.Uint64 and serving a JSON snapshot over /tmp/celeris-validation.sock.
External property-test harnesses read the socket on every poll to feed property predicates. The counter shape is stable across both build modes via the Counters struct and the Snapshot function — the only thing that changes is whether reads return live atomics or the zero value.
Call-pattern convention ¶
Two call patterns coexist and SHOULD be used consistently:
- For unconditional event counts (panic recovered, etc.) call the RecordX() helper defined in hooks.go (e.g. validation.RecordPanic()). Helpers carry no build tag — they delegate to Counter.Add, which inlines to nothing under !validation.
- For predicate-bearing checks (a value violates an invariant) call the per-package validate*() helper defined in that package's validation_check.go (under //go:build validation) with a no-op stub in validation_default.go (under //go:build !validation). Examples: middleware/jwt.validateAdmission, middleware/ratelimit.validateBucket.
Bare validation.Counter.Add(1) outside a validate*() helper is a bug — use a RecordX() helper instead.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RecordPanic ¶
func RecordPanic()
RecordPanic increments the recovered-panic counter. Defined without a build tag so callers reach the production no-op (Counter.Add is a zero-cost stub) under regular builds and the live atomic under -tags=validation.
Call-pattern convention:
- Bare Counter.Add(1) is reserved for predicate-bearing validate*() helpers in the validation_check.go files (each middleware/engine package owns its own helper). Those helpers are split across build tags so the production binary inlines to nothing.
- Unconditional event counts (panic recovered, etc.) go through a thin RecordX() helper in this file. Keeping the convention consistent makes the call-site grep a one-liner: any direct Counter.Add outside a validate*() function is a bug.
Types ¶
type Counter ¶
type Counter struct{}
Counter is the no-op stub installed in production builds. It carries no state and inlines to nothing. Importers (engine/iouring, middleware/...) call validation.X.Add(1) without a build-tag guard at the call site — production simply discards the increment. Add returns nothing so static-check tools cannot flag callers for ignoring an unused return value.
var ( PanicCount Counter RatelimitTokenViolations Counter SessionOwnerMismatches Counter JWTLateAdmits Counter IouringSQECorruptions Counter )
PanicCount and friends are the stub instances. Same identifier surface as the wrapped counter vars in assertions.go so call sites compile identically in both modes.
type Counters ¶
type Counters struct {
PanicCount uint64 `json:"panic_count"`
RatelimitTokenViolations uint64 `json:"ratelimit_token_violations"`
SessionOwnerMismatches uint64 `json:"session_owner_mismatches"`
JWTLateAdmits uint64 `json:"jwt_late_admits"`
IouringSQECorruptions uint64 `json:"iouring_sqe_corruptions"`
}
Counters is the JSON-serializable shape returned by Snapshot.
Defined unconditionally so external callers (probatorium's validator-checker, callers in observe.Snapshot) can reference the type regardless of build tag. In a production build the values are always zero; in a validation build the values are loaded from the per-counter atomics in assertions.go.