Runtime pack and licenses

Runtime pack and licenses

A bundle compile (--embed-python=bundle, or the default auto mode when the compiler’s interpreter has no usable shared libpython, as inside the release binary) links and runs a pinned CPython runtime pack instead of the host interpreter.
The pack is fetched once, sha256-verified, cached, and carries its licenses with it.

This page is the licensing and provenance contract that the pack and every output built from it follow.

What the pack contains

The pack for each supported platform (macOS arm64 and Linux x86_64 in the first slice) is a tarball produced from the pinned python-build-standalone release recorded in packaging/runtime.lock.toml:

  • libpython3.14 shared library and the version headers,
  • the standard library as a safe-trimmed zip (modules a program can import; test, idle, tkinter and similar trees are not bundled),
  • the licenses/ directory from the matching full archive: the CPython license text plus the license of every third-party component compiled into libpython (for example expat, libffi, mpdecimal, OpenSSL, SQLite, zlib, zstd; the authoritative list is the inventory in the pack, not this page).

The lock file pins the release tag, the asset name, the sha256 and the CPython micro version. A pack that does not match the lock is refused before anything is linked.

Fetching and caching

pyscriptc embed fetch              # this machine
pyscriptc embed fetch --platform linux-x86_64

The compressed pack is tens of megabytes (roughly 27 MB on macOS arm64, 35 MB on Linux x86_64). It is fetched on demand, not at install time: a bundle compile without a cached pack says so and names the fetch command before downloading. The unpacked pack is cached under ~/.cache/pyscriptc/runtime/<pack-id>/.

Offline and air-gapped machines point PYSC_RUNTIME_DIR at an already-unpacked pack directory; the compiler then never touches the network.

Licenses in the output

The CPython license (PSF License Agreement) is permissive and allows redistribution in binary form provided the license text and copyright notice travel with the binary. A bundle compile therefore writes OUT/share/licenses/ with:

  • LICENSE.cpython.txt, the license text of the bundled runtime itself, copied from the pack (lib/pythonX.Y/LICENSE.txt of the pinned asset),
  • one LICENSE.<component>.txt per third-party component compiled into libpython (expat, libffi, mpdecimal, OpenSSL, SQLite, zlib, zstd, ncurses and the rest of the inventory in packaging/third-party.toml); the texts are vendored in the compiler’s package data (pyscriptc/pbs_licenses/) and copied into the output,
  • NOTICE, the third-party notice rollup of the bundled runtime, generated by scripts/gen_third_party_notices.py from packaging/third-party.toml and the runtime lock, and
  • one directory per bundled third-party distribution (issue #660), holding the licenses/ tree of its *.dist-info metadata.

The third-party texts are vendored because the pinned python-build-standalone asset the pack is cut from (install_only_stripped) ships no licenses/ directory; the full archive of the same release does, and scripts/sync_pbs_licenses.py vendors its texts with their sha256 pinned in packaging/pbs_licenses.toml. A runtime bump refreshes the vendored texts, the inventory and NOTICE together (see the operational doc).

The compiler itself is Apache-2.0; the preflight checker (pyrefly, MIT) ships inside the install. Neither license changes when a program is compiled with a bundled runtime, but the runtime’s own licenses must be present in every redistributed output directory, which the bundle step does automatically.

Asking before shipping

The third-party inventory of the first pack build is confirmed with the project owner before the first public release that ships a pack (the inventory check, not legal advice).

The self-compiled pyscriptc

pyscriptc can compile itself into a binary that needs no Python, uv or pip on the host. The build is one command from a checkout:

pyscriptc compile src/pyscriptc --program --skip-check --island --emit bin \
  --embed-python bundle --embed-headers --bundle-exclude mypy -o dist/selfbin

The output is a relocatable directory, dist/selfbin/, laid out as:

  • bin/selfbin, the compiler itself (about 3 MB; every module of src/pyscriptc is compiled into it, and the program’s non-Python files live in bin/pyscriptc-data/: the C runtime sources ps_*.c, the vendored typeshed, the pyright stubs and the runtime lock),
  • lib/, the pinned runtime: libpython3.14.so, the stdlib zip and lib/python3.14/ (dynload modules plus the bundled pure-Python dependencies),
  • include/, the CPython headers, staged by --embed-headers, so the binary can compile island programs (which include Python.h) at run time,
  • share/licenses/, the license texts of the bundled runtime.

This is a two-file layout in spirit: the directory is the artifact, and it runs from any path. It is not a single file because the interpreter is a shared library and the stdlib is a zip; both must sit on disk next to the binary for the dynamic loader and the import system.

What the binary needs at run time, and what it does not:

  • No Python, uv or pip. The interpreter and the stdlib travel in lib/. env -i PATH=/nonexistent dist/selfbin/bin/selfbin prints its usage, and compiling a program that does not touch the island needs nothing else.
  • A C toolchain on PATH (clang or zig) for --emit bin and run, exactly like the installed compiler. A compile with no toolchain fails with PS0001, which names the install options.
  • pyrefly for check and the preflight of a compile without --skip-check. The binary resolves it like the installed compiler: the PYREFLY_BIN override, then a pyrefly on PATH. When none is found the compile says so instead of half-running.
  • No network and no pack cache for --embed-python bundle compiles: the binary links user programs against the runtime it carries.

--bundle-exclude mypy declares the optional mypy bridge an extern dependency: the import stays in the compiled compiler, and the host answers it exactly like CPython answers a dead import. Without the exclusion the bundle refuses the compile, because mypy ships a compiled extension and a bundle carries pure Python only (PS4302).