# How the OpenHuman Approval Gate Works with Autonomy Policies: A Technical Deep Dive

> Understand the OpenHuman approval gate and its autonomy policies. Learn how it secures tool calls, automates low-risk actions, and escalates high-risk operations for human review.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: deep-dive
- Published: 2026-08-29

---

**The OpenHuman approval gate acts as a security intermediary that evaluates tool calls against configured autonomy policies, automatically allowing low-risk operations while escalating medium and high-risk actions to human reviewers through a pending approval system.**

The OpenHuman framework from the `tinyhumansai/openhuman` repository implements a tiered security model where autonomous agent capabilities meet human oversight. At the center of this architecture lies the **Approval Gate**, which mediates between the agent's autonomy policies and actual tool execution. Understanding how this gate evaluates risk tiers and manages pending approvals is essential for developers configuring secure AI agent deployments.

## Request Flow and Decision Logic

The approval process begins when the LLM generates a `ToolCall`. According to the source code in [`src/openhuman/security/approval/gate.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/gate.rs), the `ApprovalGate` evaluates each call through a five-step pipeline:

1. **Tool Call Generation** – The LLM produces a `ToolCall` (e.g., `openhuman.run_code`).

2. **Policy Classification** – The `ToolDispatcher` consults [`src/openhuman/tools/policy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/policy.rs) to classify the call as Read, Write, Network, or Destructive.

3. **Gate Evaluation** – The `ApprovalGate` runs `gate.pending_decision` to assess the call against current autonomy settings.

4. **Autonomy Policy Check** – The gate reads the `Access` tier from [`src/openhuman/security/autonomy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/autonomy.rs) and the tool's `ApprovalMode` (from [`src/openhuman/tools/schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/schemas.rs)). 
   - **Full/Supervised** autonomy tiers allow auto-approval of low-risk tools.
   - **Read-only** autonomy blocks any write-type tool and forces pending approval.

5. **Outcome Resolution** – The gate returns one of three variants:
   - `GateOutcome::Allowed` – The tool executes immediately.
   - `GateOutcome::Pending` – Creates a pending approval entry and parks the turn until human intervention.
   - `GateOutcome::Blocked` – Rejects the tool without creating a pending request.

## Risk Evaluation and Policy Enforcement

The `ApprovalGate::decide` method matches the tool's `RiskLevel` against the agent's autonomy tier and configuration flags. As implemented in [`src/openhuman/security/approval/gate.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/gate.rs), the gate evaluates three primary risk factors:

- **Tool-level risk** (`approvalMode`): Defined in the tool's JSON schema ([`src/openhuman/tools/schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/schemas.rs)) and set during registration via `ToolBuilder::risk()`.
- **Agent autonomy tier**: The `Access` object from [`src/openhuman/security/autonomy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/autonomy.rs) defines whether the agent operates in Full, Supervised, or Read-only mode.
- **Session context**: Interactive sessions (checked via `core::runtime::context::is_interactive`) may auto-approve more aggressively than background sessions.

If `require_approval_for_medium_risk` is set to `true` in the configuration (the default), medium-risk tools always traverse the pending-approval path regardless of the autonomy tier.

## Persistence and Audit Logging

When the gate returns `GateOutcome::Pending`, the system creates a pending approval entry in an SQLite database stored at `<workspace>/approval/approval.db`. The CRUD operations and schema definitions reside in [`src/openhuman/security/approval/store.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/store.rs).

Upon human decision via the `openhuman.approval_decide` RPC, the store updates the row and the core re-dispatches the parked tool call. All execution outcomes are recorded in the audit log implementation found in [`src/openhuman/security/approval/audit.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/audit.rs).

## RPC Interface for Human-in-the-Loop

The approval domain exposes three RPC methods defined in [`src/openhuman/security/approval/schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/schemas.rs) and registered in [`src/openhuman/security/approval/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/mod.rs):

- `openhuman.approval_list_pending` – Returns pending approval rows for the current workspace.
- `openhuman.approval_list_recent_decisions` – Returns the audit trail of recent decisions.
- `openhuman.approval_decide` – Submits a human decision (`ApproveOnce`, `ApproveAlways`, or `Deny`).

These methods enable external clients to render approval UIs and submit decisions that resume parked turns.

## Key Implementation Details

### Gate Initialization and Decision Logic

The `ApprovalGate::new` constructor builds a gate holding references to the `Config` and `ApprovalStore`. The core decision logic in `ApprovalGate::decide` evaluates whether the combination of `RiskLevel` and `Access` tier permits automatic execution.

### Turn Parking and Resumption

When a decision is pending, the gate returns `GateOutcome::Pending` containing a `PendingApprovalId`. The core's turn manager parks the turn and attaches this ID. Upon human decision, `ApprovalGate::resume` re-injects the original turn into the execution queue.

## Practical Code Examples

### Querying Pending Approvals

```rust
use openhuman_core::client::CoreClient;

let client = CoreClient::new("http://127.0.0.1:8080", rpc_token)?;
let pending = client
    .call("openhuman.approval_list_pending", json!({}))
    .await?;
println!("Pending approvals: {:#?}", pending);

```

### Submitting an Approval Decision

```rust
let decision = json!({
    "id": "a1b2c3d4",                 // PendingApprovalId from list_pending
    "decision": "ApproveOnce"        // ApproveOnce | ApproveAlways | Deny
});
client.call("openhuman.approval_decide", decision).await?;

```

### Registering Tools with Risk Levels

```rust
use openhuman_core::openhuman::tools::registry::ToolBuilder;
use openhuman_core::openhuman::security::approval::RiskLevel;

ToolBuilder::new("run_code")
    .risk(RiskLevel::Medium)          // sets `approvalMode` in the schema
    .register();

```

### Custom Autonomy Checks in Tool Implementation

```rust
fn execute_tool(ctx: &ToolContext) -> Result<ToolResult, ToolError> {
    // Autonomy check – reject if the agent is read‑only
    if ctx.access.is_readonly() {
        return Err(ToolError::PermissionDenied);
    }

    // Let the approval gate decide
    match ctx.approval_gate.decide(&ctx.tool_schema, &ctx.access) {
        GateOutcome::Allowed => { /* run tool */ }
        GateOutcome::Pending(pending_id) => {
            return Err(ToolError::PendingApproval(pending_id));
        }
        GateOutcome::Blocked => {
            return Err(ToolError::BlockedByPolicy);
        }
    }
}

```

## Summary

- The **Approval Gate** in [`src/openhuman/security/approval/gate.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/gate.rs) serves as the enforcement point between autonomy policies and tool execution.
- **Autonomy tiers** (Full, Supervised, Read-only) defined in [`src/openhuman/security/autonomy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/autonomy.rs) establish baseline permissions for agent operations.
- **Risk levels** assigned during tool registration determine whether calls execute immediately, enter pending approval, or get blocked.
- Pending approvals persist in SQLite storage ([`src/openhuman/security/approval/store.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/store.rs)) until resolved via RPC calls.
- The architecture ensures that dangerous actions—such as file writes or network calls—require explicit human consent through the `GateOutcome::Pending` mechanism.

## Frequently Asked Questions

### What triggers a pending approval in the OpenHuman approval gate?

A pending approval triggers when `ApprovalGate::decide` determines that the tool's `RiskLevel` exceeds the current autonomy tier's automatic execution threshold, or when the `require_approval_for_medium_risk` configuration flag is enabled. The gate returns `GateOutcome::Pending` containing a `PendingApprovalId`, and the turn manager parks the execution until the human submits a decision via `openhuman.approval_decide`.

### How do autonomy policies affect automatic tool execution?

Autonomy policies defined in [`src/openhuman/security/autonomy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/autonomy.rs) create the `Access` tier that gates tool execution. Full autonomy allows automatic execution of low and medium-risk tools, Supervised mode requires approval for medium-risk operations, and Read-only mode blocks all write-type tools regardless of their individual risk ratings.

### Where are approval decisions stored in OpenHuman?

Pending approvals and audit trails are stored in an SQLite database located at `<workspace>/approval/approval.db`. The schema and data access layer are implemented in [`src/openhuman/security/approval/store.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/store.rs), while the audit logging logic resides in [`src/openhuman/security/approval/audit.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/audit.rs).

### Can developers customize which tools require approval?

Yes. Developers assign risk levels during tool registration using `ToolBuilder::risk()` in [`src/openhuman/tools/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/registry.rs), setting the `approvalMode` in the tool's JSON schema. Additionally, the global `require_approval_for_medium_risk` configuration flag forces medium-risk tools to require approval regardless of the agent's autonomy tier.