How the OpenHuman Approval Gate Works with Autonomy Policies: A Technical Deep Dive
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, the ApprovalGate evaluates each call through a five-step pipeline:
-
Tool Call Generation – The LLM produces a
ToolCall(e.g.,openhuman.run_code). -
Policy Classification – The
ToolDispatcherconsultssrc/openhuman/tools/policy.rsto classify the call as Read, Write, Network, or Destructive. -
Gate Evaluation – The
ApprovalGaterunsgate.pending_decisionto assess the call against current autonomy settings. -
Autonomy Policy Check – The gate reads the
Accesstier fromsrc/openhuman/security/autonomy.rsand the tool'sApprovalMode(fromsrc/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.
-
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, the gate evaluates three primary risk factors:
- Tool-level risk (
approvalMode): Defined in the tool's JSON schema (src/openhuman/tools/schemas.rs) and set during registration viaToolBuilder::risk(). - Agent autonomy tier: The
Accessobject fromsrc/openhuman/security/autonomy.rsdefines 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.
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.
RPC Interface for Human-in-the-Loop
The approval domain exposes three RPC methods defined in src/openhuman/security/approval/schemas.rs and registered in 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, orDeny).
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
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
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
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
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.rsserves as the enforcement point between autonomy policies and tool execution. - Autonomy tiers (Full, Supervised, Read-only) defined in
src/openhuman/security/autonomy.rsestablish 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) 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::Pendingmechanism.
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 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, while the audit logging logic resides in 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, 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →