Start

Native helper policy

Native helper policy

@openclaw/fs-safe declares exact-version optional packages for Linux x64/arm64 (glibc or musl), macOS x64/arm64, Windows x64/arm64, and FreeBSD x64/arm64. The FreeBSD 14.4+ bindings expose the qualified subset. Package-manager OS, CPU, and libc filters install only the matching package. The loader requires that package lazily, without runtime downloads, postinstall scripts, or a consumer Rust build.

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

configureFsSafeNative({ mode: "auto" });    // default
configureFsSafeNative({ mode: "off" });     // disable the addon; reject native-only operations
configureFsSafeNative({ mode: "require" }); // fail closed when the binding is unavailable

The equivalent environment variables are FS_SAFE_NATIVE_MODE and OPENCLAW_FS_SAFE_NATIVE_MODE. Accepted values are auto, off, require, true, false, on, never, required, 1, and 0.

#Modes

ModeBehavior
autoPrefer native primitives when the current platform package loads; otherwise use supported fallbacks and reject native-only operations.
offDo not load a native package. Use supported fallbacks and reject native-only operations deterministically.
requireThrow FsSafeError("helper-unavailable") instead of falling back when an operation needs the native binding and it cannot load.

See What needs the native binding for the native-only APIs and options.

See Archive extraction for native, bundled WASM, and ZIP backends. Native archive-operation failures are terminal; auto does not retry them through a fallback. require rejects missing bindings or required capabilities.

A loaded binding may provide only a subset of native operations. Each operation checks its required capabilities before native dispatch: missing capabilities select the existing guarded fallback in auto, or report helper-unavailable in require. A failure from an available native method is still an operation failure; it is not treated as a missing capability.

Windows owner/DACL inspection, private-directory creation, and secure reads can use Windows helper fallbacks in auto and off; require stays strict, and native operation failures remain terminal. Bun macOS/Linux canonicalization uses the addon under its runtime requirements.

Configure the mode once during startup. Loading is lazy and cached; changing from auto to require after a failed load changes failure policy but does not repeatedly probe the binary.

#Runtime health checks

getFsSafeNativeStatus(options?: { probe?: boolean }): Promise<FsSafeNativeStatus> is exported from @openclaw/fs-safe/config and the main entry point. It reports the loader state without parsing warnings, on every supported runtime. A doctor can explicitly probe without changing the configured mode or mutating the filesystem:

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

const status = await getFsSafeNativeStatus({ probe: true });
console.log(status.loaded ? "Native helper active" : `Native helper: ${status.reason}`);
if (status.guidance) console.log(status.guidance);
FieldsMeaning
mode, modeSourceEffective policy and its source; only environment variable names are exposed.
reason, loadednot-probed, loaded, disabled, unsupported-target, package-missing, load-failed, or capability-missing; loaded is true only for loaded.
targetRuntime platform/architecture/libc, including unsupported targets.
packageName, packageVersionExpected platform package and exact pinned manifest version; absent for unsupported targets.
loadErrorCodeAllowlisted code or UNKNOWN, only for load-failed.
guidanceThe same next-step text as fallback warnings; absent for not-probed and loaded.

Await the returned promise. With no options, this API only reads the cache and never loads the addon; not-probed means no load has been attempted. With { probe: true }, it uses the existing lazy loader and cache. Mode off always reports disabled without attempting a load. Failures are reported without throwing, including in require mode. Core capability-missing does not expose internal function names, and a loaded result does not promise that every operation is available or kernel-atomic. Fields omit paths and raw loader errors.

Each module instance has its own status, so probe real Workers separately when their health matters. After repairing an installation, start a fresh process or Worker: the cached failure is not retried by another probe or mode change. See the full API reference for exported types and field details.

#Troubleshooting fallback warnings

Windows security and private-creation fallbacks emit FS_SAFE_NATIVE_FALLBACK once per feature in each JavaScript context (the main thread or a Worker). Warnings identify the reason and, on supported targets, the expected platform package and its exact version from fs-safe's manifest. They omit caller paths and raw loader error messages. Other guarded fallbacks, including Linux/macOS operations, do not currently emit this warning.

ReasonNext step
package-missingReinstall with optional dependencies included: npm --include=optional, or pnpm without --no-optional / optional=false. Check that the lockfile and registry mirror include the named package at the exact version. Install for the deployed Node architecture and Linux libc.
disabledThe warning names the effective source: programmatic configuration, FS_SAFE_NATIVE_MODE, or OPENCLAW_FS_SAFE_NATIVE_MODE. If native loading is intended, set that source to auto or require. Programmatic configuration takes precedence, then FS_SAFE_NATIVE_MODE, then OPENCLAW_FS_SAFE_NATIVE_MODE. Keep intentional off settings for isolated runtimes.
unsupported-targetPortable mode is expected on this platform/architecture/libc target; no matching native package is published. Native-only operations remain unavailable.
load-failedCheck Node architecture, Linux libc compatibility, and OS code-integrity policy. A missing dependency inside an installed platform package is also a load failure. Reinstalling identical bytes may not help; resolve compatibility or approved signing/policy requirements without disabling security controls.
capability-missingUse matching fs-safe and platform-package versions. A loaded binding may intentionally provide a platform-specific subset; see the FreeBSD capabilities.

Restart the process or Worker after repairing an installation: failed loads are cached. These diagnostics do not change auto, off, or require behavior, and a loaded addon does not imply that every operation is kernel-atomic.

Native-created descriptors retain their originating native close operation through normal and error cleanup, including later mode changes. Node-created roots and directory handles keep Node's close operation, and borrowed handles keep their caller-owned lifetime. This preserves Node worker-thread descriptor tracking without unmanaged-descriptor warnings. A helper missing native close support is unavailable before descriptor allocation.

Close retained native resources and let in-flight operations finish before forcibly terminating a worker. Native-created descriptors are not registered with Node's automatic worker-exit cleanup; Worker.terminate() can leave them open until process exit. The native close operation handles explicit cleanup, not forced worker termination.

Temp workspaces have an independent cleanupSafety policy: "compatible" can use guarded JavaScript cleanup even in native require mode; "require-bounded" rejects before child creation without the required cleanup capabilities. See the temp workspace contract. Retained-directory staging requires native support on Linux/macOS and is unsupported on Windows.

#Native boundary

The internal Darwin descriptor ACL inspector requires its matching native capability in both auto and require; off, a missing package, or an older binding without inspectDarwinAcl rejects with helper-unavailable. Inspection failure or malformed facts reject with permission-unverified; there is no mode-bit or pathname fallback for this capability. See Darwin clone normalization for descriptor-bound admission and terminal errors after a clone payload exists.

The native layer exposes policy-free filesystem mechanisms: beneath-root open/mkdir/link, replace and no-replace rename, identity reads, archive decode/execution, clone/copy/hash workers, POSIX canonicalization, and Windows security descriptor calls. The TypeScript layer owns policy, retries, filters, budgets, modes, cleanup, error normalization, and the decision to fall back.

Platform mechanisms and containment limits are documented in the security model and native architecture.

Linux root lookups reject negative descriptor sentinels before borrowing a handle or resolving a relative path; they never substitute the process working directory for an admitted root. Public Root operations already supply retained, admitted handles.

On Linux and macOS, native asynchronous file-copy admission rejects negative source and parent descriptors before creating a stage, preserving source-before-parent error ordering. Callers must keep nonnegative source and parent descriptors open until the operation settles.

Low-level Unix query, hash, copy, clone, staging, and owned-tree cleanup calls also reject negative descriptors before using them, preserving each operation's path-validation, cancellation, and cleanup order. Descriptor-relative mutations never accept a working-directory sentinel as a retained capability. On modern macOS, beneath opens reject negative roots with EBADF before calling openat, rather than returning EIO after an OS failure or working-directory operation. This check does not establish the validity of arbitrary nonnegative integers: callers must supply live descriptors and retain them until synchronous calls return or asynchronous operations settle.

replaceDirectoryAtomic() requires renameNoReplaceWithIdentity before it creates a missing target parent. On POSIX the dedicated entry point keeps the existing pre-dispatch exact receipt fence but dispatches direct-child names through the retained parents without another receipt, duplicate, reopen, or macOS F_GETPATH; the documented final source-name substitution window remains there. Deeper names retain guarded parent traversal.

No-clobber Root.move() requires descriptor-relative parent admission. On Linux, auto can use identity-checked link/unlink for files or plain descriptor-relative rename for directories when RENAME_NOREPLACE is unsupported. Directory fallback can replace an empty directory created concurrently; require stays strict. See the move contract and the fallback contract.

Guarded JavaScript mutations can have out-of-root effects before a post-check detects a hostile parent swap. Native require does not require a kernel-atomic resolver: inspect the operation's containment and use OS isolation for hostile concurrent actors. The security model is authoritative for operation and platform guarantees.

#Migration from the Python helper

The Python configuration bridge was intentionally removed in 0.23.0. A remaining FS_SAFE_PYTHON_MODE or OPENCLAW_FS_SAFE_PYTHON_MODE now raises a migration error when no explicit native policy is selected; it never silently selects auto. Replace either variable with its corresponding NATIVE_MODE name and preserve the value (require → require, off → off, auto → auto), or call configureFsSafeNative() explicitly before using fs-safe. Follow the 0.5 migration checklist for the removed settings and current precedence.

#Retained existing Windows file

The maintained Windows helper supports the public retainFileInDirectory lifecycle on fixed local NTFS. Private native handles, exact identity/generation checks, writable-section admission and explicit close results stay behind that public API. Older helpers without this capability are unsupported; there is no pathname deletion fallback. No Windows namespace persistence barrier is provided.