ADR 0115 (proposed): a route may state the largest request body it carries #123

Closed
jschoubben wants to merge 1 commits from decide/0115-a-route-may-limit-the-body-it-carries into main
2 changed files with 82 additions and 0 deletions
Showing only changes of commit 81bdf8df56 - Show all commits
@@ -0,0 +1,81 @@
---
topic: the tiers
status: proposed
date: 2026-09-26
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0108-a-route-carries-the-policy-applied-to-a-request.md
---
# 115. A route may state the largest request body it carries, which is the fifth policy
## Context
[ADR 0108](0108-a-route-carries-the-policy-applied-to-a-request.md) closed the set of route policies
at four — authentication, refusal scoped to a path, path-scoped routing with priority, redirect — and
said what a fifth would cost: *"A fifth is an amendment to this record, deliberately — each addition
should be earned by a dependent that exists."*
**The dependent exists, and it is the registry's public door.** The artifact store is reached by two
names: one inside the private network, and one the world can push to. A registry takes image layers
in single requests of gigabytes. The predecessor served the registry's public name with exactly a
body-size middleware in front of it, because without one the proxy refuses the push at its own
default long before the registry sees it. So a public name for the registry that cannot state its
limit is a public name nothing can be pushed to — the route would be written, reported as served, and
fail on first use.
Nothing in the four covers it. Refusal scoped to a path refuses by *where* a request arrives, not by
*how large* it is, and the two are not substitutes: the registry's push path is the path that must
work.
An implementation of this exists, written before the policy set was closed, on a branch in the
controller and the catalogue (`feat/registry-public-route`). It cannot merge as written — it predates
0108 and builds on the routing table 0108 replaced — and it is what this record would let be ported.
## Decision
**A route contribution may state the largest request body it carries, in bytes. It is the fifth
policy, and the set is closed at five.**
**Absent means no limit**, which is what every route gets today: the mesh's own proxy has never
limited a body, and a default arriving with the field would change every route that never asked for
one.
**A value that is not a whole positive number of bytes is refused when the contribution is read** —
named, with the module and the route, like a port that is not a port. It is not rounded, and it is
not dropped.
**A limit the proxy cannot express is a route that is not written.** Writing the route without its
limit would carry exactly what the module said not to carry, and report success; that is the failure
mode 0108 exists to prevent, arriving one field later.
**The limit is enforced in front of the workload**, so an oversized request is refused by the proxy
with the proxy's own answer, and the workload never sees it.
## Options rejected
**A single limit configured on the proxy.** One number for every route: large enough for the registry
means large enough for every admin surface behind the same proxy, and small enough for those means the
registry cannot be pushed to. The value belongs to the route, which is the thing that knows.
**Carrying it outside the policy set, as a transport detail beside certificate verification.** This
works, and it is how the existing implementation would most cheaply be merged. It is rejected because
a body limit *refuses requests*, and 0108 exists to put every request-refusing behaviour in the record
that says where such behaviours live. A closed set with an exception written next to it is not a closed
set, and the next reader has no way to know the exception is there.
**Leaving the registry's public name on the adopted ingress.** It defers the problem at the cost of
keeping the predecessor's proxy alive for exactly one route, which is the standing exception 0108 was
written to end.
## Consequences
**The set is closed at five, and the same terms apply to a sixth.** This record's own precedent is
that an addition needs a dependent that already exists, not one that might.
**Every provider of `route` must understand one more key**, which is the cost 0108 already named for
the four and accepts again here.
**The contribution's vocabulary becomes checkable in one more place**, which is worth stating because
a limit that is silently ignored is worse than no limit: the push fails at the proxy, and the module's
manifest says it should not have.
+1
View File
@@ -125,6 +125,7 @@ python3 00-META/checks/index.py fail if stale
- **0098** — [A fact a provider makes at first start is fetched from it, not carried in its manifest](0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md)
- **0108** — [A route carries the policy applied to a request, and names a secret rather than holding one](0108-a-route-carries-the-policy-applied-to-a-request.md)
- **0109** — [A package registry seat is one per ecosystem, not one for all of them](0109-a-package-registry-seat-is-one-per-ecosystem.md)
- **0115** — [A route may state the largest request body it carries, which is the fifth policy](0115-a-route-may-limit-the-body-it-carries.md) *(proposed)*
### What runs on them, and how it gets there