#auto-approvals not working `exec-approvals.json`
1 messages · Page 1 of 1 (latest)
Two big things jump out from your exec-approvals.json:
- It’s not valid JSON right now (so it may not be loading at all).
In your paste there’s a broken entry aroundop item get(missing fields / braces) and then another{starts immediately after. If the gateway/node can’t parse the file, it’ll fall back to defaults and you’ll keep getting prompts.
Quick sanity check on the host that’s executing exec:
openclaw --version
python3 -m json.tool ~/.openclaw/exec-approvals.json >/dev/null && echo OK
If that JSON check fails, fix the file (or edit via UI / CLI), then restart the gateway.
- Your allowlist patterns won’t match, because allowlists match resolved binary paths, not command text.
From the docs: “Manual allowlist enforcement matches resolved binary paths only (no basename matches). Patterns should resolve to binary paths (basename-only entries are ignored).”
So entries like:
"op item create""op read"
"export OP_SERVICE_ACCOUNT_TOKEN""~/.openclaw/workspace/.secrets/1password.env"(not an executable)
…won’t ever allow anything. You need to allowlist the actual op executable path, e.g.:
which op
# then allowlist that path, e.g. /home/openclaw/.local/bin/op
Also: if the agent runs compound shell commands (e.g. curl ... && unzip ... && mv ...), every segment’s executable must be allowlisted (or it’ll still prompt). That’s why “I allowlisted curl but it still prompts” is common.
What I’d do next (fastest path)
- Confirm where exec is running (Gateway host vs Node host). Approvals are per execution host. If you’re executing on a node, editing the gateway’s approvals file won’t help.
- Validate + load-check:
openclaw approvals get --gateway
(This confirms what the gateway thinks the approvals policy is, instead of guessing based on the file.)
3. Add the real binary path(s) via CLI (less error-prone than hand-editing JSON):
openclaw approvals allowlist add --gateway --agent main "$(which op)"
About “approval pop-up sometimes doesn’t show in Control UI”
That usually means your operator client/UI isn’t connected when the gateway broadcasts the approval request. A reliable workaround is to forward approvals to chat and approve with /approve (docs show the approvals.exec config block):
https://docs.openclaw.ai/tools/exec-approvals (section “Approval forwarding to chat channels”)
Docs references:
- Exec approvals: https://docs.openclaw.ai/tools/exec-approvals
- Approvals CLI: https://docs.openclaw.ai/cli/approvals
- Exec tool allowlist behavior: https://docs.openclaw.ai/tools/exec
If you paste:
openclaw --version
openclaw approvals get --gateway- and (if you’re using a node)
openclaw approvals get --node <id>
…I can tell you exactly which host/file is being used and why the allowlist isn’t matching.
No — don’t include args in the allowlist pattern.
Exec approvals allowlists match the resolved executable path only (the “binary”), not the full command line. So you want an entry like:
/home/openclaw/.local/bin/op(whateverwhich opprints)
…and then op item create, op item get, op read, etc will all be allowed because they all resolve to the same op binary.
What to do
On the machine where the command is actually executing (gateway vs node):
which op
openclaw approvals allowlist add --gateway --agent main "$(which op)"
openclaw approvals get --gateway
(If you’re executing on a node, use --node <id|name|ip> instead of --gateway.)
Why you still got prompted a lot
- Entries like
"op item create"won’t ever match (not a path). - Entries like
"export OP_SERVICE_ACCOUNT_TOKEN"also won’t match (that’s a shell builtin, not a binary). - If you run a chained install command (
curl ... && unzip ... && mv ... && chmod ...), you must allowlist each executable used (/usr/bin/curl,/usr/bin/unzip,/usr/bin/mv,/usr/bin/chmod, etc). Otherwise it’s still an allowlist miss and it will prompt.
Docs: https://docs.openclaw.ai/tools/exec-approvals (see “Allowlist (per agent)”) and https://docs.openclaw.ai/cli/approvals
If you paste openclaw approvals get --gateway (and --node ... if applicable) I can tell you immediately whether you’re editing the right host + whether the gateway is actually loading your policy.
For those exact commands (source … && export … && op …), there isn’t a “perfect” allowlist that only allows that and nothing else, because:
sourceandexportare shell builtins (not binaries), so they can’t match a “resolved executable path”.- The presence of
&&means the exec layer treats it as shell text, and on allowlist mode that’s typically an allowlist miss unless the shell itself is allowlisted.
So you have two practical options:
Option A (recommended): change the command shape so only op runs
If you make the agent run only op … (no source, no export, no &&), then your allowlist can simply include the op binary path:
- Find the actual
oppath:
which op
# e.g. /home/openclaw/.local/bin/op
- Allowlist it:
openclaw approvals allowlist add --gateway --agent main "$(which op)"
- Then ensure
OP_SERVICE_ACCOUNT_TOKENis already in the environment (so the agent doesn’t needsource/export). On a VPS this usually means adding it to the gateway service environment (systemd / the DO image’s service config), so everyexec host=gatewaysees it.
Result: op item create/list/get/read … should stop prompting, because it’s always the same executable.
Option B (works, but is basically “no approvals for shell”): allowlist the shell too
If you insist on keeping source … && export … && op … as one chained command, you’ll typically need to allowlist the shell executable that runs it (often /usr/bin/bash or /bin/bash, sometimes /usr/bin/sh).
Example allowlist entries you’d add (patterns are for binaries only):
/usr/bin/bash(or/bin/bash//usr/bin/sh— whichever your approvals dialog shows as the resolved executable)/home/openclaw/.local/bin/op
CLI way:
openclaw approvals allowlist add --gateway --agent main /usr/bin/bash
openclaw approvals allowlist add --gateway --agent main "$(which op)"
Important caveat: once bash is allowlisted, the agent can run essentially any shell command without prompts (because it’s all “bash running some text”). If your goal is “no approvals ever”, that’s consistent — but it’s a big trust decision.
One more correction based on your JSON
Entries like these will never help:
op item create/op read(not a path)export OP_SERVICE_ACCOUNT_TOKEN(builtin)~/.openclaw/workspace/.secrets/1password.env(not an executable)
They’re ignored for allowlisting purposes.
If you paste the resolved executable path shown in the approval request (or the relevant gateway log line for the denied exec), I can tell you exactly whether you need to allowlist /usr/bin/bash vs /usr/bin/sh, and whether you’re editing the right host (gateway vs node).
Yep — avoiding allowlisting the whole shell is the right instinct.
1) Where to store OP_SERVICE_ACCOUNT_TOKEN (so you can stop doing source … && export …)
Best practice on a VPS: put it in the Gateway’s env, so the agent can run plain op … without any source/export/&& at all.
OpenClaw loads env vars from ~/.openclaw/.env (non-overriding) and the process environment. Docs: https://docs.openclaw.ai/help/environment
So on your DO box (as the user that runs the gateway — likely openclaw):
~/.openclaw/.env
OP_SERVICE_ACCOUNT_TOKEN=op_service_account_token_goes_here
Then restart the gateway so the daemon picks it up.
This is better than ~/.openclaw/workspace/.secrets/1password.env because it removes the need for shell builtins (source, export) entirely.
2) The allowlist you actually want (without allowlisting bash/sh)
Once the token is already in the gateway env, your commands become just:
op item create ...op item list ...op item get ...op read ...
…and then the allowlist can be only the op binary path (no args):
- Find the resolved path:
which op
- Allowlist it for agent
mainon the gateway host:
openclaw approvals allowlist add --gateway --agent main "$(which op)"
That’s it. Do not add patterns like "op item create" — those won’t match.
If you want “never prompt me for allowlisted stuff”, make sure your approvals policy for main is:
security: "allowlist"ask: "off"(or keepon-missif you do want prompts for anything that misses)
Docs: https://docs.openclaw.ai/tools/exec-approvals
3) Are you on the wrong track with “agent-managed vault”?
Conceptually you’re on the right track, with two important safety constraints:
- Treat that service account token as full access to whatever vault(s) you grant. So keep a dedicated vault for the agent, and don’t store anything there you wouldn’t be okay with the agent potentially leaking in a worst case (prompt injection/model mistake/etc).
- Auto-creating accounts on websites can violate site ToS / trigger anti-abuse systems. A safer workflow is: agent does the research + drafts signup steps, and only automates credential storage + logins for sites you explicitly approve.
If you paste the output of:
openclaw approvals get --gateway
which op
…I can sanity-check that you’re editing the right host and that the allowlist will match the path OpenClaw is resolving.
/opt/openclaw.env on the DigitalOcean 1‑click image is totally plausible — that file is typically wired into the systemd service as an EnvironmentFile=... (so the gateway daemon can read tokens even though it isn’t started from your interactive shell).
So: your setup is not necessarily wrong just because ~/.openclaw/.env doesn’t exist.
Why you still get approvals (even though op is allowlisted)
Your allowlist output already shows:
~/.local/bin/opis allowlisted âś…
But the command you’re running is not “run op” — it’s a shell script line:
source ... && export ... && op ...
In allowlist mode, that usually resolves to the shell binary (bash/sh) as the executable, not op, so it misses (and prompts) unless you allowlist the shell (which you don’t want — fair).
Fix (no shell allowlisting): make OP_SERVICE_ACCOUNT_TOKEN part of the gateway’s environment
Then the agent can run only op ... and it’ll match the op allowlist entry.
Because your droplet already uses /opt/openclaw.env, the simplest approach is:
- Add this line to
/opt/openclaw.env:
OP_SERVICE_ACCOUNT_TOKEN=...
- Restart the gateway service.
I don’t know the exact unit name on the DO image, so please run:
systemctl list-units | grep -i openclaw
systemctl cat <the-unit-name>
…and paste it. I’ll tell you exactly where it loads env from and which command to restart.
(Alternative that’s also supported by OpenClaw in general: ~/.openclaw/.env, docs here: https://docs.openclaw.ai/help/environment — but on your image /opt/openclaw.env is likely the “right” place.)
Then, change the agent’s exec usage to just:
op item create ...op item list ...op item get ...op read ...
(no source, no export, no &&)
ToS question
Yes: I meant the target websites’ Terms of Service often forbid automated signup / bot-driven account creation. (Not OpenClaw’s ToS.) Even if it’s “your agent”, to them it still looks like automation.
If you paste systemctl cat ... for the gateway unit, we can lock in the correct env file + restart command for your specific droplet.
-
“DO image” = DigitalOcean image (the 1‑click Marketplace droplet image you installed). Nothing more exotic than that.
-
Your
systemctl catoutput confirms exactly what we needed: the Gateway service loads env from:
EnvironmentFile=/opt/openclaw.env- runs as
User=openclaw
So if you want the agent to run plain op … (no source/export/&&) and have it “just work”, the cleanest approach on this droplet is:
A) Put the 1Password token into /opt/openclaw.env
Add:
OP_SERVICE_ACCOUNT_TOKEN=...
Then restart:
sudo systemctl restart openclaw.service
sudo systemctl status openclaw.service -n 50 --no-pager
Sanity check (without leaking the token):
sudo -u openclaw bash -lc 'test -n "$OP_SERVICE_ACCOUNT_TOKEN" && echo OP_TOKEN_PRESENT || echo OP_TOKEN_MISSING'
Docs background on env loading: https://docs.openclaw.ai/help/environment
B) Stop using shell builtins in the exec command
Right now you’re running:
source … && export … && op …
That forces the exec to be treated as “shell text”, which is why you keep getting approval prompts even though op is allowlisted.
Once the token is in the service env, have the agent run only:
op item create …op item list …op item get …op read …
(no source, no export, no &&, no pipes/redirections)
C) Allowlist only the op binary (what you already basically did)
Your table already shows ~/.local/bin/op is allowlisted ✅. To be extra explicit, I’d add the fully-resolved absolute path too:
sudo -u openclaw bash -lc 'which op'
# then allowlist that path (gateway host, agent main)
openclaw approvals allowlist add --gateway --agent main "/home/openclaw/.local/bin/op"
(Allowlist matches the executable path, not op item create etc.)
“Auto-creating accounts on websites can violate ToS” — whose ToS?
Yes: the target website’s Terms of Service often prohibit automated signups / bot-like behavior. Just something to be aware of.
If after moving the token into /opt/openclaw.env you still see prompts, paste one approval request showing the resolved executable path (it’ll say what binary it thinks it’s running). That will immediately tell us what’s still forcing “shell mode.”
That check is misleading:
sudo -u openclaw bash -lc 'echo $OP_SERVICE_ACCOUNT_TOKEN' runs a fresh interactive shell as the openclaw user, and it will not inherit the systemd service environment from openclaw.service.
What we need to check is whether the running gateway process got the variable from EnvironmentFile=/opt/openclaw.env.
1) Verify the gateway service process has the env var
Run:
pid="$(systemctl show -p MainPID --value openclaw.service)"
echo "MainPID=$pid"
sudo tr '\0' '\n' < "/proc/$pid/environ" | grep '^OP_SERVICE_ACCOUNT_TOKEN=' && echo PRESENT || echo MISSING
- If it prints
PRESENT, you’re good: the gateway has the token, and then you can/should run plainop ...(nosource/ noexport/ no&&). - If it prints
MISSING, systemd didn’t load it from/opt/openclaw.env.
2) If it’s missing: likely /opt/openclaw.env line format issue
In a systemd EnvironmentFile, the line must be exactly KEY=VALUE (no export).
Good examples:
OP_SERVICE_ACCOUNT_TOKEN=ops_xxx...
# or, if it contains special chars:
OP_SERVICE_ACCOUNT_TOKEN="ops_xxx..."
Bad examples:
export OP_SERVICE_ACCOUNT_TOKEN=...
OP_SERVICE_ACCOUNT_TOKEN = ...
Also check for systemd warnings about the env file:
sudo journalctl -u openclaw.service -b --no-pager | tail -n 80
(You don’t need to paste the token; just any warnings about EnvironmentFile=.)
After fixing /opt/openclaw.env, do:
sudo systemctl restart openclaw.service
…and re-run the /proc/$pid/environ check above.
3) Once the token is in the service env, your approvals problem goes away
Because you can stop running:
source … && export … && op …
…and instead run just:
op item create ... / op item list ... / etc
Since ~/.local/bin/op is already in your allowlist, those should no longer prompt.
If you paste the output of step (1) (PRESENT vs MISSING) + any warnings from journalctl, I’ll tell you exactly which case you’re in.
- Yep — good catch. Don’t paste that output anywhere (Discord/Telegram/etc). The command I gave prints the full
OP_SERVICE_ACCOUNT_TOKEN=...line if it’s present.
Use a “presence check” that doesn’t echo the value:
pid="$(systemctl show -p MainPID --value openclaw.service)"
sudo tr '\0' '\n' < "/proc/$pid/environ" \
| grep -q '^OP_SERVICE_ACCOUNT_TOKEN=' \
&& echo PRESENT || echo MISSING
That only prints PRESENT/MISSING and won’t leak the token.
(And at this point you already know it’s present, so you can stop checking.)
- Here’s a copy/paste prompt to send your agent on Telegram to re-test without
source/export/&&(so it should match theopallowlist and avoid approvals):
Prompt to your agent:
Re-check 1Password CLI integration using only direct
opcommands (nosource, noexport, no shell chaining).
- Run
op --versionto confirmopworks.- List vaults with
op vault listand confirm the vault named "Shared with Yug" exists (if not, ask me which vault to use).- Create a test item in that vault:
op item create --vault "Shared with Yug" --category password --title "Test Entry - OpenClaw" username="openclaw-test-user" password="openclaw-test-pass"
- Immediately list items and show me the matching entry (title only is fine):
op item list --vault "Shared with Yug"
- Fetch the created item by title and confirm it exists (do not print any sensitive fields; just show id + title):
op item get --vault "Shared with Yug" "Test Entry - OpenClaw" --format json | jq -r '.id, .title'If any command fails due to auth, report the exact error output.
Notes:
- Step 5 uses
jq. Ifjqisn’t installed/allowlisted, tell it to skip that part or use--format jsonwithout piping. (Pipes can trigger extra approval logic; simplest is no pipes.)
If you want an even safer version (no jq, no pipes at all), use this instead for step 5:
op item get --vault "Shared with Yug" "Test Entry - OpenClaw"
…and tell it “don’t print passwords / sensitive fields”.
When you run this test, if you still get an approval prompt, paste only the approval dialog’s “resolved executable path” (but not the token/command output).
That allowlist will “work”, but it’s way broader than you need for the 1Password goal and a couple entries are pretty concerning from a security standpoint:
What’s concerning (highest risk first)
/usr/bin/python3— this is basically “run arbitrary code”. If the agent can runpython3, it can do almost anything theopenclawuser can do (read files, make network calls, spawn processes, etc.). I would not keep this allowlisted unless you explicitly want that./usr/bin/openclaw— this lets the agent run the OpenClaw CLI itself. Depending on permissions/config, that can be a lot of power (changing config, reading status/logs, etc.). Usually not needed for normal agent tasks./usr/bin/cat— enables reading arbitrary files on the gateway host (cat /etc/...,cat ~/.ssh/..., etc.). Again: not needed for 1Password operations./usr/bin/curl— broad network egress. Useful for installing things, but after setup it’s typically not needed and expands the blast radius if something goes wrong.
What looks fine / intended
op— yes, this is the one you actually want for your use case. I’d make it the only entry long-term.
Also: I’d prefer making the op entry an absolute path (no ~), e.g. /home/openclaw/.local/bin/op, just to avoid any path-expansion weirdness.
Recommended “tight” allowlist for your stated goal
Keep only:
/home/openclaw/.local/bin/op(or whateverwhich opresolves to for the gateway service user)
…and remove the rest unless you have a specific ongoing need.
If you want to remove entries via CLI:
openclaw approvals allowlist remove --gateway --agent main /usr/bin/python3
openclaw approvals allowlist remove --gateway --agent main /usr/bin/openclaw
openclaw approvals allowlist remove --gateway --agent main /usr/bin/cat
openclaw approvals allowlist remove --gateway --agent main /usr/bin/curl
After that, the agent should still be able to op item create/list/get/read without approvals (since op stays allowlisted), and everything else will correctly prompt/deny.
If you paste openclaw approvals get --gateway (redact token fields), I can sanity-check that security/ask are set to what you intend too.
1) Approval dialogs sometimes don’t show up in Control UI
That almost always boils down to: the gateway broadcasted exec.approval.requested, but you didn’t have a connected operator client that actually surfaced it (tab disconnected, browser throttled the background tab, UI connected to the wrong gateway URL/token, etc.).
Two pragmatic fixes:
A) Make approvals reliable by forwarding them to Telegram (recommended on a VPS).
Then you’ll always see the prompt in chat even if the dashboard is asleep/disconnected, and you can approve with /approve.
Docs + config example are here (scroll to “Approval forwarding to chat channels”):
https://docs.openclaw.ai/tools/exec-approvals
Config shape (put in ~/.openclaw/openclaw.json):
{
approvals: {
exec: {
enabled: true,
mode: "both", // "session" | "targets" | "both"
agentFilter: ["main"],
targets: [
{ channel: "telegram", to: "tg:YOUR_TELEGRAM_ID" }
]
}
}
}
(Use your actual Telegram “to” value you already use for messages; if you’re unsure, paste your channels.telegram config block with secrets redacted.)
B) Verify the Control UI is actually connected/healthy when you expect prompts
On the VPS, run:
openclaw gateway status
openclaw logs --follow
Then trigger one approval and watch logs for exec.approval.requested and whether any operator clients are connected at that moment.
Also: update. You’re on 2026.2.12 and UI/reliability fixes land frequently; being a couple patch versions behind can absolutely show up as “sometimes UI misses things”. Updating docs: https://docs.openclaw.ai/install/updating
2) Agent responds too slowly (big delays)
The common causes on a DO 1‑click droplet are:
- Model/API latency or rate limiting (provider slow, 429s, retries)
- Gateway host resource pressure (small droplet → CPU pegged or swapping)
- Tool waits (exec approvals, long-running
opcalls, network calls)
- Gateway restarts / reconnect churn (causes gaps + missed UI events)
Fast triage to pinpoint which one it is:
A) Check gateway logs while you send one message from Telegram
openclaw logs --follow
Send a simple “ping” to the bot, and see if logs show:
- long model request durations
- retry/rate-limit messages
- tool calls taking ages
- reconnect loops
B) Check droplet health
uptime
free -h
If you see high load averages and low free RAM / swap usage climbing, that’s a smoking gun: the droplet is underpowered for fast agentic work.
C) Update from 2026.2.12
Even if it’s not the root cause, it removes a whole class of “weirdness”. Follow: https://docs.openclaw.ai/install/updating
If you paste a small snippet from openclaw logs covering one slow interaction (timestamps included, redact tokens), I can tell you whether it’s model latency vs host resources vs reconnect/approval delays.
That openclaw approvals get --gateway output is actually a really good sign:
- Gateway is reading
~/.openclaw/exec-approvals.json - Agent
mainhas exactly 1 allowlist entry (~/.local/bin/op) - So any time the agent runs
op …directly, it should not require an approval dialog.
1) “Sometimes I don’t see approval dialogs” — that can be expected now
There are two different cases:
Case A (good): no dialog because it didn’t need approval
If the command is allowlisted (e.g. op item list …), then no approval prompt should appear anywhere. That’s the whole point of allowlisting.
Case B (bad): dialog was required but you didn’t see it
That only happens when the command was an allowlist miss (or ask=always) and your operator client didn’t surface it (Control UI tab disconnected, browser background-throttled, you weren’t connected at that moment, etc.).
On VPS setups, the most reliable fix is to forward exec approvals to Telegram so you never miss them due to a flaky dashboard connection:
https://docs.openclaw.ai/tools/exec-approvals (section “Approval forwarding to chat channels”)
If you want, paste your channels.telegram config block (redact secrets), and I’ll tell you the exact approvals.exec.targets line for your Telegram “to” value.
2) The #1 thing that still triggers approvals: shell chaining
If your agent is still doing this:
source … && export … && op …
…then it’s no longer “run the op binary”, it’s “run a shell script line”, and approvals will keep happening unless you allowlist the shell (which you correctly don’t want).
Now that you’ve confirmed the token is in the service env, make sure the agent runs only:
op item create …op item list …op item get …op read …
No source, no export, no &&, no pipes.
One small improvement: change the allowlist entry from ~/.local/bin/op to the absolute path /home/openclaw/.local/bin/op (same thing, just less ambiguity).
3) “Agent responds too slowly” — most common causes
Two big ones in your situation:
A) It’s waiting on approvals/timeouts
If it runs something that triggers approval and you miss the dialog, it’ll sit there until it times out → feels like “the agent is slow”.
Forwarding approvals to Telegram fixes that class of delay.
B) VPS resource / model latency
Quick checks on the droplet:
uptime
free -h
And check logs during a slow interaction:
openclaw logs --follow
If you paste ~30–60 lines around a slow message (timestamps included, redact secrets), I can tell you if it’s model latency/retries vs tool waits vs approvals.
Also worth doing: you’re on 2026.2.12 — updating tends to improve Control UI + reliability over time:
https://docs.openclaw.ai/install/updating
That’s normal: the CLI/UI will often pretty-print paths using ~ (home-dir contraction) even if the file stores the expanded absolute path.
So:
~/.local/bin/op(display)/home/openclaw/.local/bin/op(on disk)
…are the same thing as long as the approvals are evaluated in the openclaw user context (which your systemd unit does: User=openclaw).
If you want to remove any ambiguity anyway
You can force it to be absolute by removing the ~ entry and re-adding the absolute one:
openclaw approvals allowlist remove --gateway --agent main "~/.local/bin/op"
openclaw approvals allowlist add --gateway --agent main "/home/openclaw/.local/bin/op"
openclaw approvals get --gateway
(If the remove command complains about not finding the exact string, just edit via the Control UI → Nodes → Exec approvals, or openclaw approvals set --gateway --file ....)
But functionally: you’re fine as-is. The bigger determinant of “will it prompt” is still whether the agent runs op ... directly vs doing source/export/&& shell text.
Don’t try to paste a live --follow stream. Grab a small, time-bounded slice around one slow interaction.
Here are 3 good ways (pick one):
Option A (easiest): reproduce once, then paste last ~200 lines
- In one terminal, run:
openclaw logs --follow
- Send a message to your agent that you know is “slow”.
- As soon as you see the agent finally respond (or you notice it stalled), stop the log stream with Ctrl+C.
- Then run:
openclaw logs | tail -n 200
Paste that output (redact secrets).
Option B (best signal): journalctl slice for the systemd service
Since you’re running openclaw.service via systemd, this is clean:
- Right before you send a “slow” message, note the time (UTC is fine).
- After it responds, run:
sudo journalctl -u openclaw.service --since "10 minutes ago" --no-pager | tail -n 300
Paste that.
(If 10 minutes is too much noise, change it to "2 minutes ago".)
Option C (keyword filter): only show model/tool/approval-ish lines
This is handy when logs are super chatty:
sudo journalctl -u openclaw.service --since "10 minutes ago" --no-pager \
| egrep -i "error|warn|rate|429|timeout|retry|model|llm|exec|approval|tool|telegram|latency|slow|queue" \
| tail -n 200
Redaction guidance (important)
Before pasting, remove anything that looks like:
- tokens / API keys (
sk-...,OP_SERVICE_ACCOUNT_TOKEN=...,OPENCLAW_GATEWAY_TOKEN=...) - long bearer strings
- your full gateway URL if you don’t want it public
Once you paste ~100–300 lines around the slow period, I can usually tell immediately if it’s:
- rate limiting/retries from the model provider
- CPU/RAM pressure on the droplet
- approval waits/timeouts
- Telegram delivery/backpressure
- reconnect loops
That log snippet is useful, but it’s not enough to diagnose “slow responses” yet — it only shows one concrete problem:
What your logs do show
At 04:38:27 the agent tried to read a daily memory file that doesn’t exist:
read failed: ENOENT ... '/home/openclaw/.openclaw/workspace/memory/2026-03-01.md'
If the agent keeps trying this repeatedly, it can absolutely add annoying latency (failing tools + retries + extra reasoning).
Quick safe fix (create the folder + today’s file)
Run as root on the droplet:
sudo -u openclaw mkdir -p /home/openclaw/.openclaw/workspace/memory
sudo -u openclaw touch /home/openclaw/.openclaw/workspace/memory/$(date -u +%F).md
Then try one slow interaction again and see if it improves.
How to capture the “slow response” logs properly
Your egrep ... | tail filtered out almost everything. Instead, grab a tight time window around one slow request.
- Reproduce the slowness once (send a message on Telegram).
- Immediately run this (adjust times around when you sent it):
sudo journalctl -u openclaw.service \
--since "2026-03-01 04:37:00" --until "2026-03-01 04:41:00" \
--no-pager
Paste that whole chunk (you can redact tokens/keys).
If you don’t want to deal with exact timestamps, do:
sudo journalctl -u openclaw.service --since "5 minutes ago" --no-pager | tail -n 400
Also grab 2 quick system health checks (often the real cause on small droplets)
uptime
free -h
Post those + the 4–5 minute journal slice and I can tell you if the delay is:
- model/provider latency / retries / rate limits
- the gateway being CPU/RAM starved
- approval waits/timeouts
- something looping in tools (like the missing memory file)
You shouldn’t have to create a “daily memory file” manually — that log is just telling us the agent tried to read a journal-style file and it wasn’t there, so the read tool returned ENOENT. Two important points:
1) Why is it looking for a memory file at all?
That behavior doesn’t come from “core OpenClaw memory” (the semantic memory stuff). It usually comes from the agent’s own prompt/instructions (or a skill) that says something like “keep a daily log in workspace/memory/YYYY-MM-DD.md” and tries to read it at the start of a run.
If that file doesn’t exist, it’s not fatal, but it can add latency if the agent keeps retrying/looping.
Quick workaround is totally harmless:
sudo -u openclaw mkdir -p /home/openclaw/.openclaw/workspace/memory
sudo -u openclaw touch /home/openclaw/.openclaw/workspace/memory/$(date -u +%F).md
Long-term “real” fix is: remove/adjust whatever instruction is making it do that (if you want, we can hunt it down after).
2) Why March 1 when your laptop is Feb 28?
Because your VPS is almost certainly running in UTC, and OpenClaw logs are timestamped in UTC. So it can already be Mar 1 UTC while it’s still Feb 28 in your local timezone.
Check on the droplet:
date
date -u
timedatectl | sed -n '1,6p'
You can change the server timezone for your own convenience, but even if you do, many systems still use UTC internally for filenames/logs.
If you want, paste the output of timedatectl (first ~6 lines is enough) and I’ll confirm what timezone the droplet is using.
That explains it: you do have a “daily log” file mechanism already, but it only exists for 2026-02-27:
~/.openclaw/workspace/memory/2026-02-27.md
So when the agent tries to read today’s file (on the server), it 404s and you get that ENOENT.
Why you “have to create it”
You don’t have to for OpenClaw to function — this is almost certainly coming from your agent instructions / a skill / a template prompt that uses a “daily memory/journal file” convention.
Core OpenClaw memory ≠this file. This is just a plain markdown file in the workspace that some prompts like to use.
If you want the errors/latency to stop without changing prompts, just create the expected file for “today” on the VPS.
Why it’s looking for Mar 1
Because your VPS is running in UTC, and OpenClaw uses UTC timestamps in logs. It can be Mar 1 on the VPS while it’s still Feb 28 on your laptop.
Check:
date
date -u
Quick fix (safe)
Create the file for the VPS’s current UTC date:
sudo -u openclaw bash -lc 'd=$(date -u +%F); mkdir -p ~/.openclaw/workspace/memory; touch ~/.openclaw/workspace/memory/$d.md; ls -l ~/.openclaw/workspace/memory/$d.md'
Longer-term fix (optional)
If you don’t want the agent doing this at all, we can remove the instruction that tells it to read/write workspace/memory/YYYY-MM-DD.md — but we’d need to find where that instruction lives (often in your agent’s system prompt / workspace docs). A quick way to locate it is:
sudo -u openclaw rg -n "workspace/memory|/memory/\\d{4}-\\d{2}-\\d{2}\\.md" /home/openclaw/.openclaw/workspace -S
If you paste whatever file it matches (just the relevant lines), I can tell you what to change.
Good — that should eliminate the repeated ENOENT tool-failure noise.
To test “slowness” in a way we can actually diagnose, do one controlled run and capture a tight log window.
Step 1: run a simple baseline “fast” request
From Telegram, send something trivial like:
“Reply with exactly: OK”
Note how long it takes.
Step 2: run a “tool” request that used to be slow
Send one 1Password command request, e.g.:
“Run
op --versionand paste the output.”
(or your create/list test)
Step 3: capture a 2–3 minute log slice for each test
Right after you send the message (or right after it finishes), run on the VPS:
sudo journalctl -u openclaw.service --since "3 minutes ago" --no-pager | tail -n 400
Paste that output here.
That slice usually contains:
- when the Telegram message arrived
- when the model request started/ended
- any retries / rate limits
- tool calls + durations
- any “waiting for approval” / timeouts
Step 4: also paste droplet resource stats
Run:
uptime
free -h
What I’ll look for in the logs
- Long gaps before the model call → queue/backpressure or event loop blocked
- Model call takes ages → provider latency / rate limiting / retries
- Tool call takes ages →
opcommand or network delays - Approval pending → missed prompt causing timeout
- Frequent reconnect logs → Telegram or gateway connectivity issues
If you want to be extra precise about timing, tell me “I sent the message at HH:MM UTC” (or run date -u before you send it).
Those timings are not normal (1 min for “OK” means the delay is happening before it even does any real work).
Since you already fixed the missing daily memory file, the next step is to see where the time is going: Telegram → Gateway → LLM call → reply.
0) Quick sanity: re-test once now that 2026-03-01.md exists
Send “Reply with exactly: OK” again. If it suddenly drops to a few seconds, the missing-file loop was a big part of it. If it’s still ~1 min, keep going.
1) Capture a clean 3–5 minute log slice (most important)
Right after you send a slow message (or right after it finally replies), run:
sudo journalctl -u openclaw.service --since "5 minutes ago" --no-pager | tail -n 500
Paste that output here.
If you’re worried about secrets: journal output shouldn’t print your env tokens, but still quickly scan before pasting and redact anything that looks like a key/token.
What I’m looking for in that slice:
- how long between “telegram message received” and “llm request started”
- how long the LLM request takes (often logged as a duration)
- any retries / 429 rate limits / timeouts
- any approvals waiting/timeouts
2) Check whether the droplet is underpowered (very common)
Run and paste:
uptime
free -h
ps -o pid,cmd,%cpu,%mem --sort=-%cpu | head -n 10
If RAM is tight and it’s swapping, everything will feel “laggy” and model calls can backlog.
3) Check for service restarts / crashes (causes long gaps + missed dialogs)
sudo journalctl -u openclaw.service -b --no-pager | egrep -i "restarting|killed|oom|fatal|panic|uncaught" | tail -n 50
Once you paste (1) + (2), I can tell you which bucket you’re in:
- model/provider latency or rate limiting
- CPU/RAM pressure on the droplet
- approval waits/timeouts
- service restart / reconnect churn
- something looping in tools
Blank 2026-03-01.md is totally fine — the point was just to stop the “file not found” tool error loop.
15 seconds is a lot better than 60s, and it strongly suggests the missing-file retries were part of the delay. That said, 15s for “OK” is still slower than it should be, so there’s probably another latency source (model/provider latency, droplet load, or retries).
Next: measure where the 15 seconds went (one more log slice)
Right after you run another “Reply with exactly: OK”, grab:
sudo journalctl -u openclaw.service --since "2 minutes ago" --no-pager | tail -n 300
Paste that here. In that chunk I’m looking for timestamps around:
- telegram inbound message
- model request start/end (or retries)
- outbound send
Also paste droplet health (often the culprit)
uptime
free -h
If it’s consistently ~10–20s even when the VPS is idle
That’s usually model/provider latency (or retry/backoff). In that case, the fix is typically “use a faster model / set better fallbacks” (models docs: https://docs.openclaw.ai/concepts/models — and for what’s fastest right now, ask in the server’s #models channel since it changes quickly).
Send the log slice + uptime/free -h and we can pinpoint it.
Those VPS stats look healthy:
- load average
0.00→ not CPU-bound - ~1.1GiB “available” RAM, no swap → not memory-starved
So the remaining ~15s delay is very likely outside “droplet performance”, i.e.:
- Model/API latency (slow provider response, retries, rate limits/backoff)
- Telegram delivery latency / reconnect churn (less common, but happens)
- Gateway doing extra work per message (tool retries, long system prompts, etc.)
Next step: get the right log slice for one “OK” message
Do this immediately after you send “Reply with exactly: OK” once:
sudo journalctl -u openclaw.service --since "2 minutes ago" --no-pager | tail -n 400
If that’s still too noisy, use this filtered version (keeps timing-ish lines):
sudo journalctl -u openclaw.service --since "2 minutes ago" --no-pager \
| egrep -i "telegram|inbound|outbound|send|deliver|llm|model|completion|rate|429|retry|timeout|error|warn" \
| tail -n 250
What I’m trying to see is: where the 15 seconds sits:
- before the LLM call (queue / processing)
- inside the LLM call (provider latency/retries)
- after (Telegram send/delivery)
Also run these two (quick)
openclaw channels status --probe
openclaw status
If logs show the LLM call itself takes ~10–20s consistently
That’s basically “your current model/provider is slow for your region / current load / your prompt size”. The fix is usually switching the agent to a faster model (docs: https://docs.openclaw.ai/concepts/models). For which model is fastest right now, ask in the server’s #models channel (it changes constantly, and I don’t want to guess and mislead you).
Paste the 2‑minute journal slice and I’ll call out exactly which bucket it is.
-- No entries -- from journalctl is actually plausible here.
Even though your systemd unit has StandardOutput=journal, OpenClaw often writes the “interesting” stuff to its own gateway log file (and only emits to stdout/journal on warnings/errors). So a clean “OK” round-trip can produce zero journal lines.
Use OpenClaw’s log tail (best for this case)
Right after you run a slow “Reply with exactly: OK”, run:
openclaw logs --limit 400 --local-time | tail -n 200
If that’s still too chatty, filter it:
openclaw logs --limit 800 --local-time \
| egrep -i "telegram|inbound|outbound|deliver|send|llm|model|completion|rate|429|retry|timeout|error|warn|took|ms" \
| tail -n 250
That’s the chunk I need to see where the 10–15 seconds is spent.
If even openclaw logs doesn’t show timing info: temporarily increase log level
Add this to /opt/openclaw.env:
OPENCLAW_LOG_LEVEL=debug
Then:
sudo systemctl restart openclaw.service
Re-test once, then grab:
openclaw logs --limit 600 --local-time | tail -n 300
(After we’re done debugging, you can revert to info-level; debug can get noisy.)
Interpretation (based on your VPS stats)
Since CPU/RAM look fine, a steady 10–15s for “OK” is most commonly model/provider latency or retries. The logs will confirm that (you’ll see long “LLM request took …ms” or rate-limit/retry lines).
Run it after you get the response (or after you decide it’s “taking too long”).
When I say “right after sending”, I mean “right after the interaction finishes”, so the relevant log lines are still near the end of the file and easy to grab.
A good workflow is:
- Send:
Reply with exactly: OK - Wait until the reply arrives on Telegram
- Immediately run:
openclaw logs --limit 400 --local-time | tail -n 200
If you wait 10+ minutes and other stuff happens, the interesting lines get buried and the slice becomes harder to interpret.
If the message is still hanging, you can also run it while waiting (it won’t hurt), but the most useful slice is the one that includes the completion/send.
Once you paste that log tail, I’ll point to the exact timestamp gap that accounts for the 10–15 seconds.
Totally expected that you can’t find Reply with exactly: OK in the logs.
OpenClaw generally does not log the raw message text (privacy + to avoid accidentally writing secrets into logs). So searching for your exact prompt often won’t match anything even when the message was processed.
If you want something you can safely paste here, do summary-style extraction instead of raw logs.
Safe-ish “signal only” snippet (no message bodies)
Run this right after you get a response:
openclaw logs --limit 1500 --plain --no-color --local-time \
| egrep -i "model\.usage|message\.processed|queue\.lane|telegram|deliver|send|429|rate|retry|timeout|error|warn" \
| tail -n 200
That usually captures timing / retries / queueing / delivery without dumping payloads.
If that still doesn’t show anything useful
Use channel-focused logs (often less noisy):
openclaw channels logs --channel telegram | tail -n 200
Docs: https://docs.openclaw.ai/cli/logs and https://docs.openclaw.ai/cli/channels
Why your journalctl approach showed nothing
Your service can be running fine while not emitting anything new to journald for a clean request; a lot of detail goes to the gateway’s rolling file log, which openclaw logs tails.
If you paste the output of the first command (200 lines), I can tell you whether the 10–15s is:
- inside the model call (provider latency / retries),
- queueing before the model,
- or slow Telegram delivery.
In Discord you can attach files the normal way:
- Drag & drop the file into the thread message box, or
- Click the “+” / paperclip next to the message box → Upload a File → select it → send.
If the file is on your VPS (not on your laptop), easiest is to dump the relevant snippet into a paste:
openclaw logs --limit 1500 --plain --no-color --local-time \
| egrep -i "model\.usage|message\.processed|queue\.lane|telegram|deliver|send|429|rate|retry|timeout|error|warn" \
| tail -n 200 > /tmp/openclaw-log-snippet.txt
Then either:
- download
/tmp/openclaw-log-snippet.txtto your laptop and attach it, or - paste the contents into Pastebin (or similar) and send the link.
If you tell me whether you can SSH from your laptop to the droplet, I can give you the exact one-liner to copy the file down (e.g. scp ...).
I can’t access that LimeWire link’s actual file contents from here (it’s a JS-driven download page; web_fetch only sees the “Download logs.txt” wrapper, not the text file). So I can’t read the log from that URL.
Best options:
Option A (best): attach the file directly in this Discord thread
Use the Discord upload button (paperclip / “+”) or drag & drop logs.txt into the message box and send it here.
Option B: re-upload somewhere with a raw text URL (Pastebin / GitHub Gist)
On the VPS, generate a small snippet file first, then paste/upload that:
openclaw logs --limit 2000 --plain --no-color --local-time \
| egrep -i "model\\.usage|message\\.processed|queue\\.|telegram|deliver|send|429|rate|retry|timeout|error|warn" \
> /tmp/openclaw-slow-snippet.txt
Then either attach /tmp/openclaw-slow-snippet.txt in Discord, or paste it to Pastebin/Gist and share the raw link.
Option C: if you’re worried about secrets, auto-redact before sharing
sed -E \
-e 's/(OP_SERVICE_ACCOUNT_TOKEN=)[^[:space:]]+/\\1REDACTED/g' \
-e 's/(OPENCLAW_GATEWAY_TOKEN=)[^[:space:]]+/\\1REDACTED/g' \
/tmp/openclaw-slow-snippet.txt > /tmp/openclaw-slow-snippet.redacted.txt
Attach ...redacted.txt.
If you can attach either the snippet (Option B) or the full file directly in Discord (Option A), I’ll point to exactly where the 10–15s delay is happening (queueing vs model call vs Telegram send).
Got it — I can read the Pastebin and there are two big findings (plus a “what’s sensitive” answer).
Does the paste contain sensitive info?
I don’t see obvious API keys / tokens in what you posted (good). But it does contain some stuff you probably don’t want publicly indexed forever:
- your Telegram bot username:
@openclaw3_bookkeeper_bot - a Telegram numeric id target:
telegram:8577900743 - your 1Password vault name:
"Shared with Yug" - item titles like
"AutoApproved2 - Test Entry - OpenClaw" - internal run/session ids
So: not “secrets leaked”, but still personal metadata. I’d delete the paste once we’re done and re-share redacted snippets next time.
1) Why approvals “sometimes don’t show up” (this is clear in the logs)
You have repeated errors:
403: Forbidden: bots can't send messages to bots
and specifically:
exec approvals: failed to deliver to telegram:8577900743
That means your exec-approval forwarding is trying to DM a Telegram bot account, which Telegram forbids. So any approval prompts forwarded to that target will silently fail, which feels like “the approval popup didn’t show”.
Fix: configure approvals forwarding to your human Telegram chat id, or simpler: don’t use a fixed target at all.
Best approach on Telegram is to forward to the same session that triggered the exec:
// ~/.openclaw/openclaw.json
{
approvals: {
exec: {
enabled: true,
mode: "session",
agentFilter: ["main"],
},
},
}
Docs: https://docs.openclaw.ai/tools/exec-approvals (Approval forwarding to chat channels)
2) Why it feels slow (your droplet is fine — it’s model/provider latency)
Your VPS isn’t CPU/RAM bound (you showed load ~0, plenty of RAM), and your logs show lots of runs like:
durationMs=63154(63s)durationMs=52218(52s)durationMs=93968(94s)durationMs=117805(118s)
And they’re consistently using:
provider=custom-integrate-api-nvidia-com model=moonshotai/kimi-k2.5
So the main delay is simply LLM round-trip time (plus sometimes extra tool-call churn — you also have read tool called without path warnings, which usually means the model is producing malformed tool calls and the agent has to recover).
What to do:
- If you want snappier chat, switch the agent’s default model/provider to something lower-latency. I’m not going to name models here (it changes fast), but ask in the server’s #models channel and/or follow the models docs: https://docs.openclaw.ai/concepts/models
- Updating off
2026.2.12is also worth doing (lots of fixes land quickly): https://docs.openclaw.ai/install/updating
Bonus: there’s also an op command error in your logs
You have:
unknown command "version" for "op"
So somewhere the agent ran op version (wrong) instead of op --version (correct). Not the main latency issue, but it can cause extra back-and-forth.
If you paste your current approvals.exec config block (redact tokens), I can point out exactly where that telegram:8577900743 target is coming from and what to change.
If you posted it anonymously (not while logged into a Pastebin account), Pastebin usually treats that paste as not owned by your account, so you won’t get a delete button later.
What you can do:
1) Check if you still have the delete key / edit link
When you create an anonymous paste, Pastebin often shows (or sometimes emails) a “delete key” or gives you a special edit/delete URL right after posting. If you still have that tab open or browser history, look for a URL containing something like:
pastebin.com/edit/...- a
?delete=/ delete token - or a “Delete” link on the page you originally saw after creation
If you find that, open it and you can delete.
2) If you don’t have the delete key, contact Pastebin support
Without the delete token, you generally can’t delete an anonymous paste yourself. Your options are:
- Use Pastebin’s Report / DMCA / privacy request flow (explain it contains accidentally shared personal data and you’re the author).
- Or email their support with the URL and ask for removal.
3) Mitigation right now (recommended)
Even if you can’t delete it immediately:
- Remove the link from this Discord thread if possible (edit your message if you can).
- Rotate/redact anything that might be sensitive (you didn’t leak tokens in that paste from what I saw, so you probably don’t need to rotate keys).
- For future logs: use a redacted snippet first (I can give you a one-liner that strips common token patterns before you upload).
If you want, paste a screenshot of what you see when you open the paste (blur the content), and I’ll tell you exactly where the “report” / “flag” option is on that page.
I can help you write this as a polished blog post via a quick 3‑stage workflow (context → draft → reader test). If you want that, tell me: who’s the audience (OpenClaw beginners vs power users) and what install target (DigitalOcean 1‑click only vs “any Linux VPS”).
Meanwhile, here’s a solid v1 article you can copy/paste and publish.
Setting up 1Password with OpenClaw (so your agent can create & store credentials without constant approvals)
If you’re running OpenClaw on a VPS and you want your agent to generate credentials and store them in 1Password automatically, the clean approach is:
- Use 1Password Service Accounts (not your personal login)
- Put the
OP_SERVICE_ACCOUNT_TOKENinto the Gateway service environment - Allowlist only the
opbinary in OpenClaw Exec Approvals - Make the agent run direct
op …commands (nosource … && export … && …shell chaining)
This keeps things automated without allowlisting the whole shell (which is too dangerous).
Why approvals kept popping up (the root cause)
OpenClaw exec approvals allowlists match the resolved executable path (the binary), not command text.
So these do not help:
op item createexport OP_SERVICE_ACCOUNT_TOKENsource ~/.openclaw/workspace/...
Also, commands like:
source ~/.openclaw/workspace/.secrets/1password.env && export OP_SERVICE_ACCOUNT_TOKEN && op item create ...
force the exec request to be treated as shell text (builtins + &&). In allowlist mode that typically becomes an allowlist miss unless you allowlist bash/sh (which you should avoid).
The fix is to make OP_SERVICE_ACCOUNT_TOKEN available to the OpenClaw service without needing source/export.
Prerequisites
- A 1Password account that supports Service Accounts
- A dedicated vault for the agent (recommended)
- OpenClaw Gateway running on Linux (systemd service on a VPS is common)
- 1Password CLI (
op) installed on the same machine where OpenClaw runs host exec (usually the gateway host)
Security note: a service account token is effectively “the keys” to whatever vaults it can access. Keep it scoped.
Step 1 — Install the 1Password CLI (op) on the gateway host
Install op and confirm it works:
op --version
(How you install it is up to you; just ensure the final binary path is stable, e.g. /home/openclaw/.local/bin/op.)
Step 2 — Put OP_SERVICE_ACCOUNT_TOKEN into the Gateway’s environment (no shell chaining)
General OpenClaw approach (works broadly)
OpenClaw can load env vars from ~/.openclaw/.env (see docs):
https://docs.openclaw.ai/help/environment
DigitalOcean 1‑click “OpenClaw droplet” approach (what you have)
Your service definition shows:
EnvironmentFile=/opt/openclaw.env
User=openclaw
So put this into /opt/openclaw.env (no export keyword):
OP_SERVICE_ACCOUNT_TOKEN="ops_xxx_your_token_here"
Then restart:
sudo systemctl restart openclaw.service
Sanity check without printing the token:
pid="$(systemctl show -p MainPID --value openclaw.service)"
sudo tr '\0' '\n' < "/proc/$pid/environ" \
| grep -q '^OP_SERVICE_ACCOUNT_TOKEN=' \
&& echo PRESENT || echo MISSING
Step 3 — Allowlist only the op binary (and nothing else)
Exec approvals are stored per host at:
~/.openclaw/exec-approvals.json
Use the CLI (recommended) so you don’t fight JSON formatting:
# find the actual binary path the gateway user sees
sudo -u openclaw which op
# allowlist it for the gateway + main agent
openclaw approvals allowlist add --gateway --agent main "/home/openclaw/.local/bin/op"
# verify
openclaw approvals get --gateway
Docs: https://docs.openclaw.ai/tools/exec-approvals and https://docs.openclaw.ai/cli/approvals
What NOT to allowlist
Avoid allowlisting:
/bin/bash//usr/bin/sh(effectively “run arbitrary shell text”)python3(effectively “run arbitrary code”)
cat(easy file exfiltration)curl(broad outbound network)
If your goal is “agent can use 1Password”, the allowlist can be just op.
Step 4 — Make the agent run direct op … commands (no source, no export, no &&)
Once the token is in the service environment, your agent should run commands like:
op item create --vault "Your Vault" --category password --title "Example" username="u" password="p"
op item list --vault "Your Vault"
op item get --vault "Your Vault" "Example"
No environment setup per command needed.
Quick test prompt you can send your agent
Run these commands using host exec. Do not use
source,export,&&, pipes, or redirections.
op --versionop vault list(confirm my vault exists)op item create --vault "Shared with Yug" --category password --title "Test Entry - OpenClaw" username="openclaw-test-user" password="openclaw-test-pass"op item list --vault "Shared with Yug"
If you still get approval prompts at this point, it usually means the exec request isn’t resolving to op (i.e., the agent is still doing shell chaining).
Step 5 (optional) — Make approval prompts reliable on a VPS
If you forward exec approvals to Telegram, make sure you’re not trying to message a bot account (Telegram error: “bots can’t send messages to bots”). The simplest reliable mode is forwarding to the same chat session that triggered the exec:
{
approvals: {
exec: {
enabled: true,
mode: "session",
agentFilter: ["main"],
},
},
}
Docs: https://docs.openclaw.ai/tools/exec-approvals (Approval forwarding to chat channels)
Troubleshooting checklist
openclaw approvals get --gatewayshows youropallowlist entry
- The gateway process actually has
OP_SERVICE_ACCOUNT_TOKENin its environment (check with/proc/<pid>/environ+grep -q) - The agent is running
op …directly (not shell text) - If things feel slow: it’s often model/provider latency, not VPS CPU/RAM
Safety / operational advice
- Use a dedicated vault for the agent.
- Give the service account the minimum vault access it needs.
- Rotate the service account token if it ever lands in logs/paste sites.
- Be cautious with fully automating website signups: some sites forbid automated account creation in their ToS.
If you tell me:
- do you want this blog to be DO 1‑click specific or generic Linux?
- what’s your preferred title + tone (super short “quickstart” vs long “deep dive”)?
…I’ll revise v1 into a polished final post (with a clean intro, diagrams, and a short troubleshooting matrix).
1) Structure: happy path first + pitfalls after
Yep — that’s the best format for this topic. I’d do:
- What you’ll achieve (2–3 bullets)
- Happy path (copy/paste steps)
- Verification (quick tests)
- Common pitfalls (symptom → cause → fix)
- Security notes (short but clear)
If you want, I can rewrite the draft I sent into exactly that layout (shorter + more “do this, then this”).
2) Copy/paste to a Twitter article
“Twitter articles” depends on what you mean:
- If you mean X (Twitter) Articles: that feature isn’t available to every account and the UI changes a lot. If you have it, it’s usually: Post composer → Article / Longform. Then you paste Markdown-ish text and format headings manually.
- If you mean a long X post/thread: you’ll need to split the article into a thread (I can do that too).
- If you mean publish elsewhere and share on X: common flow is publish on Medium/Dev.to/Substack/Notion → share the link on X.
Tell me which of these you want:
- X Article (native)
- X thread
- External blog + link on X
…and I’ll format it appropriately (X threads need very different formatting).
If you want the “happy path” version now (ready to paste)
Here’s a tighter Happy Path section you can paste into an article right away:
Happy path: OpenClaw + 1Password Service Account (no approval spam)
Goal: Let OpenClaw run op item create/list/get/read without prompting, without allowlisting bash/sh.
Step 1 — Install op on the gateway host
op --version
Step 2 — Put OP_SERVICE_ACCOUNT_TOKEN into the Gateway service environment
On the DigitalOcean 1‑click OpenClaw image, the systemd unit uses:
EnvironmentFile=/opt/openclaw.env
Add:
OP_SERVICE_ACCOUNT_TOKEN="ops_..."
Restart:
sudo systemctl restart openclaw.service
Verify without printing the token:
pid="$(systemctl show -p MainPID --value openclaw.service)"
sudo tr '\0' '\n' < "/proc/$pid/environ" | grep -q '^OP_SERVICE_ACCOUNT_TOKEN=' && echo PRESENT || echo MISSING
Step 3 — Allowlist only the op binary for agent main
Find the binary path:
sudo -u openclaw which op
Allowlist it:
openclaw approvals allowlist add --gateway --agent main "/home/openclaw/.local/bin/op"
openclaw approvals get --gateway
Step 4 — Use direct op … commands (no source, no export, no &&)
Now the agent should run:
op item create --vault "Shared with Yug" --category password --title "Test Entry - OpenClaw" username="u" password="p"
op item list --vault "Shared with Yug"
op item get --vault "Shared with Yug" "Test Entry - OpenClaw"
If you confirm you want X thread vs X Article, I’ll rewrite the full blog into the final “happy path + pitfalls” version and format it for that destination.
Before diving into knobs: quick threat-model framing for 1Password + agents
If you’re letting an agent run op ..., assume anything it can read/write in that vault has a non‑trivial chance of leaking (logs, prompts, compromised agent, misrouted outputs, etc.). Strongly recommend: dedicated vault + service account, least-privilege, and only put creds you can live with rotating/losing (i.e. “automation creds”, not your primary human account / crown jewels).
Now, why your auto-approvals aren’t “sticking”:
1) Your exec-approvals.json is (very likely) not being applied because the allowlist entries don’t match how OpenClaw matches
Exec approvals allowlists match resolved executable paths (glob), not “command strings”.
So entries like:
"pattern": "op item create""pattern": "export OP_SERVICE_ACCOUNT_TOKEN""pattern": "~/.openclaw/workspace/.secrets/1password.env"
…won’t do what you expect. OpenClaw is looking for patterns like:
/home/openclaw/.local/bin/op(or whereveropactually is)/usr/bin/curl,/usr/bin/unzip,/usr/bin/mv, etc.
Docs: https://docs.openclaw.ai/tools/exec-approvals (see “Allowlist (per agent)”)
2) Your pasted file looks malformed JSON
In your paste, the op item get entry is missing closing fields/braces and there’s a stray { before the next item. If the real file on disk looks like that, the approvals service may fail to load it (and you’ll keep getting prompts/denies).
3) You restarted the gateway, but approvals are enforced on the execution host
Approvals live on the machine where exec actually runs (host=gateway or host=node), at:
~/.openclaw/exec-approvals.json
So the two most common “I edited it but nothing changed” causes are:
- you edited the file under the wrong Linux user (e.g. root vs
openclaw) - your agent is executing on a node host, but you edited the gateway approvals (or vice versa)
Fast sanity check on the droplet:
openclaw --version
openclaw approvals get --gateway
4) Why you got “tonnes” of approvals even for one task
Your install command chains a bunch of binaries (curl && unzip && mv && chmod && ...). In security=allowlist, every segment’s executable must be allowlisted, otherwise you’ll get prompted again and again.
What I’d do (minimal + reliable)
-
Use the Control UI to add allowlist entries so it writes the correct executable paths for you:
Control UI → Nodes → Exec approvals (select Gateway vs Node correctly)
Docs: https://docs.openclaw.ai/tools/exec-approvals#control-ui-editing -
Add allowlist patterns for the binaries, e.g.:
/home/openclaw/.local/bin/op(or the real resolved path)/usr/bin/curl/usr/bin/unzip/usr/bin/mv/usr/bin/chmod
- Keep
ask: "on-miss"until you’ve stabilized the workflow. If you setask: "off", you’ll stop seeing prompts, but new binaries will just get denied (which can feel like “it’s broken” unless you expect it).
About the missing approval pop-up in Control UI
If you keep missing the modal, enable approval forwarding to chat and approve from Discord with /approve … (much harder to miss than a flaky websocket modal):
Docs + config example: https://docs.openclaw.ai/tools/exec-approvals#approval-forwarding-to-chat-channels
If you paste these two outputs, I can tell you exactly what’s being applied and where the mismatch is:
openclaw --versionopenclaw approvals get --gateway(redact the socket token)
Also: paste one exact command line that the agent is still asking you to approve (the “resolved executable path” shown in the approval dialog is especially useful).
Here’s a full, beginner-friendly blog post you can publish (Markdown). It’s written to be readable even if someone’s only “kinda comfortable” with SSH, and it uses a simple story + short sections for skim-readers.
Stop copy‑pasting API keys: use 1Password (Service Accounts) with OpenClaw
You know that moment when your OpenClaw agent finally works… so you celebrate by pasting an API key into a config file on a random cloud server?
Yeah. That’s the part that should make you a little nervous.
This guide shows a safer pattern that still feels “easy mode”:
- Put your OpenClaw secrets in a dedicated 1Password vault
- Give OpenClaw access using a 1Password Service Account (not your personal account)
- Let OpenClaw fetch secrets at runtime using the 1Password CLI (
op)
No extra servers. No secret sprawl. Much easier to rotate when (not if) you need to.
TL;DR (for fast readers)
You’ll do four things:
- Create a dedicated vault (example:
OpenClaw Automation) - Create a 1Password Service Account that can read that vault
- Install the
opCLI on your OpenClaw machine and setOP_SERVICE_ACCOUNT_TOKEN - Configure OpenClaw “Secrets Management” to read API keys via
op read ...
The mindset (this is the part most guides skip)
Don’t use your personal 1Password vault for automation
For agent automation, assume the environment is “leak-prone”:
- logs get copied around
- terminals get screenshotted
- tokens end up in shell history
- a future bug prints more than it should
- you accidentally share a config file
So treat the “automation vault” like a containment zone:
only store credentials you can rotate quickly and would survive if exposed.
That’s why this guide uses:
- a dedicated vault
- a service account
- least privilege access
(1Password explicitly positions service accounts for automation and least privilege use cases.)
What you’re building
OpenClaw has a Secrets Management system that can resolve secret values from an exec provider (a trusted CLI command). We’ll configure it so OpenClaw runs:
op read op://<Vault>/<Item>/<field>
…and uses the output as the API key for your model provider.
OpenClaw docs reference:
- Secrets management + 1Password example: https://docs.openclaw.ai/gateway/secrets
What you need (beginner checklist)
- A 1Password account where you can create Service Accounts (or an admin who can)
- SSH access to the machine running OpenClaw (DigitalOcean Marketplace droplet is perfect)
- OpenClaw already running on that machine
- 10–15 minutes
Step 1: Create a dedicated vault (in 1Password)
Create a vault named something like:
OpenClaw Automation(good)OpenClaw - StagingandOpenClaw - Prod(better, if you run multiple environments)
Put only automation secrets here.
Examples of “good automation secrets”:
- a limited-scope OpenAI key (or a project key)
- a bot token with minimal permissions
- a throwaway account password you can reset quickly
Examples of “don’t do this”:
- your personal email password
- the “god mode” AWS root key
- anything you’d hate to rotate under pressure
Step 2: Create a 1Password Service Account (least privilege)
In 1Password, create a Service Account and grant it access to only the vault you made above.
Important notes from 1Password:
- Service account permissions/vault access are intentionally rigid (if you need more vaults later, you typically create a new service account).
- The token is shown once—store it safely immediately.
Official docs:
- Service accounts overview: https://developer.1password.com/docs/service-accounts/
- Getting started: https://developer.1password.com/docs/service-accounts/get-started/
Step 3: Install the 1Password CLI (op) on your server
SSH into your server.
You want op available to the same user that runs the OpenClaw Gateway process.
Check if it’s installed:
op --version
If it’s not installed, install it using 1Password’s official instructions for your OS:
https://developer.1password.com/docs/cli/get-started/
(Keeping install steps “source of truth” with 1Password helps this blog stay evergreen.)
Step 4: Authenticate op using the service account token
1Password Service Accounts authenticate by setting an environment variable:
OP_SERVICE_ACCOUNT_TOKEN
Official docs:
https://developer.1password.com/docs/service-accounts/use-with-1password-cli/ (it explicitly calls out OP_SERVICE_ACCOUNT_TOKEN)
The bootstrap problem (be honest about it)
You still need to store one secret somewhere: the service account token itself.
That token is the “key to the vault”—treat it like a password:
- don’t paste it into screenshots
- don’t commit it
- don’t leave it in shell history if you can avoid it
Where to put it (two common options)
Option A (simple for beginners): set it in the environment before starting OpenClaw
If you start OpenClaw manually from a shell, you can export it in that shell session.
Option B (better for servers): set it for the OpenClaw service
If OpenClaw runs as a daemon/service, set OP_SERVICE_ACCOUNT_TOKEN in the service environment so it’s always available after reboot.
OpenClaw’s env-var loading rules (useful background):
https://docs.openclaw.ai/help/environment
Step 5: Put a secret in the vault
Create an item in the OpenClaw Automation vault, for example:
- Item name:
OpenAI API Key - Field:
password(or whatever you prefer, butpasswordis common)
You’ll reference it like:
op://OpenClaw Automation/OpenAI API Key/password
Step 6: Test op read works on the server (quick sanity check)
With OP_SERVICE_ACCOUNT_TOKEN set, run:
op read "op://OpenClaw Automation/OpenAI API Key/password"
You should see the secret printed.
If this fails, fix it here before touching OpenClaw config.
Common failure causes:
- token not set for the user/process running
op - service account doesn’t have access to that vault
- you tried to use a Personal/Private vault (service accounts can’t access those)
opis older than required (service accounts require op CLI v2.18.0+)- you have 1Password Connect env vars set; they can take precedence (1Password calls this out in their docs)
Step 7: Configure OpenClaw to read the API key via 1Password CLI
OpenClaw supports secrets providers. We’ll create an exec provider that calls op read ..., then point the model provider’s apiKey at that secret ref.
Example openclaw.json snippet
This is adapted directly from OpenClaw’s secrets docs (with the path updated to a typical Linux install location). You must set the correct command path for your server.
{
secrets: {
providers: {
onepassword_openai: {
source: "exec",
command: "/usr/local/bin/op", // change to wherever `op` lives on your server
args: ["read", "op://OpenClaw Automation/OpenAI API Key/password"],
passEnv: ["HOME", "OP_SERVICE_ACCOUNT_TOKEN"],
jsonOnly: false,
},
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [{ id: "gpt-5", name: "gpt-5" }],
// This tells OpenClaw: resolve the apiKey by running the exec provider above
apiKey: { source: "exec", provider: "onepassword_openai", id: "value" },
},
},
},
}
Source (OpenClaw docs):
https://docs.openclaw.ai/gateway/secrets (see “1Password CLI” example)
Two “gotchas” to call out in the blog
commandmust be an absolute path to theopbinarypassEnvmust includeOP_SERVICE_ACCOUNT_TOKEN(otherwise the provider runs but can’t authenticate)
Step 8: Reload OpenClaw and verify
After updating config:
- restart/reload the OpenClaw Gateway (use whatever method you normally use on your droplet)
- then run a simple test that forces a model call
If OpenClaw can’t resolve the secret on startup, it should fail fast (that’s good: you’ll notice immediately).
Troubleshooting (copy/paste friendly)
“op works in my SSH session, but OpenClaw can’t read secrets”
That usually means OpenClaw is running as a service without your interactive shell environment.
Fix: ensure OP_SERVICE_ACCOUNT_TOKEN is present in the Gateway process environment, not just your terminal.
OpenClaw env precedence reference: https://docs.openclaw.ai/help/environment
“Service account can’t see my vault”
Service accounts can’t access personal/private/default shared vaults. Use a dedicated vault and explicitly grant access.
1Password docs: https://developer.1password.com/docs/service-accounts/get-started/
“It’s slow / rate-limited”
Service accounts have request limits. Also, some op commands make multiple requests. Prefer a single op read ... for runtime resolution (and avoid unnecessary op item list loops).
1Password note on request counts: https://developer.1password.com/docs/service-accounts/use-with-1password-cli/
Security checklist (practical, not perfect)
- Use a dedicated vault for automation secrets
- Use a service account with least privilege
- Store only secrets you can rotate quickly
- Treat the service account token like a password
- Rotate tokens periodically (and immediately if you suspect exposure)
Bonus: X/Twitter thread version (paste-ready)
1/ The easiest way to leak your API keys is “just for now” copy-pasting them into a server config. I did it too. Here’s a cleaner setup: OpenClaw + 1Password Service Account + op read (no extra servers).
2/ Rule #1: don’t use your personal 1Password vault for automation. Make a dedicated vault like OpenClaw Automation. Assume anything in there might leak someday. Store only rotate-friendly creds.
3/ Create a 1Password Service Account. Give it access to only that vault. Least privilege. (Docs: developer.1password.com → Service Accounts)
4/ Install 1Password CLI (op) on the OpenClaw machine.
5/ Set OP_SERVICE_ACCOUNT_TOKEN for the process that runs OpenClaw (not just your SSH session).
6/ Put your API key in the automation vault (example item: “OpenAI API Key”).
7/ Test: op read "op://OpenClaw Automation/OpenAI API Key/password"
8/ Configure OpenClaw Secrets (exec provider) to run op read ..., then point your model apiKey at that SecretRef.
9/ Result: no API keys sitting in configs, easier rotation, cleaner ops.
If you tell me which providers you want in the blog examples (OpenAI only? Anthropic too? OpenRouter?), and whether you want separate “staging vs prod” sections, I can tailor the config snippets and the story beats.
Below are only the rewritten parts you asked for (install commands + “where to put it” with real commands + clearer “Step 5” + story-based step titles).
All commands below are written for a typical Ubuntu/Debian DigitalOcean droplet and were accurate as of 2026‑03‑01. If anything drifts, use the official 1Password CLI install page to grab the newest method/version: https://developer.1password.com/docs/cli/get-started/ (use it to confirm the latest Linux install steps + latest CLI version).
Scene 1 — “Meet your courier”: install the 1Password CLI (op) on your server
Option A (recommended): install a pinned version from 1Password’s CDN (copy/paste)
This is the most deterministic way (no guessing, no distro repo surprises).
# 0) Prereqs
sudo apt-get update
sudo apt-get install -y curl unzip
# 1) Pick a version (update this later if needed)
OP_VERSION="2.30.3"
# 2) Download + install
curl -sSLo /tmp/op.zip "https://cache.agilebits.com/dist/1P/op2/pkg/v${OP_VERSION}/op_linux_amd64_v${OP_VERSION}.zip"
unzip -o /tmp/op.zip -d /tmp/op
sudo install -m 0755 /tmp/op/op /usr/local/bin/op
# 3) Verify
op --version
If you’re on ARM (rare on DO, but possible), swap amd64 → arm64 in the URL.
Option B: use the official instructions (best when your OS is unusual)
Open: https://developer.1password.com/docs/cli/get-started/
What to do with that link: scroll to Linux → choose your install method → follow exactly.
Scene 2 — “Give OpenClaw the key”: set OP_SERVICE_ACCOUNT_TOKEN (two crystal-clear options)
You have two valid setups:
Option 1 — Quick test (good for setup, NOT durable)
This sets the token only for your current SSH session.
# Paste this, then paste your token when prompted (it won't echo)
read -s OP_SERVICE_ACCOUNT_TOKEN
export OP_SERVICE_ACCOUNT_TOKEN
echo
echo "Token loaded into this shell session."
Now op commands in this session will authenticate as the service account.
Option 2 — Persistent (recommended on a server): add token to the systemd service
This makes the token available to the OpenClaw Gateway service even after reboot.
2.1) Find your OpenClaw service name (no guessing)
systemctl list-units --type=service | grep -E 'openclaw-gateway'
You’ll usually see something like:
openclaw-gateway.service
(oropenclaw-gateway-<profile>.service)
For the commands below, I’ll assume: openclaw-gateway.service
If yours differs, replace it.
2.2) Create a root-only env file (safe-ish, keeps token out of shell history)
sudo mkdir -p /etc/openclaw
sudo install -m 600 /dev/null /etc/openclaw/op.env
sudo nano /etc/openclaw/op.env
In the editor, paste exactly this (with your real token):
OP_SERVICE_ACCOUNT_TOKEN=op_sa_XXXXXXXXXXXXXXXXXXXXXXXX
Save + exit.
2.3) Tell systemd to load that env file
sudo systemctl edit openclaw-gateway.service
Paste this drop-in:
[Service]
EnvironmentFile=/etc/openclaw/op.env
Save + exit, then restart:
sudo systemctl daemon-reload
sudo systemctl restart openclaw-gateway.service
sudo systemctl status openclaw-gateway.service --no-pager
Scene 3 — “Bury the treasure”: create the vault item you want OpenClaw to read (clear + explicit)
In 1Password, create:
- Vault:
OpenClaw Automation - Item:
OpenAI API Key - Field that holds the key: usually
password
That gives you a stable secret reference path:
op://OpenClaw Automation/OpenAI API Key/password
Don’t “guess” if the field is called password
If you’re not sure what the field name is, list item details (this does not print the secret by default):
op item get "OpenAI API Key" --vault "OpenClaw Automation"
Look for the field label you want to read (commonly password).
Scene 4 — “Prove the map works”: test the secret read on the server (without printing it)
This checks access without dumping the secret to your terminal:
op read "op://OpenClaw Automation/OpenAI API Key/password" >/dev/null && echo "OK: secret is readable"
If this fails, 2 common fixes:
- Your OpenClaw process doesn’t have
OP_SERVICE_ACCOUNT_TOKEN(use Scene 2, Option 2) - You accidentally have 1Password Connect env vars set (1Password says they can take precedence). Check:
env | grep -E '^OP_CONNECT_' || true
If you want, paste what you’re using as the OpenClaw service name from systemctl list-units ... and I’ll rewrite the commands with the exact service string so it’s 100% copy/paste for your droplet.
Substack doesn’t have a great “upload this Markdown file and publish” flow (and there isn’t a stable, official public API you can rely on for fully automated publishing), so the realistic goal is:
- one clean transfer
- zero manual reformatting after paste
These are the two workflows that actually work in practice:
Option A (simplest, zero drama): Google Docs → paste into Substack (format survives)
Why this works: Substack’s editor preserves rich-text structure really well when the source is Google Docs.
Do this:
- Paste the article into Google Docs.
- Apply styles:
- Title = Title
- Section headers = Heading 2
- Subsections = Heading 3
- Lists as normal bullets/numbered lists
- In Google Docs:
Ctrl/Cmd + A→Ctrl/Cmd + C - In Substack: New post → click into editor →
Ctrl/Cmd + V
What usually survives perfectly: headings, bold/italics, links, lists, blockquotes.
What you may still adjust: code blocks (sometimes Substack turns them into normal text).
If your post has code blocks, see Option B.
Option B (best if your “source of truth” is Markdown): Markdown → HTML → paste into Substack
Why this works: Substack doesn’t “speak Markdown” reliably, but it does accept pasted HTML/rich content nicely.
Step 1) Convert Markdown to HTML (one command)
On your computer (or wherever the markdown lives), install pandoc:
- macOS (Homebrew):
brew install pandoc
- Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y pandoc
Convert:
pandoc article.md -f markdown -t html -o article.html
Step 2) Paste the rendered HTML into Substack (no reformatting)
- Open
article.htmlin a browser. - Select all (
Ctrl/Cmd + A) → copy. - Paste into Substack editor.
Notes:
- Headings/lists/links generally paste clean.
- Images won’t “upload themselves” from local HTML—you’ll still insert/upload images in Substack.
- Code blocks: if you care about them, do a quick check after paste. If Substack flattens them, use Substack’s “code block” formatting on those sections only.
If you truly want “no copy/paste at all”
Then you’re in automation territory (browser automation / unofficial scripts). It’s doable, but brittle (Substack UI changes, auth/cookies, editor quirks). If you tell me what you mean by “avoid copy-pasting”:
- “I’m fine pasting once, I just don’t want reformatting” → Option A/B above
- “I want to push from GitHub/Markdown and it shows up as a draft automatically” → tell me your writing setup (Obsidian? Notion? Google Docs? Git repo?) and I’ll suggest the least-brittle pipeline
Which one are you aiming for?
Stop copy‑pasting API keys: plug 1Password into OpenClaw (Service Account + op read)
A friend of mine (let’s call her Sam) spun up OpenClaw on a fresh DigitalOcean droplet. Ten minutes later, everything worked… except the part where she had to paste API keys into config files on a remote server.
It felt normal. It was also the exact moment the “security debt” started compounding.
This guide is the pattern that fixes it without turning your setup into enterprise cosplay:
- Create a dedicated 1Password vault for automation
- Use a 1Password Service Account (not your personal account)
- Let OpenClaw fetch secrets at runtime using the 1Password CLI (
op)
No extra servers. No “secret manager migration project”. Just a clean, rotatable setup.
The one rule that makes this safe-ish
Don’t use your personal 1Password vault for automation.
Instead, create a dedicated vault like OpenClaw Automation and pretend:
“Everything in this vault has a higher-than-normal chance of leaking.”
That sounds dramatic, but it’s the right default when you’re running automation on a server with logs, shells, config files, and (potentially) agents. So only put credentials there that you can rotate quickly and can live without for a bit.
That’s why we use a service account + least privilege.
What you’ll end up with (simple mental model)
OpenClaw will not store your API key in openclaw.json.
Instead it will run a command like:
op read "op://OpenClaw Automation/OpenAI API Key/password"
…and use the output as the API key at runtime.
Step-by-step playbook (beginner-friendly, copy/paste)
Chapter 1 — “Build the vault”: create a dedicated automation vault
In 1Password:
- Create a vault named:
OpenClaw Automation - Add an item, for example:
- Item name:
OpenAI API Key
- Put the key in the password field (recommended)
You’re doing this so your secret reference path is stable:
op://OpenClaw Automation/OpenAI API Key/password
Chapter 2 — “Hire a robot”: create a 1Password Service Account (least privilege)
Create a Service Account in 1Password and grant it access to only the OpenClaw Automation vault.
During creation you’ll get a service account token. Treat it like a password.
(You can optionally keep the official docs handy, but you won’t need them to follow this guide.)
Chapter 3 — “Give your server a mouth”: install the 1Password CLI (op)
These install commands work as of 2026‑03‑01 on Ubuntu/Debian (typical DigitalOcean droplets).
For the latest official instructions (if anything changes), use:
https://developer.1password.com/docs/cli/get-started/ (use it to confirm the newest Linux install steps/version)
Install op (pinned, deterministic)
# Prereqs
sudo apt-get update
sudo apt-get install -y curl unzip
# Pick a version (you can bump later)
OP_VERSION="2.30.3"
# Download + install
curl -sSLo /tmp/op.zip "https://cache.agilebits.com/dist/1P/op2/pkg/v${OP_VERSION}/op_linux_amd64_v${OP_VERSION}.zip"
unzip -o /tmp/op.zip -d /tmp/op
sudo install -m 0755 /tmp/op/op /usr/local/bin/op
# Verify
op --version
Chapter 4 — “Give OpenClaw the key”: set OP_SERVICE_ACCOUNT_TOKEN
You have two valid paths. Pick one.
Option A (fastest): set token just for your current SSH session
Good for testing. Not durable across reboots.
# Paste this, then paste your token when prompted (it won't echo)
read -s OP_SERVICE_ACCOUNT_TOKEN
export OP_SERVICE_ACCOUNT_TOKEN
echo
echo "OK: token loaded into this SSH session."
Option B (recommended on servers): set token for the OpenClaw systemd service (persistent)
1) Find your OpenClaw gateway service name
systemctl list-units --type=service | grep -E 'openclaw-gateway'
Most commonly it’s:
openclaw-gateway.service
If yours is different, substitute the name below.
2) Create a root-only env file
sudo mkdir -p /etc/openclaw
sudo install -m 600 /dev/null /etc/openclaw/op.env
sudo nano /etc/openclaw/op.env
Paste this line (with your real token):
OP_SERVICE_ACCOUNT_TOKEN=op_sa_XXXXXXXXXXXXXXXXXXXXXXXX
Save and exit.
3) Attach that env file to the OpenClaw service
sudo systemctl edit openclaw-gateway.service
Paste:
[Service]
EnvironmentFile=/etc/openclaw/op.env
Save and exit.
4) Restart OpenClaw
sudo systemctl daemon-reload
sudo systemctl restart openclaw-gateway.service
sudo systemctl status openclaw-gateway.service --no-pager
Chapter 5 — “Prove it works”: test op read from the server (without printing the secret)
This tests access without dumping the secret into your terminal:
op read "op://OpenClaw Automation/OpenAI API Key/password" >/dev/null && echo "OK: secret is readable"
If it fails, check these two things first
1) Are you accidentally using 1Password Connect env vars?
1Password Connect env vars can take precedence over the service account token.
Check:
env | grep -E '^OP_CONNECT_' || true
If you see them and you’re not using Connect, remove them from your environment/service configuration and retry.
2) Does the service account actually have access to that vault?
Double-check the service account’s vault access in 1Password.
Chapter 6 — “Teach OpenClaw to ask 1Password”: configure Secrets Management (exec provider)
Now the fun part: tell OpenClaw to fetch the API key by running op.
1) Find the absolute path to op
command -v op
If you installed with the commands above, it should be:
/usr/local/bin/op
2) Edit your OpenClaw config (openclaw.json)
Add a secrets exec provider + point your model provider’s apiKey to it.
Example (JSON5 style):
{
secrets: {
providers: {
onepassword_openai: {
source: "exec",
command: "/usr/local/bin/op",
args: ["read", "op://OpenClaw Automation/OpenAI API Key/password"],
// Important: pass the service token into the exec provider’s environment
passEnv: ["HOME", "OP_SERVICE_ACCOUNT_TOKEN"],
// op prints plain text; not JSON
jsonOnly: false,
},
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [{ id: "gpt-5", name: "gpt-5" }],
// This tells OpenClaw: resolve the apiKey by running the provider above
apiKey: { source: "exec", provider: "onepassword_openai", id: "value" },
},
},
},
}
This configuration pattern comes from OpenClaw’s Secrets docs (with minor Linux-friendly tweaks):
https://docs.openclaw.ai/gateway/secrets (optional reference; use it if you want to understand every knob)
Chapter 7 — “Restart and verify”: make sure OpenClaw can actually use it
Restart the gateway (if you’re using systemd):
sudo systemctl restart openclaw-gateway.service
sudo systemctl status openclaw-gateway.service --no-pager
Then trigger a simple model call in OpenClaw.
If secrets resolution fails, it will usually fail loudly (which is good: you find out immediately).
What you gained (the real reason this is worth doing)
- Your API key is no longer sitting in a config file on a server
- Rotation is now a “change vault value” operation, not a “hunt down where this key got pasted” operation
- Your automation is tied to a service account, not your personal 1Password identity
- You can split environments cleanly (create
OpenClaw Automation - Staging/Prodvaults later)
Quick FAQ (the stuff that trips people)
“op works in my SSH session, but OpenClaw can’t read secrets”
Your SSH shell has the token, but the systemd service doesn’t. Use Option B in Chapter 4.
“Where should I store the service account token?”
On servers, the most practical compromise is a root-only env file (/etc/openclaw/op.env) + systemd drop-in. Keep permissions tight (chmod 600) and rotate if exposed.
“Can I use my personal/private vault?”
Service accounts can’t access personal/private/default vaults. Use a dedicated shared vault like OpenClaw Automation.
If you want, tell me:
- which provider(s) you’re using (OpenAI only? Anthropic? OpenRouter?), and
- whether you want staging vs prod separation,
and I’ll adjust the config snippet + theop://...paths so you can paste it as-is.
Two likely causes here, and yes one of them can be “your OpenClaw is too old”.
0) First: what version are you on?
On the droplet, run and paste:
openclaw --version
secrets.providers is a newer config area. If your DigitalOcean 1‑click image is behind, the Dashboard will reject secrets: { ... } as an unknown/invalid config key.
1) Your snippet might not be valid as a standalone config edit
What you pasted starts with:
"secrets": { ... },
That’s only valid inside a surrounding { ... } object. If you pasted just that block into the Dashboard editor (replacing the whole config), it will be invalid.
Try it as a complete object (and remove the trailing comma at the end if the UI is being strict):
{
secrets: {
providers: {
onepassword_nvidia_key: {
source: "exec",
command: "/home/openclaw/.local/bin/op",
args: ["read", "op://Shared with Yug/NVIDIA_API_KEY/password"],
passEnv: ["HOME", "OP_SERVICE_ACCOUNT_TOKEN"],
jsonOnly: false,
},
},
},
}
(Full docs: https://docs.openclaw.ai/gateway/secrets)
2) command might be a symlink (and secrets exec provider rejects symlinks by default)
OpenClaw’s exec-secret provider requires command to be a regular file unless you explicitly allow symlinks.
Check:
ls -l /home/openclaw/.local/bin/op
If it shows op -> ... (symlink), either:
- point
commandat the real file path, or - add:
allowSymlinkCommand: true,
trustedDirs: ["/home/openclaw/.local"],
Docs note: by default symlink command paths are rejected (see config reference: https://docs.openclaw.ai/gateway/configuration-reference#secrets)
Fastest way to get the exact reason
When you hit “Save” in Dashboard, the Gateway usually logs the validation error. On the droplet:
openclaw logs --follow
Then try saving again and paste the first error line about config validation.
If you paste:
openclaw --version- the exact Dashboard “invalid config” message (or the log line)
…I can tell you which of the above it is in one shot.
That error explains the whole thing: your config currently contains an env-var substitution like:
apiKey: "${NVIDIA_API_KEY}"
…and ${…} substitutions are validated at config load. If the env var is missing/empty, OpenClaw refuses to load the config (and the Dashboard will call it “invalid config” too).
So it’s not your 1Password block that’s “wrong” yet — OpenClaw can’t even get past the missing NVIDIA_API_KEY.
Why root “works” but openclaw doesn’t
- Your gateway/config lives under
/home/openclaw/.openclaw/openclaw.json - Running CLI as
openclawloads that config → hits${NVIDIA_API_KEY}→ fails - Running as
rootis looking at a different home/config (or no config), so it doesn’t hit that substitution.
Fix (recommended): switch the model apiKey to a SecretRef (no ${NVIDIA_API_KEY} at all)
Update the NVIDIA provider section to use the exec secret provider:
{
secrets: {
providers: {
onepassword_nvidia_key: {
source: "exec",
command: "/home/openclaw/.local/bin/op",
args: ["read", "op://Shared with Yug/NVIDIA_API_KEY/password"],
passEnv: ["HOME", "OP_SERVICE_ACCOUNT_TOKEN"],
jsonOnly: false,
},
},
},
models: {
providers: {
"custom-integrate-api-nvidia-com": {
// replace the old "${NVIDIA_API_KEY}" with this:
apiKey: { source: "exec", provider: "onepassword_nvidia_key", id: "value" },
},
},
},
}
Once you remove ${NVIDIA_API_KEY}, the config can validate without needing that env var at all.
“Unblock me right now” workaround (temporary)
If you can’t edit the config easily because the Dashboard refuses to save, you can temporarily define the env var just to get past validation:
sudo -u openclaw bash -lc 'echo NVIDIA_API_KEY=tmp > /home/openclaw/.openclaw/.env'
Then reload/restart, save your new config (switch to SecretRef), and remove that .env line afterward.
Docs for env loading precedence (why ~/.openclaw/.env works): https://docs.openclaw.ai/help/environment
About your version (2026.2.12)
It’s pretty old, and DO 1‑click images often lag. Even if this specific error is config-related, I’d still upgrade soon.
Official update doc (safe path): https://docs.openclaw.ai/install/updating
Most common upgrade command path is re-running the installer:
curl -fsSL https://openclaw.ai/install.sh | bash
One thing to paste that will make this 100% certain
Paste the line from your openclaw.json where models.providers.custom-integrate-api-nvidia-com.apiKey is currently set — I’ll tell you exactly what to replace it with for your setup.
That error is not “can’t find the config” — it’s “found the config, but config load aborts because ${NVIDIA_API_KEY} is missing”.
Why Telegram is still working
Because your Gateway process is already running (and likely started at a time when NVIDIA_API_KEY was present in its environment). Your Telegram bot talks to that already-running Gateway.
Meanwhile, when you run openclaw as the openclaw user, the CLI tries to load the config fresh and immediately fails on ${NVIDIA_API_KEY}. CLI failure ≠Gateway instantly stops.
If the Gateway ever restarts/reloads config without NVIDIA_API_KEY available, then Telegram will break.
Why root behaves differently
Two common reasons:
rootmay be using a different$HOME→ different~/.openclaw/openclaw.json(or none), so it doesn’t hit that${NVIDIA_API_KEY}reference.- The systemd service may inject env vars from
/opt/openclaw.env, but your interactiveopenclawshell session does not.
About /opt/openclaw.env
OpenClaw does not automatically load arbitrary env files like /opt/openclaw.env for CLI runs. By default it loads:
.envin the current working directory
~/.openclaw/.env
Docs: https://docs.openclaw.ai/help/environment
So yes: it’s totally possible that the service sees NVIDIA_API_KEY (via systemd EnvironmentFile=), but your openclaw user CLI does not.
Fix it (ignore 1Password for now): make NVIDIA_API_KEY available to the openclaw config loader
Step 1) Put the key into ~openclaw/.openclaw/.env
This makes both the Gateway and the openclaw CLI happy (and doesn’t depend on /opt/openclaw.env).
sudo -u openclaw mkdir -p /home/openclaw/.openclaw
sudo -u openclaw nano /home/openclaw/.openclaw/.env
Add this line (no export needed):
NVIDIA_API_KEY=YOUR_KEY_HERE
Then lock permissions:
sudo chmod 600 /home/openclaw/.openclaw/.env
sudo chown openclaw:openclaw /home/openclaw/.openclaw/.env
Now re-test:
sudo -u openclaw openclaw --version
Step 2) Restart the gateway (so it’s not surviving on “old good env”)
sudo systemctl restart openclaw-gateway.service
sudo systemctl status openclaw-gateway.service --no-pager
If your service name differs, run:
systemctl list-units --type=service | grep -E 'openclaw-gateway'
Optional: confirm where the running service currently gets env vars from
This will tell you if systemd is using /opt/openclaw.env:
sudo systemctl show openclaw-gateway.service -p EnvironmentFiles -p Environment
If you paste the output of:
systemctl list-units --type=service | grep -E 'openclaw-gateway'sudo systemctl show openclaw-gateway.service -p EnvironmentFiles
…I can tell you the exact service name + whether /opt/openclaw.env is actually wired into the service or if it’s just “a file that exists”.
Cool, this helps — you’ve basically discovered you have two separate OpenClaw “worlds” on that box:
- root world:
/root/.openclaw/... - openclaw user world (the real one for your droplet service):
/home/openclaw/.openclaw/...
And the error is specifically: config contains ${NVIDIA_API_KEY} but when OpenClaw loads config, it can’t see NVIDIA_API_KEY in its environment.
1) About /root/.openclaw/openclaw.json — should you delete it?
Don’t delete yet. It’s not harmful, but it’s confusing because running openclaw ... as root will use that config and make it look like “things work” (or break) differently.
If you want to clean up safely, do a rename (reversible):
sudo mv /root/.openclaw /root/.openclaw.BAK
Only do a real delete once you’re 100% sure nothing uses it.
2) Why Telegram “still works” even though openclaw CLI errors
Because your Gateway service is already running and Telegram is talking to that running process.
The CLI (openclaw --version) tries to load config fresh every time, and that fails if the env var is missing. That doesn’t automatically kill the already-running gateway.
But: if the gateway restarts (or you try to save config and it triggers a reload), you’ll hit the same problem.
3) “Key is in /opt/openclaw.env and systemd links it — so why missing?”
Two common causes:
A) The env file format is wrong for systemd
EnvironmentFile= expects plain KEY=value lines.
If your file has export NVIDIA_API_KEY=... or spaces like NVIDIA_API_KEY = ..., systemd may ignore it.
Check the exact line:
sudo grep -n 'NVIDIA_API_KEY' /opt/openclaw.env
It should look like exactly:
NVIDIA_API_KEY=xxxxxxxx
(no export, no spaces around =)
B) You have more than one gateway service / mismatch between “what you think is running” and “what is running”
Let’s verify what the gateway service really has wired:
sudo systemctl show openclaw-gateway.service -p User -p EnvironmentFiles -p ExecStart
Paste that output.
4) Fast, reliable fix (even if /opt/openclaw.env is being weird)
Put the key in the OpenClaw-supported env location for the openclaw user:
sudo -u openclaw mkdir -p /home/openclaw/.openclaw
sudo -u openclaw nano /home/openclaw/.openclaw/.env
Add:
NVIDIA_API_KEY=xxxxxxxx
Then:
sudo chmod 600 /home/openclaw/.openclaw/.env
sudo chown openclaw:openclaw /home/openclaw/.openclaw/.env
Now this should stop erroring:
sudo -u openclaw openclaw --version
And then restart gateway so you’re not relying on “old good state”:
sudo systemctl restart openclaw-gateway.service
sudo systemctl status openclaw-gateway.service --no-pager
(Env loading rules reference: https://docs.openclaw.ai/help/environment)
Paste these 2 outputs and I’ll tell you exactly what’s going on (service vs env file)
sudo systemctl show openclaw-gateway.service -p User -p EnvironmentFiles -p ExecStartsudo grep -n 'NVIDIA_API_KEY' /opt/openclaw.env
Once we see those, we’ll know whether /opt/openclaw.env is actually being applied, and whether its formatting is the culprit.
You’re basically right about interactive shells: when you su - openclaw / SSH in as openclaw, that session will not automatically load /opt/openclaw.env, so ${NVIDIA_API_KEY} can be “missing” for the CLI even if the service has it.
But that doesn’t explain the Dashboard save failing.
If the Control UI says “invalid config” when you save, that validation is happening in the running Gateway. So the Gateway process itself is almost certainly missing NVIDIA_API_KEY at reload time (or you’re editing a config that still contains ${NVIDIA_API_KEY} but the Gateway can’t see that env var).
The big red flag in your output
This:
sudo systemctl show openclaw-gateway.service -p User -p EnvironmentFiles -p ExecStart
User=
…should also show ExecStart=... and usually EnvironmentFiles=.... Getting basically nothing strongly suggests you’re not looking at the unit that’s actually running the gateway (or it’s a user service / different name / different profile).
Let’s find the real gateway service + confirm env wiring (no secrets pasted)
Run these and paste output (they won’t include your key value):
1) List all OpenClaw-related services
sudo systemctl list-units --type=service --all | grep -i openclaw
2) For the service that looks like the gateway, show the important fields
(Replace <UNIT> with the exact service name you see above.)
sudo systemctl show <UNIT> -p Id -p LoadState -p ActiveState -p MainPID -p User -p ExecStart -p EnvironmentFiles -p FragmentPath -p DropInPaths
This will tell us definitively:
- which user it runs as (root vs openclaw)
- which config/state dir it’s likely using
- whether
/opt/openclaw.envis actually attached viaEnvironmentFile=
Why it “used to work” and now saving fails
Most common explanation: the gateway has been running for a while in a “good” state, but config save triggers a reload, and during reload OpenClaw re-validates ${NVIDIA_API_KEY} and now can’t see it → “invalid config”.
This can happen if:
- the service env file link got lost (service edited/updated)
- the unit name changed (profile service vs default)
/opt/openclaw.envis linked to a different unit than the one actually running- gateway is running as root, but your env/key/config live under openclaw (or vice versa)
Quick temporary unblock (so you can at least save config)
If you just want the error gone immediately (and you don’t want to touch systemd yet), put the key in the OpenClaw-supported location for the openclaw user:
sudo -u openclaw mkdir -p /home/openclaw/.openclaw
sudo -u openclaw nano /home/openclaw/.openclaw/.env
Add:
NVIDIA_API_KEY=...
That fixes CLI config load for openclaw and often helps the gateway too (if the gateway runs as openclaw and uses the default state dir).
Docs for env loading: https://docs.openclaw.ai/help/environment
Paste the outputs from the two systemctl ... commands and we’ll pin down exactly why the env file isn’t being seen by the gateway reload.
Hi! This thread is getting very long and the answers may not be of the highest quality as the context grows. Please create a new thread for new issues 🙂
Thank you!
You’re right — this thread has turned into a “multi-issue mega thread” (approvals → 1Password → env/config validation → systemd). The signal-to-noise is dropping.
Please open a new thread (in #help) with a focused title like:
“Control UI says invalid config + MissingEnvVarError for NVIDIA_API_KEY (DO 1‑click, 2026.2.12)”
And include just these bullets (copy/paste template):
- openclaw version(s)
openclaw --versionas root:openclaw --versionas openclaw:
-
Exact error (the MissingEnvVarError block)
-
Where the gateway config lives
- Does
/home/openclaw/.openclaw/openclaw.jsonexist? (yes/no)
- systemd unit details (no secrets)
Run and paste output:
sudo systemctl list-units --type=service --all | grep -i openclaw
and then (replace <UNIT> with the gateway unit you see):
sudo systemctl show <UNIT> -p Id -p ActiveState -p MainPID -p User -p ExecStart -p EnvironmentFiles -p FragmentPath -p DropInPaths
- Env file line format (redact value)
sudo grep -n 'NVIDIA_API_KEY' /opt/openclaw.env
In the new thread, we can solve it cleanly in ~2–3 messages.
(And once it’s fixed: please mark the solution so Answer Overflow indexes it for the next person.)
write a blog for auto-approvals
Auto‑Approvals in OpenClaw (without turning your server into a free‑for‑all)
You set up an agent. You ask it to do something simple.
And suddenly you’re approving 20 popups just to complete one task.
This post is the “make it practical” guide to Exec Approvals in OpenClaw: what they are, how auto‑approvals actually work, and how to configure them so you get fewer prompts without accidentally granting “run anything on my host”.
The 30‑second version (TL;DR)
- Exec approvals are the safety interlock for running commands on a real machine (
host=gatewayorhost=node). - “Auto‑approve” does not mean “trust all commands”. The safe way is:
security: "allowlist"ask: "on-miss"(prompts only when something isn’t allowlisted)- Maintain an allowlist of binary paths (not command text)
- If you truly want “no prompts ever”, that’s
ask: "off"— but you should keepsecurity: "allowlist"unless you fully understand the risk.
Docs (source of truth):
https://docs.openclaw.ai/tools/exec-approvals
Why approvals exist (the part people skip)
OpenClaw agents often run in a sandbox. That’s good.
But the moment you let an agent run curl, op, git, python, rm, etc. on the host, you’re giving it a lever that can:
- exfiltrate files
- modify system state
- install tooling
- persist changes
Exec approvals are there so you can say:
“I trust this agent to run these exact executables on this host — and nothing else.”
What “auto‑approvals” actually means in OpenClaw
There are three knobs that matter:
1) security (what’s allowed)
deny→ nothing runs on hostallowlist→ only allowlisted executables can runfull→ everything runs (equivalent to elevated / “just trust it”)
2) ask (when to prompt)
off→ never prompton-miss→ prompt only if not allowlisted (recommended)always→ prompt every time
3) Allowlist entries (what you trust)
Important: allowlist patterns match the resolved executable path (glob), not “command strings”.
So this works:
/usr/bin/curl~/.local/bin/op/opt/homebrew/bin/rg
And these do not work the way you think:
op item createexport OP_SERVICE_ACCOUNT_TOKEN=...cat ~/.secrets/foo
Docs line that matters: allowlist “Patterns should resolve to binary paths (basename-only entries are ignored).”
Where approvals live (and why people edit the wrong file)
Approvals are enforced on the execution host:
- If your agent runs
exec host=gateway→ approvals file is on the gateway machine - If your agent runs
exec host=node→ approvals file is on the node machine
File location on that host:
~/.openclaw/exec-approvals.json
Docs: https://docs.openclaw.ai/tools/exec-approvals
Setup: “prompts only once, then it’s automatic”
Step 1 — Decide your “comfort level”
Recommended default:
security: "allowlist"ask: "on-miss"askFallback: "deny"(if UI can’t be reached, don’t run random commands)
That means:
- Known tools run automatically
- New tools trigger a prompt once
- You can “Always allow” to grow the allowlist safely
Step 2 — Use the Control UI (best UX)
Go to:
Control UI → Nodes → Exec approvals
Pick the target:
- Gateway (approvals for gateway-host execution)
- or your Node (approvals for node-host execution)
Then set:
security = allowlistask = on-miss
Add allowlist patterns, then Save.
Docs: https://docs.openclaw.ai/tools/exec-approvals#control-ui-editing
Step 3 — The best way to build your allowlist (low effort)
Run your workflow once.
When prompted, click:
- Allow once (just this time), or
- Always allow (adds the resolved executable path to the allowlist)
This is the fastest way to avoid “I typed the wrong path pattern” problems.
Example: why one task creates “tons of approvals”
Let’s say the agent runs:
curl ... && unzip ... && mv ... && chmod ... && op item create ...
In allowlist mode, each segment uses a different executable:
/usr/bin/curl/usr/bin/unzip/usr/bin/mv/usr/bin/chmod~/.local/bin/op(or whereveropis installed)
If only curl is allowlisted, you’ll get prompted again at unzip, again at mv, etc.
So the fix isn’t “allow curl && unzip ...”.
The fix is “allow the binaries involved”.
CLI workflow (if you prefer terminal over UI)
Docs: https://docs.openclaw.ai/cli/approvals
Common commands:
openclaw approvals get
openclaw approvals get --gateway
openclaw approvals get --node <id|name|ip>
Add allowlist entries:
openclaw approvals allowlist add --agent main "/usr/bin/curl"
openclaw approvals allowlist add --agent main "/usr/bin/unzip"
openclaw approvals allowlist add --agent main "/usr/bin/mv"
openclaw approvals allowlist add --agent main "/usr/bin/chmod"
openclaw approvals allowlist add --agent main "~/.local/bin/op"
Replace approvals from a file:
openclaw approvals set --file ./exec-approvals.json
openclaw approvals set --gateway --file ./exec-approvals.json
openclaw approvals set --node <id|name|ip> --file ./exec-approvals.json
“Sometimes I don’t see the approval popup” (fix: forward approvals to chat)
If the Control UI prompt is flaky/missed, you can forward approvals to a chat target and approve with /approve.
Docs: https://docs.openclaw.ai/tools/exec-approvals#approval-forwarding-to-chat-channels
Config example:
{
approvals: {
exec: {
enabled: true,
mode: "targets", // "session" | "targets" | "both"
agentFilter: ["main"],
targets: [
{ channel: "telegram", to: "123456789" }
]
}
}
}
Then in chat:
/approve <id> allow-once
/approve <id> allow-always
/approve <id> deny
This is a huge quality-of-life improvement if you’re not always staring at the dashboard.
Safe bins: the “auto-allow tiny utilities” shortcut (optional)
OpenClaw has tools.exec.safeBins for stdin-only utilities (think jq, head, tail), where it rejects file paths/flags that could turn it into a file reader.
This reduces prompts for harmless pipeline filters.
Key warning from the docs: don’t add interpreters (python3, node, bash, etc.) to safe bins.
Docs section: https://docs.openclaw.ai/tools/exec-approvals#safe-bins-stdin-only
Common mistakes (aka “why it still prompts”)
- You allowlisted command text, not a binary path
- “
op item create” won’t match; allowlist must match the resolved executable path.
- Wrong host
- You edited gateway approvals, but the agent executes on a node (or vice versa).
- Wrong agent scope
- Allowlists are per-agent (
agents.main, etc.). Make sure you’re editing the right agent.
- You accidentally routed through a shell
- Allowlisting
/bin/bashor/bin/sheffectively opens the door wide (becausebash -lc "anything").
- Prefer allowlisting the actual tool binaries instead.
Final recommendation (sane defaults)
For most people who want “auto approvals” but still want safety:
security: "allowlist"ask: "on-miss"- Build allowlist by using “Always allow” on first run
- Forward approvals to chat if you miss UI popups
That gives you: quiet automation + controlled trust.
If you want, tell me: