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

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 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 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.

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

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 (enum definitions) and 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 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.

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 →