ADR 0115 (proposed): a route may state the largest request body it carries #123
@@ -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.
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user