Skip to content

Using in your project

This guide is for project owners who want to ship their pixi environments to airlocked machines.

Run the first command from the root of your Pixi project on the connected side:

Connected hosts need Pixi installed. Install the native package from the canonical channel, then initialise:

Terminal window
pixi global install --channel https://prefix.dev/archont561/archont561 --channel conda-forge pixi-sandbox
pixi-sandbox init

pixi-sandbox init creates the preferred pixi-sandbox.toml, the GitHub workflow, and exactly one launcher for the connected host:

pixi-sandbox.toml
.github/workflows/publish-sandbox.yml
restore.sh # Linux/macOS
# restore.ps1 # Windows instead

Review and commit those files. Existing projects that carry .pixi-sandbox.toml keep using it; an explicit --config PATH wins over both conventional names. Pushing the project runs the generated workflow and publishes the configured sandbox/<bundle>-<platform> branches. Transfer Git history containing the project and sandbox branch into the disconnected network. On the airlock, the second product command is:

Terminal window
./restore.sh

That script only uses local Git objects: it archives the platform’s sandbox branch, locates the verified nested binary, and restores everything offline. It never contacts GitHub. Windows users run .\\restore.ps1 instead.

The Git push, workflow completion, and transfer across the air gap are deployment operations, not additional pixi-sandbox commands. The airlock clone must contain the sandbox branch.

Configure Pixi’s channel or mirror settings before running the canonical install command when your organisation proxies prefix.dev.

  • Your project has pixi.toml + pixi.lock
  • Optional: Cargo.toml + Cargo.lock if you want vendored crates
  • On the connected host: Pixi and Git
  • On the airlock: Git and tar; Pixi and pixi-sandbox do not need to be installed

The connected host installs pixi-sandbox from the signed native package channel. Standalone release binaries and SHA256SUMS remain available for verified transport bootstrap.

Not every env in pixi.toml should be an airlock branch. Declare explicitly in pixi-sandbox.toml:

pixi-sandbox.toml
schema = 1
branch_prefix = "sandbox"
cargo_vendor = true # false if you only need conda envs
[[bundle]]
name = "developer"
environments = ["dev", "docs"]
platforms = ["linux-64", "osx-arm64", "win-64"]
[[bundle]]
name = "minimal"
environments = ["default"]
platforms = ["linux-64"]
cargo_vendor = false

Validate:

Terminal window
pixi-sandbox plan --config pixi-sandbox.toml
pixi-sandbox plan --config pixi-sandbox.toml --json

This emits a GitHub Actions matrix — one job per bundle × platform.

Terminal window
# Build static self-binary
cargo build -p pixi-sandbox --release
# Pack
pixi-sandbox pack \
--repo-root . \
--envs dev,docs \
--output-dir .sandbox-out \
--platform linux-64 \
--cargo-vendor \
--fetch-tools \
--self-bin target/release/pixi-sandbox
# Verify (writes nothing)
pixi-sandbox doctor --branch-location .sandbox-out --verify
# Publish
pixi-sandbox publish \
--input-dir .sandbox-out \
--branch-name sandbox/developer-linux-64 \
--remote origin

Never pack $(command -v pixi-sandbox). Installed with pixi global install, that path is a launcher trampoline, not the standalone binary: it looks for trampoline_configuration/pixi-sandbox.json beside the executable, which only exists in the global prefix — so the packed branch passes every hash check and then dies at restore with Couldn't open "…/trampoline_configuration/pixi-sandbox.json" (issue #81). Pack probes --self-bin by executing it under an empty environment and refuses such files; doctor --verify applies the same check to a transport before anyone restores it. The only correct sources are the release asset (pixi-sandbox-<target>, verified against the release’s SHA256SUMS) or a binary you built yourself.

sandbox/developer-linux-64 (orphan branch)
├── .pixi-sandbox/
│ ├── manifest.json # SHA-256 catalog, source commit, tool versions
│ ├── envs/dev/pack/ # pixi-pack --directory-only output
│ │ ├── channel/linux-64/*.conda
│ │ ├── environment.yml
│ │ └── pixi-pack.json
│ ├── envs/docs/pack/ # second env, shared .conda files deduped by git
│ ├── tools/linux-64/ # pixi, pixi-unpack, pixi-sandbox (static, verified)
│ └── vendor/ # cargo vendor --versioned-dirs (if enabled)
├── README.md # human restore instructions
└── AGENTS.md # agent instructions

Sizes (measured small project, 2 envs, 33 crates, linux-64):

Part Size
conda envs (.conda) 133 MB
cargo vendor (loose) 33 MB
tools (pixi, pixi-unpack, self) 96 MB
transport dir 262 MB
orphan branch after git dedup ~110 MB

Tools dominate small bundles — expected, git stores each tool blob once.

After the project and its sandbox branch have crossed the air gap:

Terminal window
./restore.sh

The generated script reads pixi-sandbox.toml and selects <branch_prefix>/<bundle>-<platform> for the native platform — the same name pixi-sandbox plan gives the publisher, so renaming a bundle or prefix needs no regenerated launcher. It then uses git archive into .pixi/.restore-transport and delegates verification and restoration to the branch’s .pixi-sandbox/tools/<platform>/pixi-sandbox. When the config cannot decide — no bundle covers this platform, or several do — it falls back to the branch baked in at init time. Override branch selection only when needed:

Terminal window
PIXI_SANDBOX_BRANCH=sandbox/developer-linux-64 ./restore.sh # exact branch
PIXI_SANDBOX_BUNDLE=developer ./restore.sh # pick among several bundles

See Airlock Restore for transfer options and the operator checklist.

If you only need conda envs:

pixi-sandbox.toml
schema = 1
cargo_vendor = false
[[bundle]]
name = "python"
environments = ["dev", "test"]
platforms = ["linux-64", "win-64"]

And pack without --cargo-vendor:

Terminal window
pixi-sandbox pack --repo-root . --envs dev --output-dir .sandbox-out --platform linux-64 --fetch-tools --self-bin target/release/pixi-sandbox
Keep envs explicit — never infer every env from pixi.toml. Test/bench envs shouldn’t be published.
Work dir on same filesystem — default .pixi/.restore-work. Never use small tmpfs /tmp (pixi-unpack stages into TMPDIR).
Verify before write — doctor –verify is the same code as restore’s preflight. CI must run it.

pixi-sandbox init writes only the files it owns — the publisher workflow, the relock workflow, the offline launcher, and pixi-sandbox.toml when it does not exist yet. Your pixi.toml is left byte-identical: the CLI is installed globally with an explicit channel (pixi global install --channel https://prefix.dev/archont561/archont561 …), which never reads the project’s channel list, and no generated file needs the project to know where the CLI came from.

Upgrading from 0.4.3? That release appended https://prefix.dev/archont561 — the publisher’s namespace root, which is a web page, not a channel — to [workspace].channels, so pixi lock failed with a 404 on any project with at least one dependency. Remove that entry from your channels array; nothing needs to replace it.