Skip to content

Grok Build Privilege Configuration Reference

This page covers how to control what xAI's Grok Build (the grok CLI) is allowed to do: which tools an agent gets, permission rules, folder trust, hooks and the OS sandbox. It also covers what a --framework grok team generated by agentteams sets for you.

Facts here are pinned to the Grok Build release in FORMAT_SPECS["grok"].pinned_version. The full evidence (docs, source and spike results) is in references/grok-agent-infrastructure-expert.md. Grok ships a version-matched user guide at ~/.grok/docs/user-guide/.


Configuration Files

File Scope What it may set
~/.grok/config.toml This user, all projects Everything ([ui], [compat.*], [subagents], [sandbox], [permission], …)
.grok/config.toml This project Only [mcp_servers], [plugins] and [permission]
~/.grok/sandbox.toml, .grok/sandbox.toml User / project Custom sandbox profiles
.claude/settings.json, .claude/settings.local.json (project and ~/.claude/) Claude compatibility Permission rules, defaultMode and hooks, all read by Grok too
/etc/grok/requirements.toml, ~/.grok/requirements.toml Enterprise pins Values nothing lower can override (e.g. disable_bypass_permissions_mode)

A project cannot change the user-level settings: the permission mode, the Claude compatibility switches, the active sandbox profile. Those stay with the operator.


Agent Tool Limits: What agentteams Emits

Every generated agent in .grok/agents/<slug>.md carries:

tools: Read, Grep, Glob          # Claude tool vocabulary; the exact allow-list
injectDefaultTools: false        # Grok's default (true) adds session tools on top of `tools:`
mcpInheritance: none             # Grok's default (all) passes every parent MCP server

Why all three: - An empty tools: list means "inherit every tool" in Grok, so agentteams refuses to render one. - With the two defaults left on, the agent receives more than its list. - Unknown tool names are skipped by Grok, which could leave an empty list. agentteams translates names fail-closed instead: - Task becomes Agent - TodoWrite is dropped (a narrowing) - a scoped entry (Bash(git diff:*), Agent(plan), any Name(...)) is never emitted, because Grok Build ignores the scope and widens the agent (a full shell plus an edit tool, in a live test): a scoped allow entry is dropped, and a scoped disallowedTools entry becomes the whole tool, each with a [GROK-SCOPE] notice - MCPTool is dropped too: grok does not accept it, and one such entry discards the whole list - Grok ignores disallowedTools, so every denied tool is left out of tools: instead (absence is the only limit it honours); the disallowedTools: line is kept as a record - any other unrecognised name, an untranslatable disallowedTools entry, or a deny list that removes every allowed built-in tool stops the render

Recognised tool names: Bash, Read, Edit (and Write), Grep (and Glob), WebFetch, WebSearch, Agent, emitted bare (never with a scope). On the pinned release Write, WebFetch and WebSearch grant nothing.


Permission Rules

Rules come from [permission] in every .grok/config.toml (user and project) and from Claude's settings.json files. They are merged into one set and evaluated by severity: any deny rejects, then any ask prompts, then allow.

# .grok/config.toml
[permission]
deny  = ["Edit(**/.grok/**)", "Bash(git push *)"]
ask   = ["Bash(rm *)"]
allow = ["Bash(pytest *)"]

Modes are set by the user ([ui] permission_mode) or per run: - interactive: ask (the default), auto, always-approve - headless (--permission-mode): dontAsk, acceptEdits, bypassPermissions

A project can set a deny-by-default posture through Claude compatibility ("defaultMode": "dontAsk" in .claude/settings.json).

What agentteams emits. With a confined or exclusive privilege_profile (host feature grok:sandbox), agentteams writes a managed block into .grok/config.toml. Its [permission] deny list covers every control-plane file: team switches, rosters, verify-key stores, team markers, .grok/config.toml, .grok/sandbox.toml, .grok/hooks/**, Claude's settings and hooks, and the operator key directory. - The rules are unrooted **/ globs, because Grok resolves relative patterns against the session directory and treats ~/ literally. - With grok:mcp, each wired MCP server's tools also get an ask rule. - If you already have a [permission] table, agentteams leaves it alone and tells you. - These rules stop Grok's edit tools, not shell writes, and they load only after folder trust. Treat them as declared. The OS sandbox is what stops shell writes.

User-only settings agentteams cannot emit but you may want in ~/.grok/config.toml: - [ui] permission_mode - [shell_environment_policy], which limits the environment variables passed to tool subprocesses


Folder Trust

Grok loads a project's instructions, hooks, MCP servers, LSP servers and skills only after the folder is trusted (grok --trust, or /hooks-trust in a session).

Before trusting a repository, review: - .grok/hooks/, .grok/workflows/, .grok/plugins/ and .grok/lsp* - .claude/settings.json hooks - .mcp.json - .grok/config.toml

The agentteams confinement launcher (sandbox/confine-run.sh) keeps the whole .grok folder read-only. On every run it also warns about [permission]/[mcp_servers]/[plugins] tables outside the agentteams block, and about hook, workflow, plugin or LSP files it did not emit.

Custom agents are the exception. .grok/agents/ and .claude/agents/ load even in an untrusted folder, and those agents run without the project instructions. Generated agent bodies therefore carry their own Invariant Core.


Hooks Fail Open

Grok runs Claude-schema hooks from .grok/hooks/*.json and from Claude's settings.json. PreToolUse can block a call, with exit code 2 or {"decision":"deny"}. Every hook failure is fail-open: a timeout, a crash or malformed output lets the call through (confirmed live in the 2026-10-14 spike). Treat a Grok hook as an advisory control, never as enforcement.

What agentteams emits. With a confined or exclusive profile, agentteams emits a hook gate made of three files: - .grok/hooks/agentteams-gate.json, which runs the shim - .grok/hooks/agentteams-gate-shim.py, the shim - .grok/hooks/constitutional-gate.py, the same gate Claude teams use

Grok sends hooks its own tool names (run_terminal_command, read_file, search_replace), so the shim translates them to Claude's before running the gate. Without it, the gate would not recognise a Grok shell command or file write. The gate asks before sudo, pipe-to-shell, force-push and similar commands, and denies writes that contain credentials. It only runs after folder trust, and it fails open, so it is classified as declared, not enforced.


OS Sandbox

Sandbox profiles: - built-in: off (the default), workspace, devbox, read-only, strict - custom: [profiles.<name>] in .grok/sandbox.toml, with extends, read_only, read_write, deny and restrict_network

With a confined or exclusive profile, agentteams emits this profile as .grok/sandbox.toml. It never overwrites a file of yours that lacks its agentteams-managed first line:

# .grok/sandbox.toml
[profiles.agentteams]
extends = "workspace"
deny = ["**/*.pem", "**/*.key", "**/.config/agentteams/keys/**"]   # workspace-only globs

Grok's own sandbox cannot protect your agent team. Tested on 2026-10-14: - A read_only entry did not stop writes inside the workspace, from the shell or from Grok's edit tool. - A deny entry blocks reads too, so it can't be used on files Grok must read. - A relative deny glob matches only inside the workspace, so the keys entry above does not protect ~/.config/agentteams/keys.

The real boundary is the agentteams launcher. The emitted .grok/confined-run.example.sh runs Grok inside sandbox/confine-run.sh, which: - keeps the .grok and .claude folders, the control plane, .agentteams and the prompt roots read-only - masks credential folders - protects the user-level config in a dedicated GROK_HOME, including the folder-trust store

Grok's own sandbox is switched off there (--sandbox off), because it can't run nested. The profile above is used only by GROK_CONFINE_MODE=profile, which first refuses if your ~/.grok/sandbox.toml defines a profile with the same name or a requirements.toml pins sandbox settings.

Things to know: - A project can define a profile but cannot switch it on. Start Grok with grok --sandbox agentteams (or GROK_SANDBOX=agentteams). - deny blocks reads as well as writes, for in-process tools and shell commands alike. Use it for secrets, never for agent definitions, instruction files or settings.json, which Grok and its agents must read. - The built-in workspace profile does not protect .claude/agents/. A write there went through in the spike. - If your ~/.grok/sandbox.toml defines a profile with the same name as the project's, yours wins. - Blocking network access from child processes works on Linux only.


Known limits of the confined setup

  • The launcher's warning is best-effort. It reports Grok config tables and hook, workflow, plugin or LSP files outside the agentteams-managed markers. A planted file that copies those markers is not reported. Inside the launcher .grok is read-only, so this matters only for Grok run outside it.
  • Your API key is readable inside the sandbox. Launcher mode passes XAI_API_KEY through, so every agent command can read it, as with Codex's auth. Prefer a key with a low spending limit, used only for this project.
  • The hook gate runs code from the repository. After you trust the folder, Grok runs .grok/hooks/agentteams-gate-shim.py and the gate beside it. Anyone who can write .grok/hooks can already run code after trust, which is why the launcher keeps .grok read-only. The shim ignores modules planted next to it and runs the gate with python -I.
  • If the shim or the gate fails, the call goes ahead. Grok hooks fail open. The exception is an explicitly confined or exclusive profile, where the gate copy is emitted fail-closed and a shim failure denies instead.
  • Permission rules wait for trust. The emitted [permission] deny rules apply only once the folder is trusted. In an untrusted folder they are ignored (spike R4).

Interop and Absorb

A Grok team can be an interop source or target (--interop-from) and an absorb source; convert and bridge refuse it. - Grants: Grok does not enforce grants (declared), so projecting a narrowed agent into Grok needs --interop-allow-cooperative <slug>, recorded in the destination team's decision log. Without both, the import refuses: it fails closed. - Shared AGENTS.md: written only through Grok's co-ownership guard. A file another framework owns gets the Grok block only (with --merge or --overwrite); a hand-written file is never written. - Hand-written agents: a Grok agent without both exact-toolset keys exports as full capabilities, with a warning.

Isolating Automated Runs

Grok also imports your user-level Claude configuration: permission rules, hooks and skills from ~/.claude/. Setting GROK_HOME does not prevent this. For scripted, eval or red-team runs, switch it off per process:

GROK_CLAUDE_HOOKS_ENABLED=false GROK_CLAUDE_SKILLS_ENABLED=false \
GROK_CLAUDE_MCPS_ENABLED=false GROK_CLAUDE_RULES_ENABLED=false GROK_CLAUDE_AGENTS_ENABLED=false \
grok -p "…" --output-format json

GROK_CLAUDE_AGENTS_ENABLED covers Claude's CLAUDE*.md files under .claude/ and ~/.claude/ only. No switch turns off .claude/agents/, which Grok always loads. The emitted confined-run launcher sets all five.

Headless exit codes: - 0: success - 1: a configuration error or hitting the turn limit - 130 / 143: a signal

Headless delegation to a subagent needs an approval mode, or the call is cancelled. In Grok Build 1.0.52 a custom agent also cannot be spawned as a subagent at all ("Unknown subagent type"); run each agent as the session agent by its path (grok --agent .grok/agents/<slug>.md), and never retry a failed spawn without a type: an untyped spawn starts a general-purpose agent that has every tool.


Verification

grok inspect          # agents, instruction files, permissions, hooks, skills and compat switches Grok will use
grok inspect --json   # the same, machine-readable

With a Claude team and a Grok team in the same repository, grok inspect should list each agent once, from .grok/agents. agentteams prints a notice when a Grok agent's name: differs from its Claude twin's, because Grok would then load both.