Guide 03 / Policy · 2026-08-21

Write down what the agent may reach

Permission prompts ask the agent to police itself. A box policy is resolved before the agent starts, enforced outside its process, and digested into every receipt.

Start narrow. Grant the workspace, the system paths required to run, the destinations required for the task, and a finite wall clock. Add authority only after a refusal explains why it is needed.
A checked-in h5i policy resolves into a complete policy file, a SHA-256 digest, and receipts stamped with that digest
Intent is checked into the repository. Enforcement is fully resolved before creation. The digest connects later evidence to the rules that actually ran.

Command-line flags are convenient for experiments and poor as a long-term security policy. They arrive one at a time, disappear from code review, and are easy to vary between developers. A repository profile makes the boundary one object that can be discussed before any agent process exists.

1. Add a named profile

Create .h5i/env.toml in the repository. This example supports a bounded review that needs the GitHub API:

.h5i/env.toml
[profile.review]
isolation = "supervised"

[profile.review.fs]
read  = ["/usr", "/etc"]
write = ["$WORK"]

[profile.review.net]
mode   = "deny"
egress = ["api.github.com"]
unix   = false

[profile.review.resources]
mem   = "4G"
procs = 256
wall  = "30m"

$WORK is the box workspace, not your current checkout. mode = "deny" makes the allowlist meaningful: everything not named is refused.

Read every field as authority

The filesystem block says which existing data enters the box and where writes can land. The network block says whether destinations exist from the box's point of view. The resource block bounds how long a mistaken loop or fork-heavy build can consume the machine. None is application configuration. Each is part of the security claim.

2. Choose the tier by threat model

TierUse it forBoundary to remember
workspaceCheckout separation onlyNo confinement
processFast local build and testShared kernel; network is deny or host
supervisedUntrusted dependencies and bounded egressShared kernel; L3/L4 egress enforcement
containerPortable image-based environmentsProxy-respecting L7 egress only
microvmWork that must not share the host kernelNeeds virtualization, msb, and a pre-pulled image

container buys portability. It does not provide tighter egress enforcement than supervised. Pick the property you need instead of assuming every higher-sounding rung is stronger in every dimension.

3. Prove the requested policy is satisfiable

host
$ h5i box probe
$ h5i box create policy-check --profile review
$ h5i box status policy-check
$ h5i box doctor policy-check

An explicit tier either exists or creation fails. h5i does not silently downgrade. The status prints the resolved policy, while doctor checks that the box can still support its claim.

The stored policy.resolved.toml is the version to audit after creation. Variables such as $WORK, platform-specific grants, engine selection, and runtime defaults have been expanded there. Editing .h5i/env.toml later does not retroactively change an existing box; create a new one if the boundary changes.

4. Let denials guide refinement

host
$ h5i box run policy-check -- npm test
$ h5i box log policy-check
$ h5i box export policy-check --out ./policy-check-report

A denied registry host may justify one more destination. A denied telemetry host usually does not. Treat each addition as a reviewable transfer of authority, not a way to make the error disappear.

Do not turn on Unix sockets casually. unix = true permits AF_UNIX sockets, which can carry file descriptors through SCM_RIGHTS. The browser profile needs this; most build profiles do not.

5. Commit the policy with the code

A checked-in profile gives reviewers one file to discuss. At creation, h5i resolves machine-specific values, serializes the result, hashes it, and puts that digest on the receipts. The repository states the intended boundary; the receipt names the boundary that actually ran.

Understand what the same egress list means at each tier

The profile may contain the same hostname list while the enforcement changes underneath it. At supervised, h5i resolves and pins addresses, installs nftables rules in a private network namespace, pins DNS through a hosts file, and gates socket creation. A client that ignores proxy variables still meets packet-layer rules.

At container, the list configures an HTTP/HTTPS CONNECT proxy. This covers ordinary package managers, SDKs, and command-line HTTP clients that respect proxy configuration. It does not constrain arbitrary raw connections through rootless NAT. The policy syntax is shared; the reported enforcement layer tells you what the list proves.

At microvm, the guest network stack evaluates destination rules. Enforcement is L3/L4, but denied attempts do not currently produce the same per-host receipt summary. Stronger blocking and richer evidence are independent properties.

Add credentials as grants, not environment inheritance

If the task needs an authenticated API, do not add the real token to env.pass. Declare an auth grant whose credential is resolved on the host and whose client can be pointed at a base URL. The box receives a per-run dummy; the broker injects the real credential only toward the pinned upstream.

Keep the service token narrow anyway. The broker protects possession and destination. It does not turn repository-wide administration into read-only access.

Resource limits are platform claims too

Wall-clock limits are enforceable everywhere. Memory and process-count ceilings at the host-kernel tiers are not honestly enforceable on macOS, so h5i marks them rather than pretending. Choose container or microvm if a real ceiling is part of the threat model.

A limit should match the workload with enough headroom for ordinary peaks. A browser build that legitimately needs three gigabytes will teach nobody anything when capped at one. The useful ceiling prevents unbounded behavior without converting normal execution into noise.

Test the failure path, not only the happy path

After creation, deliberately request one path and one destination that should be denied. Then inspect the log or export. This confirms both enforcement and evidence routing on the current host.

inside and outside
$ h5i box run policy-check -- sh -c 'cat ~/.ssh/id_ed25519'
# expected: read refused or path absent
$ h5i box run policy-check -- curl https://example.invalid
# expected: destination refused
$ h5i box log policy-check

Do this with harmless targets. The exercise is not a penetration test; it is a smoke test that the written boundary appears in behavior and in the review record.

Common policy mistakes

Reference

Questions that come up

What happens if my machine cannot provide the requested tier?
Creation fails before a partial box is left behind. Explicit isolation requests are never silently downgraded.
Why is container egress weaker than supervised egress?
The container tier uses an HTTP/HTTPS proxy allowlist, so software that ignores proxy settings can bypass that L7 route. The supervised tier enforces destination access in a private network namespace at L3/L4.
Are memory and process limits enforced on macOS?
Not at the process and supervised tiers. h5i marks those values instead of claiming enforcement. Use container or microvm when a hard memory or process ceiling is required.
Design rationale

Five tiers, five different promises

Read the threat-model argument behind the ladder.

Make authority reviewable

A small policy file is easier to reason about than a trail of permission clicks.