Contributing
Thanks for considering a contribution. Hermetic is small and maintained by one person — issues and pull requests are welcome, but please read this page first to avoid wasted effort.
Project goals (and non-goals)
Goals
- A predictable, low-surprise sandbox runner for Python console scripts.
- A library for in-process guards that are easy to use from pytest fixtures and decorator-style code.
- Honest documentation of what is and isn't defended against.
- Zero runtime dependencies.
Non-goals
- Defeating a determined adversary with arbitrary code execution. Anyone who needs that should use a real sandbox; see Threat Model.
- Replacing OS-level isolation. Hermetic complements containers,
seccomp, etc.; it does not replace them. - Adding runtime dependencies. New features need to fit inside the standard library.
If your idea is in the "non-goal" column, please open an issue to discuss before submitting code.
Getting set up
git clone https://gitlab.com/matthewdeanmartin/hermetic
cd hermetic
uv sync # installs the dev group
uv run pytest # run the test suite
uv run mkdocs serve # preview docs at http://127.0.0.1:8000
The Justfile and Makefile at the repo root encode common
recipes (just test, just docs, etc.). Either works; pick the
one your shell prefers.
Project layout
hermetic/
├── __init__.py # public re-exports
├── __main__.py # `python -m hermetic`
├── cli.py # argparse, entry point
├── runner.py # in-process vs bootstrap dispatch
├── resolver.py # target-name → module/exec resolution
├── blocker.py # context manager / decorator
├── profiles.py # named flag bundles
├── bootstrap.py # AUTO-GENERATED inlined sitecustomize
├── errors.py # PolicyViolation, BootstrapError
├── util.py # arg-splitting, which()
└── guards/ # the actual monkey-patches
├── network.py
├── subprocess_guard.py
├── filesystem.py
└── imports_guard.py
scripts/
└── build_bootstrap.py # regenerates hermetic/bootstrap.py
spec/
└── secure_secure.md # security review and roadmap
test/
├── ... # unit tests, mirrors hermetic/ layout
└── test_security/ # known-bypass tests
When adding or changing a guard
A change to any guard in hermetic/guards/ almost always also
needs a change to hermetic/bootstrap.py — the bootstrap
sitecustomize ships an inlined copy of the same logic so that it
works without hermetic being importable in the target
interpreter.
The workflow:
- Edit the in-process guard in
hermetic/guards/<name>.py. Each guard module exposes its bootstrap snippet as theBOOTSTRAP_CODEconstant. - Update the corresponding
BOOTSTRAP_CODEblock in the same file, or extend the in-file generator. - Run
python scripts/build_bootstrap.pyto regeneratehermetic/bootstrap.py. - Add tests under
test/test_guards/for the in-process path and (where it differs) undertest/test_security/for the bootstrap path. - Update the relevant page under
docs/guards/if the user- visible behavior changed.
The bootstrap.py header carries a WARNING: AUTO-GENERATED
notice. Do not hand-edit it.
When adding or changing a flag
- Add it to the CLI parser in
hermetic/cli.py. - Add it to
GuardConfiginhermetic/profiles.py(and toBlockConfiginhermetic/blocker.pyif it should also be exposed viahermetic_blocker(...)). - Wire it through
runner.run()and the bootstrap flag passing. - Document it in
docs/cli-usage.mdand the relevant guard or feature page. - Consider whether it should appear in any built-in profile.
Testing
uv run pytestruns the full suite.uv run pytest test/test_security/runs the known-bypass catalogue. These tests assert both that bypasses are blocked with the relevant guard on, and that they would succeed with the guard off — so we don't claim to block something the host Python doesn't permit anyway.- New tests should not require network or external services. The test suite is run under hermetic in CI.
Documentation
- Markdown sources live under
docs/. - The published site is built by Read the Docs from
mkdocs.yml. Local preview:uv run mkdocs serve. - Keep the prose honest — if a guard is best-effort, say so. Overclaiming security guarantees is the worst outcome for this project.
Pull requests
- Small, focused PRs are easier to review than large ones.
- Include a test that fails before your change and passes after.
- Update docs in the same PR if the user-visible behavior changes.
- The CI runs lint (
ruff), type-check (mypy), tests, and doc build. Please run them locally before pushing.
Reporting bugs
- File an issue at https://gitlab.com/matthewdeanmartin/hermetic.
- Include: hermetic version, Python version, OS, the exact
command you ran, and the full output (with
--traceif a guard is involved). - For security-relevant issues, see the Security page first — some issues warrant a private advisory rather than a public bug.
Code of conduct
Be kind. Assume good faith. The maintainer's time is finite.