grok-build-upstream-mirror/crates/codegen/xai-grok-pager/docs/user-guide/18-sandbox.md

283 lines
16 KiB
Markdown
Raw Normal View History

# Sandbox Mode
Sandbox mode restricts what the agent process and its spawned commands can access on your filesystem and network using OS-level kernel primitives (Landlock on Linux, Seatbelt on macOS). The kernel enforces these limits for the process lifetime.
Sandbox mode is off by default.
---
## Quick Start
```bash
# Run with workspace sandbox (read everywhere, write to CWD + temp dirs + ~/.grok/)
grok --sandbox workspace
# Read-only mode (read everywhere, write only to ~/.grok/ + temp dirs)
grok --sandbox read-only
# Most restrictive profile (read CWD + system paths, write CWD + temp dirs + ~/.grok/, no child network)
grok --sandbox strict
```
---
## Built-in Profiles
| Profile | FS Read | FS Write | Child Network | Use Case |
| --------------------- | ------------------ | ---------------------------------------------- | ------------- | --------------------------------- |
| `off` (default) | Unrestricted | Unrestricted | Unrestricted | No sandbox |
| `workspace` | Everywhere | CWD + `~/.grok/` + `/tmp` + `/var/tmp` | Allowed | Normal development |
| `devbox` | Everywhere | All top-level dirs except `/data` | Allowed | Disposable dev VMs |
| `read-only` | Everywhere | `~/.grok/` + `/tmp` + `/var/tmp` | Blocked¹ | Exploration, code review |
| `strict` | CWD + system paths | CWD + `~/.grok/` + `/tmp` + `/var/tmp` | Blocked¹ | Untrusted code |
¹ Child-network blocking is enforced on **Linux only** (via seccomp). On macOS it is a no-op — these profiles do not restrict child-process network there.
To block specific files (e.g. `.env` or credential paths) on top of a profile, define a [custom profile](#custom-profiles) with a `deny` list — it is kernel-enforced (read + write/rename) and supports glob patterns like `**/*.pem`.
### Profile Details
**workspace** -- The recommended profile for everyday development. The agent can read any file on the system (for understanding dependencies, system libraries, etc.) but can only write to the current working directory, `~/.grok/`, and temp directories (`/tmp`, `/var/tmp`, plus the macOS temp dirs). Network access is allowed for tools like `web_search` and MCP servers.
**devbox** -- A reserved built-in profile for disposable development VMs. The agent can read everywhere and write to every top-level directory except `/data` and the virtual filesystems (`/proc`, `/sys`, `/dev`), including the home directory. Network access is allowed. `--sandbox devbox` runs the built-in profile, which shadows any `[profiles.devbox]` you define in `sandbox.toml`.
**read-only** -- Use when you want the agent to analyze code without modifying your project files. The agent can read everything but can only write to `~/.grok/` (needed for session persistence) and temp directories. Child-process network access is blocked on Linux (no-op on macOS).
**strict** -- The most restrictive profile, for reviewing untrusted code. The agent can only read files within the current working directory and essential system paths. Writes are limited to CWD, `~/.grok/`, and temp directories. Child-process network access is blocked on Linux (no-op on macOS).
Synced from monorepo Synced from monorepo Changes: - Workspace server: report `/ready` as failed with dwell on hub connect failure - Refresh OIDC token for the Grok agent in the shell - ACP terminal output recorder - Cross-platform provider auth commands in the shell - Default `/resume` to Grok sessions with a hint for hidden external sessions - Resume sessions by title with `--resume` - Limit app-builder archive size - Data-driven tag labels for slash commands - Doctor fixes for tmux - Custom provider gateways and subprocess environment policy in the shell - `/tutorial` — opt-in onboarding tour of Grok Build - Soft and required CLI version checks in the shell - Privacy banner env overrides survive live settings updates - Add remote flag to override the image-edit model - Return profile fields from auth info even when the access token is expired - Add edit control on queued prompt rows - Keep fail-closed policy when clearing orphans with no team - Setting to disable the Ctrl+Space/F8 voice shortcut - Pass `--raw` to pw-record so Linux dictation works on older PipeWire - Validate git URLs when adding marketplace entries - Stop shipping stale tool-doc parameter and tool names - Re-point dashboard attach after `/fork` only when the parent was attached - Surface Grok Computer media-generation results as file-path chunks - Clear web background-task tray on kill and keep the task description - Show privacy upsell banner in agent view until acted on - Add tools-server client callback surface - Protect persistent global hook sources Source-Revision: 95d84f443eddcbed6cbfd6eed22e2eafe6b3939d
2026-07-23 17:12:33 +00:00
### Direct global hook write protection
Under `workspace`, `read-only`, and `strict` (and custom profiles that extend those bases), the Grok state directory remains writable for session/runtime files, but the kernel **write-denies** the Grok-owned direct disk paths used as user-global hook sources (they stay readable):
- `~/.grok/hooks/` (hook directory)
- `~/.grok/hooks-paths` (registry file; not loaded as hook JSON — only its absolute targets are)
- Absolute targets listed in `hooks-paths` (relative lines are ignored; missing targets refuse sandbox start)
On first launch under these profiles, Grok creates a real empty `hooks/` directory and empty `hooks-paths` file when they are missing (never symlinks or wrong types). Claude/Cursor global settings are **not** covered by this write-deny; discovery of those vendors remains separately gated by compatibility settings.
A symlinked `$GROK_HOME` or a `hooks-paths` entry with a symlink component is refused at sandbox start (prevents retargeting). Existing parent directories of protected paths are pinned so they cannot be renamed out from under the deny (siblings remain writable). On Linux, nested user namespaces are disabled inside bubblewrap so mount binds cannot be rearranged. Project hooks remain gated by folder trust. The `devbox` profile does not apply this protection (disposable VMs). Profiles that require it refuse to start if the kernel policy cannot be applied (including Linux without verified read-only mounts).
---
## Custom Profiles
Create custom sandbox profiles in `~/.grok/sandbox.toml` (global) or `.grok/sandbox.toml` (per-project):
```toml
[profiles.project]
# Start from a built-in profile, then add overrides
extends = "workspace"
restrict_network = true
# Paths the agent can read but NOT write/delete
read_only = ["/data"]
# Additional writable paths
read_write = ["/tmp/scratch"]
# Paths or globs to kernel-deny (read + write/rename, enforced; see notes below)
deny = ["/data/shared-secrets", "**/.env", "**/*.pem"]
```
Use the custom profile:
```bash
grok --sandbox project
```
A custom profile can't reuse a built-in name. `--sandbox devbox` always runs the built-in `devbox` profile, shadowing any `[profiles.devbox]` you define.
Synced from monorepo Changes: - Non-blocking coding-data sharing upsell banner - Consolidate remediation in Doctor - Auto mode defers fail-closed gate asks to the classifier - Coalesce marketplace list fetches - Allow removing a marketplace source by name - Contain hung git marketplace sources (timeouts, non-blocking refresh, unbrick modal) - Label failed workspace RPCs with error_kind - Drop redundant explicit tonic/prost deps from xai-grok-shell - Report real exit codes for completed background shells - Narrow the date-rollover reminder to date-bearing templates - Wire toolOverrides through the session and agent - Security: Bash(git:*) allowlist matches whole command chain by prefix - Split prompt-trigger telemetry and record classifier provenance - Raise connectors-manager timeout to 60s - Auto classifier honors recorded approvals for repeat actions - Apply doctor fixes in the TUI - Auto-mode classifier timeouts prompt instead of silently denying - Scope subagent completion drains to the owning session - Add the toolOverrides wire types - Set client_identifier=grok-agent-sdk - Accept both spellings of the workspace-teleport kill switch - Persist one-shot occurrence journal - Stop turns that poll the exact same tool call 16x in a row - Copy compaction checkpoint files when forking sessions - Auto-focus permission prompt from scrollback - Esc cancels the running turn in non-vim and minimal modes - List Ctrl+Z undo and redo in keyboard shortcuts - Out-of-process macOS mic capture - Show active auth mode on session-info - Install the npm binary under $GROK_HOME - Remove hover/click dead zones between dashboard items - Route startup warnings to doctor - Document [feedback.user] author identity config - Extend bang command timeout - Close combine-queued edit-hold race - Integrate relocation recovery - Expose privacy notice rollout flag - Break harness discovery ref cycle so connections can idle-evict - Shift/Alt+Enter inserts newline when editing a queued prompt - Gate project Claude permissions on folder trust - Echo response.create.event_id on response.created - Toast when session creation fails from disk full - Add shared test process lifecycle - Enable dynamic workflows by default - Add relocation transaction state machine - Add shared test sandbox - Surface auth failures on model-switch compact - Persist durable scheduler expiry - Confirm before removing extensions-modal items - Re-run compact and prompt after login when compact hit expired auth - Recap sends hosted tools under backend search
2026-07-22 19:18:53 +01:00
If the user and project files define the same custom profile differently, Grok uses the user profile and shows a startup warning. Run `/doctor` to see both file locations and how to resolve the conflict. Identical definitions do not produce a warning.
### Custom Profile Fields
| Field | Type | Description |
| ------------------ | -------- | ---------------------------------------------------- |
| `extends` | String | Base built-in profile to inherit from (`workspace`, `devbox`, `read-only`, `strict`). Defaults to `workspace` when omitted |
| `restrict_network` | Boolean | Block network access for child processes |
| `read_only` | String[] | Additional read-only paths |
| `read_write` | String[] | Additional read-write paths |
| `deny` | String[] | Paths or globs to kernel-deny (read + write/rename; see notes). An entry with `*`, `?`, or `[` is a glob |
> **Note on `deny`:** A non-empty `deny` list is **kernel-enforced**. Denied paths
> are **read-denied and write/rename-denied** via Seatbelt on macOS and a bwrap
> bind-over on Linux, so a denied path can neither be read (via `bash`, `grep`, or
> subagents) nor relocated out of the deny set and read elsewhere (the
> `mv secret x && cat x` bypass is closed). On **Linux**, read-deny requires
> `bubblewrap`: if it is missing (or any single deny path can't be bound), Grok
> refuses to start rather than run with denied paths exposed (`devbox`, which only
> write-denies `/data`, still falls back to Landlock). Writes to paths **not** in
> `deny` are controlled by what you grant in `read_write`.
> **Globs in `deny`:** An entry is a **glob** if it contains `*`, `?`, or `[`.
> Those characters **always** mean glob — to deny a literal file whose name
> contains them, name a parent directory instead. The supported, gitignore-style
> subset is:
>
> - `*` — any run of characters within one path segment (stops at `/`)
> - `?` — exactly one character within a segment
> - `**` — spans directories (as a whole path segment, e.g. `**/`, `a/**`); `**/`
> also matches zero directories, so `**/.env` matches `.env` and `sub/.env`
> - `[abc]` / `[a-z]` — character classes; a leading `!` **or** `^` negates
> (`[!a]` and `[^a]` both mean "not `a`")
>
> Brace alternation (`{a,b}`), backslash-escapes, and the unusual class forms
> `[]…]` (literal `]` first) and POSIX `[[:…:]]` are **not** supported, so the two
> platforms can never interpret a glob differently. A glob using an unsupported
> metacharacter, or one that is malformed, makes Grok **refuse to start** (fail
> closed) on **both** platforms — write `*.pem` and `*.key` as separate entries
> rather than `*.{pem,key}`.
>
> Relative globs are anchored at the workspace; absolute globs (e.g.
> `/home/**/.ssh`) at their literal prefix. Non-glob entries keep exact-path
> matching. Enforcement otherwise differs by platform:
>
> - **macOS is airtight:** each glob becomes a Seatbelt regex applied at runtime,
> so matching files are denied **even if created after Grok starts**.
> - **Linux is best-effort:** a mount namespace can't glob at runtime, so each
> glob is expanded to the files that **exist at launch** and those are bound
> over. Files created **later** that match a glob are **not** covered — name
> exact paths for anything that must be airtight on Linux. A glob that matches
> too many files, or whose tree is too deep/broad to walk, makes Grok **refuse
> to start** rather than under-enforce.
---
## How It Works
The sandbox is applied to the **entire grok process** at startup using kernel primitives -- not per-command wrapping. This means all tool operations are covered:
- `read_file`, `search_replace`, `list_dir` -- restricted by Landlock/Seatbelt in-process
- `bash` commands, `grep` (rg) -- child processes inherit FS restrictions automatically
- Network -- on Linux, child processes can be blocked via seccomp; on macOS this is a no-op
When a non-`off` sandbox profile is **requested** (CLI, `GROK_SANDBOX`, config, or a managed requirement):
- The agent runs **in-process**, not through the shared leader, so tool calls stay in this process when the profile is enforced. If leader mode would otherwise have been on, a one-line note at startup says so
- If a built-in profile fails to apply, Grok warns and continues without enforcement (see [Platform Support](#platform-support)), but still refuses the leader so tools are not delegated elsewhere
- `grok workspace start`, `restart`, and `resume` are unavailable; `pause`, `stop`, and `status` still work
Disable the profile at the source that selected it to use the refused commands.
The sandbox is **irreversible** once applied. The agent cannot relax restrictions at runtime.
---
## Resuming Sessions
The profile a session was started with is saved with the session and is **fixed
for the life of the session**. When you resume it (`grok --resume <id>`,
`grok --continue`, or `grok -r`), Grok restores that same profile automatically —
so a session started with `--sandbox workspace` won't silently come back under a
stricter default and break commands that previously worked.
Resuming will **not** change a session's sandbox:
- Omitting `--sandbox` on resume uses the session's saved profile.
- Passing `--sandbox <profile>` that **matches** the saved profile is allowed.
- Passing `--sandbox <profile>` that **differs** from the saved profile is
**refused with an error** — changing a resumed session's sandbox is a safety
footgun (it could widen access the session was meant to be confined to, or
break a session that relied on broader access). Start a new session to use a
different profile.
Profile resolution order for a **new** session:
1. An explicit `--sandbox <profile>` flag or `GROK_SANDBOX` environment variable
2. The `[sandbox] profile` in your config
3. `off` (no sandbox)
---
## Platform Support
| Platform | Mechanism | Minimum Version |
| -------- | --------- | ---------------------- |
| Linux | Landlock | Kernel 5.13 or later |
| macOS | Seatbelt | macOS (all versions) |
If the sandbox cannot be applied (e.g., unsupported kernel, missing entitlements), Grok logs a warning and continues without enforcement. The exception is an explicitly-requested **custom profile**: on **both macOS and Linux**, if it cannot be applied (unknown profile, malformed `sandbox.toml`, or — on Linux — `bubblewrap` unavailable for a non-empty `deny`), Grok refuses to start rather than run with its denied paths exposed.
---
## Network Restrictions
On Linux, profiles with `restrict_network` block network access in **child processes** (bash commands, scripts) via seccomp. On macOS, network blocking is a no-op. Built-in tools that make HTTP requests in-process (web search, LLM API calls) are never affected -- the agent needs network access to function.
In practice, on Linux this means:
- `web_search`, `web_fetch`, and the LLM API always have network access
- `bash` commands like `curl`, `wget`, and `npm install` are blocked when `restrict_network` is enabled
---
Synced from monorepo Synced from monorepo Changes: - Workspace server: report `/ready` as failed with dwell on hub connect failure - Refresh OIDC token for the Grok agent in the shell - ACP terminal output recorder - Cross-platform provider auth commands in the shell - Default `/resume` to Grok sessions with a hint for hidden external sessions - Resume sessions by title with `--resume` - Limit app-builder archive size - Data-driven tag labels for slash commands - Doctor fixes for tmux - Custom provider gateways and subprocess environment policy in the shell - `/tutorial` — opt-in onboarding tour of Grok Build - Soft and required CLI version checks in the shell - Privacy banner env overrides survive live settings updates - Add remote flag to override the image-edit model - Return profile fields from auth info even when the access token is expired - Add edit control on queued prompt rows - Keep fail-closed policy when clearing orphans with no team - Setting to disable the Ctrl+Space/F8 voice shortcut - Pass `--raw` to pw-record so Linux dictation works on older PipeWire - Validate git URLs when adding marketplace entries - Stop shipping stale tool-doc parameter and tool names - Re-point dashboard attach after `/fork` only when the parent was attached - Surface Grok Computer media-generation results as file-path chunks - Clear web background-task tray on kill and keep the task description - Show privacy upsell banner in agent view until acted on - Add tools-server client callback surface - Protect persistent global hook sources Source-Revision: 95d84f443eddcbed6cbfd6eed22e2eafe6b3939d
2026-07-23 17:12:33 +00:00
## Shell Environment Policy
The sandbox controls which files and network a subprocess can reach. The top-level `[shell_environment_policy]` table controls which environment variables it inherits, so a tool command the model runs cannot read a secret that happens to sit in your shell environment.
```toml
[shell_environment_policy]
inherit = "core" # all (default) | core | none
ignore_default_excludes = false # also drop *KEY* / *SECRET* / *TOKEN*
exclude = ["ACME_*", "CI_*"] # drop these names
include_only = ["PATH", "HOME"] # if set, keep only these names
set = { MY_FLAG = "1" } # force these values
```
Grok builds the child environment in order: it starts from `inherit` (`all` keeps everything, `core` keeps a small platform set such as `PATH` and `HOME`, `none` starts empty); drops the built-in secret patterns `*KEY*`, `*SECRET*`, and `*TOKEN*` unless `ignore_default_excludes = true`; drops any `exclude` matches; applies `set`; and, when `include_only` is non-empty, keeps only the matching names. Patterns are case-insensitive globs (`*`, `?`).
The default (`inherit = "all"`, `ignore_default_excludes = true`) leaves the environment untouched, so nothing changes until you configure a policy. On the non-persistent backend the policy also filters variables captured from your login shell, so an `.rc` file export cannot slip a secret past `exclude` or `include_only`. The persistent shell is one exception: it applies the policy to its base environment, but variables that an `.rc` file exports during login are replayed from a snapshot and are not re-filtered, so keep secrets out of shell startup files there. Enforcement covers the bash tool and terminals on macOS, Linux, and Windows.
---
## Event Logging
Sandbox events are logged to `~/.grok/sandbox-events.jsonl` for debugging. Events include:
- Profile applied (which profile, timestamp)
- Violations (attempted access to denied paths)
---
## When to Use Sandbox Mode
**Use `workspace` when:**
- Working on your own projects and you want basic write protection
- Running in shared environments where you want to limit the scope of changes
**Define a custom profile with a `deny` list when:**
- You need to block specific files (e.g. `.env` or credential paths) on top of a base profile
- You need kernel enforcement that covers `bash`, `grep`, and subagents — not just the `read_file` tool
**Use `read-only` when:**
- Reviewing code you do not trust
- Exploring a codebase without risk of accidental modification
- Running code analysis or audits
**Use `strict` when:**
- Analyzing untrusted or third-party code
- Running in security-sensitive environments
- You want maximum isolation
**Skip sandbox when:**
- The agent needs to install dependencies (`npm install`, `pip install`)
- The agent needs to modify files outside the working directory
- You are working in a trusted environment and want maximum flexibility
---
## Trade-offs
| Aspect | Without Sandbox | With Sandbox |
| ----------- | -------------------------- | ------------------------------- |
| Safety | Agent has full system access | Agent restricted to profile rules |
| Capability | Can do anything | Limited by profile |
| Performance | No overhead | Negligible overhead |
| Recovery | Must trust the agent | Kernel enforces boundaries |
The sandbox enforces limits at the OS level -- through Landlock or a mount namespace on Linux, and Seatbelt on macOS -- not a separate VM.