Secure file operations in OpenClaw: fs-safe library and Python helper defaults
Learn how OpenClaw uses the @openclaw/fs-safe library for secure local file operations and why the optional Python helper is disabled by default. This page is for operators configuring file security.
Read this when
- Changing file access, archive extraction, workspace storage, or plugin filesystem helpers
OpenClaw uses @openclaw/fs-safe for security-sensitive local file operations: root-bounded reads and writes, atomic replacement, archive extraction, temporary workspaces, JSON state, and secret-file handling.
It acts as a library guardrail for trusted OpenClaw code that receives untrusted path names, not as a sandbox. The actual blast radius is still determined by host filesystem permissions, OS users, containers, and the agent or tool policy.
Default: no Python helper
OpenClaw sets the fs-safe POSIX Python helper to off by default:
- the gateway should not start a persistent Python sidecar unless an operator opts in;
- most installations do not require the extra parent-directory mutation hardening;
- disabling Python keeps runtime behavior consistent across desktop, Docker, CI, and bundled-app environments.
OpenClaw only changes the default. An explicit setting always takes precedence:
# Default OpenClaw behavior: Node-only fs-safe fallbacks.
OPENCLAW_FS_SAFE_PYTHON_MODE=off
# Opt into the helper when available, falling back if unavailable.
OPENCLAW_FS_SAFE_PYTHON_MODE=auto
# Fail closed if the helper cannot start.
OPENCLAW_FS_SAFE_PYTHON_MODE=require
# Optional explicit interpreter path.
OPENCLAW_FS_SAFE_PYTHON=/usr/bin/python3
The generic fs-safe environment variable names also work: FS_SAFE_PYTHON_MODE and FS_SAFE_PYTHON.
Use require (not auto) when the helper is part of your security posture; auto silently falls back to Node-only behavior if the helper cannot start.
What stays protected without Python
With the helper off, OpenClaw still provides fs-safe's Node-only guardrails:
- rejects relative-path escapes (
..), absolute paths, and path separators where only bare names are allowed; - resolves operations through a trusted root handle instead of ad-hoc
path.resolve(...).startsWith(...)checks; - refuses symlink and hardlink patterns on APIs that require that policy;
- opens files with identity checks where the API returns or consumes file contents;
- writes state and config files via atomic sibling-temp plus rename;
- enforces byte limits for reads and archive extraction;
- applies private file modes for secrets and state files where the API requires them.
This covers OpenClaw's normal threat model: trusted gateway code handling untrusted model, plugin, or channel path input inside a single trusted operator boundary.
What Python adds
On POSIX, the optional helper keeps one persistent Python process and uses fd-relative filesystem operations for parent-directory mutations: rename, remove, mkdir, stat and list, and some write paths.
This narrows same-UID race windows where another process swaps a parent directory between validation and mutation. It provides defense in depth on hosts where untrusted local processes can modify the same directories OpenClaw operates in.
If your deployment has that risk and Python is guaranteed to be available, set:
OPENCLAW_FS_SAFE_PYTHON_MODE=require
Plugin and core guidance
- Plugin-facing file access should go through
openclaw/plugin-sdk/*helpers, not rawfs, when a path comes from a message, model output, config, or plugin input. - Core code should use the fs-safe wrappers under
src/infra/*so OpenClaw's process policy applies consistently. - Archive extraction should use the fs-safe archive helpers with explicit size, entry-count, link, and destination limits.
- Secrets should use OpenClaw secret helpers or fs-safe secret and private-state helpers; do not hand-roll mode checks around
fs.writeFile. - For hostile local-user isolation, do not rely on fs-safe alone. Run separate gateways under separate OS users or hosts, or use sandboxing.
Related: Security, Sandboxing, Exec approvals, Secrets.