Permission policies
Set a permission policy for each agent tool. Run it silently, ask your phone, leave it to the agent's own prompt, or block it outright.
A permission policy decides what happens when your AI agent is about to run a tool, such as a shell command or a file edit. Pushary can run the call silently, ask your phone, leave it to the agent's own prompt, or block it. Each rule matches a tool pattern like Bash, Bash(git status) or Bash(npm test:*), with a * rule as the fallback.
Policies are enforced by the hooks that npx pushary@latest setup installs. A hook is a small program the agent runs before each tool call. Agents connected over MCP only (Windsurf, Claude Desktop, Cowork, ChatGPT, or Cursor without its hooks) can send notifications and ask questions, but their tool calls are not gated. Supported agents lists which agents enforce.
Change a policy in the dashboard
Pick a preset, or edit single rules
Permission presets set a whole agent at once: Cautious, Balanced, Autonomous or Custom. For one tool, open Advanced: individual tool rules and click Edit on a row.
Set where you decide and what happens on a timeout
Choose Where to decide (the mode), Timeout (0 to 300 seconds) and If you don't respond (Auto approve, Auto deny, Ask in terminal, or Wait for phone). Push window applies only to When away.
Add a rule for a specific tool or command
Type a pattern such as Bash, Bash(git status), Bash(npm test:*) or Edit(src/**), choose All repos or one repository, and click Add a rule. The * row is the catch-all and cannot be removed.
The running agent picks up the change on its next tool call.
The four approval modes
Every rule has a mode. The mode decides where the approval happens. New workspaces start on Always.
| Label | Mode id | Where you are asked |
|---|---|---|
| When away | push_first | On your phone when you are away from the agent's machine. In the agent, or on the Mac app, when you are there |
| Always | push_only | On your phone for every approval, even while you are at the machine |
| Alert only | notify_only | In the agent's own prompt. Your phone gets an alert with no approve or deny |
| In agent | terminal_only | In the agent's own prompt. No approval alert |
push_first
Shown as When away. Pushary asks your phone only when it cannot place you at the agent's machine. Two signals place you there:
- The Pushary Mac app on that machine reports you are there: the screen is unlocked and awake, you used the keyboard or mouse in the last minute, and no quiet scene is on.
- A CLI agent hook on that machine recorded a prompt in the last five minutes and tool activity in the last minute.
Editor plugins report no presence of their own. Your phone is asked when neither signal holds, or when the question carries no machine id, as with Cowork and Claude Desktop.
While you are at the Mac, the decision stays on the Mac however long it waits. Time alone never sends it to your phone. Leave the Mac and the decision moves to your phone. When a CLI or editor agent skips your phone because you are at the keyboard, the decision goes straight to the agent's own prompt.
Some callers have no prompt of their own. They ask your phone every time, as under Always, and wait up to timeoutSeconds:
- a Cursor file edit, or a session started from your phone, from
pushary1.9.13 - Antigravity CLI, from 1.9.14
- a Codex edit outside a scope you agreed, from 1.9.15
- Codex set to never ask for approval (full access), from 1.9.17
In Codex full access, a call nobody answers is refused with "No response within timeout", even when the rule approves on timeout. Older versions refuse a Cursor file edit and tell you to switch to Always.
Anywhere else, when your phone is asked, the hook waits up to pushFirstSeconds. Yes runs the call, No denies it, and no answer hands the decision back to the agent's own prompt.
push_only
Shown as Always, and where a new workspace starts. Pushary sends the push for every approval, even while you are at the machine. The agent waits until you answer or timeoutSeconds runs out, then the rule's timeout action applies. On a Mac with the Pushary app, the card also shows there and the first answer wins. Use it for destructive operations where the agent should wait for you.
terminal_only
Shown as In agent. Pushary sends no approval alert, and no approval card opens on its own on the Mac (Pushary for Mac 0.1.35 or later). The agent's own permission flow decides. A host with no permission prompt of its own refuses the action, as described under Alert only. Task updates are a separate setting and still arrive if you turned them on. Use it for tools you always want to approve at the keyboard.
notify_only
Shown as Alert only. Your phone gets a one-way alert titled Agent needs approval, with the tool description as the body. There is no approve or deny on the phone, and no approval card opens on its own on the Mac (Pushary for Mac 0.1.35 or later). You decide in the agent's own prompt, and the agent never waits on Pushary.
A host with no permission prompt of its own refuses the action. A Cursor file edit is denied, and from Cursor plugin 0.2.3 or @pushary/agent-hooks 0.95.7 the denial says why. The unattended Gemini bridge also refuses with an explanation from 0.95.7. Earlier versions pass the call to Gemini, which refuses it without saying why. Use Alert only when you want to know about approvals without a remote gate.
When Pushary is unreachable
If the hook cannot reach Pushary, it returns no opinion, so the agent's own permission flow decides as if the hook were not installed. An offline agent cannot reach your phone anyway, so you approve at the keyboard. A host with no prompt of its own, such as a Cursor file hook, refuses the action instead.
Policy fields
Prop
Type
The auto-approve shortcut
A rule with timeoutSeconds: 0 and timeoutAction: "approve" runs the call silently, in any mode. There is no push and no terminal prompt. This keeps read-only tools quiet, and it is what Teach-on-Tap writes when you tap always allow.
The mirror also holds. timeoutSeconds: 0 with timeoutAction: "deny" refuses the call at once, with no push and no prompt. Pair it with a pattern like Bash(rm -rf:*) to block a command family.
Read-only shell commands such as ls and git status run without asking under a bare Bash or * rule. A deny still covers them: under a Bash deny or a * deny they are denied too. The reverse also holds. A command Pushary rates destructive, such as rm -rf, never auto-approves through a bare Bash, PowerShell or Monitor rule or a * rule. In PowerShell that includes Remove-Item -Recurse under any of its aliases, and cmd /c rd /s. Only an exact or prefix rule you wrote yourself can approve it.
The kill switch overrides the auto-approve shortcut. A stopped session is denied before any policy is read, so even silently approved tools stop.
Pattern syntax
The tool field accepts four forms, modeled on Claude Code's own permission rules:
| Pattern | Matches | Example |
|---|---|---|
Tool | Every call to that tool | Bash |
Tool(exact arg) | Calls whose argument equals the text exactly | Bash(git status) |
Tool(prefix:*) | Calls whose argument starts with the prefix | Bash(npm test:*) |
Tool(glob) | Calls whose argument matches the glob. * stays inside one folder, ** crosses folders | Edit(src/**) |
The argument depends on the tool:
| Tools | Argument |
|---|---|
Bash, PowerShell, Monitor | command |
Read, Edit, Write, MultiEdit | file_path |
NotebookEdit | notebook_path |
LS | path |
Other tools have no argument to match. The dashboard refuses an argument pattern for them, so write the bare tool name. A path is matched as written and also relative to the agent's working folder, so Edit(src/**) covers files under src in the current project.
Shell commands are split at &&, ||, ; and |, and each part is checked:
- A rule that approves needs every part to match.
Bash(npm test:*)does not auto-approvenpm test && rm -rf dist. - A rule that denies or asks needs only one part to match.
Bash(git push:*)catchescd repo && git push.
Argument patterns need @pushary/agent-hooks 0.13.0 or later, and path globs need 0.55.0 or later. On older versions such a rule never matches, and the bare-tool and * rules still apply.
Which rule wins when several match
- Your own rules come before preset rules, and preset rules come before Pushary's built-in defaults. One exception: a preset's rule for a specific tool beats your own
*rule. - Within one layer, the most specific match wins: an exact argument, then a prefix or glob, then the bare tool name.
- On a tie, a rule for one session or one repository beats a workspace-wide rule, then the longer pattern wins.
* is a literal catch-all, not a glob. It applies only when no other rule in its layer matches.
Default policies
A new workspace starts with one rule of its own, the catch-all *: Always, a 60 second timeout, and ask in the terminal if nobody answers. The rule also carries a 20 second push window, which applies once you switch it to When away. Because your own * rule outranks the built-in rules, it governs every tool until you add a specific rule or pick a preset.
A site with no * rule of its own falls back to these built-in rules:
| Tool | Timeout | If no response | Mode | Push window |
|---|---|---|---|---|
Bash | 60s | Auto deny | push_first | 20s |
Write | 60s | Ask in terminal | push_first | 20s |
Edit | 45s | Ask in terminal | push_first | 20s |
Read | 0s | Auto approve | terminal_only | not used |
* | 60s | Ask in terminal | push_first | 20s |
Built-in Read uses the auto-approve shortcut, so file reads never interrupt you. Built-in Bash denies on timeout: if you do not approve a shell command, it does not run.
The hook caches your policy in your temp folder as pushary-policy-<hash>.json for 60 seconds. Every change you save in the dashboard bumps a policy version that the hook checks on each tool call, so a running agent uses the new rule on its next call without a restart.
Permission presets
A preset sets five capabilities for one agent, or for all agents: Read files, Edit and write files, Run shell commands, Reach the network, and Delete or force push.
| Preset | What the agent does on its own |
|---|---|
| Cautious | Reads. Asks before editing, running commands, or reaching the network |
| Balanced | Reads and edits. Asks before running commands or reaching the network |
| Autonomous | Works on its own. Asks only for network and destructive actions |
| Custom | Your own mix, set per capability |
Destructive actions always ask, whatever you choose. An agent-specific preset takes effect once that agent's CLI is up to date. The All agents preset applies everywhere at once. A rule you write under Advanced can override a preset.
Set the mode from the command line
pushary mode changes the mode for the whole site:
npx pushary@latest mode push_only --for 30m # Always, for the next 30 minutes
npx pushary@latest mode status # show the current override
npx pushary@latest mode clear # back to your per-tool rulesWith --for, the mode is a temporary override that wins over every rule's own mode until it expires. Durations are <n>m or <n>h, up to 24 hours. Without --for, the command changes the mode of your * rule and stays until you change it. updates-only is accepted as another name for notify_only.
pushary wait <seconds> sets the push window of your * rule (0 to 300). pushary wait clear puts it back to 20 seconds.
Changing the mode or the push window needs a key created in the dashboard. Keys from pushary login, phone pairing and the Mac app run approvals and notifications, not account settings, so the command tells you to change it in the app or at pushary.com/dashboard/agent/settings instead. pushary mode status works with any key.
Teach-on-Tap: "always allow this tool"
When a question is about a specific tool, the decision page shows Always allow <tool>. Tapping it approves the current call and saves a rule so you are not asked again.
The rule is as narrow as Pushary can make it:
- For a shell command, it covers the command's first words. Approving
npm test -- --watchwritesBash(npm test:*). - For
Edit,WriteandMultiEdit, it covers the whole tool.
Pushary does not save the rule for a command it rates destructive or high consequence, such as rm -rf or git push, or for one that hides what it runs, such as bash -c or eval. The current call is still approved. Under the hood the rule is timeoutSeconds: 0, timeoutAction: "approve", mode: "push_only", pushFirstSeconds: 0, which trips the auto-approve shortcut.
The rule applies to every agent on your site, in every project. Once you always allow Bash(npm test:*), every agent runs npm test without asking. Remove it any time in the policy editor.
An always-allow for all of Bash still asks when the command shows a real consequence: a force push, rm -r, git clean -f, throwing away uncommitted changes, publishing or deploying, a production deploy or migration, a SQL client that writes, gh pr merge, writing to a .env or secrets file, or running downloaded code such as curl ... | sh. Everything else runs, including commits, tests, npx, inline scripts, git push, git push --dry-run, git clean -n and a plain rm or git rm of a file. To let one of the held commands run without asking, write a specific rule for it, such as Bash(docker push:*).
An always-allow for all of PowerShell or all of Monitor asks about the same consequences. A Monitor command is judged exactly as the same Bash command would be. In PowerShell the held commands also include their PowerShell spellings: a recursive Get-ChildItem piped into Remove-Item, irm ... | iex or a downloaded script then run, Set-Content, Out-File, Copy-Item or Remove-Item on a .env file, Stop-Service and other service changes, Format-Volume, Clear-Disk and scheduled task changes. A plain Remove-Item file.txt, git push, Stop-Process, -WhatIf previews and every read such as Get-ChildItem still run.
Rule suggestions from Policy Autopilot
Pushary watches how you answer. After you answer the same tool pattern at least 5 times in 30 days, almost always the same way, it suggests an auto-approve or always-deny rule. Suggestions never apply themselves. Accept them under Policy Autopilot in the policy editor, which needs a paid plan, or from the terminal:
npx pushary@latest suggestions
npx pushary@latest suggestions accept <id>Like pushary mode, the suggestions command needs a key created in the dashboard.
Approval routing sends a tool's approvals to a role or a named teammate. It is on the same page and needs the Team plan or above.
Next steps
Stop a runaway agent
The kill switch denies every tool call in a session, even the ones your policy auto-approves.
Review every decision
The audit log records each question, approval and denial, and exports to CSV or JSON.