Skip to content

Security & release integrity ​

What runs where ​

  • The engine is one native binary. It never runs a shell: every command it spawns (uv, pip, pixi, nvidia-smi) is an argument vector, so paths with spaces or quotes cannot be reinterpreted. install-pixi downloads pixi's official installer script to a file first and runs that file, rather than piping the network into a shell; pixi's own PIXI_HOME, PIXI_VERSION and PIXI_NO_PATH_UPDATE variables are honoured.
  • It reads your project files and PyPI metadata, and runs package managers only when you ask it to change something (setup, install, fix, a quick fix, a notification button).
  • Network access: PyPI (package metadata), GitHub (engine releases, data tables from raw.githubusercontent.com), prefix.dev (the PyPI-to-conda name mapping used by the pixi migration), and pixi's installer (pixi.sh) only when you run install-pixi. No telemetry.
  • 1.3 It keeps a local log of what it ran, in the cache directory, with credentials masked; nothing in it leaves the machine. See Logs.

Where releases come from ​

Engine releases are built by the Release workflow on GitHub-hosted runners from the tagged commit, with --locked so exactly Cargo.lock is used. A single publish job uploads everything at once, so a failed platform never leaves a partial release.

AssetWhat
pypilot-linux-x64.tar.gz, pypilot-darwin-x64.tar.gz, pypilot-darwin-arm64.tar.gz, pypilot-windows-x64.zipThe engine
SHA256SUMS 1.3One <sha256> <asset> line (two spaces) per archive, sorted by name
pypilot-helper.cdx.json 1.3CycloneDX 1.5 SBOM of the engine
1.3 Every archive, SHA256SUMS and the SBOM carry a GitHub build-provenance attestation, which ties the file to the workflow run and commit that built it. The first release with SHA256SUMS, attestations and the SBOM is engine v1.3.0; 1.2.2 and older have none of them.

Verifying a download 1.3 ​

From engine v1.3.0 on, you can check both that an archive is intact (SHA256SUMS) and that it was built by this repository's Release workflow (the attestation). The examples use v1.3.0; replace it with the release you downloaded.

1. Download the archive and SHA256SUMS from the release page, or with the GitHub CLI:

bash
gh release download v1.3.0 -R pypilot-dev/pypilot -p 'pypilot-linux-x64.tar.gz' -p SHA256SUMS

2. Check the hash. SHA256SUMS has one <64 lowercase hex> <asset> line (two spaces) per archive, sorted by name. --ignore-missing skips the archives you did not download:

bash
sha256sum -c SHA256SUMS --ignore-missing
# pypilot-linux-x64.tar.gz: OK
bash
shasum -a 256 -c SHA256SUMS --ignore-missing
powershell
$want = (Select-String -Path SHA256SUMS -Pattern 'pypilot-windows-x64.zip').Line.Split(' ')[0]
$got  = (Get-FileHash pypilot-windows-x64.zip -Algorithm SHA256).Hash.ToLower()
if ($got -eq $want) { 'OK' } else { 'MISMATCH' }

3. Check the provenance with the GitHub CLI. This verifies the Sigstore-signed attestation the Release workflow made, and prints the workflow run and commit it ties the file to:

bash
gh attestation verify pypilot-linux-x64.tar.gz -R pypilot-dev/pypilot
gh attestation verify SHA256SUMS -R pypilot-dev/pypilot
gh attestation verify pypilot-helper.cdx.json -R pypilot-dev/pypilot

Each command exits 0 when the attestation checks out. For v1.3.0 it names the signer workflow .github/workflows/release.yml@refs/tags/v1.3.0 and the tagged commit. To also require that the file came from the Release workflow at a given tag, add:

bash
gh attestation verify pypilot-linux-x64.tar.gz -R pypilot-dev/pypilot \
  --signer-workflow pypilot-dev/pypilot/.github/workflows/release.yml \
  --source-ref refs/tags/v1.3.0

A file that was changed after the build, or built anywhere else, fails this check. Engine 1.2.2 and older have neither SHA256SUMS nor attestations, so there is nothing to verify them against.

Reproducing a build. Releases build with --locked from a clean target directory, and the run's log records rustc -Vv. Check out the tag and build with that compiler version to reproduce a binary.

What each wrapper downloads and checks ​

WrapperDownloadsVerifies
Zed extensionThe newest release; its manifest only allows downloads from github.com/pypilot-dev/pypilotHTTPS
VS Code 1.3.0 1.3The engine version pinned in its package.json ("pypilotEngine": { "version", "sha256": { … } }); with pypilot.engineUpdates: "latest", newer releases tooEvery archive is hashed before extraction: against the pinned SHA-256, or the release's SHA256SUMS for other versions. A mismatch, or a release without SHA256SUMS, installs nothing. There is no switch for unverified engines; use pypilot.helperPath for your own build
pypilot-cli 1.3.0 1.3Usually nothing: the platform wheels for Windows x64, Linux x86-64 (glibc 2.34+) and macOS arm64 and x64 bundle the pinned engine (v1.3.0). Elsewhere, the pinned release (DEFAULT_TAG = "v1.3.0"); with PYPILOT_ENGINE_VERSION, that releaseEvery download is hashed before it is extracted or run: against ENGINE_SHA256 for the pinned release, against the release's SHA256SUMS otherwise. A mismatch deletes the file and fails; a release without SHA256SUMS is refused unless PYPILOT_ALLOW_UNVERIFIED=1. The wheels are built from archives checked against ENGINE_SHA256
VS Code 1.2.1 and olderThe latest releaseHTTPS
pypilot-cli 1.2.2 and olderThe release it pins (DEFAULT_TAG, "v1.2.2" in 1.2.2)HTTPS; .tar.gz archives are extracted with Python's data filter where available, so nothing lands outside the target folder

After each engine release, the ecosystem-bump workflow opens a pull request in each wrapper repository that bumps its pin and hashes (scripts/bump_engine_pins.py, reading the release's SHA256SUMS). It needs an ECOSYSTEM_TOKEN secret with write access to those repositories; without it, it only logs the command to run by hand. Merging a pin PR does not release the wrapper; that stays a manual step.

Supply chain ​

  • cargo deny runs on every push, pull request and daily: RustSec advisories, licenses, banned crates (OpenSSL) and crates.io as the only source.
  • A nightly job runs against the real PyPI, PyTorch, NVIDIA and conda-forge and opens a data-drift issue when upstream changed under the data tables.
  • Release and CI builds use --locked, so they build exactly Cargo.lock.
  • 1.3 The VS Code extension's CI and release run bun audit on the dependencies it bundles, and each GitHub release carries a CycloneDX SBOM (pypilot-vscode-X.Y.Z.cdx.json) of them. pypilot-cli's CI and publish job run pip-audit, and its releases carry pypilot-cli.cdx.json, which lists the bundled engine archives with their hashes.

Data tables ​

The data tables are JSON, not code. Installed engines download them from the repository's main branch every data_refresh_days (7 by default), validate that they parse, and otherwise keep the copy compiled into the binary. Changes to them go through pull requests like any code. Set data_refresh_days = 0 to never fetch them.

Offline use ​

  1. Put an engine on the machine: unpack a release archive (verify it as above) or cargo install --path helper --locked, and place pypilot on PATH. Both editor extensions and pypilot-cli use it instead of downloading.
  2. Set data_refresh_days = 0 in the global config file to stop table refreshes; see Configuration.
  3. Warm the PyPI metadata cache while online (any pypilot doctor does), or accept that unknown packages cannot be judged offline. Released versions are cached forever; "newest release" lookups for 24 hours.
  4. With pypilot-cli, 1.3 install a platform wheel (the engine is inside) and set PYPILOT_OFFLINE=1 so the wrapper never tries a download. See pypilot-cli offline.

What you see offline: pypilot doctor lists packages it has no cached metadata for as Could not resolve with the lookup error, and they do not narrow the supported range. 1.3 In the editor those imports get no diagnostic, instead of a false "PyPI has no project named …". pypilot update-data --check exits 2.

Reporting a vulnerability ​

Please do not open a public issue. Contact the maintainer privately through the GitHub profile linked from the contributing guide.

Engine: AGPL-3.0-or-later. Zed and VS Code extensions: Apache-2.0. pypilot-cli: AGPL-3.0-or-later.