@doable/dovault¶
Zero-overhead runtime jail for Node.js processes. Three independent layers you can compose:
- Config Guard — lock server-side config files (Vite, Tailwind, Postgres, …) to safe templates.
- Process Jail — Node.js Permission Model wrapper.
- Resource Limiter — OS-level limits via systemd cgroups (Linux) or Win32 Job Objects (Windows).
Lives at packages/dovault/. Zero runtime dependencies.
Quick start¶
import { createVault } from "@doable/dovault";
const vault = createVault({
resourceLimits: { memoryMax: "150M", cpuQuota: "30%", tasksMax: 32 },
});
const proc = await vault.spawn("vite", ["--port", "3100"], {
cwd: projectPath,
jail: projectPath, // file system root the process can read/write
});
proc.on("exit", (code) => console.log("vite exited", code));
The spawned process can only:
- Read & write inside
projectPath. - Use up to 150 MB RAM, 30% of one CPU, 32 tasks.
API¶
createVault(options)¶
Returns a Vault instance.
| Option | Notes |
|---|---|
resourceLimits |
{ memoryMax, cpuQuota, tasksMax } — strings as accepted by systemd, or numbers (bytes/percent) |
backend |
'auto' \| 'systemd' \| 'windows' \| 'win-heap' \| 'direct' |
Vault#spawn(command, args, opts)¶
SpawnOptions:
| Field | Notes |
|---|---|
cwd |
Working directory |
jail |
Filesystem root the process is confined to |
env |
Environment variables |
extraReadPaths |
Extra paths the process may read (e.g. node_modules) |
extraWritePaths |
Extra writable paths |
permissions |
{ allowFs?: boolean, allowNet?: boolean, allowChildProcess?: boolean, allowWorker?: boolean } |
Vault#exec(command, args, opts)¶
Same as spawn but waits for completion and returns { stdout, stderr, exitCode }.
Standalone primitives¶
If you want one layer without the others:
import { ConfigGuard, ProcessJail, ResourceLimiter } from "@doable/dovault";
ConfigGuard.lock("/path/to/vite.config.ts", "vite-default");
const wrap = ProcessJail.wrap("node", ["server.js"], { jail: projectPath });
const wrap2 = ResourceLimiter.wrap(wrap.command, wrap.args, { memoryMax: "200M" });
Backends¶
| Backend | Where | What it does |
|---|---|---|
SystemdBackend |
Linux with systemd | systemd-run --user --scope --property=MemoryMax=… etc. |
WindowsBackend |
Windows | Win32 Job Objects (kill-on-close, memory limit) |
WindowsHeapBackend |
Windows fallback | V8 heap cap via --max-old-space-size |
DirectBackend |
Anywhere | No isolation — for dev/macOS |
createVault({ backend: "auto" }) picks the strongest available.
Why the layers are independent¶
- A static-site builder may need Config Guard but no resource limits.
- A short-lived script may need Process Jail but no Config Guard.
- A long-running daemon needs all three.
Composing them only when needed keeps overhead at literal zero in the cases that don't.
Used in Doable¶
The API spawns the per-project Vite dev server through vault.spawn(). See services/api/src/projects/dev-server.ts.
The AI agent's worker processes (started by @doable/docore) also go through dovault when the configured isolation backend is "direct" — adding a permission model layer even without nsjail.