Skip to content

Restore on the airlock

This page is written for the person (or agent) standing in front of the disconnected machine. Nothing here needs network access — if any step tries to reach out, stop and report it.

Fetch orphan branch
Terminal window
# on the connected host, include both refs in the transferred Git bundle
git bundle create project-airlock.bundle main sandbox/dev-linux-64

The sandbox branch carries its static tooling under .pixi-sandbox/tools/<platform>/; its root contains Markdown only. The normal project branch carries the minimal launchers generated by pixi-sandbox init.

One-command offline reconstruction
Terminal window
git clone project-airlock.bundle project
cd project
./restore.sh # Linux/macOS
# .\\restore.ps1 # Windows

The launcher resolves its branch from the config selected by init (pixi-sandbox.toml is preferred and .pixi-sandbox.toml is the compatibility fallback), derives <branch_prefix>/<bundle>-<platform> for the host, calls git archive, extracts into project-local scratch, and invokes the verified nested binary. When a connected clone lacks the sandbox ref, it fetches that one branch into origin/<branch>; set PIXI_SANDBOX_FETCH=skip to require local Git objects only. Set PIXI_SANDBOX_BRANCH to pin an exact branch, or PIXI_SANDBOX_BUNDLE to choose between bundles that cover the same platform.

Verify every byte
Terminal window
/tmp/sb/.pixi-sandbox/tools/linux-64/pixi-sandbox doctor \
--branch-location /tmp/sb --verify

This checks every sha256 in manifest.json and reports the payload breakdown. It writes nothing. Treat any failure as a hard stop.

Keep evidence when the primary log is unreachable

Section titled “Keep evidence when the primary log is unreachable”

If an Actions or operator log cannot cross the airlock boundary, ask the connected side to rerun pack, doctor, or publish with a fresh --log-file <PATH>. The file is flushed after every phase and includes the complete causal error chain on failure; -v mirrors phase names to stderr and -vv adds reviewed non-secret context. Transfer that bounded text file beside the incident report, not inside the transport branch. Never reuse its path: pixi-sandbox refuses to overwrite an existing diagnostic log so the first failure is not silently destroyed. Remote credentials and common token/password query values are not recorded.

For a local airlock check, the same binary can write evidence without changing doctor’s normal report:

Terminal window
/tmp/sb/.pixi-sandbox/tools/linux-64/pixi-sandbox doctor \
--branch-location /tmp/sb --verify \
--log-file /path/to/new/doctor-attempt.log
Materialize environments
Terminal window
cd /path/to/project
/tmp/sb/.pixi-sandbox/tools/linux-64/pixi-sandbox restore \
--branch-location /tmp/sb \
--output-path . --force

What it does, in order: verifies the tools, stages a copy of each pack under a work dir on the project’s filesystem, unpacks with the bundled pixi-unpack, moves each environment to .pixi/envs/<env>, writes pixi’s conda-meta markers, materialises the vendored crates, writes Cargo source replacement to sandbox-owned .pixi-sandbox/cargo-home/config.toml, adds per-environment Pixi/Conda activation hooks that set CARGO_HOME, removes any legacy .pixi/sandbox-env.sh, verifies the restored tree against the manifest’s per-file digests, and — only after that verification — registers the user tools (below).

There is one supported command entrypoint after restore: pixi. A verified restore puts small managed launchers for pixi and pixi-sandbox into ~/.local/bin (Windows: %USERPROFILE%\.pixi-sandbox\bin) and adds that directory to your shell profile’s PATH (Windows: the user PATH in the registry — no administrator rights). In a new shell, pixi and pixi sandbox then work directly; pixi discovers pixi sandbox by finding pixi-sandbox on PATH, which is why both are registered. The launchers exec the manifest-verified copies under <project>/.pixi/tools/<platform>/ and carry a managed by pixi-sandbox marker; restoring another project retargets them, so the most recently registered restore is the user-level tool source. An already-running shell cannot be mutated — open a new one, or run the export PATH=... line the restore prints.

No project-level activation script is generated or supported. Do not source .pixi/sandbox-env.sh and do not run bare cargo, rustc, or bun from a restored prefix; use pixi run ... so pixi selects the environment, loads the restore-owned Cargo activation hook, and keeps the lockfile/offline contract visible.

Registration refuses to overwrite a pixi or pixi-sandbox you already have in the user bin directory unless you pass --force. For CI runners, shared accounts, or locked-down airlocks, set PIXI_SANDBOX_USER_TOOLS=skip before running the generated launcher (or append --user-tools skip when you call pixi-sandbox restore yourself and know the binary understands it): restore then touches nothing outside the project, and verification and project output are identical either way. The generated launchers pass the policy as an environment variable rather than a flag on purpose — the binary a launcher executes comes from the packed branch, which may predate the flag, and an unknown variable is ignored where an unknown flag is a hard error. --user-bin <dir> relocates the launchers for sites with their own bin-directory policy.

Two things follow from that staging. Before each environment is moved into place, every text file in it has the staging path rewritten to the final one, so nothing in .pixi/envs/<env> points into scratch — a .pc file, a CMake config or an activation script keeps working at the airlock path. And on success the work dir is removed: a completed restore leaves no scratch behind. A failed restore keeps it, because that is where the partial materialisation is worth looking at.

Offline assertions
Terminal window
pixi install --frozen --offline # must be a no-op, must not touch the network
pixi run --frozen -- cargo build --offline # uses the vendored crates only

In a new shell, plain pixi and pixi sandbox resolve through the registered user tools. If registration was skipped, call the manifest-owned binary explicitly: ./.pixi/tools/<platform>/pixi run --frozen <task>.

If pixi install --frozen --offline starts doing real work, the environment on disk does not match pixi.lock — do not let it continue; re-run the restore instead. If your project already tracks .cargo/config.toml for aliases, rustflags, target runners, or [env], the default restore leaves it byte-for-byte intact; Cargo’s vendor replacement lives in .pixi-sandbox/cargo-home/config.toml and is selected by Pixi’s environment activation.

Rust toolchain — ships crates, not rustc. Pack rust into env or have it on machine (conda-forge rust env ~1.8 GB unpacked).
Build artifacts — airlock compiles from scratch.
Credentials — nothing authenticates to anything.

Unpacking runs package post-link scripts, i.e. code from the bundle executes on your machine. That is inherent to conda environments — the bundle is signed/verified content and the branch should be protected accordingly. Ask for --verify-only runs and signed manifests if your site requires it.