ADR 0108: a route carries the policy applied to a request
Issue 116 found the mesh's proxy applies nothing to a request — host lookup, forward. Against what the replaced ingress actually relies on, four capabilities are missing: authentication (three dependents, each gating an admin surface with no login of its own), refusal scoped to a path (one, a live incident mitigation), path-scoped routing with priority, and redirect. Policy goes on the route rather than beside it. A proxy-side settings layer keyed by route name would keep the grant literally clean, but then "what protects this route" is answered from two files nothing keeps in step — and a route's protection is part of what a route is. The set is closed at those four, so a fifth is an amendment and each addition is earned by a dependent that exists. An open middleware surface was rejected: it recreates what is being replaced, and narrowing one later is far harder than widening a closed one. Where policy needs a credential the declaration names a secret and never carries the value, which keeps the existing secret machinery the only thing holding credentials. Inlining a hash was rejected as the first credential in a declaration — a precedent easier to set than withdraw. This re-keys the routing table by host and path with priority, which follows from the decision rather than being a separate one: two of the four need one host routed more than one way. Equal priorities must resolve identically every time or the proxy stops being reproducible. The record says how it is checked, including the negative case that rots quietly — a declaration carrying a credential value rather than a reference must be refused, so the rejected option cannot return by accident. 08-connectivity §3 names the record and gains the subsection; issue 116 gains amended-design.
This commit is contained in:
@@ -7,11 +7,12 @@ code:
|
||||
- mesh-controller internal/identity/authority.go
|
||||
- mesh-host internal/identity/serving.go
|
||||
- mesh-host internal/apply (the service that reflects a rule set)
|
||||
updated: 2026-09-23
|
||||
updated: 2026-09-25
|
||||
decisions:
|
||||
- 02-DECISIONS/0104-a-provision-may-be-answered-by-an-adapter-to-the-predecessor.md
|
||||
- 02-DECISIONS/0106-the-bus-is-nats.md
|
||||
- 02-DECISIONS/0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md
|
||||
- 02-DECISIONS/0108-a-route-carries-the-policy-applied-to-a-request.md
|
||||
- 02-DECISIONS/0103-what-an-adopted-node-holds-and-what-its-guard-refuses.md
|
||||
- 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
|
||||
- 02-DECISIONS/0099-a-step-that-runs-once-names-what-it-reads.md
|
||||
@@ -442,6 +443,39 @@ exactly the per-module cost it is meant to remove. The design is the composition
|
||||
stopgap until the manifest layer can carry a label and a domain separately
|
||||
([ADR 0066](../../02-DECISIONS/0066-public-routing-is-name-agnostic.md)).
|
||||
|
||||
### A route also carries what a request arriving at it may do
|
||||
|
||||
*2026-09-25, from comparing the mesh's proxy against the ingress it would replace
|
||||
([issue 116](../../04-ISSUES/116-route-proxy-has-no-auth-or-ip-restriction/00-report.md)),
|
||||
decided in [ADR 0108](../../02-DECISIONS/0108-a-route-carries-the-policy-applied-to-a-request.md).*
|
||||
|
||||
A grant hands back a name. It did not say what the name admits, and the proxy admitted everything —
|
||||
its request path was a host lookup and a forward. Measured against what the replaced ingress
|
||||
actually relies on, four things were missing: **authentication**, **refusal scoped to a path**,
|
||||
**path-scoped routing with priority**, and **redirect**. Three modules depend on the first, each to
|
||||
gate an admin surface that has no login of its own; one dependent of the second is a live incident
|
||||
mitigation.
|
||||
|
||||
**Policy belongs to the route, not beside it.** A contribution carries it along with the name, the
|
||||
port and the location. The alternative — a proxy-side settings layer keyed by route name — keeps the
|
||||
grant literally clean but makes *"what protects this route"* a question answered from two files that
|
||||
nothing keeps in step. A route's protection is part of what a route is.
|
||||
|
||||
**The set is closed at those four.** A fifth is an amendment, so each addition is earned by a
|
||||
dependent that exists rather than added because a middleware surface was open. An open surface would
|
||||
recreate the thing being replaced, and is far harder to narrow later than a closed one is to widen.
|
||||
|
||||
**Where policy needs a credential, the declaration names a secret; it never carries one.** The mesh
|
||||
already mints and holds credentials, and that machinery stays the only thing that does — so a hash
|
||||
never reaches anything regenerated, synced or committed.
|
||||
|
||||
**This re-keys the table.** Two of the four need one host routed more than one way, so the proxy
|
||||
matches on host **and path**, with priority, rather than mapping a host to a single target. Equal
|
||||
priorities must resolve identically every time, or the proxy stops being reproducible.
|
||||
|
||||
The proxy remains a reference implementation: the contract is the file the mesh writes, not the
|
||||
program that reads it, and another proxy may implement the same file.
|
||||
|
||||
## 4 — Filtering
|
||||
|
||||
**Derived from what is assigned here, and from the overlay's shape** — a node's open ports are a
|
||||
|
||||
Reference in New Issue
Block a user