@molecule/api-code-sandbox-flyio 1.3.8 → 1.3.9

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.
Files changed (2) hide show
  1. package/README.md +72 -139
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-11-01T09:52:42.927Z
6
+ Generated: 2026-11-04T14:19:55.149Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-code-sandbox-flyio
@@ -484,169 +484,95 @@ Every option is also readable from an environment variable (see
484
484
 
485
485
  ```typescript
486
486
  interface FlyioConfig {
487
- /**
488
- * Fly API token (sent as `Authorization: Bearer <token>`). Falls back to
489
- * `FLY_API_TOKEN`, then `FLY_ACCESS_TOKEN`. Required — the provider throws a
490
- * named error on the first API call if none is resolvable.
491
- */
487
+ /** Fly API token (sent as `Authorization: Bearer <token>`). */
492
488
  apiToken?: string
493
- /**
494
- * Machines API base URL, INCLUDING the `/v1` path segment. Defaults to
495
- * `FLY_API_HOSTNAME` (with `/v1` appended when the value has no path) or
496
- * `https://api.machines.dev/v1`. From inside a Fly private network the
497
- * documented internal endpoint is `http://_api.internal:4280/v1`.
498
- */
489
+ /** Machines API base URL, INCLUDING the `/v1` path segment. */
499
490
  apiUrl?: string
500
- /**
501
- * Fly organization slug that owns the sandbox apps. Falls back to
502
- * `FLY_ORG_SLUG`, then `personal`. Required for app creation and for
503
- * {@link FlyioConfig.appPerProject} listing.
504
- */
491
+ /** Fly organization slug that owns the sandbox apps. */
505
492
  orgSlug?: string
506
- /**
507
- * Shared Fly app that holds every sandbox Machine. Only used when
508
- * `appPerProject` is `false`. Falls back to `FLY_SANDBOX_APP`.
509
- *
510
- * A shared app puts every tenant on ONE 6PN private network, where each
511
- * sandbox can reach every other sandbox's dev-server ports by private IPv6.
512
- * That is the same cross-tenant exposure the Docker bond's `bridge` network
513
- * has, so this mode is REFUSED in production.
514
- */
493
+ /** Shared Fly app that holds every sandbox Machine. */
515
494
  appName?: string
516
- /**
517
- * Name prefix for the per-project Fly app when `appPerProject` is `true`
518
- * (default). The app is `<appPrefix>-<sanitized projectId>`. Falls back to
519
- * `FLY_SANDBOX_APP_PREFIX`, then `mol-sandbox`.
520
- */
495
+ /** Name prefix for the per-project Fly app when `appPerProject` is `true` (default). */
521
496
  appPrefix?: string
522
- /**
523
- * Create one Fly app — on its own custom 6PN private network — per project,
524
- * so tenants are network-isolated from each other. Defaults to `true`.
525
- * Set `false` (or `FLY_SANDBOX_APP_PER_PROJECT=false`) only for a
526
- * single-tenant deployment; it is refused in production.
527
- */
497
+ /** Create one Fly app — on its own custom 6PN private network — per project, so tenants are network-isolated from each other. */
528
498
  appPerProject?: boolean
529
- /**
530
- * Custom 6PN network name for a per-project app. Defaults to the app name, so
531
- * every project lands on its own private network. Ignored when
532
- * `appPerProject` is `false`. A Fly app's network CANNOT be changed after
533
- * creation.
534
- */
499
+ /** Custom 6PN network name for a per-project app. */
535
500
  network?: string
536
- /**
537
- * Default Fly region for Machines and volumes (e.g. `iad`). Defaults to
538
- * `iad`; `SandboxConfig.region` overrides it for one recorded project create.
539
- * The API Machine's own `FLY_REGION` is never a tenant-placement fallback.
540
- */
501
+ /** Default Fly region for Machines and volumes (e.g. `iad`). */
541
502
  region?: string
542
- /**
543
- * OCI image every sandbox Machine runs. Must be pullable by Fly — a tag in
544
- * the org's `registry.fly.io` repository or a public registry. A LOCAL
545
- * `molecule-sandbox:latest` is not reachable by Fly; push it first. Falls
546
- * back to `FLY_SANDBOX_IMAGE`, then `registry.fly.io/molecule-sandbox:latest`.
547
- */
503
+ /** OCI image every sandbox Machine runs. */
548
504
  baseImage?: string
549
- /** Default vCPU count (`guest.cpus`). Defaults to 1. */
505
+ /** Default vCPU count (`guest.cpus`). */
550
506
  defaultCpu?: number
551
- /** Default `guest.cpu_kind` — `shared` or `performance`. Defaults to `shared`. */
507
+ /** Default `guest.cpu_kind` — `shared` or `performance`. */
552
508
  defaultCpuKind?: string
553
- /** Default memory in MB (`guest.memory_mb`). Defaults to 1024. */
509
+ /** Default memory in MB (`guest.memory_mb`). */
554
510
  defaultMemoryMB?: number
555
- /**
556
- * Size in GB of the volume created for a sandbox's `/workspace` when
557
- * `SandboxConfig.volumeName` is set. Fly volumes are sized in whole GB, so the
558
- * core's `resources.diskMB` is rounded UP to the next GB. Defaults to 10.
559
- */
511
+ /** Size in GB of the volume created for a sandbox's `/workspace` when `SandboxConfig.volumeName` is set. */
560
512
  defaultVolumeGB?: number
561
- /** Internal port the preview service forwards to (the Vite dev server). Defaults to 5173. */
513
+ /** Internal port the preview service forwards to (the Vite dev server). */
562
514
  previewPort?: number
563
- /**
564
- * Preview URL template. Placeholders `{app}`, `{machineId}` and `{port}` are
565
- * all replaced globally. Defaults to `https://{app}.fly.dev`.
566
- *
567
- * For a control plane that runs on the SAME Fly 6PN, the private form
568
- * `http://{machineId}.vm.{app}.internal:{port}` reaches the Machine with no
569
- * public exposure at all — but it does NOT work across custom 6PNs, which is
570
- * exactly what `appPerProject` creates. See the module `@remarks`.
571
- */
515
+ /** Preview URL template. */
572
516
  previewUrlTemplate?: string
573
- /**
574
- * Attach a public Fly Proxy service (`80` → http, `443` → tls+http) forwarding
575
- * to {@link FlyioConfig.previewPort}. Defaults to `true`. Set `false` for a
576
- * fully private sandbox reached only over 6PN/Flycast.
577
- */
517
+ /** Attach a public Fly Proxy service (`80` → http, `443` → tls+http) forwarding to {@link FlyioConfig.previewPort}. */
578
518
  publicService?: boolean
579
- /**
580
- * Fly Proxy idle behaviour for the preview service. Defaults to `'off'` —
581
- * never idle the Machine — because the autostop timer counts the PROXY
582
- * service's idle time, not the workload's, so a sandbox with nobody looking at
583
- * its preview is suspended out from under a build that is still running.
584
- * `suspend` is the scale-to-zero mapping (resume from a memory snapshot on the
585
- * next request) and `stop` is a full stop with a cold boot on wake; opt into
586
- * either deliberately.
587
- *
588
- * This is the provider's STEADY-STATE policy, reported as
589
- * `SandboxProvider.defaultIdleAction`. A caller can override it for one sandbox
590
- * with `SandboxConfig.idleAction` and restore this value afterwards with
591
- * `Sandbox.setIdleAction` — which is what a production deploy does so its
592
- * destination cannot suspend mid-copy.
593
- */
519
+ /** Fly Proxy idle behaviour for the preview service. */
594
520
  autostop?: 'off' | 'stop' | 'suspend'
595
- /**
596
- * Allocate a shared Anycast IPv4 for a newly-created app so `<app>.fly.dev`
597
- * serves traffic. Defaults to `false` (opt-in). See the module `@remarks` —
598
- * the accepted `type` values for the IP-assignment endpoint are NOT enumerated
599
- * in Fly's OpenAPI specification.
600
- */
521
+ /** Allocate a shared Anycast IPv4 for a newly-created app so `<app>.fly.dev` serves traffic. */
601
522
  assignSharedIpv4?: boolean
602
- /** IP type requested when {@link FlyioConfig.assignSharedIpv4} is on. Defaults to `shared_v4`. */
523
+ /** IP type requested when {@link FlyioConfig.assignSharedIpv4} is on. */
603
524
  ipAssignmentType?: string
604
- /** Prefix for the Machine metadata keys this provider owns. Defaults to `molecule-sandbox`. */
525
+ /** Prefix for the Machine metadata keys this provider owns. */
605
526
  metadataPrefix?: string
606
- /** Durable create-attempt ownership. Required for automatic failed-create reconciliation. */
527
+ /** Durable create-attempt ownership. */
607
528
  createAttemptJournal?: FlyCreateAttemptJournal
608
- /** Timeout for a single Machines API request, in ms. Defaults to 30000. */
529
+ /** Timeout for a single Machines API request, in ms. */
609
530
  requestTimeoutMs?: number
610
- /**
611
- * TOTAL budget, in seconds, that `create()`/`start()`/`wake()` block waiting
612
- * for the Machine to actually reach `started`. Fly's `GET .../wait` blocks
613
- * for at most 60 seconds per call, so a larger budget is spent as consecutive
614
- * wait rounds. Defaults to 180 — a Machine whose image is not yet cached on
615
- * its host (every first boot after an image push) pulls it before starting,
616
- * and that alone can exceed a single 60 s round.
617
- *
618
- * Without this wait, `start()` resolves the moment Fly ACCEPTS the request,
619
- * and the caller's very next `exec` hits a Machine that is not running yet.
620
- */
531
+ /** TOTAL budget, in seconds, that `create()`/`start()`/`wake()` block waiting for the Machine to actually reach `started`. */
621
532
  startTimeoutSeconds?: number
622
- /**
623
- * Ports a sandbox Machine may open outbound connections on. Setting this makes
624
- * the provider apply a Fly **network policy** to every app it provisions;
625
- * leaving it unset applies no policy at all, and `verifyEgress()` will then
626
- * observe (correctly) that egress is `open`. Falls back to
627
- * `FLY_SANDBOX_EGRESS_ALLOWED_PORTS` (`tcp:3128,udp:53`).
628
- *
629
- * Read {@link https://fly.io/docs/machines/guides-examples/network-policies/}
630
- * before choosing a value, because the mechanism is narrower than it looks:
631
- * rules match protocol and port ONLY — no host, no CIDR, no ranges — so this
632
- * can never be a host allowlist. `tcp:443` lets a sandbox reach EVERY host on
633
- * the internet that listens on 443. To get a host allowlist, allow only the
634
- * port of an egress proxy you control and route sandbox traffic through it.
635
- *
636
- * An empty array is rejected rather than treated as "deny all": Fly documents
637
- * the deny default as a consequence of an `allow` rule existing, and says
638
- * nothing about a rule with no ports.
639
- */
533
+ /** Ports a sandbox Machine may open outbound connections on. */
640
534
  egressAllowedPorts?: FlyNetworkPolicyPort[]
641
- /** Name of the egress policy this provider owns on each app. Defaults to `molecule-sandbox-egress`. */
535
+ /** Name of the egress policy this provider owns on each app. */
642
536
  egressPolicyName?: string
643
- /**
644
- * Apps every sandbox must be able to reach ACROSS its per-project 6PN — the
645
- * tenant Postgres cluster and the control plane's egress proxy. For each one,
646
- * this provider allocates a **Flycast** private address into the project's own
647
- // … truncated — the full declaration is in node_modules/@molecule/api-code-sandbox-flyio/dist/types.d.ts
537
+ /** Apps every sandbox must be able to reach ACROSS its per-project 6PN — the tenant Postgres cluster and the control plane's egress proxy. */
538
+ privateServices?: FlyPrivateService[]
539
+ /** Per-create regional overrides for {@link FlyioConfig.privateServices}. */
540
+ privateServicesByRegion?: Record<string, FlyPrivateService[]>
541
+ /** `type` sent to `POST /apps/{app}/ip_assignments` when allocating a Flycast address. */
542
+ privateIpAssignmentType?: string
543
+ /** Literal `ip:port` targets `verifyEgress()` attempts raw TCP connections to. */
544
+ egressProbeTargets?: string[]
545
+ /** Per-connection timeout for the egress probe, in ms. */
546
+ egressProbeTimeoutMs?: number
547
+ /** Image the throwaway `verifyEgress()` probe Machine runs. */
548
+ egressProbeImage?: string
549
+ /** Bucket holding sandbox templates. */
550
+ templateBucket?: string
551
+ /** S3 endpoint for {@link FlyioConfig.templateBucket}, e.g. Tigris's `https://t3.storage.dev`. */
552
+ templateEndpoint?: string
553
+ /** Region used to sign template-store requests. */
554
+ templateRegion?: string
555
+ /** Access key for the template store. */
556
+ templateAccessKeyId?: string
557
+ /** Secret key for the template store. */
558
+ templateSecretAccessKey?: string
559
+ /** Session token for temporary template-store credentials. */
560
+ templateSessionToken?: string
561
+ /** Key prefix every template object lives under. */
562
+ templatePrefix?: string
563
+ /** Address the template bucket path-style (`https://endpoint/bucket/key`) instead of virtual-host style. */
564
+ templateForcePathStyle?: boolean
565
+ /** Lifetime of the presigned capture/restore URL handed to a sandbox, in seconds. */
566
+ templateUrlExpirySeconds?: number
567
+ /** Wall-clock budget for one capture or restore transfer, in ms. */
568
+ templateTransferTimeoutMs?: number
569
+ /** Largest template archive this provider will store, in bytes. */
570
+ templateMaxArchiveBytes?: number
571
+ }
648
572
  ```
649
573
 
574
+ _(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/api-code-sandbox-flyio/dist/types.d.ts`.)_
575
+
650
576
  #### `ProcessEnv`
651
577
 
652
578
  Environment variables the Fly.io sandbox provider reads. Every one is
@@ -1605,8 +1531,15 @@ its own 6PN: under a policy allowing only `udp:53`, `molecule-pg-tenant.flycast`
1605
1531
  and `molecule-api.flycast` both RESOLVED and every TCP connect to them was
1606
1532
  dropped; re-applying the policy with `tcp:443` opened 443 and nothing else.
1607
1533
  Fly's sentence is about INGRESS — a Machine's egress TOWARD a Flycast address
1608
-
1609
- _(description truncated — read the full text in `node_modules/@molecule/api-code-sandbox-flyio/dist/flycast.d.ts`.)_
1534
+ is filtered like any other. Drop a declared service's port and every database
1535
+ connection in the fleet goes with it.
1536
+
1537
+ The cost of each derived port, stated plainly because a Fly policy has no
1538
+ destination field of any kind (also measured — see the `egress.ts` module
1539
+ description): the port is opened to EVERY host on the internet, not only to
1540
+ the private service it was derived for. That residual cannot be closed at this
1541
+ layer; it is named in `verifyEgress()`'s verdict and in
1542
+ `docs/sandbox-egress-enforcement.md`.
1610
1543
 
1611
1544
  ```typescript
1612
1545
  function mergeEgressPorts(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-code-sandbox-flyio",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
4
4
  "description": "Fly.io Machines (Firecracker microVM) code sandbox provider",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "license": "Apache-2.0",
32
32
  "devDependencies": {
33
33
  "@molecule/api-bond": "1.0.4",
34
- "@molecule/api-code-sandbox": "1.5.1",
34
+ "@molecule/api-code-sandbox": "1.6.1",
35
35
  "@types/node": "26.6.5",
36
36
  "typescript": "6.0.3",
37
37
  "vitest": "4.1.12"