@namzu/sandbox 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +234 -0
- package/README.md +205 -105
- package/dist/backends/aci-standby-pool/__tests__/unenforceable-controls.test.d.ts +2 -0
- package/dist/backends/aci-standby-pool/__tests__/unenforceable-controls.test.d.ts.map +1 -0
- package/dist/backends/aci-standby-pool/__tests__/unenforceable-controls.test.js +61 -0
- package/dist/backends/aci-standby-pool/__tests__/unenforceable-controls.test.js.map +1 -0
- package/dist/backends/aci-standby-pool/index.d.ts +2 -1
- package/dist/backends/aci-standby-pool/index.d.ts.map +1 -1
- package/dist/backends/aci-standby-pool/index.js +36 -1
- package/dist/backends/aci-standby-pool/index.js.map +1 -1
- package/dist/backends/docker/__tests__/hardening.test.d.ts +2 -0
- package/dist/backends/docker/__tests__/hardening.test.d.ts.map +1 -0
- package/dist/backends/docker/__tests__/hardening.test.js +32 -0
- package/dist/backends/docker/__tests__/hardening.test.js.map +1 -0
- package/dist/backends/docker/__tests__/leaf-permissions.smoke.test.d.ts +1 -1
- package/dist/backends/docker/__tests__/leaf-permissions.smoke.test.js +1 -1
- package/dist/backends/docker/index.d.ts +47 -3
- package/dist/backends/docker/index.d.ts.map +1 -1
- package/dist/backends/docker/index.js +138 -5
- package/dist/backends/docker/index.js.map +1 -1
- package/dist/backends/firecracker/__tests__/agent-timeout-clamp.test.d.ts +16 -0
- package/dist/backends/firecracker/__tests__/agent-timeout-clamp.test.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/agent-timeout-clamp.test.js +37 -0
- package/dist/backends/firecracker/__tests__/agent-timeout-clamp.test.js.map +1 -0
- package/dist/backends/firecracker/__tests__/backend.test.js +11 -3
- package/dist/backends/firecracker/__tests__/backend.test.js.map +1 -1
- package/dist/backends/firecracker/__tests__/control-plane-mtls.test.js +10 -2
- package/dist/backends/firecracker/__tests__/control-plane-mtls.test.js.map +1 -1
- package/dist/backends/firecracker/__tests__/egress-policy.test.d.ts +2 -0
- package/dist/backends/firecracker/__tests__/egress-policy.test.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/egress-policy.test.js +67 -0
- package/dist/backends/firecracker/__tests__/egress-policy.test.js.map +1 -0
- package/dist/backends/firecracker/__tests__/fixtures/ipc-path.d.ts +21 -0
- package/dist/backends/firecracker/__tests__/fixtures/ipc-path.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/fixtures/ipc-path.js +30 -0
- package/dist/backends/firecracker/__tests__/fixtures/ipc-path.js.map +1 -0
- package/dist/backends/firecracker/__tests__/protocol.test.js +7 -17
- package/dist/backends/firecracker/__tests__/protocol.test.js.map +1 -1
- package/dist/backends/firecracker/__tests__/transport.test.js +11 -3
- package/dist/backends/firecracker/__tests__/transport.test.js.map +1 -1
- package/dist/backends/firecracker/index.d.ts +20 -1
- package/dist/backends/firecracker/index.d.ts.map +1 -1
- package/dist/backends/firecracker/index.js +60 -15
- package/dist/backends/firecracker/index.js.map +1 -1
- package/dist/egress/__tests__/allowlist.test.d.ts +2 -0
- package/dist/egress/__tests__/allowlist.test.d.ts.map +1 -0
- package/dist/egress/__tests__/allowlist.test.js +85 -0
- package/dist/egress/__tests__/allowlist.test.js.map +1 -0
- package/dist/egress/__tests__/proxy.test.d.ts +2 -0
- package/dist/egress/__tests__/proxy.test.d.ts.map +1 -0
- package/dist/egress/__tests__/proxy.test.js +177 -0
- package/dist/egress/__tests__/proxy.test.js.map +1 -0
- package/dist/egress/allowlist.d.ts +40 -0
- package/dist/egress/allowlist.d.ts.map +1 -0
- package/dist/egress/allowlist.js +81 -0
- package/dist/egress/allowlist.js.map +1 -0
- package/dist/egress/index.d.ts +4 -0
- package/dist/egress/index.d.ts.map +1 -0
- package/dist/egress/index.js +3 -0
- package/dist/egress/index.js.map +1 -0
- package/dist/egress/proxy.d.ts +90 -0
- package/dist/egress/proxy.d.ts.map +1 -0
- package/dist/egress/proxy.js +194 -0
- package/dist/egress/proxy.js.map +1 -0
- package/dist/index.d.ts +101 -190
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -80
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +18 -39
- package/dist/index.test.js.map +1 -1
- package/package.json +5 -4
- package/src/backends/aci-standby-pool/__tests__/unenforceable-controls.test.ts +69 -0
- package/src/backends/aci-standby-pool/index.ts +42 -1
- package/src/backends/docker/__tests__/hardening.test.ts +43 -0
- package/src/backends/docker/__tests__/leaf-permissions.smoke.test.ts +1 -1
- package/src/backends/docker/index.ts +204 -12
- package/src/backends/firecracker/__tests__/agent-timeout-clamp.test.ts +48 -0
- package/src/backends/firecracker/__tests__/backend.test.ts +76 -65
- package/src/backends/firecracker/__tests__/control-plane-mtls.test.ts +10 -2
- package/src/backends/firecracker/__tests__/egress-policy.test.ts +91 -0
- package/src/backends/firecracker/__tests__/fixtures/ipc-path.ts +31 -0
- package/src/backends/firecracker/__tests__/protocol.test.ts +8 -23
- package/src/backends/firecracker/__tests__/transport.test.ts +11 -3
- package/src/backends/firecracker/index.ts +66 -13
- package/src/egress/__tests__/allowlist.test.ts +103 -0
- package/src/egress/__tests__/proxy.test.ts +212 -0
- package/src/egress/allowlist.ts +82 -0
- package/src/egress/index.ts +7 -0
- package/src/egress/proxy.ts +294 -0
- package/src/index.test.ts +19 -41
- package/src/index.ts +170 -259
package/src/index.ts
CHANGED
|
@@ -1,69 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @namzu/sandbox — pluggable
|
|
3
|
-
*
|
|
4
|
-
* The SDK
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* commodity Linux without nested virt. Locally via Linux Docker
|
|
36
|
-
* runtime; not available on macOS Docker Desktop.
|
|
37
|
-
*
|
|
38
|
-
* • `passthrough` — no isolation, runs commands directly. For
|
|
39
|
-
* tests and trusted environments only. Off by default; opt-in.
|
|
40
|
-
*
|
|
41
|
-
* **What we deliberately do NOT build** is yet-another Firecracker
|
|
42
|
-
* scheduler — that is E2B's and Fly's entire product, and writing
|
|
43
|
-
* our own is a years-long detour. We adapt to theirs.
|
|
44
|
-
*
|
|
45
|
-
* **Cloud portability:** the backend interface is cloud-agnostic.
|
|
46
|
-
* `docker` works on every cloud; `e2b` and `fly-machines` are
|
|
47
|
-
* managed services not tied to any cloud; `firecracker` and
|
|
48
|
-
* `gvisor` need infrastructure the host chooses (GKE Sandbox, AWS
|
|
49
|
-
* Fargate, self-hosted KVM, etc.). Picking a stronger backend may
|
|
50
|
-
* imply moving cloud — that's the host's call, not the SDK's.
|
|
51
|
-
*
|
|
52
|
-
* **Local dev story:** Phase 1 (`docker`) runs everywhere. Phase 2
|
|
53
|
-
* (`e2b` / `fly-machines`) hits the managed service from a dev
|
|
54
|
-
* laptop with no infra setup. Phase 3 (`firecracker` / `gvisor`)
|
|
55
|
-
* needs Lima/Colima on macOS or native KVM on Linux — only the
|
|
56
|
-
* adversarial-multi-tenant prod path needs that and it's clearly
|
|
57
|
-
* documented as such.
|
|
58
|
-
*
|
|
59
|
-
* This file is the public surface. Concrete backend implementations
|
|
60
|
-
* land under `./backends/<kind>/` in subsequent commits — the
|
|
61
|
-
* `SandboxBackend` interface here is the contract they implement.
|
|
62
|
-
*
|
|
63
|
-
* Refs: `e2b.dev/docs/sandbox`, `fly.io/docs/machines`,
|
|
64
|
-
* `firecracker-microvm.github.io`, `gvisor.dev/docs`,
|
|
65
|
-
* `cloud.google.com/kubernetes-engine/docs/concepts/sandbox-pods`,
|
|
66
|
-
* `aws.amazon.com/blogs/aws/firecracker-lightweight-virtualization-for-serverless-computing`.
|
|
2
|
+
* @namzu/sandbox — pluggable containment for @namzu/sdk.
|
|
3
|
+
*
|
|
4
|
+
* The SDK declares the `SandboxProvider` shape
|
|
5
|
+
* (`packages/sdk/src/types/sandbox/index.ts`); this package implements
|
|
6
|
+
* it with concrete BACKENDS chosen at construction time. A backend is
|
|
7
|
+
* named for the mechanism it drives, because that is what it has to
|
|
8
|
+
* speak on the wire — never for a system whose ideas it borrowed.
|
|
9
|
+
*
|
|
10
|
+
* Two tiers, each a trust boundary:
|
|
11
|
+
*
|
|
12
|
+
* • `container` — one OCI container per task, seccomp on, tmpfs
|
|
13
|
+
* workdir, no network unless asked. The same path on a laptop and
|
|
14
|
+
* on a Linux replica anywhere. The tier for trusted prompts and
|
|
15
|
+
* contained workloads. Boundary: kernel namespaces, or a
|
|
16
|
+
* userspace-kernel runtime where one is installed.
|
|
17
|
+
*
|
|
18
|
+
* • `microvm` — one hardware-virtualized guest per task. The boundary
|
|
19
|
+
* to reach for when the prompt itself is adversarial, at the cost
|
|
20
|
+
* of running or renting the machinery that starts them.
|
|
21
|
+
*
|
|
22
|
+
* Every shape in {@link SandboxBackendConfig} has a backend behind it,
|
|
23
|
+
* which used not to be true: a `process` tier, a `passthrough` tier and
|
|
24
|
+
* two adapters to third-party schedulers were declared here and never
|
|
25
|
+
* written, so four of the shapes this package offered could only ever
|
|
26
|
+
* type-check and then throw. They are gone rather than pending.
|
|
27
|
+
* Confining an agent to the operator's own host is the SDK's local
|
|
28
|
+
* sandbox provider, which is implemented; a host that wants no
|
|
29
|
+
* confinement configures no sandbox.
|
|
30
|
+
*
|
|
31
|
+
* namzu does not build its own microVM scheduler. That is a years-long
|
|
32
|
+
* detour from an agent kernel, and the boundary a guest gives is the
|
|
33
|
+
* same whoever started it — so the microvm tier is an interface to a
|
|
34
|
+
* scheduler, not a scheduler.
|
|
67
35
|
*/
|
|
68
36
|
|
|
69
37
|
import type {
|
|
@@ -120,35 +88,27 @@ export {
|
|
|
120
88
|
// ---------------------------------------------------------------------------
|
|
121
89
|
|
|
122
90
|
/**
|
|
123
|
-
* Top-level sandbox tier
|
|
124
|
-
*
|
|
125
|
-
* - `
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* per task. Hardware-virtualization trust boundary; the
|
|
139
|
-
* industry standard for adversarial multi-tenancy
|
|
140
|
-
* (AWS Lambda/Fargate, Fly Machines, Replit, E2B, Daytona
|
|
141
|
-
* all converged here). Sub-second cold-start via
|
|
142
|
-
* snapshot/restore.
|
|
143
|
-
*
|
|
144
|
-
* - `passthrough` — no isolation; for tests and explicitly
|
|
145
|
-
* trusted environments.
|
|
91
|
+
* Top-level sandbox tier, and the trust boundary it buys:
|
|
92
|
+
*
|
|
93
|
+
* - `container` — one OCI container per task. Namespaces. The
|
|
94
|
+
* default for trusted prompts and contained workloads, and the
|
|
95
|
+
* same code path on a laptop and on a Linux replica anywhere.
|
|
96
|
+
*
|
|
97
|
+
* - `microvm` — one hardware-virtualized guest per task. The
|
|
98
|
+
* boundary to reach for when the prompt itself is adversarial.
|
|
99
|
+
*
|
|
100
|
+
* Two tiers, not four. A `process` tier and a `passthrough` tier were
|
|
101
|
+
* declared here and never built: every construction threw, so the
|
|
102
|
+
* only thing they offered a caller was a shape that compiles and an
|
|
103
|
+
* exception at runtime. Confining the agent to the operator's own
|
|
104
|
+
* host is the SDK's local sandbox provider, which is implemented; a
|
|
105
|
+
* host that wants no confinement configures no sandbox.
|
|
146
106
|
*
|
|
147
107
|
* The concrete implementation inside a tier is picked via the
|
|
148
|
-
* tier-specific config (see {@link
|
|
149
|
-
* {@link
|
|
108
|
+
* tier-specific config (see {@link ContainerBackendConfig},
|
|
109
|
+
* {@link MicroVMBackendConfig}).
|
|
150
110
|
*/
|
|
151
|
-
export type SandboxTier = '
|
|
111
|
+
export type SandboxTier = 'container' | 'microvm'
|
|
152
112
|
|
|
153
113
|
/**
|
|
154
114
|
* Discriminated union of sandbox backend configurations. Each
|
|
@@ -156,19 +116,17 @@ export type SandboxTier = 'process' | 'container' | 'microvm' | 'passthrough'
|
|
|
156
116
|
* shape automatically via TS narrowing.
|
|
157
117
|
*/
|
|
158
118
|
export type SandboxBackendConfig =
|
|
159
|
-
| ProcessBackendConfig
|
|
160
119
|
| ContainerBackendConfig
|
|
161
120
|
| ACIStandbyPoolBackendConfig
|
|
162
121
|
| MicroVMBackendConfig
|
|
163
|
-
| PassthroughBackendConfig
|
|
164
122
|
|
|
165
123
|
/**
|
|
166
124
|
* Azure Container Instances Standby Pool backend. Container tier,
|
|
167
125
|
* managed-microvm-ish: every claim is a fresh ACI container group
|
|
168
126
|
* pre-warmed in an Azure-managed standby pool (`Microsoft.StandbyPool`).
|
|
169
127
|
* ~1.5 s claim latency vs ~10-30 s for cold ACI spawn. Trust boundary
|
|
170
|
-
* =
|
|
171
|
-
* SKU
|
|
128
|
+
* = the provider's isolation host, whose strength varies by SKU; the
|
|
129
|
+
* Confidential SKU adds an AMD SEV-SNP trusted execution environment.
|
|
172
130
|
*
|
|
173
131
|
* No host filesystem — workspace mounts ride `azureFileShare` sources
|
|
174
132
|
* (the host provisions a per-task Azure Files share upstream and
|
|
@@ -211,34 +169,17 @@ export interface ACIStandbyPoolBackendConfig {
|
|
|
211
169
|
readonly containerNamePrefix?: string
|
|
212
170
|
}
|
|
213
171
|
|
|
214
|
-
/**
|
|
215
|
-
* `process` tier. Auto-detects the platform's native primitive
|
|
216
|
-
* unless overridden:
|
|
217
|
-
*
|
|
218
|
-
* - `bubblewrap` on Linux / WSL2 (`bwrap`)
|
|
219
|
-
* - `seatbelt` on macOS (`sandbox-exec`)
|
|
220
|
-
*
|
|
221
|
-
* Both are what `@anthropic-ai/sandbox-runtime` ships. Cold-start
|
|
222
|
-
* is process spawn (~ms). Use this when the agent runs on the
|
|
223
|
-
* end-user's developer machine — Claude Code's deployment model.
|
|
224
|
-
*/
|
|
225
|
-
export interface ProcessBackendConfig {
|
|
226
|
-
readonly tier: 'process'
|
|
227
|
-
readonly engine?: 'auto' | 'bubblewrap' | 'seatbelt'
|
|
228
|
-
}
|
|
229
|
-
|
|
230
172
|
/**
|
|
231
173
|
* `container` tier. Two runtime options:
|
|
232
174
|
*
|
|
233
175
|
* - `docker` (default) — plain OCI container on the host's
|
|
234
176
|
* Docker daemon. No special runtime required.
|
|
235
|
-
* - `runsc` —
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* `gvisor.dev/docs/user_guide/quick_start/docker`.
|
|
177
|
+
* - `runsc` — a userspace-kernel runtime: the guest's syscalls
|
|
178
|
+
* are served by a user-space implementation rather than the
|
|
179
|
+
* host kernel, which is a stronger boundary than namespaces and
|
|
180
|
+
* runs on commodity Linux without nested virtualization.
|
|
181
|
+
* Requires the runtime installed on the container daemon (Linux
|
|
182
|
+
* only).
|
|
242
183
|
*
|
|
243
184
|
* `image` is the container image to spawn per task. The package
|
|
244
185
|
* ships a reference Dockerfile (compass-platform pattern) with
|
|
@@ -286,119 +227,91 @@ export interface ContainerBackendConfig {
|
|
|
286
227
|
}
|
|
287
228
|
|
|
288
229
|
/**
|
|
289
|
-
* `microvm` tier
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* calls" rather than "Python REPL".
|
|
301
|
-
* - `self-hosted` — direct `firecracker-containerd` against a
|
|
302
|
-
* KVM-enabled host. For deployments where E2B and Fly are
|
|
303
|
-
* both off the table for policy reasons. Operationally the
|
|
304
|
-
* heaviest path; everything below ships first.
|
|
305
|
-
*
|
|
306
|
-
* Local dev: `e2b` and `fly-machines` work from any laptop with
|
|
307
|
-
* an API key (no infra setup). `self-hosted` requires Linux + KVM
|
|
308
|
-
* (Lima/Colima on macOS).
|
|
230
|
+
* `microvm` tier, against namzu's own guest orchestrator.
|
|
231
|
+
*
|
|
232
|
+
* Two adapters to third-party managed schedulers were declared here
|
|
233
|
+
* and never written: both threw on construction, and each demanded
|
|
234
|
+
* required credentials for a call that was never made. A config
|
|
235
|
+
* shape whose only reachable outcome is an exception is worse than
|
|
236
|
+
* no shape, because it type-checks.
|
|
237
|
+
*
|
|
238
|
+
* What remains is the orchestrator namzu runs: the control plane
|
|
239
|
+
* mints a guest per task and resumes it copy-on-write from a golden
|
|
240
|
+
* snapshot, so a cold start is a resume rather than a boot.
|
|
309
241
|
*/
|
|
310
|
-
export type MicroVMBackendConfig =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
* CONTROL-plane mTLS client material. When present, the orchestrator
|
|
384
|
-
* control-plane calls (create/destroy POSTs to `orchestratorEndpoint`)
|
|
385
|
-
* dial over mTLS — presenting this client cert and pinning this CA —
|
|
386
|
-
* instead of plain `fetch`. Secures the control plane when
|
|
387
|
-
* `orchestratorEndpoint` is an `https://` URL reached over the PUBLIC
|
|
388
|
-
* internet (the non-VNet-integrated caller→FC-host hop), where the
|
|
389
|
-
* shared-secret bearer alone would be exposed. The bearer is STILL sent
|
|
390
|
-
* (defense in depth). Same `{ca,cert,key,servername}` shape + the same
|
|
391
|
-
* consumer-injected dependency boundary as `mtls` (the one fleet CA
|
|
392
|
-
* secures both planes). Absent → plain `fetch` control plane (the
|
|
393
|
-
* single-host VSOCK default, unchanged).
|
|
394
|
-
*/
|
|
395
|
-
readonly controlPlaneMtls?: {
|
|
396
|
-
readonly ca: string | Buffer
|
|
397
|
-
readonly cert: string | Buffer
|
|
398
|
-
readonly key: string | Buffer
|
|
399
|
-
readonly servername?: string
|
|
400
|
-
}
|
|
401
|
-
}
|
|
242
|
+
export type MicroVMBackendConfig = {
|
|
243
|
+
readonly tier: 'microvm'
|
|
244
|
+
readonly service: 'self-hosted'
|
|
245
|
+
/**
|
|
246
|
+
* Control-plane base URL, and the bearer minted for it.
|
|
247
|
+
*
|
|
248
|
+
* Both are REQUIRED, which is a correction: they were optional
|
|
249
|
+
* beside three required fields (`firecrackerBinary`,
|
|
250
|
+
* `kernelImage`, `rootfsImage`) belonging to a local-daemon shape
|
|
251
|
+
* that was never implemented. So the only working configuration
|
|
252
|
+
* had to supply three values nothing reads, and omitting these
|
|
253
|
+
* two type-checked its way to a runtime throw.
|
|
254
|
+
*
|
|
255
|
+
* `getToken` is a closure rather than a credential, so this
|
|
256
|
+
* package carries no cloud SDK: the host runtime owns how the
|
|
257
|
+
* bearer is obtained.
|
|
258
|
+
*/
|
|
259
|
+
readonly orchestratorEndpoint: string
|
|
260
|
+
readonly getToken: () => Promise<string>
|
|
261
|
+
/** Golden snapshot revision to resume copy-on-write. */
|
|
262
|
+
readonly template?: string
|
|
263
|
+
/**
|
|
264
|
+
* Resume this per-agent captured snapshot (layered on its base
|
|
265
|
+
* golden) INSTEAD of a fresh golden boot. Tier-agnostic, additive,
|
|
266
|
+
* optional: the backend that supports it (the owned firecracker
|
|
267
|
+
* backend) honors it; others ignore it. Absent ⇒ the create body is
|
|
268
|
+
* byte-identical and the generic golden-resume hot path is unchanged
|
|
269
|
+
* (the field is only ever set by the host's per-agent trigger path).
|
|
270
|
+
* Sibling to `template` (base-golden selector) — see
|
|
271
|
+
* {@link AgentSnapshotRef}.
|
|
272
|
+
*/
|
|
273
|
+
readonly agentSnapshot?: AgentSnapshotRef
|
|
274
|
+
/** Fixed guest AF_VSOCK port the in-VM agent listens on. */
|
|
275
|
+
readonly agentVsockPort?: number
|
|
276
|
+
readonly readyTimeoutMs?: number
|
|
277
|
+
readonly readyPollIntervalMs?: number
|
|
278
|
+
/**
|
|
279
|
+
* NETWORK-mode mTLS client material (ses_051 P4 client-proxy
|
|
280
|
+
* bridge). When present, the orchestrator returns an `mtls` agent
|
|
281
|
+
* handle (host/port/sandboxId, NO cert material) and this CA/cert/key
|
|
282
|
+
* is MERGED onto that handle before the transport dials the per-host
|
|
283
|
+
* relay over mTLS. Injected by the consumer's runtime (the Vandal
|
|
284
|
+
* host layer reads it from `VANDAL_SANDBOX_FC_TLS_*`), NEVER fetched
|
|
285
|
+
* inside this package — same dependency boundary as `getToken`, so
|
|
286
|
+
* `@namzu/sandbox` stays Azure-SDK-free. Absent for the single-host
|
|
287
|
+
* VSOCK default (the live proofs).
|
|
288
|
+
*/
|
|
289
|
+
readonly mtls?: {
|
|
290
|
+
readonly ca: string | Buffer
|
|
291
|
+
readonly cert: string | Buffer
|
|
292
|
+
readonly key: string | Buffer
|
|
293
|
+
readonly servername?: string
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* CONTROL-plane mTLS client material. When present, the orchestrator
|
|
297
|
+
* control-plane calls (create/destroy POSTs to `orchestratorEndpoint`)
|
|
298
|
+
* dial over mTLS — presenting this client cert and pinning this CA —
|
|
299
|
+
* instead of plain `fetch`. Secures the control plane when
|
|
300
|
+
* `orchestratorEndpoint` is an `https://` URL reached over the PUBLIC
|
|
301
|
+
* internet (the non-VNet-integrated caller→FC-host hop), where the
|
|
302
|
+
* shared-secret bearer alone would be exposed. The bearer is STILL sent
|
|
303
|
+
* (defense in depth). Same `{ca,cert,key,servername}` shape + the same
|
|
304
|
+
* consumer-injected dependency boundary as `mtls` (the one fleet CA
|
|
305
|
+
* secures both planes). Absent → plain `fetch` control plane (the
|
|
306
|
+
* single-host VSOCK default, unchanged).
|
|
307
|
+
*/
|
|
308
|
+
readonly controlPlaneMtls?: {
|
|
309
|
+
readonly ca: string | Buffer
|
|
310
|
+
readonly cert: string | Buffer
|
|
311
|
+
readonly key: string | Buffer
|
|
312
|
+
readonly servername?: string
|
|
313
|
+
}
|
|
314
|
+
}
|
|
402
315
|
|
|
403
316
|
/**
|
|
404
317
|
* A reference to a per-agent captured snapshot, layered on top of a base
|
|
@@ -421,14 +334,6 @@ export interface AgentSnapshotRef {
|
|
|
421
334
|
readonly version: string
|
|
422
335
|
}
|
|
423
336
|
|
|
424
|
-
/**
|
|
425
|
-
* `passthrough` tier. No isolation — runs commands directly in
|
|
426
|
-
* the host process. Tests and trusted environments only.
|
|
427
|
-
*/
|
|
428
|
-
export interface PassthroughBackendConfig {
|
|
429
|
-
readonly tier: 'passthrough'
|
|
430
|
-
}
|
|
431
|
-
|
|
432
337
|
/**
|
|
433
338
|
* Egress allowlist resolution. Host-supplied policy decides whether
|
|
434
339
|
* an outbound request is allowed before the proxy opens a socket.
|
|
@@ -449,6 +354,17 @@ export interface PassthroughBackendConfig {
|
|
|
449
354
|
* problem — the host owns the closure, the SDK runtime
|
|
450
355
|
* doesn't have to forward identity through `provider.create`.
|
|
451
356
|
*/
|
|
357
|
+
export {
|
|
358
|
+
EgressProxy,
|
|
359
|
+
isHostAllowed,
|
|
360
|
+
splitAuthority,
|
|
361
|
+
} from './egress/index.js'
|
|
362
|
+
export type {
|
|
363
|
+
BrokeredCredential,
|
|
364
|
+
EgressProxyOptions,
|
|
365
|
+
RunningEgressProxy,
|
|
366
|
+
} from './egress/index.js'
|
|
367
|
+
|
|
452
368
|
export type EgressPolicy =
|
|
453
369
|
| { readonly kind: 'deny-all' }
|
|
454
370
|
| { readonly kind: 'allow-all' }
|
|
@@ -547,7 +463,7 @@ export type SandboxProviderConfig =
|
|
|
547
463
|
readonly layout: ContainerSandboxLayout
|
|
548
464
|
})
|
|
549
465
|
| (SandboxProviderConfigBase & {
|
|
550
|
-
readonly backend:
|
|
466
|
+
readonly backend: MicroVMBackendConfig
|
|
551
467
|
})
|
|
552
468
|
|
|
553
469
|
interface SandboxProviderConfigBase {
|
|
@@ -564,25 +480,22 @@ interface SandboxProviderConfigBase {
|
|
|
564
480
|
* the chosen backend.
|
|
565
481
|
*
|
|
566
482
|
* Backends are loaded lazily — the package only imports the
|
|
567
|
-
* platform-specific modules (
|
|
568
|
-
* Docker SDK, the
|
|
483
|
+
* platform-specific modules (the host sandbox runtime, the
|
|
484
|
+
* Docker SDK, the microVM SDK, …) when the corresponding backend is
|
|
569
485
|
* requested. That keeps `@namzu/sandbox` reasonable to install in
|
|
570
486
|
* environments where one backend is genuinely impossible.
|
|
571
487
|
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
* Calling this function now throws
|
|
584
|
-
* {@link SandboxBackendNotImplementedError} so consumers get a
|
|
585
|
-
* clear signal during the staged rollout.
|
|
488
|
+
* Every shape in {@link SandboxBackendConfig} has a backend behind
|
|
489
|
+
* it. That is a recent property: this file used to declare a staged
|
|
490
|
+
* roadmap of tiers and adapters, most of which threw, so the surface
|
|
491
|
+
* described a plan and the runtime described the truth. The shapes
|
|
492
|
+
* that were never built are gone rather than pending — a config that
|
|
493
|
+
* type-checks and can only throw teaches a caller the wrong thing
|
|
494
|
+
* about what this package does.
|
|
495
|
+
*
|
|
496
|
+
* {@link SandboxBackendNotImplementedError} survives for the untyped
|
|
497
|
+
* caller: a JS host that invents a tier gets a named refusal instead
|
|
498
|
+
* of a provider that confines nothing.
|
|
586
499
|
*/
|
|
587
500
|
export function createSandboxProvider(config: SandboxProviderConfig): SandboxProvider {
|
|
588
501
|
const backend = pickBackend(config)
|
|
@@ -723,13 +636,11 @@ function pickBackend(config: SandboxProviderConfig): SandboxBackend {
|
|
|
723
636
|
/**
|
|
724
637
|
* Human-readable backend label for error messages. Returns the
|
|
725
638
|
* tier plus the concrete service / runtime when present, e.g.
|
|
726
|
-
* `'microvm:
|
|
639
|
+
* `'microvm:self-hosted'` or `'container:runsc'`.
|
|
727
640
|
*/
|
|
728
641
|
function describeBackend(config: SandboxBackendConfig): string {
|
|
729
642
|
if (config.tier === 'microvm') return `microvm:${config.service}`
|
|
730
|
-
|
|
731
|
-
if (config.tier === 'process') return `process:${config.engine ?? 'auto'}`
|
|
732
|
-
return config.tier
|
|
643
|
+
return `container:${config.runtime ?? 'docker'}`
|
|
733
644
|
}
|
|
734
645
|
|
|
735
646
|
// ---------------------------------------------------------------------------
|