first commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# WorkerEngine
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`WorkerEngine` uses real `node:worker_threads` for true OS-thread isolation. Each `execute` call spawns a fresh `Worker` constructed from an inline source string (`eval: true`), posts the code + context + modules over the structured-clone message channel, and awaits the structured reply. Intrinsics are NOT shared with the host realm — the worker has its own V8 isolate, its own heap, and its own event loop. Timeouts are enforced by calling `worker.terminate()`, which is a hard stop the running script cannot intercept.
|
||||
|
||||
Use `WorkerEngine` when the code being sandboxed may be adversarial: a true OS-thread boundary defeats the prototype-chain escapes that compromise `vm-engine`. The cost is per-call worker startup, which is non-trivial; pair with `SandboxPool` when latency matters.
|
||||
|
||||
## Features
|
||||
|
||||
- Real `node:worker_threads` execution — OS-thread isolation.
|
||||
- Worker spawned per call, terminated after every result.
|
||||
- `worker.terminate()` enforces timeout — script cannot intercept.
|
||||
- Structured-clone passes context + modules.
|
||||
- Output-size cap applied inside the worker before postMessage.
|
||||
- OTel `recordExecution` + `recordDuration` per call.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { WorkerEngine } from "@suaveplan/sandbox/worker-engine";
|
||||
|
||||
const e = new WorkerEngine({
|
||||
timeout: 5000,
|
||||
maxOutputSize: 1024,
|
||||
memoryLimit: 50 * 1024 * 1024,
|
||||
});
|
||||
const r = await e.execute("2 + 2");
|
||||
e.destroy();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Wrap the engine in a pool to amortise startup:
|
||||
|
||||
```ts
|
||||
import { SandboxPool } from "@suaveplan/sandbox/pool";
|
||||
import { WorkerEngine } from "@suaveplan/sandbox/worker-engine";
|
||||
|
||||
const pool = new SandboxPool({
|
||||
minSize: 2,
|
||||
maxSize: 8,
|
||||
engineConfig: { timeout: 5000, maxOutputSize: 1024, memoryLimit: 5e7 },
|
||||
factory: (cfg) => new WorkerEngine(cfg),
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `new WorkerEngine(config: SandboxEngineConfig)`.
|
||||
- `execute(code, context?, options?): Promise<SandboxResult>`.
|
||||
- `registerModule(mod): void`.
|
||||
- `unregisterModule(name): boolean`.
|
||||
- `reset(): void`.
|
||||
- `destroy(): void`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The inline worker source is a single string literal. The worker uses a
|
||||
runtime `Reflect.construct(Function, [...keys, source])` to wrap the user
|
||||
code in an isolated function scope — this is acceptable inside the worker
|
||||
because the worker IS the security boundary. Outside the worker, in the host,
|
||||
no dynamic code construction happens. The post-message channel uses
|
||||
structured clone, which copies primitives + plain objects + typed arrays but
|
||||
not functions or symbols — module exports must therefore be data, not host
|
||||
references; for host bridges use `spawn-engine` plus an IPC channel.
|
||||
Reference in New Issue
Block a user