# OpenHuman CommandClass Permission Model: Read, Write, Network, Install, and Destructive Explained

> Understand OpenHuman's CommandClass permission model: Read, Write, Network, Install, and Destructive. Enforce granular access controls by classifying shell commands.

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

---

**OpenHuman's security framework classifies every shell command into one of five CommandClass tiers—Read, Write, Network, Install, and Destructive—to enforce granular access controls based on the session's AutonomyLevel.**

The `tinyhumansai/openhuman` repository implements a **fail-closed CommandClass permission model** that automatically categorizes every tool-invoked shell command before execution. This system ensures that potentially dangerous operations require explicit approval while allowing safe, read-only commands to proceed unimpeded. The classification logic treats any command not provably read-only as at least **Write**, applying the highest risk level detected across piped command segments.

## The Five CommandClass Tiers

The enum defined in [`src/openhuman/security/policy/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/types.rs) establishes five distinct permission buckets that govern how the harness handles shell execution.

### Read

**Read** classifies provably read-only and observational operations. These commands appear on a curated safe-read allowlist and represent pure information retrieval without state modification. Commands like `cat file` or `ls` typically fall into this category.

### Write

**Write** covers state-changing operations that are not inherently catastrophic. This serves as the default classification for any command not explicitly recognized as read-only, network-bound, or destructive. The system defaults to this tier for unrecognized commands to maintain a fail-closed security posture.

### Network

**Network** identifies commands that reach external networks, including `curl`, `wget`, `ssh`, and `scp`. These always trigger **Prompt** decisions regardless of the current autonomy tier, preventing silent data exfiltration.

### Install

**Install** captures package manager operations that install software globally, such as `npm install -g`, `pip install`, or `cargo install`. Like Network commands, these require explicit approval in every acting tier including Full autonomy.

### Destructive

**Destructive** flags catastrophic, irreversible, or privilege-escalating operations such as `rm -rf /` or system shutdown commands. These always prompt for confirmation, even when the session operates in Full autonomy mode.

## How Commands Are Classified

The `SecurityPolicy::classify_command` method in [`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs) parses command strings and evaluates each shell segment. The implementation follows a **fail-closed** philosophy: if a command is not explicitly recognized as safe, it defaults to at least Write classification.

```rust
pub fn classify_command(&self, command: &str) -> CommandClass {
    let mut class = CommandClass::Read;
    // Parse the command into segments
    // Each segment may raise the class
    class = class.max(CommandClass::Write);
    // ...
    class
}

```

When processing piped commands, the classifier applies the **highest class wins** rule. For example, `ls | curl ...` evaluates to **Network** because the curl segment elevates the entire command to that tier, ensuring the strictest permission requirements govern the entire pipeline.

## Gate Decisions and Autonomy Levels

Once classified, the `gate_decision` method determines whether to **Allow**, **Prompt**, or **Block** the command based on the session's **AutonomyLevel** (ReadOnly, Supervised, or Full).

The mapping follows these enforcement rules:

- **ReadOnly tier**: Blocks any Write-classified command, allowing only pure Read operations
- **Supervised tier**: Prompts for any command that is not pure Read
- **Full tier**: Allows Write commands but continues to prompt for Network, Install, and Destructive classes

```rust
pub fn gate_decision(&self, class: CommandClass) -> GateDecision {
    match (self.autonomy, class) {
        (AutonomyLevel::ReadOnly, CommandClass::Write) => GateDecision::Block,
        (AutonomyLevel::Supervised, CommandClass::Write) => GateDecision::Prompt,
        (AutonomyLevel::Full, CommandClass::Network) => GateDecision::Prompt,
        (AutonomyLevel::Full, CommandClass::Destructive) => GateDecision::Prompt,
        _ => GateDecision::Allow,
    }
}

```

## Practical Implementation Examples

The following Rust examples demonstrate how the security policy behaves across different autonomy levels:

```rust
use openhuman::security::{SecurityPolicy, AutonomyLevel, GateDecision};

let policy = SecurityPolicy::new(AutonomyLevel::Supervised);

// Read-only commands pass immediately
assert_eq!(
    policy.gate_decision(policy.classify_command("cat README.md")),
    GateDecision::Allow
);

// Write commands trigger prompts in Supervised mode
assert_eq!(
    policy.gate_decision(policy.classify_command("touch new_file")),
    GateDecision::Prompt
);

// Network commands always prompt, even in Full tier
let full_policy = SecurityPolicy::new(AutonomyLevel::Full);
assert_eq!(
    full_policy.gate_decision(full_policy.classify_command("curl https://example.com")),
    GateDecision::Prompt
);

```

## Summary

- **CommandClass** categorizes every shell command into Read, Write, Network, Install, or Destructive tiers based on potential risk and operational impact
- The classification system is **fail-closed**, defaulting to Write for unrecognized commands and taking the maximum risk level across piped segments
- **AutonomyLevel** determines the harness response: ReadOnly blocks Writes, Supervised prompts for non-Read commands, and Full allows Writes but protects against Network, Install, and Destructive operations
- Core implementation resides in [`src/openhuman/security/policy/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/types.rs) (enum definitions) and [`src/openhuman/security/policy/command_checks.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/command_checks.rs) (classification and gating logic)

## Frequently Asked Questions

### How does OpenHuman handle piped commands with mixed risk levels?

When a command contains multiple segments connected by pipes, the classifier evaluates each segment independently and applies the **highest CommandClass** detected across the entire pipeline. For instance, combining a Read command like `ls` with a Network command like `curl` results in an overall **Network** classification, ensuring the stricter permission requirements apply to the entire operation.

### Why does the Full autonomy level still prompt for Network and Destructive commands?

The **Full** tier allows standard write operations to proceed without interruption, but **Network** and **Destructive** classes always trigger prompts regardless of autonomy settings. This design prevents silent external data exfiltration and protects against catastrophic system damage even when the user has granted broad execution permissions to the agent.

### Where is the CommandClass enum defined in the OpenHuman codebase?

The **CommandClass** enum is defined in [`src/openhuman/security/policy/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/types.rs) at lines 78-94. This file also contains the **AutonomyLevel** and **GateDecision** enums that work together with CommandClass to implement the complete permission model.

### What happens when a command is not recognized by the allowlist?

The classification logic follows a **fail-closed** approach: any command not explicitly identified as read-only or belonging to a specific higher tier defaults to the **Write** classification. This ensures that potentially dangerous unrecognized commands receive appropriate scrutiny through prompts or blocks rather than executing silently.