The sandbox answers a question fundamentally different from the one the run mode answers. The mode decides whether you have to be asked before an action. The sandbox decides what a process can physically read, where it can write and what it can connect to. Those are two independent layers: a command can be allowed without questions and at the same time strictly limited in what it has access to. Confusing them means expecting from one mechanism what the other provides, and discovering the gap at the worst possible moment.
The configuration lives in two files, a user one and a project one, and the project file has higher priority. Above them act administrative and built-in restrictions that a lower layer cannot loosen. That asymmetry is deliberate: the organization sets a mandatory minimum, and the project refines it for its own specifics without being able to open what is closed from above. The practical consequence is that an unexplained refusal is investigated from the bottom up: first the project file, then the user one, then whatever the administrator set and nobody edits at all.
It helps to see a complete configuration once. Below are the access mode for the workspace, additional read paths, a ban on writing to the temporary directory and a network policy with an explicit list of allowed addresses. It reads as a description of boundaries: what may be touched, where writing is allowed and who may be talked to. Every line here is a decision rather than configuration decoration.
The fields are worth understanding by meaning rather than by name. The type sets the base mode: write access to the workspace, read-only, or no isolation. Additional paths widen access precisely - for example to a neighboring package of schemas needed for the build. A ban on writing to the temporary directory closes a popular workaround. And the network policy works on the principle of deny by default with explicit exceptions - the only form that can be explained in review.
| Field | What it sets |
|---|---|
| type | The base mode: workspace write, read-only or no isolation |
| additionalReadwritePaths | Extra paths the agent can read and write; apply only with the read-write type |
| additionalReadonlyPaths | Extra paths available for reading |
|---|
| disableTmpWrite | A ban on writing to the temporary directory |
|---|
| enableSharedBuildCache | Permission for a shared build cache |
|---|
| networkPolicy | The default behavior plus allow and deny lists |
|---|
The difference between read and write in those additional paths matters more than it seems, and an ordinary scenario shows it well. The build needs schemas from a neighboring directory: reading them is enough, so the path goes into the read-only list, and the agent will not be able to change them even if it decides that would make the task simpler. An error in that direction is cheap, but a missing path is not recognized at once: the sandbox's refusal reaches the application as an ordinary environment error - file not found, permission denied, connection not established. It looks like a broken project while in fact it is a working policy, and the first thing to check on such symptoms after a policy change is the policy itself.
The most important and most skipped part is platform differences. On macOS the isolation is built with one set of operating-system facilities, on Linux with another, and the level of support depends on the kernel and its settings. Where support is insufficient, some actions may require approval or extra configuration at the system level. Hence the rule: a policy is verified on every operating system by actual behavior rather than carried over by the name of the mode.
Verification must be a negative test, and that is worth making a habit. After changing the policy, ask for a knowingly forbidden safe action: read a test blocked file, reach a disallowed domain, write where writing should not be possible. The policy is proved only when the refusal is observed. A configuration file that looks strict but has not been tested is an assumption about security, not security.
The sandbox has a limit of applicability worth stating plainly. It protects the machine from the process, not the project from the agent. Inside a workspace with write access the agent can rewrite almost anything: delete a file that was needed, spoil a migration, commit something half-finished; only a narrow set of service files stays untouchable: the tool's and the editor's own configuration, the ignore file and repository service files such as the version control config and its hooks. The isolation will have worked flawlessly - that is exactly what it was allowed to do. So a different layer protects you from wrong edits: a narrow task, a look at the diff, tests and a version control system the change can be rolled back from. Expecting code quality from the sandbox is the same substitution as expecting file-system security from approvals.
The engineering conclusion is simple: a narrow boundary first, convenience second. Widening access costs less than narrowing it after an incident, and every widening should be pointed - a specific path, a specific domain. The temptation to switch isolation off entirely so it does not get in the way is understandable, but it turns a managed boundary into its absence.
The typical failures are predictable. Confusing the sandbox with approvals and expecting from one what the other gives. Carrying a policy from macOS to Linux by name without checking actual behavior. Leaving open write access to the temporary directory as a workaround. Taking a policy refusal for a broken project and fixing the wrong thing. And skipping the negative test, taking the text of the configuration for proof.
// .cursor/sandbox.json - the project file outranks the user one
{
"type": "workspace_readwrite",
"additionalReadonlyPaths": ["../shared-schemas"],
"additionalReadwritePaths": [],
"disableTmpWrite": false,
"networkPolicy": {
"default": "deny",
"allow": ["registry.npmjs.org", "*.github.com"],
"deny": []
}
}