The direct answer
Count actual expanded bytes under one cumulative allowance before releasing any result to a parser. Keep every decompression call positively bounded, preserve the same object and its unconsumed input, and require complete-stream and no-remainder checks. A trailer size, small compressed upload or flush buffer setting is not an expansion budget.
Put the byte boundary before text or JSON decoding
The strict JSON intake guide starts with an already decompressed body. That leaves a prior question: how did compressed input become a bounded byte sequence? Checking length after an unlimited expansion can discover an oversized result only after producing it.
This original design accepts one immutable, owned gzip object and stages its output privately. It uses CPython zlib documentation identifying Python 3.15.2; no installed Python or linked zlib build was inspected. Record both before implementation. Automatic HTTP content decoding, tar extraction and downstream parsing are outside this profile.
Invented limits are 64 KiB compressed input, 8 KiB expanded output, 1 KiB returned per call and 128 calls per attempt. They are worksheet choices, not measured safe defaults. No compressed bytes, hostile input, decompressor object or runtime fixture was created or exercised.
Choose a stricter member policy than the format requires
Python's zlib reference documents decompressobj wrapper selection; wbits=31 selects the gzip-header/trailer path with the largest documented window. This profile does not switch to raw or automatic zlib/gzip detection when that path rejects an object.
RFC 1952 is an Informational gzip format specification. It describes a sequence of members and defines ISIZE as the original size modulo 232. Our requirement of exactly one member and no trailing bytes is a narrower application choice. ISIZE describes trailer metadata; it neither reserves a byte allowance nor substitutes for counting produced output.
Keep original bytes stable and establish the compressed-input ceiling before this stage. If upstream middleware already expanded them, this boundary cannot retroactively constrain that work. A filename, media-type label or asserted size is not evidence that the selected path ran.
Maintain one object and one cumulative ledger
Let B be the 8 KiB allowance and S the staged expanded-byte count, initially zero. Supply the complete bounded compressed object once. Each proposed call uses a positive output cap min(1024, B-S+1). Count the returned bytes immediately; if the new S exceeds B, reject the entire attempt before appending that chunk to accepted staging.
The extra byte is a detection allowance, not permitted output. At S=B, the cap remains one so the receiver can process a remaining trailer without accidentally selecting the API's unlimited mode. Python documents that a nonzero max_length bounds the returned value, while zero expands the whole supplied input. Never replace the cap with zero or an omitted argument.
Reuse the same decompression object. Python describes unconsumed_tail as input withheld because of the output limit and requires feeding it back to continue. After the first call, submit exactly that tail, including an empty tail when draining buffered work. Do not resubmit the original object or create a fresh decoder that resets S.
After each call, check the budget and completion state. If incomplete, continue only when output was produced or the submitted tail became shorter. Otherwise reject no progress. Enforce the call ceiling too; never spin indefinitely or fall back to an unlimited operation. These are proposed strict admission rules, not proof that every valid gzip object will be accepted.
Finish through bounded calls, not an unlimited flush
Python documents eof as the stream-completion indicator and unused_data as bytes beyond the compressed stream. Success here requires eof, S at or below B, empty unused_data and no unaccounted input or tail. Because the complete compressed object was supplied initially, later unseen input must not be silently ignored.
This profile makes no flush() call. Its documented length parameter sets an initial output-buffer size, not a maximum returned-output allowance. If bounded continuation ends without eof, reject incomplete or unsupported completion; do not flush and hope the result fits. Any decompression error also rejects the attempt.
At exactly B, accept only if completion and no-remainder checks succeed without another output byte. If an additional byte appears, reject; do not trim it. A second member or unexplained suffix is rejected rather than passed to another decoder. Prefixes stay unavailable to JSON, XML, text extraction, indexing and model adapters until the complete intake decision succeeds.
Review eight inert state cards
| Inert state | Proposed decision |
|---|---|
| Small ordinary note; complete stream; output below B; no remainder | Eligible for later owned-content and parser checks |
| Output equals B; a bounded one-byte-cap continuation reaches eof without output | Accept the exact-size result if every remainder check passes |
| Output equals B; the next bounded call produces one byte | Reject whole attempt; no trimming or parser release |
| Output cap leaves unconsumed_tail | Continue on the same object with the same cumulative ledger |
| Empty-tail call produces nothing, changes no input state and eof remains false | Reject no progress/incomplete completion; no unlimited finish |
| eof is true but trailing data or unresolved input remains | Reject under the single-member policy, including another member |
| Small claimed ISIZE accompanies an over-budget produced-byte count | Reject by actual count; metadata cannot enlarge the allowance |
| A flush length or unlimited zero cap is proposed to finish | Reject that implementation path; its name is not a byte ceiling |
The positive note and exact-budget card preserve a useful permitted path. These are symbolic state descriptions, not generated gzip files or observed library results. A label recorder would establish only its own decisions.
Keep the resource and product claim narrow
A per-call returned-byte limit differs from cumulative accepted expansion, internal allocations, compressed-header processing, CPU time and whole-worker memory. The call ceiling does not bound how long one call takes. Independently reviewed worker time/memory limits and isolation remain necessary; this design demonstrates none of them.
Completion also does not authenticate the supplier, authorize an object, make text instructions trustworthy or permit provider disclosure. Apply current ownership and the downstream parser/content policy separately. Keep only minimized attempt, revision, byte-count and rejection-category evidence, not document contents.
All eight checks are UNRUN: no zlib import, decompression, parser execution, benchmark, installation, API or model experiment occurred for them. Boundary's prelaunch plans do not provide a shipped gzip intake service. Its historical mock policy checks and separate synthetic JSON-export checks concern different behavior.
Primary sources
Official references supporting the guidance. Examples in this guide are synthetic and are not reports of a production incident.