# Native source rebuild review

Review date: 2026-09-23. This is an implementation review and a tested patch-application ledger, not a completed WASM rebuild or a legal compliance determination.

## Exact library/build inputs

| Input | Exact revision | Required material / role |
| --- | --- | --- |
| Replicad | `e4b05f67dc4e2393a876ce8c5064a9c93db05bf1` | `packages/replicad-opencascadejs/build-config/custom_build_single.yml`, six adjacent `wrappers/*.cpp`, original `build-source/` templates and package/build instructions. Full archive is appropriate. |
| OpenCascade.js | `ebd263f15337b440b391492af073662707e86482` | Full source tree, scripts, Python/Node locks, filters, patches, pristine libembind snapshot plus hashes. |
| OCCT | `b8f597c677811d1f9f4d8a97f5ae2825c0353a42` / V8_0_1 | Full source and original Git commit metadata. |
| RapidJSON | `24b5e7a8b27f42fa16b96fc70aade9106cf7102f` | Source/header tree plus original Git commit metadata. |
| FreeType | `de8b92dd7ec634e9e2b25ef534c54a3537555c11` / 2.13.0 | Source/header tree plus original Git commit metadata. Required build input; final binary inclusion remains unproved. |
| Emscripten | 5.0.1; supplied source revision `8c5f43157a3f069ade75876e23061330521eabde` | Delivered source excludes top-level `test/` but retains emitted JS runtime and compiled system runtime source, including musl/mimalloc/libc++/libc++abi. Its libembind source is additionally patched by OCJS. |
| emsdk | Supplied source revision `14c18b569f55138fe4963924162244251f454fb0` | Installer/manifest source, then install and activate fixed SDK 5.0.1. Does not require `.git` for this installation path. |

Preserving all four core bare repositories (OCJS, OCCT, RapidJSON, FreeType) is a valid compact self-hosted route. Clone them locally after extraction to obtain source worktrees with original commit identities. Do not run stock `clone-deps.sh` against plain OCCT/RapidJSON/FreeType source directories: it tries to clone into a nonempty directory when `.git` is absent.

Two other script requirements rule out blindly omitting Git metadata:

- `scripts/patch-state.py` uses OCCT `git status`, `git rev-parse HEAD` and tracked-file restoration with strict subprocess checks.
- `scripts/cmake-state.py` uses `git rev-parse HEAD` for `OCJS_ROOT/deps/rapidjson` and `OCJS_ROOT/deps/freetype`, also with strict subprocess checks. These paths are hardcoded for identity calculation, so environment root overrides alone do not solve this.

The OCJS worktree itself may be a plain archive only when `OCJS_SOURCE_COMMIT=ebd263f15337b440b391492af073662707e86482` and `SOURCE_DATE_EPOCH=1785713531` are explicitly provided. The latter is that commit's committer timestamp, `2026-08-02T23:32:11Z`. Keeping its Git repository is simpler.

## Host tools and downloaded inputs

Use a supported Linux x86_64/aarch64 or macOS arm64 host. The upstream bootstrap has LLVM17 prebuilt mappings only for those three targets. Linux prerequisites reflected in the Dockerfile are Git, Bash, a C++ toolchain (`build-essential` and `libc6-dev`), curl, xz, unzip, Doxygen, and ordinary command-line tools. Install uv and Node; upstream Docker uses Node24, while package.json requires Node>=22. These tool versions are not all frozen by the upstream repository, so do not promise bit-for-bit reproduction.

- `.python-version` is **3.14.4**; create the local environment with that exact version before `clone-deps.sh`, whose own new-environment fallback only asks for 3.14.
- `uv.lock` and `pyproject.toml` pin **cerberus1.3.8, cmake4.3.2, libclang18.1.1, pyyaml6.0.3** and development dependencies; `uv sync --frozen --all-groups` is the upstream install command.
- `package-lock.json` is installed by `npm ci`; this supplies locked Node build tools such as Nx22.5.2. Preserve it.
- LLVM**17.0.6** prebuilt parse-side libc++ and Clang resource headers are a separate build input. `DEPS.json` contains platform filenames, SHA256s and release URLs; `clone-deps.sh` downloads and verifies them. This is not the Emscripten compiler's LLVM version.
- Doxygen is deliberately unpinned. The script accepts the system version and records it; upstream says its documentation extraction works across1.9.x–1.16.x.
- emsdk installs compiler/tool binaries, including its own Node, Binaryen/wasm-opt, and compiler/linker. The route below compiles the CAD library from source; it does not bootstrap every host compiler/tool from source.
- Replicad's generated YAML and wrappers are already committed, so ytt is unnecessary for rebuilding this exact recipe. Regenerating YAML templates invokes ytt, whose version is not pinned in Replicad's package script.

For ordinary host tools, preserve versions, manifests, lockfiles, upstream source locations and install steps. Mechanically, their full sources do not need to be in this CAD source tree. This is not a legal opinion that a URL is an adequate substitute for any corresponding-source obligation. Emscripten's runtime sources require separate treatment because portions become part of the delivered JS/WASM.

## Concrete native route using self-hosted bare source archives

The following assumes the four downloaded bare archives have been extracted as `sourceRoot/{opencascade-js-git,occt-git,rapidjson-git,freetype-git}`; Replicad has been extracted beneath `sourceRoot/replicad`; and the provided emsdk source archive is at `sourceRoot/emsdk.tar.gz`. Adapt only those extraction names to the final package layout. Use a new, empty `rebuildRoot`.

```bash
sourceRoot=/absolute/path/to/extracted-source-delivery
rebuildRoot=/absolute/path/to/fresh-rebuild
mkdir -p "$rebuildRoot"

git clone --no-hardlinks "$sourceRoot/opencascade-js-git" "$rebuildRoot/opencascade.js"
mkdir -p "$rebuildRoot/opencascade.js/deps"
git clone --no-hardlinks "$sourceRoot/occt-git" "$rebuildRoot/opencascade.js/deps/OCCT"
git clone --no-hardlinks "$sourceRoot/rapidjson-git" "$rebuildRoot/opencascade.js/deps/rapidjson"
git clone --no-hardlinks "$sourceRoot/freetype-git" "$rebuildRoot/opencascade.js/deps/freetype"
mkdir -p "$rebuildRoot/opencascade.js/deps/emsdk"
tar -xzf "$sourceRoot/emsdk.tar.gz" --strip-components=1 -C "$rebuildRoot/opencascade.js/deps/emsdk"

cd "$rebuildRoot/opencascade.js"
export OCJS_ROOT="$PWD"
export OCJS_SOURCE_COMMIT=ebd263f15337b440b391492af073662707e86482
export SOURCE_DATE_EPOCH=1785713531
export OCJS_STRICT_DEPS=1
uv venv --python 3.14.4 .venv
export PATH="$OCJS_ROOT/.venv/bin:$PATH"
./scripts/clone-deps.sh --dest deps
source deps/emsdk/emsdk_env.sh
export PATH="$OCJS_ROOT/.venv/bin:$PATH"
npm ci

export OCJS_OUTPUT_DIR="$rebuildRoot/outputs"
./build-wasm.sh --config single-threaded full \
  "$sourceRoot/replicad/packages/replicad-opencascadejs/build-config/custom_build_single.yml"
```

The existing exact Git dependencies cause `clone-deps.sh` to skip the three library clones/fetches. An executable emsdk launcher in the extracted emsdk directory causes its clone step to be skipped. It still installs/activates SDK5.0.1 and obtains standard build tools/headers from their upstream locations. No precompiled OpenCascade.js Docker image or OCCT object cache is used.

Set `OCJS_OUTPUT_DIR` explicitly. The actual shell script defaults to `OCJS_ROOT/dist`, despite prose in MAINTAINER.md suggesting the YAML's directory. Prepend `.venv/bin` to PATH because the native script selects the venv Python directly but otherwise calls `cmake` through PATH.

`full` initializes provenance; applies patches; prepares headers/PCH; generates and compiles bindings; compiles OCCT libraries with CMake; extracts wrapper registrations; links; validates bindings; and finalizes provenance. Preserve all emitted files, including `replicad_single.provenance.json`, `replicad_single.build-manifest.json` and symbols in addition to the loader/WASM/types. Check final outputs rather than assuming the four filenames accepted by the npm package are the complete build record.

## Intentional user modifications

The upstream patch-state ownership guard can reject uncommitted OCCT edits, and can restore its previously owned files if the expected patch state changes. Before rerunning the full pipeline after editing OCCT, commit intentional changes in that local OCCT checkout, including already applied upstream patches when appropriate. For the intentionally modified checkout, set `OCJS_STRICT_DEPS=0`; retain the original source manifest separately. The changed Git commit then becomes part of CMake's rebuild identity. Do not claim arbitrary uncommitted edits will be preserved by `full`.

OCJS always restores libembind from its supplied pristine snapshot before applying its patch. To intentionally modify that component, modify the maintained patch and its expected hash, rather than relying on an edit to the temporary installed libembind file.

## Patch and provenance findings

See `upstream-patch-ledger.md`, `upstream-patch-ledger.json` and `upstream-occt-patches.diff`. All four OCCT patches applied successfully to the exact pristine source commit in an isolated scratch clone:19 changed files. The libembind patch applied to its pristine supplied snapshot and matched both upstream before/after hashes. None of the four OCCT patch outputs inserts a change date. Some files contain OCJS markers; these do not establish that every modified file carries a prominent dated notice. A separate ledger alone is not literal file-level notice compliance.

Original Replicad packaging copies only `.js`, `.wasm` and `.d.ts` from its build-config directory and its npm files allowlist excludes provenance, build-manifest and symbols. Its Docker recipe identifies an OCJS canary tag, but the original npm payload does not provide those sidecars or a complete linker/build record. Supplying this matching source recipe, exact patches and archives materially improves source availability; it does not prove the original binary was reproduced from them. No full WASM rebuild or source-to-binary byte comparison was performed in this review.

Primary paths in exact upstream commits: OCJS `DEPS.json`, `MAINTAINER.md`, `Dockerfile`, `build-wasm.sh`, `scripts/clone-deps.sh`, `scripts/patch-state.py`, `scripts/cmake-state.py`, `src/ocjs_bindgen/provenance/clock.py`, `src/provenance.py`; Replicad `packages/replicad-opencascadejs/package.json` and `build-config/`.
