Husk
GitHub

Security

The threat model

What Husk protects you from, what it does not, and how to tell in one command which of the two you are running.

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:

  1. Accident. The model writes rm -rf $BUILD_DIR and BUILD_DIR is empty.
  2. Confusion. It believes it is in /work and is actually in your home directory.
  3. 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 doctor
husk doctor, computers section

isolated 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

ControlScopePage
Kernel isolationdocker, podman, flyIsolation
Path jailFilesystem API on every providerIsolation
Command deny listEvery provider, every execIsolation
Environment scrubEvery provider, every execSecrets
Output redactionEvery tool result, before the model sees itSecrets
Network policyfetch_url and http_request; none also at the kernel on containersNetwork
Internal-host floorEvery mode, including fullNetwork
Approvalsdangerous toolsApprovals
Output capsEvery exec and every tool resultIsolation
Bearer auth, loopback bindhusk serveSelf-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 serve binds 127.0.0.1, and it refuses to start on a non-loopback address without HUSK_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 through shell or husk exec sees the whole filesystem your user can see. Details.
  • network.mode: egress is enforced in the tools, not by a firewall. curl run through shell is 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 auth field 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.