OpenHuman action_dir and workspace_dir Explained: How the Sandbox Enforces Path Boundaries
OpenHuman uses action_dir as the agent's isolated filesystem root for all read/write operations, while workspace_dir stores internal core state; the sandbox enforces boundaries by validating that all resolved paths start with action_dir and checking against a hard-coded denylist of system locations.
OpenHuman separates agent-accessible storage from internal application data using two distinct directory configurations. Understanding how action_dir and workspace_dir function is critical for securing AI agent operations and preventing filesystem escapes. This guide examines the source code implementation in the tinyhumansai/openhuman repository to explain how the sandbox validates every filesystem access.
What Are action_dir and workspace_dir in OpenHuman?
OpenHuman defines two filesystem roots that serve entirely different security purposes. The separation ensures that AI agents operate within a controlled sandbox while the core system maintains private state storage.
action_dir: The Agent's Read/Write Sandbox
The action_dir represents the root directory where agents can freely read and write files. By default, this location is set to ~/OpenHuman/projects, though it can be overridden using the OPENHUMAN_ACTION_DIR environment variable.
According to the source in [src/openhuman/config/schema/types_part_01.rs](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/types_part_01.rs#L83-L92), the effective value is determined by Config::action_dir(), which first checks for an override stored in Config.action_dir_override (set via the Settings UI) before falling back to the default path. All tool executions resolve their current working directory (CWD) against this path, ensuring agents cannot access files outside this boundary.
workspace_dir: Isolated Internal State
The workspace_dir serves as the internal state directory for core system operations. Located at ~/.openhuman/users/<id>/workspace by default, this directory stores sensitive resources such as SQLite databases, authentication tokens, and internal logs.
As implemented in [src/openhuman/web_chat/session.rs](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/web_chat/session.rs#L165), this path is injected into the core at startup and is never exposed to agents or tools. The sandbox architecture explicitly prevents any tool from reading or writing to this location, maintaining strict isolation between agent operations and system internals.
How the OpenHuman Sandbox Enforces Path Boundaries
The sandbox implements a multi-layered validation system that checks every filesystem access before execution. This ensures agents remain confined to their designated action_dir while protecting critical system locations.
Path Resolution via ToolRunContext
All built-in tools interact with the filesystem through ToolRunContext, which carries a PathPolicy containing the authorized action_dir. When a tool needs to resolve a filesystem path, it calls effective_action_dir_for_context(), implemented in [src/openhuman/tools/impl/system/shell.rs](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/impl/system/shell.rs#L154-L166):
fn effective_action_dir_for_context(&self, context: Option<&dyn ToolRunContext>) -> PathBuf {
// Directly use the security-policy-provided action_dir.
self.security.action_dir.clone()
}
This method returns a clone of the security policy's action_dir, which serves as the root for all subsequent path resolution operations.
The Boundary Validation Check
Before executing any file operation, the sandbox validates that the resolved absolute path remains within the action_dir boundary:
let resolved = workspace_dir.join(&rel_path);
if !resolved.starts_with(workspace_dir) {
return Err(ToolError::ForbiddenPath);
}
If the resolved path fails the starts_with validation against action_dir, the tool immediately returns ToolError::ForbiddenPath. The core translates this error into an approval-gate denial, preventing the operation from executing.
The Security Denylist Layer
Beyond the action_dir boundary check, the sandbox applies a hard-coded denylist through security::is_always_forbidden(). This function blocks access to known-dangerous locations such as /etc, the user's home directory, and the core's internal workspace_dir.
The denylist is applied after the action_dir validation, ensuring that even if a path technically resolves within action_dir, it will still be rejected if it matches a forbidden pattern. This defense-in-depth strategy prevents agents from exploiting symbolic links or edge-case path traversals to access sensitive system locations.
Implementation Examples
The following examples demonstrate how tools interact with the sandbox boundaries in practice. As shown in [src/openhuman/tools/ops.rs](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/ops.rs), tools are registered with the sandbox by passing Config.action_dir to ensure proper isolation:
// Example: creating a tool that writes a file under the agent's sandbox.
let cfg = Config::load_or_init(); // loads action_dir & workspace_dir
let tool = FileWriteTool::new(cfg.action_dir.clone());
tool.run("notes.txt", b"Hello world!")?; // succeeds – path resolves inside action_dir
// Attempt to write outside the sandbox (will error):
let illegal_path = cfg.action_dir.parent().unwrap().join("outside.txt");
tool.run(illegal_path, b"Oops!")?; // → ToolError::ForbiddenPath
When implementing custom tools, developers must resolve paths through the provided context to maintain sandbox guarantees:
// Inspect the resolved CWD inside a tool implementation:
fn run(&self, cmd: &str, ctx: Option<&dyn ToolRunContext>) -> Result<(), ToolError> {
let cwd = self.effective_action_dir_for_context(ctx);
// cwd is guaranteed to be a sub-directory of `action_dir`.
self.runtime.build_shell_command(cmd, &cwd)?;
// ...
}
Summary
- Dual-directory architecture: OpenHuman separates
action_dir(agent workspace) fromworkspace_dir(internal core state) to prevent data leakage and unauthorized access. - Configurable agent root: The
action_dirdefaults to~/OpenHuman/projectsbut can be overridden via environment variables or the Settings UI throughConfig::action_dir(). - Strict path validation: Every filesystem operation validates that resolved paths
start_withtheaction_dir, returningToolError::ForbiddenPathfor violations. - Defense in depth: A secondary denylist (
security::is_always_forbidden) blocks access to system-critical paths even if they somehow pass the primary boundary check. - Source verification: The test suite in [
shell_tests_part_01_tests.rs](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/impl/system/shell_tests_part_01_tests.rs) confirms that tools route CWD throughaction_dirand never fall back toworkspace_dir.
Frequently Asked Questions
What is the default location for action_dir in OpenHuman?
By default, OpenHuman sets action_dir to ~/OpenHuman/projects. This location can be changed by setting the OPENHUMAN_ACTION_DIR environment variable before startup, or by using the Settings UI which writes to Config.action_dir_override and takes precedence over the environment default.
How does OpenHuman prevent agents from accessing workspace_dir?
The sandbox enforces this separation through compile-time architecture and runtime validation. The workspace_dir is never passed to tool contexts or PathPolicy structures, and the effective_action_dir_for_context() method exclusively returns the action_dir. Additionally, the security::is_always_forbidden denylist explicitly blocks the workspace_dir path, ensuring any attempted access results in a ToolError::ForbiddenPath error.
Can the action_dir location be customized after installation?
Yes. Users can override the action_dir location through the Settings UI, which persists the path to Config.action_dir_override. When the core loads configuration from [src/openhuman/config/schema/types_part_01.rs](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/types_part_01.rs#L83-L92), the Config::action_dir() method checks for this override before falling back to the default or environment variable setting.
What happens when an agent tries to write a file outside action_dir?
When a tool attempts to write to a path that does not start with the action_dir base path, the sandbox's boundary validation logic detects the violation and returns ToolError::ForbiddenPath. This error propagates through the execution pipeline and typically results in an approval-gate denial presented to the user, preventing the unauthorized filesystem access without crashing the agent session.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →