插件目录 / Security / topolyte-windows-acl
topolyte-windows-acl
已验证 · 实测可装 Ranecc
功能简介
Windows ACL write-restriction sandbox provider for DeepSeek Harness with non-blocking workspace-grant materialization.
可用 — 实测通过,早期项目
Windows ACL write-restriction sandbox provider for DeepSeek Harness with non-blocking workspace-grant materialization. 实测能干净安装、正常启动。早期项目,但功能可用。
「已验证」表示我们的自动化 CI 在干净 profile 里实际执行了 dsh plugin add 并启动成功——仅此而已。功能描述与版本兼容性均为作者声明。这不是安全审计,也不代表对第三方代码的背书。
README
@topolyte/windows-acl
Windows ACL write-restriction sandbox provider for DeepSeek Harness
with non-blocking workspace-grant materialization.
The official windows-acl backend materializes a workspace's standing write ACE
synchronously inside confine(): on a large workspace the eager
inheritable-ACE propagation (SetNamedSecurityInfoW) walks the whole tree for
minutes on the Node event loop, freezing the entire server (all sessions,
HTTP, RPC) on first use. This package fixes that — the tree walk runs in agrant-cli child process, fire-and-forget, so the first provision never
freezes a command spawn.
Installs on the official harness as a ctx.sandbox replacement via the
Cordis bundle-patch mechanism — no fork required. The provider is
Windows-only by construction; on Linux/macOS the bundle patch leaves the
official platform-neutral sandbox chain (bwrap/landlock/seatbelt) untouched and
this provider's row is inserted disabled — install is a safe no-op there, so
the same one-liner works on every platform.
What it does
- Non-blocking authorization — the workspace's standing write ACE is
materialized out-of-band by agrant-clichild process (spawn, notspawnSync); concurrent first-time calls for one workspace coalesce onto a
single helper spawn, and workspace roots are canonicalized
(realpathSync.native, per the officialworkspaceWriteSidcontract) so
every spelling of one directory shares one in-flight grant, one SID, and one
failure counter. - Fail-closed, no early child starts — a workspace-write command is not
started until its grant is confirmed: while the grant
ispreparingorfailed,confine()refuses withSandboxUnavailableError
(never a freeze, never a silent half-authorized run); once it stands, the
exact-ACE skip makes every later provision O(1). - Standing / revocable lifecycle preserved — the workspace ACE is the
cross-session reuse cache (never revoked); each session/workspace pair gets a
random private temp directory with its own capability SID, revoked on
provider dispose. prewarmconfig — start the walk at boot for known workspaces, shrinking
the fail-closed window before the first command.- Bounded retry — a per-workspace consecutive-failure counter
(maxGrantRetries, default 3): past the cap the workspace is pinned tofailedand automatic retries stop (fail-closed, never a freeze); an
operator or agent resets it withretryWorkspaceGrant. - Explicit grant state —
workspaceGrantState(root)reportspreparing/ready/failed, so agents/executors can distinguish states
instead of guessing. - Settle-aware shutdown — provider dispose waits for every in-flight
grant-cli helper to settle before revoking temp ACEs, so a helper still
walking the tree is never orphaned or revoked mid-flight. - Built on the official
@deepseek-ai/dsh-sandbox-windows-aclprimitives
(AclWriteGrant,workspaceWriteSid,tempWriteSid) and the officialSandboxProviderbase class — the full official sandbox semantics are
inherited.
Install
From the npm registry (after npm publish — the same one-liner as any plugin):
dsh plugin --profile <name> add @topolyte/windows-acl
First install on a fresh profile: pnpm 11 blocks
koffi's postinstall
build script by default (ERR_PNPM_IGNORED_BUILDS: Ignored build scripts: [email protected]). Allow it once in the profile'spnpm-workspace.yaml:allowBuilds: koffi: trueThis is pnpm 11's universal behavior for native-module dependencies — the
upstream windows-acl backend requires the same approval.
From a GitHub release tarball (no registry needed — download the .tgz
attached to the release):
dsh plugin --profile <name> add topolyte-windows-acl-0.2.0.tgz
From a local checkout:
dsh plugin --profile <name> add /path/to/@topolyte/windows-acl
The bundle's cordis.patch.yml disables the official @deepseek-ai/dsh-sandbox-local
row and inserts this provider under its own id (topolyte-sandbox) — both
actions gated on process.platform === 'win32' — so on Windows it becomes the
only live ctx.sandbox service, while on Linux/macOS the official row stays
live and this row is inserted disabled (never selected as ctx.sandbox).
Configure the workspaces to pre-warm at boot:
- insert:
- id: sandbox
name: '@topolyte/windows-acl'
config:
prewarm:
- C:/path/to/your/workspace
How it works
tool-pwsh / tool-bash
└─ confine() ──► TopolyteWindowsAclProvider (extends SandboxProvider)
├─ kickOffWorkspaceGrant(root) spawn grant-cli child (never awaited)
│ └─ grant-cli: AclWriteGrant.add(root, standing) ← full-tree walk HERE
├─ workspaceGrantState(root) preparing / ready / failed
│ └─ not ready → SandboxUnavailableError (command not started)
└─ runner argv: --workspace --temp --mode --write-sid --temp-write-sid
The expensive grantWrite → SetNamedSecurityInfoW eager inheritance walk runs
in the grant-cli child process, so the harness event loop never blocks.
Grant lifecycle (preparing / ready / failed)
The out-of-process design is a state-machine change (per the community repair
invariants in the DeepSeek Harness Handbook):
no child may run before the grant is confirmed. Each workspace root moves
through three explicit states, exposed via workspaceGrantState(root):
stateDiagram-v2
[*] --> preparing: kickOffWorkspaceGrant (grant-cli child spawned)
preparing --> ready: helper exit 0 → standing ACE stands
preparing --> failed: helper exit 127 × maxGrantRetries
failed --> preparing: retryWorkspaceGrant() resets the counter
ready --> ready: exact-ACE skip (O(1) per command)
ready --> [*]: provider dispose (standing ACE stays; temp ACEs revoked)
preparing— the grant is walking in the background (minutes on a large
tree);confine()refuses to start the command withSandboxUnavailableError.
Agents either retry,await ensureWorkspaceGrant(root), or rely on aprewarmed workspace that was started at boot.ready— the standing ACE is confirmed; every later provision hits the
exact-ACE skip and is O(1).failed—maxGrantRetriesconsecutive grant failures (default 3) pin the
workspace here: automatic retries stop and commands stay refused until an
operator or agent callsretryWorkspaceGrant(root).
Measured impact


Measured on synthetic trees (1k–30k files, rounds=7, first-add measured on 3
independent dirs per scale; scripts/bench-acl-deep.ts, raw reportdocs/bench-acl-deep.json). To cross-validate on a real monorepo (e.g. the
deepseek-harness checkout itself), runpnpm exec node --import tsx/esm scripts/bench-acl-deep.ts --real <path> --report docs/bench-acl-deep-real.json
— note it writes a standing write ACE (never revoked) to that directory.
| Files | Sync first median (server freeze) | First min/max | Subsequent median (skip) | Async stall max / p50 | Confirm cold (fail-closed window) | Confirm prewarmed |
|---|---|---|---|---|---|---|
| 1,000 | 202.2ms | 196.1/245.3 | 0.8ms | 86.5/15.7ms | 597.7ms | 342.1ms |
| 5,000 | 1,173.2ms | 974.7/1,237.0 | 1.0ms | 79.1/15.5ms | 1,641.1ms | 325.3ms |
| 10,000 | 1,931.8ms | 1,862.4/2,470.9 | 0.7ms | 70.9/15.5ms | 3,027.8ms | 358.8ms |
| 20,000 | 5,015.3ms | 4,511.1/5,314.1 | 0.8ms | 74.8/15.6ms | 6,223.8ms | 356.7ms |
| 30,000 | 7,574.6ms | 7,241.8/8,072.1 | 0.6ms | 117.6/15.6ms | 8,919.4ms | 345.1ms |
- The sync first provision follows a power law t = 0.133·n^1.06 (R² = 0.995)
— statistically ~O(file count) — and is a whole-server freeze: a 5mssetIntervalfires ZERO times for the duration. - Every later provision is flat at 0.6–1.0ms median (exact-ACE skip),
tree-size independent — the gap a user feels grows from ~250x at 1k files to
~12,600x at 30k. Honest caveat: the skip is not strict O(1) — roughly 1 in
10–20 later provisions falls back to the full-tree merge path (thehasExactGrantimplausible-ACL guard) and takes ~the first-add wall clock
again. On the official sync path that tail is a server freeze on the event
loop; with this package it happens inside thegrant-clichild, so the
server's responsiveness is unaffected either way. - With this package the same first-tree walk runs in the
grant-clichild; the
server's largest event-loop gap is ~70–120ms (child-spawn cold start, p50
flat at ~15ms) and does not grow with tree size. - Fail-closed confirm window: while the grant is
preparing,confine()
refuses the command (SandboxUnavailableError). That refusal lastsconfirm cold— child spawn + full-tree propagation (0.6s at 1k → 8.9s at
30k), all off the event loop. Aprewarmed workspace (standing ACE already
landed) cuts it toconfirm prewarmed, a flat ~325–360ms of Node/tsx cold
start with the tree walk skipped — tree-size independent.
Why (the mechanism behind the numbers)
grant.add(root, true)callsSetNamedSecurityInfoWwith OI|CI inheritance.
The kernel eagerly re-applies the inheritable ACE to every live
descendant — one synchronous call, linear in file count, on the event
loop. That is the freeze.- The exact-root-ACE skip (
hasExactGrant) is not an optimization but a guard:
re-applying the identical ACE would re-trigger the same O(n) walk. When the
guard misses it re-walks (see the long-tail caveat above). - Moving the walk into the
grant-clichild (spawn, notspawnSync) keeps
that O(n) cost off the event loop; the main thread only pays the spawn
syscall (~70–120ms, p50 ~15ms), regardless of tree size.
Test
# unit tests
pnpm --filter @topolyte/windows-acl exec vitest run
# real end-to-end (ACL semantics via icacls; spawns real grant-cli + runner)
pnpm --filter @topolyte/windows-acl exec node --import tsx/esm scripts/e2e-acl.ts
Why not a fork / PR
The upstream maintainers are aware of the ACL cost (see the "eager inheritance;
minutes on large workspaces" note in the official acl.ts) but do not accept
PRs. This package ships the same fix as an independent, installable plugin so
any official-harness user gets the non-blocking behavior without maintaining a
fork.
Third-party notices
This package builds on, and in no way modifies, the following upstream projects
(their licenses apply to the linked sources, not to this package's own code):
- DeepSeek Harness (MIT) —
SandboxProviderbase class and thedshplugin/bundle runtime this plugin
plugs into (@deepseek-ai/dsh-sandbox,@deepseek-ai/dsh-sandbox-windows-acl,@deepseek-ai/dsh-sandbox-local). - Cordis (MIT) — the plugin/bundle
container (ctx,plugin, bundle-patch layers). - koffi (MIT) — the FFI layer used by the
upstream windows-acl backend forSetNamedSecurityInfoW; not a direct
dependency here but transitively exercised by the ACL primitives we reuse. - Win32 API
SetNamedSecurityInfoW
— Microsoft docs reference for the inherited-ACE propagation semantics this
fix avoids.
All reuse is via the official packages' published public APIs and type
declarations; no upstream source is vendored or copied into this repository.
Contributing
Contributions are welcome but should stay within the package's narrow scope:
non-blocking Windows ACL sandboxing for DeepSeek Harness. Before opening a PR,
please:
- Keep every change on a real code path — no empty skeletons, no
not-implementedstubs, no hardcoded values. - Preserve the fail-closed invariant: a missing standing ACE must deny, never
freeze, and never silently widen the grant. - Add or update tests: unit tests (
vitest run) and, for ACL semantics, the
real end-to-end script (scripts/e2e-acl.ts, Win32-only, asserts viaicacls). - Verify with
pnpm --filter @topolyte/windows-acl exec vitest runbefore
submitting.
Report bugs and upstream concerns via GitHub issues rather than forks; this
package deliberately avoids forking the official harness.
License
MIT
安装
装一次目录插件,之后本站所有插件都能让 DeepSeek Harness 自动找、自动装:
dsh plugin add dshbase-catalog 然后对 agent 说「帮我装 topolyte-windows-acl」,它会在目录里找到并自动安装。文档:dshbase-catalog · 已验证场景包。
该插件是 GitHub 源码(未发 npm)——直接从仓库装:
Web profile:
dsh plugin --profile web add github:Ranecc/topolyte-windows-acl Headless(CLI)profile:
dsh plugin --profile headless add github:Ranecc/topolyte-windows-acl 实测报告
验证通过:从 GitHub 源码完成 L1 安装 + L2 加载 + L3 运行(dsh 0.1.0-rc.6)。