OpenHuman Approval Gate Mechanism for Agent Tool Execution: 10-Minute TTL Policy Explained

OpenHuman parks every agent tool call behind a global Approval Gate that requires explicit human approval before execution, automatically denying requests that remain pending longer than a configurable TTL that defaults to 10 minutes.

The OpenHuman agent framework implements a fail-closed safety layer that intercepts tool calls before they reach external systems. This approval gate mechanism for agent tool execution ensures that every potentially side-effecting operation waits for human or policy-driven consent, while a built-in TTL policy prevents orphaned requests from blocking the agent indefinitely.

How the Approval Gate Mechanism Works

Gate Installation and Global Registration

When the core starts, ensure_approval_gate creates a global ApprovalGate instance in src/openhuman/security/approval/gate.rs. The gate is wrapped in an Arc and attached to the core’s audit store, making it discoverable throughout the system via ApprovalGate::try_global.

Policy Hook Interception

Tools declare the "approval_gate" policy in src/openhuman/tools/policy.rs. The policy handler intercepts the tool call and routes it through the gate as crate::openhuman::tools::policy::PolicyDecision::Ask. If the core configuration sets approval_gate: false, the request bypasses the gate and executes immediately.

Parking a Turn and Suspending Execution

When a tool request reaches the gate, ApprovalGate::park creates a pending-approval row in the store with an expires_at timestamp. The turn is suspended, and the UI surfaces a request card to the user. The park duration can be bounded by a caller-provided timeout; the effective timeout is the minimum of the caller bound and the gate’s TTL.

Decision Processing and TTL Expiration

When the user sends an Allow, Deny, or Ask response, ApprovalGate::decide updates the pending row and un-parks the turn. If no decision arrives before the timeout expires, the gate TTL-denies the request (fail-closed), records a Deny, and removes the pending row.

TTL Policy and Timeout Configuration

Default 10-Minute TTL

The default approval window is defined in src/openhuman/security/approval/gate.rs at line 66 as Duration::from_secs(60 * 10), which equals 10 minutes. This value is persisted in the audit store so that a core restart or crash does not lose the timeout.

Copilot-Streaming TTL Override

For tools invoked from a copilot-streaming context, the gate applies a shorter COPILOT_APPROVAL_TTL of 3 minutes (Duration::from_secs(180)), defined at line 78 in the same file. As noted around line 103, this keeps streaming turns responsive by preventing long parks during active sessions.

Debug Environment Overrides

On debug builds only, the OPENHUMAN_APPROVAL_TTL_SECS environment variable overrides the boot-time TTL. The implementation at lines 311–346 reads this variable, but the value is clamped to the copilot TTL when the copilot context applies.

Caller-Bound Clamping

If a tool caller supplies its own timeout bound, the gate caps the park to min(bound, gate_TTL), as described in the comments around line 103. This prevents any single tool from parking longer than the global policy allows.

Source Code Examples

Here is how the gate is installed, invoked, and resolved in practice:

// Install the gate during core boot
let gate = Arc::new(ApprovalGate::new(config, session_id, DEFAULT_APPROVAL_TTL));
core_state.insert_approval_gate(gate);
// A tool that requires approval declares the policy
impl Tool for SomeNetworkTool {
    fn policy(&self) -> &'static str {
        "approval_gate"
    }
}
// Parking a request inside the gate implementation
let pending = gate.park(request_id, caller_bound);
// pending.expires_at is set to now + min(caller_bound, gate.ttl)
// UI sends an Allow decision via RPC
// ApprovalGate::decide(pending_id, Decision::Allow)
// The core then resumes the original turn.

Key files involved in this flow include:

Summary

  • The approval gate mechanism for agent tool execution in OpenHuman intercepts every tool call through a global ApprovalGate instance registered at boot time.
  • Tools opt into the gate by declaring the "approval_gate" policy in src/openhuman/tools/policy.rs.
  • Requests are parked via ApprovalGate::park, creating a pending row with an expires_at timestamp.
  • The default TTL is 10 minutes, with a 3-minute copilot-streaming override and debug-only environment variable customization.
  • Unresolved requests are automatically denied when the TTL expires, ensuring the system fails closed.

Frequently Asked Questions

What happens when an agent tool request exceeds the 10-minute TTL?

When the TTL expires without a user decision, the gate logic around line 383 records an automatic Deny and removes the pending row. The suspended turn is un-parked with a denial result, preventing the tool from executing.

How does the approval gate handle copilot-streaming contexts differently?

Copilot-streaming contexts use a shorter COPILOT_APPROVAL_TTL of 3 minutes (Duration::from_secs(180)) instead of the default 10 minutes. This prevents long-running parks from stalling active streaming sessions.

Can the approval gate TTL be customized or disabled?

You can override the TTL in debug builds by setting the OPENHUMAN_APPROVAL_TTL_SECS environment variable. The gate can also be disabled entirely by setting the approval_gate configuration flag to false, which causes the policy handler to forward tool calls immediately without parking.

Where is the approval gate state stored if the core restarts?

The pending-approval row and its expires_at timestamp are persisted in the audit store. Because the TTL is stored durably, a core restart or crash does not reset or lose the timeout state.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →