You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 573557a
Browse filesBrowse the repository at this point in the historyBrowse files
fix(deploy): match Cloudflare's Worker size limit on Void Cloud (#439)
## Problem
Void Cloud refused Workers that Cloudflare accepts. The napi.rs deploy
failed with:
```
Deploy failed (HTTP 413): request body exceeds 67108864 bytes
```
`@cloudflare/vite-plugin` 1.64 writes Worker source maps by default. The
CLI sent every Worker module, as base64, inside the deploy request, so
the maps counted against the API's 64 MiB body cap.
```
Cloudflare Void Cloud before this PR
code limit 64 MiB uncompressed JSON body ≤ 64 MiB, as base64
→ ≈ 48 MiB raw, maps included
source maps not counted (15 MB gz each) counted
per module no limit 32 MiB
napi.rs: 26.8 MB code ✓ accepted ✗ 413 (≈ 67.3 MB body)
+ 23.7 MB maps
```
Raising the cap does not fix it. The API Worker has 128 MB per isolate
and held about two full copies of the modules: one while it parsed the
body, and one in the `FormData` it sent to Cloudflare.
## What this PR changes
Worker modules now take the same path as static assets. The API streams
them to Cloudflare and never holds the whole bundle.
```
CLI API (request) R2
│ POST deploy/worker-upload-urls │ │
│ {modules:[name,size,sha256]} ──▶│ code without maps ≤ 64 MiB? │
│ │ no → 413 worker_too_large │
│◀── {id, urls[]} ──────────────────│ presigned PUT, size signed │
│ PUT module i ─────────────────────┼──────────────────────────────────▶│ deployment-inputs/{project}/{id}/{i}
│ POST deploy {workerInput} ───────▶│ one LIST: every module present, │
│ │ exact size │
│ │ queue {deploymentId} │
▼
queue: one module open at a time
R2 ──▶ size + SHA-256 check ──▶ multipart body ──▶ Cloudflare
(FixedLengthStream; a mismatch stops the body before
the closing boundary, so Cloudflare gets no complete form)
```
Old clients keep working:
```
CLI ──GET /.well-known/void-platform──▶ directWorkerUpload: true ?
yes → direct path (above)
no → inline workerFiles, as before (temporary, see Rollout)
no usable answer → ask once more, then decide
```
A finalize retry with the same idempotency key finds the admitted
operation before the upload is listed. It gets the receipt even after
the operation's cleanup deleted the modules.
## Decisions
| Question | Decision | Why |
| --- | --- | --- |
| Where the 64 MiB rule lives | API only, when it issues upload URLs |
One place to update when Cloudflare changes it. A Worker that is too
large fails before any byte is uploaded. |
| Source maps | No Void limit | Cloudflare counts them separately and
enforces its own 15 MB gzipped limit. |
| Integrity | SHA-256 per module, checked while streaming | One R2 read
per module, no buffering. A changed module never reaches Cloudflare. |
| Size | `Content-Length` signed into each PUT URL, checked again by one
LIST at finalize | R2 enforces the declared size. One LIST costs less
than a HEAD per module (napi.rs has 668 modules). |
| Inline path for old CLIs | Kept, with its old caps | Its memory limits
still apply. Its 413 text now names Worker modules and says to upgrade
the CLI, instead of blaming assets. |
| Custom Durable Object snapshots | Copied and restored as streams |
They re-upload the user Worker, so they need the same memory bound. |
## Rollout
```
merge ──▶ staging deploys (main) ──▶ prod (fast-forward prod) ──▶ npm release X
│
old CLIs ─────────── inline path keeps working ─────────────────────┤
new CLI + old platform (1.1.11) ── inline fallback ─────────────────┤
new CLI + new platform ─────────── direct path ─────────────────────┘
▼
later: MIN_PLATFORM_VERSION = X, delete the inline fallback
```
## Known limits
- Connected apps that publish with OAuth approval still send inline
`workerFiles`. The approval digest covers inline bytes only, so they
keep the 64 MiB body limit. Changing that needs module digests in the
approved artifact. That is a product decision, so this PR leaves it
alone.
- Until `MIN_PLATFORM_VERSION` reaches the release that ships this, the
new CLI keeps the inline fallback for platforms without
`directWorkerUpload`.
- R2 does not check SHA-256 at PUT time. Wrong bytes with the right size
fail the deploy at the Cloudflare upload step, not at finalize.
## Testing
| Check | Result |
| --- | --- |
| `vp run check` | 33/33 |
| `vp test run` | 712 files, 13,583 tests pass |
| Live Cloudflare upload from workerd (Miniflare, local R2), napi.rs
Worker: 668 modules, 26.8 MB code + 23.7 MB maps | Cloudflare returned
200 and created the script in 9.6 s. Test script deleted. |
| Same upload with one module changed by one byte, same size | The
stream stopped with an integrity error. Cloudflare created no script. |
| New tests | upload-URL route (limit, maps not counted, deploy-token
access), finalize with `workerInput` (queued and inline execution,
missing or wrong-size module, both fields sent, idempotent retry, reused
upload id), streamed multipart (exact bytes, escaped names, mismatch,
retry re-opens streams), Durable Object snapshot streaming, CLI path
selection and request shapes |
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
0 commit comments