Packaging & Distribution
14.1 Build from source (all platforms)
Use this checklist when you want to compile and share binaries with an alpha tester.
Prerequisites: Set up your development environment first (see §2 Development Environment Setup).
Install Rust (
rustup) and ensure the active toolchain uses edition 2021 (or later for edition 2024 support).Set up dependencies (choose one method from §2):
Conda (recommended):
./scripts/setup-conda-env.sh, thenconda activate orbitron-dev(see §2.3)System packages: Follow platform-specific instructions in §2.2
Docker:
docker-compose -f docker-compose.dev.yml build # Build image if needed docker-compose -f docker-compose.dev.yml run --rm dev cargo build --releaseThe container has neither
nodenortrunk, so the viewer assets in step 3 must be staged on the host (see §2.1).
Stage the WASM viewer assets.
ui/shellembeds three generated, gitignored files withinclude_bytes!, so any release build of the GUI fails without this:(cd viewer/wasm/ts && npm ci) extensions/python-bridge/scripts/stage_viewer_assets.shBuild binaries:
# Build all binaries at once cargo build --release --workspace # Or build individually: cargo build --release -p orbitron-ui-shell cargo build --release -p orbitron-cli --bin orbitron cargo build --release -p orbitron-tui --bin orbitron-tuiArtifacts land in
target/release/:- GUI:
target/release/orbitron-viewer - CLI:
target/release/orbitron - TUI:
target/release/orbitron-tui
- GUI:
Note: Linux and macOS builds use the HDF5 sources bundled by hdf5-metno; CMake is required, but no external HDF5 installation or HDF5_DIR is needed. Windows builds use the HDF5 package from the conda environment and set HDF5_DIR to its Library directory.
14.2 macOS desktop bundle
Run this after the build-from-source checklist to produce a signed .app (and optional .dmg).
Install the bundler tooling once:
cargo install cargo-bundleEnsure the GUI crate exposes a binary target (
ui/shell/src/bin/orbitron-viewer.rs) that launchesstart_ui.Confirm bundle metadata in
ui/shell/Cargo.tomlunder[package.metadata.bundle].Get your signing identity:
security find-identity -v -p codesigningBuild the
.app(and optional.dmg):cargo bundle --release -p orbitron-ui-shell --bin orbitron-viewer cargo bundle --release -p orbitron-ui-shell --bin orbitron-viewer --format dmgOutput lands in
target/release/bundle/osx/.Sign, notarize, and staple (recommended sequence). Use the bundle name reported by
cargo bundle(Orbitron Viewer.app, set via[package.metadata.bundle.bin.orbitron-viewer]):# ensure Resources exists (can be empty) mkdir -p "target/release/bundle/osx/Orbitron Viewer.app/Contents/Resources" # sign the app bundle codesign --force --deep --options runtime --timestamp \ --sign "Developer ID Application: YOUR NAME (TEAMID)" \ "target/release/bundle/osx/Orbitron Viewer.app" # verify signature codesign --verify --deep --strict --verbose=2 \ "target/release/bundle/osx/Orbitron Viewer.app" # create a zip for notarization ditto -c -k --keepParent \ "target/release/bundle/osx/Orbitron Viewer.app" \ orbitron-ui-shell.zip # submit for notarization (use a keychain profile) xcrun notarytool submit orbitron-ui-shell.zip \ --keychain-profile "notary-orbitron" \ --wait # staple the notarization ticket xcrun stapler staple "target/release/bundle/osx/Orbitron Viewer.app" # verify Gatekeeper acceptance spctl -a -vv "target/release/bundle/osx/Orbitron Viewer.app"If you see
source=Unnotarized Developer ID, the app is signed correctly but has not been notarized/stapled yet. Ensure thenotarytool submit ... --waitandstapler staplesteps succeed, then re-runspctl. You can also check ticket state with:xcrun stapler validate "target/release/bundle/osx/Orbitron Viewer.app"For a single-command local workflow, use:
scripts/macos-notarize-app.sh \ --identity "Developer ID Application: YOUR NAME (TEAMID)" \ --profile "notary-orbitron" \ --app "target/release/bundle/osx/Orbitron Viewer.app"No runtime dylibs need bundling on macOS. All three binaries link only system frameworks — HDF5 is built from source and linked statically, and the
ssh2crate left with the SFTP removal, taking libssh2, libcurl and the Kerberos chain with it. Check before assuming otherwise:otool -L target/release/orbitron-viewer | grep -v '/System/\|/usr/lib/'That prints only the binary’s own path today.
scripts/build-macos-release.shtherefore ships nolib/directory beside the binaries.If a future dependency does introduce one, the fix is to copy the exact versioned dylib from
$CONDA_PREFIX/libintoContents/Frameworks, add an rpath, and re-sign:APP="target/release/bundle/osx/Orbitron Viewer.app" mkdir -p "$APP/Contents/Frameworks" cp "$CONDA_PREFIX/lib/<name>.dylib" "$APP/Contents/Frameworks/" install_name_tool -add_rpath "@executable_path/../Frameworks" \ "$APP/Contents/MacOS/orbitron-viewer" codesign --force --deep --options runtime --timestamp \ --sign "Developer ID Application: YOUR NAME (TEAMID)" \ "$APP"Create the keychain profile once:
xcrun notarytool store-credentials "notary-orbitron" \ --apple-id "you@example.com" \ --team-id "TEAMID" \ --password "APP_SPECIFIC_PASSWORD"
14.3 Windows installer
Install WiX tooling and the Cargo helper:
cargo install cargo-wixGenerate an initial installer manifest (runs once to scaffold
wix/main.wxs):cargo wix init --bin orbitron-viewer --output wixBuild the
.msi:cargo wix --nocapture --release --bin orbitron-viewerThe resulting installer lands in
target/wix/. Sign withsigntool.exebefore shipping.
14.4 Linux packages
Deb:
cargo install cargo-deb cargo deb --package orbitron-ui-shell --no-buildRPM (optional):
cargo install cargo-rpm cargo rpm build --releaseUpdate
Cargo.tomlmetadata (maintainer,license, post-install scripts) as required by the distribution.
14.5 Precompiled archive binaries
Build the desired targets with
cargo build --release --target <triple>.Package the artifacts alongside README, LICENSE, and sample config:
tar czf orbitron-viewer-${VERSION}-${TARGET}.tar.gz \ -C target/${TARGET}/release orbitron-viewer orbitron README.md LICENSEUse
.ziparchives on Windows for ease of extraction.Publish checksum files (e.g.,
shasum -a 256 <file>).
Automate the above via CI (GitHub Actions/Gitea runners) using a matrix over macos-latest, windows-latest, and ubuntu-latest. Reuse the same pipelines to upload artifacts to the chosen release host.
14.6 Python Wheels
Python wheels provide the Orbitron API for use in Jupyter notebooks and Python scripts.
Build Script
./scripts/build-python-wheels.sh [OPTIONS]Options: - --skip-widget - Skip Jupyter widget frontend build
That is the only flag the script accepts; anything else prints the usage line and exits 1. The build is always maturin build --release — there is no debug mode.
Output
The local script builds one wheel for the current platform. A tagged release aggregates the three platform jobs into this layout:
dist/python-wheels/
├── orbitron-X.Y.Z-cp310-abi3-manylinux_2_28_x86_64.whl # Linux
├── orbitron-X.Y.Z-cp310-abi3-macosx_11_0_arm64.whl # macOS Apple Silicon
├── orbitron-X.Y.Z-cp310-abi3-win_amd64.whl # Windows
└── README.txt # Installation instructions
# abi3: one wheel per platform runs on CPython 3.10+. Intel macOS isn't built.
Manual Build
# From the repository root. The PEP 517 backend stages both viewer frontends.
conda run --no-capture-output -n orbitron-dev \
python -m pip wheel extensions/python-bridge --no-build-isolation \
--wheel-dir ~/scratch/orbitron-wheelsPlatform-Specific Builds
macOS: The release job builds the supported Apple Silicon wheel on its macOS arm64 runner. Orbitron does not currently publish an Intel or universal2 wheel.
Linux (manylinux):
# The tagged-release workflow runs this through PyO3/maturin-action in a
# manylinux_2_28 container. Reproduce it in that container rather than labeling
# a wheel built against the host glibc as manylinux.
maturin build --release --features python --manylinux 2_28Windows:
# Build on Windows machine or CI
maturin build --release --features pythonTesting Python Wheels
./scripts/test-python-wheel.sh [path/to/wheel.whl]
# Or test latest wheel
./scripts/test-python-wheel.shTests performed: 1. Create clean virtual environment 2. Install wheel 3. Run 6 smoke tests: - Import orbitron module - Create Orbitron instance - Load molecule - Measure distance - Serialize to bytes - Access properties
Troubleshooting Python Wheels
Error: “maturin not found”
./scripts/setup-conda-env.sh
conda run -n orbitron-dev maturin --versionError: “Could not find HDF5 headers”
# Ensure conda environment is active
conda activate orbitron-dev
# Verify HDF5
conda list | grep hdf5Error: “Rust compiler version mismatch”
# Project uses Rust 1.92.0 (pinned in rust-toolchain.toml)
rustup show # Should show 1.92.0
# If wrong version
rustup update
cd /path/to/orbitron # Let rust-toolchain.toml activateWheel platform mismatch:
# Check the wheel tag, then build on the matching platform.
ls ~/scratch/orbitron-wheels/14.7 WASM Web Viewer
The WASM web viewer provides an interactive 3D molecular viewer for embedding in websites.
Prerequisites
./scripts/setup-conda-env.sh
(cd viewer/wasm/ts && npm ci)
conda run -n orbitron-dev trunk --versionThe conda environment pins Trunk 0.21.14 and installs the wasm32-unknown-unknown target through the repository setup.
Build Script
conda run --no-capture-output -n orbitron-dev \
extensions/python-bridge/scripts/stage_viewer_assets.shThis builds the Rust WASM module and TypeScript custom element, then stages the runtime used by Python, Jupyter, and desktop Web View export. Set ORBITRON_SKIP_VIEWER_BUILD=1 only when the current build products already exist and the purpose is to restage them.
Output
The build products are viewer/wasm/orbitron-viewer.js and viewer/wasm/dist/orbitron-viewer-wasm{,_bg.wasm}. The staged package copy is under extensions/python-bridge/python/orbitron/viewer_assets/.
The documentation site has a separate tracked deployment copy under website/viewer/. After rebuilding the runtime, update all three files and render the site:
cp viewer/wasm/orbitron-viewer.js website/viewer/orbitron-viewer.js
cp viewer/wasm/dist/orbitron-viewer-wasm.js \
website/viewer/orbitron-viewer-wasm.js
cp viewer/wasm/dist/orbitron-viewer-wasm_bg.wasm \
website/viewer/orbitron-viewer-wasm_bg.wasm
conda run --no-capture-output -n orbitron-dev quarto render websiteManual Build
conda run --no-capture-output -n orbitron-dev \
extensions/python-bridge/scripts/stage_viewer_assets.sh
ls -lh viewer/wasm/orbitron-viewer.js viewer/wasm/dist/Customization
Edit viewer/wasm/Trunk.toml to customize: - Output directory - Asset paths - Build hooks - Optimization level
Testing WASM Viewer
ORBITRON_SKIP_VIEWER_BUILD=1 \
conda run --no-capture-output -n orbitron-dev ./scripts/dev-smoke.sh
env -u DYLD_LIBRARY_PATH \
conda run --no-capture-output -n orbitron-dev \
python scripts/dev-smoke-browser.pyThe first gate checks staging, serving, and self-contained HTML invariants. The second runs 11 Chromium scenarios with pixel-level paint assertions, scene ordering checks, multiviewer isolation, packed trajectory playback, and MRC density rendering. On macOS, unsetting an inherited DYLD_LIBRARY_PATH avoids injecting conda libraries into Chromium.
Troubleshooting WASM Viewer
Error: “trunk not found”
./scripts/setup-conda-env.sh
conda run -n orbitron-dev trunk --versionError: “wasm32-unknown-unknown not installed”
rustup target add wasm32-unknown-unknownBlank viewer in browser: - Check browser console (F12) for errors - Check CORS: Serve via HTTP server, not file:// protocol - A null navigator.gpu is not the cause on its own. The renderer feature-detects and pins a single backend per canvas — BROWSER_WEBGPU when a usable WebGPU adapter is found, WebGL2 (Backends::GL) otherwise (core/render/src/renderer/init/mod.rs) — so a browser without WebGPU still renders.
Large .wasm file size: - Ensure using --release flag (optimized build) - Check Cargo.toml for optimization settings - Measure the artifact rather than enforcing an old fixed size. On 2026-08-27, the release WASM was 6,134,411 bytes and the complete self-contained benzene smoke page was 8.08 MB.
14.8 Standalone HTML Viewer
Use the production orbitron.export_html path. It embeds the current custom element, WASM runtime, and one scene or bundle into a single file with no CDN or local-server dependency:
conda run --no-capture-output -n orbitron-dev \
python -m pip install -e extensions/python-bridge
conda run -n orbitron-dev python -c '
from pathlib import Path
import orbitron
source = "io/pipelines/tests/fixtures/xyz/benzene.xyz"
destination = Path.home() / "scratch" / "orbitron-benzene.html"
scene = orbitron.Orbitron().load(source)
orbitron.export_html(scene, destination)
print(destination)
'Pass a .orbpack path instead of a Scene when the output must retain trajectory frames, normal modes, volumetric grids, or recorded presentation state. The output size follows the runtime plus the encoded scene; do not pin a 5 MB expectation. The 2026-08-27 smoke artifact was 8.08 MB.
The older scripts/build-wasm-viewer.sh and scripts/build-wasm-standalone.sh wrappers still contain pre-.orbpack instructions and a v0.4.0 fallback. They remain wired into the tagged-release workflow and must be replaced before the next release rather than used as the developer reference. This is tracked in notes/open-items.md.
14.9 Complete Release Build
Tagged releases are assembled by .github/workflows/release.yml. Its native matrix builds CLI, TUI, desktop, and platform wheels; the distribution job builds the manylinux wheel and web artifacts; the final job checks expected files and creates the GitHub release.
For a local candidate, build only the platform you are on with the scripts in this chapter, then run the release checklist. Do not emulate the multi-platform job by relabeling local binaries. The web-distribution step currently carries the legacy-wrapper issue noted in §14.8.
14.10 Distribution Sizes
Recent sizes are evidence, not release gates. Compression, target platform, embedded viewer assets, and the chosen scene all change them.
| Distribution | Size | Notes |
|---|---|---|
| Python wheel | Inspect the built wheel | Varies by platform and bundled native libraries. |
| Raw WASM runtime | 6,134,411 bytes on 2026-08-27 | The optimized .wasm, before ZIP or base64 overhead. |
| Self-contained HTML | 8.08 MB on 2026-08-27 | Benzene plus the inlined runtime; larger bundles increase it. |
| Native archives | Inspect each platform artifact | GUI bundles, symbols, signing, and native libraries differ by OS. |
14.11 Build Times
Build time depends on CPU count, Cargo and npm cache state, target architecture, and whether HDF5 and the WASM dependency graph are cold. Record elapsed time and peak memory with the release artifact when it matters; do not use an old M1 laptop estimate as a timeout.
14.12 Release Checklist
Before releasing distributions, verify:
See RELEASE_CHECKLIST.md for complete pre-release verification.