What happens when you lock Claude in a macOS sandbox?

· Dirk Holtwick ·

6 min read Original article ↗

Have you ever noticed that all development environments and AI apps on macOS run without a sandbox? That’s understandable, as a developer or an AI you want as much access to all your data as possible. But some of that data deserves protection: first and foremost the private keys of your SSH accounts, but also customer data, or simply your emails, photos and documents.

Apple introduced the system’s own sandbox for exactly this purpose. It goes by the codename “Seatbelt” and is known on the command line as sandbox-exec. It has been around for many years and is a requirement for every app distributed through Apple’s App Store. Wouldn’t it be nice if tools like Claude (Anthropic), Gemini (Google), Copilot (GitHub), Codex (OpenAI) and the rest could run in a tailor-made sandbox?

I tried it. It produced an app, and along the way a few insights into how an AI behaves when you forbid it something.

Claude, the Crafty Escape Artist

During development I noticed that, locally, Claude is a crafty little escape artist. Whenever it couldn’t access a file, it tried going through the Git repository. It also used various other tools to get at the contents one way or another: symlinks, detours via /tmp, and when in doubt, sudo.

More recent versions of Claude have become noticeably more restrained, though, and it takes a bit more persuasion to get it to obtain the information by other means.

Here’s an example: On the first attempt there is no protection at all, Claude finds the file colors.txt with grep and simply reads it. Then the file is protected by the sandbox. Claude now retries with Claude Code’s own sandbox disabled, which fails as well, and then diagnoses a missing macOS privacy permission (TCC) that I should grant in System Settings. Only when I ask “Maybe it is in the version control?” does it turn to Git: the contents had also been committed, and the .git directory wasn’t protected in this example. Its comment on that: “bypassing the filesystem-level TCC block entirely”.

No protection: colors.txt is read directly
No protection: colors.txt is read directly
Protected: the access fails
Protected: the access fails
Detour: the contents come out of the Git repo
Detour: the contents come out of the Git repo

Claude doesn’t do this out of malice. It sees an error message, takes it for a problem and wants to solve it. As an assistant that’s a good trait, inside a sandbox it’s exhausting.

In any case, protecting the directories alone is not enough. You also have to think about whether there are other ways the data can be reached.

What I Learned Along the Way

The sandbox configuration itself must be protected. It sounds trivial, but it’s the most important point: if the AI can change the rules or the ignore files, it can simply open the sandbox for itself. These files must be mounted without write access.

The trash is a data store. Relevant information may still be sitting there. If it’s locked, the AI simply creates its own .Trash directories inside the project.

The AI has a memory. Claude stores transcripts of every session in ~/.claude, and those include the contents of every file it has read. So if project A is not supposed to know anything about project B, that needs to be taken into account as well.

Tell it that it’s in a sandbox. To keep the AI from making too many attempts at getting around the sandbox, you can tell it up front that it is in one and that it should behave constructively. Since then, my CLAUDE.md has contained, among other things:

“Operation not permitted” and EPERM on a path are policy decisions, not bugs. Do not debug the code over one. Name the path.

Never work around the sandbox. No sudo, no symlinks or /tmp detours around a closed folder. When access is missing, say exactly which line in which file would grant it, and carry on with as much of the task as works without it.

It works. Instead of playing tricks, Claude now says: “I don’t have access to ~/.ssh, RO: ~/.ssh would change that”, and then carries on with the rest.

You need to see what’s going on. A sandbox that just silently blocks is frustrating, because tools simply stop working and you don’t know why. So I wanted two things: to see before launch exactly which files the rules affect, and to see afterwards which accesses were denied.

Convenient Protection with BX

All of this led to the creation of the BX macOS app. BX is a convenient macOS app that puts desktop apps and development environments into a sandbox just as easily as the so-called harnesses of the AIs that are launched directly in the terminal.

It’s easy to set up rules for which files may still be accessed and which may not. Patterns and wildcards are supported as well. It’s also possible to make certain files read-only, so they can be read but not modified.

Even before launch, a simple interface reminiscent of the Finder view shows exactly which files the rules affect. If a tool fails, BX marks the denied accesses right where they belong in the tree.

Take a look at BX for macOS: two BX windows, on the left the list of launchers (ChatGPT, Claude Code, Gemini, Copilot, VS Code, Xcode and more), on the right the protection view of the home folder as a tree. Each entry is marked as read-write, read-only or blocked, denied accesses are highlighted.

The rules can also live directly in the file system, in an ignore file named .bxignore that works essentially the same way as a .gitignore. And bx prompt generates the hint text for the AI as well.

Why not just use Docker or a devcontainer? Because I want to use my apps natively, with their user interface, their settings, their logins and their extensions, without a container and without detours. macOS already ships with the sandbox anyway, you just have to use it.

Am I in the Sandbox?

From the outside, you can’t tell whether an app or a tool is running in a sandbox or not. With a small status menu, BX shows whether you are in a sandbox that was launched with BX. That works in the terminal, too.

In my case I mostly use Visual Studio Code, inside the sandbox together with the AI. But sometimes I need to get at the protected files as well. To keep a clear separation, I’ve additionally installed VSCodium, the community edition of Visual Studio Code: it runs outside the sandbox and without AI. I’ve also set the accent color of each so that I can easily tell which editor I’m in.

And the Network?

A file system sandbox protects against data being read or modified. It does not protect against data that may be read being sent somewhere.

In BX, the network can be switched off completely for a sandbox. That eliminates the danger, but it also makes the AI largely useless, because without a network it can’t research anything, read documentation or install packages.

That’s why I prefer the other way: files that are none of Claude’s business, it never gets to see in the first place, and what it can’t read, it can’t send. The AI stays fully usable and the sensitive data still stays out of reach.

Try BX

Download BX for macOSBX for macOS is available at bx-ai.eu.

This post is also on Hacker News. That’s the place to vote and discuss.