# QuickJS Skills Sandbox Security Boundaries in OpenHuman

> Learn how OpenHuman secures its QuickJS skills sandbox with memory limits CPU counters filesystem restrictions and safe network APIs preventing resource exhaustion and unauthorized access.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: security
- Published: 2026-05-12

---

**OpenHuman enforces strict security boundaries in its QuickJS skills sandbox through memory limits, CPU step counters, filesystem restrictions, and mediated network APIs, ensuring user JavaScript cannot exhaust host resources or access unauthorized system functions.**

The OpenHuman project implements hardened **QuickJS skills sandbox security boundaries** using the Rust `rquickjs` crate to create isolated execution environments for user-contributed JavaScript. This architecture strictly controls resource consumption and API exposure, ensuring untrusted code cannot compromise the host system or access unauthorized resources.

## Memory and Execution Limits

### Heap Size Constraints

Each skill operates within a dedicated QuickJS runtime instance configured with strict memory boundaries. In [`src/openhuman/skills/ops_create.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/ops_create.rs) at lines 42-49, the system initializes the runtime with a **32 MiB heap limit** using `set_memory_limit(32 * 1024 * 1024)`, causing immediate termination if the JavaScript heap exceeds this allocation.

### Instruction Step Limits

To prevent infinite loops and CPU exhaustion, OpenHuman implements a **per-instruction step counter** enforced by the QuickJS engine. The code at [`src/openhuman/skills/ops_create.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/ops_create.rs) lines 51-56 configures `set_step_limit(10_000_000)`, which throws an `ExecutionError` and terminates the script once the JavaScript execution exceeds ten million steps.

```rust
// src/openhuman/skills/ops_create.rs
let runtime = rquickjs::Runtime::new();
runtime.set_memory_limit(32 * 1024 * 1024);          // 32 MiB
runtime.set_step_limit(10_000_000);                // 10M JS steps
let ctx = rquickjs::Context::full(&runtime)?;

```

## Filesystem and Network Sanitization

### Restricted Filesystem Access

The sandbox deliberately excludes Node-style filesystem APIs, eliminating direct disk access from user code. Instead, skills interact with files exclusively through the **`openhuman.fs`** host module defined in [`src/openhuman/skills/bus.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/bus.rs) at lines 12-20, which enforces workspace isolation and read-only or write-only modes based on the skill's permissions.

### Mediated HTTP Requests

Native `fetch` and `XMLHttpRequest` objects are unavailable in the sandbox environment. Network access is routed through the **`openhuman.http`** API implemented in [`src/openhuman/skills/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/ops.rs) at lines 78-85, which validates URLs against a whitelist, caps request sizes, and enforces timeouts before executing any outbound connection.

```javascript
// Skill script executing inside the sandbox
log("Skill started");

// URL is validated against whitelist by host
const resp = await openhuman.http.get("https://api.example.com/data");

// Filesystem access restricted to skill's private workspace
await openhuman.fs.writeFile("output.json", JSON.stringify(resp));

```

## Runtime Environment Lockdown

### Controlled Host API Surface

OpenHuman exposes only explicitly registered functions to the JavaScript global scope. The `register_host` function in [`src/openhuman/skills/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/mod.rs) at lines 18-30 injects only vetted utilities like `log`, `http`, `fs`, and `env`, while stripping all other globals before script execution begins.

```rust
// src/openhuman/skills/mod.rs
fn register_host(ctx: &rquickjs::Context) -> Result<()> {
    ctx.globals().set("log", rquickjs::function(|msg: String| {
        tracing::debug!("[skill] {}", msg);
    }))?;
    // http and fs are similarly registered with validation
    Ok(())
}

```

### No Native Extension Loading

**QuickJS** does not support loading native shared libraries or external binaries. This architectural constraint inherent to the `rquickjs` implementation prevents skills from invoking unsafe OS-level code or loading arbitrary dynamic libraries, effectively blocking escape attempts through native extensions.

### Fresh Runtime Per Execution

Each skill invocation starts from a **deterministic, fresh runtime** with no persistent state from previous executions. As implemented in [`src/openhuman/skills/ops_parse.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/ops_parse.rs) at lines 23-31, state persistence is only possible through the sandboxed `store` API, preventing data leakage across skill boundaries.

## Safe Inter-Process Communication

### Typed Request Bus Architecture

Communication between the sandbox and OpenHuman's core uses the **typed native request bus** (`event_bus::request_native_global`) defined in [`src/core/event_bus/native_request.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/event_bus/native_request.rs) at lines 38-46. This bus transmits only `Send + 'static` data types, eliminating risks of pointer sharing or reference leakage between the JavaScript runtime and the host Rust process.

## Summary

OpenHuman's QuickJS skills sandbox implements eight critical security boundaries:

- **Memory isolation** via 32 MiB heap limits per runtime configured in [`ops_create.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops_create.rs).
- **CPU throttling** through 10-million-step execution caps enforced by the QuickJS engine.
- **Filesystem restrictions** requiring use of vetted `openhuman.fs` APIs instead of native Node modules.
- **Network mediation** through the whitelisted `openhuman.http` module with URL validation.
- **API minimization** exposing only functions registered in `openhuman::skills::host`.
- **Native code blocking** by QuickJS's inability to load shared libraries or external binaries.
- **Runtime freshness** ensuring no state persistence between runs except via the sandboxed `store`.
- **Type-safe IPC** using the `event_bus` with `Send + 'static` constraints to prevent pointer leakage.

## Frequently Asked Questions

### How does OpenHuman prevent memory exhaustion from malicious JavaScript skills?

OpenHuman configures each QuickJS runtime with a fixed heap size limit of 32 MiB in [`src/openhuman/skills/ops_create.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/ops_create.rs). If a skill attempts to allocate memory beyond this boundary, the QuickJS engine immediately aborts execution, preventing the script from consuming excessive host resources.

### Can skills access the filesystem or network directly?

No. The sandbox removes native Node.js filesystem and network APIs. Skills must use the host-provided `openhuman.fs` and `openhuman.http` modules, which enforce workspace isolation and URL whitelisting respectively, as implemented in [`src/openhuman/skills/bus.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/bus.rs) and [`src/openhuman/skills/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/ops.rs).

### What happens if a skill enters an infinite loop?

The sandbox counts JavaScript execution steps and enforces a hard limit of 10 million steps per skill invocation. When exceeded, QuickJS throws an `ExecutionError` and terminates the script, effectively neutralizing infinite loops or excessive CPU consumption attempts.

### Is it possible for skills to load native code or binary extensions?

No. QuickJS does not support loading native shared libraries or external binaries. This design constraint, inherent to the `rquickjs` implementation, prevents skills from escaping the sandbox through native code execution or invoking unsafe system calls.