Run plugins in a QuickJS sandbox in the browser - #580
Draft
gschier wants to merge 5 commits into
Draft
Conversation
Adds packages/plugin-sandbox: QuickJS-ng compiled to wasm, running in a dedicated worker, with a runtime shell inside it that loads a plugin bundle and answers the same InternalEventPayload events the Node runtime answers. Plugins are unmodified. Wires the browser host's template function, authentication, cURL import and template render commands to it, and relaxes TemplateCallback's Send bound on wasm32 so the engine's renderer can call back out to a plugin.
The sandbox's context builder was a near-copy of the Node runtime's. Both now come from createPluginContext in @yaakapp-internal/lib, with each runtime supplying only a transport. The two places hosts genuinely differ are optional transport methods: `stream` (a window reporting navigation until it closes) and `form` (a prompt that re-renders as values change). The sandbox has neither, so openUrl refuses and a form is drawn once from its defaults.
Rationale that explains a decision rather than the code below it belongs in the sandbox README or the PR, not in a header paragraph on every file.
Rebased onto main after #572 merged. The wasm crate moved to crates/yaak-wasm and crates-server/yaak-web is now the server, so the render_template export, the regenerated pkg/ and the proxy/server wording move with them.
gschier
force-pushed
the
claude/serene-dewdney-4c1527
branch
from
August 18, 2026 23:36
45adc2d to
252b151
Compare
| if (message.type === "host_result") { | ||
| const settle = pendingHostCalls.get(message.id); | ||
| pendingHostCalls.delete(message.id); | ||
| settle?.(message.error != null ? new Error(message.error) : (message.reply ?? "{}")); |
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.
Stacks on #572 (base is that branch, not main). The bearer proof needs the send proxy.
Adds
packages/plugin-sandbox: QuickJS compiled to wasm, in a dedicated worker, with a runtime shell inside it that loads a plugin bundle and answers the sameInternalEventPayloadevents the Node runtime answers. The three proof plugins are unmodified; only their bundling changes.Pins (reasons in
packages/plugin-sandbox/README.md, which is written as the spec for the eventual Rust host):awaitthrough a deferred promise.terminate()only if it is not the worker holding the database.Perf. GitHub's 12.3 MB OpenAPI description, 1220 requests, identical output in both engines:
8x on the first run, ~12x once V8 has compiled. Inside the 10 to 50x folklore, and first run is what a user waits for. Importers stay in the sandbox. Reproduce with
node packages/plugin-sandbox/bench/import.mjs <spec.json> 6.Verified against the production build in real Chrome, screenshots in the thread:
timestamp.iso8601()rendered into a sent URL and echoed by httpbin, a pasted curl command becoming a request, andAuthorization: Bearer ...applied by auth-bearer and echoed back through the send proxy.Desktop is untouched. Three helpers moved from
packages/plugin-runtime/srcinto@yaakapp-internal/libso both runtimes share them; the vendoredindex.cjsrebuilds byte identical.Known gaps, all slice 2:
SendableHttpRequest(that type lives inyaak-http, which does not build for wasm32). Fine for bearer, basic and apikey. AWS SigV4 and OAuth 1.0 would sign a slightly different URL than the proxy sends, so they are refused instead of mis-signed.template-function-uuiddoes not run:uuidreaches fornode:crypto. Used-timestampinstead. The noble conversion is the decided fix.scripts/bundle-sandbox-plugins.mjs.