Profiles

A profile is a named bundle of flags. Use one when you don't want to spell out every guard, or when a team wants a shared baseline.

hermetic --profile net-hermetic -- my_app

Profiles compose by union. Passing --profile A --profile B enables every flag set in either. Combining a profile with explicit flags is allowed — explicit flags can only add to a profile, never remove from it.

Built-in profiles

block-all

Lock down everything.

Equivalent flags
--no-network --no-subprocess --fs-readonly --no-environment --no-code-exec --no-interpreter-mutation --block-native

Use when you want a target to fail fast at the first sign of any side-effect.

net-hermetic

Block network, but keep localhost reachable so test fixtures and loopback servers still work.

Equivalent flags
--no-network --allow-localhost

The standard "no internet, but a local Postgres is fine" setup.

exec-deny

Block subprocess invocation.

Equivalent flags
--no-subprocess

fs-readonly

Make the filesystem read-only (writes denied; reads anywhere allowed).

Equivalent flags
--fs-readonly

block-native

Refuse to load native extensions and FFI libraries.

Equivalent flags
--block-native

Composition rules

When merging a profile into the current config, hermetic only ever turns flags on:

  • Boolean flags merge by OR. Once on, never off.
  • List flags (e.g. allow_domains) merge by extension.
  • String flags (e.g. fs_root) overwrite if the new value is truthy and the existing one is empty.

In other words: a later profile cannot undo an earlier one.

# All of network, subprocess, filesystem-write, and native imports blocked.
hermetic --profile block-all --profile net-hermetic -- my_app
# (net-hermetic adds --allow-localhost on top of block-all.)

Custom profiles

There is no config-file mechanism for user-defined profiles yet. For now, define a thin wrapper script:

#!/usr/bin/env bash
# my-hermetic
exec hermetic \
    --no-network \
    --allow-domain api.internal.com \
    --no-subprocess \
    --fs-readonly=./workspace \
    -- "$@"

Or build a config in Python and pass kwargs to hermetic_blocker:

from hermetic import hermetic_blocker

DEFAULT_POLICY = dict(
    block_network=True,
    allow_domains=["api.internal.com"],
    block_subprocess=True,
    fs_readonly=True,
    fs_root="./workspace",
    block_environment=True,
    block_code_exec=True,
    block_interpreter_mutation=True,
)

with hermetic_blocker(**DEFAULT_POLICY):
    ...

Profile reference (for tool authors)

Profiles are defined in hermetic/profiles.py as a dict[str, GuardConfig]. The merge logic is:

# pseudo-code
def apply_profile(base: GuardConfig, name: str) -> GuardConfig:
    prof = PROFILES[name]
    merged = copy(base)
    for k, v in prof.fields():
        if v is truthy:
            if bool: merged[k] = True
            if list: merged[k].extend(v)
            if str:  merged[k] = v
    return merged