The controller accepts request bodies of any size, so a single operator request can exhaust its memory and take down every background worker with it.
Dockerfile:35 and docker-compose.yml:20 set BODY_SIZE_LIMIT=Infinity, which turns off adapter-node's body cap for the whole app.
The auth, agent and game-file routes use bounded readers (authBody, objectBody, 8 MiB upload chunks). The rest buffer the whole body:
api/scripts/+server.ts:38,59, scripts/[id]/versions/+server.ts:25,30, scripts/inspect/+server.ts:7,12 and betas/[id]/revisions/+server.ts:10,18 call request.formData() and then arrayBuffer()
31 request.json() calls across 29 route files, such as servers/+server.ts:10, provisioning/+server.ts:25 and plugins/github/import/+server.ts:8
native-hosts.md:661-662 says "application routes still enforce these limits", which isn't true for these. With port 3000 published directly (#67), a proxy limit doesn't cover it either. The agent also rejects files over 32 MiB (ec_agent.py:40), so an oversized artifact uploads fine and only fails at deploy time.
Fix
Read JSON bodies through a bounded reader (authBody or objectBody)
Check Content-Length and count bytes before calling formData(), and cap artifacts at the agent's 32 MiB
Correct native-hosts.md:661-662
The controller accepts request bodies of any size, so a single operator request can exhaust its memory and take down every background worker with it.
- `Dockerfile:35` and `docker-compose.yml:20` set `BODY_SIZE_LIMIT=Infinity`, which turns off adapter-node's body cap for the whole app.
- The auth, agent and game-file routes use bounded readers (`authBody`, `objectBody`, 8 MiB upload chunks). The rest buffer the whole body:
- `api/scripts/+server.ts:38,59`, `scripts/[id]/versions/+server.ts:25,30`, `scripts/inspect/+server.ts:7,12` and `betas/[id]/revisions/+server.ts:10,18` call `request.formData()` and then `arrayBuffer()`
- 31 `request.json()` calls across 29 route files, such as `servers/+server.ts:10`, `provisioning/+server.ts:25` and `plugins/github/import/+server.ts:8`
`native-hosts.md:661-662` says "application routes still enforce these limits", which isn't true for these. With port 3000 published directly (#67), a proxy limit doesn't cover it either. The agent also rejects files over 32 MiB (`ec_agent.py:40`), so an oversized artifact uploads fine and only fails at deploy time.
**Fix**
- [x] Read JSON bodies through a bounded reader (`authBody` or `objectBody`)
- [x] Check `Content-Length` and count bytes before calling `formData()`, and cap artifacts at the agent's 32 MiB
- [x] Correct `native-hosts.md:661-662`
Reviewed 9eb3aa84c3. Every route that reads a body now has a size limit, artifacts are capped at the agent's 32 MiB, and the docs match, so I'm closing this.
Checklist
Read JSON bodies through a bounded reader. The new request-body.ts adds jsonBody, with a 1 MiB default. It refuses an oversized Content-Length before reading, counts the bytes it actually receives, and copies them into 64 KiB blocks so many tiny chunks can't inflate memory. No request.json(), formData(), text() or arrayBuffer() calls on the request remain anywhere under src. Routes that turned bad JSON into an empty object or 400 now re-throw 413 first. request-body-routes.test.ts sends declared and undeclared oversized bodies to all 31 JSON routes and expects 413 from each. It also checks that malformed JSON still gets 400.
Check size before formData() and cap artifacts at 32 MiB.artifactFormBody buffers at most 33 MiB (65 MiB for beta revisions) before it parses anything, then rejects any file or text field over MAX_ARTIFACT_BYTES (32 MiB, matching the agent's MAX_FILE). createScript and uploadNewVersion enforce the same cap for non-form callers, and the GitHub import now uses it in place of 64 MiB. Tests cover:
a 32 MiB file plus form overhead is accepted, and 1 byte more is refused, for both file and text fields
the beta revision cap holds even without a declared length
all six upload routes refuse an oversized declared body before any version is written
Correct native-hosts.md:661-662. The "Proxy limits and older uploads" section (native-hosts.md:658) now lists each route's real limit. It explains why BODY_SIZE_LIMIT=Infinity stays for legacy source-host imports, and it raises the suggested proxy limit to 128 MiB to cover agent operation results. plugin-releases.md now says 32 MiB.
Review notes
Good extra catch: telemetry goes through jsonBody too, which covers the first fix item of #74.
The 5 body-limit, plugin-import, script and guard test files pass locally (142 tests), and CI run 838 passed on this commit.
Minor and non-blocking: an upload is still held in memory about three times over: the bounded body, the parsed form, then Buffer.from(await file.arrayBuffer()) (scripts/+server.ts:63, scripts/[id]/versions/+server.ts:38, betas/[id]/revisions/+server.ts:24). That's roughly 100 MiB per maximum-size upload. It's bounded per request, but not across simultaneous uploads.
Reviewed 9eb3aa84c3f2e6b2a7d18880c3bc53cd218f8852. Every route that reads a body now has a size limit, artifacts are capped at the agent's 32 MiB, and the docs match, so I'm closing this.
**Checklist**
- [x] **Read JSON bodies through a bounded reader.** The new `request-body.ts` adds `jsonBody`, with a 1 MiB default. It refuses an oversized `Content-Length` before reading, counts the bytes it actually receives, and copies them into 64 KiB blocks so many tiny chunks can't inflate memory. No `request.json()`, `formData()`, `text()` or `arrayBuffer()` calls on the request remain anywhere under `src`. Routes that turned bad JSON into an empty object or 400 now re-throw 413 first. `request-body-routes.test.ts` sends declared and undeclared oversized bodies to all 31 JSON routes and expects 413 from each. It also checks that malformed JSON still gets 400.
- [x] **Check size before `formData()` and cap artifacts at 32 MiB.** `artifactFormBody` buffers at most 33 MiB (65 MiB for beta revisions) before it parses anything, then rejects any file or text field over `MAX_ARTIFACT_BYTES` (32 MiB, matching the agent's `MAX_FILE`). `createScript` and `uploadNewVersion` enforce the same cap for non-form callers, and the GitHub import now uses it in place of 64 MiB. Tests cover:
- a 32 MiB file plus form overhead is accepted, and 1 byte more is refused, for both file and text fields
- the beta revision cap holds even without a declared length
- all six upload routes refuse an oversized declared body before any version is written
- [x] **Correct `native-hosts.md:661-662`.** The "Proxy limits and older uploads" section (`native-hosts.md:658`) now lists each route's real limit. It explains why `BODY_SIZE_LIMIT=Infinity` stays for legacy source-host imports, and it raises the suggested proxy limit to 128 MiB to cover agent operation results. `plugin-releases.md` now says 32 MiB.
**Review notes**
- Good extra catch: telemetry goes through `jsonBody` too, which covers the first fix item of #74.
- The 5 body-limit, plugin-import, script and guard test files pass locally (142 tests), and CI [run 838](https://gitea.thewrightserver.net/josh/electric-cherry/actions/runs/838) passed on this commit.
- Minor and non-blocking: an upload is still held in memory about three times over: the bounded body, the parsed form, then `Buffer.from(await file.arrayBuffer())` (`scripts/+server.ts:63`, `scripts/[id]/versions/+server.ts:38`, `betas/[id]/revisions/+server.ts:24`). That's roughly 100 MiB per maximum-size upload. It's bounded per request, but not across simultaneous uploads.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The controller accepts request bodies of any size, so a single operator request can exhaust its memory and take down every background worker with it.
Dockerfile:35anddocker-compose.yml:20setBODY_SIZE_LIMIT=Infinity, which turns off adapter-node's body cap for the whole app.authBody,objectBody, 8 MiB upload chunks). The rest buffer the whole body:api/scripts/+server.ts:38,59,scripts/[id]/versions/+server.ts:25,30,scripts/inspect/+server.ts:7,12andbetas/[id]/revisions/+server.ts:10,18callrequest.formData()and thenarrayBuffer()request.json()calls across 29 route files, such asservers/+server.ts:10,provisioning/+server.ts:25andplugins/github/import/+server.ts:8native-hosts.md:661-662says "application routes still enforce these limits", which isn't true for these. With port 3000 published directly (#67), a proxy limit doesn't cover it either. The agent also rejects files over 32 MiB (ec_agent.py:40), so an oversized artifact uploads fine and only fails at deploy time.Fix
authBodyorobjectBody)Content-Lengthand count bytes before callingformData(), and cap artifacts at the agent's 32 MiBnative-hosts.md:661-662Reviewed
9eb3aa84c3. Every route that reads a body now has a size limit, artifacts are capped at the agent's 32 MiB, and the docs match, so I'm closing this.Checklist
request-body.tsaddsjsonBody, with a 1 MiB default. It refuses an oversizedContent-Lengthbefore reading, counts the bytes it actually receives, and copies them into 64 KiB blocks so many tiny chunks can't inflate memory. Norequest.json(),formData(),text()orarrayBuffer()calls on the request remain anywhere undersrc. Routes that turned bad JSON into an empty object or 400 now re-throw 413 first.request-body-routes.test.tssends declared and undeclared oversized bodies to all 31 JSON routes and expects 413 from each. It also checks that malformed JSON still gets 400.formData()and cap artifacts at 32 MiB.artifactFormBodybuffers at most 33 MiB (65 MiB for beta revisions) before it parses anything, then rejects any file or text field overMAX_ARTIFACT_BYTES(32 MiB, matching the agent'sMAX_FILE).createScriptanduploadNewVersionenforce the same cap for non-form callers, and the GitHub import now uses it in place of 64 MiB. Tests cover:native-hosts.md:661-662. The "Proxy limits and older uploads" section (native-hosts.md:658) now lists each route's real limit. It explains whyBODY_SIZE_LIMIT=Infinitystays for legacy source-host imports, and it raises the suggested proxy limit to 128 MiB to cover agent operation results.plugin-releases.mdnow says 32 MiB.Review notes
jsonBodytoo, which covers the first fix item of #74.Buffer.from(await file.arrayBuffer())(scripts/+server.ts:63,scripts/[id]/versions/+server.ts:38,betas/[id]/revisions/+server.ts:24). That's roughly 100 MiB per maximum-size upload. It's bounded per request, but not across simultaneous uploads.