Skip to content

build(webcam): stage ONNX Runtime so the segmentation effect can actually run - #530

Merged
EtienneLescot merged 1 commit into
feat/webcam-effectsfrom
claude/stage-onnxruntime
Aug 29, 2026
Merged

build(webcam): stage ONNX Runtime so the segmentation effect can actually run#530
EtienneLescot merged 1 commit into
feat/webcam-effectsfrom
claude/stage-onnxruntime

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #493, follows #529. The compositor has segmented the camera since #493, and on macOS since #529 — but ort is linked load-dynamic and nothing has ever put a library where ensureOnnxRuntimeOnPath looks, so the feature has been dark on every platform since it landed. This provisions it.

The script

scripts/fetch-onnxruntime.mjs, wired into build:mac, build:win and build:win:store. Modelled on fetch-ffmpeg.mjs: pinned to an immutable release tag, SHA-256 verified before the archive is opened, only the plain CPU assets (the gpu_cuda* ones are 200–320 MB and carry NVIDIA redistribution terms).

The extracted library is then checked three ways before it is allowed near electron/native/bin/:

  1. the archive's own LICENSE really does begin MIT License — an asset name is not evidence;
  2. the binary really is a PE / Mach-O / ELF for the target platform;
  3. it really carries the pinned version string.

The digest alone only proves we got the archive we asked for. It says nothing about having lifted the right file out of it, and that is where a re-pin actually goes wrong: a renamed member silently vendors a 20 KB provider stub, and the failure surfaces months later as "the effect does nothing".

It never fails a build. Missing ONNX Runtime is a designed-for state — the compositor logs one line and draws the camera unsegmented.

The test earns its place

crates/Cargo.toml gives ort the feature api-27. That is the minimum runtime minor version it accepts: below it GetApi returns null and ort panics rather than erroring — on the render thread, on the first frame with an effect. Above it, ort logs a compatibility warning into a stderr no packaged app shows.

Nothing would notice those two pins drifting apart. They are in different files, in different languages, edited by different tasks, and the diff of either one reads fine alone. scripts/fetch-onnxruntime.test.mjs cross-checks them in both directions:

crates/Cargo.toml asks ort for api-27, so ONNX Runtime must be >= 1.27.x, but
fetch-onnxruntime.mjs pins 1.19.2. A runtime below the floor makes GetApi return
null and ort PANICS. Re-pin VERSION and every sha256 together.

I checked that message is real by temporarily pinning 1.19.2 — the guard fires rather than passing vacuously, which is the same discipline the existing fetch-ffmpeg.test.mjs was written with.

CI now actually exercises inference

The macOS compositor job stages the library and sets ORT_DYLIB_PATH. Without it runtime_available() is false and every segmentation test returns early — the suite went green having run no inference at all. That is precisely how the ort-panics-when-absent bug got in, per this feature's own docs. ~30 MB, next to nothing beside the brew install ffmpeg already in that job.

Two findings that contradict how this was scoped

The doc estimated "roughly 15 MB per platform". Measured on the real 1.27.1 artifacts:

target library size shipped
win32-x64 onnxruntime.dll 15.4 MB yes
darwin-arm64 libonnxruntime.dylib 38.5 MB yes
linux-x64 libonnxruntime.so 23.7 MB pinned, not shipped
darwin-x64 no upstream build
  • macOS is 2.5x Windows, not parity. strip -x takes it from 38.5 MB to 23.7 MB, but stripping means shipping something other than the artifact the digest vouches for — which is the entire point of pinning. Not done; flagged in the doc in case installer size ever forces the trade. Your call if you'd rather take the 15 MB.
  • Intel Macs cannot have it at all. Microsoft publishes no osx-x86_64 or universal asset for any release from 1.27 on — I checked 1.27.0, 1.27.1, 1.28.0, 1.28.1 and 1.29.0. Since build.yml ships an x64 DMG from macos-15-intel, that DMG goes out without the library and the effect is simply absent there. The script says so plainly and exits 0, because failing would break that release build over a feature designed to degrade. The only alternative is building ORT from source, an fetch-ffmpeg-macos.mjs-sized script for a shrinking platform.

Linux is pinned in the same table but deliberately not wired into build:linux: its back-end carries the shader half only, so the library would be 23 MB of installer for a code path that cannot run. Landing the Linux capture half makes it a one-line change.

An unrelated licence gap I hit on the way

The MediaPipe model is Apache-2.0 and ships inside app.asar (Vite copies public/dist/, and dist is in files). Its provenance README is stripped by electron-builder's "!*.md" filter, and it had no entry in THIRD-PARTY-NOTICES.md — so the attribution Apache-2.0 §4 asks for reached no user at all. Added, alongside the ONNX Runtime entry. Say the word if you'd rather that moved to its own PR.

Verified

Same caveat as #529: ci.yml does not trigger on PRs into feat/webcam-effects, so no job runs here either. Locally, on an M1:

CI job result
Rust test (macOS compositor) 156 passed with ORT_DYLIB_PATH set to the staged file — the full-loop inference test runs for real rather than skipping
Test 182 files, 2178 passed, 2 skipped
Lint clean (14 pre-existing warnings, unchanged)
Type Check clean
Docs OK (34 files)

Beyond the jobs: ran the script for real (fetch → verify → vendor), confirmed it is idempotent, confirmed it detects and re-fetches a corrupted file rather than trusting the filename, and confirmed the darwin-x64 branch exits 0. Also cross-fetched --target=win32-x64 from this Mac, which verified the Windows DLL's PE magic and version string without a Windows machine.

…ually run

The compositor has segmented the webcam since #493, and on macOS since #529 —
but `ort` is linked `load-dynamic`, nothing ever put a library where
`ensureOnnxRuntimeOnPath` looks, and so the feature has been dark on every
platform since it landed. This provisions it.

`scripts/fetch-onnxruntime.mjs`, wired into `build:mac`, `build:win` and
`build:win:store`. Modelled on `fetch-ffmpeg.mjs`: pinned to an immutable release,
SHA-256 verified BEFORE the archive is opened, and the extracted library checked
three ways before it is allowed near `electron/native/bin/` — the archive's own
LICENSE really is MIT, the binary really is a PE/Mach-O/ELF for the target, and it
really carries the pinned version string. The digest alone proves only that we got
the archive we asked for; it says nothing about having lifted the right file out of
it, which is where a re-pin actually goes wrong.

`scripts/fetch-onnxruntime.test.mjs` guards a coupling that crosses a language
boundary: `crates/Cargo.toml` gives `ort` the feature `api-27`, which is the
MINIMUM runtime minor version it accepts — below it `GetApi` returns null and `ort`
panics rather than erroring, on the render thread, on the first frame with an
effect. Nothing else would notice those two pins drifting apart; they are in
different files, different languages, and touched by different tasks.

CI now stages the library for the macOS compositor job and points ORT_DYLIB_PATH at
it. Without that, `runtime_available()` is false and every segmentation test returns
early — the suite went green having exercised no inference at all, which is exactly
how the ort-panics-when-absent bug got in.

Two findings, both now in the docs rather than the estimate they replace:

- macOS costs 38.5 MB, not the "roughly 15 MB per platform" this was scoped as —
  2.5x Windows' 15.4 MB. `strip -x` would halve it, but stripping means shipping
  something other than the artifact the digest vouches for.
- Intel Macs cannot have it: upstream publishes no osx-x86_64 or universal asset
  from 1.27 on. The x64 DMG ships without it and the effect is absent there,
  degrading as designed. The script says so and exits 0 rather than failing that
  build.

Linux is pinned but not wired in: its back-end has the shader half only, so the
library would be 23 MB of installer for a code path that cannot run.

Also adds the MediaPipe model to THIRD-PARTY-NOTICES.md. It is Apache-2.0 and ships
inside app.asar, but its provenance README is stripped by electron-builder's
`"!*.md"` filter — so the attribution reached no user at all.
@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 715c7744-226b-4a3a-9739-f2eb237db15e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot merged commit e81993d into feat/webcam-effects Aug 29, 2026
2 checks passed
@EtienneLescot
EtienneLescot deleted the claude/stage-onnxruntime branch August 29, 2026 17:16
EtienneLescot added a commit that referenced this pull request Aug 29, 2026
…use it

#530 pinned every target but wired only `build:mac`, `build:win` and
`build:win:store`, for a stated reason: the Linux compositor carried the
segmentation shader and nothing that feeds it, so the library would have been 23 MB
of installer for a code path that could not run. "Landing the Linux capture half
makes it a one-line change," said the script. The previous commit lands it; this is
the one line, plus the two places that repeated the reasoning.

It also closes a gap the previous commit opened. That commit removes
`supportsWebcamSegmentation`, so the camera-background control is now visible on
Linux — and a control is only worth showing where a mask can actually reach the
shader. Without the library staged, a packaged Linux build would offer the setting
and do nothing with it, which is precisely what the gate existed to prevent. The two
changes have to travel together.

CI stages it for the Linux compositor job too, mirroring what #530 did for macOS and
for the same reason: without a library on `ORT_DYLIB_PATH`, `runtime_available()` is
false and every segmentation test returns early. The suite goes green having
exercised no inference at all — which is exactly how the `ort`-panics-when-absent bug
got in. It is what makes `the_whole_loop_produces_a_mask_from_compose_frame_alone` a
real test on that runner rather than a skipped one. `fetch-onnxruntime.mjs` imports
only node builtins, so it needs no `npm ci`, same as `fetch:ffmpeg:sdk` beside it.

Verified locally rather than assumed: `node scripts/fetch-onnxruntime.mjs
--target=linux-x64` downloads the pinned asset, passes the SHA-256 check before
unpacking, and passes all three post-extraction checks — MIT LICENSE, ELF magic,
version string — yielding a 22.6 MB `libonnxruntime.so`. Pointing `ORT_DYLIB_PATH` at
THAT file (not a hand-installed one) runs the whole compositor suite green with
inference actually executing.

No new pin and no new digest: the `linux-x64` entry, its SHA-256 and the `api-27`
cross-check in `fetch-onnxruntime.test.mjs` all already existed and are untouched.
EtienneLescot added a commit that referenced this pull request Aug 29, 2026
…use it

#530 pinned every target but wired only `build:mac`, `build:win` and
`build:win:store`, for a stated reason: the Linux compositor carried the
segmentation shader and nothing that feeds it, so the library would have been 23 MB
of installer for a code path that could not run. "Landing the Linux capture half
makes it a one-line change," said the script. The previous commit lands it; this is
the one line, plus the two places that repeated the reasoning.

It also closes a gap the previous commit opened. That commit removes
`supportsWebcamSegmentation`, so the camera-background control is now visible on
Linux — and a control is only worth showing where a mask can actually reach the
shader. Without the library staged, a packaged Linux build would offer the setting
and do nothing with it, which is precisely what the gate existed to prevent. The two
changes have to travel together.

CI stages it for the Linux compositor job too, mirroring what #530 did for macOS and
for the same reason: without a library on `ORT_DYLIB_PATH`, `runtime_available()` is
false and every segmentation test returns early. The suite goes green having
exercised no inference at all — which is exactly how the `ort`-panics-when-absent bug
got in. It is what makes `the_whole_loop_produces_a_mask_from_compose_frame_alone` a
real test on that runner rather than a skipped one. `fetch-onnxruntime.mjs` imports
only node builtins, so it needs no `npm ci`, same as `fetch:ffmpeg:sdk` beside it.

Verified locally rather than assumed: `node scripts/fetch-onnxruntime.mjs
--target=linux-x64` downloads the pinned asset, passes the SHA-256 check before
unpacking, and passes all three post-extraction checks — MIT LICENSE, ELF magic,
version string — yielding a 22.6 MB `libonnxruntime.so`. Pointing `ORT_DYLIB_PATH` at
THAT file (not a hand-installed one) runs the whole compositor suite green with
inference actually executing.

No new pin and no new digest: the `linux-x64` entry, its SHA-256 and the `api-27`
cross-check in `fetch-onnxruntime.test.mjs` all already existed and are untouched.
EtienneLescot added a commit that referenced this pull request Aug 30, 2026
…use it

#530 pinned every target but wired only `build:mac`, `build:win` and
`build:win:store`, for a stated reason: the Linux compositor carried the
segmentation shader and nothing that feeds it, so the library would have been 23 MB
of installer for a code path that could not run. "Landing the Linux capture half
makes it a one-line change," said the script. The previous commit lands it; this is
the one line, plus the two places that repeated the reasoning.

It also closes a gap the previous commit opened. That commit removes
`supportsWebcamSegmentation`, so the camera-background control is now visible on
Linux — and a control is only worth showing where a mask can actually reach the
shader. Without the library staged, a packaged Linux build would offer the setting
and do nothing with it, which is precisely what the gate existed to prevent. The two
changes have to travel together.

CI stages it for the Linux compositor job too, mirroring what #530 did for macOS and
for the same reason: without a library on `ORT_DYLIB_PATH`, `runtime_available()` is
false and every segmentation test returns early. The suite goes green having
exercised no inference at all — which is exactly how the `ort`-panics-when-absent bug
got in. It is what makes `the_whole_loop_produces_a_mask_from_compose_frame_alone` a
real test on that runner rather than a skipped one. `fetch-onnxruntime.mjs` imports
only node builtins, so it needs no `npm ci`, same as `fetch:ffmpeg:sdk` beside it.

Verified locally rather than assumed: `node scripts/fetch-onnxruntime.mjs
--target=linux-x64` downloads the pinned asset, passes the SHA-256 check before
unpacking, and passes all three post-extraction checks — MIT LICENSE, ELF magic,
version string — yielding a 22.6 MB `libonnxruntime.so`. Pointing `ORT_DYLIB_PATH` at
THAT file (not a hand-installed one) runs the whole compositor suite green with
inference actually executing.

No new pin and no new digest: the `linux-x64` entry, its SHA-256 and the `api-27`
cross-check in `fetch-onnxruntime.test.mjs` all already existed and are untouched.
EtienneLescot added a commit that referenced this pull request Aug 30, 2026
…use it

#530 pinned every target but wired only `build:mac`, `build:win` and
`build:win:store`, for a stated reason: the Linux compositor carried the
segmentation shader and nothing that feeds it, so the library would have been 23 MB
of installer for a code path that could not run. "Landing the Linux capture half
makes it a one-line change," said the script. The previous commit lands it; this is
the one line, plus the two places that repeated the reasoning.

It also closes a gap the previous commit opened. That commit removes
`supportsWebcamSegmentation`, so the camera-background control is now visible on
Linux — and a control is only worth showing where a mask can actually reach the
shader. Without the library staged, a packaged Linux build would offer the setting
and do nothing with it, which is precisely what the gate existed to prevent. The two
changes have to travel together.

CI stages it for the Linux compositor job too, mirroring what #530 did for macOS and
for the same reason: without a library on `ORT_DYLIB_PATH`, `runtime_available()` is
false and every segmentation test returns early. The suite goes green having
exercised no inference at all — which is exactly how the `ort`-panics-when-absent bug
got in. It is what makes `the_whole_loop_produces_a_mask_from_compose_frame_alone` a
real test on that runner rather than a skipped one. `fetch-onnxruntime.mjs` imports
only node builtins, so it needs no `npm ci`, same as `fetch:ffmpeg:sdk` beside it.

Verified locally rather than assumed: `node scripts/fetch-onnxruntime.mjs
--target=linux-x64` downloads the pinned asset, passes the SHA-256 check before
unpacking, and passes all three post-extraction checks — MIT LICENSE, ELF magic,
version string — yielding a 22.6 MB `libonnxruntime.so`. Pointing `ORT_DYLIB_PATH` at
THAT file (not a hand-installed one) runs the whole compositor suite green with
inference actually executing.

No new pin and no new digest: the `linux-x64` entry, its SHA-256 and the `api-27`
cross-check in `fetch-onnxruntime.test.mjs` all already existed and are untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants