@openclaw/fs-safe 0.5.6 → 0.7.0
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/CHANGELOG.md +89 -0
- package/README.md +23 -10
- package/dist/advanced.d.ts +2 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -0
- package/dist/archive-crc32.d.ts +2 -0
- package/dist/archive-crc32.d.ts.map +1 -0
- package/dist/archive-crc32.js +14 -0
- package/dist/archive-deadline.d.ts +3 -0
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +44 -8
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +1 -0
- package/dist/archive-errors.d.ts +1 -0
- package/dist/archive-errors.d.ts.map +1 -1
- package/dist/archive-errors.js +3 -0
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +26 -16
- package/dist/archive-kind.js +2 -2
- package/dist/archive-limits.d.ts +11 -3
- package/dist/archive-limits.d.ts.map +1 -1
- package/dist/archive-limits.js +24 -0
- package/dist/archive-native.d.ts +3 -2
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +31 -9
- package/dist/archive-policy.d.ts +2 -0
- package/dist/archive-policy.d.ts.map +1 -1
- package/dist/archive-policy.js +9 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +88 -41
- package/dist/archive-staging.d.ts +3 -0
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +91 -43
- package/dist/archive-tar-admission.d.ts +7 -0
- package/dist/archive-tar-admission.d.ts.map +1 -0
- package/dist/archive-tar-admission.js +43 -0
- package/dist/archive-tar-gnu.d.ts +2 -0
- package/dist/archive-tar-gnu.d.ts.map +1 -0
- package/dist/archive-tar-gnu.js +20 -0
- package/dist/archive-tar-header.d.ts +8 -0
- package/dist/archive-tar-header.d.ts.map +1 -0
- package/dist/archive-tar-header.js +47 -0
- package/dist/archive-tar-meta.d.ts +29 -1
- package/dist/archive-tar-meta.d.ts.map +1 -1
- package/dist/archive-tar-meta.js +135 -19
- package/dist/archive-tar-pax.d.ts +8 -0
- package/dist/archive-tar-pax.d.ts.map +1 -0
- package/dist/archive-tar-pax.js +100 -0
- package/dist/archive-tar-runtime.d.ts +4 -0
- package/dist/archive-tar-runtime.d.ts.map +1 -1
- package/dist/archive-tar-runtime.js +3 -0
- package/dist/archive-tar.d.ts.map +1 -1
- package/dist/archive-tar.js +8 -2
- package/dist/archive-zip-admission.d.ts +7 -0
- package/dist/archive-zip-admission.d.ts.map +1 -0
- package/dist/archive-zip-admission.js +60 -0
- package/dist/archive-zip-count.d.ts +2 -0
- package/dist/archive-zip-count.d.ts.map +1 -0
- package/dist/archive-zip-count.js +140 -0
- package/dist/archive-zip-directory.d.ts +8 -0
- package/dist/archive-zip-directory.d.ts.map +1 -0
- package/dist/archive-zip-directory.js +224 -0
- package/dist/archive-zip-integrity.d.ts.map +1 -1
- package/dist/archive-zip-integrity.js +1 -14
- package/dist/archive-zip-names.d.ts +12 -0
- package/dist/archive-zip-names.d.ts.map +1 -0
- package/dist/archive-zip-names.js +99 -0
- package/dist/archive-zip-preflight.d.ts +1 -1
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +8 -145
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +63 -42
- package/dist/atomic.d.ts +1 -1
- package/dist/atomic.d.ts.map +1 -1
- package/dist/bounded-read-stream.d.ts.map +1 -1
- package/dist/bounded-read-stream.js +2 -4
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +3 -10
- package/dist/byte-budget.d.ts +5 -0
- package/dist/byte-budget.d.ts.map +1 -0
- package/dist/byte-budget.js +9 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +25 -19
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +26 -9
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +9 -5
- package/dist/file-store-limit.d.ts +2 -0
- package/dist/file-store-limit.d.ts.map +1 -0
- package/dist/file-store-limit.js +8 -0
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +38 -8
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +26 -26
- package/dist/json-durable-queue-directory.d.ts +2 -0
- package/dist/json-durable-queue-directory.d.ts.map +1 -0
- package/dist/json-durable-queue-directory.js +20 -0
- package/dist/json-durable-queue-ownership.d.ts +14 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -0
- package/dist/json-durable-queue-ownership.js +168 -0
- package/dist/json-durable-queue-retirement.d.ts +9 -0
- package/dist/json-durable-queue-retirement.d.ts.map +1 -0
- package/dist/json-durable-queue-retirement.js +126 -0
- package/dist/json-durable-queue-transfer-lock.d.ts +2 -0
- package/dist/json-durable-queue-transfer-lock.d.ts.map +1 -0
- package/dist/json-durable-queue-transfer-lock.js +19 -0
- package/dist/json-durable-queue.d.ts +1 -0
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +90 -57
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +27 -8
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +4 -2
- package/dist/native-binding.d.ts +15 -3
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts +4 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +22 -6
- package/dist/native-pinned-write-windows.d.ts +8 -0
- package/dist/native-pinned-write-windows.d.ts.map +1 -0
- package/dist/native-pinned-write-windows.js +92 -0
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +136 -127
- package/dist/native-staged-file.d.ts +24 -0
- package/dist/native-staged-file.d.ts.map +1 -0
- package/dist/native-staged-file.js +337 -0
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +4 -4
- package/dist/opened-realpath.d.ts +2 -0
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +12 -7
- package/dist/output-sibling.d.ts.map +1 -1
- package/dist/output-sibling.js +11 -110
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +4 -2
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +2 -1
- package/dist/permission-exec.d.ts +19 -0
- package/dist/permission-exec.d.ts.map +1 -1
- package/dist/permission-exec.js +57 -11
- package/dist/permissions-public.d.ts +1 -1
- package/dist/permissions-public.d.ts.map +1 -1
- package/dist/permissions-windows.d.ts +3 -0
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +16 -5
- package/dist/permissions.d.ts +5 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +27 -48
- package/dist/pinned-write.d.ts +6 -0
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +26 -37
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +3 -2
- package/dist/private-temp-workspace.d.ts +3 -1
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +81 -56
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +2 -4
- package/dist/read-opened-file.d.ts.map +1 -1
- package/dist/read-opened-file.js +6 -4
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +136 -82
- package/dist/replace-file-copy-fallback.d.ts +3 -1
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +21 -36
- package/dist/replace-file-copy-source.d.ts +21 -0
- package/dist/replace-file-copy-source.d.ts.map +1 -0
- package/dist/replace-file-copy-source.js +112 -0
- package/dist/replace-file-descriptor.d.ts +13 -3
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +32 -7
- package/dist/replace-file-rename-policy.d.ts +7 -0
- package/dist/replace-file-rename-policy.d.ts.map +1 -0
- package/dist/replace-file-rename-policy.js +30 -0
- package/dist/replace-file-temp-owner.d.ts +46 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -0
- package/dist/replace-file-temp-owner.js +346 -0
- package/dist/replace-file.d.ts +6 -1
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +72 -58
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +118 -98
- package/dist/root-paths.d.ts +11 -14
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +36 -27
- package/dist/root-write-verification.d.ts +11 -0
- package/dist/root-write-verification.d.ts.map +1 -0
- package/dist/root-write-verification.js +91 -0
- package/dist/secret-file.d.ts +1 -6
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +49 -120
- package/dist/secret-read-async.d.ts +1 -1
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +51 -72
- package/dist/secret-read-policy.d.ts +13 -0
- package/dist/secret-read-policy.d.ts.map +1 -0
- package/dist/secret-read-policy.js +28 -0
- package/dist/secret.d.ts +2 -1
- package/dist/secret.d.ts.map +1 -1
- package/dist/secret.js +2 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +42 -29
- package/dist/sibling-staged-file.d.ts +15 -0
- package/dist/sibling-staged-file.d.ts.map +1 -0
- package/dist/sibling-staged-file.js +148 -0
- package/dist/sibling-temp.d.ts +3 -0
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +30 -74
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +48 -27
- package/dist/sidecar-lock-handle.d.ts +6 -2
- package/dist/sidecar-lock-handle.d.ts.map +1 -1
- package/dist/sidecar-lock-handle.js +17 -3
- package/dist/sidecar-lock-policy.d.ts +2 -0
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +29 -0
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +29 -7
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +28 -16
- package/dist/staged-directory.d.ts +16 -0
- package/dist/staged-directory.d.ts.map +1 -0
- package/dist/staged-directory.js +60 -0
- package/dist/staged-file-types.d.ts +56 -0
- package/dist/staged-file-types.d.ts.map +1 -0
- package/dist/staged-file-types.js +1 -0
- package/dist/staged-file.d.ts +10 -0
- package/dist/staged-file.d.ts.map +1 -0
- package/dist/staged-file.js +15 -0
- package/dist/strict-file-identity.d.ts +6 -0
- package/dist/strict-file-identity.d.ts.map +1 -0
- package/dist/strict-file-identity.js +48 -0
- package/dist/suppressed-error.d.ts +6 -0
- package/dist/suppressed-error.d.ts.map +1 -0
- package/dist/suppressed-error.js +15 -0
- package/dist/temp-cleanup.d.ts +2 -0
- package/dist/temp-cleanup.d.ts.map +1 -1
- package/dist/temp-cleanup.js +25 -10
- package/dist/temp-workspace-owner.d.ts +23 -0
- package/dist/temp-workspace-owner.d.ts.map +1 -0
- package/dist/temp-workspace-owner.js +320 -0
- package/dist/temp.d.ts +1 -1
- package/dist/temp.d.ts.map +1 -1
- package/dist/test-hooks.d.ts +5 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/windows-owner.d.ts +3 -0
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +10 -2
- package/docs/advanced.md +19 -2
- package/docs/archive.md +250 -35
- package/docs/atomic.md +11 -2
- package/docs/config.md +7 -0
- package/docs/contributing.md +45 -7
- package/docs/durability.md +18 -5
- package/docs/errors.md +16 -1
- package/docs/file-store.md +2 -0
- package/docs/index.md +3 -1
- package/docs/install.md +20 -8
- package/docs/json.md +8 -4
- package/docs/migrating-to-0.5.md +7 -7
- package/docs/migrating-to-0.6.md +43 -0
- package/docs/native-helper.md +30 -8
- package/docs/native.md +73 -17
- package/docs/output.md +10 -0
- package/docs/path-scope.md +28 -2
- package/docs/permissions.md +13 -2
- package/docs/public-api.md +6 -3
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +1 -1
- package/docs/regular-file.md +9 -2
- package/docs/root.md +3 -1
- package/docs/secret-file.md +12 -0
- package/docs/secure-file.md +21 -3
- package/docs/security-model.md +33 -1
- package/docs/sidecar-lock.md +15 -1
- package/docs/staged-file.md +178 -0
- package/docs/store.md +13 -0
- package/docs/temp.md +128 -14
- package/docs/testing.md +1 -1
- package/docs/writing.md +19 -0
- package/package.json +16 -9
- package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
- package/dist/native/darwin-x64/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
- package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
package/docs/install.md
CHANGED
|
@@ -85,19 +85,27 @@ Use the main entry for the common surface, or the focused subpaths when you want
|
|
|
85
85
|
|
|
86
86
|
## Runtime dependencies
|
|
87
87
|
|
|
88
|
-
`@openclaw/fs-safe` lists `jszip` and `tar` as optional dependencies for [archive extraction](archive.md). They are loaded lazily
|
|
88
|
+
`@openclaw/fs-safe` lists `jszip` and `tar` as optional dependencies for JavaScript ZIP/TAR [archive extraction](archive.md). They are loaded lazily; the JavaScript archive fallback requires the corresponding codec and reports a missing-optional-dependency error without it. Public subpaths remain safe to import with all optional dependencies omitted, but imports do not prove native availability.
|
|
89
89
|
|
|
90
|
-
There are no peer dependencies.
|
|
90
|
+
There are no peer dependencies. Exact-version optional packages carry the seven
|
|
91
|
+
native targets and npm-compatible OS, CPU, and Linux libc filters install only
|
|
92
|
+
the matching binary. Consumers do not run a native build, download code at
|
|
93
|
+
runtime, or execute a postinstall step. Omitting optional dependencies keeps
|
|
94
|
+
non-archive fallback-capable operations working in `auto` or `off`. Native-only
|
|
95
|
+
features, including strict owned-tree temp cleanup, retained-directory staging,
|
|
96
|
+
atomic `rename-noreplace`, zstd/bzip2 TAR handling, and Windows private-directory
|
|
97
|
+
creation, remain unavailable. Operations needing the binding in `require` mode fail with
|
|
98
|
+
`helper-unavailable` when the matching package is absent or incompatible.
|
|
91
99
|
|
|
92
|
-
Upgrading an existing consumer? Follow [Migrating to 0.
|
|
93
|
-
before
|
|
100
|
+
Upgrading an existing 0.5 consumer? Follow [Migrating to 0.6](migrating-to-0.6.md)
|
|
101
|
+
before deploying with native mode `require` or native-only features.
|
|
94
102
|
|
|
95
103
|
## Native helper policy
|
|
96
104
|
|
|
97
|
-
The
|
|
105
|
+
The platform native binaries provide fd-relative open/link/mkdir primitives,
|
|
98
106
|
atomic no-replace rename, and file identity checks. The default is `auto`: use
|
|
99
107
|
the matching binary when it loads, otherwise silently keep the guarded
|
|
100
|
-
JavaScript path. Platforms without one of the seven
|
|
108
|
+
JavaScript path. Platforms without one of the seven published targets therefore
|
|
101
109
|
continue through the documented fallback in `auto` mode.
|
|
102
110
|
|
|
103
111
|
```ts
|
|
@@ -116,10 +124,14 @@ FS_SAFE_NATIVE_MODE=off # auto | off | require
|
|
|
116
124
|
|
|
117
125
|
`OPENCLAW_FS_SAFE_NATIVE_MODE` is also accepted.
|
|
118
126
|
|
|
119
|
-
Disabling native loading keeps
|
|
127
|
+
Disabling native loading keeps fallback-capable operations working through Node path
|
|
120
128
|
operations guarded by lexical and canonical checks plus identity verification.
|
|
121
129
|
Use `require` when native-backed operations must fail instead of falling back.
|
|
122
|
-
|
|
130
|
+
Temp workspaces retain compatible JavaScript quarantine cleanup in `auto` and
|
|
131
|
+
`off`. Set `cleanupSafety: "require-bounded"` to reject before child creation
|
|
132
|
+
unless native no-replace quarantine and descriptor-bounded tree removal are
|
|
133
|
+
available. See the [temp workspace contract](temp.md#private-temp-workspaces). The exact boundary
|
|
134
|
+
for other operations is documented in [native helper policy](native-helper.md).
|
|
123
135
|
|
|
124
136
|
## Verify the install
|
|
125
137
|
|
package/docs/json.md
CHANGED
|
@@ -135,10 +135,14 @@ where lower latency matters more than crash-durability.
|
|
|
135
135
|
|
|
136
136
|
Synchronous variant. It pretty-prints with two spaces, appends a newline,
|
|
137
137
|
creates parents at `0o700`, writes at `0o600`, and synchronizes the parent
|
|
138
|
-
directory best-effort.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
directory best-effort. File-mode tightening carries the staged bigint identity
|
|
139
|
+
through rename and applies `fchmod` only when the reopened descriptor and current
|
|
140
|
+
pathname still name that same single-link regular file; a swap is preserved and
|
|
141
|
+
skips this best-effort step. It has no options bag. On `EPERM`/`EEXIST`, its legacy
|
|
142
|
+
compatibility path removes the existing destination and retries the staged-file
|
|
143
|
+
rename, so that fallback is temporarily non-atomic while retaining the staged
|
|
144
|
+
file's `0600` mode; use the async `writeJson()`/`replaceFileAtomic()` surfaces
|
|
145
|
+
when fallback policy must be explicit.
|
|
142
146
|
|
|
143
147
|
```ts
|
|
144
148
|
writeJsonSync("./prefs.json", { theme: "dark" });
|
package/docs/migrating-to-0.5.md
CHANGED
|
@@ -8,8 +8,8 @@ description: "Ordered checklist for moving a 0.4 consumer from the Python helper
|
|
|
8
8
|
Use this checklist from top to bottom. Version 0.5 replaces the Python worker,
|
|
9
9
|
changes the default archive mode policy, and adds explicit contracts for
|
|
10
10
|
publication, walking, locks, secrets, and native-only features. Nothing in this
|
|
11
|
-
guide requires a Rust toolchain: all supported native binaries are prebuilt
|
|
12
|
-
|
|
11
|
+
guide requires a Rust toolchain: all supported native binaries are prebuilt.
|
|
12
|
+
Current releases install the matching exact-version optional platform package.
|
|
13
13
|
|
|
14
14
|
## 1. Update the package and runtime
|
|
15
15
|
|
|
@@ -17,10 +17,10 @@ bundled in `@openclaw/fs-safe`.
|
|
|
17
17
|
- Update `@openclaw/fs-safe` and regenerate every lock or shrinkwrap file your
|
|
18
18
|
deployment consumes.
|
|
19
19
|
- Keep optional dependencies enabled if you want JavaScript ZIP/TAR support.
|
|
20
|
-
Native loading no longer depends on
|
|
21
|
-
|
|
22
|
-
can still import fs-safe, but
|
|
23
|
-
errors.
|
|
20
|
+
Native loading no longer depends on Python. Keep optional dependencies enabled
|
|
21
|
+
for the matching native package and JavaScript ZIP/TAR decoders. An install
|
|
22
|
+
that omits them can still import fs-safe, but native-only features and missing
|
|
23
|
+
JS archive decoders fail with actionable errors.
|
|
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
|
|
@@ -42,7 +42,7 @@ configureFsSafeNative({ mode: "auto" });
|
|
|
42
42
|
| `configureFsSafePython({ mode })` | `configureFsSafeNative({ mode })` |
|
|
43
43
|
| `FS_SAFE_PYTHON_MODE` | `FS_SAFE_NATIVE_MODE` |
|
|
44
44
|
| `OPENCLAW_FS_SAFE_PYTHON_MODE` | `OPENCLAW_FS_SAFE_NATIVE_MODE` |
|
|
45
|
-
| `pythonPath`, `FS_SAFE_PYTHON`, pinned-Python aliases | Nothing;
|
|
45
|
+
| `pythonPath`, `FS_SAFE_PYTHON`, pinned-Python aliases | Nothing; prebuilt native binaries do not use an interpreter |
|
|
46
46
|
|
|
47
47
|
The old names warn once and map `auto`, `off`, or `require` so a shipped 0.4
|
|
48
48
|
deployment does not silently change policy. Interpreter paths are ignored and
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Migrating to 0.6
|
|
3
|
+
description: "Upgrade checklist for the platform-native package split."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrating from 0.5 to 0.6
|
|
7
|
+
|
|
8
|
+
Version 0.6 moves native bindings out of the root `@openclaw/fs-safe` tarball
|
|
9
|
+
and into exact-version platform packages. This removes unrelated operating
|
|
10
|
+
system and architecture binaries from each installation.
|
|
11
|
+
|
|
12
|
+
## Keep optional dependencies enabled for native mode
|
|
13
|
+
|
|
14
|
+
Package managers select one binding by OS, CPU, and Linux libc. A normal
|
|
15
|
+
install needs no command change:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @openclaw/fs-safe
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If a deployment currently installs 0.5 with `--omit=optional`, `--no-optional`,
|
|
22
|
+
or an equivalent lockfile policy, change that policy before upgrading when it
|
|
23
|
+
uses native mode `require` or any native-only feature. Version 0.5 kept its
|
|
24
|
+
binding in the root tarball; version 0.6 intentionally does not.
|
|
25
|
+
|
|
26
|
+
Omitting optional dependencies remains supported for fallback-capable APIs in
|
|
27
|
+
`auto` mode. It disables native-only features such as zstd/bzip2 TAR handling,
|
|
28
|
+
retained-directory staging, atomic `rename-noreplace`, and Windows private
|
|
29
|
+
directory creation. Native mode `require` reports `helper-unavailable` when the
|
|
30
|
+
matching package is absent or incompatible.
|
|
31
|
+
|
|
32
|
+
## Deployment checklist
|
|
33
|
+
|
|
34
|
+
1. Remove any option or policy that omits optional dependencies when native
|
|
35
|
+
support is required.
|
|
36
|
+
2. Regenerate every lockfile or shrinkwrap file consumed by deployment.
|
|
37
|
+
3. Verify that the lock contains the matching `@openclaw/fs-safe-*` package.
|
|
38
|
+
4. Run a native-required operation on every deployed OS/libc target.
|
|
39
|
+
5. Keep an `FS_SAFE_NATIVE_MODE=off` lane when the guarded JavaScript fallback
|
|
40
|
+
is part of the application contract.
|
|
41
|
+
|
|
42
|
+
No Rust toolchain, postinstall build, or runtime download is introduced. See
|
|
43
|
+
[Native helper policy](native-helper.md) for the exact fallback boundary.
|
package/docs/native-helper.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Native helper policy
|
|
3
|
-
description: "How fs-safe loads its
|
|
3
|
+
description: "How fs-safe loads its platform-specific native filesystem primitives and how auto, require, and off affect guarded fallbacks."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Native helper policy
|
|
7
7
|
|
|
8
|
-
`@openclaw/fs-safe`
|
|
8
|
+
`@openclaw/fs-safe` declares seven exact-version optional packages for Linux
|
|
9
|
+
x64/arm64 (glibc or musl), macOS x64/arm64, and Windows x64. Package-manager
|
|
10
|
+
OS, CPU, and libc filters install only the matching package. The loader requires
|
|
11
|
+
that package lazily, without runtime downloads, postinstall scripts, or a
|
|
12
|
+
consumer Rust build.
|
|
9
13
|
|
|
10
14
|
```ts
|
|
11
15
|
import { configureFsSafeNative } from "@openclaw/fs-safe/config";
|
|
@@ -21,23 +25,40 @@ The equivalent environment variables are `FS_SAFE_NATIVE_MODE` and `OPENCLAW_FS_
|
|
|
21
25
|
|
|
22
26
|
| Mode | Behavior |
|
|
23
27
|
|---|---|
|
|
24
|
-
| `auto` | Prefer native primitives when the current
|
|
25
|
-
| `off` | Do not load a
|
|
28
|
+
| `auto` | Prefer native primitives when the current platform package loads; otherwise silently use the guarded JavaScript path. |
|
|
29
|
+
| `off` | Do not load a native package. Use the guarded JavaScript path deterministically. |
|
|
26
30
|
| `require` | Throw `FsSafeError("helper-unavailable")` instead of falling back when an operation needs the native binding and it cannot load. |
|
|
27
31
|
|
|
28
32
|
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.
|
|
29
33
|
|
|
34
|
+
[`tempWorkspace()` and its scoped/sync variants](temp.md#private-temp-workspaces)
|
|
35
|
+
remain available in every mode. Their default compatible cleanup uses guarded
|
|
36
|
+
JavaScript quarantine when owned native tree removal is unavailable.
|
|
37
|
+
`cleanupSafety: "require-bounded"` instead rejects before child creation unless
|
|
38
|
+
no-replace quarantine plus descriptor-relative owned-tree removal are available.
|
|
39
|
+
On Linux, admission probes the exact `openat2` child-directory flags, including
|
|
40
|
+
`RESOLVE_NO_XDEV`, at runtime. An unavailable or denied probe selects compatible
|
|
41
|
+
JavaScript cleanup even in global `require` mode; `require-bounded` rejects before
|
|
42
|
+
child creation.
|
|
43
|
+
Already-created strict workspaces retain their binding
|
|
44
|
+
and descriptors across later mode changes.
|
|
45
|
+
|
|
46
|
+
[`stageFileInDirectory()`](staged-file.md) always requires native support on
|
|
47
|
+
Linux/macOS and rejects before creation when off, unavailable, or missing the
|
|
48
|
+
required capability. Windows is unsupported for this lifecycle. This does not
|
|
49
|
+
change the mode policy of existing fallback-capable APIs.
|
|
50
|
+
|
|
30
51
|
## Native boundary
|
|
31
52
|
|
|
32
|
-
The
|
|
53
|
+
The native layer exposes policy-free filesystem mechanisms: beneath-root
|
|
33
54
|
open/mkdir/link, replace and no-replace rename, identity reads, archive decode/execution,
|
|
34
55
|
clone/copy/hash workers, and Windows security descriptor calls. The TypeScript
|
|
35
56
|
layer owns policy, retries, filters, budgets, modes, cleanup, error
|
|
36
57
|
normalization, and the decision to fall back.
|
|
37
58
|
|
|
38
|
-
- Linux uses `openat2` with `RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS`, `renameat`, and `renameat2(RENAME_NOREPLACE)`.
|
|
39
|
-
- macOS 15.4 and newer prefer `O_RESOLVE_BENEATH`; older kernels resolve components with `O_NOFOLLOW` and restart in-root symlinks from the pinned root descriptor. Both routes use an `F_GETPATH` post-open escape detector and report `best-effort` because directory rename races are not atomic with that check. Publication uses `renameat` for replacement and `renameatx_np(RENAME_EXCL)` for no-replace
|
|
40
|
-
- Windows uses handle-relative `NtCreateFile`, rejects reparse points,
|
|
59
|
+
- Linux uses `openat2` with `RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS`, `renameat`, and `renameat2(RENAME_NOREPLACE)`. Owned-tree cleanup enumerates and unlinks through retained directory descriptors and rejects device crossings.
|
|
60
|
+
- macOS 15.4 and newer prefer `O_RESOLVE_BENEATH`; older kernels resolve components with `O_NOFOLLOW` and restart in-root symlinks from the pinned root descriptor. Both routes use an `F_GETPATH` post-open escape detector and report `best-effort` because directory rename races are not atomic with that check. Publication uses `renameat` for replacement and `renameatx_np(RENAME_EXCL)` for no-replace; owned-tree cleanup uses descriptor-relative `openat`/`unlinkat`.
|
|
61
|
+
- Windows uses handle-relative `NtCreateFile`, rejects reparse points during root-bounded traversal, uses `FileRenameInfoEx` with replacement selected explicitly by the TypeScript policy layer, and deletes owned trees through exact opened handles with `FileDispositionInfoEx`; symlink/reparse entries in owned trees are removed as leaves and never traversed.
|
|
41
62
|
|
|
42
63
|
Native primitives back create-only and replacing pinned writes, async sidecar creation,
|
|
43
64
|
guarded publication, archive acceleration, and direct Windows ACL operations.
|
|
@@ -89,3 +110,4 @@ consumer performs its 0.5 upgrade.
|
|
|
89
110
|
- [File locks](sidecar-lock.md)
|
|
90
111
|
- [Durability](durability.md)
|
|
91
112
|
- [Migrating to 0.5](migrating-to-0.5.md)
|
|
113
|
+
- [Migrating to 0.6](migrating-to-0.6.md)
|
package/docs/native.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Native architecture
|
|
3
|
-
description: "The
|
|
3
|
+
description: "The platform-specific native bindings, fd-relative beneath model, platform mechanisms, loader security, and JavaScript fallback contract."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Native architecture
|
|
7
7
|
|
|
8
|
-
`@openclaw/fs-safe`
|
|
8
|
+
`@openclaw/fs-safe` uses native bindings that supply mechanisms Node does
|
|
9
9
|
not expose directly. The Rust layer is deliberately not a second policy engine.
|
|
10
10
|
TypeScript owns trusted-root selection, path validation, archive filtering,
|
|
11
11
|
budgets, modes, identity fencing, cleanup decisions, and error normalization.
|
|
@@ -14,8 +14,9 @@ platform syscall sequence that can preserve the boundary.
|
|
|
14
14
|
|
|
15
15
|
Every operation that has an equivalent safe Node implementation keeps that
|
|
16
16
|
guarded JavaScript path. Native loading is lazy; installs do not compile Rust,
|
|
17
|
-
run postinstall code, or fetch binaries
|
|
18
|
-
|
|
17
|
+
run postinstall code, or fetch binaries at runtime. Seven exact-version optional
|
|
18
|
+
packages are filtered by OS, CPU, and Linux libc, so an installation receives
|
|
19
|
+
only its matching prebuilt binding.
|
|
19
20
|
Native-only formats and creation-time Windows DACL guarantees fail explicitly
|
|
20
21
|
instead of substituting a weaker implementation.
|
|
21
22
|
|
|
@@ -63,12 +64,39 @@ returns a bounded manifest. TypeScript applies the shared path, filter, strip,
|
|
|
63
64
|
mode, and byte policies and returns an index-bound extraction plan. Rust then
|
|
64
65
|
creates only those planned entries beneath a private staging descriptor.
|
|
65
66
|
|
|
66
|
-
A
|
|
67
|
-
|
|
68
|
-
metadata
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
A raw meter sits between decompression and the TAR crate, with matching
|
|
68
|
+
TypeScript admission before node-tar. It parses 512-byte headers and bounded
|
|
69
|
+
local PAX `x` metadata, using supported effective sizes to locate the following
|
|
70
|
+
member body. GNU long-name/link `L`/`K` payloads remain
|
|
71
|
+
supported. `maxMetaEntryBytes` bounds each metadata body before allocation;
|
|
72
|
+
unsupported global/old metadata and sparse forms fail closed rather than being
|
|
73
|
+
interpreted as ordinary members. See [bounded local PAX support](archive.md#bounded-local-pax-support).
|
|
74
|
+
|
|
75
|
+
Every raw pass receives only TypeScript's resolved `maxEntries`,
|
|
76
|
+
`maxMetaEntryBytes`, and `maxDecodedBytes`. Shared resolution caps metadata and
|
|
77
|
+
decoded byte fields at JavaScript's safe-integer maximum and entry counts at
|
|
78
|
+
`2^32 - 1` before backend selection. Large finite limits remain accepted;
|
|
79
|
+
native conversion mirrors those caps and rejects malformed non-finite or
|
|
80
|
+
negative direct-call values before casting. Logical member headers count
|
|
81
|
+
before filtering/stripping; metadata records do not. `maxEntryBytes` and
|
|
82
|
+
`maxExtractedBytes` remain exclusively in TypeScript's accepted-plan builder,
|
|
83
|
+
after strip/filter policy, and are absent from the raw meter's interface.
|
|
84
|
+
Bounded reads use the default count/metadata/decoded bounds; public `maxBytes`
|
|
85
|
+
bounds only the requested output. TypeScript derives the internal decoded cap
|
|
86
|
+
by safely adding `maxExtractedBytes` and `maxArchiveBytes`, clamped to the safe
|
|
87
|
+
integer maximum. Every native pass receives that same cap and charges headers,
|
|
88
|
+
metadata, bodies, padding, EOF blocks, and trailing zeros. It rejects overflow
|
|
89
|
+
with `archive-decoded-size-exceeds-limit`; no ratio policy is implied.
|
|
90
|
+
Extraction and entry reads drain the metered reader through physical EOF after
|
|
91
|
+
TAR iteration. Trailing framing or decoded-limit failures propagate before
|
|
92
|
+
directory modes are finalized, staging is published, or selected bytes return.
|
|
93
|
+
Native reads stop at framing boundaries so a rejected header does not request
|
|
94
|
+
its body from the decoder; codec buffering can still read ahead internally.
|
|
95
|
+
Inspection finishes the complete bounded framing pass before parsing. Directory
|
|
96
|
+
and link bodies, missing two-block EOF, and nonzero trailers reject on both
|
|
97
|
+
backends, as detailed in [raw TAR framing](archive.md#raw-tar-framing). Raw and
|
|
98
|
+
padded sizes above JavaScript's safe-integer maximum reject as invalid framing
|
|
99
|
+
before applying member budgets, including when local PAX overrides the size.
|
|
72
100
|
|
|
73
101
|
## Publication and hashing
|
|
74
102
|
|
|
@@ -86,9 +114,36 @@ workers rather than the JavaScript event loop.
|
|
|
86
114
|
| `require` | Try once, cache the result | Throw `FsSafeError("helper-unavailable")` |
|
|
87
115
|
| `off` | Never attempt a binding load | Always use guarded JavaScript |
|
|
88
116
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`helper-unavailable` when native support is absent or off.
|
|
117
|
+
Features without a safe JavaScript implementation, including zstd/bzip2 TAR,
|
|
118
|
+
Windows private-directory creation, and [retained-directory staging](staged-file.md),
|
|
119
|
+
fail with `helper-unavailable` when native support is absent or off. Staging
|
|
120
|
+
is currently Linux/macOS only and rejects Windows with `unsupported-platform`.
|
|
121
|
+
|
|
122
|
+
The staged-file owner also serves POSIX native pinned writes, including streaming.
|
|
123
|
+
Unpublished files remain at `0600`; requested modes are applied through the
|
|
124
|
+
owned file descriptor only after rename and published-entry identity validation.
|
|
125
|
+
Post-rename chmod or sync failures retain the publication receipt and final name.
|
|
126
|
+
Its direct-child exclusive `openat` hands off the descriptor before any fallible
|
|
127
|
+
post-open checks; non-following `statat` compares against that descriptor with
|
|
128
|
+
exact native identities, and cleanup uses `unlinkat` in the retained parent.
|
|
129
|
+
The separate checks and unlink are not atomic conditional deletion. Windows
|
|
130
|
+
pinned writes and other fallback-capable APIs retain their existing mechanisms.
|
|
131
|
+
Native writers share root and parent admission, but keep their platform identity
|
|
132
|
+
checks and leaf ownership. POSIX coordinator disposal uses `SuppressedError` to
|
|
133
|
+
retain both an operation failure and a disposal failure, including their receipts;
|
|
134
|
+
stage preparation and cleanup keep their documented error mappings.
|
|
135
|
+
Root replacement verification borrows the published descriptor after final mode
|
|
136
|
+
application, while a private coordinator retains the staged owner until the
|
|
137
|
+
asynchronous check finishes. The owner never escapes that coordinator; public
|
|
138
|
+
staging methods and receipts expose no descriptor or verification callback.
|
|
139
|
+
Verification failures preserve the published name, and disposal still retains
|
|
140
|
+
both verification and cleanup errors when both fail.
|
|
141
|
+
The private verification channel carries exact bigint identity from the original
|
|
142
|
+
owned descriptor (or the content-accepted FUSE descriptor). Root compares it
|
|
143
|
+
against exact fd and pathname metadata; legacy helper return facts and public
|
|
144
|
+
read metadata behavior are unchanged. Missing Windows pathname identity still
|
|
145
|
+
requires a guarded path reopen and comparison with the original retained file;
|
|
146
|
+
that fallback does not apply to POSIX no-read modes.
|
|
92
147
|
|
|
93
148
|
## JavaScript fallback guarantees and delta
|
|
94
149
|
|
|
@@ -116,10 +171,10 @@ infer native loading from timing.
|
|
|
116
171
|
Importing fs-safe never executes a child process. Linux libc selection uses
|
|
117
172
|
the Node process report, conventional musl library filenames, and the ELF
|
|
118
173
|
`PT_INTERP` field of `process.execPath`. If all probes are inconclusive, the
|
|
119
|
-
loader conservatively attempts the
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
174
|
+
loader conservatively attempts the glibc package and lets normal module loading
|
|
175
|
+
fail into `auto` fallback. The loader requires only the package selected from
|
|
176
|
+
the detected target; it never probes unrelated packages, downloads code, or
|
|
177
|
+
runs a postinstall step. A missing or incompatible binary
|
|
123
178
|
silently selects the JavaScript fallback in `auto`, throws typed
|
|
124
179
|
`helper-unavailable` in `require`, and is never inspected in `off`. Tests reject
|
|
125
180
|
`child_process`, `exec`, or `spawn` usage in the loader.
|
|
@@ -132,3 +187,4 @@ silently selects the JavaScript fallback in `auto`, throws typed
|
|
|
132
187
|
- [Durability](durability.md)
|
|
133
188
|
- [Permissions](permissions.md)
|
|
134
189
|
- [Migrating to 0.5](migrating-to-0.5.md)
|
|
190
|
+
- [Migrating to 0.6](migrating-to-0.6.md)
|
package/docs/output.md
CHANGED
|
@@ -48,6 +48,8 @@ The requested `path` must name a file. Missing destination parents are created
|
|
|
48
48
|
by the helper because the operation is "produce this output file under the
|
|
49
49
|
root"; callers should choose the filename before calling this API.
|
|
50
50
|
|
|
51
|
+
`maxBytes` must be a non-negative safe integer or positive `Infinity`; zero is an active cap and `Infinity` disables it. Invalid values reject before the producer or filesystem staging runs.
|
|
52
|
+
|
|
51
53
|
Use `maxBytes` when the external producer can create arbitrarily large files,
|
|
52
54
|
and `mode` when the finalized file needs a specific POSIX mode. Both staging
|
|
53
55
|
modes enforce them after the producer returns and before committing the target.
|
|
@@ -80,6 +82,14 @@ rename. If an error leaves the sibling temp in place and immediate cleanup
|
|
|
80
82
|
fails, its verified identity remains registered for a best-effort process-exit
|
|
81
83
|
cleanup retry.
|
|
82
84
|
|
|
85
|
+
Sibling staging shares the [callback sibling owner](temp.md#sibling-temp-writes):
|
|
86
|
+
it checks exact pre-open, descriptor, and current-path identities, retains the
|
|
87
|
+
descriptor through publication, and never chmods or reads a replacement by path.
|
|
88
|
+
Cleanup preserves unverified paths, including partial output when the callback
|
|
89
|
+
throws before admission. Native-off and Windows operation remain supported with
|
|
90
|
+
the platform limits and non-atomic rename/unlink identity checks described there.
|
|
91
|
+
When `mode` is omitted, output-sibling staging preserves the producer's mode.
|
|
92
|
+
|
|
83
93
|
## Why not pass the final path to the library?
|
|
84
94
|
|
|
85
95
|
If a target parent can be swapped after validation, handing an external library
|
package/docs/path-scope.md
CHANGED
|
@@ -24,6 +24,8 @@ await sharp(photo.path).resize(800).toFile(/* … */);
|
|
|
24
24
|
## Signature
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
|
+
import type { FsSafeError } from "@openclaw/fs-safe/errors";
|
|
28
|
+
|
|
27
29
|
function pathScope(rootDir: string, options: PathScopeOptions): PathScope;
|
|
28
30
|
|
|
29
31
|
type PathScopeOptions = {
|
|
@@ -36,6 +38,10 @@ type PathScopeResolveOptions = {
|
|
|
36
38
|
|
|
37
39
|
type PathResult = { ok: true; path: string } | { ok: false; error: string };
|
|
38
40
|
type PathsResult = { ok: true; paths: string[] } | { ok: false; error: string };
|
|
41
|
+
// Illustrative result alias, not a named package export.
|
|
42
|
+
type DirectoryResult =
|
|
43
|
+
| { ok: true; path: string }
|
|
44
|
+
| { ok: false; error: string; diagnostic?: FsSafeError };
|
|
39
45
|
|
|
40
46
|
type PathScope = {
|
|
41
47
|
rootDir: string;
|
|
@@ -45,7 +51,7 @@ type PathScope = {
|
|
|
45
51
|
existing(requestedPaths: string[]): Promise<PathsResult>;
|
|
46
52
|
files(requestedPaths: string[]): Promise<PathsResult>;
|
|
47
53
|
writable(requestedPath: string, options?: PathScopeResolveOptions): Promise<PathResult>;
|
|
48
|
-
ensureDir(requestedPath: string, options?: PathScopeResolveOptions & { mode?: number }): Promise<
|
|
54
|
+
ensureDir(requestedPath: string, options?: PathScopeResolveOptions & { mode?: number }): Promise<DirectoryResult>;
|
|
49
55
|
};
|
|
50
56
|
```
|
|
51
57
|
|
|
@@ -92,9 +98,29 @@ await fs.writeFile(t.path, body);
|
|
|
92
98
|
|
|
93
99
|
Async. `mkdir -p` inside the scope. Walks each segment, refuses any symlink in the path, creates missing directories. Optional `mode` sets the directory mode.
|
|
94
100
|
|
|
101
|
+
Failures remain nonthrowing results with `error: string`. Policy rejections
|
|
102
|
+
(including traversal, NUL input, symlinks and non-directory segments) omit
|
|
103
|
+
`diagnostic`. Operational failures from `lstat`, `realpath` or `mkdir` include
|
|
104
|
+
an `FsSafeError` with `code: "helper-failed"`, `category: "operational"`, and
|
|
105
|
+
the exact original error in `cause`, including its native `code`, `errno` and
|
|
106
|
+
`syscall` when available. Only `ensureDir()` adds this diagnostic; other scope
|
|
107
|
+
methods retain their existing result shapes.
|
|
108
|
+
|
|
109
|
+
`error` equals `diagnostic.message` for operational failures, for example
|
|
110
|
+
`"Could not prepare uploads directory: ENAMETOOLONG during lstat"`. Display
|
|
111
|
+
text bounds and escapes the label and native code/syscall, without copying
|
|
112
|
+
the requested path or native error message. Treat the raw cause as sensitive
|
|
113
|
+
local diagnostic data, not as text to return to an untrusted caller. A failure
|
|
114
|
+
can leave already-created parent directories; it does not roll them back or
|
|
115
|
+
retry through an unchecked filesystem path.
|
|
116
|
+
|
|
95
117
|
```ts
|
|
96
118
|
const dir = await uploads.ensureDir("inbox", { mode: 0o755 });
|
|
97
|
-
if (!dir.ok)
|
|
119
|
+
if (!dir.ok) {
|
|
120
|
+
// Pass the cause only to an appropriately restricted local diagnostic sink.
|
|
121
|
+
if (dir.diagnostic) recordLocalFailure(dir.diagnostic.cause);
|
|
122
|
+
return reply(dir.diagnostic ? 500 : 400, dir.error);
|
|
123
|
+
}
|
|
98
124
|
```
|
|
99
125
|
|
|
100
126
|
## Result type vs throwing
|
package/docs/permissions.md
CHANGED
|
@@ -75,10 +75,21 @@ Built-in PowerShell, `icacls.exe`, and `whoami.exe` invocations have a fixed
|
|
|
75
75
|
30-second per-process deadline. A command failure or timeout returns an
|
|
76
76
|
unverified result (`source: "unknown"`) so callers fail closed. Advanced callers
|
|
77
77
|
that inject a custom `exec` implementation own that executor's deadline.
|
|
78
|
+
Failed owner and ACL inspections retain `error` text and an optional
|
|
79
|
+
`errorDetail: PermissionCommandFailure` with `command`, integer `durationMs`,
|
|
80
|
+
`timedOut`, `exitCode`, `signal`, and `stderr`. The type is exported from both
|
|
81
|
+
`@openclaw/fs-safe/permissions` and `@openclaw/fs-safe/advanced`. Built-in
|
|
82
|
+
execution measures elapsed time; injected execFile-shaped failures receive
|
|
83
|
+
best-effort command diagnostics. Plain errors have no `errorDetail`.
|
|
84
|
+
Display reasons and stderr escape control characters and are limited to 400
|
|
85
|
+
characters, including a trailing `…` when truncated. Diagnostics do not copy
|
|
86
|
+
stdout or read target file contents. The separate `errorCause` retains the
|
|
87
|
+
original exception for restricted local diagnosis; do not serialize or expose
|
|
88
|
+
it as display text.
|
|
78
89
|
The parser is on the advanced surface so tests and CLIs can process captured
|
|
79
90
|
`icacls` output without spawning a process.
|
|
80
91
|
|
|
81
|
-
When the
|
|
92
|
+
When the native binding is available, `inspectPathPermissions()`
|
|
82
93
|
reads the owner and DACL directly with Windows security APIs. It classifies the
|
|
83
94
|
current user, LocalSystem, and built-in Administrators as trusted and reports
|
|
84
95
|
the world/group read/write facts consumed by secure reads. Descriptor forms it
|
|
@@ -128,7 +139,7 @@ Object-specific and other ACE layouts are not guessed: they are omitted,
|
|
|
128
139
|
`complete` becomes false, and their numeric types appear in
|
|
129
140
|
`unsupportedAceTypes`, allowing a security-sensitive caller to fail closed.
|
|
130
141
|
Non-Windows systems return `{ status: "unsupported-platform", platform }`.
|
|
131
|
-
Windows requires the
|
|
142
|
+
Windows requires the native binding; if it is unavailable or forced
|
|
132
143
|
off, the call throws `FsSafeError("helper-unavailable")`. The existing coarse
|
|
133
144
|
`inspectPathPermissions()` API still owns its compatibility fallback and trust
|
|
134
145
|
classification.
|
package/docs/public-api.md
CHANGED
|
@@ -69,9 +69,12 @@ The file-lock diagnostics surface includes `FileLockHeldEntry`,
|
|
|
69
69
|
manager controls; production code should not use them as lock recovery.
|
|
70
70
|
|
|
71
71
|
Standalone walkers use the `WalkEntryKind` and `WalkSymlinkPolicy` unions.
|
|
72
|
-
Private workspaces expose `TempPathIdentityReceipt
|
|
73
|
-
`TempWorkspaceCleanupResult`
|
|
74
|
-
|
|
72
|
+
Private workspaces expose `TempPathIdentityReceipt`,
|
|
73
|
+
`TempWorkspaceCleanupResult`, and `TempWorkspaceCleanupSafety` so callers can
|
|
74
|
+
distinguish outcomes and select compatible or required bounded-tree cleanup.
|
|
75
|
+
Strict workspace creation requires native no-replace quarantine plus retained
|
|
76
|
+
parent/workspace descriptors; see the
|
|
77
|
+
[creation and cleanup contract](temp.md#private-temp-workspaces).
|
|
75
78
|
|
|
76
79
|
## Atomic replacement and durability
|
|
77
80
|
|
package/docs/quickstart.md
CHANGED
|
@@ -145,7 +145,7 @@ await withTempWorkspace({ rootDir: "/srv/jobs/tmp", prefix: "build-" }, async (w
|
|
|
145
145
|
});
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
The directory is mode `0700
|
|
148
|
+
The directory is mode `0700` under the caller-provided root, and cleanup runs when the callback returns or throws. Compatible cleanup remains available without native support; pass `cleanupSafety: "require-bounded"` to require native no-replace quarantine and descriptor-bounded tree removal before creating a child. Cleanup preserves raced or ambiguous entries under a private quarantine name. See the [temp workspace compatibility and security contract](temp.md#private-temp-workspaces).
|
|
149
149
|
|
|
150
150
|
## Where to next
|
|
151
151
|
|
package/docs/reading.md
CHANGED
|
@@ -89,7 +89,7 @@ type RootReadOptions = {
|
|
|
89
89
|
};
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
`maxBytes` is enforced eagerly: the library reads up to `maxBytes + 1` and throws `too-large` if there is more, so a hostile target cannot silently exhaust memory.
|
|
92
|
+
`maxBytes` is enforced eagerly: the library reads up to `maxBytes + 1` and throws `too-large` if there is more, so a hostile target cannot silently exhaust memory. Values must be non-negative safe integers or positive `Infinity`; zero is an active cap, while `Infinity` disables it. Explicitly forwarding `undefined` preserves the Root default.
|
|
93
93
|
|
|
94
94
|
`nonBlockingRead` remains as a compatibility hint. Safe reads always add the platform's nonblocking open flag where available so a raced FIFO cannot pin a worker indefinitely; regular-file descriptor reads retain normal Node behavior.
|
|
95
95
|
|
package/docs/regular-file.md
CHANGED
|
@@ -64,7 +64,11 @@ processLog(result.buffer);
|
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
The result is `{ buffer, stat }`. Missing files preserve the normal `ENOENT`
|
|
67
|
-
shape; non-regular targets throw.
|
|
67
|
+
shape; non-regular targets throw. Reads compare exact bigint identities from the
|
|
68
|
+
preview, opened descriptor, and current path before consuming bytes; the returned
|
|
69
|
+
`stat` remains numeric Node `Stats`. Unknown Windows identities receive one
|
|
70
|
+
re-inspection without reopening; persistent unknowns or replacements throw
|
|
71
|
+
`FsSafeError("path-mismatch")`.
|
|
68
72
|
|
|
69
73
|
Throws `FsSafeError` with code `too-large` if the file exceeds `maxBytes`. Other I/O errors propagate as `NodeJS.ErrnoException`.
|
|
70
74
|
|
|
@@ -101,7 +105,10 @@ type AppendRegularFileOptions = {
|
|
|
101
105
|
};
|
|
102
106
|
```
|
|
103
107
|
|
|
104
|
-
The helper refuses symlink and hardlinked final targets.
|
|
108
|
+
The helper refuses symlink and hardlinked final targets. Preview, opened
|
|
109
|
+
descriptor, and current pathname identities remain exact bigints through the
|
|
110
|
+
append boundary; rounded-equal replacements and persistent unknown Windows
|
|
111
|
+
identities reject before chmod or writing bytes. With
|
|
105
112
|
`rejectSymlinkParents: true`, it also rejects symlinked ancestor directories.
|
|
106
113
|
|
|
107
114
|
### `appendRegularFileSync(options)`
|
package/docs/root.md
CHANGED
|
@@ -38,6 +38,8 @@ type DenyMutationPolicy = {
|
|
|
38
38
|
|
|
39
39
|
`defaults` apply to every method on the returned handle. Per-call options on individual methods override the defaults for that call only, except `denyMutations`: root and per-call deny entries are merged so a call cannot clear a root-level deny.
|
|
40
40
|
|
|
41
|
+
Every `maxBytes` value must be a non-negative safe integer or positive `Infinity`. Zero is an active zero-byte cap; `Infinity` explicitly disables the cap. An omitted or explicitly `undefined` per-call value preserves the configured Root default instead of clearing it.
|
|
42
|
+
|
|
41
43
|
## The `Root` interface
|
|
42
44
|
|
|
43
45
|
Every method on the returned handle accepts paths relative to the root and rejects anything that would escape it.
|
|
@@ -140,7 +142,7 @@ new destination name is subject to the portable guard.
|
|
|
140
142
|
|
|
141
143
|
## Native helper mode
|
|
142
144
|
|
|
143
|
-
Create-only writes prefer the
|
|
145
|
+
Create-only writes prefer the platform native helper for fd-relative opens and
|
|
144
146
|
atomic no-replace rename. Operations without native wiring retain their guarded
|
|
145
147
|
JavaScript implementations.
|
|
146
148
|
|
package/docs/secret-file.md
CHANGED
|
@@ -73,6 +73,8 @@ type SecretFileReadOptions = {
|
|
|
73
73
|
};
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
`maxBytes` must be a non-negative safe integer or positive `Infinity`; zero is an active cap, `Infinity` disables it, and omitted or explicitly `undefined` values preserve the 16 KiB default.
|
|
77
|
+
|
|
76
78
|
The reader trims the file content and rejects empty results. Symlink paths are
|
|
77
79
|
followed and pinned by default; set `rejectSymlink: true` when the pathname
|
|
78
80
|
itself must not be an alias. Hardlinks are rejected by default so another
|
|
@@ -89,6 +91,16 @@ credential must also fail on broad permissions or unexpected ownership.
|
|
|
89
91
|
the same pinned-handle validation, byte cap, trimming, error codes, and strict
|
|
90
92
|
versus missing-is-undefined naming semantics.
|
|
91
93
|
|
|
94
|
+
Both sync and async readers compare lossless bigint identities from the preview,
|
|
95
|
+
opened descriptor, resolved target, and current input path before reading. POSIX
|
|
96
|
+
opens are nonblocking, so a raced FIFO is rejected by descriptor type instead of
|
|
97
|
+
waiting for a writer. An
|
|
98
|
+
allowed symlink must still point to the opened file. On Windows, a zero device
|
|
99
|
+
or inode is unverified: that inspection is retried once without reopening the
|
|
100
|
+
file, preserving known identity components and link checks. Definite mismatches
|
|
101
|
+
and persistent ambiguity fail with `path-mismatch`; optional reads do not treat
|
|
102
|
+
these failures as missing files.
|
|
103
|
+
|
|
92
104
|
If an already validated descriptor fails while reading, both readers throw an
|
|
93
105
|
operational `FsSafeError` with `code: "read-failed"`; inspect `cause` for the
|
|
94
106
|
underlying Node filesystem code such as `EIO`.
|