@runblackcat/sdk 0.1.0-alpha.70 → 0.1.0-alpha.72

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/README.md CHANGED
@@ -212,6 +212,47 @@ blackcat_placement_rejected_total{reason="…"} no_capacity
212
212
  A fleet pinned at its ceiling shows up as `blackcat_capacity_target_nodes`
213
213
  persistently above `blackcat_nodes`.
214
214
 
215
+ ## Recommended production shape: bare metal
216
+
217
+ Run workers on bare metal, one or a few large `.metal` hosts, and keep nested virtualization (a VM with `/dev/kvm`) as the quick-start
218
+ default: it launches in under a minute and needs no special quota. On metal every guest page fault costs about 7.6 times less than
219
+ nested, so the same fleet restores 2 to 3 times faster, fans out 30 times faster at 100 clones, costs a third as much per clone and
220
+ does not lose VMs to nested VM-entry failures.
221
+
222
+ | | bare metal (`c7i.metal-24xl`) | nested (`m8i.4xlarge`) |
223
+ |---|---|---|
224
+ | create p50 | 29 ms | 60 ms |
225
+ | first exec | 33 ms | 67 ms |
226
+ | snapshot restore | 13 ms | 43 ms |
227
+ | fan-out of 100 clones | 0.6 s | 17.9 s |
228
+ | cost per clone-hour | $0.0043 | $0.0132 |
229
+ | VM-entry deaths | 0 | 2.9 per 1,000 hot restores, 325 per 1,000 at density |
230
+
231
+ (Bake-off, 2 vCPU / 1 GiB app template, `contrib/bench/bakeoff/RESULTS.md`, "Huge pages". The hosts are different CPU
232
+ generations and metal has 12 times the CPUs, so the fan-out column is not virtualization alone. Metal is cheaper per clone because it
233
+ carries about 1,000 clones where the nested host was measured at 64.)
234
+
235
+ North star on one `c7i.metal-48xl` (500 clones of a built-from-source web app at once, each running a short test then destroyed):
236
+ 99% done 112 s after the PR arrived, all 500 passed with no replacements and no VMM deaths, about $0.34 per PR from a stopped node,
237
+ against 300 s, 264 replacements and about $1.00 on five `m8i.8xlarge`.
238
+
239
+ Trade-offs, plainly:
240
+
241
+ * **Boot.** A metal reboot has been quoted at about 7.5 minutes. Measured on a `c7i.metal-48xl`: a fresh launch was registered with SSM
242
+ in 74 s and a stopped node was serving again 47 s after `StartInstances`, but stopping it took 320 s (unbilled). Time from a
243
+ planner launch to an enrolled node on metal has not been measured (see
244
+ `contrib/bench/results/2026-10-04-wave-c-metal-northstar.md`). Nested hosts register in 40 to 45 s.
245
+ * **Coarse sizes.** Metal is `.metal` sizes only: 96 or 192 vCPUs on the `c7i` family. A small or spiky fleet pays for a whole box
246
+ or runs nested.
247
+ * **Thin Spot capacity.** Metal Spot pools are small; the planner falls back to on-demand when one refuses, and the price you plan
248
+ with is the cheaper Spot one.
249
+ * **Large blast radius.** One host carries up to about a thousand clones. Losing it, or restarting its agent, hits all of them; keep
250
+ `fleet max-clones-per-host` as the cap you can accept and keep more than one host for anything that must survive a host loss.
251
+
252
+ The capacity planner prefers metal when a reservation's `needed_by` allows its boot time and the price per clone is lower
253
+ ([docs/capacity-planning.md](docs/capacity-planning.md)); `blackcat_vm_entry_deaths` counts the VM-entry failures that make nested
254
+ hosts unreliable at density. Setup: [docs/bare-mode.md](docs/bare-mode.md#recommended-production-shape-bare-metal).
255
+
215
256
  ## Install (alpha)
216
257
 
217
258
  Identical on bare metal and on AWS:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runblackcat/sdk",
3
- "version": "0.1.0-alpha.70",
3
+ "version": "0.1.0-alpha.72",
4
4
  "description": "TypeScript SDK for the Blackcat sandbox gateway",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/TreebeardHQ/blackcat",
@@ -262,7 +262,13 @@ export interface CreateTemplateResponse {
262
262
  };
263
263
  }
264
264
  export type CreateSandboxInput = {
265
- template: string;
265
+ /**
266
+ * The template name or version id. Omitted, it is the tenant's `default`
267
+ * template, which the gateway builds on the tenant's first use: that first
268
+ * create fails with a 503 `default_template_building` until the build is
269
+ * ready, and repeating the same request is how to wait for it.
270
+ */
271
+ template?: string;
266
272
  /** Must equal the checkpoint's vCPUs, which are fixed at capture; omit it. */
267
273
  vcpus?: number;
268
274
  /**
package/sdk/dist/index.js CHANGED
@@ -18,6 +18,9 @@ var BLACKCAT_ERROR_CODES = {
18
18
  SANDBOX_TRANSITIONING: "sandbox_transitioning",
19
19
  FLEET_UPGRADE_QUIESCING: "fleet_upgrade_quiescing"
20
20
  };
21
+ function withTemplate(input) {
22
+ return { ...input, template: input.template ?? "default" };
23
+ }
21
24
 
22
25
  class BlackcatApiError extends Error {
23
26
  status;
@@ -444,10 +447,10 @@ class SandboxesResource {
444
447
  return this.client.json("GET", "/v1/sandboxes", undefined, "token");
445
448
  }
446
449
  async create(input) {
447
- return parseCreateSandboxResponse(await this.client.json("POST", "/v1/sandboxes", input, "token", false), input.request_id);
450
+ return parseCreateSandboxResponse(await this.client.json("POST", "/v1/sandboxes", withTemplate(input), "token", false), input.request_id);
448
451
  }
449
452
  async createBatch(input) {
450
- return parseSandboxBatch(await this.client.json("POST", "/v1/sandboxes", input, "token", false), input.request_id);
453
+ return parseSandboxBatch(await this.client.json("POST", "/v1/sandboxes", withTemplate(input), "token", false), input.request_id);
451
454
  }
452
455
  async batch(requestId) {
453
456
  return parseSandboxBatch(await this.client.json("GET", `/v1/sandboxes/batches/${pathSegment(requestId)}`, undefined, "token"), requestId);