Fenzi

Start

Getting started

Run the agent you already use inside a sandbox on your own machine. You will see an allow, a deny, and an approval, then take the agent's changes back into your repo.

Free planNo accountmacOS on Apple Silicon, Linux, Windows through WSL 2 (experimental)

Before you start

You need three things.

  • A supported machine. A Mac with Apple Silicon, or Linux. Windows works through WSL 2, which upstream marks experimental. On a Mac, the sandbox runtime installs through Homebrew; if you do not have it, setup offers to install it first. See Install and platforms.
  • A container runtime or VM. Docker Desktop, Docker Engine 28 or later, Podman 5 on Linux, or the MicroVM driver. The sandbox runs inside one of these. On a Mac, the Linux isolation runs inside that VM, not on the Mac kernel. If yours is installed but stopped, setup starts it. On colima, setup writes the three gateway settings colima needs when you have no gateway config yet; see Troubleshooting.
  • An API key for your agent's model. For Claude Code, that is ANTHROPIC_API_KEY. Agents run with an API key. Signing in with a Claude, ChatGPT or Grok subscription is not supported yet. On its first start, Claude Code asks whether to use the key it found: choose Yes, it is the sandbox's placeholder. See Model keys.
  1. Install and set up

    cd ~/code/web curl -fsSL https://fenzi.dev/install.sh | sh

    Run it from the project you want the agent to work on. The installer downloads fenzi, checks its sha256 checksum, and starts fenzi setup, a guided setup in five steps. Move with the arrow keys and press Enter. Press Esc to go back to the previous question. The installer changes one thing without asking: if the folder it installs into is not on your PATH, it adds one line to your shell profile. FENZI_NO_MODIFY_PATH=1 stops that; see Install the CLI. After that, setup asks before it installs or changes anything.

    1. This machine. Checks the sandbox runtime, the gateway, and the image builder. If OpenShell is missing, setup offers to install the pinned release. If the gateway is not running, it offers to start it. If the gateway approves requests by itself, it offers to turn that off.
    2. Agent. Pick the agent. Agents without a reviewed profile are listed as not available yet.
    3. Model key. Paste the key. It is not shown and does not land in your shell history. If ANTHROPIC_API_KEY is already set, setup offers to use it. The key stays on this machine, in the sandbox's credential store. Inside the sandbox, the agent holds a placeholder. The real key is added on the way out, only on calls to the model's own API.
    4. Project folder. The current folder is the default. Your home folder is refused.
    5. Agent image. Build it now, or on the first run.
    Example output
    ■ This machine  1 of 5
    
    ✓ Runtime           OpenShell 0.1.2
    ✓ Gateway           connected
    ✓ Sandbox driver    docker
    ✓ Approvals         a person approves each request
    ✓ Log and requests  can be turned on
    ✓ Policy check      installed
    ✓ Image builds      docker
    
    ■ Agent  2 of 5
    
    ? Which agent should run in the sandbox? Claude Code
    
    ■ Model key  3 of 5
    
    ? Paste your Claude Code API key (ANTHROPIC_API_KEY) ›
      received, 108 characters, not shown
    ✓ Key               stored on this machine
    
    ■ Project folder  4 of 5
    
    ? Which folder should Claude Code work in? (~/code/web) ›
    ✓ Folder            ~/code/web
    
    ■ Agent image  5 of 5
    
    ✓ Image             fenzi/claude:2.1.288
    
    ┌─ Ready ──────────────────────────────────────────────┐
    │                                                      │
    │  Agent     Claude Code 2.1.288                       │
    │  Folder    ~/code/web                                │
    │  Policy    developer-default (built in)              │
    │                                                      │
    │  Reaches   api.anthropic.com, with your key          │
    │            api.github.com, read-only                 │
    │            registry.npmjs.org, read-only             │
    │            pypi.org, read-only                       │
    │            files.pythonhosted.org, read-only         │
    │  Waits     any other host, until you approve it      │
    │  Never     your home folder or the real key          │
    │                                                      │
    └──────────────────────────────────────────────────────┘
    
    ? Start Claude Code now? Start it in ~/code/web

    If a check fails and you choose not to fix it, setup stops and says what is missing. Run fenzi setup again when it is fixed. See Troubleshooting for each failure. Without a terminal, for example in CI, the installer only installs the command. Set FENZI_NO_SETUP=1 to get the same on a laptop.

  2. Run the agent

    Choose Start at the end of setup. Next time, start it from the project folder:

    cd ~/code/web fenzi run

    run starts the agent you picked in setup. Name another one to use it instead, as in fenzi run codex. Everything after the agent's name is passed to the agent, as in fenzi run claude --resume. If you did not build the image during setup, the first run builds it on your machine, so it takes longer than the next one.

    Type fenzi on its own for a menu of this folder: start the agent, review changes that are waiting, connect to a running sandbox, or answer requests.

    Example output
    policy    developer-default (built in) + fenzi.yaml, local-9e41c2d0
    prover    within_boundary
    image     fenzi/claude:2.1.288, building on this machine
    key       bl-claude, already stored on this machine
    sandbox   bl-web-4f2a, created
    upload    project copied to /sandbox/web, .gitignore respected, .git left out
              your home directory is not copied
    settings  waiting for the sandbox to load them
    guide     how to ask for access, added to the agent's instructions
    start     claude --permission-mode manual
    note      Claude may ask whether to use the API key it found. Choose Yes: it is the sandbox's placeholder, not your key.

    The first time Claude Code starts in a sandbox, it asks whether to use the API key it found in the environment, with No marked as recommended. Choose Yes. The value it found is the sandbox's placeholder, not your key, and with No Claude has no key to call the model with.

    Claude Code starts in its manual permission mode: it asks you before each command, and the policy decides what the command can reach. In auto mode, Claude's own check can refuse a command before it reaches the sandbox, and the steps below would not show you the sandbox at all. To choose another mode, pass it to Claude, as in fenzi run claude --permission-mode auto.

    The agent starts in your terminal as usual. It works on a copy of the project inside the sandbox. Your files on disk do not change until step 5. Git history stays on your machine, so the agent can read and edit the files but cannot run git log on them.

  3. Watch it say no

    Ask the agent to try four things. Each one hits a different limit.

    Ask the agent toWhat happensWhy
    Read ~/.aws/credentialsNo such file.Your home directory was never copied in. There is nothing to hide because it is not there.
    Write a file in /etcPermission denied.The policy makes /etc read-only. The kernel enforces it inside the sandbox.
    Run curl https://paste.example.netCould not resolve host.The host is not in the policy. The only way out of the sandbox is through a proxy, and it refuses to look the name up. The reason goes to the log.
    Send a POST to api.github.comHTTP 403 with policy_denied and the rule that is missing.GitHub is in the policy, read-only. The proxy reads the request, so it can say exactly what was refused.

    Then read the log from another terminal:

    fenzi log --deny
    Example output
    ■ 2 denies  bl-web-4f2a, running, ~/code/web
    
      time      process  destination                   reason
      10:42:07  -        paste.example.net             policy_dns_ineligible
      10:42:31  -        POST api.github.com/markdown  L7_REQUEST deny POST api.github.com:443/markdown reason=POST /markdown not permitted by policy

    A failed lookup by itself gives the agent little to go on. The guide Fenzi adds to the agent's instructions tells it where to look up the refusal and how to ask for access. The 403 says enough for the agent to recover on its own. Only network decisions reach the log. A blocked file write shows up as Permission denied in the agent's terminal and nowhere else.

  4. Approve a host

    Ask the agent to fetch a file from raw.githubusercontent.com. That host is not in the default policy, so the call is blocked. The agent files a request for the one file it needs, with its reason, and tells you it is waiting.

    fenzi requests
    Example output
    ■ 1 request from the agent  bl-web-4f2a
    
        7f3a91c2  raw.githubusercontent.com:443
                  /usr/bin/curl  “User asked for the schema file linked from the README.”
    
        2 more were drafted by the sandbox from refused connections, not asked
        for by the agent: github.com:443, downloads.claude.ai:443.
    
        fenzi requests --all  list the drafts too
    
    ? 7f3a91c2  raw.githubusercontent.com:443
      ❯ Approve       the agent can retry in about 10 seconds
        Deny          you give a reason; the agent sees it
        Decide later
    
    ✓ Approved          7f3a91c2 in bl-web-4f2a
      The sandbox loads it on its next poll, about 10 seconds. The agent can retry then.

    Choose Approve. The sandbox loads the new rule on its next poll, about ten seconds later. Deny asks for a reason, and the agent sees it. The list shows what the agent asked for. The sandbox also drafts a request from every blocked call, including calls the agent's own program makes in the background, such as a download check or a git fetch. Those are counted under the list, with their hosts. fenzi requests --all lists them too, so you can answer them.

    In a script, use the request's id instead. The id is the start of a longer one; any unique start works.

    fenzi approve 7f3a
    Door: applies while it runs

    Network rules change without a restart. The agent sees the approval, retries, and the call goes through. Requests never approve themselves, and the agent cannot approve its own request.

    To say no, use fenzi deny 7f3a --reason "use the vendored copy". The reason is kept with the request.

  5. Take the changes back

    Quit the agent. Fenzi saves the log, copies the project out of the sandbox into a staging folder, shows what changed, and asks what to do with it. Only then does it ask about the sandbox.

    Example output
    agent     exited
    log       saved, fenzi log
    changes   3 files staged, not applied
    
    ■ Claude Code exited  bl-web-4f2a
    
    ■ 3 files changed from the agent  not applied yet
    
      edited  src/api/client.ts    +14 −3
      new     src/api/schema.json  +40
      deleted src/api/legacy.ts    −22
                                   +54 −25 in total
    
    ? What should happen to these changes?
      ❯ Show the changes
        Apply them to ~/code/web
        Keep them for later

    Show the changes prints each file's changed lines with line numbers, in a pager when they are longer than the screen. Then the question comes back with Apply first. Keep them for later leaves your folder as it was. The changes stay staged on this machine, and you can come back to them from the project folder:

    fenzi diff fenzi apply

    Nothing in your repo changes until you apply. Deleted files are deleted only then. If a file also changed on your side while the agent worked, apply refuses and lists it, so your own edits are never overwritten.

    The sandbox keeps running, with the agent's session, its files, and this run's approvals. fenzi connect resumes the agent where it stopped, with its conversation, and fenzi stop ends the sandbox and brings any newer changes back. fenzi run in the same folder offers to resume it too. To delete the sandbox when the agent exits, run with --rm. Deleting does not touch the changes, whether you applied them or kept them for later.

Set up by hand

Setup runs the same checks as these commands. Use them in scripts, or to look at one part again.

fenzi doctor fenzi key set claude fenzi agents build claude

doctor checks the machine. Each line gets a ✓, or a ! with what to change. It fails if the gateway approves requests automatically; approvals are the point. fenzi doctor --fix turns that setting off. key set asks for the key without showing it. If ANTHROPIC_API_KEY is already exported, the first fenzi run stores it.

Next

  • How it works: what is inside the sandbox, and what a laptop can and cannot enforce.
  • Default access: what the agent can reach before you change anything.
  • Policy file: add hosts for a project with fenzi.yaml.
  • Workspace and changes: what is copied in, and how changes come back.
  • Agents: Claude Code, Codex, OpenCode, Grok, and the key each one needs.
  • Security model: what each layer stops, for whoever reviews this.
  • Team plan: one policy for every machine, one decision log, approvals from the console.