@aerol-ai/aerolvm-sdk 0.5.4 → 0.7.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/dist/types.d.ts CHANGED
@@ -135,6 +135,15 @@ export interface GPUOptions {
135
135
  }
136
136
  import type { Image } from "./Image.js";
137
137
  export interface CreateOptions {
138
+ /**
139
+ * Optional name, unique among the caller's sandboxes (another account may
140
+ * use the same name). A repeat create with a name the caller already holds
141
+ * returns HTTP 409. Names starting with `owner:` or shaped like a sandbox
142
+ * ID (`sb-` plus 16 hex) are rejected.
143
+ */
144
+ name?: string;
145
+ /** Free-form key/value labels; filter on them with `ListOptions.tags`. */
146
+ tags?: Record<string, string>;
138
147
  /**
139
148
  * The base image for this sandbox. A bare image reference (e.g.
140
149
  * `"ubuntu:22.04"`) is pulled by the daemon as-is. An {@link Image} builder
@@ -152,21 +161,43 @@ export interface CreateOptions {
152
161
  osUser?: string;
153
162
  networkBlockAll?: boolean;
154
163
  /**
155
- * Egress allowlist of CIDRs. When set, the sandbox may reach only these
156
- * destinations; all other outbound traffic is dropped by the host firewall.
157
- * Mutually exclusive with `networkDenyOut`. For a full block use
158
- * `networkBlockAll` instead.
164
+ * Egress allowlist: CIDRs, hostnames (`pypi.org`), `*.suffix` wildcards and
165
+ * `host:port` entries. Alone, the sandbox may reach only these. With
166
+ * `networkDenyOut`, allow wins, then deny, then the default is allow; a
167
+ * deny of `0.0.0.0/0` makes it an allowlist. For a full block use
168
+ * `networkBlockAll`.
159
169
  */
160
170
  networkAllowOut?: string[];
161
171
  /**
162
- * Egress blocklist of CIDRs. When set, the sandbox may reach anything except
163
- * these destinations. Mutually exclusive with `networkAllowOut`.
172
+ * Egress blocklist of CIDRs (never hostnames). Alone, the sandbox may reach
173
+ * anything except these destinations.
164
174
  */
165
175
  networkDenyOut?: string[];
166
176
  /**
167
- * Whether the sandbox may be exposed to the public internet. Defaults to
168
- * true; set `false` to make `exposePort` fail — the sandbox stays reachable
169
- * only via the toolbox proxy and SSH gateway, not a public URL.
177
+ * Named egress profiles whose entries join `networkAllowOut` (see
178
+ * `putEgressProfile`). A profile change reaches every sandbox that
179
+ * references it.
180
+ */
181
+ egressProfiles?: string[];
182
+ /**
183
+ * `"learn"` gives the sandbox open egress and records what it reaches, so
184
+ * `learned()` can suggest an allow list. Trusted runs only. Needs no lists,
185
+ * profiles or block-all. Omitted is `"enforce"`.
186
+ */
187
+ networkEgressMode?: "enforce" | "learn";
188
+ /**
189
+ * Method and path rules that refine hosts the allow list already admits
190
+ * (at most 32). A rule with `inspect: true` makes the egress gateway
191
+ * terminate TLS on 443 with the node's CA, which only a sandbox created
192
+ * with such a rule trusts, so set inspect rules here rather than adding
193
+ * them later.
194
+ */
195
+ networkEgressRules?: EgressRule[];
196
+ /**
197
+ * Whether the sandbox is public from boot. Omitted or `false` creates it
198
+ * private: no public URL and no ingress route. `true` makes it public from
199
+ * boot. Either way the first `exposePort` call makes the sandbox public, so
200
+ * `false` does not stop a later `exposePort`.
170
201
  */
171
202
  allowPublicTraffic?: boolean;
172
203
  /**
@@ -211,15 +242,22 @@ export interface CreateOptions {
211
242
  *
212
243
  * GPU access is not supported with `"gvisor"`.
213
244
  */
214
- runtime?: "docker" | "gvisor" | "kata" | "firecracker" | "wasm";
245
+ runtime?: "docker" | "gvisor" | "kata" | "firecracker" | "wasm" | "isolate";
215
246
  /**
216
247
  * Survival class across daemon restarts. Omit for the runtime default:
217
- * `passivatable` for docker/gvisor/firecracker, `ephemeral` for wasm.
248
+ * `passivatable` for docker/gvisor/firecracker, `ephemeral` for wasm/isolate.
218
249
  * `durable` is wasm-only and not yet implemented.
219
250
  */
220
251
  durability?: Durability;
221
- /** WASM module reference. When runtime is wasm, may be used instead of image. */
252
+ /** WASM module / isolate bundle reference. When runtime is wasm or isolate, may be used instead of image. */
222
253
  moduleRef?: string;
254
+ /**
255
+ * Isolate-group key for `runtime: "isolate"`. Sandboxes with the same
256
+ * tenant share one workerd process. Server-authorized; when omitted the
257
+ * group key falls back to the authenticated identity. Ignored by other
258
+ * runtimes.
259
+ */
260
+ tenantId?: string;
223
261
  /**
224
262
  * Attach GPU resources to the sandbox. Omit for CPU-only workloads.
225
263
  * Not compatible with runtime="gvisor".
@@ -232,6 +270,13 @@ export interface CreateOptions {
232
270
  * `sandbox.customDomains.add(host)` for each entry after create.
233
271
  */
234
272
  customDomains?: string[];
273
+ /**
274
+ * Firecracker template id (from `createTemplate`); the template must be
275
+ * ready. Requires `runtime: "firecracker"` and `image`: pass the image the
276
+ * template was built from. The sandbox boots from the template's prepared
277
+ * rootfs instead of building one from `image` on every create.
278
+ */
279
+ templateId?: string;
235
280
  }
236
281
  export interface ResizeOptions {
237
282
  /** Number of CPU cores. Fractional values supported (e.g. 0.5). */
@@ -247,6 +292,25 @@ export interface ListOptions {
247
292
  * or pass an empty map to list every sandbox visible to the PAT.
248
293
  */
249
294
  tags?: Record<string, string>;
295
+ /**
296
+ * When true, include sandbox env vars in the response (`?include_env=true`).
297
+ * Env is omitted by default — opt-in reads are audited server-side.
298
+ */
299
+ includeEnv?: boolean;
300
+ /**
301
+ * Filter to the caller's sandbox with exactly this name (`?name=<name>`);
302
+ * the result has zero or one entries. Names are unique per owner. Servers
303
+ * that predate name lookup ignore the filter and return a normal list, so
304
+ * prefer `MicroVM.getByName`, which checks the reply.
305
+ */
306
+ name?: string;
307
+ }
308
+ export interface GetOptions {
309
+ /**
310
+ * When true, include sandbox env vars in the response (`?include_env=true`).
311
+ * Env is omitted by default — opt-in reads are audited server-side.
312
+ */
313
+ includeEnv?: boolean;
250
314
  }
251
315
  export interface CreateSessionOptions {
252
316
  name?: string;
@@ -389,6 +453,19 @@ export interface SandboxSnapshot {
389
453
  memoryMB?: number;
390
454
  diskGB?: number;
391
455
  }
456
+ /** Options for {@link MicroVM.listSnapshots}: one name-ordered page. */
457
+ export interface ListSnapshotsOptions {
458
+ /** Maximum snapshots to return in this page. */
459
+ limit?: number;
460
+ /** Exclusive name cursor — pass the last name of the previous page. */
461
+ startingAfter?: string;
462
+ }
463
+ /** One page of snapshots from {@link MicroVM.listSnapshots}. */
464
+ export interface SnapshotList {
465
+ snapshots: SandboxSnapshot[];
466
+ /** True when more snapshots remain after this page. */
467
+ hasMore: boolean;
468
+ }
392
469
  /**
393
470
  * Discriminated result from `exposePort`. Branch on `protocol` to access the
394
471
  * fields that are meaningful for the chosen wire protocol — only the raw-TCP
@@ -409,6 +486,10 @@ export type ExposeResult = {
409
486
  };
410
487
  export interface Sandbox {
411
488
  id: string;
489
+ /** Name set at create time, unique per owner. Absent for unnamed sandboxes. */
490
+ name?: string;
491
+ /** Labels set at create time. */
492
+ tags?: Record<string, string>;
412
493
  image: string;
413
494
  status: SandboxStatus;
414
495
  publicURL: string;
@@ -420,6 +501,24 @@ export interface Sandbox {
420
501
  osUser: string;
421
502
  env?: Record<string, string>;
422
503
  networkBlockAll: boolean;
504
+ /**
505
+ * Hostname-egress state on `get` for a sandbox whose allow list names
506
+ * hosts (container runtimes): "active", "held" or "unavailable";
507
+ * "unenforced" when the server's firewall is off and nothing filters the
508
+ * sandbox. Absent otherwise, and on list results.
509
+ */
510
+ egressStatus?: string;
511
+ /** Egress profiles this sandbox references. */
512
+ egressProfiles?: string[];
513
+ /**
514
+ * The generation of each referenced profile that is live on the sandbox,
515
+ * so a caller can see when a profile change has reached it.
516
+ */
517
+ egressProfilesApplied?: EgressProfileRef[];
518
+ /** `"learn"` while the sandbox records its egress; absent otherwise. */
519
+ networkEgressMode?: string;
520
+ /** Method and path rules on the sandbox's egress; absent when it has none. */
521
+ networkEgressRules?: EgressRule[];
423
522
  toolboxEnabled: boolean;
424
523
  sshPublicKey?: string;
425
524
  sshPrivateKey?: string;
@@ -435,13 +534,15 @@ export interface Sandbox {
435
534
  * Container runtime this sandbox is running under. Empty string indicates
436
535
  * a pre-migration row that resolves to the host default at start time.
437
536
  */
438
- runtime: "" | "docker" | "gvisor" | "kata" | "firecracker" | "wasm";
537
+ runtime: "" | "docker" | "gvisor" | "kata" | "firecracker" | "wasm" | "isolate";
439
538
  /** Survival class this sandbox was created with. */
440
539
  durability?: Durability;
441
- /** Resolved WASM module reference when runtime is wasm. */
540
+ /** Resolved WASM module / isolate bundle reference when runtime is wasm or isolate. */
442
541
  moduleRef?: string;
443
542
  /** sha256 hex digest of the resolved WASM module bytes. */
444
543
  moduleDigest?: string;
544
+ /** Isolate-group key this sandbox was created under (`runtime: "isolate"` only). */
545
+ tenantId?: string;
445
546
  /** GPU configuration this sandbox was created with. Absent means no GPU. */
446
547
  gpus?: GPUOptions;
447
548
  }
@@ -463,6 +564,270 @@ export interface NetworkUsage {
463
564
  /** Absent until the netstats poller has produced at least one sample. */
464
565
  lastSampledAt?: string;
465
566
  }
567
+ /** One record from a sandbox's audit log (`sandbox.audit()`). */
568
+ export interface AuditEvent {
569
+ time: string;
570
+ /** "egress" for outbound connections and denials; secret kinds otherwise. */
571
+ kind?: string;
572
+ /** "success", or "failure" for a denial. */
573
+ result: string;
574
+ /** Why it was denied, e.g. "host_not_allowed", "sni_not_allowed". */
575
+ reason?: string;
576
+ /** host:port (or host) the sandbox tried to reach. */
577
+ destination?: string;
578
+ network?: string;
579
+ actor?: string;
580
+ ref?: string;
581
+ eventID?: string;
582
+ incarnationID?: string;
583
+ /** Records lost at this point (a gap record). */
584
+ dropped?: number;
585
+ }
586
+ export interface AuditPage {
587
+ events: AuditEvent[];
588
+ /** Which nodes answered; `partial` is true when some could not. */
589
+ coverage: {
590
+ answered: string[];
591
+ missing: string[];
592
+ partial: boolean;
593
+ };
594
+ /** Pass to `audit({ cursor })` for the next page. */
595
+ nextCursor?: string;
596
+ }
597
+ export interface AuditOptions {
598
+ /** Only this kind, e.g. "egress". */
599
+ kind?: string;
600
+ limit?: number;
601
+ cursor?: string;
602
+ incarnationID?: string;
603
+ }
604
+ /**
605
+ * Would a sandbox created with these egress fields reach `destination`?
606
+ * `destination` is "host", "host:port", "IP" or "IP:port"; a bare host is
607
+ * checked as the web ports.
608
+ */
609
+ export interface NetworkPolicyCheckOptions {
610
+ networkBlockAll?: boolean;
611
+ networkAllowOut?: string[];
612
+ networkDenyOut?: string[];
613
+ destination: string;
614
+ }
615
+ export interface NetworkPolicyCheckResult {
616
+ allowed: boolean;
617
+ /** The entry that decided; "" when the default verdict did. */
618
+ matchedRule: string;
619
+ /** "allow" or "deny": what happens to a destination no entry matches. */
620
+ defaultVerdict: string;
621
+ /** The first allow entry outside this deployment's ceiling, if any. */
622
+ outsideCeiling?: string;
623
+ }
624
+ /**
625
+ * A sandbox's whole egress policy, for `setNetworkPolicy`. It replaces the
626
+ * current policy: a field left out is cleared, so `{}` means open egress.
627
+ * The grammar is the create one (hostnames, `*.` wildcards, `host:port` and
628
+ * CIDRs in the allow list; CIDRs only in the deny list).
629
+ */
630
+ export interface NetworkPolicyOptions {
631
+ networkBlockAll?: boolean;
632
+ networkAllowOut?: string[];
633
+ networkDenyOut?: string[];
634
+ egressProfiles?: string[];
635
+ networkEgressMode?: "enforce" | "learn";
636
+ /**
637
+ * Replaces the method and path rules. Adding an inspect rule to a
638
+ * container sandbox created without one is refused with 409: recreate it
639
+ * with the rule.
640
+ */
641
+ networkEgressRules?: EgressRule[];
642
+ }
643
+ /** The policy a sandbox enforces after `setNetworkPolicy`. */
644
+ export interface NetworkPolicy {
645
+ networkBlockAll: boolean;
646
+ networkAllowOut: string[];
647
+ networkDenyOut: string[];
648
+ egressProfiles: string[];
649
+ /** `"enforce"` or `"learn"`. */
650
+ networkEgressMode: string;
651
+ networkEgressRules: EgressRule[];
652
+ /** Hostname entries in force: inline plus every profile's (at most 1024). */
653
+ effectiveHostnameCount: number;
654
+ /**
655
+ * "active", "held" or "unavailable" for hostname rules on a container;
656
+ * "unenforced" when the server's firewall is off.
657
+ */
658
+ egressStatus?: string;
659
+ }
660
+ /**
661
+ * One method, path or program rule. Rules refine a host the allow list
662
+ * already admits: a request to a ruled host passes when some rule for that
663
+ * host admits its program, method and path, and gets a 403 otherwise. A host
664
+ * no rule names keeps its allow-list decision.
665
+ */
666
+ export interface EgressRule {
667
+ /** An exact name or `*.` wildcard, without a port. */
668
+ host: string;
669
+ /**
670
+ * `[80]` by default, or `[443]` with `inspect`; only 80 and 443, except
671
+ * that a rule with only `binaries` may name any port the allow list opens.
672
+ */
673
+ ports?: number[];
674
+ /** Exact, upper case (`GET`, `POST`); empty allows any. */
675
+ methods?: string[];
676
+ /**
677
+ * Path globs: `*` within one segment, `**` as a whole segment for any
678
+ * number of them. Empty allows any path.
679
+ */
680
+ paths?: string[];
681
+ /**
682
+ * Terminate TLS on 443 with the node's CA so the rule can see requests.
683
+ * The request's Host must then equal the TLS server name.
684
+ */
685
+ inspect?: boolean;
686
+ /**
687
+ * Replace a header on the requests this rule allows with a secret from
688
+ * the sandbox's own `env`, which the sandbox itself only sees as a
689
+ * placeholder. Needs `inspect: true`.
690
+ */
691
+ inject?: EgressInject;
692
+ /**
693
+ * Limit the rule to connections opened by these executables: clean
694
+ * absolute paths inside the sandbox, at most 16. For an interpreter
695
+ * (python, node, a shell) the script it runs counts too, so
696
+ * `/usr/local/bin/pip` works. A rule with only `binaries` decides whole
697
+ * connections, on any port the allow list opens. Runc sandboxes only
698
+ * (docker and containerd); least privilege for trusted tooling, not a
699
+ * security boundary.
700
+ */
701
+ binaries?: string[];
702
+ }
703
+ /**
704
+ * A rule's credential injection. The sandbox's env holds
705
+ * `aerolvm-placeholder:<KEY>` in place of the value, and the egress gateway
706
+ * replaces `header` with the real value on each request the rule allows, so
707
+ * code in the sandbox never holds the secret.
708
+ */
709
+ export interface EgressInject {
710
+ /**
711
+ * The header to replace, such as `Authorization`. It is replaced, never
712
+ * added to a body or URL. Headers that frame or route the request, such as
713
+ * `Host` or `Content-Length`, can't be injected.
714
+ */
715
+ header: string;
716
+ /**
717
+ * `"env:<KEY>"`: a key in the create's `env`, whose value is the whole
718
+ * header value (for example `Bearer ghp_...`). Rotating it means
719
+ * recreating the sandbox.
720
+ */
721
+ secretRef: string;
722
+ }
723
+ /**
724
+ * A named allowlist sandboxes reference through `egressProfiles`. Profiles
725
+ * belong to your account; `generation` goes up by one on every change.
726
+ */
727
+ export interface EgressProfile {
728
+ name: string;
729
+ /** Hostnames, `*.` wildcards, `host:port` entries and CIDRs (at most 512 hostnames). */
730
+ allowOut: string[];
731
+ description?: string;
732
+ generation: number;
733
+ createdAt: string;
734
+ updatedAt: string;
735
+ }
736
+ /** The body of `putEgressProfile`: a full replace. */
737
+ export interface EgressProfileOptions {
738
+ allowOut: string[];
739
+ description?: string;
740
+ }
741
+ export interface ListEgressProfilesOptions {
742
+ cursor?: string;
743
+ limit?: number;
744
+ }
745
+ export interface EgressProfileList {
746
+ profiles: EgressProfile[];
747
+ /** Pass back as `cursor` for the next page; absent on the last one. */
748
+ nextCursor?: string;
749
+ }
750
+ /** Who the caller is, as `microvm.whoami()` reports it. */
751
+ export interface WhoAmIPrincipal {
752
+ /** `"operator"`, `"token"`, `"oidc"`, `"controlplane"`, ... */
753
+ kind: string;
754
+ /** Stable id: `"operator"`, `"tok_…"`, `"oidc:<issuer>:<sub>"`, ... */
755
+ id: string;
756
+ /** Display name; absent when the server has none. */
757
+ name?: string;
758
+ }
759
+ /** The body of `microvm.whoami()`: the caller's own resolved identity. */
760
+ export interface WhoAmI {
761
+ principal: WhoAmIPrincipal;
762
+ /** `"viewer"`, `"runner"`, `"auditor"` or `"admin"`. */
763
+ role: string;
764
+ /** `"*"` for every team, a team name, or absent for a managed account. */
765
+ team?: string;
766
+ /** When the credential stops working (RFC 3339); absent if it never does. */
767
+ expiresAt?: string;
768
+ }
769
+ /** One scoped API token as `microvm.tokens.list()` returns it. Never the secret. */
770
+ export interface APIToken {
771
+ /** Public id (`tok_…`): what `tokens.revoke` takes and audit records name. */
772
+ id: string;
773
+ name: string;
774
+ role: string;
775
+ /** A team name, or `"*"` for every team. */
776
+ team: string;
777
+ createdBy: string;
778
+ createdAt: string;
779
+ expiresAt: string;
780
+ /** Expired tokens no longer work; they stay listed for 30 days. */
781
+ expired: boolean;
782
+ }
783
+ /** `microvm.tokens.create()`'s result: the token plus its secret. */
784
+ export interface CreatedAPIToken extends APIToken {
785
+ /** The secret (`avm_…`), returned this once and never stored by the server. */
786
+ token: string;
787
+ }
788
+ /** The body of `microvm.tokens.create()`. */
789
+ export interface CreateAPITokenOptions {
790
+ /** 1 to 64 of `A-Z a-z 0-9 . _ -`, unique among the team's tokens. */
791
+ name: string;
792
+ /** `"viewer"`, `"runner"`, `"auditor"` or `"admin"`. */
793
+ role: string;
794
+ /** A team name, or `"*"` for every team. `auditor` and `admin` require `"*"`. */
795
+ team: string;
796
+ /** Required lifetime: `"90d"`, `"720h"` or any Go duration. */
797
+ expiresIn: string;
798
+ }
799
+ /** `microvm.tokens.list()`'s result. */
800
+ export interface APITokenList {
801
+ tokens: APIToken[];
802
+ }
803
+ /** One destination a learn-mode sandbox reached. */
804
+ export interface NetworkLearnedEntry {
805
+ host: string;
806
+ /** Connection ports; empty when the name was only resolved. */
807
+ ports: number[];
808
+ firstSeen: string;
809
+ lastSeen: string;
810
+ hits: number;
811
+ }
812
+ /**
813
+ * What a sandbox reached in learn mode, and the allow list that would have
814
+ * allowed it: `suggestedAllowOut` when it fits 64 hostnames, otherwise
815
+ * `suggestedProfile` (a body for `putEgressProfile`).
816
+ */
817
+ export interface NetworkLearned {
818
+ mode: string;
819
+ /** Recording stopped at its cap. */
820
+ truncated: boolean;
821
+ entries: NetworkLearnedEntry[];
822
+ cidrs: string[];
823
+ suggestedAllowOut: string[];
824
+ suggestedProfile?: EgressProfileOptions;
825
+ }
826
+ /** A referenced profile and the generation of it that is live on a sandbox. */
827
+ export interface EgressProfileRef {
828
+ name: string;
829
+ generation: number;
830
+ }
466
831
  export interface SetNetworkLimitsOptions {
467
832
  /** Omit (undefined) to leave unchanged. `0` means unlimited. */
468
833
  networkBytesInLimit?: number;
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@aerol-ai/aerolvm-sdk",
3
- "version": "0.5.4",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/aerol-ai/microvm.git",
8
+ "directory": "sdk/typescript"
9
+ },
5
10
  "type": "module",
6
11
  "main": "./dist/index.js",
7
12
  "types": "./dist/index.d.ts",
@@ -21,8 +26,8 @@
21
26
  "test": "tsc -p tsconfig.test.json --outDir dist-test && node --test dist-test/*.test.js dist-test/internal/*.test.js"
22
27
  },
23
28
  "devDependencies": {
24
- "@types/node": "^25.9.3",
25
- "typescript": "^6.0.3",
26
- "tsx": "^4.22.4"
29
+ "@types/node": "^26.6.6",
30
+ "typescript": "^7.0.2",
31
+ "tsx": "^4.23.15"
27
32
  }
28
33
  }