How OpenHuman Privacy Mode Guarantees Local-Only Inference

OpenHuman Privacy Mode enforces a LocalOnly setting that blocks cloud inference providers and prevents any external data transmission by running pure-function checks before any network I/O occurs.

The tinyhumansai/openhuman project implements a privacy-first architecture that separates privacy policy from agent autonomy, ensuring that when OpenHuman Privacy Mode is activated, no user data can leave the local machine. This guarantee is enforced through a two-layer validation system that intercepts both model inference requests and external network calls before they execute.

The Architecture: Separating Policy from Autonomy

In the OpenHuman codebase, privacy policy determines whether user data may leave the device, while autonomy controls what actions the agent may take. This separation allows the system to restrict data flow without limiting the agent's internal reasoning capabilities. The PrivacyMode enum defined in src/openhuman/config/schema/privacy.rs provides three states: LocalOnly, Standard, and Sensitive, with LocalOnly representing the strictest guarantee.

Layer 1: Enforcing Local-Only Inference Providers

Before any model inference occurs, the provider factory validates the current privacy mode. Located in src/openhuman/inference/provider/factory.rs, this factory contains the local_only_violation and enforce_local_only_inference logic that reads the process-global policy via live_policy::current_privacy_mode().

When LocalOnly is active, the factory maintains an allowlist of pure-local runtimes including Ollama, LM Studio, MLX, and local OpenAI-compatible servers. Any attempt to instantiate a cloud-hosted provider triggers an early bail! that aborts the turn before any network connection is established.

// src/openhuman/inference/provider/factory.rs
match provider {
    // Only allow local runtimes when privacy mode is LocalOnly
    Provider::Ollama | Provider::LmStudio | Provider::Mlx => { /* OK */ }
    _ => {
        if mode == PrivacyMode::LocalOnly {
            bail!("Local-only privacy mode is active – external provider blocked");
        }
    }
}

Layer 2: Blocking Unauthorized Data Egress

Even if inference is local, tools and integrations might attempt external network calls. OpenHuman prevents this through the egress enforcement system in src/openhuman/security/egress/enforce.rs. Every network-bound operation constructs an EgressDescriptor that classifies the request type and destination.

The enforce_egress function calls local_only_blocks(mode, desc) to evaluate whether the operation should proceed. If the mode is LocalOnly and the descriptor indicates an external transfer (desc.is_external) that is not a control-plane request (such as sign-in or billing), the system rejects the call with a clear error message.

// src/openhuman/security/egress/enforce.rs
let desc = EgressDescriptor {
    provider_slug: "http".into(),
    service: "api.example.com".into(),
    reason: EgressReason::Integration,
    is_external: true,
    // …other fields…
};
if let Err(e) = enforce_egress(&desc) {
    // The request is aborted; the error message explains why
    return Err(e);
}

This check occurs in local_only_tool_block and related functions, ensuring that data-sending operations cannot bypass the inference gate through alternative routes.

The Live Policy System

Both enforcement layers rely on the process-global live policy defined in src/openhuman/security/live_policy.rs. This module maintains the current privacy mode in a thread-safe RwLock<PrivacyMode>, allowing instant propagation of policy changes to all subsequent checks.

When users modify settings through the UI, RPC handlers in src/openhuman/config/ops/privacy.rs—specifically config_get_privacy_mode and config_set_privacy_mode—update the policy atomically.

// Accessing the current privacy mode anywhere in the codebase
use openhuman::security::live_policy::current_privacy_mode;
let mode = current_privacy_mode(); // PrivacyMode::LocalOnly, Standard, or Sensitive

The React Settings UI invokes these handlers via the core RPC client:

import { coreRpcClient } from "@/services/coreRpcClient";

async function setPrivacyMode(mode: "local_only" | "standard") {
  await coreRpcClient.invoke("openhuman.config_set_privacy_mode", { mode });
}

Changes take effect immediately for all new operations, while in-flight requests complete under their original policy.

Fail-Closed Design and Thread Safety

OpenHuman Privacy Mode implements a fail-closed philosophy: any unknown or future egress route is treated as user data and therefore blocked under LocalOnly. Because the validation functions are pure functions that execute before any network I/O, the guarantee holds even for code running on separate threads.

Network tools in src/openhuman/tools/impl/network/mod.rs check current_privacy_mode before executing, ensuring consistent enforcement across the entire tool ecosystem. End-to-end tests in app/test/playwright/specs/settings-privacy-mode.spec.ts verify that the UI correctly reflects and enforces these policy changes.

Summary

  • Two-layer validation: OpenHuman Privacy Mode enforces LocalOnly at both the inference provider selection (factory.rs) and external egress enforcement (enforce.rs) layers.
  • Pure-local inference: Only Ollama, LM Studio, MLX, and compatible local servers are permitted; cloud providers trigger immediate rejection via bail!.
  • Egress blocking: All external data transfers are blocked unless they are control-plane operations (sign-in, billing), evaluated via EgressDescriptor inspection in local_only_blocks.
  • Atomic updates: The live policy system in live_policy.rs propagates mode changes instantly via RwLock, with RPC handlers in config/ops/privacy.rs managing persistence.
  • Fail-closed safety: Unknown routes are blocked by default, and checks run as pure functions before network I/O, ensuring thread-safe, local-only operation.

Frequently Asked Questions

What is OpenHuman Privacy Mode?

OpenHuman Privacy Mode is a configuration setting that determines whether user data may leave the local device. When set to LocalOnly as defined in src/openhuman/config/schema/privacy.rs, the system enforces strict local-only operation by blocking all cloud inference providers and external data transfers through the provider factory and egress enforcement layers.

How does OpenHuman Privacy Mode differ from Standard mode?

Standard mode allows the agent to use cloud-hosted inference providers and external tools that transmit data to third-party services. In contrast, when Privacy Mode is set to LocalOnly, the enforce_local_only_inference function in src/openhuman/inference/provider/factory.rs and the local_only_blocks function in src/openhuman/security/egress/enforce.rs reject any operation that would transmit data off-device, restricting inference to local runtimes like Ollama or MLX.

Can network tools still function in LocalOnly mode?

Network tools that attempt to send user data to external services are blocked by the local_only_tool_block logic in src/openhuman/security/egress/enforce.rs. However, control-plane requests such as authentication or billing verification may still be permitted if explicitly classified as non-external in the EgressDescriptor. Standard web-fetching or API-calling tools will fail with a clear error message when LocalOnly is active.

How is the privacy mode setting persisted and synced?

The current privacy mode is stored in the process-global LivePolicy struct protected by an RwLock in src/openhuman/security/live_policy.rs. When changed via the Settings UI, the config_set_privacy_mode RPC handler in src/openhuman/config/ops/privacy.rs updates this value atomically. All subsequent inference and egress checks read from this global state via current_privacy_mode(), ensuring immediate synchronization across threads without requiring application restarts.

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 →