Change realm download to not use in-memory blob#3944
Conversation
Previously, realm downloads used fetch() + response.blob() which loaded the entire archive into JavaScript memory before triggering the download. For large realms, this could cause memory issues. Now the client uses native browser download streaming by: - Adding support for auth token via query param on the server - Creating an anchor element to trigger direct browser navigation - Letting the browser handle the download natively (streams to disk) Adds server-side tests for token query param auth and Playwright E2E test. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Preview deployments |
Simplifies the download implementation by using BoxelButton with @as='anchor' and the download attribute, rather than programmatically creating and clicking an anchor element via JavaScript. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4a6615042
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| private get downloadRealmURL() { | ||
| let downloadURL = new URL('/_download-realm', this.args.realmURL); | ||
| downloadURL.searchParams.set('realm', this.args.realmURL); | ||
| // Include token for authenticated streaming download (browser navigates directly) | ||
| let token = this.realm.token(this.args.realmURL); |
There was a problem hiding this comment.
Reauth is bypassed for expired download tokens
This link now bakes the current realm token into the URL, which means downloads no longer go through network.authedFetch’s authorization middleware (see packages/runtime-common/authorization-middleware.ts) that retries on 401 by calling reauthenticate. When a user has been logged in long enough for the JWT to expire, clicking “Download” will yield a 401 and there’s no retry or recovery path, whereas the previous implementation would refresh the token and succeed. Consider using a short-lived download token minted on demand or a fetch-based flow that can reauthenticate before redirecting.
Useful? React with 👍 / 👎.
| let downloadURL = new URL('/_download-realm', this.args.realmURL); | ||
| downloadURL.searchParams.set('realm', this.args.realmURL); | ||
| // Include token for authenticated streaming download (browser navigates directly) | ||
| let token = this.realm.token(this.args.realmURL); |
There was a problem hiding this comment.
Another approach could be to attach a signature obtained by hashing the token plus the URL, so that token only authorizes downloading the particular URL you are sending. Might be good in case we need to use this approach for anything else.
When authenticating via query param token, a signature is now required that binds the token to the specific download URL. This prevents someone from intercepting the URL and reusing the token for other API endpoints. The signature is HMAC-SHA256(token, urlPath) computed by: - Browser: Web Crypto API (async) - Server: Node.js crypto module (sync) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This changes the endpoint to support either a header or token so the UI can download via a link and not need to contain the entire realm in memory.
I also added a Playwright test since this uses the endpoint in a slightly different way.