Start

Install

Install

fs-safe is published to npm as @openclaw/fs-safe. It targets Node 22 or newer, ships ESM only, and works on macOS, Linux, and Windows.

#Package managers

pnpm add @openclaw/fs-safe
npm install @openclaw/fs-safe
yarn add @openclaw/fs-safe
bun add @openclaw/fs-safe

#Node version

Minimum Node 22. The package uses fs.promises, fs.constants.O_NOFOLLOW where available, and node:stream/promises. Earlier Node releases will fail at import time.

Verify the runtime:

node --version
# v22.0.0 or newer

#TypeScript

Types ship with the package — no @types/openclaw__fs-safe needed. The exports map in package.json provides typed entries for every subpath:

import { root, FsSafeError } from "@openclaw/fs-safe";
import { writeJson } from "@openclaw/fs-safe/json";
import { extractArchive } from "@openclaw/fs-safe/archive";

A working tsconfig.json for consumers:

{
  "compilerOptions": {
    "target": "es2022",
    "module": "node18",
    "moduleResolution": "node16",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

#Subpath exports

Use the main entry for the common surface, or the focused subpaths when you want a leaner import or to depend on a narrower contract:

SubpathContents
@openclaw/fs-safeSmall common surface: root, root types, and errors.
@openclaw/fs-safe/rootroot(), Root, RootDefaults, related types.
@openclaw/fs-safe/configProcess-global Python helper configuration.
@openclaw/fs-safe/pathisPathInside, safeRealpathSync, isWithinDir, error helpers.
@openclaw/fs-safe/jsontryReadJson, readJson, readJsonIfExists, writeJson, sync variants.
@openclaw/fs-safe/storefileStore(), fileStoreSync(), and jsonStore<T>().
@openclaw/fs-safe/secretSecret file read/write helpers.
@openclaw/fs-safe/atomicreplaceFileAtomic, writeTextAtomic, replaceDirectoryAtomic, movePathWithCopyFallback.
@openclaw/fs-safe/temptempWorkspace, withTempWorkspace, sync variants, resolveSecureTempRoot.
@openclaw/fs-safe/secure-filereadSecureFile for pinned absolute file reads with permissions checks.
@openclaw/fs-safe/file-lockacquireFileLock, withFileLock, createFileLockManager, and related lock types.
@openclaw/fs-safe/permissionsPOSIX mode and Windows ACL inspection/remediation helpers.
@openclaw/fs-safe/walkwalkDirectory, walkDirectorySync, related types. Budget-bounded, not root-bounded.
@openclaw/fs-safe/archiveextractArchive, resolveArchiveKind, limits, preflight helpers.
@openclaw/fs-safe/advancedLower-level composition helpers: path scopes, root-file open, install paths, local-root readers, temp-file targets, sibling-temp writes, regular-file helpers, pathExists, withTimeout, and related advanced types. This surface is less stable than the focused public subpaths.
@openclaw/fs-safe/errorsFsSafeError, FsSafeErrorCode.
@openclaw/fs-safe/typesShared types: DirEntry, PathStat, BasePathOptions, …
@openclaw/fs-safe/test-hooksTest-only hooks for injecting races. Active under NODE_ENV=test.

#Runtime dependencies

@openclaw/fs-safe lists jszip and tar as optional dependencies for archive extraction. They are loaded lazily and only required when ZIP/TAR helpers run. Installs that omit optional dependencies can still import and use every non-archive subpath; archive calls fail with a clear missing-optional-dependency message.

There are no peer dependencies and no native build step.

#Python helper policy

On POSIX, root() uses one persistent Python helper process for the fd-relative operations Node does not expose cleanly. The default is auto: use the helper when it starts, fall back to Node-only behavior when it is disabled or unavailable.

import { configureFsSafePython } from "@openclaw/fs-safe/config";

configureFsSafePython({ mode: "auto" });    // default
configureFsSafePython({ mode: "off" });     // never spawn Python
configureFsSafePython({ mode: "require" }); // fail closed if unavailable

Environment variables are read at runtime:

FS_SAFE_PYTHON_MODE=off      # auto | off | require
FS_SAFE_PYTHON=/usr/bin/python3

OpenClaw compatibility aliases are also accepted: OPENCLAW_FS_SAFE_PYTHON_MODE, OPENCLAW_FS_SAFE_PYTHON, OPENCLAW_PINNED_PYTHON, and OPENCLAW_PINNED_WRITE_PYTHON.

Disabling Python keeps the public API working, but downgrades POSIX mutation hardening from fd-relative syscalls to Node path operations guarded by lexical and canonical checks plus identity verification. Use require for security-sensitive deployments where that downgrade should be a startup/runtime failure instead of a fallback. The full tradeoff is documented in Python helper policy.

#Verify the install

import { root, FsSafeError } from "@openclaw/fs-safe";
import os from "node:os";
import path from "node:path";

const dir = path.join(os.tmpdir(), "fs-safe-smoke");
await import("node:fs/promises").then((fs) => fs.mkdir(dir, { recursive: true }));

const fs = await root(dir);
await fs.write("hello.txt", "ok\n");
console.log(await fs.readText("hello.txt"));

try {
  await fs.write("../escape.txt", "x");
} catch (err) {
  if (err instanceof FsSafeError) console.log("blocked:", err.code);
}

If the script prints ok followed by blocked: outside-workspace, your install is healthy.

#Next

  • Quickstart — write, read, atomic, temp.
  • Security model — what the boundary defends against.
  • Errors — the closed code union you'll be catching.