# How the OpenHuman Approval Gate Handles Interactive vs Background Turns

> Discover how the OpenHuman approval gate distinguishes between interactive and background turns. Learn about its consent mechanisms and auto-approval logic.

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

---

**The OpenHuman approval gate intercepts tool calls in [`src/openhuman/security/approval/gate.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/gate.rs) and requires explicit user consent for interactive turns, while auto-approving background turns unless the tool schema explicitly demands approval via `require_approval_for_medium_risk`.**

OpenHuman implements a security layer called the **approval gate** to protect against unauthorized AI agent actions. This system distinguishes between **interactive turns** initiated by users through the chat interface and **background turns** executed by scheduled jobs or sub-agents. Understanding how the approval gate processes these two turn types is essential for configuring secure yet efficient automated workflows in the `tinyhumansai/openhuman` repository.

## Where the Approval Gate Intercepts Tool Calls

The approval gate lives in the **tool execution path** inside the OpenHuman core. When an AI agent decides to invoke a tool, the request passes through `ApprovalGate::check` in [`src/openhuman/security/approval/gate.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/gate.rs) before reaching the actual tool implementation.

The gate evaluates three critical factors:
- The current **access tier** (e.g., `Access::full()`, `Access::supervised()`)
- The **risk level** of the tool (defined in [`src/openhuman/tools/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/registry.rs) as `Medium`, `High`, etc.)
- The **origin type** of the turn (interactive UI vs. background process)

This evaluation determines whether the tool executes immediately, gets parked pending approval, or is denied based on policy.

## Interactive Turns: User-Driven Approval Flow

Interactive turns originate from the desktop UI (`app/`) via `CoreRpcClient::run_turn` or direct user chat messages. By default, the environment variable `OPENHUMAN_APPROVAL_GATE=1` ensures the gate is **installed** for every interactive session.

### Decision Flow for Interactive Turns

When a user-initiated turn triggers a tool call, the gate executes the following sequence:

1. **Risk Classification**: The gate checks the tool's risk level against the workspace's access tier permissions in the tool registry.
2. **Parking**: If the tool's risk exceeds the tier's allowance, the gate **parks** the turn and creates a pending approval entry in the SQLite database at [`src/openhuman/security/approval/store.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/store.rs).
3. **UI Prompt**: The desktop application renders an **ApprovalRequestCard** offering four options: **Approve Once**, **Always Approve**, **Deny**, or **Ask Later**.
4. **Resumption**: Once the user decides, the gate resumes the parked turn using `gate.pending_for_thread(...).as_deref()`, records the decision for auditing, and finally executes the tool.

Interactive turns **never bypass** required decisions because the user is present to review high-risk actions.

## Background Turns: Automated Execution with Policy Override

Background turns are generated by the **cron scheduler**, **sub-agents**, or **workflow runners** without an attached UI. While the gate remains active for these turns, its default policy shifts to **auto-approve** unless the tool explicitly opts into scrutiny.

### Auto-Approval and Deferred Decisions

For background execution, the decision flow differs:

1. **Schema Check**: The gate examines the tool's JSON schema for `require_approval_for_medium_risk`. If this flag is `false` (the default for background contexts), the call auto-approves regardless of risk level.
2. **Forced Parking**: If the tool schema sets `require_approval_for_medium_risk = true`, the gate **parks** the turn and inserts a pending approval entry identical to interactive mode.
3. **RPC Fallback**: Without a UI, the system relies on the **"Ask Later"** path. Administrators must resolve parked turns via the RPC endpoints `openhuman.approval_list_pending` and `openhuman.approval_decide` defined in [`src/openhuman/security/approval/rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/rpc.rs).

Background-parked approvals carry a configurable **deadline** specified in [`src/openhuman/security/approval/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/types.rs). If no decision arrives before the deadline expires, the gate treats the request as **denied** and records a failure in the execution log.

## Approving Pending Requests via RPC

When background turns require manual intervention, administrators interact with the approval system through HTTP RPC endpoints:

```bash

# List all pending approvals across background jobs

curl -H "Authorization: Bearer $TOKEN" \
     http://localhost:8000/rpc/openhuman.approval_list_pending

# Approve a specific request by audit_id

curl -X POST -H "Authorization: Bearer $TOKEN" \
     -d '{"audit_id":"<audit_id>","decision":"ApproveOnce"}' \
     http://localhost:8000/rpc/openhuman.approval_decide

```

These endpoints allow automated cleanup scripts or admin dashboards to manage the approval queue for headless workloads.

## Persistence and Audit Trail

All pending and final decisions are stored in a per-workspace SQLite file located at `<workspace>/approval/approval.db`. The store layer in [`src/openhuman/security/approval/store.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/store.rs) provides CRUD operations used by both the gate logic and the RPC layer.

Audited decisions are exposed via `openhuman.approval_list_recent_decisions`, which powers the **Approval History** panel in the desktop UI. This ensures every tool invocation—whether approved automatically or manually—leaves a forensic trail for security review.

## Configuration Options

You can tune the approval gate behavior through environment variables and tool-level flags.

### Disabling the Gate Entirely

For testing scenarios, set the environment variable before initializing the core:

```rust
std::env::set_var("OPENHUMAN_APPROVAL_GATE", "0");

```

This bypasses all approval checks and should never be used in production environments handling sensitive data.

### Tool-Level Risk Flags

Each tool's definition in [`src/openhuman/tools/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/registry.rs) can specify:

```json
{
  "name": "send_email",
  "risk_level": "Medium",
  "require_approval_for_medium_risk": true
}

```

Setting `require_approval_for_medium_risk` to `true` forces the approval gate to park the turn even when running in background mode, ensuring critical operations receive human oversight.

## Summary

- **Interactive turns** always trigger the approval gate for high-risk tools, presenting an `ApprovalRequestCard` to the user and waiting for explicit consent via `gate.pending_for_thread`.
- **Background turns** auto-approve by default unless the tool schema explicitly sets `require_approval_for_medium_risk = true`, falling back to RPC endpoints for deferred decisions.
- All decisions are persisted in `<workspace>/approval/approval.db` via [`store.rs`](https://github.com/tinyhumansai/openhuman/blob/main/store.rs) and exposed through `openhuman.approval_list_recent_decisions` for auditing.
- The gate can be disabled via `OPENHUMAN_APPROVAL_GATE=0` for testing, but tool-level flags provide granular control over which operations require oversight.

## Frequently Asked Questions

### How does the approval gate distinguish between interactive and background turns?

The gate inspects the turn's **origin type** passed through the execution context. Interactive turns originate from `CoreRpcClient::run_turn` in the UI layer, while background turns come from the cron scheduler or sub-agent workers. This origin metadata determines whether the gate presents a UI card or applies the background auto-approval policy.

### What happens when a background turn requests approval but no administrator is available?

If a background turn parks for approval and the configurable deadline (defined in [`src/openhuman/security/approval/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/types.rs)) expires without a decision via `openhuman.approval_decide`, the gate automatically treats the request as **denied**. The turn fails with an audit entry recording the timeout, preventing workflows from hanging indefinitely.

### Can I require approval for specific tools only in background mode?

Yes. In [`src/openhuman/tools/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/registry.rs), set the tool schema flag `require_approval_for_medium_risk = true`. This forces the approval gate to park the turn even during background execution, creating a pending entry that must be resolved through the RPC endpoints rather than auto-approving.

### Where does the approval gate store pending decisions?

The gate persists all pending and completed decisions to a SQLite database at `<workspace>/approval/approval.db`. The [`src/openhuman/security/approval/store.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/store.rs) module handles all database operations, while [`src/openhuman/security/approval/rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/approval/rpc.rs) exposes query endpoints for external tools to inspect the approval queue.