# OpenHuman Command Permission Model: Classification, Gate, and Forbidden Checks

> Explore the OpenHuman command permission model. Understand how classification, gate decisions, and forbidden checks ensure secure command execution in your applications.

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

---

**OpenHuman enforces a fine-grained two-step permission model where commands are first classified by risk level using `SecurityPolicy::classify_command`, then evaluated against tier-based policies via `SecurityPolicy::gate_decision` to determine if execution is allowed, prompted, or blocked.**

The `tinyhumansai/openhuman` repository implements a deterministic security framework that prevents unauthorized system modifications by analyzing shell commands before execution. This architecture ensures that AI agents operate within strictly defined boundaries, with every command string undergoing semantic analysis to detect filesystem modifications, network activity, or destructive operations.

## Command Classification with classify_command

Every raw command string entering the system is parsed by `SecurityPolicy::classify_command` to determine its potential impact on the host environment. This function, implemented in **[`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs)** at line 126, examines shell features including pipes, redirects, variable expansions, and subshells to assign a `CommandClass` value.

### The Five CommandClass Categories

The classifier assigns one of five security classifications based on detected behavior:

- **Read** – Commands that only read files without modification (`cat`, `less`, `head`, `grep` without redirects)
- **Write** – Any operation that modifies the filesystem, including redirections (`>`, `>>`), file moves (`mv`), or deletions (`rm`)
- **Network** – Commands performing network I/O (`curl`, `wget`, `ssh`, `nc`)
- **Install** – Software installation commands (`cargo add`, `npm install`, `pip install`)
- **Destructive** – Host-level destructive operations (`reboot`, `shutdown`, `poweroff`, `halt`)

### Fail-Closed Security Design

The classification system operates on a **fail-closed** principle: any command containing unrecognized shell constructs or complex syntax that cannot be safely parsed is automatically upgraded to `Write` classification. This prevents adversarial commands from bypassing security through obfuscation. The comprehensive test suite in **[`src/openhuman/security/policy/policy_tests.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/policy_tests.rs)** (lines 438-553) validates this behavior across hundreds of edge cases.

## Gate Decision Logic and Tier Enforcement

After classification, the system applies policy rules through `SecurityPolicy::gate_decision`, defined in **[`src/openhuman/security/policy/policy_command.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/policy_command.rs)** at line 214. This function maps the command's `CommandClass` against the agent's assigned tier (e.g., *readonly*, *supervised*, *full*) to produce a `GateDecision`: `Allow`, `Prompt`, or `Block`.

### Tier-Based Access Control

Each tier defines maximum permissible command classes:

- **Readonly tiers** typically allow only `Read` commands
- **Supervised tiers** may permit `Write` operations after user confirmation
- **Full tiers** can execute `Network` and `Install` commands with appropriate checks

### The Raise-Only Rule

The gate logic enforces a strict **"raise-only"** policy: a tier may escalate the security classification of a command (treating a `Read` as `Write` for safety), but can never downgrade or lower the classification. This guarantees that once the policy identifies a potential risk, no subsequent logic can override the decision to block or prompt. Property-based tests in **[`src/openhuman/security/policy/proptest_tests.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/proptest_tests.rs)** (lines 17-28) verify this invariant.

## Real-World Implementation

The permission model integrates throughout the core codebase. The **shell tool** invokes these checks at **[`src/openhuman/tools/impl/system/shell.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/impl/system/shell.rs)** (lines 251 and 1068) before spawning subprocesses. Similarly, the **Node runtime** applies gating logic in **[`src/openhuman/runtime/node/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/runtime/node/ops.rs)** at line 38 to sandbox JavaScript execution.

Typical execution flow follows this pattern:

```rust
// 1. Classification
let class = security_policy.classify_command(raw_cmd);

// 2. Optional raise by the caller (e.g., a tool declares it needs Write)
let class = class.max(declared_class);

// 3. Gate decision based on the agent's tier
match security_policy.gate_decision(class) {
    GateDecision::Allow   => { /* execute */ }
    GateDecision::Prompt  => { /* ask the user */ }
    GateDecision::Block   => { /* reject */ }
}

```

## Practical Integration Examples

### Manually Checking Commands Before Execution

```rust
use openhuman::security::policy::{SecurityPolicy, Tier};

let policy = SecurityPolicy::new(Tier::Supervised);
let raw = "cat /etc/passwd > /tmp/passwd_copy";

// Classify the command (will be upgraded to Write because of the redirect)
let class = policy.classify_command(raw);

// Apply the policy gate
match policy.gate_decision(class) {
    GateDecision::Allow => {
        // Safe to run the command
        std::process::Command::new("sh")
            .arg("-c")
            .arg(raw)
            .status()
            .expect("failed to execute");
    }
    GateDecision::Prompt => {
        // Prompt the user before proceeding
        println!("The command '{raw}' requires permission. Allow? (y/n)");
        // …handle response…
    }
    GateDecision::Block => {
        eprintln!("Command blocked by security policy");
    }
}

```

### Embedding Checks in Custom Tools

```rust
use openhuman::tools::Tool;
use openhuman::security::policy::{SecurityPolicy, GateDecision};

pub struct MyTool {
    policy: SecurityPolicy,
}

impl Tool for MyTool {
    fn run(&self, cmd: &str) -> ToolResult {
        let class = self.policy.classify_command(cmd);
        match self.policy.gate_decision(class) {
            GateDecision::Allow => {
                // …run the command safely…
                ToolResult::Success
            }
            GateDecision::Prompt => {
                // …return a prompt to the model…
                ToolResult::UserPrompt
            }
            GateDecision::Block => ToolResult::PermissionDenied,
        }
    }
}

```

## Summary

- **Command classification** in [`command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/command_checks.rs) parses shell syntax to assign `Read`, `Write`, `Network`, `Install`, or `Destructive` labels, defaulting to `Write` for unrecognized commands.
- **Gate decisions** in [`policy_command.rs`](https://github.com/tinyhumansai/openhuman/blob/main/policy_command.rs) apply tier-based rules (`Allow`, `Prompt`, `Block`) following a "raise-only" policy that prevents security downgrades.
- **Integration points** include the shell tool ([`shell.rs`](https://github.com/tinyhumansai/openhuman/blob/main/shell.rs)) and Node runtime ([`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs)), ensuring all subprocess execution undergoes permission checks.
- **Test coverage** spans unit tests ([`policy_tests.rs`](https://github.com/tinyhumansai/openhuman/blob/main/policy_tests.rs) lines 438-553) and property-based tests ([`proptest_tests.rs`](https://github.com/tinyhumansai/openhuman/blob/main/proptest_tests.rs) lines 17-28) that verify the fail-closed behavior.

## Frequently Asked Questions

### How does OpenHuman classify complex shell commands with pipes and redirects?

`SecurityPolicy::classify_command` parses the abstract syntax tree of the command string to detect shell metacharacters. Any presence of output redirection operators (`>`, `>>`), pipes (`|`), command substitution, or variable expansion immediately upgrades the classification to `Write`, even if the base command (like `cat` or `echo`) would normally be `Read`-only. This ensures that `cat /etc/passwd > /tmp/leak` is treated as a write operation requiring appropriate tier permissions.

### What is the "raise-only" rule in OpenHuman's permission gate?

The raise-only rule states that once `gate_decision` determines a command's minimum security classification based on its syntax, higher-tier policies may only increase (raise) the required permission level or maintain it. A supervised tier cannot override a `Write` classification down to `Read` to bypass user prompts. This prevents privilege escalation attacks where malicious code attempts to downgrade dangerous commands to execute silently.

### Which CommandClass is assigned to unknown or unrecognized commands?

The classifier implements a **fail-closed** strategy: any command that cannot be parsed or contains unrecognized shell constructs is automatically classified as `Write`. This conservative default ensures that obfuscated commands, novel syntax, or edge-case shell features cannot slip past security controls under the assumption they are safe.

### Where is the permission model implemented in the OpenHuman codebase?

The core logic resides in two primary locations: **[`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs)** (line 126) contains the `classify_command` implementation and `CommandClass` definitions, while **[`src/openhuman/security/policy/policy_command.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/policy_command.rs)** (line 214) houses the `gate_decision` function and tier-based enforcement logic. Usage examples appear in **[`src/openhuman/tools/impl/system/shell.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/impl/system/shell.rs)** and **[`src/openhuman/runtime/node/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/runtime/node/ops.rs)**, with comprehensive test coverage in **[`src/openhuman/security/policy/policy_tests.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/policy_tests.rs)** and **[`src/openhuman/security/policy/proptest_tests.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/proptest_tests.rs)**.