Sealed Mode
By default, hermetic guards are reversible — uninstall_all()
restores the originals. That's the right behavior for tests and
ad-hoc scripts: you wouldn't want a pytest fixture to
permanently break socket access for the rest of the session.
For everything else, sealed mode turns guard installation into a one-way door.
What sealed mode does
When you pass --seal (CLI) or sealed=True (API):
- The first call to
install_all()activates a process-wide_SEALEDlatch. - Subsequent calls to
uninstall_all()are no-ops. Guards are never removed. _HermeticBlocker.__exit__still runs reference counting, but the eventual uninstall step is suppressed.- Nested
hermetic_blockerblocks may still widen the policy (add more guards) — they cannot weaken it.
What sealed mode does not do
- It does not hide hermetic's
_originalsdicts. An attacker who knows where to look can still find them insys.modules['hermetic.guards.network']._originalsand reapply them by hand. - It does not prevent direct module attribute writes. Code that
does
socket.socket = originalfrom outside hermetic still works (hermetic doesn't install a__setattr__veto on the module). - It does not survive interpreter restart. A fresh process starts unsealed.
In short, sealed mode raises the bar from "one line of code removes the guards" to "an attacker has to know the structure of hermetic's internals". Useful against careless code and mildly mischievous LLMs; not useful against a determined adversary.
When to use it
- LLM tool runners. The model has access to a Python
interpreter and you want it to fail loudly if it tries
hermetic.guards.uninstall_all(). - Long-lived sandboxed processes. A worker that should enforce a single policy for its whole lifetime.
- CI guardrails where the policy should not be re-negotiable by the test code itself.
When not to use it:
- Inside a pytest run with multiple tests that need different policies. Once one test seals, all subsequent tests are stuck with that policy.
- Inside a REPL or notebook where you want to experiment with flag combinations.
- Inside library code that other people will call. Sealing on import is an antisocial choice.
Examples
CLI:
hermetic --seal --no-network --no-subprocess --block-native \
-- python long_running_worker.py
Programmatic:
from hermetic import hermetic_blocker
with hermetic_blocker(
block_network=True,
block_subprocess=True,
block_native=True,
sealed=True,
):
# Guards stay on for the rest of the process — even after this
# block exits.
run_forever()
Decorator:
from hermetic import with_hermetic
@with_hermetic(block_network=True, sealed=True)
def main():
...
main()
# Network is still blocked here; cannot be re-enabled.
Combining sealed with the bootstrap
In bootstrap mode, sealed mode applies to the target's
interpreter. The target's process can no longer remove the
guards even by importing hermetic and calling uninstall_all().
The hermetic launcher process is short-lived (it execves into
the target on POSIX, or spawns and waits on Windows), so sealing
only matters for the target.