@openclaw/openshell-sandbox 2026.10.5-beta.4 → 2026.10.10-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/node_modules/@openclaw/fs-safe/CHANGELOG.md +148 -0
- package/node_modules/@openclaw/fs-safe/README.md +20 -4
- package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +28 -3
- package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-read.js +11 -5
- package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/bounded-read.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/bounded-read.js +9 -2
- package/node_modules/@openclaw/fs-safe/dist/clone-metadata.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/clone-metadata.js +2 -1
- package/node_modules/@openclaw/fs-safe/dist/config.d.ts +6 -0
- package/node_modules/@openclaw/fs-safe/dist/config.js +5 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +3 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +8 -7
- package/node_modules/@openclaw/fs-safe/dist/copy-link.d.ts +26 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-link.js +170 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-metadata.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-metadata.js +39 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-path-admission.js +11 -2
- package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.d.ts +2 -4
- package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/copy.js +3 -3
- package/node_modules/@openclaw/fs-safe/dist/create.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/creation-boundary.js +7 -4
- package/node_modules/@openclaw/fs-safe/dist/darwin-acl.d.ts +12 -0
- package/node_modules/@openclaw/fs-safe/dist/darwin-acl.js +29 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-durability.d.ts +7 -4
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +20 -5
- package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +27 -2
- package/node_modules/@openclaw/fs-safe/dist/directory-receipt.d.ts +7 -7
- package/node_modules/@openclaw/fs-safe/dist/directory-receipt.js +9 -4
- package/node_modules/@openclaw/fs-safe/dist/durability.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/exact-identity.d.ts +17 -0
- package/node_modules/@openclaw/fs-safe/dist/exact-identity.js +29 -0
- package/node_modules/@openclaw/fs-safe/dist/file-handle-transfer.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/file-hash.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/file-hash.js +11 -9
- package/node_modules/@openclaw/fs-safe/dist/file-identity.d.ts +7 -1
- package/node_modules/@openclaw/fs-safe/dist/file-identity.js +27 -5
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-admission.js +1 -2
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -1
- package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/file-lock.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/file-write-lease.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/file-write-lease.js +42 -0
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +22 -15
- package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.js +5 -0
- package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/index.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.js +16 -3
- package/node_modules/@openclaw/fs-safe/dist/local-file-descriptor.js +24 -13
- package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +85 -18
- package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +5 -0
- package/node_modules/@openclaw/fs-safe/dist/native-config.js +11 -0
- package/node_modules/@openclaw/fs-safe/dist/native-directory-observation.js +6 -4
- package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.js +26 -2
- package/node_modules/@openclaw/fs-safe/dist/native-guidance.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-guidance.js +21 -0
- package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/native-operations.js +8 -6
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +44 -4
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +85 -15
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts +3 -3
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +33 -6
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts +4 -1
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +22 -3
- package/node_modules/@openclaw/fs-safe/dist/native-policy-directory-observation.js +4 -3
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.js +16 -0
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +15 -5
- package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.js +7 -0
- package/node_modules/@openclaw/fs-safe/dist/native-status.d.ts +17 -0
- package/node_modules/@openclaw/fs-safe/dist/native-status.js +30 -0
- package/node_modules/@openclaw/fs-safe/dist/native.d.ts +31 -4
- package/node_modules/@openclaw/fs-safe/dist/native.js +107 -9
- package/node_modules/@openclaw/fs-safe/dist/opened-realpath.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/overwrite-file-handle.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/permission-exec.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/permissions-windows.js +83 -47
- package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-open.js +8 -3
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-input.js +13 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +10 -8
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-types.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +7 -5
- package/node_modules/@openclaw/fs-safe/dist/pipe.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/pipe.js +26 -0
- package/node_modules/@openclaw/fs-safe/dist/private-directory.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +2 -5
- package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.d.ts +3 -2
- package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.js +38 -10
- package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +92 -10
- package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts +5 -3
- package/node_modules/@openclaw/fs-safe/dist/publish-file.js +26 -20
- package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/read-opened-file.js +3 -4
- package/node_modules/@openclaw/fs-safe/dist/realpath.js +32 -0
- package/node_modules/@openclaw/fs-safe/dist/regular-file.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/regular-file.js +11 -15
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +3 -2
- package/node_modules/@openclaw/fs-safe/dist/root-copy.d.ts +18 -0
- package/node_modules/@openclaw/fs-safe/dist/root-copy.js +128 -0
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +8 -13
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +252 -27
- package/node_modules/@openclaw/fs-safe/dist/root-directory-creation.js +5 -1
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +11 -8
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +35 -13
- package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.js +108 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file.d.ts +7 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file.js +15 -0
- package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/root-impl.js +154 -175
- package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.js +239 -0
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +37 -15
- package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.d.ts +32 -0
- package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.js +56 -0
- package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/root-public-types.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +95 -16
- package/node_modules/@openclaw/fs-safe/dist/root-remove.js +6 -4
- package/node_modules/@openclaw/fs-safe/dist/root-walk.js +23 -19
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -6
- package/node_modules/@openclaw/fs-safe/dist/root-write-publication.js +5 -1
- package/node_modules/@openclaw/fs-safe/dist/secure-file-windows.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/secure-file.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/secure-file.js +10 -4
- package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-retained.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-target.js +8 -1
- package/node_modules/@openclaw/fs-safe/dist/staged-directory.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/staged-directory.js +29 -0
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +5 -3
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.js +13 -2
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.js +71 -3
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.js +22 -0
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-types.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/test-hooks.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/trash.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/types.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-native.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.d.ts +5 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.js +39 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-owner.js +25 -36
- package/node_modules/@openclaw/fs-safe/dist/windows-security-command.js +24 -5
- package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.js +4 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.exe +0 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.js +7 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.d.ts +16 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.js +51 -0
- package/node_modules/@openclaw/fs-safe/dist/write-file-handle.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/write-file-handle.js +12 -10
- package/node_modules/@openclaw/fs-safe/docs/advanced.md +2 -1
- package/node_modules/@openclaw/fs-safe/docs/archive.md +16 -0
- package/node_modules/@openclaw/fs-safe/docs/config.md +71 -1
- package/node_modules/@openclaw/fs-safe/docs/contributing.md +45 -5
- package/node_modules/@openclaw/fs-safe/docs/copy.md +45 -5
- package/node_modules/@openclaw/fs-safe/docs/durability.md +34 -7
- package/node_modules/@openclaw/fs-safe/docs/errors.md +8 -0
- package/node_modules/@openclaw/fs-safe/docs/guest.md +2 -0
- package/node_modules/@openclaw/fs-safe/docs/install.md +47 -27
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +6 -6
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.6.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/native-helper.md +79 -6
- package/node_modules/@openclaw/fs-safe/docs/native.md +279 -8
- package/node_modules/@openclaw/fs-safe/docs/permissions.md +64 -15
- package/node_modules/@openclaw/fs-safe/docs/pipe.md +81 -0
- package/node_modules/@openclaw/fs-safe/docs/platform-capabilities.md +63 -0
- package/node_modules/@openclaw/fs-safe/docs/public-api.md +10 -0
- package/node_modules/@openclaw/fs-safe/docs/queue-link-free-design.md +237 -0
- package/node_modules/@openclaw/fs-safe/docs/quickstart.md +23 -3
- package/node_modules/@openclaw/fs-safe/docs/reading.md +56 -2
- package/node_modules/@openclaw/fs-safe/docs/regular-file.md +2 -0
- package/node_modules/@openclaw/fs-safe/docs/secret-file.md +5 -5
- package/node_modules/@openclaw/fs-safe/docs/secure-file.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/security-model.md +35 -0
- package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +31 -13
- package/node_modules/@openclaw/fs-safe/docs/staged-file.md +18 -0
- package/node_modules/@openclaw/fs-safe/docs/store.md +15 -0
- package/node_modules/@openclaw/fs-safe/docs/temp.md +39 -0
- package/node_modules/@openclaw/fs-safe/docs/testing.md +13 -0
- package/node_modules/@openclaw/fs-safe/docs/types.md +51 -81
- package/node_modules/@openclaw/fs-safe/docs/walk.md +4 -1
- package/node_modules/@openclaw/fs-safe/docs/watch.md +3 -2
- package/node_modules/@openclaw/fs-safe/docs/writing.md +121 -14
- package/node_modules/@openclaw/fs-safe/package.json +21 -12
- package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-freebsd-arm64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-freebsd-arm64/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-freebsd-x64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-freebsd-x64/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
- package/package.json +5 -5
- package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.cs +0 -336
- package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.ps1 +0 -90
|
@@ -100,6 +100,29 @@ On Linux x64 or arm64 with Docker, run `pnpm build`, copy the matching GNU artif
|
|
|
100
100
|
artifact and run native security and no-replace move tests in Rocky Linux 8
|
|
101
101
|
(glibc 2.28). Both GNU architectures execute this load test on matching runners.
|
|
102
102
|
|
|
103
|
+
### Windows ARM64 builds
|
|
104
|
+
|
|
105
|
+
`windows-11-arm` builds and runs the ARM64 MSVC addon using native ARM64 Node
|
|
106
|
+
and Rust, and separately tests x64 Node and Rust under Windows emulation. Both
|
|
107
|
+
lanes run the native suite, main-thread and Worker private-creation/descriptor
|
|
108
|
+
proof, and root-only npm/pnpm install smoke. PE inspection matches the loaded
|
|
109
|
+
consumer binary's hash, architecture and required NT imports. Omitted-optionals
|
|
110
|
+
creation probes in the x64-emulated cell have a 600-second deadline: three
|
|
111
|
+
pre-helper measurements had a 183.18-second median, with 140 PowerShell launches
|
|
112
|
+
accounting for 98–99% of wall time. This is over three times that median, rounded
|
|
113
|
+
up to a whole minute. Native ARM64 and x64 cells, and every other consumer probe,
|
|
114
|
+
retain their 120-second deadlines. Release builds
|
|
115
|
+
also run the ARM64 native security and Worker tests before uploading the binding.
|
|
116
|
+
|
|
117
|
+
The only linked C codec is `zstd-sys`; `cargo test --workspace --locked` and the
|
|
118
|
+
release addon build compile it for `aarch64-pc-windows-msvc`. Bzip2 uses
|
|
119
|
+
`libbz2-rs-sys`, and deflate uses `miniz_oxide`, both Rust implementations.
|
|
120
|
+
The portable WASM build uses the checksum-pinned x64 WASI SDK under Windows
|
|
121
|
+
emulation because that SDK has no Windows ARM64 host archive. Its compiler and
|
|
122
|
+
archiver produce wasm32 only; the addon still uses MSVC for the selected target.
|
|
123
|
+
Bun 1.4.2 has no Windows ARM64 artifact; the existing Windows x64 Bun lanes remain
|
|
124
|
+
the Bun runtime proof. Native Windows ARM64 support is qualified with Node.
|
|
125
|
+
|
|
103
126
|
## Test
|
|
104
127
|
|
|
105
128
|
```bash
|
|
@@ -213,6 +236,16 @@ checkout synchronization; record the tested source revision or diff, Testbox ID,
|
|
|
213
236
|
and Actions run. This backend is Linux-only and does not accept Crabbox's direct
|
|
214
237
|
SSH `--script` or `--download` flags. The workflow provides no application secrets.
|
|
215
238
|
|
|
239
|
+
### Node baseline benchmark
|
|
240
|
+
|
|
241
|
+
After `pnpm build` and `pnpm native:build`, run `pnpm benchmark:node --json results.json --markdown results.md` on an idle machine. This runs native `off` and `require` in separate processes and writes combined JSON, one JSON file per mode, and a side-by-side Markdown table. `require` fails if its binding cannot load. `--mode off` runs without a native build.
|
|
242
|
+
|
|
243
|
+
Use `--temp-root /path/on/the/target/filesystem` to select the measured storage (otherwise the OS temporary directory is used). In particular, avoid tmpfs for durable-write comparisons. Record the filesystem, OS, Node version, CPU and load with the results; the JSON includes filesystem magic, artifact hashes, runtime details and sample observations. The [benchmark guide](https://github.com/openclaw/fs-safe/tree/main/benchmarks#node-baseline-comparison) describes the workloads and optional Linux syscall tracing.
|
|
244
|
+
|
|
245
|
+
Defaults are seven alternating raw/safe/control blocks, three warmup invocations per arm, and 30 calls per sample (fewer for traversal and extraction). Each sample is a mean of timed calls; reported latency is the median sample mean, not a tail percentile. Setup, verification and cleanup are outside timing. Adjust `--iterations`, `--samples`, `--warmup`, or use `--filter read` for a focused run.
|
|
246
|
+
|
|
247
|
+
Ratios divide fs-safe time by raw Node time; smaller is faster. The A/A row and per-operation A/A column compare identical raw calls and show measurement noise. Raw Node is **not security-equivalent**: raw writes truncate in place, while fs-safe stages and atomically publishes; durable raw writes sync the file, while fs-safe also synchronizes publication where supported. Each row documents the actual comparison. Treat ratios near the A/A spread cautiously, and retain all samples when comparing changes. No benchmark changes a production safety or durability default.
|
|
248
|
+
|
|
216
249
|
### Method benchmarks
|
|
217
250
|
|
|
218
251
|
`pnpm benchmark:methods` measures the callable library surface against synthetic
|
|
@@ -262,14 +295,14 @@ OS/CPU/libc selection, a native-required SHA-256 operation, and fresh-process
|
|
|
262
295
|
optionals. Omitted-optionals installs also verify that all public subpaths can
|
|
263
296
|
be imported, without implying every operation remains available.
|
|
264
297
|
|
|
265
|
-
Host-only smoke supplies the
|
|
298
|
+
Host-only smoke supplies the seven foreign packages using their unchanged real
|
|
266
299
|
manifests and clearly marked synthetic, non-executable payloads. Every foreign
|
|
267
300
|
metadata/tarball endpoint is checked before installation, so a missing fixture
|
|
268
301
|
cannot masquerade as successful platform filtering. These temporary fixtures
|
|
269
302
|
never enter `packages/`, release artifacts, or the publish manifest. They prove
|
|
270
303
|
installer filtering, not foreign native compilation or execution. Full release
|
|
271
|
-
collection uses the actual
|
|
272
|
-
`pnpm package:collect` after assembling all
|
|
304
|
+
collection uses the actual eight collected native tarballs instead. Run it with
|
|
305
|
+
`pnpm package:collect` after assembling all eight real bindings; missing targets
|
|
273
306
|
fail collection. `pnpm package:collect --allow-host-only` exercises the same
|
|
274
307
|
lifecycle boundary locally but proves only the host. Both collection commands
|
|
275
308
|
require the pnpm lifecycle CLI path; JavaScript CLIs run through Node and standalone
|
|
@@ -277,7 +310,7 @@ require the pnpm lifecycle CLI path; JavaScript CLIs run through Node and standa
|
|
|
277
310
|
direct `node` invocation without lifecycle metadata is unsupported. Archive
|
|
278
311
|
codecs and their dependencies are packed from the installed dependency graph.
|
|
279
312
|
|
|
280
|
-
PR CI builds and executes all
|
|
313
|
+
PR CI builds and executes all eight shipped bindings. The existing check names
|
|
281
314
|
stay stable; extra runner/runtime combinations add checks without changing the
|
|
282
315
|
repository ruleset. Native lanes run Node 24; the GNU host lanes also exercise
|
|
283
316
|
Bun 1.4.2 (as do macOS and Windows).
|
|
@@ -294,6 +327,7 @@ Bun 1.4.2 (as do macOS and Windows).
|
|
|
294
327
|
| Native check | `macos-15-intel` | x64 | Node 24, Bun 1.4.2 |
|
|
295
328
|
| Native check | `fs-safe-windows-16core` (`windows-latest`) | x64 | Node 24, Bun 1.4.2 |
|
|
296
329
|
| Native check | `windows-2022` (standard hosted) | x64 | Node 24, Bun 1.4.2 |
|
|
330
|
+
| Windows ARM64 host | `windows-11-arm` | arm64 native and x64 emulated | Node 24 |
|
|
297
331
|
| Native check (Alpine 3.24) | `ubuntu-latest` | x64 / musl | Node 24 |
|
|
298
332
|
| Native check (Alpine 3.24) | `ubuntu-24.04-arm` | arm64 / musl | Node 24 |
|
|
299
333
|
| GNU glibc 2.28 build + Rocky Linux 8 load | `ubuntu-latest` | x64 / glibc | Node 24 |
|
|
@@ -302,11 +336,17 @@ Bun 1.4.2 (as do macOS and Windows).
|
|
|
302
336
|
| Coverage | `ubuntu-latest`, `macos-15`, `fs-safe-windows-16core` | host | Node 22 |
|
|
303
337
|
|
|
304
338
|
Both musl lanes also run root-only package smoke with the real host binding.
|
|
305
|
-
The
|
|
339
|
+
The eight-target source build matrix still runs on release tags. The smoke writes manager versions,
|
|
306
340
|
cases, and synthetic-fixture scope to `release-artifacts/consumer-proof.json`.
|
|
307
341
|
|
|
308
342
|
## Docs
|
|
309
343
|
|
|
344
|
+
`pnpm docs:check` runs examples against the built package and compiles every
|
|
345
|
+
TypeScript block in `README.md`, the lock API signatures, and the custom stale-lock
|
|
346
|
+
policy example against its declarations. Run `pnpm build` first.
|
|
347
|
+
The compiler supplies typed application context for excerpted variables such as
|
|
348
|
+
`download`; package API calls and callback values remain fully type-checked.
|
|
349
|
+
|
|
310
350
|
The docs site is rendered recursively from Markdown files under `docs/` by `scripts/build-docs-site.mjs`. Build locally to preview:
|
|
311
351
|
|
|
312
352
|
```bash
|
|
@@ -68,12 +68,24 @@ On Linux, automatic byte copying also uses the native binding when available. It
|
|
|
68
68
|
|
|
69
69
|
Tree-copy cleanup attempts every acquired close once, even when another close fails. Portable file handles close output before input; completed directories close their pinned source before target; the public wrapper then closes its source before the destination parent. A copy, identity, metadata, native-clone, or cancellation failure already observed at one of those scopes remains the reported value instead of being replaced by cleanup. If that scope otherwise succeeded, its first close failure is reported unchanged. Concurrent siblings have no global structural close order: the first observed sibling failure stops new work while admitted work settles. Portable copying records caller cancellation when it occurs, so a later abort during cleanup cannot replace an earlier copy failure.
|
|
70
70
|
|
|
71
|
+
For guarded per-file copying, [`Root.copyIn`](writing.md#copy-metadata-and-source-links) can preserve timestamps and Windows non-permission attributes with `preserveMetadata`, and copy literal links with `sourceSymlinks: "copy-link"`. Permission selection remains separate. Windows reparse payload handling is shared with this tree copier.
|
|
72
|
+
|
|
71
73
|
Clones preserve file contents, empty directories, timestamps, executable modes where supported, and literal symbolic links. Editing a clone does not modify its source. Unsupported filesystem operations fail; callers may choose their own copy or checkout fallback after the failed operation has settled.
|
|
72
74
|
|
|
73
75
|
Native Windows byte copies can store large zero-filled chunks as sparse ranges when the destination is initially empty and its filesystem supports sparse files. This still reads every source byte and creates an independent copy.
|
|
74
76
|
|
|
75
77
|
The ReFS backend rejects files with alternate data streams and unsupported reparse-point types instead of silently losing their contents. Symbolic links and junctions are preserved.
|
|
76
78
|
|
|
79
|
+
For successful native ReFS clones through `copyTree` or `Root.copyIn`, all full
|
|
80
|
+
clusters are block-cloned; a final partial cluster may be copied. Both preserve exact bytes and logical file size.
|
|
81
|
+
The shared implementation keeps EOF at the source size and rounds only the final
|
|
82
|
+
clone request to the allocation-cluster boundary. Microsoft's
|
|
83
|
+
[block-cloning rules](https://learn.microsoft.com/en-us/windows/win32/fileio/block-cloning#restrictions-and-remarks)
|
|
84
|
+
require cluster-aligned ranges and setting destination EOF before cloning;
|
|
85
|
+
rounding a request does not promise physical sharing of a partial final cluster.
|
|
86
|
+
Windows x64 and ARM64 tests compare every full-cluster LCN for both APIs,
|
|
87
|
+
including the 4 MiB shared prefix of a 4 MiB + 1 byte file.
|
|
88
|
+
|
|
77
89
|
Failed ReFS clones attempt to remove their partial output through the retained directory handles. On Windows versions that reject the ignore-readonly deletion flag, clone rollback retries without that flag so ordinary output can be removed. It never clears readonly attributes: readonly output can remain, and the original clone error includes the cleanup failure. Other processes retaining output handles can delay deletion beyond settlement; a failed call does not guarantee an absent destination.
|
|
78
90
|
|
|
79
91
|
XFS and ZFS preserve regular-file and directory modes, timestamps, extended attributes, and ACLs. They reject special files, symlink extended attributes, non-UTF-8 names, and directory nesting deeper than 128 levels. Hardlinked source files become independent reflinked files. Portable byte copying preserves file contents, empty directories, modes where supported, file and directory timestamps, and literal symbolic links; it does not promise ownership, ACL, extended-attribute, alternate-stream, or sparse-layout preservation. On Windows, byte copying rejects unresolved symbolic links because Node does not expose their file/directory link type; resolved links keep their literal target and source type. POSIX dangling links are preserved. Choose a copying policy that meets the caller's metadata requirements; automatic copying can select either path.
|
|
@@ -192,6 +204,16 @@ Byte copying retains fractional file and directory access/modification timestamp
|
|
|
192
204
|
|
|
193
205
|
After building the host native binding, run `pnpm test test/clone.test.ts test/copy-tree.test.ts`. APFS tests can use the normal macOS temporary directory. For Btrfs, ReFS, XFS, or ZFS, set `FS_SAFE_CLONE_TEST_ROOT` to an existing writable directory on that filesystem. The test creates and cleans only its own temporary children. An explicitly configured unsupported directory fails the test rather than silently skipping platform proof. XFS and ZFS metadata tests require the `attr` and `acl` utilities.
|
|
194
206
|
|
|
207
|
+
The `ReFS copyIn` CI workflow creates a disposable 2 GiB expanding VHD on
|
|
208
|
+
Windows Server 2025 x64 and Windows 11 ARM64. It formats the latter as a Dev
|
|
209
|
+
Drive, verifies that the mounted filesystem is ReFS, and sets
|
|
210
|
+
`FS_SAFE_CLONE_TEST_ROOT`. `test/root-copy-refs.test.ts` runs the built package on
|
|
211
|
+
the main thread and in a real Node Worker, checks shared physical extents with
|
|
212
|
+
`readWindowsFileExtents`, independent writes, exclusive publication, byte limits,
|
|
213
|
+
cancellation cleanup, and revocation between file copies. Both jobs also run the
|
|
214
|
+
tree clone suite, then detach and delete the VHD even after failure. A failed
|
|
215
|
+
volume setup fails the job; it never substitutes NTFS or skips the ReFS proof.
|
|
216
|
+
|
|
195
217
|
Run `node scripts/clone-xfs-proof.mjs MOUNT` on a real XFS volume to verify the public API, hashes, independent writes, and shared physical extents. It requires `filefrag` from `e2fsprogs`. Add `no-reflink` for an XFS fixture formatted with reflinks disabled; strict copying must fail and automatic copying must succeed through byte copying.
|
|
196
218
|
|
|
197
219
|
Run `node scripts/clone-zfs-proof.mjs MOUNT POOL` on a dedicated, otherwise idle Linux ZFS pool with compression and deduplication disabled. It verifies both `copyTree` and `Root.copyIn` through hashes and changes in the documented `bclonesaved` pool counter. It requires `zfs`, `zpool`, and `findmnt`, including permission to run `zpool sync`. Add `no-reflink` for a pool without block cloning to verify strict refusal and automatic byte fallback. The script creates and removes only its temporary directory; it does not create pools or change their properties.
|
|
@@ -226,6 +248,10 @@ readable/writable `fd`, with idempotent `close()` and `Symbol.dispose`, and retu
|
|
|
226
248
|
bigint destination `identity` and `sourceIdentity` (`dev`, `ino`). Both identities
|
|
227
249
|
are readonly and frozen. Closing releases the descriptor, not the file.
|
|
228
250
|
|
|
251
|
+
Use `fs.stat(path, { bigint: true })` for exact Windows file IDs.
|
|
252
|
+
`expectedSourceIdentity` requires bigint components and rejects Number inputs
|
|
253
|
+
with `FsSafeError("path-mismatch")`, including unsafe rounded Numbers.
|
|
254
|
+
|
|
229
255
|
Callers that already pinned a source can pass `expectedSourceIdentity: { dev, ino }`
|
|
230
256
|
from `fs.fstatSync(pinnedSourceFd, { bigint: true })`. fs-safe still admits and
|
|
231
257
|
opens its own source descriptor, and compares its exact bigint identity before
|
|
@@ -280,15 +306,15 @@ for (const name of ["plugin.js", "package.json"]) {
|
|
|
280
306
|
}
|
|
281
307
|
```
|
|
282
308
|
|
|
283
|
-
On canonical POSIX paths, the batch retains root and ancestor directory
|
|
309
|
+
On canonical POSIX and ordinary Windows drive paths, the batch retains root and ancestor directory
|
|
284
310
|
identities across files. Every use still checks those identities without
|
|
285
311
|
following directory symlinks; root and parent canonical paths are rechecked.
|
|
286
312
|
Source opens, source and destination file identities, hardlink policy, exclusive
|
|
287
313
|
creation, bounds, permissions, and cleanup are checked separately for every file.
|
|
288
314
|
No source descriptor, file contents, or successful file-admission result is cached.
|
|
289
315
|
An ancestor replacement rejects even when the leaf parent has not changed.
|
|
290
|
-
Windows
|
|
291
|
-
alias handling. There is no option to bypass validation.
|
|
316
|
+
Windows namespaces, short-name spellings, trailing-dot/space components, and
|
|
317
|
+
noncanonical path spellings keep the full per-file resolver and its alias handling. There is no option to bypass validation.
|
|
292
318
|
|
|
293
319
|
Keep the batch scoped to one capture or materialization, with unchanged source
|
|
294
320
|
and destination directory topology. `close()` and `Symbol.dispose` idempotently
|
|
@@ -299,9 +325,23 @@ identity-checked cleanup contract; the batch is not an atomic tree transaction
|
|
|
299
325
|
or a coherent snapshot of concurrent edits. The single-file helper also reuses
|
|
300
326
|
directory admissions for the duration of its one copy.
|
|
301
327
|
|
|
302
|
-
Both APIs reuse the freshly checked canonical root during initial
|
|
328
|
+
Both APIs reuse the freshly checked canonical root during initial canonical path
|
|
303
329
|
admission. This avoids repeatedly resolving every absolute root component while
|
|
304
330
|
retaining lexical traversal and the before/after root and parent identity checks.
|
|
305
|
-
Windows and root aliases retain the ordinary resolver, including its handling
|
|
331
|
+
Windows namespace spellings and root aliases retain the ordinary resolver, including its handling
|
|
306
332
|
of dot segments in symlink targets. No caller changes are needed for the
|
|
307
333
|
single-file improvement.
|
|
334
|
+
|
|
335
|
+
## Exact identity receipts
|
|
336
|
+
|
|
337
|
+
`copyRootFileSync()` returns exact bigint `identity` (destination) and
|
|
338
|
+
`sourceIdentity` values. Pass `sourceIdentity` to `expectedSourceIdentity` to
|
|
339
|
+
bind another copy to that source, or the destination `identity` when copying the
|
|
340
|
+
new file. `Root.copyIn()` reports exact bigint `dev`/`ino` through
|
|
341
|
+
`onDestinationPublished`; retain that callback receipt when reopening the output.
|
|
342
|
+
`PublishedWriteIdentity` callbacks already use the same exact shape.
|
|
343
|
+
|
|
344
|
+
`readCloneFileMetadata()` now also supplies `exactIdentity`, derived from the
|
|
345
|
+
same native metadata record as its legacy `dev` and `ino` fields. These remain
|
|
346
|
+
point-in-time observations. Exact identity does not grant ownership or protect
|
|
347
|
+
against later replacement; use the operation's identity-checking API.
|
|
@@ -77,15 +77,16 @@ otherwise usable directory. A failed preflight identity check never syncs the
|
|
|
77
77
|
descriptor; a replacement discovered after sync still rejects the operation.
|
|
78
78
|
|
|
79
79
|
`DirectoryReceipt.identity` remains a numeric Node `Stats` object for metadata
|
|
80
|
-
compatibility
|
|
81
|
-
|
|
80
|
+
compatibility. Its `exactIdentity` companion contains immutable bigint `dev`
|
|
81
|
+
and `ino`, projected from the same observation. Library-created receipts and their identity objects retain a
|
|
82
82
|
private exact snapshot; mutating their public fields cannot change the
|
|
83
83
|
directory authorized by that snapshot. Each returned receipt owns a mutable
|
|
84
84
|
numeric metadata copy. Later admissions retain the original metadata snapshot
|
|
85
85
|
even if advisory fields such as mode or timestamps were edited; changed paths
|
|
86
86
|
or identity components reject. Pass the receipt or its original
|
|
87
|
-
identity object through to later operations to retain this evidence.
|
|
88
|
-
|
|
87
|
+
identity object through to later operations to retain this evidence. When copying
|
|
88
|
+
a receipt, preserve `exactIdentity`; pass it to `expectedExistingIdentity`. A copied
|
|
89
|
+
numeric identity without its exact companion is accepted only when both components are
|
|
89
90
|
safe integers and, on Windows, nonzero. Rounded or unknown caller identities
|
|
90
91
|
fail with `path-mismatch` rather than authorizing a different directory.
|
|
91
92
|
|
|
@@ -149,6 +150,27 @@ target. It pins the source with nonblocking `O_NOFOLLOW`, optionally verifies
|
|
|
149
150
|
`expectedSourceIdentity`, tries a hardlink first, then synchronizes the target
|
|
150
151
|
parent directory.
|
|
151
152
|
|
|
153
|
+
Pass `expectedSourceIdentity` from `fs.stat(path, { bigint: true })` (or
|
|
154
|
+
`fs.lstat`) for exact Windows file IDs. Unsafe Number components cannot prove
|
|
155
|
+
identity and reject with `FsSafeError("path-mismatch")` before publication;
|
|
156
|
+
safe integer Numbers remain supported. Internal source fences use bigint stats.
|
|
157
|
+
|
|
158
|
+
Every successful publication returns `exactIdentity: Readonly<{ dev: bigint;
|
|
159
|
+
ino: bigint }>` alongside the unchanged `identity: Stats` metadata view. Both
|
|
160
|
+
come from one final bigint `fstat` of the retained descriptor. Pass
|
|
161
|
+
`result.exactIdentity` back as `expectedSourceIdentity` when publishing that file
|
|
162
|
+
again. Passing the whole publication result is also supported: its explicit
|
|
163
|
+
`exactIdentity` takes precedence. `sameFileIdentity()` and
|
|
164
|
+
`copyRootFileSync().expectedSourceIdentity` accept that same receipt shape.
|
|
165
|
+
|
|
166
|
+
On Windows, the Number view may round an NTFS file ID; it must not be used for
|
|
167
|
+
identity checks. Do not re-stat a pathname to reconstruct an earlier publication
|
|
168
|
+
receipt: that can observe a replacement. After a failed publication,
|
|
169
|
+
`PublishFileExclusiveFailureDetails.exactTargetIdentity` identifies the created
|
|
170
|
+
target when known; `targetIdentity` remains the legacy numeric view. Retain the
|
|
171
|
+
exact failure field when inspecting a preserved target. Unknown or mismatching
|
|
172
|
+
identities still fail closed.
|
|
173
|
+
|
|
152
174
|
Trusted relative source and target paths are resolved to absolute paths before
|
|
153
175
|
authority checks. On Windows, ordinary drive-relative operands are anchored at
|
|
154
176
|
entry before namespace-alias admission. A caller-supplied `parentReceipt` must
|
|
@@ -171,7 +193,7 @@ try {
|
|
|
171
193
|
onSyncFailure: "preserve",
|
|
172
194
|
parentReceipt: backupDirectory,
|
|
173
195
|
});
|
|
174
|
-
recordDurableBackup(result.
|
|
196
|
+
recordDurableBackup(result.exactIdentity, result.directorySync);
|
|
175
197
|
} catch (error) {
|
|
176
198
|
if (
|
|
177
199
|
error instanceof FsSafeError &&
|
|
@@ -224,9 +246,11 @@ count limits need not mean the entire filesystem lacks hard-link support.
|
|
|
224
246
|
`link-required` and `link-or-copy` do not use `renameat2` and are unchanged.
|
|
225
247
|
|
|
226
248
|
`"link-required"` propagates an unsupported hardlink failure.
|
|
227
|
-
`"link-or-copy"` falls back only for `EPERM`, `EXDEV`, `ENOTSUP`,
|
|
249
|
+
`"link-or-copy"` falls back only for `EACCES`, `EPERM`, `EXDEV`, `ENOTSUP`,
|
|
228
250
|
`EOPNOTSUPP`, or `ENOSYS`; `isHardlinkFallbackError()` exposes that exact
|
|
229
|
-
classifier
|
|
251
|
+
classifier, including Android/Termux hardlink permission refusals. A genuine
|
|
252
|
+
permission denial still fails the fallback's exclusive target creation; `EEXIST`
|
|
253
|
+
never permits fallback. The fallback copies from the pinned source into a `wx` target,
|
|
230
254
|
fsyncs it, and fences source and target identity and content before reporting
|
|
231
255
|
success. `parentReceipt`, when supplied, must name the target's direct parent.
|
|
232
256
|
|
|
@@ -319,6 +343,9 @@ thread. Both paths stream bounded buffers rather than loading the file into memo
|
|
|
319
343
|
The fallback sizes its scratch buffer to small files and grows it if a stale
|
|
320
344
|
size hint is exceeded, while still probing for actual EOF and byte-limit overflow.
|
|
321
345
|
Native mode `require` keeps its usual fail-closed loader semantics.
|
|
346
|
+
On Windows, pathname hashing can read through its privately owned handle
|
|
347
|
+
without reopening it. Hashing a caller-owned handle still uses an independent
|
|
348
|
+
reader to preserve the caller's position.
|
|
322
349
|
|
|
323
350
|
### Synchronous hashing
|
|
324
351
|
|
|
@@ -38,6 +38,14 @@ class FsSafeError extends Error {
|
|
|
38
38
|
|
|
39
39
|
`cause` is available through the standard `Error` `cause` property when the failure was triggered by a `NodeJS.ErrnoException` (e.g. a wrapped `EACCES`). Inspect it for the original `code` / `errno` / `syscall` if you need finer-grained reporting.
|
|
40
40
|
|
|
41
|
+
Native Windows failures, including private directory and file creation failures,
|
|
42
|
+
retain the original numeric Win32 error in `errno` (for example, 183 for
|
|
43
|
+
`ERROR_ALREADY_EXISTS` or 5 for `ERROR_ACCESS_DENIED`). This also applies to the
|
|
44
|
+
Windows security command fallback. A wrapped failure retains that number on its
|
|
45
|
+
original `cause`; the semantic `FsSafeError.code` remains the contract. Policy
|
|
46
|
+
checks and preflight rejections that do not execute a failing Windows operation
|
|
47
|
+
have no Win32 error number. Node-managed filesystem errors retain Node's own errno.
|
|
48
|
+
|
|
41
49
|
Guarded write preparation describes permission, read-only filesystem, and disk-space
|
|
42
50
|
failures with messages such as `permission denied (EACCES)` or
|
|
43
51
|
`no space left on device (ENOSPC)`. Other errno failures include their code in
|
|
@@ -56,6 +56,8 @@ can replace only an **empty directory created concurrently**; non-empty
|
|
|
56
56
|
directories and non-directory targets are refused as `EEXIST`. An existing
|
|
57
57
|
empty directory observed by the absence check is also refused. No empty claim
|
|
58
58
|
directory is exposed before publishing the completed source.
|
|
59
|
+
An `ENOTDIR` rename failure rechecks the source before reporting a destination
|
|
60
|
+
collision; a detected source substitution reports `ESTALE` instead of `EEXIST`.
|
|
59
61
|
|
|
60
62
|
Identity checks and mutation remain separate, not source-name compare-and-swap.
|
|
61
63
|
The fallback verifies the published identity afterward. A file fallback failure
|
|
@@ -106,6 +106,7 @@ Use the main entry for the common surface, or the focused subpaths when you want
|
|
|
106
106
|
| `@openclaw/fs-safe/json` | `tryReadJson`, `readJson`, `readJsonIfExists`, `writeJson`, sync variants. |
|
|
107
107
|
| `@openclaw/fs-safe/store` | `fileStore()`, `fileStoreSync()`, and `jsonStore<T>()`. |
|
|
108
108
|
| `@openclaw/fs-safe/secret` | Secret file read/write helpers. |
|
|
109
|
+
| `@openclaw/fs-safe/pipe` | Native-owned, reopenable anonymous pipes for one-shot child input. |
|
|
109
110
|
| `@openclaw/fs-safe/atomic` | `replaceFileAtomic`, `writeTextAtomic`, `replaceDirectoryAtomic`, `movePathWithCopyFallback`. |
|
|
110
111
|
| `@openclaw/fs-safe/durability` | Pinned directories, strict sync, durable directory creation, exclusive publication, and streaming SHA-256. |
|
|
111
112
|
| `@openclaw/fs-safe/temp` | `tempWorkspace`, `withTempWorkspace`, sync variants, `resolveSecureTempRoot`. |
|
|
@@ -125,9 +126,11 @@ Use the main entry for the common surface, or the focused subpaths when you want
|
|
|
125
126
|
|
|
126
127
|
`@openclaw/fs-safe` bundles an import-free WASM build of its Rust TAR parser and zstd/bzip2 codecs for guarded [archive extraction and bounded entry reads](archive.md). Plain TAR, gzip, zstd, and bzip2 work in `off` and missing-native `auto`, including installs with all optional dependencies omitted; gzip uses Node's built-in decoder. These archive fallbacks need no runtime interpreter, download, or consumer compiler. ZIP fallback uses lazily loaded optional `jszip` and reports a missing-dependency error without it. Public subpaths remain safe to import with all optional dependencies omitted, but imports do not prove native availability. Native `require` remains strict, and an available native operation's failure never triggers a WASM retry.
|
|
127
128
|
|
|
128
|
-
There are no peer dependencies. Exact-version optional packages carry the
|
|
129
|
+
There are no peer dependencies. Exact-version optional packages carry the
|
|
129
130
|
native targets and npm-compatible OS, CPU, and Linux libc filters install only
|
|
130
|
-
the matching binary.
|
|
131
|
+
the matching binary. Windows ARM64 Node selects
|
|
132
|
+
`@openclaw/fs-safe-win32-arm64-msvc`; x64 Node on the same host selects the x64
|
|
133
|
+
package. Consumers do not run a Rust build, download code at
|
|
131
134
|
runtime, or execute a postinstall step. Omitting optional dependencies keeps
|
|
132
135
|
fallback-capable operations working in `auto` or `off`. Native-only
|
|
133
136
|
features, including strict owned-tree temp cleanup, retained-directory staging,
|
|
@@ -145,42 +148,59 @@ before deploying with native mode `require` or native-only features.
|
|
|
145
148
|
The native addon supports kernels without `openat2` (before Linux 5.6) and
|
|
146
149
|
containers returning `ENOSYS` or probe-time `EPERM` for that syscall. Beneath
|
|
147
150
|
opens use a guarded no-follow component walk and report `best-effort`.
|
|
148
|
-
Nested no-clobber moves retain
|
|
149
|
-
|
|
151
|
+
Nested no-clobber moves retain native parent admission and exact identity checks;
|
|
152
|
+
keep native mode enabled. If `renameat2(RENAME_NOREPLACE)` is also unsupported,
|
|
153
|
+
Linux `auto` uses [move fallbacks](writing.md#fs-move-from-to-options), including
|
|
154
|
+
a directory rename that can replace a concurrently created empty directory.
|
|
155
|
+
Native `require` refuses these fallbacks. Strict bounded cleanup still
|
|
150
156
|
requires `openat2` with `RESOLVE_NO_XDEV` and reports `helper-unavailable`
|
|
151
157
|
without it. See [Linux capability behavior and limits](native.md#linux-without-openat2).
|
|
152
158
|
|
|
153
159
|
### Windows security fallback
|
|
154
160
|
|
|
155
|
-
Windows raw owner/DACL inspection, private-directory creation, and secure-file
|
|
156
|
-
reads work without the addon in `auto` or `off` mode
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
161
|
+
Windows pathname permission inspection, raw owner/DACL inspection, private-directory creation, and secure-file
|
|
162
|
+
reads work without the addon in `auto` or `off` mode using the packaged
|
|
163
|
+
`windows-security-helper.exe`. This prebuilt AnyCPU console application targets
|
|
164
|
+
.NET Framework 4.6.2 or newer (CLR v4.0.30319), with the modern path handling
|
|
165
|
+
used by Windows PowerShell 5.1. It runs as native x64 on x64 Windows and native ARM64 on Windows
|
|
166
|
+
11 ARM with .NET Framework 4.8.1, including when the calling Node is x64.
|
|
167
|
+
No compiler, .NET SDK, or PowerShell is needed by this fallback at runtime.
|
|
168
|
+
|
|
169
|
+
Each check starts a separate helper process. The compatibility owner/ACL query
|
|
170
|
+
passes `["owner", targetPath]` as arguments and preserves .NET's named security
|
|
171
|
+
lookup and ACL normalization. Other paths and expected identities stay
|
|
172
|
+
in environment variables, and descriptor checks inherit the actual borrowed
|
|
173
|
+
handle through stdin. No security facts are cached or checks combined. The
|
|
174
|
+
fallback preserves private DACLs at creation and inspects the same open handle
|
|
175
|
+
that supplies secure-file bytes. Each capability emits a path-free
|
|
176
|
+
`FS_SAFE_NATIVE_FALLBACK` warning once per feature in each JavaScript context.
|
|
177
|
+
It names the native availability reason and expected package version; see
|
|
178
|
+
[troubleshooting fallback warnings](native-helper.md#troubleshooting-fallback-warnings).
|
|
179
|
+
|
|
180
|
+
Release Windows native addons and `windows-security-helper.exe` are
|
|
181
|
+
Authenticode-signed by **OpenClaw Foundation** with a timestamp. The executable
|
|
182
|
+
is included in the main npm package and covered by its npm provenance, just
|
|
183
|
+
like its JavaScript. Source lives in the repository; consumers receive only the
|
|
184
|
+
executable. Missing or disallowed execution fails closed with a rebuild/reinstall
|
|
185
|
+
hint. Signing does not guarantee acceptance by every Smart App Control,
|
|
186
|
+
AppLocker, or WDAC policy; local policy can still block execution in user
|
|
187
|
+
directories. There is no PowerShell retry. Such hosts can also restrict
|
|
188
|
+
PowerShell to Constrained Language Mode, where the former Add-Type fallback
|
|
189
|
+
was unavailable. The package does not bypass policies or add exclusions.
|
|
190
|
+
|
|
191
|
+
Native `require` still rejects a missing binding or capability, and an available
|
|
192
|
+
native operation's error never triggers a command retry. See
|
|
193
|
+
[Permissions](permissions.md), [Secure file reads](secure-file.md), and
|
|
194
|
+
[reproducible helper builds](native.md#windows-helper-build).
|
|
177
195
|
|
|
178
196
|
## Native helper policy
|
|
179
197
|
|
|
180
198
|
### Supported native platforms
|
|
181
199
|
|
|
182
200
|
Prebuilt bindings cover Linux x64/arm64 (GNU glibc **2.28 or newer**, or musl),
|
|
183
|
-
macOS x64/arm64,
|
|
201
|
+
macOS x64/arm64, Windows x64/arm64, and FreeBSD 14.4+ x64/arm64. FreeBSD provides the
|
|
202
|
+
[documented subset](native.md#freebsd-14-4-and-newer); its other operations retain
|
|
203
|
+
guarded fallbacks in `auto`. The GNU baseline includes RHEL 8, Rocky Linux 8,
|
|
184
204
|
and AlmaLinux 8. A compatible Node 22+ runtime and the kernel/filesystem features
|
|
185
205
|
required by each operation are still necessary; a loadable addon alone does not
|
|
186
206
|
guarantee every native capability. Systems older than glibc 2.28 are outside the
|
|
@@ -16,11 +16,11 @@ Current releases install the matching exact-version optional platform package.
|
|
|
16
16
|
- Run on Node.js 22 or newer.
|
|
17
17
|
- Update `@openclaw/fs-safe` and regenerate every lock or shrinkwrap file your
|
|
18
18
|
deployment consumes.
|
|
19
|
-
- Keep optional dependencies enabled
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
- Keep optional dependencies enabled for the matching native package and the
|
|
20
|
+
JSZip fallback. Current TAR/gzip/zstd/bzip2 fallbacks are bundled and work
|
|
21
|
+
with optional dependencies omitted. Native loading no longer depends on
|
|
22
|
+
Python. See [runtime dependencies](install.md#runtime-dependencies) for the
|
|
23
|
+
current installation contract.
|
|
24
24
|
|
|
25
25
|
If you call `resolveRootPath()` or `resolveRootPathSync()` directly, upgrade to
|
|
26
26
|
0.5: versions through 0.4.7 could approve an in-root symlink traversal that
|
|
@@ -170,7 +170,7 @@ See [File locks](sidecar-lock.md), [Secret files](secret-file.md), and
|
|
|
170
170
|
## 7. Gate native-only features
|
|
171
171
|
|
|
172
172
|
Version 0.5 required native support for `createPrivateDirectory()`. Current
|
|
173
|
-
releases also support a packaged
|
|
173
|
+
releases also support a packaged .NET Framework helper in native `auto` and `off`
|
|
174
174
|
modes, retaining its creation-time protected DACL. Check the
|
|
175
175
|
[Windows security fallback prerequisites](install.md#windows-security-fallback)
|
|
176
176
|
before relying on this route. The API remains Windows-only, explicit native
|
|
@@ -31,7 +31,7 @@ Zstd/bzip2 TAR extraction and bounded reads required the binding in version 0.6;
|
|
|
31
31
|
current versions also support bundled WASM codecs in `auto` and `off` through the
|
|
32
32
|
same guarded TAR pipeline. ZIP fallback still requires optional JSZip.
|
|
33
33
|
Windows private-directory creation required the binding in version 0.6; current
|
|
34
|
-
releases also support [packaged Windows security
|
|
34
|
+
releases also support [packaged Windows security helper](install.md#windows-security-fallback)
|
|
35
35
|
in `auto` and `off` mode while preserving the creation-time protected DACL.
|
|
36
36
|
|
|
37
37
|
## Deployment checklist
|
|
@@ -5,8 +5,10 @@ description: "How fs-safe loads its platform-specific native filesystem primitiv
|
|
|
5
5
|
|
|
6
6
|
# Native helper policy
|
|
7
7
|
|
|
8
|
-
`@openclaw/fs-safe` declares
|
|
9
|
-
x64/arm64 (glibc or musl), macOS x64/arm64, and
|
|
8
|
+
`@openclaw/fs-safe` declares exact-version optional packages for Linux
|
|
9
|
+
x64/arm64 (glibc or musl), macOS x64/arm64, Windows x64/arm64, and FreeBSD x64/arm64.
|
|
10
|
+
The FreeBSD 14.4+ bindings expose the [qualified subset](native.md#freebsd-14-4-and-newer).
|
|
11
|
+
Package-manager
|
|
10
12
|
OS, CPU, and libc filters install only the matching package. The loader requires
|
|
11
13
|
that package lazily, without runtime downloads, postinstall scripts, or a
|
|
12
14
|
consumer Rust build.
|
|
@@ -29,18 +31,87 @@ The equivalent environment variables are `FS_SAFE_NATIVE_MODE` and `OPENCLAW_FS_
|
|
|
29
31
|
| `off` | Do not load a native package. Use supported fallbacks and reject native-only operations deterministically. |
|
|
30
32
|
| `require` | Throw `FsSafeError("helper-unavailable")` instead of falling back when an operation needs the native binding and it cannot load. |
|
|
31
33
|
|
|
34
|
+
See [What needs the native binding](native.md#what-needs-the-native-binding)
|
|
35
|
+
for the native-only APIs and options.
|
|
36
|
+
|
|
32
37
|
See [Archive extraction](archive.md) for native, bundled WASM, and ZIP backends.
|
|
33
38
|
Native archive-operation failures are terminal; `auto` does not retry them through a
|
|
34
39
|
fallback. `require` rejects missing bindings or required capabilities.
|
|
35
40
|
|
|
41
|
+
A loaded binding may provide only a subset of native operations. Each operation
|
|
42
|
+
checks its required capabilities before native dispatch: missing capabilities
|
|
43
|
+
select the existing guarded fallback in `auto`, or report `helper-unavailable`
|
|
44
|
+
in `require`. A failure from an available native method is still an operation
|
|
45
|
+
failure; it is not treated as a missing capability.
|
|
46
|
+
|
|
36
47
|
Windows owner/DACL inspection, private-directory creation, and secure reads can
|
|
37
|
-
use [
|
|
48
|
+
use [Windows helper fallbacks](install.md#windows-security-fallback) in `auto` and
|
|
38
49
|
`off`; `require` stays strict, and native operation failures remain terminal.
|
|
39
50
|
Bun macOS/Linux canonicalization uses the addon under its
|
|
40
51
|
[runtime requirements](install.md#bun-runtime).
|
|
41
52
|
|
|
42
53
|
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.
|
|
43
54
|
|
|
55
|
+
## Runtime health checks
|
|
56
|
+
|
|
57
|
+
`getFsSafeNativeStatus(options?: { probe?: boolean }): Promise<FsSafeNativeStatus>` is
|
|
58
|
+
exported from `@openclaw/fs-safe/config` and the main entry point. It reports the
|
|
59
|
+
loader state without parsing warnings, on every supported runtime. A doctor can
|
|
60
|
+
explicitly probe without changing the configured mode or mutating the filesystem:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { getFsSafeNativeStatus } from "@openclaw/fs-safe/config";
|
|
64
|
+
|
|
65
|
+
const status = await getFsSafeNativeStatus({ probe: true });
|
|
66
|
+
console.log(status.loaded ? "Native helper active" : `Native helper: ${status.reason}`);
|
|
67
|
+
if (status.guidance) console.log(status.guidance);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
| Fields | Meaning |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `mode`, `modeSource` | Effective policy and its source; only environment variable names are exposed. |
|
|
73
|
+
| `reason`, `loaded` | `not-probed`, `loaded`, `disabled`, `unsupported-target`, `package-missing`, `load-failed`, or `capability-missing`; `loaded` is true only for `loaded`. |
|
|
74
|
+
| `target` | Runtime platform/architecture/libc, including unsupported targets. |
|
|
75
|
+
| `packageName`, `packageVersion` | Expected platform package and exact pinned manifest version; absent for unsupported targets. |
|
|
76
|
+
| `loadErrorCode` | Allowlisted code or `UNKNOWN`, only for `load-failed`. |
|
|
77
|
+
| `guidance` | The same next-step text as fallback warnings; absent for `not-probed` and `loaded`. |
|
|
78
|
+
|
|
79
|
+
Await the returned promise. With no options, this API only reads the cache and
|
|
80
|
+
never loads the addon;
|
|
81
|
+
`not-probed` means no load has been attempted. With `{ probe: true }`, it uses
|
|
82
|
+
the existing lazy loader and cache. Mode `off` always reports `disabled` without
|
|
83
|
+
attempting a load. Failures are reported without throwing, including in `require`
|
|
84
|
+
mode. Core `capability-missing` does not expose internal function names, and a
|
|
85
|
+
`loaded` result does not promise that every operation is available or kernel-atomic.
|
|
86
|
+
Fields omit paths and raw loader errors.
|
|
87
|
+
|
|
88
|
+
Each module instance has its own status, so probe real Workers separately when
|
|
89
|
+
their health matters. After repairing an installation, start a fresh process or
|
|
90
|
+
Worker: the cached failure is not retried by another probe or mode change.
|
|
91
|
+
See the [full API reference](config.md#getfssafenativestatus-options) for exported
|
|
92
|
+
types and field details.
|
|
93
|
+
|
|
94
|
+
## Troubleshooting fallback warnings
|
|
95
|
+
|
|
96
|
+
Windows security and private-creation fallbacks emit `FS_SAFE_NATIVE_FALLBACK`
|
|
97
|
+
once per feature in each JavaScript context (the main thread or a Worker).
|
|
98
|
+
Warnings identify the reason and, on supported targets, the expected platform
|
|
99
|
+
package and its exact version from fs-safe's manifest. They omit caller paths
|
|
100
|
+
and raw loader error messages. Other guarded fallbacks, including Linux/macOS
|
|
101
|
+
operations, do not currently emit this warning.
|
|
102
|
+
|
|
103
|
+
| Reason | Next step |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `package-missing` | Reinstall 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. |
|
|
106
|
+
| `disabled` | The 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. |
|
|
107
|
+
| `unsupported-target` | Portable mode is expected on this platform/architecture/libc target; no matching native package is published. Native-only operations remain unavailable. |
|
|
108
|
+
| `load-failed` | Check 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. |
|
|
109
|
+
| `capability-missing` | Use matching fs-safe and platform-package versions. A loaded binding may intentionally provide a platform-specific subset; see the [FreeBSD capabilities](native.md#freebsd-14-4-and-newer). |
|
|
110
|
+
|
|
111
|
+
Restart the process or Worker after repairing an installation: failed loads are
|
|
112
|
+
cached. These diagnostics do not change `auto`, `off`, or `require` behavior,
|
|
113
|
+
and a loaded addon does not imply that every operation is kernel-atomic.
|
|
114
|
+
|
|
44
115
|
Native-created descriptors retain their originating native close operation through
|
|
45
116
|
normal and error cleanup, including later mode changes. Node-created roots and
|
|
46
117
|
directory handles keep Node's close operation, and borrowed handles keep their
|
|
@@ -109,10 +180,12 @@ is authoritative for operation and platform guarantees.
|
|
|
109
180
|
|
|
110
181
|
## Migration from the Python helper
|
|
111
182
|
|
|
112
|
-
The
|
|
113
|
-
|
|
183
|
+
The Python configuration bridge has been removed. Old Python environment
|
|
184
|
+
settings are ignored without a warning, so an old `require` or `off` setting
|
|
185
|
+
does not select the corresponding native policy. Configure
|
|
186
|
+
`FS_SAFE_NATIVE_MODE` or `configureFsSafeNative()` explicitly. Follow the
|
|
114
187
|
[0.5 migration checklist](migrating-to-0.5.md#2-replace-python-helper-configuration)
|
|
115
|
-
for
|
|
188
|
+
for the removed settings and current precedence.
|
|
116
189
|
|
|
117
190
|
## Related pages
|
|
118
191
|
|