Start

Install

Install

fs-safe is published to npm as @openclaw/fs-safe. It targets Node 20.11 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 20.11. 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
# v20.11.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/pathisPathInside, safeRealpathSync, isWithinDir, error helpers.
@openclaw/fs-safe/jsontryReadJson, readJson, readJsonIfExists, writeJson, writeText.
@openclaw/fs-safe/storefileStore(), jsonStore<T>(), and privateStateStore().
@openclaw/fs-safe/secretSecret file read/write helpers.
@openclaw/fs-safe/atomicreplaceFileAtomic, replaceDirectoryAtomic, movePathWithCopyFallback.
@openclaw/fs-safe/temptempWorkspace, withTempWorkspace, tempFile, writeSiblingTempFile.
@openclaw/fs-safe/secure-filereadSecureFile for pinned absolute file reads with permissions checks.
@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, pinned open, root-file open, install paths, local-root readers, sidecar locks, 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 depends on jszip and tar for archive extraction. Both are loaded lazily — if your code never touches the archive subpath, the runtime cost is negligible.

There are no peer dependencies and no native build step.

#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.