Skip to content

TC-1012: cap /invoke upload bodies at 95 MiB with 413 - #317

Merged
samgbafa merged 3 commits into
mainfrom
feat/tc-1012-node-body-cap
Oct 18, 2026
Merged

samgbafa merged 3 commits into
mainfrom
feat/tc-1012-node-body-cap

Conversation

@samgbafa

@samgbafa samgbafa commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

TC-1012. The node now enforces its own upload limit of 95 MiB (99,614,720 bytes). A body of exactly that size is accepted, and a body one byte longer gets 413. Requests to node.tinycloud.xyz reach the node on :8000 through the Cloudflare Worker without passing the ingress, so the node's own limit is the one that applies there.

Limits found (base 212a826)

There is no Rocket.toml and no limits config. Rocket's built-in limits apply only to its own data guards (Json, String and so on), not to the raw Data that /invoke reads. The limits that actually apply are the explicit Data::open calls in tinycloud-node-server/src/routes/mod.rs:

Path Before After
KV put, single body (invoke_impl, mod.rs:1936) d.open(1u8.gigabytes()): 1 GB, and a longer body was cut off and stored without an error open at cap+1; more than 99,614,720 bytes copied → 413
KV batch put, multipart (build_batch_kv_inputs, mod.rs:1509) data.open(1u8.gigabytes()): past 1 GB, multer saw a truncated stream → 400 multer whole_stream(99_614_720); StreamSizeExceeded → 413 (from next_field and chunk)
DuckDB import (handle_duckdb_invoke, mod.rs:3123) d.open(100u8.megabytes()): 100 MB, cut off without an error open at cap+1; more than the cap → 413

The JSON bodies (read_json_body, 1 MiB) and the share routes keep their own smaller limits, which already return 413.

Tests

  • tc1012_kv_put_accepts_exactly_95_mib_and_refuses_one_more_byte_with_413: a signed kv/put through the real /invoke route. A 99,614,720-byte body gets 2xx; 99,614,721 bytes gets 413.
  • tc1012_multipart_kv_put_accepts_95_mib_body_and_refuses_one_more_byte_with_413: a multipart body of 99,614,720 bytes total gets 2xx; one byte more gets 413.
  • Both failed on base: the over-cap requests returned 200.
  • A throwaway smoke, not committed, ran the real Rocket server over TCP with Transfer-Encoding: chunked: 99,614,720 bytes → 200 OK, 99,614,721 bytes → 413 Payload Too Large.
  • cargo test -p tinycloud-node --lib: 526/526 pass. In one of three full runs, three policy_v3::account_alias_tests failed; they pass on their own and on rerun. Those tests are outside this change.
  • cargo check -p tinycloud-node --features duckdb --tests passes, and cargo fmt --check and clippy show no new warnings.
  • deploy/ingress/test-redacted-logging.sh now runs the ingress with CLIENT_MAX_BODY_SIZE=95m and expects exactly 99,614,720 bytes → 200, 99,614,721 bytes → 413, and 99,614,721 bytes chunked over HTTP/1.1 → 413. It passes locally (DOCKER="sudo docker").

Compose

This PR does not change docker-compose.dstack-postgres.yaml, which the TC-959 N0 lock hash-pins. #309 keeps its approved bytes (32m, raw ca110b0b…), which ship with the N0 sidecar on 1.20.6. The CLIENT_MAX_BODY_SIZE=32m → 95m line moves to the main-release lineage in #315, which binds the release compose. Main's pre-N0 template and ops/rollback/*.yaml stay at 32m on purpose. Until the release compose lands, main's compose still says 32m while the docs and the ingress test here say 95m.

Not merged or deployed.

@samgbafa

Copy link
Copy Markdown
Contributor Author

APPROVE: see TC-1012

@samgbafa

Copy link
Copy Markdown
Contributor Author

APPROVE: see TC-1012 (reviewed exact head c67504b; merge-with-main delta from 494a832)

@samgbafa
samgbafa merged commit d379c21 into main Oct 18, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant