Repository navigation
fix(windows): restrict secure files in-process instead of spawning icacls - #28135
Conversation
…acls Every secure write on Windows spawned icacls.exe five times on the calling thread (the Electron main thread for most stores): verify, /reset, /inheritance:r /grant:r, verify, verify. Process creation under endpoint security costs ~0.6 s each, so each write froze the UI for seconds (#26027). The /reset + /inheritance:r pair is also what behavioural EDR scores as ransomware tradecraft (#25583). Add native/windows-security (@orca/windows-security): a small N-API addon over advapi32 that reads and writes DACLs in-process. It exposes the token user SID, a DACL read as SDDL, a DACL write (sync and on the thread pool), a create-with-DACL file write, and an additive grant. Policy and verification stay in TypeScript. - writeSecureFile creates each temp file already carrying the protected user/SYSTEM/Administrators DACL (CreateFileW + SECURITY_ATTRIBUTES), reads it back once, and renames it. No ACL is modified on the write path and no process starts. Before: 5-7 spawns, 94-138 ms per write. After: 0 spawns, ~0.5 ms. - A secure directory is verified once per process. It is rewritten only when verification fails, on the thread pool, because the write walks every descendant (~2 s on a 29k-entry userData). - The verifier follows OpenSSH's rule: only the user, SYSTEM and Administrators may be granted anything, and the path must be protected (or, for a file, inherit from a secure parent). DACLs written by the icacls-era hardening pass unchanged, so there is no migration. - If the addon cannot load, the write still happens and reports unrestricted, logged once. There is no icacls or PowerShell fallback. - grantDirAcl, the startup userData grant, and the install-dir probe and package repair use the same addon, so whoami.exe and icacls.exe are no longer spawned anywhere at runtime. - orcad's Windows slots ship the addon beside their bundles (built per arch, PE-machine checked). Its data-root privacy check fails closed, so without the addon a Windows orcad would not start. Fixes #26027 Fixes #25583
… lane The read path called restrictWindowsPathSync directly and recorded success itself; the existing sync lane already does both. One call site fewer.
Conflicts resolved: - .gitignore: kept both sides (main's native/windows-agent-hook/target/ and this branch's native/windows-security build outputs). - src/main/runtime/mobile-notification-dismissal-store.ts: took main's side (#27731 moved the store to AtomicSnapshotWriter; this branch only touched a comment there). Content changes inside this merge: - config/scripts/pr-code-change-scope.mjs: the native/windows-registry and native/windows-security sources are classified through NATIVE_CACHE_PREFIXES instead of eight literal entries, which keeps the merged file under its 600-line limit. - config/main-sync-spawn-baseline.txt: pruned. src/main/win32-utils.ts (2 -> 0) and src/shared/secure-path-windows-acl.ts (3 -> 0) no longer launch icacls/whoami.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: 🟡 Moderate · up to
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/main/startup/windows-install-dir-package-acl-repair.ts (1)
62-62: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFix the duplicated article in the comment.
Line 62 reads "or an a repair". Remove "an".
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
04d7120e-6462-43d6-9a37-44a71ff8b76a
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (72)
.github/actions/prepare-native-runtime/action.yml.github/workflows/ci-cache-warmup.yml.github/workflows/node-server-tests.yml.github/workflows/pr.yml.github/workflows/ssh-windows-hosts.yml.gitignoreconfig/electron-builder.config.cjsconfig/main-sync-spawn-baseline.txtconfig/packaged-runtime-node-modules.cjsconfig/process-host-direct-import-baseline.txtconfig/scripts/build-orcad.mjsconfig/scripts/build-windows-security-addon.mjsconfig/scripts/ci-native-cache.test.mjsconfig/scripts/ensure-native-runtime.mjsconfig/scripts/ensure-native-runtime.test.mjsconfig/scripts/node-server-change-scope.mjsconfig/scripts/orcad-windows-security.mjsconfig/scripts/orcad-windows-security.test.mjsconfig/scripts/package-electron-runtime-contract.test.mjsconfig/scripts/pr-code-change-scope.mjsconfig/scripts/rebuild-native-deps-node-pty.test.mjsconfig/scripts/rebuild-native-deps-test-fixtures.mjsconfig/scripts/rebuild-native-deps.mjsnative/windows-security/binding.gypnative/windows-security/index.d.tsnative/windows-security/index.jsnative/windows-security/package.jsonnative/windows-security/src/addon.ccpackage.jsonpnpm-workspace.yamlsrc/main/agent-hooks/installer-utils.tssrc/main/artifacts/artifact-create-intent-store.test.tssrc/main/codex-accounts/fs-utils.tssrc/main/orcad/orcad-data-root-privacy.tssrc/main/runtime/device-registry.tssrc/main/runtime/rpc/mobile-auth-acl-critical-path.test.tssrc/main/runtime/unreadable-secret-store-preservation.win32.test.tssrc/main/startup/gpu-lifecycle-install-dir-acl-guard.test.tssrc/main/startup/main-window-actions.tssrc/main/startup/windows-install-dir-acl-poison-marker.tssrc/main/startup/windows-install-dir-acl-probe.test.tssrc/main/startup/windows-install-dir-acl-probe.tssrc/main/startup/windows-install-dir-acl-recovery.test.tssrc/main/startup/windows-install-dir-acl-recovery.tssrc/main/startup/windows-install-dir-acl-repair.win32.test.tssrc/main/startup/windows-install-dir-acl.test-fixture.tssrc/main/startup/windows-install-dir-package-acl-grant.test.tssrc/main/startup/windows-install-dir-package-acl-grant.tssrc/main/startup/windows-install-dir-package-acl-repair.test.tssrc/main/startup/windows-install-dir-package-acl-repair.tssrc/main/startup/windows-user-data-acl.test.tssrc/main/startup/windows-user-data-acl.tssrc/main/win32-utils-async-acl.test.tssrc/main/win32-utils.tssrc/main/zcode/zcode-plan-api-key-store-native-windows.test.tssrc/main/zcode/zcode-plan-api-key-store-plaintext-rollback.test.tssrc/main/zcode/zcode-plan-api-key-store-publication-race.test.tssrc/shared/orcad-artifacts.test.tssrc/shared/orcad-artifacts.tssrc/shared/secure-file-windows.test.tssrc/shared/secure-file.test.tssrc/shared/secure-file.tssrc/shared/secure-path-hardening-report.tssrc/shared/secure-path-hardening-retry-budget.tssrc/shared/secure-path-metadata-snapshot.tssrc/shared/secure-path-windows-acl.tssrc/shared/secure-path-windows-acl.win32.test.tssrc/shared/windows-secure-dacl.test.tssrc/shared/windows-secure-dacl.tssrc/shared/windows-security-addon.test-fixture.tssrc/shared/windows-security-addon.tssrc/shared/windows-security-addon.win32.test.ts
💤 Files with no reviewable changes (2)
- config/process-host-direct-import-baseline.txt
- config/main-sync-spawn-baseline.txt
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
…#28135) - The descriptor parser is anchored, so a conditional ACE (its body holds parentheses) or anything else it cannot read fails closed instead of verifying the ACEs before it. - findSecureDaclViolation is one flat rule: protected, allow rules only for the user, SYSTEM and Administrators (OpenSSH's ACE rule), and full control for the user. The inherits-from-a-secure-parent exception is gone: it accepted exactly what loosening the parent reaches. - The addon and the user SID load once, behind one wrapper that adds the extended-length prefix to every path and runs async DACL writes to one path in order, so a startup grant cannot land after a re-lock and restore the broader DACL. - With the addon loaded, a failed born-restricted create throws with the matching Node error code instead of falling back to a plain write. - A grant leaves a NULL DACL untouched instead of turning 'everyone' into 'only this SID'. WriteFile runs once and fails on a short write. - The artifact create-intent store writes through writeSecureFile and drops its own temp writer, directory LRU and synchronous directory re-lock. - Test seams the global fake makes redundant are gone.
…missing or foreign-arch The @orca/windows-* workspace links exist on every host, so their presence proved nothing: a missing or wrong-architecture .node only surfaced at runtime, as unrestricted secure files or an orcad that refuses to start. beforePack now reads each addon's PE machine against the target arch.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/shared/secure-path-windows-acl.win32.test.ts (1)
63-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the duplicate
resetWindowsSecurityLoadForTests()call.Line 64 repeats the call on Line 63. The second call does nothing, so delete it.
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
5e0fca5b-6398-48f1-aa14-a43c59b10952
📒 Files selected for processing (36)
config/packaged-runtime-node-modules.cjsconfig/scripts/electron-builder-config.test.mjsconfig/scripts/orcad-windows-security.mjsconfig/scripts/orcad-windows-security.test.mjsnative/windows-security/index.d.tsnative/windows-security/src/addon.ccsrc/main/artifacts/artifact-create-intent-store.test.tssrc/main/artifacts/artifact-create-intent-store.tssrc/main/runtime/device-registry.tssrc/main/runtime/rpc/mobile-auth-acl-critical-path.test.tssrc/main/runtime/unreadable-secret-store-preservation.win32.test.tssrc/main/startup/windows-install-dir-acl-probe.test.tssrc/main/startup/windows-install-dir-acl-probe.tssrc/main/startup/windows-install-dir-acl-recovery.test.tssrc/main/startup/windows-install-dir-acl-repair.win32.test.tssrc/main/startup/windows-install-dir-acl.test-fixture.tssrc/main/startup/windows-install-dir-package-acl-grant.test.tssrc/main/startup/windows-install-dir-package-acl-grant.tssrc/main/startup/windows-user-data-acl.test.tssrc/main/startup/windows-user-data-acl.tssrc/main/win32-utils-async-acl.test.tssrc/main/win32-utils.tssrc/shared/secure-file-coarse-ctime.test.tssrc/shared/secure-file-windows.test.tssrc/shared/secure-file.test.tssrc/shared/secure-file.tssrc/shared/secure-path-hardening-report.tssrc/shared/secure-path-windows-acl.tssrc/shared/secure-path-windows-acl.win32.test.tssrc/shared/windows-secure-dacl.test.tssrc/shared/windows-secure-dacl.tssrc/shared/windows-security-addon.test-fixture.tssrc/shared/windows-security-addon.tssrc/shared/windows-security-addon.win32.test.tssrc/shared/windows-security-descriptor.test.tssrc/shared/windows-security-descriptor.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/runtime/device-registry.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.
#28135) - Sync-spawn ratchet: hardenSecurePath and the sync-flag exemption it alone used are gone, and the advice and baseline header no longer say secure writers run icacls. - The install-dir probe reads ACEs through parseSddlDacl, so an ACE it cannot parse fails the whole reading closed instead of being skipped; AC is a resolved alias there now. - The packaging check reads the addon binary through node_modules, the copy that ships. - No winCode-to-errno table: nothing reads .code off a failed secure write. - The load memo is not mutated; secure-path keeps its own log-once flag. - grantInstallDirPackageAcl is a plain function, and secure-file restricts a Windows directory and a POSIX path directly instead of through one two-platform helper.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟠 Major · Do not publish after ACL verification fails. · secure-file.ts:138-175
src/shared/secure-file.ts:138-175
🔒 Security & Privacy | 🟠 Major | ⚡ Quick winDo not publish after ACL verification fails.
writeRestrictedWindowsFilereturnsfalsewhen the addon verifies an invalid DACL.writeSecureFilestill callsrenameSync, so a credential can be published with the inherited broad DACL before the caller handles thefalseresult. Preserve the addon-unavailable fallback, but distinguish it from verification failure at the publication boundary.Suggested fix
- const restricted = + const windowsRestriction = process.platform === 'win32' ? writeRestrictedWindowsFile(tmpFile, contents) - : writeModeRestrictedFile(tmpFile, contents) + : null + const restricted = windowsRestriction?.restricted ?? writeModeRestrictedFile(tmpFile, contents) ... if (options.shouldPublish && !options.shouldPublish()) { rmSync(tmpFile, { force: true }) return false } + if (windowsRestriction && windowsRestriction.addonAvailable && !windowsRestriction.restricted) { + rmSync(tmpFile, { force: true }) + return false + } renameSync(tmpFile, targetPath)-export function writeRestrictedWindowsFile(targetPath: string, contents: string): boolean { +export function writeRestrictedWindowsFile( + targetPath: string, + contents: string +): { restricted: boolean; addonAvailable: boolean } { const security = securityFor(targetPath) if (!security) { writeFileSync(targetPath, contents, { encoding: 'utf-8', mode: 0o600 }) - return false + return { restricted: false, addonAvailable: false } } ... - return verifyAfterApply(security, targetPath, false) + return { + restricted: verifyAfterApply(security, targetPath, false), + addonAvailable: true + } }
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
190e2976-7146-46a7-8b46-44d5e06fe17f
📒 Files selected for processing (15)
config/main-sync-spawn-baseline.txtconfig/packaged-runtime-node-modules.cjsconfig/scripts/check-main-sync-spawn-ratchet.mjsconfig/scripts/check-main-sync-spawn-ratchet.test.mjsconfig/scripts/electron-builder-config.test.mjssrc/main/startup/windows-install-dir-acl-probe.test.tssrc/main/startup/windows-install-dir-acl-probe.tssrc/main/startup/windows-install-dir-package-acl-grant.test.tssrc/main/startup/windows-install-dir-package-acl-grant.tssrc/main/startup/windows-install-dir-package-acl-repair.tssrc/shared/secure-file-windows.test.tssrc/shared/secure-file.tssrc/shared/secure-path-windows-acl.tssrc/shared/windows-security-addon.tssrc/shared/windows-security-descriptor.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- config/main-sync-spawn-baseline.txt
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
…ative # Conflicts: # config/scripts/check-main-sync-spawn-ratchet.mjs
…e close fails - The GitHub Windows runner is the built-in Administrator (RID 500), whose SID reads back as LA. Production already resolves it through localDomainSidOf; the win32 test helpers parsed without it. One shared readDaclOf resolves aliases for every real-ACL suite, and the verifier's unit test covers an LA-aliased directory and LA as a stranger. - createFileWithDacl treats a failed CloseHandle like a failed write (lost data on a network volume) and deletes the torn file. - Drop a duplicate test reset and a typo.
…tructure (review #28135) - Loader: orcad's staged binary first, then the workspace package only if its real path is inside the app (process.resourcesPath, or this checkout). A bare specifier otherwise walks every parent up to C:\node_modules, which any local user may create, and orcad holds credentials. - The addon reads a DACL as { protected, aces: [{ type, flags, mask, sid }] } via GetAce, with a SID only for plain allow/deny ACEs and null for a NULL DACL. findSecureDaclViolation and the install-dir probe check that structure, so conditional, callback and object ACEs fail closed. The SDDL parser, its alias table, LA/localDomainSid resolution and getDaclSddl are gone; SDDL is only the write format. - Install-dir tree pass: files flagless as before, but a directory only when its own DACL is protected (inheritably). Re-granting inheriting directories re-propagated each subtree (3.2 s for 1,093 directories). The root is no longer granted, or counted, twice. The two passes are plain functions behind a { root, tree } seam. - One namespacing (path.win32.toNamespacedPath) at the addon boundary, also the per-path ordering key; the comment states the real sync-grant invariant. - restrictWindowsDirectory(dir): Promise<boolean> replaces the callback lane; the reporter is guarded once, so the 'settle' stage is gone. - Sync-spawn ratchet: the secure writers no longer spawn or block, so they leave the gate (109 -> 61 uses). - Errors name the Win32 call and code only; a failed GetSecurityDescriptorControl fails the write instead of defaulting to unprotected.
…old chmod order writeSecureFile now chmods the temp file to 0o600 before fsyncing it, so the fsync also covers the mode change and the file is owner-writable when opened. Assert what matters under umask 0o200: the write succeeds, the temp file is fsynced read-only, and the published file is exactly 0o600.
path.win32.toNamespacedPath rewrote POSIX temp paths (/tmp/x -> \tmp\x) in the Linux runs of the Windows-logic unit tests, so the fake addon wrote to the wrong place. The addon only runs on Windows, where the host function is the win32 one; elsewhere it is the identity. The long-path assertion runs on Windows only.
…ndows only The prefix comes from the host's toNamespacedPath, which is the identity off Windows; the Linux run of this suite expected the win32 escape.
…not be listed (review #28135) A readdir failure on userData aborted the whole grant, so the root grant that could repair a DACL refusing the listing was never attempted. The listing failure is now recorded, the root is still granted, and the marker stays withheld so the next launch retries.
…SIDs (review #28135) - The large-relock test waited only for the parent to turn protected, which happens before propagation reaches the children, then read a child. It now waits on the parent and both ends of the child range. - The token-user test rejected S-1-12-1 (Entra ID) accounts, failing the suite on Entra-joined dev machines.
ELI5
On Windows, Orca keeps secrets (pairing keys, device tokens, the local runtime token, plugin secrets) in ordinary files and locks each one so only you, the system, and administrators can read it. Until now it did that locking by starting a Windows tool,
icacls.exe, five times for every save. Each start froze Orca's window until it finished, and on PCs with corporate security software that can take seconds. Security sandboxes also flag the exacticaclscommands Orca ran as ransomware behaviour.Orca now asks Windows directly, from inside its own process, using a small native module. A secret file is created already locked. No programs are started, and a save costs about half a millisecond.
What Changed
What users see
icacls.exeorwhoami.exeprocess is ever started.How it works
New native module,
native/windows-security(@orca/windows-security). A small N-API addon over advapi32, packaged and built the same way as@orca/windows-registry. It has five jobs:The module is loaded once, together with the current user's SID, and only from inside the app: from the copy
orcadships beside its own files, or from the app's ownnode_modules. It never follows Node's usual search up parent folders, which could reach a folder likeC:\node_modulesthat any local user can create. Every path it receives gets the long-path (\\?\) prefix, and background permission changes to the same folder run one after another.Deciding what the access list should be, and checking it, stays in TypeScript.
Writing a secret (
writeSecureFile). The temporary file is created already locked to the user, SYSTEM and Administrators. Orca reads the lock back once, then renames the file into place; a rename keeps the lock. No access list is changed after the file exists, so a secret is never readable under the folder's broader permissions, not even briefly. Reading the lock back is what catches FAT32 and some network shares, which silently ignore it.Locking a folder. Each folder is checked once per process. It is rewritten only when the check fails, and the rewrite runs on a background thread. Windows applies a folder change to every file inside it, which took about 2 s on a real 29,000-file app data folder, so this must never block a save or startup. Folders that earlier versions already locked pass the check, so they are never rewritten.
The check uses OpenSSH's ACE rule for Windows: only the user, SYSTEM and Administrators may be granted anything, whether directly or inherited. Orca adds two of its own: the item must be protected from its parent folder, so loosening the parent later cannot reach it, and the user must have full control of it. (OpenSSH also checks the owner; Orca does not.) Any entry that is not a plain allow or deny (conditional, callback or object entries), and a NULL access list, fails the check and gets re-locked. Files and folders locked by the old
icaclscode pass unchanged, so there is no migration. A file that only inherits its permissions is re-locked once.If the module cannot load, the save still happens, reports "not restricted" as before, and logs once. There is no
icaclsor PowerShell fallback. If the module is loaded but creating the locked file fails, the save fails with an error naming the Windows call and error code, rather than quietly writing the secret unlocked.Packaging fails a Windows build when the native module binary that ships (the copy under
node_modules) is missing or built for the wrong CPU type, so the problem can't first appear on a user's machine.Every other runtime
icacls/whoamicaller now uses the same module:grantDirAcland its async version (the permission-error retry paths);The artifact-intent store drops its own temp-file writer, folder cache and synchronous folder re-lock, which existed only to avoid per-save launches, and saves through
writeSecureFilelike every other secure store.The repair's copy-paste commands for administrators are still shown as text; nothing runs them. The probe reads the same structure as the check, so an entry it cannot evaluate makes the whole reading "unreadable" instead of being skipped. The repair's second pass now grants every file but only the folders whose own permissions are protected (inheritably, so their contents get it too): every other folder already inherits the first pass's grant, and re-granting it re-applied the change across its whole subtree (3.2 s for 1,093 folders in a 7.6k-entry install).
orcad(the headless server) on Windows ships the module next to its bundles. It is built per architecture (x64 and arm64) and its CPU type is checked when packaged.orcadrefuses to start if it cannot lock its data folder, so a Windowsorcadwithout this module would never start.Why
Two problems shared one cause: changing a file's permissions on Windows meant starting another program.
icacls /reset+/inheritance:r, scored Malicious by behavioural sandboxes #25583: the program's commands are what security sandboxes score as malicious.Alternatives considered:
icacls, but run it asynchronously or in a worker. This removes the freeze but not the flagged behaviour. Running it asynchronously also publishes the secret before it is locked, which gives up the current guarantee.Set-Acl. Slower to start thanicacls(about 1–1.5 s cold), and a PowerShell launch is itself something security tools flag.C:\orca-data, which inherits Authenticated Users: Modify fromC:\), or when the profile is roaming or redirected. Locking that folder still has to happen in-process.orcad, which cannot use Electron's safeStorage.Locking in-process keeps today's guarantees (verify first, never publish an unlocked secret, report failure) and starts no programs at all.
Linked Issue
Fixes #26027
Fixes #25583
Visual Proof
N/A. The only visible change is that the UI no longer freezes; this is measured below rather than recorded.
Testing
Tested on Windows 11 x64 (10.0.26200). macOS and Linux behaviour is unchanged (they still use
chmod; the module is never loaded there).Spawns and blocking time per secure write (same harness, every child process counted; temp folder and a moved data folder under
C:\):icacls/whoamistarts per write, 94–138 ms blocking, plus 6 moreicaclsstarts afterwards to lock the folder.No moment of exposure. A second thread read the permissions of every temporary and final file while the main thread saved continuously for 1 s into a folder under
C:\that inherits Authenticated Users: Modify: 1,587 saves, 28,343 reads, 1 distinct access list, 0 broader than allowed.First launch after upgrade, default install.
%APPDATA%\orcaand its key files already pass the check, so nothing is rewritten. The startup-grant marker still matches, so no grant runs. Cost: module load about 2 ms, check about 1 ms. When a folder does need locking it happens in the background: 4,000 entries took about 220 ms while the save returned in 1.2 ms. A folder that already passes is not rewritten on the next launch.Real-permission tests on Windows (registered in both Windows CI lists):
windows-security-addon.win32.test.ts: the module itself. This includes that inherited entries passed in are dropped and recomputed from the parent, and that a grant keeps Windows' required order (deny, then allow, then inherited).secure-path-windows-acl.win32.test.ts: real files and folders, the moved data folder, paths longer than 260 characters, and old-format locks left unchanged.Unit tests for the check, the save and read paths (with a fake module), the startup grant,
grantDirAcl, the install-folder grant and repair, andorcadpackaging. Each process-starting path asserts it starts nothing.Native and packaging checks:
ensure-native-runtimefor Node and Electron, including a negative check: delete the built module and it is detected and rebuilt.orcadbuilt for win32-x64 with the module included; both x64 (0x8664) and arm64 (0xaa64) builds pass the CPU-type check.orcad, with nonode_modules, loads the module and locks its data folder.pnpm tc,typecheck:node,typecheck:web,typecheck:cli, oxlint, the anti-slop lint,check:code-quality:changed, the max-lines and process-host-import ratchets.I manually tested these changes locally
Automated tests added/updated
Notes
secure-file.ts. That PR's background save path is no longer needed: perf(notifications): stop every Windows notification from freezing Orca #27731 already moved its store off secure files, and this PR makes the remaining secure saves cheap.