RBE DOCS

/

Source Roles

.route — Route REL

LIVE
BRANCH · MAIN
doc/x.route/README.md
SOURCE ↗

Route REL defines HTTP entrypoints. It uses the common REL grammar while keeping HTTP authority narrow and explicit.

Structure

text
:import[json, response]

function helper(value) {
    return { ok: value != null };
}

class Route {
    async get(req) {
        return response.json(helper(req.query.value), 200);
    }
}

Current HTTP method parsing includes:

text
get post put delete patch head options

Additional methods/wildcard handlers may be added later.

File routing

Filesystem routing is active, including dynamic/catch-all parameters:

text
api/users/[uid].route       -> /api/users/:uid
api/files/[...path].route   -> /api/files/*path

Two routes whose shapes differ only by parameter name collide at boot; [id] and [slug] are not treated as distinct URL shapes.

Request snapshot

A declared route parameter receives the bounded request snapshot:

text
request.method
request.path
request.originalUrl
request.params
request.query
request.headers
request.cookies
request.body
request.rawBody
request.ip
request.forwardedFor
request.protocol
request.host
request.userAgent
request.contentType
request.contentLength

JSON bodies are decoded directly into REL values with strict object identity. Duplicate object keys are rejected with HTTP 400 at any nesting depth instead of using last-key-wins semantics. Because REL numbers use f64, integer-valued JSON numbers must stay within -9007199254740991..=9007199254740991; larger integers are rejected with HTTP 400 instead of being silently rounded, including when nested inside arrays or objects. Finite fractional JSON numbers remain valid. Other current v1 body types are exposed as UTF-8 strings; invalid UTF-8 returns HTTP 400 rather than being replacement-decoded. Invalid JSON returns HTTP 400 and configured body-limit violations return HTTP 413 before REL execution. Route REL v1 has no first-class binary request-body value type.

Forwarded client/protocol data is trusted only when trustedProxyHeaders policy is enabled. Otherwise request.ip is derived from the socket peer. When proxy trust is enabled, every textual X-Forwarded-For field-line is consumed in HeaderMap order, comma-delimited entries are flattened in order, every token must be a valid IPv4/IPv6 literal, values are canonicalized, and request.ip uses the first resulting entry; malformed, empty-present, or non-text forwarding metadata fails with HTTP 400 instead of being ignored. X-Forwarded-Proto is handled across all field-lines with the same ordering rules: every supplied token must be http or https (ASCII case-insensitive), the first token becomes the canonical lowercase request.protocol, and malformed/empty present proxy metadata returns HTTP 400 instead of silently becoming http.

Current transport behavior joins repeatable duplicate request headers, while semantic singleton headers Content-Type, Host, User-Agent, and Content-Length are rejected when repeated so the structured snapshot cannot disagree with HTTP interpretation. When Host is present it must also parse as an HTTP authority (host or host:port, including bracketed IPv6); malformed authority text returns HTTP 400 instead of being exposed through request.host. Content-Length, when present, must be non-empty ASCII decimal digits and no greater than 9007199254740991, the largest integer REL can represent exactly; malformed or lossy values return HTTP 400 instead of becoming null or a rounded number. Credential-bearing Authorization and Proxy-Authorization headers are also fail-closed singletons during request snapshot construction: repeated field-lines (even identical ones) return HTTP 400 before field resolution or handler execution, rather than being comma-joined into a synthetic credential. Single credential values, including embedded commas, remain unchanged. There is no first-class binary-body value type.

Responses

A plain REL return value remains a JSON 200 OK response for compatibility. Import response for explicit HTTP behavior:

text
response.json(body, status?)
response.text(text, status?)
response.html(html, status?)
response.status(status, body?)
response.noContent()
response.redirect(location, status?)
response.withHeader(responseValue, name, value)
response.cookie(responseValue, name, value, options?)
response.clearCookie(responseValue, name, options?)

Header/cookie values are validated again at the Rust HTTP boundary. Cookie options include path, domain, maxAge, httpOnly, secure, and sameSite.

Streaming responses, first-class binary responses, file sends, and SSE remain later transport work.

Imports and authority

Route-safe built-in concepts include the currently registered HTTP-side capabilities such as net, json, crypto, time, http, request, log, security, response, and read-only private, subject to each capability's implemented operation set.

Important deny rules:

  • Route REL cannot directly import service:*.
  • Route REL cannot import/read shared ENV by default.
  • Route REL cannot import quickDB.
  • Route REL cannot directly import Video Manager (vm / video-manager).

The normal route-to-service shape is Route -> Module -> Service.

Server middleware

Global HTTP middleware/policy is configured by server.server, not imported package-by-package into each route. Server REL lowers middleware into an ordered MiddlewarePlan; the backend combines applicable plan-driven settings with its native security stack.

See ../server.server/.

Runtime Image execution

Route sources are discovered/parsed during boot and linked as immutable RouteFile program snapshots in the Runtime Image. Normal HTTP execution does not reread the .route source on each request.

RELC also attempts native WebAssembly lowering for every route.

Native subset today

A Route can currently compile to native WASM when all of the following are true:

  • it has exactly one HTTP method;
  • it has no imports;
  • it has no helper functions;
  • the method body is exactly one return statement;
  • that returned expression is a static JSON literal/value.

Example:

text
class Route {
    get(req) {
        return { ok: true, version: 1 };
    }
}

The compiler emits deterministic WASM using the RBE output ABI and stores the artifact SHA-256 in the Runtime Image. Dynamic request expressions, helpers, imports, multiple methods, and other unsupported constructs are recorded with an explicit interpreter-fallback reason.

The public HTTP dispatcher still retains the immutable REL evaluator path while native artifact dispatch coverage is expanded. “WASM artifact exists” and “this request is dispatched through WASM” are therefore intentionally documented as separate things.

Server status gate

The active Runtime Image's ServerStatus is checked for normal requests:

  • online — normal traffic allowed.
  • readonly — GET/HEAD/OPTIONS allowed; mutating requests rejected.
  • maintenance, draining, offline — normal traffic returns service-unavailable behavior.

Health/admin/maintenance control-plane routes are exempt so operators can inspect/recover the server.

Thin-route pattern

Keep reusable logic in modules:

text
:import[module&users]

class Route {
    async get(req) {
        return users.findById(req.params.id);
    }
}

See ../x.module/.

CANONICAL DOCUMENTATION

This page follows the RBE main/doc tree and is refreshed by the Kastrick documentation bridge.

main · 5 min revalidate