How OpenHuman Classifies Commands Using gate_decision in SecurityPolicy

OpenHuman determines whether a tool command should be allowed, prompted, or blocked through a deterministic two-step process implemented in the SecurityPolicy type, where classify_command assigns risk classes and gate_decision maps them to final execution decisions based on autonomy tiers.

OpenHuman is an open-source framework designed to safely execute AI-generated shell commands by enforcing strict security boundaries. The SecurityPolicy type provides the core classification engine that evaluates every command before execution. This article examines how the gate_decision method classifies commands by combining static analysis with configurable autonomy levels.

The Two-Step Classification Process

The command classification system operates as a fail-closed pipeline that first analyzes command structure, then applies policy rules to determine the final gate decision.

Step 1: Structural Classification with classify_command

The SecurityPolicy::classify_command method performs static analysis on raw command strings to determine their inherent risk level. Located in src/openhuman/security/policy/command_checks.rs, this function parses the input by splitting on unquoted separators (;, |, &&, ||, newlines) and stripping leading environment-variable assignments.

Each command segment is evaluated against five risk classes ordered by severity:

  • Read – File read operations, directory listings
  • Write – File modifications, output redirections
  • Network – HTTP requests, socket operations
  • Install – Package managers, dependency installation
  • Destructive – Deletions, system modifications, rm -rf patterns

The system adopts the highest-risk class observed across all segments. Additionally, file redirections (> or >>) or the presence of tee automatically upgrade the classification to at least Write, as these operations modify the filesystem regardless of the base command's nature. This redirection uplift logic appears at lines 40-46 of the same file.

Step 2: Policy Decision with gate_decision

Once the CommandClass is determined, SecurityPolicy::gate_decision maps it to a concrete GateDecision (Allow, Prompt, or Block) based on the current autonomy tier:

Autonomy Tier Read Write Network/Install/Destructive
ReadOnly Allow Block Block
Supervised Allow Prompt Prompt
Full Allow Allow Prompt

The decision matrix is implemented as a match expression in gate_decision, ensuring deterministic evaluation. Under ReadOnly mode, only Read commands execute directly; any write operation or network call is immediately blocked. The Supervised tier allows reads while requiring user confirmation for higher-risk operations. Full autonomy permits read and write operations without prompting, but still requires approval for network, install, and destructive commands.

Code Implementation Examples

The following Rust code demonstrates the end-to-end flow from command string to final decision:

use openhuman::security::policy::{SecurityPolicy, AutonomyLevel, GateDecision};

// Configure a supervised policy tier
let policy = SecurityPolicy::default()
    .with_autonomy(AutonomyLevel::Supervised);

// Classify a compound command with redirection
let cmd = "git commit -m 'fix' && echo done > out.txt";
let class = policy.classify_command(cmd);
// class == CommandClass::Write (forced by redirection)

// Determine execution path
match policy.gate_decision(class) {
    GateDecision::Allow => println!("Execute immediately"),
    GateDecision::Prompt => println!("Request user approval"),
    GateDecision::Block => println!("Reject execution"),
}

Read-only tier behavior:

let read_only = SecurityPolicy::default()
    .with_autonomy(AutonomyLevel::ReadOnly);

// Safe read operation
let class = read_only.classify_command("ls -l");
assert_eq!(read_only.gate_decision(class), GateDecision::Allow);

// Destructive command blocked
let class = read_only.classify_command("rm -rf /");
assert_eq!(read_only.gate_decision(class), GateDecision::Block);

Full autonomy tier behavior:

let full = SecurityPolicy::default()
    .with_autonomy(AutonomyLevel::Full);

// Local write allowed without prompt
let class = full.classify_command("npm install");
assert_eq!(full.gate_decision(class), GateDecision::Allow);

// Network still requires confirmation
let class = full.classify_command("curl http://example.com");
assert_eq!(full.gate_decision(class), GateDecision::Prompt);

Security Guarantees and Fail-Closed Design

The classification system provides a deterministic floor that guarantees runtime safety. The gate_decision output represents the minimum restriction level for a command; the LLM-declared intent can only raise the classification higher, never lower it. This architecture ensures that even if the AI model misclassifies a command's intent, the static analysis in classify_command prevents unauthorized execution.

The implementation references several key source locations:

Summary

  • Two-step pipeline: classify_command extracts risk classes from shell syntax, then gate_decision applies tier-based policy rules.
  • Automatic write detection: Output redirections (>, >>) and tee commands force CommandClass::Write regardless of base executable.
  • Three autonomy tiers: ReadOnly blocks all non-reads, Supervised prompts for elevated operations, and Full allows writes but prompts for network/destructive actions.
  • Fail-closed guarantee: The static analysis sets a security floor that the LLM cannot override, preventing permission escalation.
  • Deterministic evaluation: All classification logic resides in src/openhuman/security/policy/command_checks.rs with no external dependencies for command evaluation.

Frequently Asked Questions

What autonomy tiers does OpenHuman support?

OpenHuman implements three autonomy levels defined in src/openhuman/security/policy/types.rs: ReadOnly, which permits only read operations; Supervised, which allows reads automatically but prompts for writes, network, install, and destructive commands; and Full, which permits reads and writes without user interaction but maintains prompts for network, installation, and destructive operations to prevent accidental system damage.

How does file redirection affect command classification?

The classify_command method detects output redirection operators (> and >>) and the tee utility during parsing. When present in any command segment, the classification automatically upgrades to at least CommandClass::Write because these operations modify the filesystem. This redirection uplift occurs at lines 40-46 of src/openhuman/security/policy/command_checks.rs, ensuring that commands like echo "data" > file.txt are treated with write-level permissions even though echo is inherently read-only.

Can the LLM override the gate_decision classification?

No. The gate_decision system operates as a deterministic floor that the LLM can only make more restrictive, never less. If the static analysis in classify_command determines a command is Destructive, the AI cannot downgrade it to Read or force an Allow decision. The LLM may provide additional context that raises the classification (for example, flagging a write operation as potentially destructive), but the final GateDecision always respects the maximum of the static analysis and the LLM's declared intent.

Where is the gate_decision logic implemented?

The core decision matrix is implemented in the gate_decision method within src/openhuman/security/policy/command_checks.rs at lines 58-73. This method takes a CommandClass enum and returns a GateDecision based on the policy's current AutonomyLevel setting. The method uses a pattern match to map each risk class to its appropriate decision for the configured tier, providing the final authorization check before command execution.

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 →