Description:
What I'm trying to do: Message my Discord bot to run commands on a remote headless node (Oracle Cloud VPS), and either have trusted commands run, or get an approval prompt I can actually action.
Setup:
OpenClaw 2026.5.7
Gateway: Docker on Unraid (ghcr.io/openclaw/openclaw:latest)
One node: headless VPS, openclaw-node.service as a systemd user service, paired + connected, advertises system.run
Channel: Discord DM
What happened: Every node.invoke for a non-allowlisted command returned SYSTEM_RUN_DENIED: approval required instantly — no exec.approval.waitDecision in the gateway logs, meaning no approval was ever created. So no card appeared in Discord or the Control UI, because there was no event to forward.
Root cause I found: The node's local ~/.openclaw/exec-approvals.json had defaults: { security: "full", ask: "always", askFallback: "deny" }. On a headless node there's no companion app / prompt surface, so ask: "always" → can't prompt → askFallback: "deny" fires before the gateway's approval handshake ever runs. The gateway's ask: "on-miss" in the invoke params is overridden by the node-local ask: "always", per the docs.
What fixed the deny: Changed the node's exec-approvals.json to security: "allowlist", ask: "on-miss", with an allowlist array for the commands I actually use. Restarted openclaw-node.service. Allowlisted commands now run with no prompt.
Questions for the team:
Is there a supported way to get a working approval prompt (Discord card or Control UI) for non-allowlisted commands on a headless node? ask: "always" + askFallback: "deny" seems to make that structurally impossible since the node denies before any approval is broadcast.
For the native Discord card path (channels.discord.execApprovals), is the correct config to disable approvals.exec and rely only on channels.discord.execApprovals? I've seen conflicting setups.
Should the docs call out that node-local ask: "always" on a headless node = guaranteed deny? It's a sharp edge.