#Set Up Exec
1 messages · Page 1 of 1 (latest)
To get exec (shell access) working from your main Discord agent on a local Mac mini, you need to line up three things:
- Tool policy must allow
exec(and usuallyprocess) - Sandboxing must be in the mode you expect (exec runs in Docker vs on your Mac)
- If you want host/macOS exec while sandboxed, you must configure elevated + approvals (or disable sandboxing)
Docs you’ll want open while doing this:
- Exec tool: https://docs.openclaw.ai/tools/exec
- Exec approvals: https://docs.openclaw.ai/tools/exec-approvals
- Elevated mode: https://docs.openclaw.ai/tools/elevated
- Sandbox vs tool policy vs elevated: https://docs.openclaw.ai/gateway/sandbox-vs-tool-policy-vs-elevated
- Config overview: https://docs.openclaw.ai/gateway/configuration
0) First: tell me your version (matters after updates)
Run on the Mac mini and paste the output:
openclaw --version
openclaw sandbox explain --json
That immediately shows whether your Discord channel session is sandboxed, and whether exec is blocked by tool policy.
1) Ensure your tool policy actually allows exec
If you have any tools.allow set, it becomes an allowlist (everything else is blocked unless included). Easiest safe baseline is using a profile that includes runtime tools:
tools.profile: "coding"includesexec+processviagroup:runtime(see config reference)tools.profile: "messaging"does not includeexec
Recommended baseline (simple)
In ~/.openclaw/openclaw.json:
{
tools: {
profile: "coding", // includes group:runtime (exec, process) + fs + sessions + memory
},
}
If you must stay on messaging, then you need an explicit allowlist that includes runtime:
{
tools: {
profile: "messaging",
allow: ["group:messaging", "group:runtime"], // plus anything else you need
},
}
(And make sure you’re not using fake entries like discord.message / web.fetch — tools.allow must be real tool names or group:*.)
2) Decide where exec should run: Docker sandbox vs your Mac
Option A (most straightforward): run exec on the Mac host
Disable sandboxing for the agent (or globally):
{
agents: {
defaults: {
sandbox: { mode: "off" } // exec runs on the Mac host
}
}
}
This is the “I want my Discord bot to run shell commands on this Mac mini” mode.
Option B (safer): keep sandboxing, only escape to host when needed
If you use:
{
agents: { defaults: { sandbox: { mode: "non-main" } } }
}
…then Discord channels are usually “non-main” and will be sandboxed, meaning exec runs inside Docker unless you use elevated or set tools.exec.host="gateway".
3) If sandboxed: enable Elevated (host exec) for your Discord user
Elevated is the clean way to say “allow me to escape the sandbox to host exec”.
Add your Discord user id to elevated allowFrom:
{
tools: {
elevated: {
enabled: true,
allowFrom: {
discord: ["id:YOUR_DISCORD_USER_ID"]
}
}
}
}
Now in Discord, in the thread/channel, send:
/elevated on(host exec, approvals still apply)- or
/elevated full(host exec + skips approvals)
Docs: https://docs.openclaw.ai/tools/elevated
Tip: you can get your Discord user id by enabling Developer Mode → right-click your user → Copy ID.
4) Approvals / allowlist (recommended if you don’t want “anything goes”)
If you want host exec but with guardrails, keep:
tools.exec.security: "allowlist"tools.exec.ask: "on-miss"(oralways)
Config knobs (docs): https://docs.openclaw.ai/tools/exec and https://docs.openclaw.ai/tools/exec-approvals
Then add allowlisted binaries from the Mac mini:
openclaw approvals get
openclaw approvals allowlist add --agent main "/usr/bin/uname"
openclaw approvals allowlist add --agent main "/bin/ls"
openclaw approvals allowlist add --agent main "/usr/bin/git"
CLI docs: https://docs.openclaw.ai/cli/approvals
If you’d rather skip prompts entirely, set ask: "off" and keep a tight allowlist.
5) Mac mini gotchas (PATH / Homebrew)
On macOS the daemon environment can be minimal (launchd), but host=gateway exec merges your login-shell PATH. If you still can’t find Homebrew bins, add:
{
tools: {
exec: {
pathPrepend: ["/opt/homebrew/bin", "/usr/local/bin"]
}
}
}
(That applies for gateway + sandbox exec; node hosts need their service env fixed instead.)
6) Quick “does it work?” tests (from Discord)
Ask your agent something explicit like:
Use exec to run
uname -aand paste the output.
If backgrounded commands matter, ensure process is allowed too (it’s part of group:runtime).
If you paste these 3 outputs, I’ll tell you exactly which branch you’re on
openclaw --versionopenclaw sandbox explain --jsonopenclaw config get tools.profile tools.allow agents.defaults.sandbox.mode tools.elevated tools.exec
2026.3.2
{
"docsUrl": "https://docs.openclaw.ai/sandbox",
"agentId": "main",
"sessionKey": "agent:main:main",
"mainSessionKey": "agent:main:main",
"sandbox": {
"mode": "off",
"scope": "agent",
"perSession": false,
"workspaceAccess": "none",
"workspaceRoot": "/Users/bigboy/.openclaw/sandboxes",
"sessionIsSandboxed": false,
"tools": {
"allow": [
"exec",
"process",
"read",
"write",
"edit",
"apply_patch",
"image",
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"subagents",
"session_status"
],
"deny": [
"browser",
"canvas",
"nodes",
"cron",
"gateway",
"telegram",
"whatsapp",
"discord",
"irc",
"googlechat",
"slack",
"signal",
"imessage"
],
"sources": {
"allow": {
"source": "default",
"key": "tools.sandbox.tools.allow"
},
"deny": {
"source": "default",
"key": "tools.sandbox.tools.deny"
}
}
}
},
"elevated": {
"enabled": true,
"channel": "webchat",
"allowedByConfig": false,
"alwaysAllowedByConfig": false,
"allowFrom": {
"global": []
},
"failures": [
{
"gate": "allowFrom",
"key": "tools.elevated.allowFrom.webchat"
}
]
},
"fixIt": [
"tools.sandbox.tools.allow",
"tools.sandbox.tools.deny",
"agents.list[].tools.sandbox.tools.allow",
"agents.list[].tools.sandbox.tools.deny",
"tools.elevated.enabled",
"tools.elevated.allowFrom.webchat"
Your sandbox explain output is actually good news: sandbox mode is off and the sandbox tool allowlist already includes exec + process, so you’re not fighting Docker right now.
Two important gotchas from what you pasted:
- That output is for
webchat(Control UI), not Discord — so any “/exec /approve /elevated doesn’t work in Discord” issues can still be Discord command authorization / allowlists, not sandboxing. - On recent builds, the thing that usually blocks host shell access even when
execis “allowed” is exec approvals / allowlist security, not the tool allowlist.
Below is the “make my main Discord agent run shell on my Mac mini” playbook.
A) Verify the Discord session + whether commands are authorized
In the Discord channel/thread, run:
/status(in Discord)
Paste what it prints (it includes the session key + whether you’re authorized for directives).
Then on the Mac mini you can correlate with:
openclaw sandbox explain --session "<SESSION_KEY_FROM_/status>" --json
This matters because /exec, /approve, /elevated only apply for authorized senders.
B) Make exec actually run host commands (choose 1 of 2 security models)
Option 1 (simple / “I trust this server”): allow full host exec
Put this in ~/.openclaw/openclaw.json:
{
tools: {
exec: {
host: "gateway",
security: "full",
ask: "off",
},
},
}
Now your agent can run arbitrary shell commands on the Mac mini via exec.
Strong warning: only do this if your Discord setup is locked down (private server, allowlisted users, etc.).
Option 2 (recommended): allowlist + approvals
Keep security: "allowlist" and then explicitly allow binaries you want.
Config (no prompts, strict allowlist):
{
tools: {
exec: {
host: "gateway",
security: "allowlist",
ask: "off",
},
},
}
Then add allowlist entries on the Mac mini:
openclaw approvals get
openclaw approvals allowlist add --agent main "/usr/bin/uname"
openclaw approvals allowlist add --agent main "/bin/ls"
openclaw approvals allowlist add --agent main "/usr/bin/sw_vers"
openclaw approvals allowlist add --agent main "/usr/bin/git"
Docs: https://docs.openclaw.ai/tools/exec-approvals and https://docs.openclaw.ai/cli/approvals
If you do want prompts sometimes, use:
ask: "on-miss"(prompt only when not allowlisted)- and then approve from chat with
/approve <id> allow-once|allow-always|deny(see slash commands doc)
C) Lock Discord down so this doesn’t become a remote-RCE-in-public
At minimum, restrict your guild to your user id.
From the Discord docs (private server pattern):
{
channels: {
discord: {
groupPolicy: "allowlist",
guilds: {
YOUR_SERVER_ID: {
requireMention: true,
users: ["YOUR_USER_ID"],
},
},
},
},
}
Docs: https://docs.openclaw.ai/channels/discord
D) Make sure you are authorized to use directives in Discord (/exec, /approve, etc.)
If you want to guarantee that only you can run command/directive messages, set:
{
commands: {
allowFrom: {
discord: ["id:YOUR_USER_ID"],
},
},
}
Note: once commands.allowFrom is set, it becomes the only allowlist for directives/commands (by design).
Docs: https://docs.openclaw.ai/tools/slash-commands
E) Quick functional test (Discord)
After the config reloads, ask in Discord:
“Use exec to run
uname -aand paste the output.”
If that still fails, paste the exact error plus:
openclaw approvals get
openclaw config get tools.exec tools.profile tools.allow tools.deny commands.allowFrom channels.discord.guilds
…and I’ll tell you precisely which gate is blocking (tool policy vs command auth vs approvals vs host selection).
tools.profile: messaging
tools.profile: "messaging" is the reason: that profile does not include exec/process, so your Discord agent will never be allowed to call the shell (even if sandbox explain showed exec under sandbox tool policy — that’s a different layer).
Fix: switch the tool profile for your main agent
You’ve got two sane choices:
Option 1 (recommended): enable “coding” tools for agent main only
Keeps the rest of the system on messaging, but gives main exec+process (and fs/session/memory tools).
// ~/.openclaw/openclaw.json
{
tools: { profile: "messaging" },
agents: {
list: [
{
id: "main",
tools: { profile: "coding" } // includes group:runtime => exec, process
}
]
}
}
Option 2: go “full” for main, then explicitly deny scary tools
If you want exec but still don’t want browser/web/etc:
{
tools: { profile: "messaging" },
agents: {
list: [
{
id: "main",
tools: {
profile: "full",
deny: ["browser", "canvas", "web_search", "web_fetch", "nodes"]
}
}
]
}
}
Docs reference for tool profiles/groups: https://docs.openclaw.ai/gateway/configuration-reference (Tool profiles)
Next: make host exec behave how you want (otherwise you may hit approvals/allowlist)
Since you’re on a Mac mini and want the real shell:
“Just run on my Mac” (wide open)
{
tools: {
exec: { host: "gateway", security: "full", ask: "off" }
}
}
“Allowlist only” (safer)
{
tools: {
exec: { host: "gateway", security: "allowlist", ask: "off" }
}
}
Then add allowed binaries:
openclaw approvals allowlist add --agent main "/usr/bin/uname"
openclaw approvals allowlist add --agent main "/bin/ls"
openclaw approvals allowlist add --agent main "/usr/bin/sw_vers"
Docs:
- Exec: https://docs.openclaw.ai/tools/exec
- Exec approvals: https://docs.openclaw.ai/tools/exec-approvals
- CLI approvals: https://docs.openclaw.ai/cli/approvals
Quick verification (in Discord)
- Run
/status(confirms which session you’re in) - Ask: “Use exec to run
uname -aand paste output.”
If it still won’t run, paste:
- the exact error text you see in Discord
openclaw approvals getopenclaw config get tools.profile agents.list tools.exec
"/status = You are not authorized to use this command.
That error is the blocker right now: if you’re not authorized for commands, then all the stuff you need for exec setup (/status, /exec, /approve, /elevated, etc.) won’t work in Discord.
Fix command authorization (Discord) first
Open ~/.openclaw/openclaw.json on the Mac mini and add your Discord user id to commands.allowFrom.
{
commands: {
// lock command/directive usage to just you
allowFrom: {
discord: ["id:YOUR_DISCORD_USER_ID"]
}
}
}
How to get YOUR_DISCORD_USER_ID:
- Discord → User Settings → Advanced → Developer Mode ON
- Right-click your avatar → Copy User ID
Then restart (or let hot-reload apply, but restart is cleaner for debugging):
openclaw gateway restart
Now try in Discord again:
/status(should work once you’re authorized)
Docs: https://docs.openclaw.ai/tools/slash-commands (see commands.allowFrom)