@namzu/sandbox 1.1.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 +474 -0
- package/LICENSE.md +110 -0
- package/README.md +148 -0
- package/dist/backends/aci-standby-pool/index.d.ts +104 -0
- package/dist/backends/aci-standby-pool/index.d.ts.map +1 -0
- package/dist/backends/aci-standby-pool/index.js +425 -0
- package/dist/backends/aci-standby-pool/index.js.map +1 -0
- package/dist/backends/docker/__tests__/leaf-permissions.smoke.test.d.ts +40 -0
- package/dist/backends/docker/__tests__/leaf-permissions.smoke.test.d.ts.map +1 -0
- package/dist/backends/docker/__tests__/leaf-permissions.smoke.test.js +157 -0
- package/dist/backends/docker/__tests__/leaf-permissions.smoke.test.js.map +1 -0
- package/dist/backends/docker/index.d.ts +118 -0
- package/dist/backends/docker/index.d.ts.map +1 -0
- package/dist/backends/docker/index.js +645 -0
- package/dist/backends/docker/index.js.map +1 -0
- package/dist/backends/firecracker/__tests__/backend.test.d.ts +13 -0
- package/dist/backends/firecracker/__tests__/backend.test.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/backend.test.js +353 -0
- package/dist/backends/firecracker/__tests__/backend.test.js.map +1 -0
- package/dist/backends/firecracker/__tests__/control-plane-mtls.test.d.ts +19 -0
- package/dist/backends/firecracker/__tests__/control-plane-mtls.test.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/control-plane-mtls.test.js +201 -0
- package/dist/backends/firecracker/__tests__/control-plane-mtls.test.js.map +1 -0
- package/dist/backends/firecracker/__tests__/fixtures/mtls-pki.d.ts +39 -0
- package/dist/backends/firecracker/__tests__/fixtures/mtls-pki.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/fixtures/mtls-pki.js +149 -0
- package/dist/backends/firecracker/__tests__/fixtures/mtls-pki.js.map +1 -0
- package/dist/backends/firecracker/__tests__/protocol.test.d.ts +6 -0
- package/dist/backends/firecracker/__tests__/protocol.test.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/protocol.test.js +77 -0
- package/dist/backends/firecracker/__tests__/protocol.test.js.map +1 -0
- package/dist/backends/firecracker/__tests__/transport.test.d.ts +20 -0
- package/dist/backends/firecracker/__tests__/transport.test.d.ts.map +1 -0
- package/dist/backends/firecracker/__tests__/transport.test.js +449 -0
- package/dist/backends/firecracker/__tests__/transport.test.js.map +1 -0
- package/dist/backends/firecracker/index.d.ts +124 -0
- package/dist/backends/firecracker/index.d.ts.map +1 -0
- package/dist/backends/firecracker/index.js +334 -0
- package/dist/backends/firecracker/index.js.map +1 -0
- package/dist/backends/firecracker/protocol.d.ts +132 -0
- package/dist/backends/firecracker/protocol.d.ts.map +1 -0
- package/dist/backends/firecracker/protocol.js +112 -0
- package/dist/backends/firecracker/protocol.js.map +1 -0
- package/dist/backends/firecracker/transport.d.ts +251 -0
- package/dist/backends/firecracker/transport.d.ts.map +1 -0
- package/dist/backends/firecracker/transport.js +524 -0
- package/dist/backends/firecracker/transport.js.map +1 -0
- package/dist/index.d.ts +611 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +376 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +28 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +670 -0
- package/dist/index.test.js.map +1 -0
- package/package.json +54 -0
- package/src/backends/aci-standby-pool/index.ts +602 -0
- package/src/backends/docker/__tests__/leaf-permissions.smoke.test.ts +169 -0
- package/src/backends/docker/index.ts +826 -0
- package/src/backends/firecracker/__tests__/backend.test.ts +418 -0
- package/src/backends/firecracker/__tests__/control-plane-mtls.test.ts +253 -0
- package/src/backends/firecracker/__tests__/fixtures/mtls-pki.ts +166 -0
- package/src/backends/firecracker/__tests__/protocol.test.ts +90 -0
- package/src/backends/firecracker/__tests__/transport.test.ts +526 -0
- package/src/backends/firecracker/index.ts +528 -0
- package/src/backends/firecracker/protocol.ts +191 -0
- package/src/backends/firecracker/transport.ts +667 -0
- package/src/index.test.ts +731 -0
- package/src/index.ts +930 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `microvm:self-hosted` (Firecracker) backend.
|
|
3
|
+
*
|
|
4
|
+
* Sibling of `docker/` and `aci-standby-pool/`: same
|
|
5
|
+
* {@link SandboxBackend} surface, same NDJSON exec-stream + base64
|
|
6
|
+
* file-IO **wire contract**, different transport and shipping
|
|
7
|
+
* mechanism.
|
|
8
|
+
*
|
|
9
|
+
* - docker `docker run`s a container and reaches an HTTP worker on a
|
|
10
|
+
* host loopback port.
|
|
11
|
+
* - aci PUTs an ACI container group and reaches the same HTTP worker
|
|
12
|
+
* over the group's IP.
|
|
13
|
+
* - firecracker POSTs the owned Azure **orchestrator** to CoW-resume a
|
|
14
|
+
* microVM off the golden snapshot, then reaches a **custom vsock
|
|
15
|
+
* agent** baked into the golden rootfs over the **vsock transport**
|
|
16
|
+
* (`transport.ts`). The wire FORMAT is identical (see `protocol.ts`);
|
|
17
|
+
* only the transport differs — HTTP for docker/aci, framed-over-vsock
|
|
18
|
+
* for FC, because across an FC snapshot resume a TCP control channel
|
|
19
|
+
* is dead-on-arrival while the vsock LISTEN socket survives
|
|
20
|
+
* (FC `snapshot-support.md`).
|
|
21
|
+
*
|
|
22
|
+
* ## Why this is a remote-copy backend (no host bind-mounts)
|
|
23
|
+
* A sibling process on the FC host cannot see the microVM's
|
|
24
|
+
* filesystem, so — exactly like ACI — the workspace is seeded by
|
|
25
|
+
* archive-sync over the control channel (tar.gz `writeFile` +
|
|
26
|
+
* in-sandbox `tar -xzf` via `exec`), driven by the Vandal-side
|
|
27
|
+
* lifecycle (`workspace-sync.ts`). This backend therefore exposes no
|
|
28
|
+
* `layout` mount rendering; it only needs the `outputs` container path
|
|
29
|
+
* as the workspace root, which the orchestrator returns as `rootDir`.
|
|
30
|
+
*
|
|
31
|
+
* ## Trust model
|
|
32
|
+
* The microVM is a hardware-virtualization (KVM) trust boundary —
|
|
33
|
+
* stronger than docker namespaces or ACI's managed isolation. The
|
|
34
|
+
* vsock control channel never traverses the guest's egress netns; the
|
|
35
|
+
* agent listens on a fixed AF_VSOCK port captured warm in the golden
|
|
36
|
+
* snapshot. Auth to the orchestrator rides a caller-supplied
|
|
37
|
+
* `getToken()` closure (the ACI `getArmToken` pattern) so
|
|
38
|
+
* `@namzu/sandbox` keeps zero Azure-SDK dependencies.
|
|
39
|
+
*
|
|
40
|
+
* ## Auth / SDK-dependency boundary
|
|
41
|
+
* `getToken()` mirrors ACI's `getArmToken`: the consumer's runtime
|
|
42
|
+
* owns Managed-Identity / federated-credential picking; this package
|
|
43
|
+
* only calls the closure on every orchestrator HTTP call so a
|
|
44
|
+
* long-running sandbox survives token rotation.
|
|
45
|
+
*/
|
|
46
|
+
import https from 'node:https';
|
|
47
|
+
import { VsockAgentTransport } from './transport.js';
|
|
48
|
+
const DEFAULT_AGENT_VSOCK_PORT = 1024;
|
|
49
|
+
const DEFAULT_READY_TIMEOUT_MS = 60_000;
|
|
50
|
+
const DEFAULT_READY_POLL_MS = 250;
|
|
51
|
+
/**
|
|
52
|
+
* Build a {@link SandboxBackend} backed by the owned Firecracker
|
|
53
|
+
* platform. Construction is synchronous; the orchestrator POST happens
|
|
54
|
+
* on the first `create()`.
|
|
55
|
+
*/
|
|
56
|
+
export function buildFirecrackerBackend(config) {
|
|
57
|
+
return {
|
|
58
|
+
tier: 'microvm',
|
|
59
|
+
name: 'firecracker',
|
|
60
|
+
async create(options) {
|
|
61
|
+
return await spawnFirecrackerSandbox(config, options);
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async function orchestratorCall(endpoint, pathSuffix, method, getToken, body, mtls) {
|
|
66
|
+
const token = await getToken();
|
|
67
|
+
const url = `${endpoint.replace(/\/+$/, '')}${pathSuffix}`;
|
|
68
|
+
const payload = body !== undefined ? JSON.stringify(body) : undefined;
|
|
69
|
+
const headers = {
|
|
70
|
+
Authorization: `Bearer ${token}`,
|
|
71
|
+
'content-type': 'application/json',
|
|
72
|
+
};
|
|
73
|
+
let res;
|
|
74
|
+
try {
|
|
75
|
+
// When CONTROL-plane mTLS material is injected the call dials over a
|
|
76
|
+
// node:https request presenting the client cert + pinning the server CA
|
|
77
|
+
// (the public-internet hop). Without it the EXISTING plain `fetch` path
|
|
78
|
+
// runs unchanged (the single-host default). node:https is used rather
|
|
79
|
+
// than fetch+undici-dispatcher because the package declares no undici
|
|
80
|
+
// dependency — node:https is always importable and needs nothing added.
|
|
81
|
+
res = mtls
|
|
82
|
+
? await httpsOrchestratorRequest(url, method, headers, payload, mtls)
|
|
83
|
+
: await fetchOrchestratorRequest(url, method, headers, payload);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const cause = err instanceof Error ? err.cause : undefined;
|
|
87
|
+
throw new Error(`firecracker orchestrator ${method} ${url} failed: ${err instanceof Error ? err.message : String(err)}${cause ? ` — cause: ${cause instanceof Error ? cause.message : String(cause)}` : ''}`, { cause: err });
|
|
88
|
+
}
|
|
89
|
+
if (res.status < 200 || res.status >= 300) {
|
|
90
|
+
throw new Error(`firecracker orchestrator ${method} ${url} → ${res.status}: ${await res.text()}`);
|
|
91
|
+
}
|
|
92
|
+
if (res.status === 204)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (res.contentType.includes('application/json'))
|
|
95
|
+
return JSON.parse(await res.text());
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
/** The plain-`fetch` control-plane path — the single-host DEFAULT, unchanged. */
|
|
99
|
+
async function fetchOrchestratorRequest(url, method, headers, payload) {
|
|
100
|
+
const init = { method, headers };
|
|
101
|
+
if (payload !== undefined)
|
|
102
|
+
init.body = payload;
|
|
103
|
+
const res = await fetch(url, init);
|
|
104
|
+
return {
|
|
105
|
+
status: res.status,
|
|
106
|
+
contentType: res.headers.get('content-type') ?? '',
|
|
107
|
+
text: () => res.text(),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The mTLS control-plane path: a node:https request presenting the injected
|
|
112
|
+
* client cert and verifying the orchestrator's server cert against the injected
|
|
113
|
+
* CA (`rejectUnauthorized: true`). Used only when `controlPlaneMtls` is present;
|
|
114
|
+
* the plain-`fetch` path above stays the default for single-host.
|
|
115
|
+
*/
|
|
116
|
+
function httpsOrchestratorRequest(url, method, headers, payload, mtls) {
|
|
117
|
+
const target = new URL(url);
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
const req = https.request({
|
|
120
|
+
protocol: target.protocol,
|
|
121
|
+
hostname: target.hostname,
|
|
122
|
+
port: target.port !== '' ? Number(target.port) : 443,
|
|
123
|
+
path: `${target.pathname}${target.search}`,
|
|
124
|
+
method,
|
|
125
|
+
headers,
|
|
126
|
+
ca: mtls.ca,
|
|
127
|
+
cert: mtls.cert,
|
|
128
|
+
key: mtls.key,
|
|
129
|
+
rejectUnauthorized: true,
|
|
130
|
+
minVersion: 'TLSv1.3',
|
|
131
|
+
...(mtls.servername !== undefined ? { servername: mtls.servername } : {}),
|
|
132
|
+
}, (res) => {
|
|
133
|
+
const chunks = [];
|
|
134
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
135
|
+
res.on('end', () => {
|
|
136
|
+
const ct = res.headers['content-type'];
|
|
137
|
+
resolve({
|
|
138
|
+
status: res.statusCode ?? 0,
|
|
139
|
+
contentType: Array.isArray(ct) ? (ct[0] ?? '') : (ct ?? ''),
|
|
140
|
+
text: async () => Buffer.concat(chunks).toString('utf8'),
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
res.on('error', reject);
|
|
144
|
+
});
|
|
145
|
+
req.on('error', reject);
|
|
146
|
+
if (payload !== undefined)
|
|
147
|
+
req.write(payload);
|
|
148
|
+
req.end();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// create()
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
async function spawnFirecrackerSandbox(config, options) {
|
|
155
|
+
const endpoint = config.orchestratorEndpoint;
|
|
156
|
+
const createBody = {
|
|
157
|
+
...(config.template !== undefined ? { template: config.template } : {}),
|
|
158
|
+
...(config.agentSnapshot !== undefined ? { agentSnapshot: config.agentSnapshot } : {}),
|
|
159
|
+
...(options.memoryLimitMb !== undefined ? { memoryLimitMb: options.memoryLimitMb } : {}),
|
|
160
|
+
...(options.maxProcesses !== undefined ? { maxProcesses: options.maxProcesses } : {}),
|
|
161
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
162
|
+
...(resolveEgressAllowlist(options) !== undefined
|
|
163
|
+
? { egressAllowlist: resolveEgressAllowlist(options) }
|
|
164
|
+
: {}),
|
|
165
|
+
};
|
|
166
|
+
let created;
|
|
167
|
+
try {
|
|
168
|
+
created = await orchestratorCall(endpoint, '/sandboxes', 'POST', config.getToken, createBody, config.controlPlaneMtls);
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
throw new Error(`firecracker: failed to create microVM sandbox — ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
172
|
+
}
|
|
173
|
+
if (!created || !created.sandboxId || !created.agent) {
|
|
174
|
+
throw new Error('firecracker: orchestrator create returned no sandboxId / agent handle');
|
|
175
|
+
}
|
|
176
|
+
const id = created.sandboxId;
|
|
177
|
+
const rootDir = created.rootDir;
|
|
178
|
+
// Normalise the orchestrator handle: fill the contract vsock port when a
|
|
179
|
+
// vsock handle omits it, and MERGE the consumer-injected mTLS material onto
|
|
180
|
+
// a network (`mtls`) handle so the transport can dial the relay. The
|
|
181
|
+
// orchestrator returns an `mtls` handle WITHOUT cert material (host/port/
|
|
182
|
+
// sandboxId only); the certs are injected here from `config.mtls`, never
|
|
183
|
+
// shipped by the control plane.
|
|
184
|
+
const handle = normalizeHandle(created.agent, config.agentVsockPort ?? DEFAULT_AGENT_VSOCK_PORT, config.mtls);
|
|
185
|
+
const transport = new VsockAgentTransport(handle, config.transport ?? {});
|
|
186
|
+
const destroy = async () => {
|
|
187
|
+
await orchestratorCall(endpoint, `/sandboxes/${encodeURIComponent(id)}:delete`, 'DELETE', config.getToken, undefined, config.controlPlaneMtls);
|
|
188
|
+
};
|
|
189
|
+
try {
|
|
190
|
+
// Readiness fence: the orchestrator's resume returns BEFORE the
|
|
191
|
+
// guest agent has reseeded entropy and re-listened on vsock. Stop
|
|
192
|
+
// the clock on the agent's healthz, exactly as the HTTP backends
|
|
193
|
+
// wait on `/healthz` — never on the orchestrator's 2xx, which
|
|
194
|
+
// fires before the guest runs (§5 clock semantics).
|
|
195
|
+
await transport.waitForReady(config.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS, config.readyPollIntervalMs ?? DEFAULT_READY_POLL_MS);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
// Best-effort orchestrator teardown so a readiness failure does not
|
|
199
|
+
// orphan a microVM/netns/UFFD handler (the reaper backstops, but
|
|
200
|
+
// surfacing the delete failure keeps the leak observable).
|
|
201
|
+
try {
|
|
202
|
+
await destroy();
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Preserve the readiness error as primary.
|
|
206
|
+
}
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
let status = 'ready';
|
|
210
|
+
return {
|
|
211
|
+
id,
|
|
212
|
+
get status() {
|
|
213
|
+
return status;
|
|
214
|
+
},
|
|
215
|
+
rootDir,
|
|
216
|
+
environment: detectEnvironment(),
|
|
217
|
+
async exec(command, argv, opts) {
|
|
218
|
+
status = 'busy';
|
|
219
|
+
try {
|
|
220
|
+
return await transport.execute({
|
|
221
|
+
command,
|
|
222
|
+
args: argv ?? [],
|
|
223
|
+
...(opts?.cwd !== undefined ? { cwd: opts.cwd } : {}),
|
|
224
|
+
...(opts?.env !== undefined ? { env: opts.env } : {}),
|
|
225
|
+
...(opts?.timeout !== undefined ? { timeoutMs: opts.timeout } : {}),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
status = 'ready';
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
async writeFile(path, content) {
|
|
233
|
+
const buf = Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf8');
|
|
234
|
+
await transport.writeFile(path, buf);
|
|
235
|
+
},
|
|
236
|
+
async readFile(path) {
|
|
237
|
+
return await transport.readFile(path);
|
|
238
|
+
},
|
|
239
|
+
async listFiles(rootPath) {
|
|
240
|
+
// Same wire as docker/aci: `find -printf '%p\t%s\n'`, parse
|
|
241
|
+
// line-by-line, map a non-zero exit (missing root) to "empty".
|
|
242
|
+
const result = await transport.execute({
|
|
243
|
+
command: 'find',
|
|
244
|
+
args: [rootPath, '-type', 'f', '-printf', '%p\t%s\n'],
|
|
245
|
+
});
|
|
246
|
+
if (result.exitCode !== 0)
|
|
247
|
+
return [];
|
|
248
|
+
const entries = [];
|
|
249
|
+
for (const rawLine of result.stdout.split('\n')) {
|
|
250
|
+
if (!rawLine)
|
|
251
|
+
continue;
|
|
252
|
+
const tab = rawLine.indexOf('\t');
|
|
253
|
+
if (tab < 0)
|
|
254
|
+
continue;
|
|
255
|
+
const filePath = rawLine.slice(0, tab);
|
|
256
|
+
const size = Number.parseInt(rawLine.slice(tab + 1), 10);
|
|
257
|
+
if (!filePath || !Number.isFinite(size))
|
|
258
|
+
continue;
|
|
259
|
+
entries.push({ path: filePath, size });
|
|
260
|
+
}
|
|
261
|
+
return entries;
|
|
262
|
+
},
|
|
263
|
+
async destroy() {
|
|
264
|
+
status = 'destroyed';
|
|
265
|
+
// Let the orchestrator DELETE failure propagate — the
|
|
266
|
+
// Vandal-side lifecycle wraps this with logging, and a
|
|
267
|
+
// swallowed error here means orphaned microVMs (and their
|
|
268
|
+
// netns / UFFD handlers) pile up with no observability handle.
|
|
269
|
+
await destroy();
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// Helpers
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
function resolveEgressAllowlist(options) {
|
|
277
|
+
const egress = options.egress;
|
|
278
|
+
if (!egress)
|
|
279
|
+
return undefined;
|
|
280
|
+
if (egress.kind === 'static')
|
|
281
|
+
return egress.allowedHosts;
|
|
282
|
+
// `deny-all` → empty allowlist (explicit). `allow-all` / `resolver`
|
|
283
|
+
// are resolved by the host before create when they apply; the
|
|
284
|
+
// backend forwards only the static, already-resolved shape. A
|
|
285
|
+
// resolver-shaped policy is the Vandal lifecycle's job to resolve
|
|
286
|
+
// upstream and pass as `static`.
|
|
287
|
+
if (egress.kind === 'deny-all')
|
|
288
|
+
return [];
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Turn the orchestrator's WIRE handle into the transport handle the dialer
|
|
293
|
+
* uses. Two transforms:
|
|
294
|
+
*
|
|
295
|
+
* - `vsock` with a missing/<=0 port → fill the contract port.
|
|
296
|
+
* - `mtls` → MERGE the consumer-injected `mtls` cert material onto the
|
|
297
|
+
* wire handle (which carries only host/port/sandboxId). The orchestrator
|
|
298
|
+
* never ships cert material; without an injected `mtls` block a network
|
|
299
|
+
* handle cannot dial, so this throws loud rather than constructing a
|
|
300
|
+
* transport that would fail at connect time.
|
|
301
|
+
*
|
|
302
|
+
* `unix` + already-correct `vsock` handles pass through untouched.
|
|
303
|
+
*/
|
|
304
|
+
export function normalizeHandle(handle, contractPort, mtls) {
|
|
305
|
+
if (handle.kind === 'mtls') {
|
|
306
|
+
if (!mtls) {
|
|
307
|
+
throw new Error('firecracker: orchestrator returned an mtls agent handle but no client cert material was injected ' +
|
|
308
|
+
'(the Vandal host layer must supply VANDAL_SANDBOX_FC_TLS_CA/_CERT/_KEY in network mode)');
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
kind: 'mtls',
|
|
312
|
+
host: handle.host,
|
|
313
|
+
port: handle.port,
|
|
314
|
+
sandboxId: handle.sandboxId,
|
|
315
|
+
tls: {
|
|
316
|
+
ca: mtls.ca,
|
|
317
|
+
cert: mtls.cert,
|
|
318
|
+
key: mtls.key,
|
|
319
|
+
...(mtls.servername !== undefined ? { servername: mtls.servername } : {}),
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (handle.kind === 'vsock' && (!handle.port || handle.port <= 0)) {
|
|
324
|
+
return { kind: 'vsock', udsPath: handle.udsPath, port: contractPort };
|
|
325
|
+
}
|
|
326
|
+
return handle;
|
|
327
|
+
}
|
|
328
|
+
function detectEnvironment() {
|
|
329
|
+
// Firecracker guests run Linux; the agent is a Linux-namespace-style
|
|
330
|
+
// worker from the SDK's perspective (the enum is host-shape, not
|
|
331
|
+
// guest internals).
|
|
332
|
+
return 'linux-namespace';
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/backends/firecracker/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE wire contract, shared across both transports.
|
|
3
|
+
*
|
|
4
|
+
* The docker (`backends/docker/`) and ACI (`backends/aci-standby-pool/`)
|
|
5
|
+
* backends speak this contract over **HTTP**: a streaming NDJSON
|
|
6
|
+
* `/execute` response and base64-bodied `/read-file` / `/write-file`
|
|
7
|
+
* JSON requests, served by `worker/server.js`. The Firecracker
|
|
8
|
+
* backend speaks the **same NDJSON shapes and the same base64 file-IO
|
|
9
|
+
* shapes** — only the transport changes from HTTP-over-TCP to
|
|
10
|
+
* framed-over-vsock (see `transport.ts`).
|
|
11
|
+
*
|
|
12
|
+
* This module is the single source of truth for the message shapes
|
|
13
|
+
* and the streaming exec-line parser, so the two transports cannot
|
|
14
|
+
* drift. It deliberately carries NO transport concern (no `fetch`, no
|
|
15
|
+
* socket) — it is pure codec. The HTTP backends keep their own inline
|
|
16
|
+
* copies (they predate this module and stay UNTOUCHED per the P0
|
|
17
|
+
* scope); this module mirrors those shapes verbatim and is the
|
|
18
|
+
* canonical definition the vsock path consumes.
|
|
19
|
+
*
|
|
20
|
+
* Why mirror rather than refactor docker/aci onto it: the P0 scope is
|
|
21
|
+
* "do NOT touch docker or aci". Centralising the shapes here, with the
|
|
22
|
+
* inline docker copy pinned by `backends/docker/__tests__` and this
|
|
23
|
+
* module pinned by `backends/firecracker/__tests__`, keeps both honest
|
|
24
|
+
* without editing the frozen HTTP path. A later cleanup pass can fold
|
|
25
|
+
* docker/aci onto this module once it is no longer release-frozen.
|
|
26
|
+
*/
|
|
27
|
+
import type { SandboxExecResult } from '@namzu/sdk';
|
|
28
|
+
/**
|
|
29
|
+
* `/execute` request body. Identical field set to the HTTP worker's
|
|
30
|
+
* `handleExecute` body (`command`, `args`, `cwd`, `env`, `stdin`,
|
|
31
|
+
* `timeoutMs`, `maxOutputBytes`). `timeoutMs` maps from the SDK's
|
|
32
|
+
* `SandboxExecOptions.timeout`.
|
|
33
|
+
*/
|
|
34
|
+
export interface ExecRequest {
|
|
35
|
+
readonly command: string;
|
|
36
|
+
readonly args?: readonly string[];
|
|
37
|
+
readonly cwd?: string;
|
|
38
|
+
readonly env?: Record<string, string>;
|
|
39
|
+
readonly stdin?: string;
|
|
40
|
+
readonly timeoutMs?: number;
|
|
41
|
+
readonly maxOutputBytes?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One NDJSON event the agent emits while streaming an `/execute`. The
|
|
45
|
+
* exact union the HTTP worker writes via `writeEvent`:
|
|
46
|
+
* { type: 'stdout_delta', data }
|
|
47
|
+
* { type: 'stderr_delta', data }
|
|
48
|
+
* { type: 'result', exitCode, timedOut, durationMs, stdoutTruncated?, stderrTruncated? }
|
|
49
|
+
* { type: 'error', error }
|
|
50
|
+
*/
|
|
51
|
+
export type ExecEvent = {
|
|
52
|
+
readonly type: 'stdout_delta';
|
|
53
|
+
readonly data: string;
|
|
54
|
+
} | {
|
|
55
|
+
readonly type: 'stderr_delta';
|
|
56
|
+
readonly data: string;
|
|
57
|
+
} | {
|
|
58
|
+
readonly type: 'result';
|
|
59
|
+
readonly exitCode: number;
|
|
60
|
+
readonly timedOut: boolean;
|
|
61
|
+
readonly durationMs?: number;
|
|
62
|
+
readonly stdoutTruncated?: boolean;
|
|
63
|
+
readonly stderrTruncated?: boolean;
|
|
64
|
+
} | {
|
|
65
|
+
readonly type: 'error';
|
|
66
|
+
readonly error: string;
|
|
67
|
+
};
|
|
68
|
+
/** `/write-file` request body. `content` is base64. */
|
|
69
|
+
export interface WriteFileRequest {
|
|
70
|
+
readonly path: string;
|
|
71
|
+
readonly content: string;
|
|
72
|
+
readonly encoding: 'base64';
|
|
73
|
+
}
|
|
74
|
+
/** `/write-file` success response. */
|
|
75
|
+
export interface WriteFileResponse {
|
|
76
|
+
readonly ok: boolean;
|
|
77
|
+
readonly bytesWritten?: number;
|
|
78
|
+
readonly error?: string;
|
|
79
|
+
}
|
|
80
|
+
/** `/read-file` request body. */
|
|
81
|
+
export interface ReadFileRequest {
|
|
82
|
+
readonly path: string;
|
|
83
|
+
readonly encoding: 'base64';
|
|
84
|
+
}
|
|
85
|
+
/** `/read-file` response. `content` is base64 on success. */
|
|
86
|
+
export interface ReadFileResponse {
|
|
87
|
+
readonly ok: boolean;
|
|
88
|
+
readonly content?: string;
|
|
89
|
+
readonly sizeBytes?: number;
|
|
90
|
+
readonly encoding?: string;
|
|
91
|
+
readonly error?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Accumulates the streamed `/execute` NDJSON into a single
|
|
95
|
+
* {@link SandboxExecResult}, exactly as the docker/aci `execViaWorker`
|
|
96
|
+
* loops do: concatenate `stdout_delta` / `stderr_delta`, capture the
|
|
97
|
+
* terminal `result`, and **throw** on an `error` event.
|
|
98
|
+
*
|
|
99
|
+
* Transport-agnostic: feed it whole parsed {@link ExecEvent}s (the
|
|
100
|
+
* transport owns newline-framing → JSON.parse → here). Malformed lines
|
|
101
|
+
* are dropped by the transport's `JSON.parse` guard before they reach
|
|
102
|
+
* this accumulator, matching the docker loop's `SyntaxError` swallow.
|
|
103
|
+
*/
|
|
104
|
+
export declare class ExecResultAccumulator {
|
|
105
|
+
private stdout;
|
|
106
|
+
private stderr;
|
|
107
|
+
private exitCode;
|
|
108
|
+
private timedOut;
|
|
109
|
+
private signal;
|
|
110
|
+
private settled;
|
|
111
|
+
private readonly start;
|
|
112
|
+
constructor(start?: number);
|
|
113
|
+
/**
|
|
114
|
+
* Apply one event. Returns `true` once a terminal `result` has been
|
|
115
|
+
* seen (so the transport can stop reading early if it wants).
|
|
116
|
+
* Throws if the event is an `error` — the same control flow the
|
|
117
|
+
* docker loop uses (`throw new Error(event.error)`).
|
|
118
|
+
*/
|
|
119
|
+
push(event: ExecEvent): boolean;
|
|
120
|
+
get done(): boolean;
|
|
121
|
+
/** Build the SDK-shaped result. `durationMs` measured host-side. */
|
|
122
|
+
finish(): SandboxExecResult;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse a single NDJSON line into an {@link ExecEvent}, or `undefined`
|
|
126
|
+
* if the line is blank or not valid JSON (the docker loop's
|
|
127
|
+
* `SyntaxError` swallow). Non-`SyntaxError` problems are impossible
|
|
128
|
+
* here because we only `JSON.parse`; structural validation is by the
|
|
129
|
+
* `type` discriminator at the call site.
|
|
130
|
+
*/
|
|
131
|
+
export declare function parseExecLine(line: string): ExecEvent | undefined;
|
|
132
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/backends/firecracker/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE wire contract, shared across both transports.
|
|
3
|
+
*
|
|
4
|
+
* The docker (`backends/docker/`) and ACI (`backends/aci-standby-pool/`)
|
|
5
|
+
* backends speak this contract over **HTTP**: a streaming NDJSON
|
|
6
|
+
* `/execute` response and base64-bodied `/read-file` / `/write-file`
|
|
7
|
+
* JSON requests, served by `worker/server.js`. The Firecracker
|
|
8
|
+
* backend speaks the **same NDJSON shapes and the same base64 file-IO
|
|
9
|
+
* shapes** — only the transport changes from HTTP-over-TCP to
|
|
10
|
+
* framed-over-vsock (see `transport.ts`).
|
|
11
|
+
*
|
|
12
|
+
* This module is the single source of truth for the message shapes
|
|
13
|
+
* and the streaming exec-line parser, so the two transports cannot
|
|
14
|
+
* drift. It deliberately carries NO transport concern (no `fetch`, no
|
|
15
|
+
* socket) — it is pure codec. The HTTP backends keep their own inline
|
|
16
|
+
* copies (they predate this module and stay UNTOUCHED per the P0
|
|
17
|
+
* scope); this module mirrors those shapes verbatim and is the
|
|
18
|
+
* canonical definition the vsock path consumes.
|
|
19
|
+
*
|
|
20
|
+
* Why mirror rather than refactor docker/aci onto it: the P0 scope is
|
|
21
|
+
* "do NOT touch docker or aci". Centralising the shapes here, with the
|
|
22
|
+
* inline docker copy pinned by `backends/docker/__tests__` and this
|
|
23
|
+
* module pinned by `backends/firecracker/__tests__`, keeps both honest
|
|
24
|
+
* without editing the frozen HTTP path. A later cleanup pass can fold
|
|
25
|
+
* docker/aci onto this module once it is no longer release-frozen.
|
|
26
|
+
*/
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Streaming exec-line accumulator — the parser docker/aci inline today,
|
|
29
|
+
// lifted out so the vsock transport reuses it byte-for-byte.
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Accumulates the streamed `/execute` NDJSON into a single
|
|
33
|
+
* {@link SandboxExecResult}, exactly as the docker/aci `execViaWorker`
|
|
34
|
+
* loops do: concatenate `stdout_delta` / `stderr_delta`, capture the
|
|
35
|
+
* terminal `result`, and **throw** on an `error` event.
|
|
36
|
+
*
|
|
37
|
+
* Transport-agnostic: feed it whole parsed {@link ExecEvent}s (the
|
|
38
|
+
* transport owns newline-framing → JSON.parse → here). Malformed lines
|
|
39
|
+
* are dropped by the transport's `JSON.parse` guard before they reach
|
|
40
|
+
* this accumulator, matching the docker loop's `SyntaxError` swallow.
|
|
41
|
+
*/
|
|
42
|
+
export class ExecResultAccumulator {
|
|
43
|
+
stdout = '';
|
|
44
|
+
stderr = '';
|
|
45
|
+
exitCode = -1;
|
|
46
|
+
timedOut = false;
|
|
47
|
+
signal;
|
|
48
|
+
settled = false;
|
|
49
|
+
start;
|
|
50
|
+
constructor(start = Date.now()) {
|
|
51
|
+
this.start = start;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Apply one event. Returns `true` once a terminal `result` has been
|
|
55
|
+
* seen (so the transport can stop reading early if it wants).
|
|
56
|
+
* Throws if the event is an `error` — the same control flow the
|
|
57
|
+
* docker loop uses (`throw new Error(event.error)`).
|
|
58
|
+
*/
|
|
59
|
+
push(event) {
|
|
60
|
+
if (event.type === 'stdout_delta') {
|
|
61
|
+
this.stdout += event.data;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (event.type === 'stderr_delta') {
|
|
65
|
+
this.stderr += event.data;
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (event.type === 'result') {
|
|
69
|
+
this.exitCode = event.exitCode;
|
|
70
|
+
this.timedOut = event.timedOut;
|
|
71
|
+
this.settled = true;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
// event.type === 'error'
|
|
75
|
+
throw new Error(event.error);
|
|
76
|
+
}
|
|
77
|
+
get done() {
|
|
78
|
+
return this.settled;
|
|
79
|
+
}
|
|
80
|
+
/** Build the SDK-shaped result. `durationMs` measured host-side. */
|
|
81
|
+
finish() {
|
|
82
|
+
return {
|
|
83
|
+
exitCode: this.exitCode,
|
|
84
|
+
stdout: this.stdout,
|
|
85
|
+
stderr: this.stderr,
|
|
86
|
+
...(this.signal ? { signal: this.signal } : {}),
|
|
87
|
+
timedOut: this.timedOut,
|
|
88
|
+
durationMs: Date.now() - this.start,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse a single NDJSON line into an {@link ExecEvent}, or `undefined`
|
|
94
|
+
* if the line is blank or not valid JSON (the docker loop's
|
|
95
|
+
* `SyntaxError` swallow). Non-`SyntaxError` problems are impossible
|
|
96
|
+
* here because we only `JSON.parse`; structural validation is by the
|
|
97
|
+
* `type` discriminator at the call site.
|
|
98
|
+
*/
|
|
99
|
+
export function parseExecLine(line) {
|
|
100
|
+
const trimmed = line.trim();
|
|
101
|
+
if (!trimmed)
|
|
102
|
+
return undefined;
|
|
103
|
+
try {
|
|
104
|
+
return JSON.parse(trimmed);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
if (err instanceof SyntaxError)
|
|
108
|
+
return undefined;
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/backends/firecracker/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AA8EH,8EAA8E;AAC9E,wEAAwE;AACxE,6DAA6D;AAC7D,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,OAAO,qBAAqB;IACzB,MAAM,GAAG,EAAE,CAAA;IACX,MAAM,GAAG,EAAE,CAAA;IACX,QAAQ,GAAG,CAAC,CAAC,CAAA;IACb,QAAQ,GAAG,KAAK,CAAA;IAChB,MAAM,CAAoB;IAC1B,OAAO,GAAG,KAAK,CAAA;IACN,KAAK,CAAQ;IAE9B,YAAY,QAAgB,IAAI,CAAC,GAAG,EAAE;QACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACnB,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,KAAgB;QACpB,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACnC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAA;YACzB,OAAO,KAAK,CAAA;QACb,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACnC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAA;YACzB,OAAO,KAAK,CAAA;QACb,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAA;YAC9B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAA;YAC9B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;YACnB,OAAO,IAAI,CAAA;QACZ,CAAC;QACD,yBAAyB;QACzB,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;IAED,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,OAAO,CAAA;IACpB,CAAC;IAED,oEAAoE;IACpE,MAAM;QACL,OAAO;YACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK;SACnC,CAAA;IACF,CAAC;CACD;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC3B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAC9B,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAA;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,GAAG,YAAY,WAAW;YAAE,OAAO,SAAS,CAAA;QAChD,MAAM,GAAG,CAAA;IACV,CAAC;AACF,CAAC"}
|