Reproducible from-source Nix flake for the mcp-chrome browser extension #1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Package the mcp-chrome Chrome MCP extension (
~/projects/mcp-chrome, an upstream GitHub checkout — pnpm + WXT monorepo) as a reproducible, from-source Nix flake. Motivation: the native-messaging bridge kept failing in non-reproducible ways (stale manifest, missingfastify, GC'd node paths, two competingmcp-chrome-bridgeinstalls). A flake makes the build deterministic and cacheable, and pins a path-independent extension ID so native messaging stays stable.Flake design (
flake.nixat repo root of the checkout, staged not committed)nixpkgs=nixos-unstable;flake-utils.wasm-bindgen-clibuilt in-flake, pinned to 0.2.121 — the lockedwasm-bindgencrate is 0.2.121 (forced byweb-sys 0.3.98, which hard-pins=0.2.121); nixpkgs ships only 0.2.117 and a CLI/crate mismatch corrupts wasm output.wasm-simd(packages/wasm-simd) compiled from source, nowasm-pack(avoids its network fetch):cargo build --target wasm32-unknown-unknown(needslldforwasm-ld) →wasm-bindgen --target web→wasm-opt -O3. This reproduceswasm-pack --releaseoffline. A pinnedCargo.lockwas generated (none existed upstream).pnpm_10.fetchDeps { fetcherVersion = 2; }offline deps → buildchrome-mcp-shared→wxt prepare→wxt build; the from-source wasm is copied intoapp/chrome-extension/workers/so nothing prebuilt is reused.chromeExtensionKeyparameter → injectsCHROME_EXTENSION_KEYfor a stable, path-independent extension ID (today's IDhbdgbgagpkpjffpklnamcljpakneikeeis path-derived; a store path would change it and break the pinned native-messagingallowed_origins).nix run .#buildapp +build.sh:nom-governed build for a separate terminal, with env overrides (TARGET,OUT_LINK,SUBS,CHROME_EXTENSION_KEY).Pinned FOD hashes
wasm-bindgen-clisrc:sha256-ZOMgFNOcGkO66Jz/Z83eoIu+DIzo3Z/vq6Z5g6BDY/w=wasm-bindgen-clicargoHash:sha256-DPdCDPTAPBrbqLUqnCwQu1dePs9lGg85JCJOCIr9qjU=pnpmDeps:sha256-fBCfTzwp/MYabu9duz9FQK+HbSiyQWD8rzJhndFO+Xk=Root cause investigated: ~70-min build
The extension build hung for 70+ min in Vite/rollup. Root cause:
app/chrome-extension/utils/semantic-similarity-engine.ts:1did a static value import of@xenova/transformers(~44 MBdist/, transitively pullingonnxruntime-web@1.14). That module was statically reachable from 4 WXT entrypoints (background/index.ts,background/semantic-similarity.ts,offscreen/main.ts,popup/App.vue). WXT builds each entrypoint as its own sequential rollup pass, so ≥4 passes each parsed/tree-shook the entire transformers+onnx graph, single-threaded,minify:false. Not a flake defect — reproducible anywhere.Fix applied (behaviour-neutral lazy-split)
In
semantic-similarity-engine.ts: replaced the staticimport { AutoTokenizer, env as TransformersEnv }with a memoized dynamicimport('@xenova/transformers')(loadTransformers()), destructured at the two already-asyncinit methods. The type-only import is kept (zero bundle cost). The engine is only ever initialized behind async/conditional paths, so behaviour is unchanged; rollup now code-splits transformers into a lazy chunk instead of inlining it into 4 entrypoints. (Edits the upstream checkout — coordinate with the localization work happening in parallel in the same tree; target files were verified clean of those edits.)Environment note: NCPS cache outage
All Nix builds during this work had to bypass the cluster substituters:
nix-cache-custom.oleks.spaceunreachable andnix-cache-mirror.oleks.space's narinfo DB (postgresql.infra.svc.cluster.local:5432) refusing connections → HTTP 500. Workaround baked intobuild.sh/apps.build:--option substituters https://cache.nixos.org(setSUBS=""once NCPS recovers). Separate cluster incident — seecluster:storage.Status / next steps
wasm-simdbuilds from source (valid wasm output verified)wxt build$outpost-fix and measure build time (build in progress)CHROME_EXTENSION_KEYand re-register native-messagingallowed_originsto the key-derived IDSUBSworkaroundRefiled to the correct repo: oleks/mcp-chrome#1 (oleks/mcp-chrome#1). The flake now lives on
oleks/mcp-chromemain(d7c097a). Closing here — track there.Final diagnosis & resolution (scope change)
The full-extension from-source target is not viable. Transformers was ruled out as the cause.
Evidence chain (nixbuild.net build IDs via the
cluster:cinixbuild skill + one local emmett run):import()out_of_memory17.7 GB; 9141631client_disconnect30.6 GB / 141 minclient_disconnect21.8 GB / 58.9 GB alloc / 146 minresolve.alias→ prebuilt distvite.resolve.aliasinto its per-entrypoint rollup builds)wasmPathsThe externalize
postPatchusedsubstituteInPlace --replace-fail, so it provably applied (a missing anchor abortspostPatchimmediately; instead it ran for over an hour). With transformers genuinely externalized the build's CPU/time/memory shape was unchanged → transformers was never the dominant cost.Root cause is intrinsic to upstream
wxt build: ~12 sequential MV3 entrypoints, 32 MB of committed onnxruntime wasm shuffled byvite-plugin-static-copy, Vue + @vue-flow + markstream across many entrypoints,minify:false. No config-level patch carried by the flake removes that; it does not complete on nixbuild.net (auto-scaled to 58 GB) or locally (OOM-killednix-daemon+ k3s pods on the first unguarded attempt).Delivered (finalized
flake.nix+build.sh)packages.default/packages.wasm-simd— Rust→wasm, ~22 s, reproducible, verified green (build 9143033 etc.)packages.wasm-bindgen-cli— 0.2.121, FOD-pinned, matches the locked crateapps.build/build.shdefaultTARGET→.#default(the viable target);nix buildcan no longer land on the broken derivationpackages.chrome-mcp-extension— kept but documented KNOWN-BROKEN / upstream-pathological; speculative transformerspostPatchremoved as proven-dead complexitysemantic-similarity-engine.tspurely as a runtime-startup improvement (orthogonal; not a build fix)SUBSworkaround is now opt-in onlySpun-off / not regressed
oleks/claude-plugin-cluster#14(nixbuild-build-introspection skill) — delivered v0.41.0, used here to get the per-build memory/status that finally proved the OOM loop.Closing as completed (resolved with scope change): the flake is finalized for the reproducible subset; the extension bundle is conclusively an upstream build-architecture problem, not a packaging gap.