# How OpenHuman Classifies Commands Using gate_decision in SecurityPolicy

> Discover how OpenHuman classifies commands using gate_decision in SecurityPolicy. Learn how commands are allowed prompted or blocked based on risk classes and autonomy tiers.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-08-30

---

**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`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/types.rs) 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`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs#L26-L48) method performs static analysis on raw command strings to determine their inherent risk level. Located in [`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/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](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs#L40-L46) of the same file.

### Step 2: Policy Decision with gate_decision

Once the `CommandClass` is determined, [`SecurityPolicy::gate_decision`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs#L58-L73) 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:

```rust
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:**

```rust
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:**

```rust
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:

- **Classification logic**: [`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs) (lines 26-48)
- **Redirection handling**: [`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs) (lines 40-46)
- **Decision matrix**: [`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs) (lines 58-73)
- **Type definitions**: [`src/openhuman/security/policy/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/types.rs)

## 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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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.