Skip to content
4 min read · 704 words

Interface: SandboxHandle

Defined in: src/batteries/sandbox/manager.ts:28

A live sandbox session: the object every sandbox tool is built against.

Remarks

THIS IS A PROCESS-GLOBAL CAPABILITY WEARING A PER-HANDLE API, and the shape is a promise narrowed rather than a promise kept. SandboxManager is a singleton, so the first createSandbox() in a process establishes the policy and a later one may only NARROW it — a request that is not a subset throws E_SANDBOX_POLICY_CONFLICT naming both. One policy per process is the safe deployment; multi-tenant agents with different policies want separate processes.

Every run() re-validates against the admission baseline before spawning, so a widening of the live policy is DETECTED. Detection is not prevention: SRT's proxies consult policy per request, so a widening already affects an in-flight child for its whole lifetime.

Properties

PropertyModifierTypeDescriptionDefined in
effectivePolicyreadonly() => DerivedRules | undefinedThe live DERIVED rules — not the SandboxPolicy that was requested. Remarks A policy cannot express what the drift check has to compare (denyOnly/allowWithinDeny, the unioned default write paths, Linux-expanded read globs, the profile-only mandatory denies), so returning one here would compare the wrong thing and pass while the live sandbox had widened. Under ADOPTION this re-derives from the live manager on every call; an owned session is cached.src/batteries/sandbox/manager.ts:46
epochreadonlySandboxEpochOpaque token issued at construction and invalidated by SandboxHandle.dispose. Remarks File-backed readers hold this rather than a filesystem reference, which is what makes disposal deterministic: a staged reader outliving its TURN is legitimate, outliving its HANDLE is not.src/batteries/sandbox/manager.ts:36

Methods

dispose()

ts
dispose(): Promise<void>;

Defined in: src/batteries/sandbox/manager.ts:76

Quiesce the session: it does not abandon in-flight work.

Returns

Promise<void>

Remarks

Rejects new work with E_SANDBOX_NOT_INITIALIZED, aborts in-flight invocations through their signals, kills spawned children, then releases the enforcer. On an ADOPTED session this is a reported NO-OP — resetting a manager we did not create would strip ACEs a host application depends on.


isEpochLive()

ts
isEpochLive(epoch: SandboxEpoch): boolean;

Defined in: src/batteries/sandbox/manager.ts:66

Predicate consumed by file-backed readers; disposal makes the epoch unusable.

Parameters

ParameterType
epochSandboxEpoch

Returns

boolean


narrow()

ts
narrow(policy: SandboxPolicy): Promise<void>;

Defined in: src/batteries/sandbox/manager.ts:64

Narrow the session policy for subsequent operations.

Parameters

ParameterTypeDescription
policySandboxPolicyMust be a subset per the per-axis rules; reads are deny-then-allow while writes are allow-only, so "narrower" is not symmetric. Throws E_SANDBOX_NARROWING_UNSUPPORTED naming the axis where the platform cannot narrow it.

Returns

Promise<void>


run()

ts
run(options: {
  argv: string[];
  correlationId: string;
  cwd: string;
  env?: Record<string, string>;
  policy: SandboxPolicy;
  signal?: AbortSignal;
}): Promise<{
  completed: Promise<{
     exitCode: number;
     failed: boolean;
  }>;
  stderr: ReadableStream<Uint8Array<ArrayBufferLike>>;
  stdout: ReadableStream<Uint8Array<ArrayBufferLike>>;
}>;

Defined in: src/batteries/sandbox/manager.ts:56

Spawn under this session's policy, optionally narrowed for the single invocation.

Parameters

ParameterTypeDescription
options{ argv: string[]; correlationId: string; cwd: string; env?: Record<string, string>; policy: SandboxPolicy; signal?: AbortSignal; }-
options.argvstring[]-
options.correlationIdstring-
options.cwdstring-
options.env?Record<string, string>An ADDITIVE per-call overlay on the child's environment, applied LAST. Remarks NOT the host-inheritance control. What a child inherits from the host is the ADAPTER's decision (the Node/SRT one denies by default and takes an allow-list at construction); this field only adds to whatever that produced, and an adapter MUST NOT let it silently widen what the deployment allowed. Leaving these semantics unstated is how the Node adapter came to spread the entire process.env into every child while this field sat unused.
options.policySandboxPolicy-
options.signal?AbortSignal-

Returns

Promise<{ completed: Promise<{ exitCode: number; failed: boolean; }>; stderr: ReadableStream<Uint8Array<ArrayBufferLike>>; stdout: ReadableStream<Uint8Array<ArrayBufferLike>>; }>

Remarks

Resolves as soon as the child is SPAWNED, handing back live stdout/stderr streams plus a separate completed promise. Drain BOTH concurrently: pipe buffers are per-fd, so draining one to completion first can block the other and hang the child. A non-zero exit is data on completed, never a rejection.