# How OpenHuman Enforces Privacy Mode at the Rust Core Level

> Discover how OpenHuman enforces privacy mode at the Rust core level. Learn about its thread-safe RwLock, RPC hot-reloading, and secure network traffic control.

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

---

**OpenHuman enforces privacy mode through a global, thread-safe `RwLock<PrivacyMode>` inside `LivePolicy` that is consulted by every egress component before allowing network traffic, with support for hot-reloading via RPC without restarting the core.**

The OpenHuman project (`tinyhumansai/openhuman`) implements a layered security architecture where privacy preferences are enforced at the Rust core level rather than at the application boundary. This design ensures that sensitive data never leaves the local machine when privacy mode is activated, regardless of which high-level tool or UI component initiates the request.

## Configuration to SecurityPolicy Mapping

Privacy enforcement begins at startup when the core parses the user-editable [`config.toml`](https://github.com/tinyhumansai/openhuman/blob/main/config.toml). The `[privacy]` section contains a `mode` field that determines the initial security posture.

In [`src/openhuman/security/policy/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/types.rs), the `SecurityPolicy` struct stores this value as `privacy_mode: PrivacyMode`. The `SecurityPolicy::from_config` constructor reads `config.privacy.mode` and maps it to the appropriate enum variant (typically `PrivacyMode::Standard` or `PrivacyMode::LocalOnly`). This static representation is then promoted to a live, mutable policy.

## Live Policy Wrapper and Hot-Reload Mechanism

Once constructed, the static `SecurityPolicy` is wrapped by `live_policy::LivePolicy`, which holds a `RwLock<PrivacyMode>`. During initialization, `live_policy::install_policy` copies the configured privacy mode into this global lock, making it accessible to all threads.

The system supports runtime modification through the RPC endpoint `openhuman.config_set_privacy_mode`. When invoked, the handler updates the persisted configuration and calls `live_policy::reload_privacy(new_mode)`. This function clones the current `SecurityPolicy`, replaces only the `privacy_mode` field, and writes the updated policy back into the `LivePolicy` lock. Changes take effect immediately for all subsequent egress checks.

Components read the active mode via `live_policy::current_privacy_mode()`, which acquires the lock and returns the current variant, falling back to `PrivacyMode::Standard` if no policy is installed.

## Runtime Enforcement Points

### Egress Descriptor Validation

Every outbound request first constructs an `EgressDescriptor` defined in [`src/openhuman/security/egress/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/egress/types.rs). During this construction phase, the builder calls `current_privacy_mode()`. If the mode is `LocalOnly`, the descriptor either masks the destination or aborts the request before any network layer interaction occurs.

### Network Tool Guards

Concrete network implementations contain explicit privacy gates. In [`src/openhuman/tools/impl/network/http_request.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/impl/network/http_request.rs), the execution path contains:

```rust
// Local‑only enforcement (privacy epic S7, #4441)
if live_policy::current_privacy_mode() == PrivacyMode::LocalOnly {
    tracing::debug!(target: "[http]", "blocked: local‑only privacy mode");
    return Err(Error::PrivacyModeBlocked);
}

```

The same check appears in [`src/openhuman/tools/impl/network/curl.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/impl/network/curl.rs) and other network tools, ensuring consistent enforcement regardless of which HTTP client backs the operation.

### Thread-Local Overrides for Testing

The test suite uses `live_policy::test_privacy_scope(mode)` to temporarily force a specific mode for a single thread. This function returns a `TestPrivacyGuard` that restores the previous mode when dropped, allowing tests to verify that tools correctly respect privacy settings without altering global state.

## Implementation Examples

Setting privacy mode via RPC from a Rust client:

```rust
use openhuman_core::client::CoreRpcClient;
use serde_json::json;

// Switch to “LocalOnly” – all outbound network traffic will be blocked
let rpc = CoreRpcClient::new("http://127.0.0.1:8000/rpc")?;
rpc.invoke(
    "openhuman.config_set_privacy_mode",
    json!({ "mode": "LocalOnly" })
).await?;

```

Reloading privacy mode after a manual config edit:

```rust
use openhuman::security::live_policy::{reload_privacy, PrivacyMode};

fn apply_new_mode() -> Result<(), String> {
    let new_mode = PrivacyMode::Standard;
    reload_privacy(new_mode)?;
    Ok(())
}

```

Checking the current mode in a custom tool:

```rust
use openhuman::security::live_policy::current_privacy_mode;
use openhuman::security::policy::PrivacyMode;

fn can_send() -> bool {
    matches!(current_privacy_mode(), PrivacyMode::Standard)
}

```

Temporarily forcing a mode in a unit test:

```rust
use openhuman::security::live_policy::{test_privacy_scope, PrivacyMode};

#[tokio::test]
async fn blocks_when_local_only() {
    let _guard = test_privacy_scope(PrivacyMode::LocalOnly);
    // any network request made here will be rejected
    assert!(http_request::fetch("https://example.com").await.is_err());
}

```

## Summary

- **Global State**: Privacy mode is stored in a `RwLock<PrivacyMode>` inside `LivePolicy`, accessible via `current_privacy_mode()`.
- **Hot-Reload**: The `reload_privacy` function and `openhuman.config_set_privacy_mode` RPC allow runtime changes without restarting the core.
- **Configuration**: The initial value originates from [`config.toml`](https://github.com/tinyhumansai/openhuman/blob/main/config.toml), parsed by `SecurityPolicy::from_config` in [`src/openhuman/security/policy/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy/types.rs).
- **Enforcement**: Every egress path—including [`http_request.rs`](https://github.com/tinyhumansai/openhuman/blob/main/http_request.rs) and [`curl.rs`](https://github.com/tinyhumansai/openhuman/blob/main/curl.rs)—checks `current_privacy_mode()` and returns `Error::PrivacyModeBlocked` when `LocalOnly` is active.
- **Testability**: `test_privacy_scope` provides thread-local overrides for isolated testing.

## Frequently Asked Questions

### Where is the privacy mode stored in memory?

The active privacy mode is stored in a global `RwLock<PrivacyMode>` inside the `LivePolicy` singleton defined in [`src/openhuman/security/live_policy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/live_policy.rs). This lock is initialized during `install_policy` and read by `current_privacy_mode()` whenever a component needs to check the current security posture.

### How can I change privacy mode without restarting OpenHuman?

Call the `openhuman.config_set_privacy_mode` RPC endpoint with the desired mode (`Standard` or `LocalOnly`). The handler triggers `live_policy::reload_privacy()`, which updates the global lock immediately. Since all components query `current_privacy_mode()` at execution time, no restart is required for the change to take effect.

### What happens when privacy mode is set to LocalOnly?

When `current_privacy_mode()` returns `PrivacyMode::LocalOnly`, egress descriptors mask or block destinations, and network tools like [`http_request.rs`](https://github.com/tinyhumansai/openhuman/blob/main/http_request.rs) return `Error::PrivacyModeBlocked` before any socket connection is established. This prevents all outbound network traffic while allowing local filesystem and in-process operations to continue.

### How does the testing framework verify privacy enforcement?

Tests use `live_policy::test_privacy_scope(PrivacyMode::LocalOnly)`, which returns a `TestPrivacyGuard`. The guard sets a thread-local override for the calling thread only, restoring the previous mode when it drops. This allows concurrent tests to run with different privacy settings without interfering with each other or the global policy state.