-
Notifications
You must be signed in to change notification settings - Fork 0
Startup Validation
The extension implements
rextension.RouteValidator.
The framework calls it once, when the route table is built — after every
extension has declared, before any listener binds.
A security requirement that cannot be met stops the deployment.
security: route DELETE /users/{id} requires scheme "jwt", which is not registered
Previously this produced a 500 on every request to that route, discoverable only by making one.
security: route DELETE /users/{id} declares roles for scheme "jwt",
which does not implement RoleEnforcer (or reports SupportsRoles() == false)
This is the serious one. Previously the route was served to every authenticated caller, with no diagnostic anywhere — an endpoint marked "admin only" that was not.
It is not detectable by an interface assertion either: BearerScheme implements
RoleValidator, and its implementation returned nil when its inner validator
did not know about roles. SupportsRoles is what moves the answer to where it
is knowable. See Roles and Scopes.
A key in RequiredRoles naming a scheme absent from RequiredSchemes is a
mistake, not an ignorable entry.
This is the only point at which every route and every scheme are both known.
-
Not at
OnInitialize— the routes do not all exist yet, and an extension cannot see another extension's routes at all. -
Not at
OnReady— the listeners are already accepting. - Not per request — a security requirement that cannot be met should stop a deployment, not degrade quietly inside one.
ErrInsecureConfiguration: routes declare requirements the configured schemes
cannot enforce:
- GET /admin/users requires scheme "jwt", which is not registered
- DELETE /users/{id} declares roles for scheme "apikey", which cannot enforce them
- POST /orders declares scopes for scheme "basic", which cannot enforce them
A startup error that names one of five misconfigured routes gets fixed one deployment at a time; one that names five gets fixed once.
The sentinel is security.ErrInsecureConfiguration.
err := security.ValidateRoutes(registry, routes)Useful in a test that asserts your route table is coherent without booting the application:
func TestRoutesAreEnforceable(t *testing.T) {
reg := security.NewSchemeRegistry(schemes()...)
if err := security.ValidateRoutes(reg, routes()); err != nil {
t.Fatal(err)
}
}That is worth having: it turns "did anyone remember to register the scheme this new route needs?" into a unit test rather than a deployment.
- That your validator is correct. It checks that a validator claiming to enforce roles exists, not that it enforces them properly.
- That a role name is spelled right. Role strings are opaque to the framework.
- That a route should be secured. A route that declares nothing is public, and that is indistinguishable from a route someone forgot to secure.
For the last one, consider your own RouteValidator — the framework runs every
extension's, and your application can be one:
type policyCheck struct{ rex.DefaultExtension }
func (p *policyCheck) ValidateRoutes(routes []rextension.RouteInfo) error {
var problems []error
for _, ri := range routes {
if ri.Router != rex.DefaultRouterName {
continue // operational listeners are exempt
}
sec, ok := ri.Route.(security.SecuredRoute)
if !ok || len(sec.RequiredSchemes()) == 0 {
if !isPublic(ri.Route.Path()) {
problems = append(problems, fmt.Errorf(
"%s %s is public and not on the allowlist",
ri.Route.Method(), ri.Route.Path()))
}
}
}
return errors.Join(problems...)
}An explicit allowlist of public paths, checked at boot, is the only mechanism that catches "someone added an endpoint and forgot".
rextension-security — authentication, authorization and CSRF for Rex · MIT · © 2026 Kryovyx