Summary
The embeddable web component (sd-component.js) is broken on cross-origin (third-party) pages at HEAD. Embedded diagrams load their project data but cannot render or simulate, because the WASM engine now runs in a module Web Worker whose chunk is fetched from app.simlin.com with no CORS headers and no same-origin fallback. This regression class is invisible to every current test and deploy smoke step, because they are all same-origin.
Finding verified independently by two audit agents and re-verified against the source below.
Mechanism
-
The engine rewrite moved WASM execution into a module Web Worker:
src/engine/src/backend-factory.browser.ts:23 does
new Worker(new URL('./engine-worker.js', import.meta.url), { type: 'module' })
with no Blob-URL / same-origin fallback. On any worker load failure, worker.onerror (lines 43-50) calls backend.handleWorkerError(...), which rejects all pending and subsequent engine operations.
-
External sites hotlink the component by exact path:
https://app.simlin.com/static/js/sd-component.js is not content-hashed (src/app/config/rsbuild/rsbuild.component.config.js:31 fixed filename + :41 filenameHash: false).
- Documented as a hard contract in
docs/dev/deploy.md:103: "external sites <script src> this exact path."
-
The component build's assetPrefix: 'auto' (rsbuild.component.config.js:51) resolves the worker chunk to an absolute app.simlin.com URL. The config comment itself states publicPath becomes https://app.simlin.com/static/js/ and that the worker URL resolves as new Worker(new URL(publicPath + chunkUrl, baseURI)). So even when sd-component.js itself is loaded from a third-party page, the worker chunk is fetched cross-origin from app.simlin.com.
-
GAE serves /static via app.yaml (url: /static, static_dir: public/static, lines 8-9) with no http_headers block, hence no Access-Control-Allow-Origin. A cross-origin module worker fetch is subject to CORS; without the header the fetch fails, worker.onerror fires, and every engine op rejects.
Net effect on a third-party embedding page: the diagram loads project data (the Express /api surface does send CORS), but the engine never initializes, so the embed cannot render or simulate.
Why it matters
- Broken product contract: the embeddable web component is an explicitly documented external-facing surface (
docs/dev/deploy.md:103). Third-party embeds are its entire reason to exist, and they are non-functional at HEAD.
- Silent / undetectable regression class: there is no test or deploy smoke step that exercises a cross-origin embed. The deploy smoke list (
docs/dev/deploy.md:102-103) only does same-origin curl checks, which cannot catch this. Same-origin usage inside app.simlin.com keeps working, so the breakage is invisible to maintainers.
- Historical nuance: in the 2022 production build the engine ran inline on the main thread. That older build also fetched WASM cross-origin, so embeds may already have been broken before this rewrite. Either way the contract has never been guarded.
Components affected
src/engine -- backend-factory.browser.ts (module worker instantiation, no fallback)
src/app -- config/rsbuild/rsbuild.component.config.js (assetPrefix: 'auto' -> absolute worker chunk URL; non-hashed sd-component.js)
- deploy --
app.yaml (/static handler has no http_headers), docs/dev/deploy.md (smoke list)
Possible approaches (not implemented here)
These are candidate fixes for whoever picks this up; choose after design discussion. Do not treat as prescribed.
- Serve CORS headers for the worker/WASM chunks. Add
http_headers with Access-Control-Allow-Origin to the /static handler in app.yaml (at least for the worker chunk and *.wasm). Simplest, but widens CORS on all of /static; scope carefully.
- Same-origin Blob-URL worker shim. In
backend-factory.browser.ts, fetch() the worker script and wrap it in a same-origin Blob URL before new Worker(...) (the standard cross-origin worker workaround). The worker still needs to fetch its own WASM/imports cross-origin, so this likely must be combined with (1) and/or importScripts-style absolute-URL handling. A module worker created from a blob also needs care so its relative import.meta.url-based fetches resolve to app.simlin.com.
- Add a cross-origin embed check to the deploy smoke list so this regression class is caught going forward (e.g. load
sd-component.js from a different origin in a headless browser and assert a diagram renders/simulates). This is orthogonal to 1/2 and worth doing regardless of which fix lands.
How it was discovered
Identified during a deploy-risk audit of HEAD; verified by two independent agents and re-confirmed by reading backend-factory.browser.ts, rsbuild.component.config.js, app.yaml, and docs/dev/deploy.md. Not currently tracked in GitHub issues or docs/tech-debt.md.
Summary
The embeddable web component (
sd-component.js) is broken on cross-origin (third-party) pages at HEAD. Embedded diagrams load their project data but cannot render or simulate, because the WASM engine now runs in a module Web Worker whose chunk is fetched fromapp.simlin.comwith no CORS headers and no same-origin fallback. This regression class is invisible to every current test and deploy smoke step, because they are all same-origin.Finding verified independently by two audit agents and re-verified against the source below.
Mechanism
The engine rewrite moved WASM execution into a module Web Worker:
src/engine/src/backend-factory.browser.ts:23doesnew Worker(new URL('./engine-worker.js', import.meta.url), { type: 'module' })with no Blob-URL / same-origin fallback. On any worker load failure,
worker.onerror(lines 43-50) callsbackend.handleWorkerError(...), which rejects all pending and subsequent engine operations.External sites hotlink the component by exact path:
https://app.simlin.com/static/js/sd-component.jsis not content-hashed (src/app/config/rsbuild/rsbuild.component.config.js:31fixed filename +:41filenameHash: false).docs/dev/deploy.md:103: "external sites<script src>this exact path."The component build's
assetPrefix: 'auto'(rsbuild.component.config.js:51) resolves the worker chunk to an absoluteapp.simlin.comURL. The config comment itself statespublicPathbecomeshttps://app.simlin.com/static/js/and that the worker URL resolves asnew Worker(new URL(publicPath + chunkUrl, baseURI)). So even whensd-component.jsitself is loaded from a third-party page, the worker chunk is fetched cross-origin fromapp.simlin.com.GAE serves
/staticviaapp.yaml(url: /static,static_dir: public/static, lines 8-9) with nohttp_headersblock, hence noAccess-Control-Allow-Origin. A cross-origin module worker fetch is subject to CORS; without the header the fetch fails,worker.onerrorfires, and every engine op rejects.Net effect on a third-party embedding page: the diagram loads project data (the Express
/apisurface does send CORS), but the engine never initializes, so the embed cannot render or simulate.Why it matters
docs/dev/deploy.md:103). Third-party embeds are its entire reason to exist, and they are non-functional at HEAD.docs/dev/deploy.md:102-103) only does same-origincurlchecks, which cannot catch this. Same-origin usage inside app.simlin.com keeps working, so the breakage is invisible to maintainers.Components affected
src/engine--backend-factory.browser.ts(module worker instantiation, no fallback)src/app--config/rsbuild/rsbuild.component.config.js(assetPrefix: 'auto'-> absolute worker chunk URL; non-hashedsd-component.js)app.yaml(/statichandler has nohttp_headers),docs/dev/deploy.md(smoke list)Possible approaches (not implemented here)
These are candidate fixes for whoever picks this up; choose after design discussion. Do not treat as prescribed.
http_headerswithAccess-Control-Allow-Originto the/statichandler inapp.yaml(at least for the worker chunk and*.wasm). Simplest, but widens CORS on all of/static; scope carefully.backend-factory.browser.ts,fetch()the worker script and wrap it in a same-origin Blob URL beforenew Worker(...)(the standard cross-origin worker workaround). The worker still needs to fetch its own WASM/imports cross-origin, so this likely must be combined with (1) and/orimportScripts-style absolute-URL handling. A module worker created from a blob also needs care so its relativeimport.meta.url-based fetches resolve toapp.simlin.com.sd-component.jsfrom a different origin in a headless browser and assert a diagram renders/simulates). This is orthogonal to 1/2 and worth doing regardless of which fix lands.How it was discovered
Identified during a deploy-risk audit of HEAD; verified by two independent agents and re-confirmed by reading
backend-factory.browser.ts,rsbuild.component.config.js,app.yaml, anddocs/dev/deploy.md. Not currently tracked in GitHub issues ordocs/tech-debt.md.