Fence: sandboxing commands without a container
Lightweight, container-free sandbox for running commands with network and filesystem restrictions
At a glance
- What is it?
- Fence is a Go CLI that wraps a command in a sandbox with outbound network access blocked by default and filesystem access governed by rule files, aimed at teams running semi-trusted builds and CLI coding agents on a shared machine.
- Who is it for?
- Adopt Fence if you run package installs, build scripts or CLI coding agents on a workstation where a stray script should not reach the network or your home directory. Skip it if you need a Windows binary, kernel-level isolation guarantees the README does not claim, or an audited rollback story, since none is documented.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Fence puts between a command and the network
Fence takes a command line, wraps it, and runs it under restrictions: no outbound network unless a config file says otherwise, and filesystem access narrowed to paths you list. The point is not to make commands faster or safer in the abstract, but to bound the blast radius of code you did not write. The README names the intended workloads directly, package installs, build scripts, CI jobs and unfamiliar repositories, and adds a second use case that is unusual for a sandbox tool, running AI coding agents as a permission manager rather than as the thing being sandboxed.
The default posture is deny. The README's first example runs a network request through the wrapper and shows the result as 403 Forbidden, which means a failure is visible to the caller instead of a silent connection that quietly succeeds. That single default changes how a semi-trusted script behaves, since the script now has to be given permission rather than it defaulting to access.
The permission manager angle is the more interesting claim. The README names Claude Code, Codex, Amp, Gemini CLI, GitHub Copilot, OpenCode and Factory (Droid) CLI as agents Fence is used with, and points to a separate agents document for the details. A wrapper that constrains an agent turns a coding assistant from an unbounded process into one whose side effects are declared up front, which is a different contract from asking the agent to behave.
sandbox-exec on macOS, bubblewrap on Linux, and the traffic in between
The enforcement is delegated to the operating system rather than to a container runtime. On macOS the sandbox is built on sandbox-exec, and on Linux it is built on bubblewrap, which the README lists as a required Linux dependency alongside socat for network bridging. The optional third piece is bpftrace, needed only when you want filesystem violation visibility while running in monitor mode with the -m flag.
That dependency list tells you the shape of the mechanism. A command is launched inside an OS-level sandbox, and the network is not simply switched off, it is brokered, with socat in the path, which is what allows a per-domain allowlist to be enforced at all. The cost of that design is a set of platform prerequisites, and on Linux the README is explicit that bubblewrap and socat have to be present before the tool can do anything useful.
Topics attached to the project include landlock, seccomp, seatbelt, bubblewrap and socat. The README, though, names only sandbox-exec for macOS and bubblewrap for Linux as the mechanisms, so treat the rest as interest in the wider kernel sandboxing space rather than as a documented list of enforcement layers. A security model document is linked from the README at fencesandbox.com/docs/reference/security-model, and that is where the precise boundaries of each platform layer should be confirmed before you rely on any of it.
Config discovery, inheritance chains and the code template
Configuration is a JSON or JSONC file, and the lookup order matters more than usual here because Fence is often used in a repository that a user does not control. Without an explicit --settings flag, Fence looks for fence.jsonc or fence.json in the current directory and then in parent directories, and only falls back to ~/.config/fence/fence.jsonc or fence.json when nothing is found closer to the working directory. Both extensions are parsed as JSONC, so comments and trailing commas are accepted in either.
The inheritance model lets a repository add rules on top of a shared base, and a special base name exists for that purpose. A file containing a key named extends with the value @base applies repo-local overrides on top of each user's normal Fence configuration, which is how a project can allow writes in the working tree without shipping a whole ruleset of its own.
A concrete config looks like this:
{
"$schema": "https://raw.githubusercontent.com/fencesandbox/fence/main/docs/schema/fence.schema.json",
"extends": "code",
"network": { "allowedDomains": ["private.company.com"] },
"filesystem": { "allowWrite": ["."] },
"command": { "deny": ["git push", "npm publish"] }
}The extends value points at a named template, and the code template is the one that opens up package registries, with npm and pypi among the allowed destinations. A deny list for commands sits alongside the network and filesystem rules, and the same key can block publishing and pushing operations, which matters when the wrapped command is a build step that shells out to a package manager.
The tool also lets you read what it resolved rather than guessing, and the split between streams is designed for scripting:
fence config show
fence config show --settings ./custom.json
fence config show --template codeThe README explains that this prints the inheritance chain to stderr and the fully resolved config as plain JSON to stdout, so the output can be piped into jq. When a wrapper hides what it is doing, the first thing to check is this output, because it tells you which template won and which file in which directory contributed the final rule.
Your first run: install, then watch a request fail
Installation on macOS and Linux is a single script fetched over the network:
curl -fsSL https://cli.fencesandbox.com/install.sh | shA Homebrew tap is offered for macOS, and Nix users can run the tool without installing it at all through nix run, including on Windows under WSL. If you prefer to build from source, the Makefile at the repository root builds a binary named fence from the cmd/fence package, and the go.mod file requires Go 1.26.0, so a local toolchain older than that will not work.
git clone https://github.com/fencesandbox/fence
cd fence
go build -o fence ./cmd/fenceNote the module path change the README calls out. The project moved from github.com/Use-Tusk/fence to github.com/fencesandbox/fence, so a Homebrew install from the old tap has to be removed and retapped, and a go install has to be repeated against the new path:
go install github.com/fencesandbox/fence/cmd/fence@latestThe first real use is deliberately boring. Run a command that expects the network and watch it fail, which confirms the sandbox is active before you trust it with anything:
fence curl https://example.comThe README shows that request coming back as 403 Forbidden. Next, hand the wrapper a whole shell string with the -c flag, and then run an agent under the code template, which opens up package registries:
fence -c "echo hello && ls"fence -t code -- claudeMonitor mode adds a log of blocked requests as they happen, and that log can be written to a file for later inspection:
fence -m --fence-log-file /tmp/fence.log -- claude
tail -f /tmp/fence.logFlags for the wrapped command go after a double dash, so passing a risky permission switch through to an agent looks like this:
fence -- claude --dangerously-skip-permissionsThat separator matters for scripting. Without it, Fence tries to interpret the agent's own flags, and the failure mode is a confusing argument error rather than a clear message.
Where Fence is the wrong tool
Fence is a permission wrapper, not a virtual machine and not a container image. Anything the sandboxed process can read is still readable by it, so a script that knows the path to a credential file can attempt to read it, and Fence's job is to make the attempt fail unless you allowed that path. If your threat model is a process that must not exist on the host at all, an operating system level sandbox is the wrong shape of tool.
The command deny list is a string match against the command you passed. Entries such as rm -rf /, git push or npm publish are blocked because they are listed, and a wrapper that renames or rearranges the same operation is not on the list. Treat the deny list as a guardrail against accidents and familiar mistakes, not as a complete policy engine for arbitrary programs.
Platform coverage is narrow. The install paths cover macOS and Linux, and the only Windows route described runs inside WSL through Nix, so a native Windows job is not served by this project. On Linux, bubblewrap and socat are mandatory and bpftrace is needed for filesystem violation visibility, which means the tool fits a developer workstation or a Linux runner you control, not every environment you might want to run it in.
There is also a documentation gap that matters for automation. The README does not document rollback behaviour, so if you adopt Fence in a pipeline, decide yourself what happens when a job fails under a ruleset and what a human has to edit to loosen it. The command denials are also string based, so an escaped or aliased command may slip past a rule you believed was enforced.
How Fence differs from a container, and from sandbox-runtime
The usual alternative to this kind of tool is a container. The difference in approach is that a container gives you filesystem isolation as a side effect of an image and a runtime daemon, and network isolation is something you have to configure in the container's network setup. Fence takes the opposite route. There is no image to build and no daemon to keep alive; you prefix one command, and the operating system's own sandboxing facility constrains it, with network blocked unless a domain is allowed.
That makes Fence a poor fit for multi-stage builds that need reproducible environments, since the point of a container is that the whole environment is pinned, not that one command is fenced. It is a good fit for the case where the environment is fine and only the side effects of one program are not, which is exactly the package install or unfamiliar script scenario the README describes.
For attribution, the README states that Fence is inspired by Anthropic's sandbox-runtime. The two are not presented as identical, and the README frames Fence's own additions as the template system, the config inheritance chain, the command deny rules, the SSH command filtering and the permission manager role for CLI agents. Where the two diverge in enforcement details, the security model document linked from the README is the place to read the specific guarantees, and it is worth reading it before assuming the inspiration implies the same coverage.
Release cadence, licence and what an upgrade costs you
The project is not archived, and the last push was on 2026-10-14, so changes are landing on the main branch as of that date. Recent tagged releases are v0.1.65 on 2026-08-01, v0.1.66 on 2026-08-06 and v0.1.67 on 2026-09-01. The version numbers sit below 1.0, which is the clearest signal available that interfaces may still move.
The licence is Apache-2.0, the same permissive licence the badge in the README points at, which is what the license identifier in the repository tree and the project page both indicate. Apache-2.0 carries a patent grant and an explicit patent termination clause, and it requires preserving notices when you redistribute the code. That is a summary of the licence terms rather than legal advice, and the LICENSE file in the repository is the authoritative text if you have a compliance question.
The upgrade cost for a user is mostly operational. Because the config file is JSONC and the schema is published at a raw URL in the main branch, a rules file that an older binary accepted may need adjusting if a key changes, and because the lookup walks parent directories, a machine that picks up a new repository-local config can behave differently without anyone changing a global setting. The most recent release in that list, v0.1.67, is dated 2026-09-01, which is a useful anchor when you file an issue. The dependencies in go.mod are modest and current looking, with cobra for the command line, jsonc and sjson for config handling, and yaml.v3 for YAML, and the Makefile covers a four platform build matrix for darwin and linux on amd64 and arm64.
Editorial conclusion
Adopt Fence if you run package installs, build scripts or CLI coding agents on a workstation where a stray script should not reach the network or your home directory. Skip it if you need a Windows binary, kernel-level isolation guarantees the README does not claim, or an audited rollback story, since none is documented. Before enabling it in CI, run fence config show on the exact working directory you will use and confirm the resolved rules match your intent, then watch fence -m output on a first real job.
Frequently asked questions
What do I need to install Fence on Linux?
Fence itself installs with a single script or through brew, nix or go install, but on Linux the README lists bubblewrap for sandboxing and socat for network bridging as additional requirements, with bpftrace optional and needed only for filesystem violation visibility when running with -m.
Does Fence block network access by default?
Yes. All outbound traffic is blocked by default, and the README shows a request through the wrapper returning 403 Forbidden. To permit package registries and similar hosts, use a template such as the code template with the -t flag, or list allowed domains in your config file.
How do I check which Fence configuration is actually in effect?
Run fence config show. It prints the inheritance chain to stderr and the fully resolved config as plain JSON to stdout, so you can pipe it into jq, and you can add --settings or --template to inspect a specific file or template.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fencesandbox-fence)Was this useful?