OpenClaw setup
Approve OpenClaw tool calls from your phone. The Pushary plugin for OpenClaw holds every exec call, and the tools you add, until you say yes, and blocks it on anything else.
Updated
Pushary sends OpenClaw's tool calls to your phone for approval. The Pushary CLI ships a plugin for OpenClaw that registers a before_tool_call hook. Before OpenClaw runs a gated tool call, Pushary asks you on your phone. Only a yes runs the call. Anything else blocks it.
It is built for an OpenClaw that runs for you alone, often always on, on a small VPS. It works on the Agent and Agent Pro plans. It needs OpenClaw 2026.11.3 or later and Node 24.16 or later.
Set it up
Run this on the machine that runs OpenClaw, as the user the gateway runs as:
npx pushary@latest setup --agents openclawOr run npx pushary@latest setup and pick OpenClaw when setup asks Which agents do you use? Setup is the only supported way to install the plugin.
Scan the QR code with your phone
Setup prints a QR code, a phone link and a fingerprint in the terminal. Scan the code with your phone's camera or the Pushary app, sign in or create an account, check that the code on your phone matches the one in the terminal, then tap Approve on Connect this computer? and allow notifications.
To skip this step, pass a key from your agent settings with --key pk_xxx.xxx. On a shared server, use --key-stdin instead, so the key does not show in the process list. If this machine already has a Pushary key, setup reuses it.
Setup installs the plugin
Setup saves your key in ~/.pushary/config.json, where the plugin reads it. It writes the plugin that ships inside the CLI to ~/.pushary/openclaw-plugin, a folder Pushary owns, then installs and enables it with OpenClaw's own commands:
openclaw plugins install ~/.pushary/openclaw-plugin --force --accept-capabilities
openclaw plugins enable pushary --accept-capabilitiesA running gateway picks the plugin up. A stopped one loads it at its next start.
Check that it loaded
openclaw plugins inspect pushary --runtime --jsontypedHooks should list before_tool_call. Then run npx pushary@latest doctor, which checks your key, your plan and the plugin. See Check that it works.
To update the plugin, run npx pushary@latest upgrade, or run setup again. Both reinstall it from the copy inside the newer CLI.
What setup changes on your machine
| Where | What changes |
|---|---|
~/.pushary/config.json | Your API key. The plugin reads it from here. |
~/.pushary/openclaw-plugin | The plugin files, copied from the CLI. |
| OpenClaw's plugins | pushary is installed and enabled, through openclaw plugins install and openclaw plugins enable. |
Run it on a VPS
OpenClaw often runs on a server that is always on, where nobody watches the terminal. Pushary is how a gated call reaches you there.
- Install OpenClaw on the server as its docs say, and check that
openclawruns in a new shell. - Connect over SSH as the same user the gateway runs as.
- Run
npx pushary@latest setup --agents openclaw. The QR code prints in your SSH session, so you sign in on your phone. The server needs no browser. - Check it with
openclaw plugins inspect pushary --runtime --jsonandnpx pushary@latest doctor.
If the gateway runs as a different user, or as a service without that user's home folder, it cannot read ~/.pushary/config.json. Put the key in that user's ~/.openclaw/.env instead, which OpenClaw loads into the gateway's environment:
PUSHARY_API_KEY=pk_xxx.xxxRestart the gateway after you add the key, so it reads the file.
No key means every gated call is blocked
A gated call that finds no key is blocked, and the reason OpenClaw shows says how to fix it. If you remove your key, remove the plugin too, with npx pushary@latest clean or openclaw plugins uninstall pushary.
What it gates
By default the plugin gates:
-
Every
execcall. -
The
processactions that send input to a running session:write,send-keys,submitandpaste. They are gated becauseprocess send-keyscan type a command into a session thatexecstarted in the background. -
The actions that could switch the gate off or change OpenClaw itself:
plugins:install,enable,disable,uninstall,reloadandreloadAndResume.gateway:update.run, which updates OpenClaw and restarts the gateway.openclaw: every action that sets or unsets config, installs or removes a plugin, creates an agent or team, sets the default model, configures a channel, search, skills or the gateway, or starts, stops or restarts the gateway.
These are the tools OpenClaw gives the agent to turn the plugin off. Without them, an agent could run
pluginswith{"action": "disable", "pluginId": "pushary"}and every later call would run without asking. The limits below list what the gate does not cover. -
A
sessions_spawnwith acwdoutside the agent's workspace or in itshooksfolder, or with aprojectId,projectGitUrlorworktree, so a child session cannot start in OpenClaw's state folder. The question names the folder. -
Every
automationsaction (the tool is also calledcron) exceptstatus,list,get,runsandnext_check. A scheduled job can start a command that never passes throughbefore_tool_call, so adding, changing, running or waking a job asks. -
Every
terminalaction exceptreadandlist.terminal inputtypes into a shared operator terminal.
The other process actions, list, poll, log, kill, clear and remove, run without asking. So do the read actions of the other three: plugins list, inspect and search; gateway config.get and config.schema.lookup; and the openclaw status, read and navigation actions such as status, config_get, plugin_list and open_setup. An action the plugin does not know is gated.
Changes to OpenClaw's own config and plugins are gated by default. Every write, edit or apply_patch into OpenClaw's own files asks: anything inside an OpenClaw state directory, ~/.config/openclaw (where Linux keeps gateway.env), ~/.pushary, a config file set by OPENCLAW_CONFIG_PATH, the .env in the gateway's working folder, and the launchd and systemd files OpenClaw installs for its gateway. That covers hooks, $include files, the state database and the plugins themselves. The one exception is the agent's own workspace when it lives inside the state directory, which is where OpenClaw puts it by default (~/.openclaw/workspace), so ordinary coding there does not ask. The workspace hooks folder always asks, wherever the workspace is, because OpenClaw can load hooks from it as code. The plugin resolves ~, relative paths against the agent's workspace, and symlinks before it decides, and asks when it cannot resolve a path. An apply_patch asks if any one of its files is protected. Writes anywhere else run without asking.
To gate more tools, add their OpenClaw tool ids to gatedTools in your OpenClaw config. The list adds to the defaults, so everything above stays gated while the plugin is on:
{
plugins: {
entries: {
pushary: {
enabled: true,
config: { gatedTools: ["message"] },
},
},
},
}Ids are matched the way OpenClaw names its tools: bash means exec, cron means automations and tool_call means dispatch_action. An id that is not a valid tool id is ignored, and the plugin logs it as an error when the gateway starts.
OpenClaw does not mark a tool call as a message send, a payment or a public post, so gate those by adding the tool that does it, such as message. Gate the tools that can do harm. Every gated call asks you, and a phone that buzzes for every call teaches you to approve without reading.
Your phone shows the command, for example Allow OpenClaw to run rm -rf ./dist?
What happens to a gated call
| Pushary says | The call |
|---|---|
| You answer yes | Runs |
| You deny | Blocked, and the agent is told not to retry |
| No answer before the wait ends | Blocked, and the question is withdrawn |
| OpenClaw cancels the run | Blocked, and the question is withdrawn |
| The agent was stopped from Pushary | Blocked |
| No phone, browser or Slack is connected | Blocked, with a link to connect one |
| Pushary cannot be reached, rejects the key or sends a reply the plugin cannot read | Blocked |
| Your delivery mode keeps approvals off the phone, or a When away push window passes with no answer | Withdrawn in Pushary and handed to OpenClaw's own approval prompt, which offers allow once or deny and blocks the call when nobody can answer there. On an OpenClaw older than 2026.11.3, the plugin blocks the call instead |
Only a yes runs the call. A missing answer never becomes an approval. A plugin error or timeout blocks the call too: OpenClaw fails closed when a before_tool_call hook throws or runs out of time.
Where approvals reach you
A gated call reaches you as a push notification on your phone, and on the other places you connected to Pushary, such as a browser or Slack. Answer on any of them. Phone calls are not available for OpenClaw yet.
How long it waits
The plugin asks OpenClaw for a five minute before_tool_call budget and stops waiting ten seconds before it ends, so it has time to withdraw the question. Your phone has about 4 minutes 50 seconds.
If you set plugins.entries.pushary.hooks.timeouts.before_tool_call or plugins.entries.pushary.hooks.timeoutMs, in milliseconds, the plugin reads that value and ends its wait before it instead:
{
plugins: {
entries: {
pushary: {
hooks: { timeouts: { before_tool_call: 120000 } },
},
},
},
}The wait is held inside one gateway process. If the gateway restarts during a wait, OpenClaw drops the call, and the question stays in Pushary until it expires after 10 minutes.
Check that it works
openclaw plugins inspect pushary --runtime --json
npx pushary@latest doctortypedHooks in the first command's output should list before_tool_call. Doctor says whether the plugin is installed, enabled and registers its gate, and whether it can read your key. It sends a real test push. Add --roundtrip to send a test question too.
Then ask OpenClaw to run a harmless command, such as ls. Your phone asks before it runs.
Where the plugin reads your key
PUSHARY_API_KEYin the gateway's environment, which OpenClaw also loads from~/.openclaw/.env.apiKeyin~/.pushary/config.json, or in the file named byPUSHARY_CONFIG_FILE.
Limits
- The gate covers the tools this page names and any you add. It does not see other tools: for example the file-transfer plugin's
file_writeis not gated unless you add it togatedTools. - ACP sessions set to approve everything, and CLI backends, run tools inside the external harness. Those calls never reach
before_tool_call, so the plugin cannot gate them. - This is an approval layer, not a sandbox. Pair it with OpenClaw's own sandboxing and
tools.fs.workspaceOnly. - The plugin talks only to
https://pushary.comandhttps://www.pushary.com, the hosts the CLI uses. Any otherPUSHARY_BASE_URLblocks every gated call, so a changed setting cannot point it at a server that always says yes. - The plugin is not told which folder a tool runs in. It resolves a relative path against the agent's workspace, any folder inside it, and the gateway's working folder, and a session spawned into any other folder asks first. A run started some other way in a folder inside OpenClaw's state is not covered.
- Config
$includefiles outside the state directory, including roots named byOPENCLAW_INCLUDE_ROOTS, are not protected. - Windows has not been tested.
- Pushary's tool rules from the dashboard are not evaluated on the machine. Every gated call asks.
- Another plugin that rewrites tool parameters can change what runs, including a plugin that runs before this one, because OpenClaw gives each plugin its own copy of the call and keeps the earlier plugin's rewrite. Pushary shows and approves the parameters it was given.
- The wait is held inside one gateway process. A gateway restart during a wait drops the call, and the question stays in Pushary until it expires.
Remove it
npx pushary@latest cleanclean removes Pushary from this machine, for every agent. For OpenClaw it uninstalls the plugin when Pushary installed it, and leaves a different plugin that uses the id pushary alone. Add --dry-run to see what it would change first.
To remove only the OpenClaw plugin, run openclaw plugins uninstall pushary. Without the plugin, OpenClaw's own exec approvals still apply, and nothing reaches your phone.
Troubleshooting
Setup says the OpenClaw CLI was not found
Setup looks for openclaw on your PATH, then in ~/.openclaw/bin and ~/.local/bin. Install OpenClaw, check that openclaw runs in a new shell as the same user, then run setup again.
Setup says another plugin already uses the id pushary
Setup does not replace a pushary plugin that Pushary did not install. Remove it with openclaw plugins uninstall pushary, then run npx pushary@latest setup --agents openclaw.
Setup says OpenClaw could not install the plugin
The plugin needs OpenClaw 2026.11.3 or later. Run openclaw update, then run setup again.
Setup says OpenClaw would not enable the plugin
Run openclaw plugins enable pushary --accept-capabilities.
typedHooks does not list before_tool_call
The plugin is installed but did not register its gate. Check that the gateway runs on Node 24.16 or later, then run npx pushary@latest setup --agents openclaw again.
Doctor says only this shell has PUSHARY_API_KEY
The gateway does not run in your shell, so it cannot see the variable. Put the key in ~/.openclaw/.env, or run setup so it saves the key in ~/.pushary/config.json.
A tool I added to gatedTools does not ask
Check the gateway's log from its last start. An id that is not a valid tool id is ignored and logged as an error. Use the tool id as OpenClaw names it, then restart the gateway.
Every exec call is blocked
Read the reason OpenClaw shows for the block. It says which of these happened:
- No key: run setup, or add
PUSHARY_API_KEYto~/.openclaw/.env, then restart the gateway. - No phone, browser or Slack connected: connect one in your agent settings.
- Pushary rejected the key: run setup again to get a new key, then restart the gateway.
- Pushary could not be reached: check the server's network. Troubleshooting shows how to test the connection.
- Pushary refused the request: answer or cancel your open questions, then try again.
Ask your own users (Partner plan)
The plugin is for an OpenClaw you run for yourself. If you build a product on OpenClaw and your users approve what it does, call decisions.ask() from @pushary/server in your own before_tool_call hook and pass each user's own id as externalId. That needs the Partner plan. Framework adapters has the hook.
Next steps
OpenClaw human in the loop
The plugin and the Partner path side by side.
Receiving notifications
Where approvals reach you, and how to connect a phone, a browser or Slack.
Human in the loop
How questions, answers and timeouts work across agents.