Offline install
This page is part of the operator guide. A checkout install that uses the network is in Getting started. This is the procedure for installing Etalon from a local wheelhouse while the install process has no network. It is evidence about that install. It does not determine that a regulation or an internal control has been met. Package version is 0.0.1.What --no-index does not prove
--no-index and --find-links tell pip to read a local wheelhouse instead
of a package index. They do not disable the machine’s network. A successful
install with those flags alone is not a claim that the host, or the install
process, was offline.
How the network is disabled
examples/offline_install.sh does two steps.
- With the caller’s network, it downloads
requirements.txt(hash-checked) and builds the project wheel into a wheelhouse.pip wheelbuild isolation may download hatchling during this step. That traffic is part of the fetch, not the install. - It re-executes the install in a new mount and network namespace:
unshare --propagation private --mount --net --map-root-user. If neither
can create a namespace, the script exits. It does not install with the host
network still attached.
--propagation private keeps the sysfs mount inside the namespace. The
host’s interfaces stay where they are. After the install process exits, the
script connects to 1.1.1.1:443 again from the host.
Before pip install, inside the namespace, the script mounts a fresh sysfs
and requires all of the following:
- that sysfs
class/netlists onlylo - a TCP connect to
1.1.1.1:443fails withENETUNREACH(errno 101) - resolving
pypi.orgfails within 5 seconds
PIP_DISABLE_PIP_VERSION_CHECK=1,
PIP_NO_CACHE_DIR=1, and PIP_NO_INDEX=1, so pip does not read a download
cache and does not query an index for a version notice. Proxy variables are
unset for the install commands.
requirements.txt. --no-deps installs that wheel without resolving
anything else. The script prints the SHA-256 of the wheel it just built.
That archive includes build timestamps, so a later rebuild can have a
different digest. The digest is not a lock. Dependency locks stay in
requirements.txt.
A fresh sysfs mount is required for the interface check. Without it,
/sys/class/net can still list the host’s interfaces after unshare --net,
while sockets in the namespace still cannot use them.
On Linux, pip ignores the Windows-only colorama line in the lock.
Run it from a checkout:
venv module (python3-venv
on Debian/Ubuntu), unshare from util-linux, and timeout from coreutils.
GitHub-hosted Ubuntu runners provide passwordless sudo; the workflow below
uses that path.
Observed run
Date: 2026-09-24. Host: Linux x86_64, kernel 6.12.94+, CPython 3.12.3. Command:bash examples/offline_install.sh.
The fetch step used the network. pip printed Downloading for the locked
requirements and upgraded the fetch environment to pip 26.2.1. pip wheel
reported Installing build dependencies while that network was still up
(pyproject.toml requires hatchling to build). The wheelhouse
held 23 files. pip printed Ignoring colorama because the lock’s
sys_platform == "win32" marker does not match Linux.
Isolation was:
pip install:
/tmp/etalon-wheels (Looking in links: /tmp/etalon-wheels and
Processing /tmp/etalon-wheels/... for each locked distribution and for
etalon-0.0.1-py3-none-any.whl) and printed Successfully installed etalon-0.0.1.
lo docker0 enp0s5 and
host connect 1.1.1.1:443: ok ('1.1.1.1', 443).
The install phase was also started in a mount namespace that did not drop
the host network (sudo unshare --propagation private --mount, without
--net). It printed sysfs net devices: docker0 enp0s5 lo, printed
expected only lo in the network namespace, and exited 1. It did not
create the install virtual environment.
GitHub Actions
.github/workflows/offline-install.yml runs the same script on
ubuntu-latest with CPython 3.12. The job fails if the namespace cannot be
created, if the egress check does not return ENETUNREACH, or if
etalon pack validate / etalon verify do not succeed. A green run is the
same class of evidence for that runner.