Most sandbox documentation is vague here. Vagueness is how people end up believing they have containment they do not have, so this section is specific to the point of being unflattering.
Who the adversary is
You are not defending against a malicious user. You are the user. You are defending against your own agent doing something irreversible, for one of three reasons:
- Accident. The model writes
rm -rf $BUILD_DIRandBUILD_DIRis empty. - Confusion. It believes it is in
/workand is actually in your home directory. - Prompt injection. It read a file, a web page, a dependency's README or an issue comment that told it to do something, and it complied.
Guardrails handle (1) and (2). Only isolation handles (3).
That third case is the one that matters, and it is the reason the rest of this section exists. An injected model is not making a mistake. It is following instructions competently, toward someone else's goal, using every tool you gave it. A deny list written for accidents does not survive an adversary who can read the deny list.
The two modes, in one command
husk doctorisolated is a field on the provider interface, not a footnote. husk doctor prints it,
the MCP server states it in its first tool result, and GET /v1/doctor returns it.
Isolation by provider is the full table of what each of the five providers actually guarantees.
The controls, and where each one lives
| Control | Scope | Page |
|---|---|---|
| Kernel isolation | docker, podman, fly | Isolation |
| Path jail | Filesystem API on every provider | Isolation |
| Command deny list | Every provider, every exec | Isolation |
| Environment scrub | Every provider, every exec | Secrets |
| Output redaction | Every tool result, before the model sees it | Secrets |
| Network policy | fetch_url and http_request; none also at the kernel on containers | Network |
| Internal-host floor | Every mode, including full | Network |
| Approvals | dangerous tools | Approvals |
| Output caps | Every exec and every tool result | Isolation |
| Bearer auth, loopback bind | husk serve | Self-hosting |
Choosing a mode
Use local for your own code on your own machine.
Use docker or podman the moment an agent will read anything you did not write: a
scraped page, a dependency, an issue body, a PDF, a CI log from a fork.
Husk never silently substitutes a weaker provider for the one you asked for. Requesting
--provider docker with the daemon down is an error, not a downgrade:
Falling back to something with weaker isolation than you asked for is exactly the kind of surprise that turns into a security incident.
What Husk deliberately does not do
-
No telemetry. Not off by default — absent. There is no analytics call, no crash reporter and no phone-home in the codebase. The only outbound requests are to the model provider you configured, and to a container registry when a flavor's image is not already pulled.
-
No credential storage. Keys are read from the process environment. No command writes one to
~/.husk. -
No remote execution by default.
husk servebinds127.0.0.1, and it refuses to start on a non-loopback address withoutHUSK_TOKEN:error refusing to bind 0.0.0.0 without an auth token: this API can execute shell commands hint: set HUSK_TOKEN=$(openssl rand -hex 32) before `husk serve --host 0.0.0.0`, or bind 127.0.0.1 -
No privilege escalation. Husk runs as you and never asks for more. When it hits a permissions problem it says so and stops:
error permission denied: /srv/data/report.csv hint: husk runs as you; it will not escalate. Fix the permissions or pick another path.
Known gaps, stated plainly
These are real and they are documented rather than hidden. Each links to the page that explains it.
- The path jail covers the filesystem API, not shell commands. On
local, a command run throughshellorhusk execsees the whole filesystem your user can see. Details. network.mode: egressis enforced in the tools, not by a firewall.curlrun throughshellis not filtered. Details.- The internal-host floor matches on the hostname string, so DNS rebinding and alternative IP encodings pass. Details.
- Redaction is a pattern list. A credential in a format it does not know reaches the model. Details.
- An HTTP trigger's
authfield is stored and never checked. Details.
Reporting a vulnerability
Open a private security advisory on the repository rather than a public issue. Include
the provider, the version from husk version, and a reproduction.