feat: add exec host approvals flow
This commit is contained in:
@@ -6,9 +6,8 @@ read_when:
|
||||
# Elevated Mode (/elevated directives)
|
||||
|
||||
## What it does
|
||||
- Elevated mode allows the exec tool to run with elevated privileges when the feature is available and the sender is approved.
|
||||
- The bash chat command (`!`; `/bash` alias) uses the same `tools.elevated` allowlists because it always runs on the host.
|
||||
- **Optional for sandboxed agents**: elevated only changes behavior when the agent is running in a sandbox. If the agent already runs unsandboxed, elevated is effectively a no-op.
|
||||
- `/elevated on` is a **shortcut** for `exec.host=gateway` + `exec.security=full`.
|
||||
- Only changes behavior when the agent is **sandboxed** (otherwise exec already runs on the host).
|
||||
- Directive forms: `/elevated on`, `/elevated off`, `/elev on`, `/elev off`.
|
||||
- Only `on|off` are accepted; anything else returns a hint and does not change state.
|
||||
|
||||
@@ -17,18 +16,9 @@ read_when:
|
||||
- **Per-session state**: `/elevated on|off` sets the elevated level for the current session key.
|
||||
- **Inline directive**: `/elevated on` inside a message applies to that message only.
|
||||
- **Groups**: In group chats, elevated directives are only honored when the agent is mentioned. Command-only messages that bypass mention requirements are treated as mentioned.
|
||||
- **Host execution**: elevated runs `exec` on the host (bypasses sandbox).
|
||||
- **Unsandboxed agents**: when there is no sandbox to bypass, elevated does not change where `exec` runs.
|
||||
- **Host execution**: elevated forces `exec` onto the gateway host with full security.
|
||||
- **Unsandboxed agents**: no-op for location; only affects gating, logging, and status.
|
||||
- **Tool policy still applies**: if `exec` is denied by tool policy, elevated cannot be used.
|
||||
- **Not skill-scoped**: elevated cannot be limited to a specific skill; it only changes `exec` location.
|
||||
|
||||
Note:
|
||||
- Sandbox on: `/elevated on` runs that `exec` command on the host.
|
||||
- Sandbox off: `/elevated on` does not change execution (already on host).
|
||||
|
||||
## When elevated matters
|
||||
- Only impacts `exec` when the agent is running sandboxed (it drops the sandbox for that command).
|
||||
- For unsandboxed agents, elevated does not change execution; it only affects gating, logging, and status.
|
||||
|
||||
## Resolution order
|
||||
1. Inline directive on the message (applies only to that message).
|
||||
@@ -38,7 +28,7 @@ Note:
|
||||
## Setting a session default
|
||||
- Send a message that is **only** the directive (whitespace allowed), e.g. `/elevated on`.
|
||||
- Confirmation reply is sent (`Elevated mode enabled.` / `Elevated mode disabled.`).
|
||||
- If elevated access is disabled or the sender is not on the approved allowlist, the directive replies with an actionable error (runtime sandboxed/direct + failing config key paths) and does not change session state.
|
||||
- If elevated access is disabled or the sender is not on the approved allowlist, the directive replies with an actionable error and does not change session state.
|
||||
- Send `/elevated` (or `/elevated:`) with no argument to see the current elevated level.
|
||||
|
||||
## Availability + allowlists
|
||||
|
||||
@@ -1,39 +1,88 @@
|
||||
---
|
||||
summary: "Exec approvals, allowlists, and sandbox escape prompts in the macOS app"
|
||||
summary: "Exec approvals, allowlists, and sandbox escape prompts"
|
||||
read_when:
|
||||
- Configuring exec approvals or allowlists
|
||||
- Implementing exec approval UX in the macOS app
|
||||
- Reviewing sandbox escape prompts and implications
|
||||
---
|
||||
|
||||
# Exec approvals (macOS app)
|
||||
# Exec approvals
|
||||
|
||||
Exec approvals are the **macOS companion app** guardrail for running host
|
||||
commands from sandboxed agents. Think of it as a per-agent “run this on my Mac”
|
||||
approval layer: the agent asks, the app decides, and the command runs (or not).
|
||||
This is **in addition** to tool policy and elevated gating; all of those checks
|
||||
must pass before a command can run.
|
||||
Exec approvals are the **companion app guardrail** for letting a sandboxed agent run
|
||||
commands on a real host (`gateway` or `node`). Think of it like a safety interlock:
|
||||
commands are allowed only when policy + allowlist + (optional) user approval all agree.
|
||||
Exec approvals are **in addition** to tool policy and elevated gating.
|
||||
|
||||
If you are **not** running the macOS companion app, exec approvals are
|
||||
unavailable and `system.run` requests will be rejected with a message that a
|
||||
companion app is required.
|
||||
If the companion app UI is **not available**, any request that requires a prompt is
|
||||
resolved by the **ask fallback** (default: deny).
|
||||
|
||||
## Settings
|
||||
## Where it applies
|
||||
|
||||
In the macOS app, each agent has an **Exec approvals** setting:
|
||||
Exec approvals are enforced locally on the execution host:
|
||||
- **gateway host** → `clawdbot` process on the gateway machine
|
||||
- **node host** → node runner (macOS companion app or headless node)
|
||||
|
||||
- **Deny**: block all host exec requests from the agent.
|
||||
- **Always ask**: show a confirmation dialog for each host exec request.
|
||||
- **Always allow**: run host exec requests without prompting.
|
||||
## Settings and storage
|
||||
|
||||
Optional toggles:
|
||||
- **Auto-allow skill CLIs**: when enabled, CLIs referenced by known skills are
|
||||
treated as allowlisted (see below).
|
||||
Approvals live in a local JSON file:
|
||||
|
||||
`~/.clawdbot/exec-approvals.json`
|
||||
|
||||
Example schema:
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"socket": {
|
||||
"path": "~/.clawdbot/exec-approvals.sock",
|
||||
"token": "base64url-token"
|
||||
},
|
||||
"defaults": {
|
||||
"security": "deny",
|
||||
"ask": "on-miss",
|
||||
"askFallback": "deny",
|
||||
"autoAllowSkills": false
|
||||
},
|
||||
"agents": {
|
||||
"main": {
|
||||
"security": "allowlist",
|
||||
"ask": "on-miss",
|
||||
"askFallback": "deny",
|
||||
"autoAllowSkills": true,
|
||||
"allowlist": [
|
||||
{
|
||||
"pattern": "~/Projects/**/bin/rg",
|
||||
"lastUsedAt": 1737150000000,
|
||||
"lastUsedCommand": "rg -n TODO",
|
||||
"lastResolvedPath": "/Users/user/Projects/.../bin/rg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Policy knobs
|
||||
|
||||
### Security (`exec.security`)
|
||||
- **deny**: block all host exec requests.
|
||||
- **allowlist**: allow only allowlisted commands.
|
||||
- **full**: allow everything (equivalent to elevated).
|
||||
|
||||
### Ask (`exec.ask`)
|
||||
- **off**: never prompt.
|
||||
- **on-miss**: prompt only when allowlist does not match.
|
||||
- **always**: prompt on every command.
|
||||
|
||||
### Ask fallback (`askFallback`)
|
||||
If a prompt is required but no UI is reachable, fallback decides:
|
||||
- **deny**: block.
|
||||
- **allowlist**: allow only if allowlist matches.
|
||||
- **full**: allow.
|
||||
|
||||
## Allowlist (per agent)
|
||||
|
||||
The allowlist is **per agent**. If multiple agents exist, you can switch which
|
||||
agent’s allowlist you’re editing. Entries are path-based and support **globs**.
|
||||
Allowlists are **per agent**. If multiple agents exist, switch which agent you’re
|
||||
editing in the macOS app. Patterns are **case-insensitive glob matches**.
|
||||
|
||||
Examples:
|
||||
- `~/Projects/**/bin/bird`
|
||||
@@ -41,66 +90,44 @@ Examples:
|
||||
- `/opt/homebrew/bin/rg`
|
||||
|
||||
Each allowlist entry tracks:
|
||||
- **last used** (timestamp)
|
||||
- **last used** timestamp
|
||||
- **last used command**
|
||||
- **last used path** (resolved absolute path)
|
||||
- **last seen metadata** (hash/version/mtime when available)
|
||||
- **last resolved path**
|
||||
|
||||
## How matching works
|
||||
## Auto-allow skill CLIs
|
||||
|
||||
1) Parse the command to determine the executable (first token).
|
||||
2) Resolve the executable to an absolute path using `PATH`.
|
||||
3) Match against denylist (if present) → **deny**.
|
||||
4) Match against allowlist → **allow**.
|
||||
5) Otherwise follow the Exec approvals policy (deny/ask/allow).
|
||||
|
||||
If **auto-allow skill CLIs** is enabled, each installed skill can contribute one
|
||||
or more allowlist entries. A skill-based allowlist entry only auto-allows when:
|
||||
- the resolved path matches, and
|
||||
- the binary hash/version matches the last approved record (if tracked).
|
||||
|
||||
If the binary changes (new hash/version), the command falls back to **Ask** so
|
||||
the user can re-approve.
|
||||
When **Auto-allow skill CLIs** is enabled, executables referenced by known skills
|
||||
are treated as allowlisted (node hosts only). Disable this if you want strict
|
||||
manual allowlists.
|
||||
|
||||
## Approval flow
|
||||
|
||||
When the policy is **Always ask** (or when a binary has changed), the macOS app
|
||||
shows a confirmation dialog. The dialog should include:
|
||||
When a prompt is required, the companion app displays a confirmation dialog with:
|
||||
- command + args
|
||||
- cwd
|
||||
- environment overrides (diff)
|
||||
- policy + rule that matched (if any)
|
||||
- agent id
|
||||
- resolved executable path
|
||||
- host + policy metadata
|
||||
|
||||
Actions:
|
||||
- **Allow once** → run now
|
||||
- **Always allow** → add/update allowlist entry + run
|
||||
- **Always allow** → add to allowlist + run
|
||||
- **Deny** → block
|
||||
|
||||
When approved, the command runs **in the background** and the agent receives
|
||||
system events as it starts and completes.
|
||||
|
||||
## System events
|
||||
|
||||
The agent receives system messages for observability and recovery:
|
||||
Exec lifecycle is surfaced as system messages:
|
||||
- `exec.started`
|
||||
- `exec.finished`
|
||||
- `exec.denied`
|
||||
|
||||
- `exec.started` — command accepted and launched
|
||||
- `exec.finished` — command completed (exit code + output)
|
||||
- `exec.denied` — command blocked (policy or denylist)
|
||||
|
||||
These are **system messages**; no extra agent tool call is required to resume.
|
||||
These are posted to the agent’s session after the node reports the event.
|
||||
|
||||
## Implications
|
||||
|
||||
- **Always allow** is powerful: the agent can run any host command without a
|
||||
prompt. Prefer allowlisting trusted CLIs instead.
|
||||
- **Ask** keeps you in the loop while still allowing fast approvals.
|
||||
- Per-agent allowlists prevent one agent’s approval set from leaking into others.
|
||||
|
||||
## Storage
|
||||
|
||||
Allowlists and approval settings are stored **locally in the macOS app** (SQLite
|
||||
is a good fit). The Markdown docs describe behavior; they are not the storage
|
||||
mechanism.
|
||||
- **full** is powerful; prefer allowlists when possible.
|
||||
- **ask** keeps you in the loop while still allowing fast approvals.
|
||||
- Per-agent allowlists prevent one agent’s approvals from leaking into others.
|
||||
|
||||
Related:
|
||||
- [Exec tool](/tools/exec)
|
||||
|
||||
@@ -14,21 +14,36 @@ Background sessions are scoped per agent; `process` only sees sessions from the
|
||||
## Parameters
|
||||
|
||||
- `command` (required)
|
||||
- `workdir` (defaults to cwd)
|
||||
- `env` (key/value overrides)
|
||||
- `yieldMs` (default 10000): auto-background after delay
|
||||
- `background` (bool): background immediately
|
||||
- `timeout` (seconds, default 1800): kill on expiry
|
||||
- `pty` (bool): run in a pseudo-terminal when available (TTY-only CLIs, coding agents, terminal UIs)
|
||||
- `elevated` (bool): run on host if elevated mode is enabled/allowed (only changes behavior when the agent is sandboxed)
|
||||
- Need a fully interactive session? Use `pty: true` and the `process` tool for stdin/output.
|
||||
Note: `elevated` is ignored when sandboxing is off (exec already runs on the host).
|
||||
- `host` (`sandbox | gateway | node`): where to execute
|
||||
- `security` (`deny | allowlist | full`): enforcement mode for `gateway`/`node`
|
||||
- `ask` (`off | on-miss | always`): approval prompts for `gateway`/`node`
|
||||
- `node` (string): node id/name for `host=node`
|
||||
- `elevated` (bool): alias for `host=gateway` + `security=full` when sandboxed and allowed
|
||||
|
||||
Notes:
|
||||
- `host` defaults to `sandbox`.
|
||||
- `elevated` is ignored when sandboxing is off (exec already runs on the host).
|
||||
- `gateway`/`node` approvals are controlled by `~/.clawdbot/exec-approvals.json`.
|
||||
- `node` requires a paired node (macOS companion app).
|
||||
- If multiple nodes are available, set `exec.node` or `tools.exec.node` to select one.
|
||||
|
||||
## Config
|
||||
|
||||
- `tools.exec.notifyOnExit` (default: true): when true, backgrounded exec sessions enqueue a system event and request a heartbeat on exit.
|
||||
- `tools.exec.host` (default: `sandbox`)
|
||||
- `tools.exec.security` (default: `deny`)
|
||||
- `tools.exec.ask` (default: `on-miss`)
|
||||
- `tools.exec.node` (default: unset)
|
||||
|
||||
## Exec approvals (macOS app)
|
||||
|
||||
Sandboxed agents can require per-request approval before `exec` runs on the host.
|
||||
Sandboxed agents can require per-request approval before `exec` runs on the gateway or node host.
|
||||
See [Exec approvals](/tools/exec-approvals) for the policy, allowlist, and UI flow.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -169,15 +169,19 @@ Core parameters:
|
||||
- `background` (immediate background)
|
||||
- `timeout` (seconds; kills the process if exceeded, default 1800)
|
||||
- `elevated` (bool; run on host if elevated mode is enabled/allowed; only changes behavior when the agent is sandboxed)
|
||||
- `host` (`sandbox | gateway | node`)
|
||||
- `security` (`deny | allowlist | full`)
|
||||
- `ask` (`off | on-miss | always`)
|
||||
- `node` (node id/name for `host=node`)
|
||||
- Need a real TTY? Set `pty: true`.
|
||||
|
||||
Notes:
|
||||
- Returns `status: "running"` with a `sessionId` when backgrounded.
|
||||
- Use `process` to poll/log/write/kill/clear background sessions.
|
||||
- If `process` is disallowed, `exec` runs synchronously and ignores `yieldMs`/`background`.
|
||||
- `elevated` is gated by `tools.elevated` plus any `agents.list[].tools.elevated` override (both must allow) and runs on the host.
|
||||
- `elevated` is gated by `tools.elevated` plus any `agents.list[].tools.elevated` override (both must allow) and is an alias for `host=gateway` + `security=full`.
|
||||
- `elevated` only changes behavior when the agent is sandboxed (otherwise it’s a no-op).
|
||||
- macOS app approvals/allowlists: [Exec approvals](/tools/exec-approvals).
|
||||
- gateway/node approvals and allowlists: [Exec approvals](/tools/exec-approvals).
|
||||
|
||||
### `process`
|
||||
Manage background exec sessions.
|
||||
|
||||
Reference in New Issue
Block a user