# Grok Build Permissions & Control Model, Explained

How Grok Build's permission modes, TOML rules, sandbox profiles, and hook system actually work — what fires in always-approve, why dontAsk is not the escape hatch, and why every hook failure is a silent allow.

# The Grok Build control model, explained

Grok Build is xAI's terminal coding agent — beta in May 2026, 1.0 on August 7, and [open-sourced in mid-July](https://github.com/xai-org/grok-build) as a ~1M-line Rust workspace (Apache 2.0, one-way mirror of the internal monorepo). That open-sourcing matters for this page: the control model below isn't inferred from marketing copy, it's read out of [the official docs](https://docs.x.ai/build/features/hooks) and the shipped hook-runner source. This page is the reference: what Grok Build ships natively for controlling tool execution, exactly how each mechanism behaves, and where the model ends.

*This page covers Grok Build's own controls. For governing Grok Build with ACP, see [the install guide](/integrations/grok-build); for the cross-harness picture, [the controls comparison](/controls).*

## Permission modes: five postures, two commonly confused

`[ui] permission_mode` (or the matching flags) selects the posture:

| Mode | What it does |
|---|---|
| `default` | Side-effectful calls prompt a human. |
| `auto` | A built-in classifier auto-approves calls it judges safe; dangerous ones may still prompt. |
| `plan` | Everything holds behind plan review — the plan gate is never skipped. |
| `acceptEdits` | File edits auto-approve; shell still prompts. |
| **always-approve** | `--always-approve` / `--yolo` / Ctrl+O: every permission check answers yes. |
| `dontAsk` | Headless: anything without an explicit allow rule is **silently denied**. |

The two worth separating, because early coverage (ours included) conflated them: **always-approve is the escape hatch; `dontAsk` is the opposite of one.** `dontAsk` exists for headless scripting and fails closed — no allow rule, no execution, no prompt. That's the strictest default posture in the whole [comparison table](/controls), and it's hiding under a name that sounds permissive. If you run Grok Build in CI, `dontAsk` plus a tight allow list is a genuinely defensible setup — with the caveat that the denials are *silent*, which brings us to the audit section.

## Rules: TOML, deny-wins

Rules live in `config.toml` (user-global) or arrive per-invocation:

```toml
[permission]
rules = [
  { action = "allow", tool = "bash", pattern = "git status*" },
  { action = "deny",  tool = "bash", pattern = "git push*" },
]
```

`--allow` / `--deny` flags take the same tool/pattern form. Precedence is clean: **deny always wins**; an explicit allow auto-approves even calls the classifier considers dangerous; a *remembered* "always allow" (from the prompt's remember option) still re-prompts on dangerous patterns like `rm` and `git push`. Tool filters cover the built-ins — Bash, Edit, Read, Grep, MCPTool, WebFetch, WebSearch.

Two things the rules are not: they are not Claude Code's `permissions.allow` syntax (despite the settings compatibility below — see the next section), and they are not project-settable in a way a repository can smuggle in. Deny rules and PreToolUse hooks apply **in every mode, including always-approve** — the single most control-friendly fact about this harness.

## The hook system: broad surface, one blocking event, fail-open core

Grok Build's [hook system](https://docs.x.ai/build/features/hooks) is the most Claude-compatible we've verified after Muse Code's, and the compatibility is deliberate — but partial in ways that matter.

**Where hooks load from.** `~/.grok/hooks/*.json` (always trusted), project `.grok/hooks/*.json` (behind folder trust, same gate as project MCP servers), TOML blocks in `config.toml` — and, on by default, **hooks found in your real `~/.claude/settings.json`, `settings.local.json`, and `~/.cursor/hooks.json`**. Matchers alias Claude tool names (`Bash` matches `run_terminal_command`), and `CLAUDE_PROJECT_DIR` is injected as a compat alias. If you have Claude Code hooks installed today, Grok Build is already running them.

**The event surface** is wide — SessionStart/End, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, PermissionDenied, Stop/SubagentStop and friends, compaction events — but only **PreToolUse can block a tool call**. UserPromptSubmit is explicitly observe-only (exit code and stdout ignored — unlike Claude Code). Stop hooks gate with Claude's block/approve vocabulary and a 600-second budget.

**The output contract is where Claude compatibility ends.** The hook runner parses a top-level `decision` field: `"deny"` (plus `reason`) blocks, `"allow"` or absent allows, and any other value is an unknown-decision error. It does **not** parse Claude's `hookSpecificOutput.permissionDecision` — xAI's own example emits that key "for forward-compatibility" only — and Claude's legacy `"approve"`/`"block"` values are unknown-decision errors. There is no `ask`. Exit code 2 is an alternate deny channel. The practical consequence: **a Claude Code hook picked up through the compat path fires, runs, and its verdict is ignored** — it parses as decision-absent, which is an allow. The hook looks installed; the control isn't there.

**And the failure posture is fail-open, everywhere.** Default timeout 5 seconds; a timed-out, crashed, or malformed hook logs to the scrollback and the call proceeds. Only an explicit deny blocks. That's a defensible choice for a harness (a broken chime shouldn't brick your session), but it means a hook-based *control* layer inherits "proceed" as its failure mode unless it brings its own posture. Compare Muse Code, whose staged review holds what it can't parse — the two harnesses made opposite calls, and Grok's is the one you have to design around.

One genuinely distinctive capability rides the same contract: `hookSpecificOutput.updatedInput` lets a PreToolUse hook **rewrite the tool input** before execution (a schema-invalid rewrite blocks instead). And `type: "http"` hooks POST the full event envelope to a URL — a control plane can stand on the call path with no local shim at all.

## Settings compatibility: hooks yes, permissions no

The `[compat.claude]` cells cover skills, rules, agents, MCP servers, and hooks. There is **no permissions cell**: `permissions.allow`/`permissions.deny` in `.claude/settings.json` are not read. If your control posture lives in Claude settings permission rules, none of it transfers — only the hooks do, and (per above) their decisions don't parse. Enterprise MDM `managed-settings.json` is honored for a subset, but Claude's `disableBypassPermissionsMode` is deliberately not applied to Grok's always-approve; locking that down needs Grok's own root-owned `[ui] disable_bypass_permissions_mode = true`.

## Sandbox

Five profiles — `off` (default), `workspace`, `devbox`, `read-only`, `strict` — enforced by Landlock (Linux) and Seatbelt (macOS), with custom `sandbox.toml` profiles and per-path `deny` globs. Two documented caveats: the sandbox is **off by default**, and child-process network blocking is **Linux-only** — a documented no-op on macOS. `--sandbox read-only` composes well with `dontAsk` for a genuinely locked-down headless run.

## Audit

A session log exists (`~/.grok/sessions` for headless runs), and the source records `PermissionDecision` events internally — but the on-disk format is undocumented and there's no query surface. `dontAsk`'s silent denials and `auto`'s classifier approvals both land in the same undocumented store. As with [every classifier this season](/blog/claude-code-auto-mode-wrote-a-policy), the decision loop got a model; the decision *record* didn't get an interface.

Context worth knowing when weighing the audit story: the July session-trace exfiltration incident (multi-gigabyte trace uploads, including unread files, followed by a retention-policy change and the open-sourcing) is precisely the class of event an independent, queryable decision ledger exists to catch.

## Escape hatches and the empty chair

The hatch is `--always-approve` / `--yolo` (`permissionMode: "bypassPermissions"` in hook payloads). Even there, deny rules and PreToolUse hooks still fire — so a deny-capable hook holds through yolo, which is more than most harnesses give you. The [empty-chair question](/blog/interactive-vs-autonomous-the-empty-chair-test) — what happens to a "should I?" when nobody's answering — resolves differently per mode: `default`/`plan` put a human in the chair; `auto` seats a classifier; always-approve answers yes on your behalf; `dontAsk` answers no, silently. Grok Build is the rare harness where the honest answer to the empty chair is "configurable in both directions" — the failure modes are the silent ones: hook failures allow quietly, and `dontAsk` denies quietly. In both cases something decided, and nothing legible recorded why.

## Frequently asked questions


