Route browser fs and logs endpoints directly to the VM - #178
Open
tnsardesai wants to merge 5 commits into
Open
Conversation
Add fs and logs to the default direct-to-VM subresource prefixes so filesystem operations and log streaming use the cached browser base_url and JWT instead of the control plane. Extensions, replays and telemetry/events stay on the control plane.
The probe Request built for cache sniffing derives a multipart content-type that carries its own boundary. Copying that header onto the routed request while re-encoding the body produced a boundary mismatch, so the browser VM rejected direct fs.upload calls with "failed to read form part". Let the routed fetch derive the header when the body derives its own content-type.
Narrows the default prefix so a future logs read served by the control plane is not swept onto the browser VM.
tnsardesai
marked this pull request as ready for review
September 2, 2026 20:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
fsandlogs/streamto the defaultKERNEL_BROWSER_ROUTING_SUBRESOURCESprefixes, so every/browsers/{id}/fs/*operation (JSON, binary read/write, multipart upload, watch SSE) and/browsers/{id}/logs/streamis rewritten by the routing fetch to the browser VM's cachedbase_urlwith?jwt=and no API-keyAuthorization.Browser lifecycle/metadata, extensions, replays,
telemetry/events, and anything else underlogs/stay on the control plane.KERNEL_BROWSER_ROUTING_SUBRESOURCESstill overrides the list, and an empty value still disables routing.Fixes one latent bug the new routing surfaced:
createRoutingFetchbuilds a probeRequestfrom the incoming body for cache sniffing, and that construction derives amultipart/form-datacontent-type carrying its own boundary. Copying that header onto the routed request while re-encoding theFormDatabody produced a boundary mismatch, and the VM rejected directfs.uploadcalls with400 failed to read form part. The routed init now drops an inherited content-type when the body derives its own (FormData,URLSearchParams,Blob) and the caller did not set one explicitly.fs.uploadis the first routed multipart endpoint, so nothing previously routed was affected.Query preservation, header stripping, signal propagation, stale-JWT eviction and the fallback body construction are otherwise unchanged; the added tests pin the fallback's existing body replay behavior for the body shapes the SDK produces.
Tests
tests/lib/browser-routing.test.ts:fsandlogs/stream, and still excludetelemetry/events,fsx/...,logs,logs/history,logstream,extensions,replayslogs/streamroutes to the VM whilelogs,logs/historyandlogstreamstay on the API originlistFiles), binaryreadFile, binarywriteFile, multipartupload(indexedfiles[0][…]field names),fs/watch/{id}/eventsSSE andlogs/streamSSE route to the VM with the query preserved,?jwt=appended and noauthorizationheaderrequest.formData(), which fails on the boundary mismatch described above (verified: the test fails without the fix)logs.streamaborts stay wired to the routed request's signalwriteFilebinary body byte for byte and theuploadmultipart body on the control plane with bearer auth, and evicts the routetelemetry/events,replays,extensions) keep the API origin and bearer authRan locally:
jest(421 passed, 227 skipped),prettier --check .,eslint .,tsc,./scripts/build.Live validation
Ran against staging with real headless browsers:
fs.writeFile,fs.readFile,fs.listFiles,fs.upload(two entries),fs.watch.start/events/stopandlogs.streamall hithttps://<browser-host>/browser/kernel/...?jwt=...and returned the expected data, with uploaded files reading back with the correct per-entry contents.telemetry/eventsand the browser delete stayed on the control plane. Thefs.uploadfailure above was found this way and re-verified as fixed; re-validated after switching the default tologs/stream.Note
Medium Risk
Changes default request paths for fs and streaming logs (data and uploads) and touches request header/body handling for routed multipart; mitigated by control-plane fallback, env allowlist, and broad test coverage.
Overview
Default direct-to-VM routing now includes
fsandlogs/stream, so browser file operations (list/read/write/upload, watch SSE) and live log SSE go to the session VM with JWT query auth instead of the API control plane. Other paths underlogs/, plustelemetry/events, extensions, and replays, stay on the API origin.Multipart fix: When rebuilding the routed
fetchbody, inheritedcontent-typefrom an intermediateRequestcould disagree with a re-encodedFormDataboundary and breakfs.upload. Routed requests now dropcontent-typewhen the body isFormData/URLSearchParams/Bloband the caller did not set it explicitly, so the runtime derives a matching boundary.Tests cover prefix matching, VM routing for binary and multipart bodies, SSE abort wiring, stale-JWT fallback with body replay, and
KERNEL_BROWSER_ROUTING_SUBRESOURCESoverrides.Reviewed by Cursor Bugbot for commit dd066e9. Bugbot is set up for automated code reviews on this repo. Configure here.