DSHarness Systems Dissection Pinned baseline 47f943859b · 36 verified / 0 in progress / 36 chapters
中文
Execution Environment·Chapter 20

Sandbox, Filesystem, and Observation Policy

How processes and files share one execution world and root policy

VerifiedUpstream 47f943859bScope: Analyze sandbox seams and backends, policy, filesystem providers, write/edit intent, read-before-edit, E2B, and cross-platform behavior.

Conclusion: there is no universal “sandbox” here, but four independent policy chains that must hold together

DeepSeek Harness separates safety and consistency into four layers: process argv confinement limits shell file-write effects; the Filesystem provider defines path identity, reads, and atomic publication; an in-process write fence rejects filesystem mutations outside the current sandbox mode; and the observation policy requires a Session to have seen a target and its version before changing it. Permission presets and escalation approval only choose a wider mode; they replace none of those layers. The decomposition is clean, but the names invite stronger assumptions than the code supports: read-only does not mean “unable to read secrets,” and workspace-write does not isolate networks, processes, devices, or credentials. SANDBOX-SEAMSANDBOX-DOC-BOUNDARY

Audit conclusion

The shipped base composition connects these layers coherently, but the core services do not prove that they all belong to the same security world. Custom compositions, shared temporary directories, out-of-process writers, symlink races, and reuse of remote process identities remain deployment boundaries that operators must understand.

1. Start by separating four contracts

LayerQuestion it answersQuestion it does not answer
SandboxProviderHow should this exact argv be wrapped, and where may its file writes land?What may the command read, whether it may use the network, or which processes it can see
FileSystemHow are paths resolved and identified, and how are files read, written, and published?It does not provide root containment or read-before-write by default
fs-sandboxIs a write/edit target inside the writable roots for the current mode?It does not fence reads and is not a kernel boundary for arbitrary code
fs-observation-policyHas this Session observed “present/absent + version,” and does it still match before mutation?It is neither identity authorization nor path authorization, and it does not prove the whole file was viewed

Sandbox mode has exactly three values—read-only, workspace-write, and danger-full-access—and the production types explicitly define it as “file-effect confinement.” A provider returns not only argv but also full/partial enforcement, denial signatures, and runner-failure rules. The consumer must execute the returned argv and may not silently fall back to the original command. SANDBOX-SEAM

2. There are two defaults: the service defaults to read-only, while the shipped base defaults to workspace-write

Do not confuse a schema default with a product default

The sandbox-policy service itself defaults to read-only. The base bundle explicitly overrides the deployment default to workspace-write and sets the approval policy to ask. A bare service instance and the shipped product composition therefore produce different results, both directly supported by source. SANDBOX-SHIPPED-POLICYSANDBOX-POLICY

The effective policy for each call follows fixed precedence: an explicitly approved mode, then the last mode event in the Session, then the deployment default. The policy root comes from the current Session cwd, with a normalized deployment root as fallback when no Session exists. The writable set for workspace-write is the workspace root, fixed /tmp, and platform os.tmpdir(), canonicalized on a best-effort basis and deduplicated; read-only has no writable root. SANDBOX-WRITABLE-ROOTS

3. Mode is a Session event, not a mutable global on a shell process

deployment default
        ↓
last sandbox/mode event in this Session
        ↓
approved per-call override (one execution only)
        ↓
SandboxExecutionPolicy { mode, root, sessionId }

Session mode is folded from sandbox/mode events, and the sole formal write path merely appends that event. Resume and replay can therefore reconstruct permission state, while a one-time approval never silently becomes the persistent Session default. SANDBOX-MODE-EVENT

4. Escalation approval is a strictly monotonic pre-execution flow

A call may escalate only from read-only to workspace/full, or from workspace to full; the same or a narrower mode is not a valid “escalation.” sandbox_permissions and justification must appear together. Approval occurs before any execution and forwards cancellation to the approval service. Only allowed-once yields a wider one-call policy; rejection, cancellation, and unavailability never execute the original command. SANDBOX-ESCALATION

Permission is not confinement

Approval answers whether this call may widen its boundary; the sandbox provider answers how the widened boundary is enforced. Treating the former as the latter gives false conclusions when approval is absent, the runner is unavailable, or enforcement is partial.

5. The local backend is a chain of platform dialects, not one cross-platform isomorphic implementation

PlatformCandidate chain / profileEnforcementMaterial difference
Linuxbwrap → Landlockbwrap full; Landlock may be partialbwrap read-only binds / and gives workspace mode a private /tmp; Landlock permits writes to host temp and workspace
macOSSeatbeltdeclared fullallow by default, deny file writes, then allow writes to the writable roots
WindowsACL helperalways partialmaterializes capability through restricted SIDs/ACEs and temporary directories, without eliminating Everyone/hard-link bypasses

Linux probes candidates and caches the verdict when several exist, and fails closed with a structured error when all fail; a sole platform candidate is selected directly. Every dialect also carries its own denial and runner-failure signatures so consumers can distinguish command failure, sandbox denial, and sandbox unavailability. SANDBOX-LOCAL-DIALECTSSANDBOX-PROFILESSANDBOX-LOCAL-SELECT

6. A custom runner is an operator trust boundary; stderr classification is diagnostic only

A non-empty runnerCommand skips platform probing and is treated as full enforcement. The framework validates command shape and failure signatures, but cannot prove that the external program implements the confinement it claims. This is an explicit trusted deployment input. SANDBOX-LOCAL-WRAP

The one-shot shell executes the provider's exact argv, records enforcement, and classifies sandbox denial or runner failure from exit status, spawn attribution, and stderr signatures. Because the classifier consumes in-band stderr, a child can emit matching text and cause false attribution. That can harm availability and diagnosis, but it cannot undo confinement that was already applied. SANDBOX-BASH-CONSUMERSANDBOX-RESULT-CLASSIFIER

7. Windows ACL support is honestly labeled a partial capability

The restricted ACE on a workspace root is a standing grant. Session temp creates a random SID and random directory, rolling back half-materialized state on failure. Disposal best-effort revokes temporary grants and removes directories, but the workspace ACE remains and a process crash may leave residue. The implementation therefore reports partial enforcement instead of letting the word “sandbox” obscure Everyone ACEs, hard links, and cleanup limits. SANDBOX-WINDOWS-CAPS

8. The Filesystem seam separates display paths from target identity

FileSystem defines opaque targets, opaque versions, present/absent observations, guarded create/replace intents, and structured FsError values. Write/edit intents are single-slot waterfalls; observed is a synchronous recorder. A provider may expose sandboxMode, which determines whether tools may advertise mutation escalation. SANDBOX-FS-TYPESSANDBOX-FS-SEAM

The local provider's cwd only resolves relative paths; it is not a containment boundary. It separates the human-readable absolute path from a canonical target key: existing targets use native realpath, while missing targets canonicalize from the nearest existing ancestor and append the missing suffix. Aliases and most symlink spellings consequently share locks and observation identity. SANDBOX-FS-LOCALSANDBOX-FS-IDENTITY

9. Read paths are layered and publication is atomic, but the replace guard is not a cross-process CAS

Local reads distinguish whole-file UTF-8, bounded raw bytes, and streams. Binary detection samples only the first 8192 bytes, raw-byte reads have an explicit cap, and streams accept cancellation. The underlying readText has no maxBytes, so “every long read is inherently bounded” is not a provider guarantee; model tools separately choose window or stream paths. SANDBOX-FS-READ-IO

Mutation runs under a provider-local lock for each canonical target: first validate the create/replace guard, then write and sync a mode-0600 file inside a mode-0700 sibling staging directory, and finally publish through hard-link no-replace create, Windows ReplaceFile, or rename. Cancellation is checked again before commit; after commit, cleanup is best effort. SANDBOX-FS-LOCAL-MUTATIONSANDBOX-FS-ATOMIC

Guarantee boundary

createIfAbsent uses a no-replace primitive at publication and is the stronger guarantee. The replaceIfVersion check occurs before staging/rename, however, and the lock covers only this provider process. Another process can write after the check and be overwritten at publication. This is “serialization inside one provider plus a pre-publication metadata check,” not a cross-process linearizable CAS. The official limitations likewise acknowledge metadata versions and process-local locks. SANDBOX-FS-LOCAL-LIMITS

10. fs-sandbox is a mutation-only path fence

fs-sandbox extends the local filesystem: read/list remain unchanged, and only write/edit check mode before delegation. Read-only rejects every mutation; workspace mode freshly resolves the target at call time and requires it to fall within canonical writable roots; full passes through. It explicitly positions itself as defense in depth for trusted in-process providers and model-supplied paths, not a kernel boundary for arbitrary native code. SANDBOX-FS-FENCE

Containment combines a separator-aware lexical check with file-identity fallback for existing roots and targets, including Windows aliases/casing. Tests cover absolute paths, .., existing symlink escape, creation beneath a symlink, stale targets, and per-call overrides. SANDBOX-CONTAINMENTSANDBOX-FS-FENCE-TEST

11. The actual read / write / edit control flow

read(path)
  resolve from Session cwd → stat/read bounded window → emit present/absent observation

write/edit(path, optional escalation)
  resolve requested policy → approval if wider → resolve from policy root
  → fs write/edit intent waterfall → provider guard + mutation
  → map structured denial → emit new present observation

Session cwd is canonicalized again during resolution so symlink-sensitive .. does not inherit a stale string; the sandbox policy root takes precedence over ordinary cwd. A successful read records present, while confirmed ENOENT records absent. Write and edit both obtain policy and trigger an intent before I/O, and refresh observation only after success. SANDBOX-SESSION-CWDSANDBOX-READ-TOOLSANDBOX-READ-TARGETSANDBOX-WRITE-TOOLSANDBOX-EDIT-TOOL

Escalation fields appear in a tool schema only when the mounted filesystem provider reports sandboxMode. Structured FS_SANDBOX_DENIED maps to the shared denial marker and hint. A bare local filesystem and the default E2B filesystem do not masquerade as an escalatable local sandbox. SANDBOX-FS-ESCALATION

12. Observation policy is an ephemeral state machine keyed by Session object and canonical target

Latest observationWrite intentEdit intent
nonereject overwrite; absence is unprovenreject
absentcreateIfAbsentreject
present(version)replaceIfVersion(version)replaceIfVersion(version)

State lives in WeakMap<Session object, Map<targetKey, observation>>; weak entries may be reclaimed once the Session owner is garbage-collected. Source has no Session-dispose hook. Explicit reset of the whole table occurs on policy-plugin disposal/HMR. Successful read/write/edit refreshes state, and a mutation with no Session owner cannot borrow another Session's observation. SANDBOX-OBSERVATION

Write/edit intent is a first-listener-wins waterfall, and registration order is not a stable contract. Removing the policy reduces mutation to the provider's unconditional call. Production tests pin present/absent decisions, owner isolation, the first-listener rule, and state reset after policy-plugin disposal. SANDBOX-OBSERVATION-DOCSANDBOX-OBSERVATION-TEST

13. “Read before edit” guarantees freshness, not completeness or authorization

Three common misreadings

First, observations are not persisted, so resume must read again. Second, bypassing tools and calling ctx.fs.read* directly does not automatically record an observation. Third, reading one window is sufficient to authorize a whole-file overwrite at the same version. The policy proves that “the caller saw some view of this target at a current version”; it does not prove that the caller saw the whole file or is authorized for the path. SANDBOX-OBSERVATION-LIMITS

The correct composition is therefore: identity/deployment authority grants possession of a capability, sandboxing decides where it may write, the observation guard prevents overwrites from stale or unseen state, and atomic publication defines the on-disk commit. None compensates for another.

14. Permission presets synchronize three states but validate only shell confinement

The permission-preset service requires a confining ctx.shell and an approval service. Shipped presets correspond to workspace-write + ask and danger-full-access + never. A user switch writes permission/preset, sandbox/mode, and approval/policy together, and pins initial state for new Sessions. SANDBOX-PERMISSION-PRESETSSANDBOX-PERMISSION-WRITE

Composition gap

The preset service checks only that the shell is confining; it does not prove that ctx.fs is provided by fs-sandbox under the same policy. The shipped base does mount observation, tool-fs, and fs-sandbox together, so the pinned baseline is coherent. A custom composition could nevertheless display “workspace-write” while connecting write/edit to a bare filesystem. The cross-capability same-policy invariant belongs to deployment composition rather than being enforced by types or startup validation. SANDBOX-SHIPPED-FSSANDBOX-SHIPPED-FS-PROVIDER

15. Persistent terminals inherit confinement but lose part of the structured observation surface

The terminal provider blocks a change to Session sandbox mode while a PTY is being created or remains active, preventing one long-lived shell from crossing a permission boundary. At creation it resolves policy for the Session, asks the sandbox provider to wrap the shell argv, and cleans up on initialization failure. SANDBOX-TERMINAL-FENCE

The terminal uses only the wrapped argv, however; unlike the one-shot shell, it does not carry enforcement, diagnostic dialect, and runner classification into each result. Runner refusal still fails, but the Host has weaker structured visibility into why a persistent PTY failed or whether enforcement was partial. This is an observation-surface difference, not a confinement bypass.

16. E2B is not another SandboxProvider runner; it replaces the whole execution world

The E2B owner holds one remote sandbox handle per Context, validates the API key and absolute Linux cwd, creates a mode-0700 runtime root, and kills the sandbox to roll back failed bootstrap. The SDK's unavoidable login shell receives a random HOME to reduce startup-file effects. This ownership scope means that multiple Sessions sharing one owner in the same Context also share the remote filesystem/process world; a Session is not itself a VM isolation unit. SANDBOX-E2B-OWNER

The remote filesystem establishes canonical identity with remote GNU realpath and provides bounded raw-byte reads, process-local target locks, guard checks, and atomic staging/publication; readText instead asks the SDK for the whole file. Once publication starts, link/rename deliberately stops receiving cancellation, avoiding an ambiguous state in which the caller sees “canceled” but cannot know whether commit occurred. SANDBOX-E2B-FSSANDBOX-E2B-PUBLICATION

Remote subprocess resolves executables and manages process groups and terminals in that same sandbox. Group signaling still has a known numeric-PGID reuse race. Official limitations also state that there is no host file synchronization, path reopen races remain, and locks are process-local. SANDBOX-E2B-SUBPROCESSSANDBOX-E2B-PGIDSANDBOX-E2B-LIMITS

17. The core invariant of remote composition is “capabilities in one world”

The headless E2B example explicitly disables local filesystem and local subprocess, then mounts the E2B owner, remote filesystem, and remote subprocess together. Its inner sandbox policy is full because the real boundary is the remote machine, not a local host runner. Equal path strings do not upload host files; inputs must enter the remote world through an explicit API or file write. SANDBOX-E2B-COMPOSITION

One more seam is easy to miss: glob/grep bypass ctx.fs and use ctx.subprocess to execute an absolute ripgrep path resolved from the host package, assuming that executable, workdir, and readable paths share one world. A custom remote adapter that cannot see that host binary cannot enable search merely by replacing the filesystem provider; composition must colocate the executable and filesystem. SANDBOX-SEARCH-WORLD

18. Cancellation, failure, and cleanup: different commit points produce different answers

PathCancellation boundaryFailure semanticsResidual risk
Approvalbefore execution; directly cancelablenever executes without approvalno mutation
Local shellforwarded to runner/subprocessspawn, denial, and runner failure are classifiedthe child's external effects depend on termination timing
Local filesystemlast check before commitpost-commit cleanup failure does not roll back the published filesibling staging residue receives best-effort cleanup
E2B filesystemcancelable before publication; commit primitive is not canceledavoids an ambiguous canceled commitremote staging cleanup is still best effort
Windows ACLgrant/revoke have separate rollback pathshalf-materialized failures are best-effort revokeda crash may leave SID/ACE/temp residue
E2B process groupsignal/kill ladderdriven by remote numeric identitya small PGID-reuse window

The Filesystem API has no generic timeout parameter; bounded raw-byte reads, streaming, and AbortSignal are its primary resource controls. The design prioritizes an explainable commit point over a promise of “instant cancellation” at any moment.

19. Static security audit: confirmed boundaries and risks

LevelFindingImpact / correct treatment
BoundaryThe three modes constrain file-write effects onlyThey cannot support claims of network, read, process, device, or credential isolation
MediumLandlock/Seatbelt/fs fence share host tempSessions under one OS user can collide or interfere; sensitive parallel work needs an isolated execution world
MediumThe fs fence explicitly accepts missing-ancestor symlink TOCTOUIt is defense in depth for trusted/model paths, not a strong adversarial boundary
MediumPermission presets do not validate fs/shell same-policy compositionThe shipped composition is not mismatched; custom compositions need an additional startup audit
MediumThe replace version guard is not cross-process CASAn external writer may be overwritten between check and publication
LowObservation is first-wins, removable, and a window read can authorize a whole-file writeIt is a freshness guard, not an authorization chain or proof of content completeness
LowRunner/denial classification depends on stderr signaturesDiagnostic attribution can be spoofed, but confinement cannot be escaped this way
Deployment trustA custom runner claims full without probingOperators must validate the runner independently; the config value is not proof
LowWindows cleanup residue and E2B PGID reuseIsolation remains partial / best effort and requires deployment-level monitoring and reclamation

20. Documentation drift: two concrete, repairable inconsistencies

DocumentCurrent statementSource fact
packages/fs/fs/README.mdIts FsErrorCode list omits sandbox denialThe type union includes FS_SANDBOX_DENIED
packages/sandbox/sandbox/README.mdIt summarizes implementations as Linux/macOS and consumers as bash onlyProduction now includes Windows ACL, a PowerShell path, and a filesystem-sandbox consumer

The first can cause provider/consumer authors to omit a structured error branch. The second causes architecture readers to underestimate the current platform surface and filesystem policy. SANDBOX-DOC-ERROR-CODESANDBOX-DOC-BACKENDS

21. Verification coverage and this chapter's boundary

Production tests cover local runner profiles, custom runners, candidate fallback/fail-closed behavior; all three fs-fence modes plus .. and symlink escape; and observation absent/present decisions, Session-owner isolation, first-wins, and disposal. SANDBOX-LOCAL-TESTSANDBOX-FS-FENCE-TESTSANDBOX-OBSERVATION-TEST

Verification status

This chapter statically traces production source, shipped composition, READMEs, and existing tests at the pinned commit, and separates source guarantees, official limitations, and this chapter's assessments. This delivery ran 15 targeted Vitest files spanning approval, presets, escalation, filesystem, process, Job, and Terminal behavior: 508 tests passed and one was skipped. That keeps the existing behavioral suite green; it does not promote a static security audit into a dynamic penetration-test conclusion.

Final assessment

The strongest design choice is the layering of capability seams, per-call policy, guarded mutation, and durable Session mode. The clearest opportunities are a cross-capability composition invariant, a cross-process replace CAS, and product-level visibility for shared temp and partial enforcement. The system's real safety comes from all layers holding together, not from the word sandbox.

My Learning Notes

Autosaved only in this browser. Nothing is uploaded or committed. Export Markdown whenever you want to keep a copy.