manifesto

Why agents need honest sandboxes.

Guardrails stop accidents. Only isolation stops a prompt-injected model. A tool that will not tell you which one you have has already made that choice on your behalf.

You are not defending against a malicious user. You are the user. You are defending against your own agent doing something irreversible, and it happens for three reasons. It writes rm -rf $BUILD_DIR and BUILD_DIR is empty. It believes it is in /work and is actually in your home directory. Or it read a file, a web page or an issue comment that told it to do something, and it complied.

The third one is different in kind, not degree. An injected model is not making a mistake. It is following instructions competently, toward someone else’s goal, using the tools you handed it. A deny list is a hint to a system that is trying to comply with you. It is nothing at all to a system that is trying to comply with someone else.

Two modes, and the word that hides them

Almost every product in this category uses one word for both modes. “Sandboxed.” “Isolated.” “Secure.” Those words are doing no work unless a mechanism is attached to them, and the mechanisms are not comparable.

A container is a kernel boundary: dropped capabilities, no new privileges, a read-only root, a pid ceiling, a locked root account, the network policy you declared. An injected model in that mode can wreck the container. It cannot reach your home directory, your SSH keys, your Docker socket or your other containers.

A guarded working directory is a different thing wearing the same word. Husk’s local provider resolves every filesystem call through realpath and rejects anything that leaves the workspace, including through a symlink created inside it. It strips credential-shaped environment variables. It refuses a short list of unrecoverable commands. It caps captured output and kills the whole process group on timeout. Those are real controls and they are tested. The agent still shares your kernel, your network and your user account, and the command policy is a deny list, and deny lists are bypassable by anyone who is trying.

Both of those are worth shipping. Calling them the same thing is not.

Vagueness is a design decision

When a product will not say which mode you are in, that is not an oversight in the documentation. It is a product decision, made because the honest sentence is unflattering and the vague one converts better. The cost is paid later, by someone who believed they had containment, in a postmortem.

So the isolation guarantee has to be a value in the program, not a paragraph on a website. In Husk it is a field on the provider interface. husk doctor prints it. The CLI says it the first time you use a provider that does not have it. The MCP server puts it in the model’s first tool result, because a model that believes it is contained when it is not will take risks it otherwise would not.

husk doctor

It names your provider, whether that provider is isolated, and what to do about it. It never silently substitutes a weaker provider for the one you asked for: asking for --provider docker with the daemon down is an error, not a downgrade.

Why the deny list stays short

An over-eager deny list gets switched off, and a switched-off deny list protects nobody. The bar for inclusion is narrow on purpose: no legitimate agent task needs this, and running it by accident is unrecoverable. rm -rf ./build is allowed. rm -rf / is not. grep -r "sudo" . is allowed, because matching the word sudo anywhere on a command line rather than in command position is exactly the false positive that teaches people to pass a flag that turns the guardrails off.

Every rule is anchored to command position — the start of a line, or after a pipe, a semicolon, a logical operator, or a substitution. That is less clever than a scanner and considerably harder to annoy someone with.

The free path is part of the argument

An honest sandbox story only matters if people can reach it. Most products in this space start at a hosted control plane and add a local option later, which inverts the trust and cost story for the people most likely to try it first: someone with no budget and a laptop.

Husk goes the other way. The local provider is the primitive and everything hosted is a plugin behind the same interface. There is no account, no card, and no telemetry — absent from the codebase, not disabled by a flag. That is not generosity. It is what makes the honesty checkable: you can read the code that decides whether you are isolated, on the machine you are asking about, in the time it takes to clone a repository.

What we concede

Husk is single-user in v1 with no hosted control plane. It does not do GPUs. Cold starts on Docker are Docker’s cold starts. The local provider is not a security boundary. Saying these first is cheaper than being caught omitting them, and it is the same discipline the isolation flag exists to enforce.

We supply the body. The agent supplies the mind. The least we can do is tell you which room it is standing in.


The mechanics behind every claim above are in the provider table and in the repository’s security model.