CLI Usage
hermetic [HERMETIC_OPTS] -- <TARGET> [TARGET_ARGS...]
The -- separator is mandatory when invoking a target. Hermetic
parses everything before -- as its own flags; everything after is
passed verbatim to the target. This avoids any flag collision between
hermetic and the program you're wrapping, even if that program uses
its own argparse with positional arguments.
If you pass -h, --help, or --version and no --, hermetic
treats the call as help-only and prints help, then exits.
Targets
<TARGET> is resolved in this order:
module:attr—pkg.module:callable. Imports the module and invokes the callable, or runs the module as__main__ifattris not callable.- Console script — anything registered under
console_scriptsentry points in any installed distribution.http,black,pytest,mytool, etc. - Executable on PATH —
python,py.exe, or any script with a Python shebang. - Module name fallback — runs as
python -m <TARGET>.
If the resolved console script lives in a different Python
interpreter (typical for pipx-installed tools), hermetic switches
to Bootstrap Mode and re-launches the target
under its own interpreter with a generated sitecustomize.py that
applies the same guards.
Flags
Network
| Flag | Effect |
|---|---|
--no-network |
Disable outbound networking — socket.connect, getaddrinfo, gethostbyname, ssl.SSLContext.wrap_socket, socketpair, fromfd, fromshare. Bind to non-loopback also denied. |
--allow-localhost |
When --no-network is on, permit connections to 127.0.0.1, ::1, localhost, 0.0.0.0. |
--allow-domain DOMAIN |
When --no-network is on, permit connections to DOMAIN and *.DOMAIN (suffix match). Repeatable. |
Cloud metadata endpoints (169.254.169.254,
metadata.google.internal, fd00:ec2::254, fe80::a9fe:a9fe,
100.100.100.200) are always denied. --allow-domain cannot
override this.
See Network Guard for the full surface.
Subprocess
| Flag | Effect |
|---|---|
--no-subprocess |
Block subprocess.Popen/run/call/..., os.system, os.exec*, os.spawn*, os.fork, posix_spawn, asyncio.create_subprocess_*, multiprocessing.Process.start, _posixsubprocess.fork_exec. |
See Subprocess Guard for the full surface.
Filesystem
| Flag | Effect |
|---|---|
--fs-readonly |
Deny all writes (open(..., "w"/"a"/"x"/"+"), os.rename, os.remove, pathlib.Path.write_text, shutil.copy*, shutil.move, shutil.rmtree, etc.). Reads anywhere are still permitted. |
--fs-readonly=ROOT |
Same as above, plus reads must lie under ROOT (resolved via os.path.realpath, so symlinks are followed before the check). |
See Filesystem Guard for the full surface.
Environment
| Flag | Effect |
|---|---|
--no-environment, --no-env |
Block os.getenv, os.environ[...], os.putenv, os.unsetenv, and mapping-style environment access. |
See Environment Guard for the full surface.
Dynamic code execution
| Flag | Effect |
|---|---|
--no-code-exec |
Block eval, exec, direct compile(...) calls, and runpy.run_module / runpy.run_path. Imports still work. |
See Dynamic Code Guard for the full surface.
Import policy
| Flag | Effect |
|---|---|
--deny-import NAME |
Deny importing NAME and its submodules. Repeatable. |
--block-native |
Refuse to load native extension modules (.so/.pyd). Refuse to import ctypes, _ctypes, cffi, _cffi_backend. Patch ctypes.CDLL, ctypes.PyDLL, cffi.FFI, etc. on already-loaded copies. When combined with --no-subprocess, also block sh, pexpect, plumbum, sarge, delegator. |
See Import Policy Guard and Native Imports Guard for the full surface.
Interpreter mutation
| Flag | Effect |
|---|---|
--no-interpreter-mutation |
Block os.chdir, os.fchdir, site.addsitedir, and mutation of sys.path, sys.meta_path, sys.path_hooks, and sys.path_importer_cache. |
See Interpreter Mutation Guard for the full surface.
Profiles
| Flag | Effect |
|---|---|
--profile NAME |
Apply a named bundle of flags. Repeatable. Profiles compose by union. |
Built-in profiles: block-all, net-hermetic, exec-deny,
fs-readonly, block-native. block-all now also enables the
environment, dynamic-code, and interpreter-mutation guards. See Profiles.
Other
| Flag | Effect |
|---|---|
--trace |
Print one structured line to stderr for every blocked action. |
--seal |
Once installed, refuse to uninstall. Raises the bar against one-line bypass; not bulletproof. See Sealed Mode. |
--version |
Print version and exit. |
-h, --help |
Print help and exit. |
Exit codes
| Code | Meaning |
|---|---|
0 (or anything the target returns) |
Target ran to completion. The target's exit code is propagated. |
2 |
A guard blocked an action — PolicyViolation raised. |
1 |
Hermetic itself failed (bad flags, target not found, bootstrap error). |
127 |
(Windows bootstrap mode only) Target executable not found. |
Examples
Block network for HTTPie:
hermetic --no-network -- http https://example.com
Allow only localhost; block subprocess; serve a dev app:
hermetic --no-network --allow-localhost --no-subprocess -- devserver --port 8000
Lock everything down for a one-shot data analyzer:
hermetic --profile block-all -- my_analyzer.py --input data.csv
Sandbox-rooted read-only filesystem:
hermetic --fs-readonly=./sandbox -- python my_script.py
Use a pipx-installed tool with sandboxing applied to its venv:
hermetic --no-network -- http https://example.com
# hermetic detects foreign Python, injects sitecustomize, execs the script
Explicit module:attr form:
hermetic --no-network -- httpie.core:main https://example.com