mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
122 lines
6.4 KiB
Markdown
122 lines
6.4 KiB
Markdown
---
|
|
description: >-
|
|
Human-in-the-loop consent for side-effecting tool calls. The agent parks any
|
|
risky action until you approve it, and fails closed if you don't.
|
|
icon: shield-check
|
|
---
|
|
|
|
# Approval Gate
|
|
|
|
The Approval Gate is the checkpoint between the agent and the outside world. Whenever the agent wants to run a tool that has a real-world effect (post to Slack, send an email, create a calendar event, run a shell command, install a package), the gate intercepts the call, shows you exactly what's about to happen, and waits for your decision before anything runs.
|
|
|
|
It's on by default. Nothing with an external effect leaves your machine in an interactive chat without you saying yes.
|
|
|
|
***
|
|
|
|
## What triggers a prompt
|
|
|
|
Every acting tool call is classified into a **command class**, and your **autonomy tier** decides whether that class runs silently, prompts, or is blocked.
|
|
|
|
| Command class | What it covers |
|
|
| ------------- | ---------------------------------------------------------- |
|
|
| Read | Provably read-only / observational (curated allowlist) |
|
|
| Write | State-changing; the fail-closed default for anything unrecognized |
|
|
| Network | Reaches the network (curl, wget, ssh, scp, …) |
|
|
| Install | Installs an OS or global language package |
|
|
| Destructive | Catastrophic / irreversible / privilege-escalating |
|
|
|
|
The tier comes from **Settings → Agent access** (`[autonomy].level`):
|
|
|
|
| Tier | Read | Write | Network / Install / Destructive |
|
|
| -------------- | ----- | ------ | ------------------------------- |
|
|
| Read-only | Allow | Block | Block |
|
|
| Supervised *(default)* | Allow | Prompt | Prompt |
|
|
| Full | Allow | Allow | Prompt |
|
|
|
|
Anything that lands on **Prompt** is parked at the gate. `Block` is refused outright: no in-tier approval can authorize it. Classification is fail-closed. A command that isn't provably read-only is treated as at least `Write`, and across a piped command the highest class wins (so `ls | curl …` is `Network`).
|
|
|
|
***
|
|
|
|
## The flow
|
|
|
|
```text
|
|
agent wants to act
|
|
│
|
|
▼
|
|
classify command ──► Block ──► refused
|
|
│
|
|
Prompt
|
|
│
|
|
▼
|
|
on "Always allow" list? ──► yes ──► run immediately
|
|
│ no
|
|
▼
|
|
park call · persist pending row · emit approval_request
|
|
│
|
|
▼
|
|
┌──────────────┬───────────────┬────────────┐
|
|
▼ ▼ ▼ ▼
|
|
Approve Always allow Deny 10-min TTL
|
|
(once) (+ allowlist) │
|
|
│ │ │ ▼
|
|
▼ ▼ ▼ Deny
|
|
run run refused (fail closed)
|
|
```
|
|
|
|
When a call is parked, an **Approval Request card** appears above the chat composer. It shows the tool name, a safe one-line summary of the action, and the (redacted) command. Three choices:
|
|
|
|
* **Approve**: run this one call.
|
|
* **Always allow**: run it, and add the tool to your `auto_approve` list so it skips the prompt next time.
|
|
* **Deny**: refuse this call.
|
|
|
|
You can also just type **yes** / **no** in chat. The reply is routed back to the parked request.
|
|
|
|
***
|
|
|
|
## Always allow
|
|
|
|
Approving with **Always allow** persists the tool name onto `[autonomy].auto_approve` (config save + live-policy reload), so the gate short-circuits to *allow* for that tool on future turns. The list ships with safe read-only tools pre-approved (`file_read`, `memory_search`, `memory_list`, `get_time`, `list_dir`, `glob`, `grep`) and is editable in **Settings → Agent access**. Remove an entry there to start being prompted again.
|
|
|
|
***
|
|
|
|
## Fail-closed behavior
|
|
|
|
Every non-approve path resolves to **Deny**:
|
|
|
|
* **Timeout**: a parked request lives for 10 minutes; if undecided it is transitioned to a terminal `deny`.
|
|
* **Persist failure** or a dropped channel: denied.
|
|
* The timeout path re-reads the stored decision first, so an approval that committed in the race still wins.
|
|
|
|
Pending requests are stored in SQLite (`{workspace_dir}/approval/approval.db`) and **survive a core restart**. After an approved tool finishes, the gate records a write-once execution outcome (success / error, error text sanitized and capped) as a durable audit trail. Everything persisted or broadcast is redacted first: PII and chat content are scrubbed and home paths stripped.
|
|
|
|
***
|
|
|
|
## Background and cron bypass
|
|
|
|
The gate is **interactive-only**. Background, triage, and cron turns carry no chat context, so there's nobody to answer a prompt. These turns are pre-authorized and pass straight through (no row, no event). Approval is only enforced for live chat turns. (The Subconscious loop has its own, separate escalation-card approval for *unsolicited* writes; see below.)
|
|
|
|
***
|
|
|
|
## Configuration & RPC
|
|
|
|
* **`OPENHUMAN_APPROVAL_GATE`**: set to `0` / `false` to skip installing the gate entirely. With no gate, `Prompt`-class calls run unprompted. On by default.
|
|
* **`[autonomy].level`** and **`[autonomy].auto_approve`**: tier and allowlist, via the `config.update_autonomy_settings` RPC or Settings → Agent access.
|
|
|
|
The `approval` controller exposes three JSON-RPC methods:
|
|
|
|
| Method | Purpose |
|
|
| ----------------------------------- | ------------------------------------------------------------------- |
|
|
| `openhuman.approval_list_pending` | The live queue of parked requests. |
|
|
| `openhuman.approval_list_recent_decisions` | Decided/executed audit rows (`limit` 1 to 500, default 50). Surfaced in **Settings → Approval history**. |
|
|
| `openhuman.approval_decide` | Apply a decision (`approve_once` / `approve_always_for_tool` / `deny`). |
|
|
|
|
`list_pending` / `list_recent_decisions` return empty (not an error) when no gate is installed; `decide` errors when the gate is absent or the request is unknown or already decided.
|
|
|
|
***
|
|
|
|
## See also
|
|
|
|
* [Privacy & Security](privacy-and-security.md): autonomy tiers, trusted roots, and path hardening.
|
|
* [Subconscious Loop](subconscious.md): the background loop and its separate escalation approvals.
|
|
* [Security architecture](../developing/architecture/security.md): the command-classification and policy internals.
|