Expand description
The compiled route table (R898-F1 / W348 §2.2) — ONE ordered artifact per domain, rendered to both front doors.
§What this is, and what it replaces
DomainConfig.routes is the declaration; this module is the compilation of
that declaration into the thing a door can execute: {path, mode, resolved origin, headers, auth} per entry, in manifest order. It is the widening of
R746/R749-F3’s route_headers_json, which compiled the same table down to
its header column and is live on yah.dev today (x-route-header-probe).
One producer, two renderers is the whole point. A per-path capability
built into one door has to be built a second time the moment the domain
flips front_door, and it does not merely cost twice — it evaporates, as
/api/releases did when yah.dev went grey and MESOFACT_BACKEND_ORIGIN
stopped executing with nothing anywhere reporting it (W348 §0.3, §2.3).
§The resolved origin is the only genuinely new datum
Everything else an entry carries is already in the manifest. The origin is not, and it is placement-time:
| mode | resolves to | by |
|---|---|---|
static | the component’s asset origin (<cdn_base>/<service>/<env>) | CdnPlacement::asset_origin |
static + bucket | the bucket’s Worker R2 binding name — no placement | r2_binding_name |
backend | the deployed unit’s address, keyed by its mesh identity | inner_door::component_workload_ident |
redirect | nothing — it carries its own target + status | — |
The declared origin field on RouteMode::Backend is deliberately NOT
the answer: it is the Worker arm’s fetch() target and nothing reads it
under passway (.yah/domains/api-noisetable-com.toml says so in its own
words, in the noisetable camp). A table that echoed it would be wrong on the
door that actually serves production.
So placement arrives as a RoutePlacement — the same shape
InnerDoorPlan::routes_file
already uses for upstream addresses, for the same reason: which node a unit
landed on is not config. An unresolved origin is refused, never skipped
— a dropped entry does not 503, it falls through to a shorter match (the
catch-all, usually) and serves the wrong thing with a 200.
§Ordering and matching are pinned, not chosen here
Manifest order, first match wins, no merging across rules — R746 pinned
it in TS (applyRouteHeaders, oss/mesofact/packages/mesofact-edge/src/router.ts)
and R749-F3 carried it into Rust (mesofact::route_headers). One path has
one entry, decided where the route was decided. The manifests already
document it as their contract: noisetable-com.toml puts /app/* above
/* precisely because of it.
matches_route_pattern is segment-aware for the same reason both of those
are: a bare starts_with routes /application to the /app entry. It is a
third copy of a two-sided wire format rather than a call into either — the
Worker is TypeScript and mesofact is a separately-released crate in
another workspace that cloud does not depend on — and it is handled the
way this repo already handles that risk for passway’s
path_route::mount_from_component: one producer, and a test pinning the
shared cases.
§Why the header projection is still the wire value
RouteTable::headers_json is the header column of this same table, byte-
identical to what DomainConfig::route_headers_json emits — and that string,
not the full table, is still what ROUTE_HEADERS / MESOFACT_ROUTE_HEADERS
carry. Widening those bindings is R898-F2 (passway) and R898-F3 (Worker).
Shipping the wide table into them here would change deployed behaviour
before either consumer can read it: every headerless route becomes a table
entry, so app.yah.dev, chat.yah.dev and scrabcake.net.yah.dev — each a
single route declaring no headers, each "[]" today — would start setting
MESOFACT_ROUTE_HEADERS on their deploys.
§A backend entry carries its prefix rewrite (R898-F3)
The two backend seams that exist today are not identity proxies:
/api/issues/42 reaches the issue tracker as /issues/42, and
/api/releases/v1.2.3 reaches the almanac as /releases/v1.2.3. Those
rewrites lived inside the Worker’s two hardcoded if blocks (R455-T4),
which is precisely what R898-F3 deleted — so an origin-only entry would have
silently started proxying /api/issues to <origin>/api/issues and changed
the upstream contract with no diff naming it.
So ResolvedRouteMode::Backend carries an optional RouteRewrite:
{from, to}, the public prefix the matched path carries and what it becomes
at the origin. It is route DATA, declared as origin_path on
RouteMode::Backend — not an escape hatch, and not a per-door special
case. The alternative was restating the two routes so the public path equals
the origin path, which this repo cannot do: it does not own either upstream’s
path layout, and both are live contracts.
§R560-F13 folds in here
“One Worker domain fronting several R2 buckets” is this table restricted to
static entries whose sources differ. Same artifact, narrower slice — W279
Gap C predicted the fold (“path-prefix -> bucket is just a route list”).
A bucket route does NOT resolve to an HTTP origin, and that is the one
deliberate asymmetry: the buckets it exists for (noisetable-releases) have
no public hostname to fetch, and a bucket root is not a placement fact
anyway — the manifest names it outright. So RouteMode::StaticBucket
compiles to ResolvedRouteMode::StaticBucket, carrying the Worker binding
it reads through (r2_binding_name), and the Worker’s binding set gains
one R2 bucket binding per distinct bucket (r2_bucket_bindings), derived
from the same entries the table ships so the two cannot name different
bindings. The key is the request path minus its leading slash, unchanged:
xlb derives a blob’s key from the URL path it serves at.
Structs§
- CdnPlacement
- The origin resolution that exists today: the CDN prefix for static routes, and a mesh-identity-keyed address map for backend ones.
- Route
Rewrite - The prefix swap a backend route performs on its way to the origin.
- Route
Table - A domain’s routes, compiled and resolved, in manifest order.
- Route
Table Entry - One entry of the compiled table.
- Worker
Placement - The Worker arm’s placement (R898-F3).
Enums§
- Resolved
Route Mode - A compiled entry’s body — the declared
RouteModewith its origin resolved. - Route
Auth - Whether a path reaching this entry has to carry a bearer.
- Worker
Assets - How the Worker arm resolves a static route’s origin (R898-T4).
Traits§
- Route
Placement - The placement-time facts a declared route cannot answer about itself.
Functions§
- matches_
route_ pattern truewhenpathis served bypattern.- r2_
binding_ name - The Worker binding name a bucket route reads its bucket through:
noisetable-releases→R2_NOISETABLE_RELEASES. - r2_
bucket_ bindings - One R2 bucket binding per DISTINCT bucket the entries read, in first-use
(manifest) order:
(binding name, bucket name). - route_
table_ for_ service - The route-driven domain routing
service, compiled againstplacement.