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
.grokis read-only, so this matters only for Grok run outside it. - Your API key is readable inside the sandbox. Launcher mode passes
XAI_API_KEYthrough, 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.pyand the gate beside it. Anyone who can write.grok/hookscan already run code after trust, which is why the launcher keeps.grokread-only. The shim ignores modules planted next to it and runs the gate withpython -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.