# How OpenHuman Privacy Mode Guarantees Local-Only Inference

> Discover how OpenHuman Privacy Mode guarantees local-only inference by blocking cloud providers and preventing data transmission with pure-function checks.

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

---

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

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

```rust
// 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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/ops/privacy.rs)—specifically `config_get_privacy_mode` and `config_set_privacy_mode`—update the policy atomically.

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

```tsx
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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/factory.rs)) and external egress enforcement ([`enforce.rs`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/live_policy.rs) propagates mode changes instantly via `RwLock`, with RPC handlers in [`config/ops/privacy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/inference/provider/factory.rs) and the `local_only_blocks` function in [`src/openhuman/security/egress/enforce.rs`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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.