Description
Design: validate it where you read it — one validated-and-bounded contract for the fleet proxy's frame and peer values
Design doc: files/designs/code-fleet-proxy-validated-bounded.md in the keep-the-four-repos-healthy goal workspace. Audit at origin/main b0c472d94. Filed by the design architect (pass 62, 2026-11-06), originating on the trigger recorded at pass 61: a fifth fleet-proxy bound/validation surface files — #6518 filed the same day, the token_file sibling of #6516 in the same function.
Summary
The fleet proxy (pkgs/fleet/crates/fleet-proxy) and its sibling client (pkgs/fleet/crates/fleet-dispatch) read two kinds of values they do not control: request frames from local callers (serve.rs parse_request) and HTTP responses from laptop services (http.rs, connect_udp.rs, dispatch's lib.rs). The stated doctrine (#5579, http.rs module doc) is that peer-declared sizes are never trusted — and sizes are capped (MAX_RESPONSE_BODY_BYTES, MAX_LINE_BYTES, strict Content-Length). But the doctrine was never extended past sizes:
parse_request(serve.rs:610, verified at audit HEAD) validatesmethod/pathstrictly (fleet-proxy(broker): method/path CRLF injection — one frame smuggles a second request onto the pooled tunnel #5750) and collapses every other field: a present-but-invalidport(70000, -1, 4801.5,"4801") becomesNoneand is silently rerouted to the default 4799 service (fleet-proxy(broker): a present-but-invalid request-frame port silently reroutes to the default 4799 service — 70000, -1, 4801.5 and "4801" are all served by laptop-agent instead of refused #6516); a present-but-non-stringtoken_filebecomesNoneand the default credential is silently substituted for the named one (fleet-proxy(broker): a present-but-non-string token_file is silently discarded — the request is authenticated with the default token instead of the named credential, or refused #6518). A present-but-invalid value is treated as an absent one.- The response reader caps every unit but no repetition: the interim-1xx, header-line and trailer-line loops in
http.rsconsume peer-controlled units forever, pinning a pooled tunnel and thread indefinitely (fleet-proxy(http): response reader caps every line but nothing caps repetition — endless 1xx heads, header lines, or trailer lines hang an exchange (and its pooled tunnel) forever #6509). - fleet-dispatch's
dechunked_len(lib.rs:303-312, verified at audit HEAD) does unchecked arithmetic on the peer-declared chunk size — panic in debug, wrap in release — while its siblingdechunkand the proxy'sread_chunkedboth guard the same value (fleet(dispatch): dechunked_len does unchecked arithmetic on the peer-declared chunk size — overflow panics in debug, wraps in release #6512). - The fourth leg, fleet-proxy(connect-udp): request-head bound is checked only after a whole line arrives — one newline-free line accumulates without limit #6511 (connect-udp head bound checked only after a whole line arrives), landed 2026-11-06 (during-the-read
take(remaining + 1)fix, connect_udp.rs:264-276 at audit HEAD) and is this design's embodiment for rule 3 — cited, not a phase.
The contract
Every value the proxy acts on is validated where it is read — type, range, arithmetic, count — and a present-but-invalid value is refused in that surface's own error shape, never defaulted, substituted, wrapped, or consumed without bound.
- Present is not absent (frame fields).
parse_requestdistinguishes absent (documented default) from present-but-invalid (refused with the standard{status: 502, body: {error: "broker: …"}}frame naming the field and bad shape; no connection opened, no token read) — fleet-proxy(broker): method/path CRLF injection — one frame smuggles a second request onto the pooled tunnel #5750's strictness extended to every field the broker acts on. - Counts are bounds too (peer readers). Every repetition loop over peer-controlled units carries a named count bound; exceeding it fails like a size cap (
HttpError::Malformed, connection non-reusable). - Arithmetic is checked, during the read. A declared size is checked and capped before it is added, indexed, or allocated from; bounds are enforced while accumulating, not after (fleet-proxy(connect-udp): request-head bound is checked only after a whole line arrives — one newline-free line accumulates without limit #6511's landed fix is the embodiment).
- One statement. The contract is written into the
http.rsmodule doc's peer-trust paragraph, extended from sizes to all four dimensions (lands with Phase 2, where the doc is).
Decisions recorded on this issue
- Port 0 is refused at the frame boundary — pinning fleet-proxy(broker): a present-but-invalid request-frame port silently reroutes to the default 4799 service — 70000, -1, 4801.5 and "4801" are all served by laptop-agent instead of refused #6516's either/or against the documented-dial-error alternative: 0 names no service in the closed routing table, and leaving one invalid shape to fail later at dial time recreates the inconsistency fleet-proxy(broker): a present-but-invalid request-frame port silently reroutes to the default 4799 service — 70000, -1, 4801.5 and "4801" are all served by laptop-agent instead of refused #6516 names. All invalid ports fail in one place, in one shape.
token_file: nullis absent — pinning fleet-proxy(broker): a present-but-non-string token_file is silently discarded — the request is authenticated with the default token instead of the named credential, or refused #6518's open rule: JSON null is how the typed proto (Option<String>) and the Python express "no token_file", andparse_requestalready treats a nullbodyas absent in the same function (serve.rs:635). Non-string non-null values are refused.- Repetition bound values stay with Phase 2's fixer (named constants beside the
MAX_*size constants, "generously above anything a legitimate laptop service sends" per fleet-proxy(http): response reader caps every line but nothing caps repetition — endless 1xx heads, header lines, or trailer lines hang an exchange (and its pooled tunnel) forever #6509's acceptance) — no legitimate-count evidence exists in-tree to pin numbers against; the contract fixes the failure shape and naming. - Out of scope: the queue/secrets brokers' transports (fleet-queue
uds_http, tracker fleet brokers: review-finding hardening for dual-bind transports (from PR #6514) #6515 — their Content-Length leg fleet-queue(uds_http): Content-Length is never validated — an unparsable value silently becomes 0 and conflicting duplicates resolve last-wins, so the brokers dispatch a request with its body dropped #6527 already landed copyinghttp.rs's rule; Phase 4 cross-references, does not adopt), Design: read it bounded — one response-read contract for mcpx's outbound calls #4375 (mcpx response reads), Validate what you forward — one validation contract for the fleet queue's packets and verb values #6448 (fleet-queue packet values) — the same stance at different boundaries, cited not merged.
Phased breakdown (sub-issues of this issue)
- Phase 1 — frame fields: fleet-proxy(broker): a present-but-invalid request-frame port silently reroutes to the default 4799 service — 70000, -1, 4801.5 and "4801" are all served by laptop-agent instead of refused #6516 + fleet-proxy(broker): a present-but-non-string token_file is silently discarded — the request is authenticated with the default token instead of the named credential, or refused #6518 (adopted as phases, linked as sub-issues). Lands first — the only phase whose failure mode is silent wrong-service delivery / credential substitution.
- Phase 2 — repetition counts: fleet-proxy(http): response reader caps every line but nothing caps repetition — endless 1xx heads, header lines, or trailer lines hang an exchange (and its pooled tunnel) forever #6509 (adopted). Includes the rule-4 contract statement in the
http.rsmodule doc. - Phase 3 — dispatch arithmetic: fleet(dispatch): dechunked_len does unchecked arithmetic on the peer-declared chunk size — overflow panics in debug, wraps in release #6512 (adopted). Independent of Phases 1–2.
- Phase 4 — closing inventory: NEW issue filed this pass (linked as sub-issue, gated on Phases 1–3): every read site in fleet-proxy + fleet-dispatch dispositioned, with required dispositions for (a) unadvertised-port routing, (b) the
bodyfield's shape rule, (c) the fleet brokers: review-finding hardening for dual-bind transports (from PR #6514) #6515 brokers cross-reference, (d) verification that the port-0 / null-token_file pins landed as recorded.
Phases with open questions stay in the design doc (hello-frame field treatment, a shared bound-constant home, refusal-count visibility) — they are not issues.
Grounding
pkgs/fleet/crates/fleet-proxy/src/serve.rs:610-646—parse_requestat audit HEAD (strict method/path, lenient port/token_file/body).pkgs/fleet/crates/fleet-proxy/src/http.rs:16-52— the fleet-proxy(http): response reader trusts peer-declared sizes (unbounded preallocation) and silently drops the body on a malformed Content-Length, desyncing the pooled connection #5579 peer-trust doc + size constants;:215/:282/:355the unbounded repetition loops (fleet-proxy(http): response reader caps every line but nothing caps repetition — endless 1xx heads, header lines, or trailer lines hang an exchange (and its pooled tunnel) forever #6509).pkgs/fleet/crates/fleet-dispatch/src/lib.rs:303-312—dechunked_lenunchecked arithmetic at audit HEAD.pkgs/fleet/crates/fleet-proxy/src/connect_udp.rs:264-276— fleet-proxy(connect-udp): request-head bound is checked only after a whole line arrives — one newline-free line accumulates without limit #6511's landed during-the-read bound (embodiment).- Origins: fleet-proxy(broker): a present-but-invalid request-frame port silently reroutes to the default 4799 service — 70000, -1, 4801.5 and "4801" are all served by laptop-agent instead of refused #6516, fleet-proxy(broker): a present-but-non-string token_file is silently discarded — the request is authenticated with the default token instead of the named credential, or refused #6518, fleet-proxy(http): response reader caps every line but nothing caps repetition — endless 1xx heads, header lines, or trailer lines hang an exchange (and its pooled tunnel) forever #6509, fleet(dispatch): dechunked_len does unchecked arithmetic on the peer-declared chunk size — overflow panics in debug, wraps in release #6512 (adopted); fleet-proxy(connect-udp): request-head bound is checked only after a whole line arrives — one newline-free line accumulates without limit #6511 (landed, embodiment); fleet-proxy(http): response reader trusts peer-declared sizes (unbounded preallocation) and silently drops the body on a malformed Content-Length, desyncing the pooled connection #5579, fleet-proxy(broker): method/path CRLF injection — one frame smuggles a second request onto the pooled tunnel #5750, fleet-proxy(http): response reader waits for a body that never comes on HEAD / 204 / 304, and treats 1xx as the final response #5921 (precedents).
Agent note: this is a kind/design issue from the fleet design architect. Its phases are its sub-issues; triage of the phases should record the phase structure rather than re-filing or re-scoping the contract — the two pins above (port 0 refused; null token_file = absent) are decided here.
Activity
developing-today commentedon Nov 6, 2026
CODE-2004
dezren39 commentedon Nov 6, 2026
🤖 Agent note · design-architect pass 62 — design filed this pass
files/designs/code-fleet-proxy-validated-bounded.mdin the keep-the-four-repos-healthy goal workspace. Audit at origin/mainb0c472d94token_filesibling of fleet-proxy(broker): a present-but-invalid request-frame port silently reroutes to the default 4799 service — 70000, -1, 4801.5 and "4801" are all served by laptop-agent instead of refused #6516 in the sameparse_requestfunctionhttp.rsmodule doc), Phase 3 = fleet(dispatch): dechunked_len does unchecked arithmetic on the peer-declared chunk size — overflow panics in debug, wraps in release #6512 (dispatch arithmetic), Phase 4 = Phase 4 of #6582 — closing read-site inventory: disposition every frame/peer value read in fleet-proxy + fleet-dispatch against the validated-and-bounded contract #6583 (closing read-site inventory, gated on Phases 1–3)token_file: nullis absent; repetition bound values stay with Phase 2's fixer (named constants, generously sized); the queue/secrets brokers (fleet brokers: review-finding hardening for dual-bind transports (from PR #6514) #6515) are out of scope — Phase 4 cross-references them