microsandbox-rb 0.12.0 → 0.13.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.
@@ -288,7 +288,14 @@ module Microsandbox
288
288
  # @param hostname [String, nil] guest hostname
289
289
  # @param labels [Hash, nil] metadata labels
290
290
  # @param scripts [Hash, nil] named scripts to install
291
- # @param entrypoint [Array<String>, nil] image entrypoint override
291
+ # @param entrypoint [Array<String>, nil] image ENTRYPOINT override. An
292
+ # explicit `[]` clears the image's ENTRYPOINT (so `exec_default` runs
293
+ # the CMD alone); `nil` (the default) inherits it.
294
+ # @param cmd [Array<String>, nil] image CMD override used by
295
+ # default-workload execution ({Sandbox#exec_default} et al., runtime
296
+ # v0.6.9). Durable configuration — it does **not** execute anything at
297
+ # create time (`create` boots the VM only). An explicit `[]` clears the
298
+ # image CMD; `nil` (the default) inherits it.
292
299
  # @param ports [Hash, nil] host_port => guest_port TCP publications
293
300
  # @param ports_udp [Hash, nil] host_port => guest_port UDP publications
294
301
  # @param volumes [Hash, nil] guest_path => mount spec. Each value is a host
@@ -322,6 +329,17 @@ module Microsandbox
322
329
  # @param ipv6_pool [String, nil] guest IPv6 address pool CIDR
323
330
  # @param max_connections [Integer, nil] cap on concurrent proxied connections
324
331
  # @param trust_host_cas [Boolean, nil] trust the host's CA bundle for upstream TLS
332
+ # @param rate_limiter [Hash, nil] per-sandbox egress/ingress token-bucket
333
+ # limits (runtime v0.6.9, local backend):
334
+ # `{ egress: { bandwidth: { size: 1_048_576, refill_time_ms: 1000,
335
+ # one_time_burst: 0 }, ops: { size: 1000, refill_time_ms: 1000 } },
336
+ # ingress: { ... } }`. `bandwidth` buckets meter bytes, `ops` buckets
337
+ # meter network frames; an omitted bucket or direction is unlimited.
338
+ # @param vsock [Hash, Array, nil] host sockets exposed on guest-to-host
339
+ # vsock ports (runtime v0.6.9): `{ "/host/api.sock" => 5000 }` (stream
340
+ # sockets), or an Array of
341
+ # `{ host_socket:, port:, socket_type: :stream|:dgram }` Hashes. Guests
342
+ # connect to host CID 2 on the given port; no in-guest proxy required.
325
343
  # @param from_snapshot [String, nil] boot from a snapshot name or digest
326
344
  # instead of an image (mutually exclusive with `image:`)
327
345
  # @param fstype [String, nil] inner filesystem type (e.g. "ext4") when
@@ -343,9 +361,12 @@ module Microsandbox
343
361
  # disk (runtime v0.6.7). An Integer is the managed ext4 upper's size cap
344
362
  # in MiB (default kind, 4 GiB when unset); a Hash picks a kind via the
345
363
  # {RootDisk} factory — `RootDisk.managed(8192)`, `RootDisk.tmpfs(2048)`
346
- # (RAM-backed, pristine on every boot), or
364
+ # (RAM-backed, pristine on every boot),
347
365
  # `RootDisk.disk("./scratch.img", format: "raw", fstype: "ext4")`
348
- # (user-supplied image attached writable).
366
+ # (user-supplied image attached writable), or
367
+ # `RootDisk.flat(8192, clone: :auto)` (a single complete ext4 root disk
368
+ # materialized from the OCI image, runtime v0.6.9 — skips the overlay
369
+ # stack; content-addressed and cached across sandboxes).
349
370
  # @param oci_upper_size [Integer, nil] deprecated alias for
350
371
  # `root_disk: <Integer>` (the managed kind); warns, and conflicts with
351
372
  # `root_disk:`
@@ -418,9 +439,9 @@ module Microsandbox
418
439
  def build_create_opts(image: nil, cpus: nil, max_cpus: nil, memory: nil, max_memory: nil,
419
440
  env: nil, workdir: nil,
420
441
  shell: nil, user: nil, hostname: nil, labels: nil, scripts: nil,
421
- entrypoint: nil, ports: nil, ports_udp: nil, volumes: nil, network: nil,
442
+ entrypoint: nil, cmd: nil, ports: nil, ports_udp: nil, volumes: nil, network: nil,
422
443
  dns: nil, tls: nil, ipv4_pool: nil, ipv6_pool: nil,
423
- max_connections: nil, trust_host_cas: nil,
444
+ max_connections: nil, trust_host_cas: nil, rate_limiter: nil, vsock: nil,
424
445
  patches: nil,
425
446
  from_snapshot: nil, fstype: nil, init: nil, ephemeral: false,
426
447
  log_level: nil, quiet_logs: false, security: nil,
@@ -465,7 +486,11 @@ module Microsandbox
465
486
  opts["env"] = stringify(env) if env
466
487
  opts["labels"] = stringify(labels) if labels
467
488
  opts["scripts"] = stringify(scripts) if scripts
468
- opts["entrypoint"] = Array(entrypoint).map(&:to_s) if entrypoint
489
+ # entrypoint/cmd: an explicit empty Array clears the image's value
490
+ # (blocking the image-config merge), so presence is keyed on the kwarg
491
+ # itself — nil (the default) inherits from the image.
492
+ opts["entrypoint"] = Array(entrypoint).map(&:to_s) unless entrypoint.nil?
493
+ opts["cmd"] = Array(cmd).map(&:to_s) unless cmd.nil?
469
494
  opts["ports"] = intify_ports(ports) if ports
470
495
  opts["ports_udp"] = intify_ports(ports_udp) if ports_udp
471
496
  opts["volumes"] = normalize_volumes(volumes) if volumes
@@ -477,6 +502,8 @@ module Microsandbox
477
502
  opts["ipv6_pool"] = ipv6_pool.to_s if ipv6_pool
478
503
  opts["max_connections"] = Integer(max_connections) if max_connections
479
504
  set_bool(opts, "trust_host_cas", trust_host_cas)
505
+ opts["rate_limiter"] = normalize_rate_limiter(rate_limiter) if rate_limiter
506
+ opts["vsock"] = normalize_vsock(vsock) if vsock
480
507
  opts["log_level"] = log_level.to_s if log_level
481
508
  opts["quiet_logs"] = true if quiet_logs
482
509
  opts["security"] = security.to_s if security
@@ -638,6 +665,7 @@ module Microsandbox
638
665
  # always set (defaulting to "no_restart"); everything else is included only
639
666
  # when provided so an unset option means "leave unchanged".
640
667
  def build_modify_opts(cpus: nil, max_cpus: nil, memory: nil, max_memory: nil,
668
+ root_disk_size: nil,
641
669
  env: nil, remove_env: nil, labels: nil, remove_labels: nil, workdir: nil,
642
670
  secrets: nil, remove_secrets: nil, policy: nil, dry_run: false)
643
671
  opts = {}
@@ -645,6 +673,7 @@ module Microsandbox
645
673
  opts["max_cpus"] = Integer(max_cpus) if max_cpus
646
674
  opts["memory"] = Integer(memory) if memory
647
675
  opts["max_memory"] = Integer(max_memory) if max_memory
676
+ opts["root_disk_size"] = Integer(root_disk_size) if root_disk_size
648
677
  opts["env"] = stringify(env) if env
649
678
  opts["remove_env"] = Array(remove_env).map(&:to_s) if remove_env
650
679
  opts["labels"] = stringify(labels) if labels
@@ -831,6 +860,71 @@ module Microsandbox
831
860
  end
832
861
 
833
862
  # Normalize the `dns:` config Hash for the native layer.
863
+ # Normalize the `rate_limiter:` Hash (v0.6.9) — per-direction
864
+ # bandwidth/ops token buckets — for the native layer. Mirrors the Python
865
+ # SDK's `NetworkRateLimiter`/`RateLimiter`/`TokenBucket` shapes as plain
866
+ # Hashes: `{egress: {bandwidth: {size:, refill_time_ms:, one_time_burst:},
867
+ # ops: {...}}, ingress: {...}}`.
868
+ def normalize_rate_limiter(rl)
869
+ raise ArgumentError, "rate_limiter: must be a Hash" unless rl.is_a?(Hash)
870
+ out = {}
871
+ %i[egress ingress].each do |dir|
872
+ spec = fetch_opt(rl, dir)
873
+ next if spec.nil?
874
+ unless spec.is_a?(Hash)
875
+ raise ArgumentError, "rate_limiter #{dir}: must be a Hash"
876
+ end
877
+ dout = {}
878
+ %i[bandwidth ops].each do |dim|
879
+ bucket = fetch_opt(spec, dim)
880
+ next if bucket.nil?
881
+ unless bucket.is_a?(Hash)
882
+ raise ArgumentError, "rate_limiter #{dir} #{dim}: must be a Hash"
883
+ end
884
+ size = fetch_opt(bucket, :size)
885
+ refill = fetch_opt(bucket, :refill_time_ms)
886
+ if size.nil? || refill.nil?
887
+ raise ArgumentError,
888
+ "rate_limiter #{dir} #{dim}: requires size: and refill_time_ms:"
889
+ end
890
+ bout = {"size" => Integer(size), "refill_time_ms" => Integer(refill)}
891
+ burst = fetch_opt(bucket, :one_time_burst)
892
+ bout["one_time_burst"] = Integer(burst) if burst
893
+ dout[dim.to_s] = bout
894
+ end
895
+ out[dir.to_s] = dout
896
+ end
897
+ out
898
+ end
899
+
900
+ # Normalize the `vsock:` create option (v0.6.9) into the native array of
901
+ # route Hashes. Accepts the Python SDK's two shapes: a Hash of
902
+ # `{ host_socket => port }` (stream sockets), or an Array of
903
+ # `{host_socket:, port:, socket_type: :stream|:dgram}` Hashes.
904
+ def normalize_vsock(vsock)
905
+ if vsock.is_a?(Hash)
906
+ vsock.map { |path, port| {"host_socket" => path.to_s, "port" => Integer(port)} }
907
+ elsif vsock.is_a?(Array)
908
+ vsock.map do |route|
909
+ unless route.is_a?(Hash)
910
+ raise ArgumentError, "vsock: array entries must be Hashes {host_socket:, port:, socket_type:}"
911
+ end
912
+ path = fetch_opt(route, :host_socket)
913
+ port = fetch_opt(route, :port)
914
+ if path.nil? || port.nil?
915
+ raise ArgumentError, "vsock route requires host_socket: and port:"
916
+ end
917
+ out = {"host_socket" => path.to_s, "port" => Integer(port)}
918
+ st = fetch_opt(route, :socket_type)
919
+ out["socket_type"] = st.to_s if st
920
+ out
921
+ end
922
+ else
923
+ raise ArgumentError,
924
+ "vsock: must be a Hash of {host_socket => port} or an Array of route Hashes"
925
+ end
926
+ end
927
+
834
928
  def normalize_dns(dns)
835
929
  raise ArgumentError, "dns: must be a Hash" unless dns.is_a?(Hash)
836
930
  out = {}
@@ -928,9 +1022,29 @@ module Microsandbox
928
1022
  h["format"] = spec["format"].to_s if spec["format"]
929
1023
  h["fstype"] = spec["fstype"].to_s if spec["fstype"]
930
1024
  h
1025
+ when "flat"
1026
+ %w[path format].each do |key|
1027
+ if spec[key]
1028
+ raise ArgumentError, "root_disk #{key}: is only valid for the disk kind"
1029
+ end
1030
+ end
1031
+ h = {"kind" => "flat"}
1032
+ if spec["size_mib"]
1033
+ h["size_mib"] = coerce_root_disk_size(spec["size_mib"], "root_disk size_mib:")
1034
+ end
1035
+ h["fstype"] = spec["fstype"].to_s if spec["fstype"]
1036
+ if spec["clone"]
1037
+ clone = spec["clone"].to_s
1038
+ unless %w[auto copy reflink].include?(clone)
1039
+ raise ArgumentError,
1040
+ "unknown root_disk clone strategy #{clone.inspect} (expected auto/copy/reflink)"
1041
+ end
1042
+ h["clone"] = clone
1043
+ end
1044
+ h
931
1045
  else
932
1046
  raise ArgumentError,
933
- "unknown root_disk kind #{kind.inspect} (expected managed/tmpfs/disk)"
1047
+ "unknown root_disk kind #{kind.inspect} (expected managed/tmpfs/disk/flat)"
934
1048
  end
935
1049
  end
936
1050
 
@@ -1116,6 +1230,21 @@ module Microsandbox
1116
1230
  exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:)))
1117
1231
  end
1118
1232
 
1233
+ # Run the image's resolved OCI `ENTRYPOINT` and `CMD` — the **default
1234
+ # workload** (runtime v0.6.9) — and collect output. {Sandbox.create} is
1235
+ # strictly boot-only, so this is how the image's own command gets
1236
+ # executed; override the durable CMD at create time via `cmd:`.
1237
+ #
1238
+ # Options match {#exec} minus the command itself.
1239
+ # @return [ExecOutput]
1240
+ # @raise [NoDefaultCommandError] when the image's effective entrypoint and
1241
+ # CMD resolve to no executable command
1242
+ def exec_default(cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil)
1243
+ ExecOutput.new(@native.exec_default(
1244
+ exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:)
1245
+ ))
1246
+ end
1247
+
1119
1248
  # Run a command and stream its output as it arrives.
1120
1249
  #
1121
1250
  # Pass +stdin: :pipe+ to feed the process interactively: {ExecHandle#stdin}
@@ -1142,6 +1271,17 @@ module Microsandbox
1142
1271
  exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:, pipe_ok: true)))
1143
1272
  end
1144
1273
 
1274
+ # Run the default workload (see {#exec_default}) and stream its output.
1275
+ # @note Like {#exec_stream}, +timeout:+ is accepted but **not applied** on
1276
+ # the streaming path.
1277
+ # @return [ExecHandle]
1278
+ # @raise [NoDefaultCommandError] when no executable default command resolves
1279
+ def exec_default_stream(cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil)
1280
+ ExecHandle.new(@native.exec_default_stream(
1281
+ exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:, pipe_ok: true)
1282
+ ))
1283
+ end
1284
+
1145
1285
  # Attach an interactive terminal to a command in the sandbox.
1146
1286
  #
1147
1287
  # Puts the **host** terminal into raw mode and forwards keystrokes (and
@@ -1181,6 +1321,32 @@ module Microsandbox
1181
1321
  @native.attach_shell
1182
1322
  end
1183
1323
 
1324
+ # Attach an interactive terminal to the image's resolved OCI `ENTRYPOINT`
1325
+ # and `CMD` — the default workload (runtime v0.6.9). See {#attach} for the
1326
+ # host-TTY requirements and {#exec_default} for default-workload semantics.
1327
+ #
1328
+ # @param cwd [String, nil] working directory
1329
+ # @param user [String, nil] user to run as
1330
+ # @param env [Hash, nil] extra environment variables
1331
+ # @param detach_keys [String, nil] detach sequence (default "ctrl-]")
1332
+ # @param rlimits [Hash, nil] resource limits (see {#exec})
1333
+ # @return [Integer] the workload's exit code (or the code at detach)
1334
+ # @raise [NoDefaultCommandError] when no executable default command resolves
1335
+ def attach_default(cwd: nil, user: nil, env: nil, detach_keys: nil, rlimits: nil)
1336
+ opts = {}
1337
+ opts["cwd"] = cwd.to_s if cwd
1338
+ opts["user"] = user.to_s if user
1339
+ opts["env"] = env.each_with_object({}) { |(k, v), a| a[k.to_s] = v.to_s } if env
1340
+ opts["detach_keys"] = detach_keys.to_s if detach_keys
1341
+ if rlimits
1342
+ opts["rlimits"] = rlimits.map do |resource, limit|
1343
+ soft, hard = limit.is_a?(Array) ? [limit[0], limit[1]] : [limit, limit]
1344
+ [resource.to_s, Integer(soft), Integer(hard)]
1345
+ end
1346
+ end
1347
+ @native.attach_default(opts)
1348
+ end
1349
+
1184
1350
  # Guest filesystem operations.
1185
1351
  # @return [FS]
1186
1352
  def fs
@@ -1241,6 +1407,11 @@ module Microsandbox
1241
1407
  # @param max_cpus [Integer, nil] desired boot-time maximum vCPU ceiling
1242
1408
  # @param memory [Integer, nil] desired effective guest memory in MiB
1243
1409
  # @param max_memory [Integer, nil] desired boot-time maximum memory (MiB)
1410
+ # @param root_disk_size [Integer, nil] desired root-disk size in MiB
1411
+ # (runtime v0.6.9): grows the sandbox-owned managed upper or flat root
1412
+ # disk. Growth-only, applied while the sandbox is stopped —
1413
+ # restart/next-start semantics and backing-specific limits are enforced
1414
+ # by the runtime
1244
1415
  # @param env [Hash, nil] environment variables to set for future execs
1245
1416
  # @param remove_env [Array<String>, nil] environment variable names to remove
1246
1417
  # @param labels [Hash, nil] labels to set
@@ -1406,8 +1577,9 @@ module Microsandbox
1406
1577
  when :pipe
1407
1578
  unless pipe_ok
1408
1579
  raise ArgumentError,
1409
- "stdin: :pipe is only valid for exec_stream/shell_stream — a blocking " \
1410
- "exec/shell cannot expose a writable stdin sink; pass a String to feed bytes"
1580
+ "stdin: :pipe is only valid for the streaming variants (exec_stream/" \
1581
+ "shell_stream/exec_default_stream) — a blocking exec cannot expose a " \
1582
+ "writable stdin sink; pass a String to feed bytes"
1411
1583
  end
1412
1584
  opts["stdin_pipe"] = true
1413
1585
  when Symbol
@@ -115,19 +115,21 @@ module Microsandbox
115
115
  end
116
116
  end
117
117
 
118
- # The result of {Snapshot.verify}. Schema-1 descriptors always record
119
- # integrity, so a returned report is always `:verified` — an integrity
120
- # mismatch raises {SnapshotIntegrityError} instead of returning.
118
+ # The result of {Snapshot.verify}. Since runtime v0.6.9 payload integrity is
119
+ # recorded only when the snapshot was created with `record_integrity: true`:
120
+ # such snapshots report `:verified` (an integrity mismatch raises
121
+ # {SnapshotIntegrityError} instead of returning), snapshots without recorded
122
+ # integrity report `:not_recorded` with `algorithm`/`content_digest` nil.
121
123
  class SnapshotVerifyReport
122
124
  # @return [String] descriptor digest
123
125
  attr_reader :digest
124
126
  # @return [String] artifact directory path
125
127
  attr_reader :path
126
- # @return [Symbol] :verified
128
+ # @return [Symbol] :verified or :not_recorded
127
129
  attr_reader :status
128
- # @return [String] digest algorithm
130
+ # @return [String, nil] digest algorithm (nil when :not_recorded)
129
131
  attr_reader :algorithm
130
- # @return [String] matched content digest
132
+ # @return [String, nil] matched content digest (nil when :not_recorded)
131
133
  attr_reader :content_digest
132
134
 
133
135
  def initialize(data)
@@ -161,8 +163,10 @@ module Microsandbox
161
163
  # is written at `dest_dir/<name>` (default: the snapshots dir)
162
164
  # @param labels [Hash, nil] user labels
163
165
  # @param force [Boolean] overwrite an existing artifact at the destination
164
- # @param record_integrity [Boolean] accepted for compatibility; schema-1
165
- # descriptors always record integrity, so this is a no-op
166
+ # @param record_integrity [Boolean] record persistent payload integrity
167
+ # (a Merkle content digest) in the artifact — opt-in since runtime
168
+ # v0.6.9 because hashing large allocated uppers is expensive; without
169
+ # it {Snapshot.verify} reports `:not_recorded`
166
170
  # @param resumable [Boolean] request a resumable (memory+device) snapshot;
167
171
  # raises {UnsupportedError} until VM pause/resume lands upstream
168
172
  # @return [SnapshotInfo]
@@ -8,12 +8,12 @@ module Microsandbox
8
8
  # Versioning section of the README for the full gem-to-runtime map. Must equal
9
9
  # the native ext's Cargo crate version (`Native.version`), enforced by
10
10
  # spec/unit/version_spec.rb.
11
- VERSION = "0.12.0"
11
+ VERSION = "0.13.0"
12
12
 
13
13
  # The upstream microsandbox runtime release this gem build embeds — the `tag`
14
14
  # pinned on the `microsandbox`/`microsandbox-network` git deps in
15
15
  # ext/microsandbox/Cargo.toml. Exposed at runtime as
16
16
  # {Microsandbox.runtime_version}. spec/unit/version_spec.rb asserts it stays in
17
17
  # sync with the Cargo tag so it can't silently drift out of date.
18
- RUNTIME_VERSION = "v0.6.8"
18
+ RUNTIME_VERSION = "v0.6.9"
19
19
  end
@@ -13,6 +13,7 @@ module Microsandbox
13
13
  @name = data["name"]
14
14
  @path = data["path"]
15
15
  @kind = data["kind"]
16
+ @default = data["default"]
16
17
  @quota_mib = data["quota_mib"]
17
18
  @used_bytes = data["used_bytes"]
18
19
  @capacity_bytes = data["capacity_bytes"]
@@ -28,6 +29,13 @@ module Microsandbox
28
29
  @kind&.to_sym
29
30
  end
30
31
 
32
+ # Whether this is the backend's default volume (runtime v0.6.9, see
33
+ # {Volume.get_default}).
34
+ # @return [Boolean]
35
+ def default?
36
+ !!@default
37
+ end
38
+
31
39
  # @return [Time, nil]
32
40
  def created_at
33
41
  @created_at_ms && Time.at(@created_at_ms / 1000.0)
@@ -154,6 +162,14 @@ module Microsandbox
154
162
  VolumeInfo.new(Native::Volume.get(name.to_s))
155
163
  end
156
164
 
165
+ # The backend's default volume (runtime v0.6.9). Cloud backend only —
166
+ # the full {VolumeInfo#fs} surface works against it; the local backend
167
+ # raises {UnsupportedError} to avoid accidental host access.
168
+ # @return [VolumeInfo]
169
+ def get_default
170
+ VolumeInfo.new(Native::Volume.get_default)
171
+ end
172
+
157
173
  # All volumes.
158
174
  # @return [Array<VolumeInfo>]
159
175
  def list
data/lib/microsandbox.rb CHANGED
@@ -17,6 +17,7 @@ rescue LoadError
17
17
  end
18
18
 
19
19
  require_relative "microsandbox/errors"
20
+ require_relative "microsandbox/backend_info"
20
21
  require_relative "microsandbox/exec_output"
21
22
  require_relative "microsandbox/exec_handle"
22
23
  require_relative "microsandbox/fs"
@@ -172,9 +173,13 @@ module Microsandbox
172
173
 
173
174
  # Install a process-wide default backend (v0.5.8 backend routing). Without a
174
175
  # call to this, operations use a local libkrun backend; the env/profile
175
- # ladder (`MSB_BACKEND`, `MSB_API_URL`+`MSB_API_KEY`, `MSB_PROFILE`,
176
- # `~/.microsandbox/config.json`) is resolved lazily on first use. Call once
177
- # at startup, before any sandbox operations.
176
+ # ladder (`MSB_BACKEND` → `MSB_PROFILE` → `~/.microsandbox/config.json`) is
177
+ # resolved lazily on first use. Since runtime v0.6.9 a bare `MSB_API_KEY`
178
+ # no longer selects the cloud — cloud intent must be explicit via
179
+ # `MSB_BACKEND=cloud` (paired with `MSB_API_URL`/`MSB_API_KEY`), a cloud
180
+ # profile, or this method; invalid cloud config raises
181
+ # {InvalidConfigError} instead of falling back to local. Call once at
182
+ # startup, before any sandbox operations.
178
183
  #
179
184
  # @param kind ["local","cloud", Symbol] backend kind
180
185
  # @param url [String, nil] cloud control-plane URL (cloud, unless `profile:`)
@@ -216,6 +221,14 @@ module Microsandbox
216
221
  Native.default_backend_kind.to_sym
217
222
  end
218
223
 
224
+ # Secret-safe description of the active default backend (runtime v0.6.9).
225
+ # Like {default_backend_kind}, the first call freezes ambient env/profile
226
+ # resolution for the process. The API key is never included.
227
+ # @return [BackendInfo]
228
+ def default_backend_info
229
+ BackendInfo.new(Native.default_backend_info)
230
+ end
231
+
219
232
  # Latest resource-usage snapshot for every running sandbox, keyed by name.
220
233
  # Mirrors the official `all_sandbox_metrics`/`allSandboxMetrics` helpers.
221
234
  # @return [Hash{String => Metrics}]
data/sig/microsandbox.rbs CHANGED
@@ -27,6 +27,7 @@ module Microsandbox
27
27
  def self.set_default_backend: (String | Symbol kind, ?url: String?, ?api_key: String?, ?profile: String?) -> void
28
28
  def self.with_backend: [T] (String | Symbol kind, ?url: String?, ?api_key: String?, ?profile: String?) { () -> T } -> T
29
29
  def self.default_backend_kind: () -> Symbol
30
+ def self.default_backend_info: () -> BackendInfo
30
31
 
31
32
  class Error < StandardError
32
33
  def self.code: () -> String
@@ -40,6 +41,7 @@ module Microsandbox
40
41
  class SandboxStillRunningError < Error end
41
42
  class ExecTimeoutError < Error end
42
43
  class ExecFailedError < Error end
44
+ class NoDefaultCommandError < Error end
43
45
  class FilesystemError < Error end
44
46
  class PathNotFoundError < Error end
45
47
  class VolumeNotFoundError < Error end
@@ -180,6 +182,7 @@ module Microsandbox
180
182
  def ping: () -> PingResult
181
183
  def touch: () -> TouchResult
182
184
  def modify: (?cpus: Integer?, ?max_cpus: Integer?, ?memory: Integer?, ?max_memory: Integer?,
185
+ ?root_disk_size: Integer?,
183
186
  ?env: Hash[untyped, untyped]?, ?remove_env: Array[String]?,
184
187
  ?labels: Hash[untyped, untyped]?, ?remove_labels: Array[String]?,
185
188
  ?workdir: String?, ?secrets: Hash[untyped, untyped]?,
@@ -245,11 +248,14 @@ module Microsandbox
245
248
  ?env: Hash[untyped, untyped]?, ?workdir: String?, ?shell: String?,
246
249
  ?user: String?, ?hostname: String?, ?labels: Hash[untyped, untyped]?,
247
250
  ?scripts: Hash[untyped, untyped]?, ?entrypoint: Array[String]?,
251
+ ?cmd: Array[String]?,
248
252
  ?ports: Hash[untyped, untyped]?, ?ports_udp: Hash[untyped, untyped]?,
249
253
  ?volumes: Hash[untyped, untyped]?, ?network: untyped?,
250
254
  ?dns: Hash[untyped, untyped]?, ?tls: Hash[untyped, untyped]?,
251
255
  ?ipv4_pool: String?, ?ipv6_pool: String?,
252
256
  ?max_connections: Integer?, ?trust_host_cas: bool?,
257
+ ?rate_limiter: Hash[untyped, untyped]?,
258
+ ?vsock: (Hash[untyped, untyped] | Array[Hash[untyped, untyped]])?,
253
259
  ?patches: Array[Hash[untyped, untyped]]?, ?from_snapshot: String?,
254
260
  ?fstype: String?, ?init: (String | Symbol | Hash[untyped, untyped])?, ?ephemeral: bool,
255
261
  ?log_level: (String | Symbol)?, ?quiet_logs: bool, ?security: (String | Symbol)?,
@@ -282,16 +288,25 @@ module Microsandbox
282
288
  ?rlimits: Hash[untyped, untyped]?) -> ExecHandle
283
289
  def shell_stream: (String script, ?cwd: String?, ?user: String?, ?env: Hash[untyped, untyped]?,
284
290
  ?timeout: Numeric?, ?tty: bool, ?stdin: (String | :pipe | :null)?, ?rlimits: Hash[untyped, untyped]?) -> ExecHandle
291
+ def exec_default: (?cwd: String?, ?user: String?, ?env: Hash[untyped, untyped]?,
292
+ ?timeout: Numeric?, ?tty: bool, ?stdin: (String | :null)?,
293
+ ?rlimits: Hash[untyped, untyped]?) -> ExecOutput
294
+ def exec_default_stream: (?cwd: String?, ?user: String?, ?env: Hash[untyped, untyped]?,
295
+ ?timeout: Numeric?, ?tty: bool, ?stdin: (String | :pipe | :null)?,
296
+ ?rlimits: Hash[untyped, untyped]?) -> ExecHandle
285
297
  def attach: (String command, ?Array[String] args, ?cwd: String?, ?user: String?,
286
298
  ?env: Hash[untyped, untyped]?, ?detach_keys: String?,
287
299
  ?rlimits: Hash[untyped, untyped]?) -> Integer
288
300
  def attach_shell: () -> Integer
301
+ def attach_default: (?cwd: String?, ?user: String?, ?env: Hash[untyped, untyped]?,
302
+ ?detach_keys: String?, ?rlimits: Hash[untyped, untyped]?) -> Integer
289
303
  def fs: () -> FS
290
304
  def ssh: () -> SshOps
291
305
  def metrics: () -> Metrics
292
306
  def ping: () -> PingResult
293
307
  def touch: () -> TouchResult
294
308
  def modify: (?cpus: Integer?, ?max_cpus: Integer?, ?memory: Integer?, ?max_memory: Integer?,
309
+ ?root_disk_size: Integer?,
295
310
  ?env: Hash[untyped, untyped]?, ?remove_env: Array[String]?,
296
311
  ?labels: Hash[untyped, untyped]?, ?remove_labels: Array[String]?,
297
312
  ?workdir: String?, ?secrets: Hash[untyped, untyped]?,
@@ -412,6 +427,7 @@ module Microsandbox
412
427
  def name: () -> String
413
428
  def path: () -> String?
414
429
  def kind: () -> Symbol?
430
+ def default?: () -> bool
415
431
  def quota_mib: () -> Integer?
416
432
  def used_bytes: () -> Integer?
417
433
  def capacity_bytes: () -> Integer?
@@ -441,6 +457,7 @@ module Microsandbox
441
457
  def self.create: (String name, ?kind: String, ?size_mib: Integer?, ?quota_mib: Integer?,
442
458
  ?labels: Hash[untyped, untyped]?) -> VolumeInfo
443
459
  def self.get: (String name) -> VolumeInfo
460
+ def self.get_default: () -> VolumeInfo
444
461
  def self.list: () -> Array[VolumeInfo]
445
462
  def self.remove: (String name) -> nil
446
463
  def self.fs: (String name) -> VolumeFs
@@ -475,8 +492,8 @@ module Microsandbox
475
492
  def digest: () -> String
476
493
  def path: () -> String
477
494
  def status: () -> Symbol
478
- def algorithm: () -> String
479
- def content_digest: () -> String
495
+ def algorithm: () -> String?
496
+ def content_digest: () -> String?
480
497
  def verified?: () -> bool
481
498
  end
482
499
 
@@ -531,6 +548,16 @@ module Microsandbox
531
548
  def self.managed: (?Integer? size_mib) -> Hash[String, untyped]
532
549
  def self.tmpfs: (?Integer? size_mib) -> Hash[String, untyped]
533
550
  def self.disk: (String path, ?format: (String | Symbol)?, ?fstype: String?) -> Hash[String, untyped]
551
+ def self.flat: (?Integer? size_mib, ?fstype: String?, ?clone: (String | Symbol)?) -> Hash[String, untyped]
552
+ end
553
+
554
+ class BackendInfo
555
+ def kind: () -> Symbol
556
+ def api_url: () -> String?
557
+ def source: () -> Symbol
558
+ def profile: () -> String?
559
+ def local?: () -> bool
560
+ def cloud?: () -> bool
534
561
  end
535
562
 
536
563
  class NetworkPolicy
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: microsandbox-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ya-luotao
@@ -61,6 +61,7 @@ files:
61
61
  - ext/microsandbox/src/volume.rs
62
62
  - lib/microsandbox.rb
63
63
  - lib/microsandbox/agent.rb
64
+ - lib/microsandbox/backend_info.rb
64
65
  - lib/microsandbox/errors.rb
65
66
  - lib/microsandbox/exec_handle.rb
66
67
  - lib/microsandbox/exec_output.rb