microsandbox-rb 0.13.0 → 0.14.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f062583f2c7fd83962ad03246dfbf6ad8c86be39756f75cf61c7c4a5e96d09e
4
- data.tar.gz: 23cc401545bb10273d470af66f182f06e9c0be6956ccace5dc6d2b43de93c92d
3
+ metadata.gz: 5347e3ff3b98575cd98ee948fce387efc1bde98da5069fb808ed62e8d065a066
4
+ data.tar.gz: 4c143ec287857130709632f19d124c56488fd22837215a85e61e7c52da609884
5
5
  SHA512:
6
- metadata.gz: b95c8b95460e7fdf03d95241e4e8d3c028a9656e00aeaefa7df28f387c7caaddecc767fae14bb82871d55074f7b24b841d739497cfbd430559c4cf9e8427214c
7
- data.tar.gz: f780f6378522e1296eae3914687f7b77e534068f7c30b194f56603234920fff951dde5fade2201e5b94dd04ca3dc9a356f287c51339c694477aa6ff56ca90dfc
6
+ metadata.gz: f4d810f63d588b685b0393e93d637fa28554bdbfbf34a09b0b5d0ca45a4962acd9ac9991e0e6bd8157e5ef31b9cf4321d6dd20a703ad7d251099b1d4a951f346
7
+ data.tar.gz: f89447fa04f1bd6112199a7286101c26e1c2bed28d66d08816d171298f357ee412474207b60782b8592838f5191914657adafa3b89745dec9600552cb0507e1b
data/CHANGELOG.md CHANGED
@@ -6,6 +6,74 @@ All notable changes to this gem are documented here. The format is based on
6
6
  microsandbox runtime it embeds; each release notes the upstream runtime tag it
7
7
  wraps, and the README's Versioning section keeps the full gem→runtime map.
8
8
 
9
+ ## [0.14.0] - 2026-08-24
10
+
11
+ Runtime tag unchanged — still upstream **`v0.6.9`**.
12
+
13
+ ### Added
14
+
15
+ - **Companion gem `microsandbox-rb-binaries`** (source in `binaries/`): the
16
+ prebuilt `msb` microVM runtime and the `libkrunfw` firmware, shipped as one
17
+ gem per platform (`arm64-darwin`, `x86_64-linux-gnu`, `aarch64-linux-gnu`;
18
+ the Linux binaries are glibc-linked, hence `required_rubygems_version >=
19
+ 3.3.11`). Install it alongside `microsandbox-rb` at the same version and your
20
+ bundle carries the runtime instead of downloading it into `~/.microsandbox`
21
+ on first use. There is no dependency edge in either direction — RubyGems has
22
+ no optional dependencies, and cloud-only users should not be made to fetch
23
+ ~50 MB of binaries. Motivated by upstream
24
+ [superradcompany/microsandbox#1305](https://github.com/superradcompany/microsandbox/issues/1305).
25
+ - **A new tier in the runtime resolver.** `require "microsandbox"` activates the
26
+ companion gem (if installed at the same version and built for the same
27
+ upstream runtime) and hands
28
+ its `msb` to the core's set-once SDK slot; the firmware is found by the
29
+ runtime's `../lib` adjacency, exactly as in the Python/Node SDKs. The
30
+ effective order is now `MSB_PATH` → `microsandbox-rb-binaries` → config file →
31
+ `~/.microsandbox/bin/msb` → `msb` on `PATH`, and
32
+ `Microsandbox.runtime_path` reports the winner. Activation never raises: a
33
+ missing gem is silent, and a gem built for a *different* runtime is reported
34
+ with a warning and skipped rather than handed to the core.
35
+ - **Lock-step guard for the new gem.** `Microsandbox::Binaries::VERSION` must
36
+ equal `Microsandbox::VERSION` and `Microsandbox::Binaries::RUNTIME_VERSION`
37
+ must equal `Microsandbox::RUNTIME_VERSION`; `spec/unit/version_spec.rb`
38
+ asserts both, so a stale companion can't ship.
39
+ - **Vendoring/build pipeline** for the companion gem:
40
+ `rake -C binaries vendor[<platform>]` downloads the upstream release bundle and
41
+ verifies every file against that release's `checksums.sha256` as committed
42
+ in `binaries/checksums/<tag>.sha256` (fail-closed — a missing entry, a digest
43
+ mismatch, an unexpected file, or a live release file that disagrees with the
44
+ committed one aborts), `rake -C binaries build[<platform>]` re-verifies the staged tree
45
+ against its manifest before packaging, `rake -C binaries verify` runs the
46
+ vendored `msb` on the host, and `vendor:all` covers every platform from any
47
+ host. CI builds all three platform gems on every run, and `release.yml`
48
+ builds and publishes them on each version tag in a separate
49
+ `publish-binaries` job that runs after the SDK gem is live (a companion-gem
50
+ failure is its own red job and never blocks the SDK release; re-runs are
51
+ idempotent). The companion gem has its own RubyGems trusted-publisher entry.
52
+
53
+ ### Changed
54
+
55
+ - **The SDK gem is now SDK-only: nothing is provisioned at build or install
56
+ time.** The core crate's default `prebuilt` feature — whose `build.rs`
57
+ downloaded the *host* runtime into `~/.microsandbox` while compiling the
58
+ extension — is off (`default-features = false`, features `keyring`, `net`,
59
+ `ssh`, matching the official SDKs). The *guest* agent `agentd` is still
60
+ embedded into the extension at build time, via a direct
61
+ `microsandbox-runtime` dependency with just the `prebuilt` sub-feature that
62
+ fetches it. Host runtime provisioning is now the companion gem's job, with the
63
+ first-use download as fallback.
64
+ - **`Microsandbox.runtime_path=` is a no-op when the companion gem is active** —
65
+ it targets the same set-once slot the gem already claimed at load time. Use
66
+ the `MSB_PATH` environment variable to override a bundled runtime.
67
+ - **`Microsandbox.ensure_runtime!` skips the installer entirely** when the
68
+ resolved runtime is the companion gem's `msb`: those binaries are already the
69
+ matching version, so nothing is downloaded or touched in `~/.microsandbox`.
70
+ Without the gem, behaviour is unchanged — the version-correcting first-use
71
+ download into `~/.microsandbox` remains the lowest tier, still opt-out-able
72
+ with `MICROSANDBOX_NO_AUTO_INSTALL`.
73
+ - `Gemfile` now uses `gemspec glob: "{,*}.gemspec"` so Bundler resolves only the
74
+ root gemspec; its default glob would also evaluate the nested
75
+ `binaries/microsandbox-rb-binaries.gemspec` in every `bundle exec` process.
76
+
9
77
  ## [0.13.0] - 2026-08-18
10
78
 
11
79
  Adopts upstream runtime **`v0.6.8` → `v0.6.9`**.
@@ -963,7 +1031,8 @@ microsandbox runtime, aligned with the official Python/Node/Go SDKs.
963
1031
  core crate has Apple-native deps). Until precompiled gems are published,
964
1032
  installing from source requires a Rust toolchain (stable >= 1.91).
965
1033
 
966
- [Unreleased]: https://github.com/ya-luotao/microsandbox-rb/compare/v0.9.0...HEAD
1034
+ [Unreleased]: https://github.com/ya-luotao/microsandbox-rb/compare/v0.14.0...HEAD
1035
+ [0.14.0]: https://github.com/ya-luotao/microsandbox-rb/compare/v0.13.0...v0.14.0
967
1036
  [0.9.0]: https://github.com/ya-luotao/microsandbox-rb/compare/v0.8.2...v0.9.0
968
1037
  [0.8.2]: https://github.com/ya-luotao/microsandbox-rb/compare/v0.8.1...v0.8.2
969
1038
  [0.8.1]: https://github.com/ya-luotao/microsandbox-rb/compare/v0.8.0...v0.8.1
data/Cargo.lock CHANGED
@@ -362,7 +362,7 @@ dependencies = [
362
362
  "nom",
363
363
  "num-traits",
364
364
  "rusticata-macros",
365
- "thiserror 2.0.18",
365
+ "thiserror",
366
366
  "time",
367
367
  ]
368
368
 
@@ -918,12 +918,6 @@ dependencies = [
918
918
  "shlex 2.0.1",
919
919
  ]
920
920
 
921
- [[package]]
922
- name = "cesu8"
923
- version = "1.1.0"
924
- source = "registry+https://github.com/rust-lang/crates.io-index"
925
- checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
926
-
927
921
  [[package]]
928
922
  name = "cexpr"
929
923
  version = "0.6.0"
@@ -1566,7 +1560,7 @@ version = "1.0.0"
1566
1560
  source = "registry+https://github.com/rust-lang/crates.io-index"
1567
1561
  checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
1568
1562
  dependencies = [
1569
- "thiserror 2.0.18",
1563
+ "thiserror",
1570
1564
  ]
1571
1565
 
1572
1566
  [[package]]
@@ -2424,10 +2418,10 @@ dependencies = [
2424
2418
  "hickory-proto",
2425
2419
  "idna",
2426
2420
  "ipnet",
2427
- "jni 0.22.4",
2421
+ "jni",
2428
2422
  "rand 0.10.2",
2429
2423
  "rustls",
2430
- "thiserror 2.0.18",
2424
+ "thiserror",
2431
2425
  "tinyvec",
2432
2426
  "tokio",
2433
2427
  "tokio-rustls",
@@ -2444,11 +2438,11 @@ dependencies = [
2444
2438
  "data-encoding",
2445
2439
  "idna",
2446
2440
  "ipnet",
2447
- "jni 0.22.4",
2441
+ "jni",
2448
2442
  "once_cell",
2449
2443
  "rand 0.10.2",
2450
2444
  "ring",
2451
- "thiserror 2.0.18",
2445
+ "thiserror",
2452
2446
  "tinyvec",
2453
2447
  "tracing",
2454
2448
  "url",
@@ -2883,22 +2877,6 @@ version = "1.0.18"
2883
2877
  source = "registry+https://github.com/rust-lang/crates.io-index"
2884
2878
  checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
2885
2879
 
2886
- [[package]]
2887
- name = "jni"
2888
- version = "0.21.1"
2889
- source = "registry+https://github.com/rust-lang/crates.io-index"
2890
- checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
2891
- dependencies = [
2892
- "cesu8",
2893
- "cfg-if",
2894
- "combine",
2895
- "jni-sys 0.3.1",
2896
- "log",
2897
- "thiserror 1.0.69",
2898
- "walkdir",
2899
- "windows-sys 0.45.0",
2900
- ]
2901
-
2902
2880
  [[package]]
2903
2881
  name = "jni"
2904
2882
  version = "0.22.4"
@@ -2908,10 +2886,10 @@ dependencies = [
2908
2886
  "cfg-if",
2909
2887
  "combine",
2910
2888
  "jni-macros",
2911
- "jni-sys 0.4.1",
2889
+ "jni-sys",
2912
2890
  "log",
2913
2891
  "simd_cesu8",
2914
- "thiserror 2.0.18",
2892
+ "thiserror",
2915
2893
  "walkdir",
2916
2894
  "windows-link",
2917
2895
  ]
@@ -2929,15 +2907,6 @@ dependencies = [
2929
2907
  "syn 2.0.118",
2930
2908
  ]
2931
2909
 
2932
- [[package]]
2933
- name = "jni-sys"
2934
- version = "0.3.1"
2935
- source = "registry+https://github.com/rust-lang/crates.io-index"
2936
- checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
2937
- dependencies = [
2938
- "jni-sys 0.4.1",
2939
- ]
2940
-
2941
2910
  [[package]]
2942
2911
  name = "jni-sys"
2943
2912
  version = "0.4.1"
@@ -3387,7 +3356,7 @@ dependencies = [
3387
3356
  "russh",
3388
3357
  "russh-sftp",
3389
3358
  "rustls",
3390
- "rustls-platform-verifier 0.7.0",
3359
+ "rustls-platform-verifier",
3391
3360
  "scopeguard",
3392
3361
  "sea-orm",
3393
3362
  "serde",
@@ -3395,7 +3364,7 @@ dependencies = [
3395
3364
  "sha2 0.11.0",
3396
3365
  "tar",
3397
3366
  "tempfile",
3398
- "thiserror 2.0.18",
3367
+ "thiserror",
3399
3368
  "tokio",
3400
3369
  "tokio-tungstenite",
3401
3370
  "tracing",
@@ -3414,7 +3383,7 @@ dependencies = [
3414
3383
  "ciborium",
3415
3384
  "microsandbox-protocol",
3416
3385
  "serde",
3417
- "thiserror 2.0.18",
3386
+ "thiserror",
3418
3387
  "tokio",
3419
3388
  "tracing",
3420
3389
  ]
@@ -3464,7 +3433,7 @@ dependencies = [
3464
3433
  "serde_json",
3465
3434
  "sha2 0.11.0",
3466
3435
  "tar",
3467
- "thiserror 2.0.18",
3436
+ "thiserror",
3468
3437
  "tokio",
3469
3438
  "tokio-util",
3470
3439
  "tracing",
@@ -3478,7 +3447,7 @@ source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.9#664552c
3478
3447
  dependencies = [
3479
3448
  "chrono",
3480
3449
  "libc",
3481
- "thiserror 2.0.18",
3450
+ "thiserror",
3482
3451
  "tracing",
3483
3452
  "windows-sys 0.61.2",
3484
3453
  ]
@@ -3527,7 +3496,7 @@ dependencies = [
3527
3496
  "smoltcp",
3528
3497
  "socket2",
3529
3498
  "system-configuration",
3530
- "thiserror 2.0.18",
3499
+ "thiserror",
3531
3500
  "time",
3532
3501
  "tokio",
3533
3502
  "tokio-rustls",
@@ -3547,7 +3516,7 @@ dependencies = [
3547
3516
  "serde",
3548
3517
  "serde_bytes",
3549
3518
  "strum 0.28.0",
3550
- "thiserror 2.0.18",
3519
+ "thiserror",
3551
3520
  "tokio",
3552
3521
  ]
3553
3522
 
@@ -3579,7 +3548,7 @@ dependencies = [
3579
3548
  "serde_json",
3580
3549
  "sha2 0.11.0",
3581
3550
  "tempfile",
3582
- "thiserror 2.0.18",
3551
+ "thiserror",
3583
3552
  "tokio",
3584
3553
  "tracing",
3585
3554
  "windows-sys 0.61.2",
@@ -3596,7 +3565,7 @@ dependencies = [
3596
3565
  "serde",
3597
3566
  "serde_json",
3598
3567
  "sha2 0.11.0",
3599
- "thiserror 2.0.18",
3568
+ "thiserror",
3600
3569
  "zeroize",
3601
3570
  ]
3602
3571
 
@@ -3628,7 +3597,7 @@ dependencies = [
3628
3597
 
3629
3598
  [[package]]
3630
3599
  name = "microsandbox_rb"
3631
- version = "0.13.0"
3600
+ version = "0.14.0"
3632
3601
  dependencies = [
3633
3602
  "chrono",
3634
3603
  "futures",
@@ -3636,6 +3605,7 @@ dependencies = [
3636
3605
  "magnus",
3637
3606
  "microsandbox",
3638
3607
  "microsandbox-network",
3608
+ "microsandbox-runtime",
3639
3609
  "rb-sys",
3640
3610
  "serde_json",
3641
3611
  "tokio",
@@ -3726,7 +3696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3726
3696
  checksum = "4c31dfbf17e640f6d661ae17e098b87c019821fe3eb4aba0e9eeada4ba678096"
3727
3697
  dependencies = [
3728
3698
  "libc",
3729
- "thiserror 2.0.18",
3699
+ "thiserror",
3730
3700
  "winapi",
3731
3701
  ]
3732
3702
 
@@ -4074,7 +4044,7 @@ dependencies = [
4074
4044
  "serde",
4075
4045
  "serde_json",
4076
4046
  "sha2 0.11.0",
4077
- "thiserror 2.0.18",
4047
+ "thiserror",
4078
4048
  "tokio",
4079
4049
  "tracing",
4080
4050
  "unicase",
@@ -4094,7 +4064,7 @@ dependencies = [
4094
4064
  "serde_json",
4095
4065
  "strum 0.27.2",
4096
4066
  "strum_macros 0.27.2",
4097
- "thiserror 2.0.18",
4067
+ "thiserror",
4098
4068
  ]
4099
4069
 
4100
4070
  [[package]]
@@ -4111,7 +4081,7 @@ dependencies = [
4111
4081
  "serde_json",
4112
4082
  "strum 0.27.2",
4113
4083
  "strum_macros 0.27.2",
4114
- "thiserror 2.0.18",
4084
+ "thiserror",
4115
4085
  ]
4116
4086
 
4117
4087
  [[package]]
@@ -4270,7 +4240,7 @@ dependencies = [
4270
4240
  "log",
4271
4241
  "rand 0.10.2",
4272
4242
  "sha2 0.11.0",
4273
- "thiserror 2.0.18",
4243
+ "thiserror",
4274
4244
  "tokio",
4275
4245
  "windows",
4276
4246
  "windows-strings",
@@ -4629,7 +4599,7 @@ dependencies = [
4629
4599
  "rustc-hash",
4630
4600
  "rustls",
4631
4601
  "socket2",
4632
- "thiserror 2.0.18",
4602
+ "thiserror",
4633
4603
  "tokio",
4634
4604
  "tracing",
4635
4605
  "web-time",
@@ -4652,7 +4622,7 @@ dependencies = [
4652
4622
  "rustls",
4653
4623
  "rustls-pki-types",
4654
4624
  "slab",
4655
- "thiserror 2.0.18",
4625
+ "thiserror",
4656
4626
  "tinyvec",
4657
4627
  "tracing",
4658
4628
  "web-time",
@@ -4865,7 +4835,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
4865
4835
  dependencies = [
4866
4836
  "getrandom 0.2.17",
4867
4837
  "libredox",
4868
- "thiserror 2.0.18",
4838
+ "thiserror",
4869
4839
  ]
4870
4840
 
4871
4841
  [[package]]
@@ -4944,7 +4914,7 @@ dependencies = [
4944
4914
  "quinn",
4945
4915
  "rustls",
4946
4916
  "rustls-pki-types",
4947
- "rustls-platform-verifier 0.7.0",
4917
+ "rustls-platform-verifier",
4948
4918
  "serde",
4949
4919
  "serde_json",
4950
4920
  "serde_urlencoded",
@@ -5105,7 +5075,7 @@ dependencies = [
5105
5075
  "ssh-encoding",
5106
5076
  "ssh-key",
5107
5077
  "subtle",
5108
- "thiserror 2.0.18",
5078
+ "thiserror",
5109
5079
  "tokio",
5110
5080
  "typenum",
5111
5081
  "universal-hash",
@@ -5138,7 +5108,7 @@ dependencies = [
5138
5108
  "log",
5139
5109
  "serde",
5140
5110
  "serde_bytes",
5141
- "thiserror 2.0.18",
5111
+ "thiserror",
5142
5112
  "tokio",
5143
5113
  "tokio-util",
5144
5114
  "wasm-bindgen-futures",
@@ -5248,27 +5218,6 @@ dependencies = [
5248
5218
  "zeroize",
5249
5219
  ]
5250
5220
 
5251
- [[package]]
5252
- name = "rustls-platform-verifier"
5253
- version = "0.6.2"
5254
- source = "registry+https://github.com/rust-lang/crates.io-index"
5255
- checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
5256
- dependencies = [
5257
- "core-foundation 0.10.1",
5258
- "core-foundation-sys",
5259
- "jni 0.21.1",
5260
- "log",
5261
- "once_cell",
5262
- "rustls",
5263
- "rustls-native-certs",
5264
- "rustls-platform-verifier-android",
5265
- "rustls-webpki",
5266
- "security-framework 3.7.0",
5267
- "security-framework-sys",
5268
- "webpki-root-certs",
5269
- "windows-sys 0.52.0",
5270
- ]
5271
-
5272
5221
  [[package]]
5273
5222
  name = "rustls-platform-verifier"
5274
5223
  version = "0.7.0"
@@ -5277,7 +5226,7 @@ checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
5277
5226
  dependencies = [
5278
5227
  "core-foundation 0.10.1",
5279
5228
  "core-foundation-sys",
5280
- "jni 0.22.4",
5229
+ "jni",
5281
5230
  "log",
5282
5231
  "once_cell",
5283
5232
  "rustls",
@@ -5408,7 +5357,7 @@ dependencies = [
5408
5357
  "sqlx",
5409
5358
  "sqlx-core",
5410
5359
  "strum 0.28.0",
5411
- "thiserror 2.0.18",
5360
+ "thiserror",
5412
5361
  "time",
5413
5362
  "tracing",
5414
5363
  "url",
@@ -5424,7 +5373,7 @@ checksum = "4c800d9db902534d7d01728faf98e33d13c1d57bb8c57d8e4c518309172bddda"
5424
5373
  dependencies = [
5425
5374
  "arrow",
5426
5375
  "sea-query",
5427
- "thiserror 2.0.18",
5376
+ "thiserror",
5428
5377
  ]
5429
5378
 
5430
5379
  [[package]]
@@ -5501,7 +5450,7 @@ dependencies = [
5501
5450
  "proc-macro2",
5502
5451
  "quote",
5503
5452
  "syn 2.0.118",
5504
- "thiserror 2.0.18",
5453
+ "thiserror",
5505
5454
  ]
5506
5455
 
5507
5456
  [[package]]
@@ -5983,7 +5932,7 @@ dependencies = [
5983
5932
  "serde_json",
5984
5933
  "sha2 0.10.9",
5985
5934
  "smallvec",
5986
- "thiserror 2.0.18",
5935
+ "thiserror",
5987
5936
  "time",
5988
5937
  "tokio",
5989
5938
  "tokio-stream",
@@ -6055,7 +6004,7 @@ dependencies = [
6055
6004
  "sha1 0.11.0",
6056
6005
  "sha2 0.11.0",
6057
6006
  "sqlx-core",
6058
- "thiserror 2.0.18",
6007
+ "thiserror",
6059
6008
  "time",
6060
6009
  "tracing",
6061
6010
  "uuid",
@@ -6093,7 +6042,7 @@ dependencies = [
6093
6042
  "smallvec",
6094
6043
  "sqlx-core",
6095
6044
  "stringprep",
6096
- "thiserror 2.0.18",
6045
+ "thiserror",
6097
6046
  "time",
6098
6047
  "tracing",
6099
6048
  "uuid",
@@ -6120,7 +6069,7 @@ dependencies = [
6120
6069
  "percent-encoding",
6121
6070
  "serde",
6122
6071
  "sqlx-core",
6123
- "thiserror 2.0.18",
6072
+ "thiserror",
6124
6073
  "time",
6125
6074
  "tracing",
6126
6075
  "url",
@@ -6353,33 +6302,13 @@ dependencies = [
6353
6302
  "windows-sys 0.52.0",
6354
6303
  ]
6355
6304
 
6356
- [[package]]
6357
- name = "thiserror"
6358
- version = "1.0.69"
6359
- source = "registry+https://github.com/rust-lang/crates.io-index"
6360
- checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
6361
- dependencies = [
6362
- "thiserror-impl 1.0.69",
6363
- ]
6364
-
6365
6305
  [[package]]
6366
6306
  name = "thiserror"
6367
6307
  version = "2.0.18"
6368
6308
  source = "registry+https://github.com/rust-lang/crates.io-index"
6369
6309
  checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
6370
6310
  dependencies = [
6371
- "thiserror-impl 2.0.18",
6372
- ]
6373
-
6374
- [[package]]
6375
- name = "thiserror-impl"
6376
- version = "1.0.69"
6377
- source = "registry+https://github.com/rust-lang/crates.io-index"
6378
- checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
6379
- dependencies = [
6380
- "proc-macro2",
6381
- "quote",
6382
- "syn 2.0.118",
6311
+ "thiserror-impl",
6383
6312
  ]
6384
6313
 
6385
6314
  [[package]]
@@ -6687,7 +6616,7 @@ dependencies = [
6687
6616
  "rustls",
6688
6617
  "rustls-pki-types",
6689
6618
  "sha1 0.11.0",
6690
- "thiserror 2.0.18",
6619
+ "thiserror",
6691
6620
  ]
6692
6621
 
6693
6622
  [[package]]
@@ -6802,17 +6731,17 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
6802
6731
 
6803
6732
  [[package]]
6804
6733
  name = "ureq"
6805
- version = "3.3.0"
6734
+ version = "3.4.0"
6806
6735
  source = "registry+https://github.com/rust-lang/crates.io-index"
6807
- checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
6736
+ checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d"
6808
6737
  dependencies = [
6809
- "base64 0.22.1",
6738
+ "base64 0.23.1",
6810
6739
  "flate2",
6811
6740
  "log",
6812
6741
  "percent-encoding",
6813
6742
  "rustls",
6814
6743
  "rustls-pki-types",
6815
- "rustls-platform-verifier 0.6.2",
6744
+ "rustls-platform-verifier",
6816
6745
  "ureq-proto",
6817
6746
  "utf8-zero",
6818
6747
  "webpki-roots 1.0.7",
@@ -6820,11 +6749,11 @@ dependencies = [
6820
6749
 
6821
6750
  [[package]]
6822
6751
  name = "ureq-proto"
6823
- version = "0.6.0"
6752
+ version = "0.6.1"
6824
6753
  source = "registry+https://github.com/rust-lang/crates.io-index"
6825
- checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
6754
+ checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613"
6826
6755
  dependencies = [
6827
- "base64 0.22.1",
6756
+ "base64 0.23.1",
6828
6757
  "http",
6829
6758
  "httparse",
6830
6759
  "log",
@@ -7256,15 +7185,6 @@ dependencies = [
7256
7185
  "windows-link",
7257
7186
  ]
7258
7187
 
7259
- [[package]]
7260
- name = "windows-sys"
7261
- version = "0.45.0"
7262
- source = "registry+https://github.com/rust-lang/crates.io-index"
7263
- checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
7264
- dependencies = [
7265
- "windows-targets 0.42.2",
7266
- ]
7267
-
7268
7188
  [[package]]
7269
7189
  name = "windows-sys"
7270
7190
  version = "0.52.0"
@@ -7301,21 +7221,6 @@ dependencies = [
7301
7221
  "windows-link",
7302
7222
  ]
7303
7223
 
7304
- [[package]]
7305
- name = "windows-targets"
7306
- version = "0.42.2"
7307
- source = "registry+https://github.com/rust-lang/crates.io-index"
7308
- checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
7309
- dependencies = [
7310
- "windows_aarch64_gnullvm 0.42.2",
7311
- "windows_aarch64_msvc 0.42.2",
7312
- "windows_i686_gnu 0.42.2",
7313
- "windows_i686_msvc 0.42.2",
7314
- "windows_x86_64_gnu 0.42.2",
7315
- "windows_x86_64_gnullvm 0.42.2",
7316
- "windows_x86_64_msvc 0.42.2",
7317
- ]
7318
-
7319
7224
  [[package]]
7320
7225
  name = "windows-targets"
7321
7226
  version = "0.52.6"
@@ -7358,12 +7263,6 @@ dependencies = [
7358
7263
  "windows-link",
7359
7264
  ]
7360
7265
 
7361
- [[package]]
7362
- name = "windows_aarch64_gnullvm"
7363
- version = "0.42.2"
7364
- source = "registry+https://github.com/rust-lang/crates.io-index"
7365
- checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
7366
-
7367
7266
  [[package]]
7368
7267
  name = "windows_aarch64_gnullvm"
7369
7268
  version = "0.52.6"
@@ -7376,12 +7275,6 @@ version = "0.53.1"
7376
7275
  source = "registry+https://github.com/rust-lang/crates.io-index"
7377
7276
  checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
7378
7277
 
7379
- [[package]]
7380
- name = "windows_aarch64_msvc"
7381
- version = "0.42.2"
7382
- source = "registry+https://github.com/rust-lang/crates.io-index"
7383
- checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
7384
-
7385
7278
  [[package]]
7386
7279
  name = "windows_aarch64_msvc"
7387
7280
  version = "0.52.6"
@@ -7394,12 +7287,6 @@ version = "0.53.1"
7394
7287
  source = "registry+https://github.com/rust-lang/crates.io-index"
7395
7288
  checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
7396
7289
 
7397
- [[package]]
7398
- name = "windows_i686_gnu"
7399
- version = "0.42.2"
7400
- source = "registry+https://github.com/rust-lang/crates.io-index"
7401
- checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
7402
-
7403
7290
  [[package]]
7404
7291
  name = "windows_i686_gnu"
7405
7292
  version = "0.52.6"
@@ -7424,12 +7311,6 @@ version = "0.53.1"
7424
7311
  source = "registry+https://github.com/rust-lang/crates.io-index"
7425
7312
  checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
7426
7313
 
7427
- [[package]]
7428
- name = "windows_i686_msvc"
7429
- version = "0.42.2"
7430
- source = "registry+https://github.com/rust-lang/crates.io-index"
7431
- checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
7432
-
7433
7314
  [[package]]
7434
7315
  name = "windows_i686_msvc"
7435
7316
  version = "0.52.6"
@@ -7442,12 +7323,6 @@ version = "0.53.1"
7442
7323
  source = "registry+https://github.com/rust-lang/crates.io-index"
7443
7324
  checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
7444
7325
 
7445
- [[package]]
7446
- name = "windows_x86_64_gnu"
7447
- version = "0.42.2"
7448
- source = "registry+https://github.com/rust-lang/crates.io-index"
7449
- checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
7450
-
7451
7326
  [[package]]
7452
7327
  name = "windows_x86_64_gnu"
7453
7328
  version = "0.52.6"
@@ -7460,12 +7335,6 @@ version = "0.53.1"
7460
7335
  source = "registry+https://github.com/rust-lang/crates.io-index"
7461
7336
  checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
7462
7337
 
7463
- [[package]]
7464
- name = "windows_x86_64_gnullvm"
7465
- version = "0.42.2"
7466
- source = "registry+https://github.com/rust-lang/crates.io-index"
7467
- checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
7468
-
7469
7338
  [[package]]
7470
7339
  name = "windows_x86_64_gnullvm"
7471
7340
  version = "0.52.6"
@@ -7478,12 +7347,6 @@ version = "0.53.1"
7478
7347
  source = "registry+https://github.com/rust-lang/crates.io-index"
7479
7348
  checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
7480
7349
 
7481
- [[package]]
7482
- name = "windows_x86_64_msvc"
7483
- version = "0.42.2"
7484
- source = "registry+https://github.com/rust-lang/crates.io-index"
7485
- checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
7486
-
7487
7350
  [[package]]
7488
7351
  name = "windows_x86_64_msvc"
7489
7352
  version = "0.52.6"
@@ -7639,7 +7502,7 @@ dependencies = [
7639
7502
  "oid-registry",
7640
7503
  "ring",
7641
7504
  "rusticata-macros",
7642
- "thiserror 2.0.18",
7505
+ "thiserror",
7643
7506
  "time",
7644
7507
  ]
7645
7508
 
data/DESIGN.md CHANGED
@@ -85,28 +85,106 @@ and the Rust→class mapping in `sdk/python/src/error.rs`.
85
85
 
86
86
  ## Runtime binary (`msb` + `libkrunfw`)
87
87
 
88
- The core crate's `prebuilt` feature (on by default) downloads the `msb` microVM
89
- runtime and `libkrunfw` firmware into `~/.microsandbox/{bin,lib}` **at build
90
- time** (`build.rs`). The path resolver checks, in order: `$MSB_PATH`
91
- SDK-set path (`Microsandbox.runtime_path=`) config file workspace build
92
- `~/.microsandbox/bin/msb` `which msb`. `Microsandbox.install` / `.installed?`
93
- expose the core `setup::install`/`is_installed` for explicit, idempotent
94
- provisioning (mirrors the Python `install()`/`is_installed()`).
95
-
96
- Build-time provisioning only helps the **source gem**, where `build.rs` runs on
97
- the user's own machine. A **precompiled gem** is built in CI, so its build-time
98
- download lands on the CI host, not the user's the user's `~/.microsandbox` is
99
- empty. `Microsandbox.ensure_runtime!` closes that gap: `Sandbox.create`/`start`
100
- call it to fetch the runtime on first use (by the *running* host's arch, which is
101
- always correct), at most once per process. `MICROSANDBOX_NO_AUTO_INSTALL` opts
102
- out (air-gapped hosts that provision out of band). libkrunfw is `dlopen`'d by
103
- `msb` at runtime and is never linked into the extension.
88
+ The gem is **SDK-only**: nothing is provisioned while it is built or installed.
89
+ The core crate's `prebuilt` feature whose `build.rs` downloads the `msb`
90
+ microVM runtime and `libkrunfw` firmware into `~/.microsandbox/{bin,lib}` at
91
+ build time is deliberately **off** (`default-features = false`, features
92
+ `keyring`/`net`/`ssh`, the set the official SDKs ship). Build-time provisioning
93
+ only ever helped the source gem: for a precompiled gem the download lands on the
94
+ CI host, and even for a source install it couples "compile a Ruby extension" to
95
+ "fetch 50 MB of host binaries", which is not the compiler's business.
96
+
97
+ The *guest* agent is a different story and is still embedded at build time: the
98
+ `agentd` binary that runs as PID 1 inside every microVM is `include_bytes!`-d
99
+ into the extension by `microsandbox-filesystem`'s `build.rs`, keyed by *target*
100
+ arch. Without its `prebuilt` feature that build script demands a locally built
101
+ `build/agentd` (workspace-only), so `ext/microsandbox/Cargo.toml` enables exactly
102
+ that one sub-feature through a direct `microsandbox-runtime` dependency
103
+ (`default-features = false, features = ["prebuilt"]`, whose `prebuilt` is just
104
+ `microsandbox-filesystem/prebuilt`) while the SDK-level host download stays off.
105
+
106
+ ### The companion gem
107
+
108
+ The host runtime ships as its own gem, **`microsandbox-rb-binaries`** (source in
109
+ `binaries/`), one platform gem per `arm64-darwin` / `x86_64-linux-gnu` /
110
+ `aarch64-linux-gnu` (Linux binaries are glibc-linked, hence
111
+ `required_rubygems_version >= 3.3.11`). It carries `vendor/bin/msb`,
112
+ `vendor/lib/libkrunfw.*` and a `vendor/manifest.json` — a layout that mirrors
113
+ both the upstream release bundle and `~/.microsandbox`, so the core finds the
114
+ firmware by `../lib` adjacency to `msb` and only the binary path needs handing
115
+ over (same trick as the Python and Node SDKs). Everything is downloaded from the
116
+ upstream GitHub release named by `Microsandbox::Binaries::RUNTIME_VERSION` and
117
+ sha256-verified fail-closed against that release's `checksums.sha256` — the copy
118
+ committed as `binaries/checksums/<tag>.sha256` when the runtime was adopted
119
+ (a GitHub release is mutable, so the live file must agree with the reviewed
120
+ one, not replace it) — at vendoring time; the gem build re-verifies the staged
121
+ tree against the manifest (regular files only), so nothing unverified is ever
122
+ packaged. Build pipeline:
123
+ `rake -C binaries vendor[<platform>] build[<platform>] verify`.
124
+
125
+ There is **no dependency edge in either direction**. RubyGems has no optional
126
+ dependencies, and cloud-only users (`MSB_BACKEND=cloud`) must not be forced to
127
+ download ~50 MB of binaries they will never execute — so the SDK discovers the
128
+ companion gem instead of depending on it, and the companion gem stays a pure
129
+ payload. The two are versioned in lockstep (`Binaries::VERSION` ==
130
+ `Microsandbox::VERSION`, `Binaries::RUNTIME_VERSION` == `RUNTIME_VERSION`, both
131
+ asserted by `spec/unit/version_spec.rb`).
132
+
133
+ ### Activation and the resolver ladder
134
+
135
+ `lib/microsandbox.rb` runs a private `activate_bundled_runtime!` once, at
136
+ `require "microsandbox"` time: `gem "microsandbox-rb-binaries", "= VERSION"`
137
+ (pins the lockstep version when RubyGems, not Bundler, picks the gem; a failure
138
+ here is tolerated) → `require "microsandbox/binaries"` (`LoadError` → the tier is
139
+ simply absent, silently) → `Binaries::VERSION` must equal `VERSION` and
140
+ `Binaries::RUNTIME_VERSION` must equal `RUNTIME_VERSION` (the `gem` pin above
141
+ cannot enforce lockstep: under Bundler it raises whenever the bundle picked any
142
+ other version, and that is swallowed) **and** both `msb_path` and
143
+ `libkrunfw_path` must exist →
144
+ `Native.set_runtime_msb_path(msb)`. The version gate is the load-bearing part: a
145
+ runtime from a different upstream release passes any exists-check and then fails
146
+ every `create` on a wire-protocol mismatch, so a mismatch is reported with a
147
+ warning and skipped rather than handed to the core. The whole path is
148
+ non-raising — a broken companion gem must never take `require "microsandbox"`
149
+ down with it.
150
+
151
+ Activation is **eager** rather than lazy, mirroring the Node SDK (`napi.ts`
152
+ pushes its platform package's `msb` into the same set-once slot at module load).
153
+ A lazy hook inside `ensure_runtime!` would only cover `Sandbox.create`/`start`
154
+ and silently miss every other entry point that resolves or spawns `msb`.
155
+
156
+ The resolver order is therefore: `$MSB_PATH` → SDK-set path (claimed by the
157
+ companion gem at load; `Microsandbox.runtime_path=` targets this same set-once
158
+ slot, so with the gem installed the setter is a **no-op** — override via
159
+ `MSB_PATH`) → config file → workspace build → `~/.microsandbox/bin/msb` →
160
+ `which msb`. `Microsandbox.runtime_path` reports the winner.
161
+ `Microsandbox.install` / `.installed?` expose the core `setup::install`/
162
+ `is_installed` for explicit, idempotent provisioning (mirrors the Python
163
+ `install()`/`is_installed()`).
164
+
165
+ Auto-provisioning stays as the **lowest tier**, deliberately (npx-style
166
+ first-use download; see upstream discussion
167
+ [superradcompany/microsandbox#1305](https://github.com/superradcompany/microsandbox/issues/1305)):
168
+ `Sandbox.create`/`start` call `Microsandbox.ensure_runtime!`, which fetches a
169
+ missing or version-stale runtime into `~/.microsandbox` on first use — by the
170
+ *running* host's arch, which is always correct — at most once per process.
171
+ `MICROSANDBOX_NO_AUTO_INSTALL` opts out (air-gapped hosts that provision out of
172
+ band). When the companion gem won the resolver (`bundled_runtime_active?`),
173
+ `ensure_runtime!` returns immediately: those binaries are already the matching
174
+ version, so nothing is downloaded or touched in `~/.microsandbox`. Note the
175
+ asymmetry in trust: the companion gem's payload is digest-verified when it is
176
+ vendored, while the first-use download is not yet content-verified (pending
177
+ upstream
178
+ [superradcompany/microsandbox#1300](https://github.com/superradcompany/microsandbox/issues/1300)).
179
+
180
+ libkrunfw is `dlopen`'d by `msb` at runtime and is never linked into the
181
+ extension.
104
182
 
105
183
  ## Core-crate dependency (self-contained)
106
184
 
107
185
  `ext/microsandbox/Cargo.toml` depends on the core crate via a **pinned git tag**
108
186
  (`microsandbox` / `microsandbox-network`, pinned to the same tag as
109
- `Microsandbox::RUNTIME_VERSION` — currently `v0.6.2`), so the gem builds anywhere
187
+ `Microsandbox::RUNTIME_VERSION` — currently `v0.6.9`), so the gem builds anywhere
110
188
  — CI, `rake-compiler-dock` release containers, and end-user source installs —
111
189
  without an adjacent checkout. For fast local development against a sibling
112
190
  microsandbox checkout, copy `.cargo/config.toml.example` to `.cargo/config.toml`
@@ -122,7 +200,8 @@ git. The override must never be committed — it would break container builds.
122
200
  (`rake-compiler-dock`) per `Gem::Platform`, shipping multi-ABI
123
201
  `lib/microsandbox/<ruby_abi>/` native artifacts — the same model Node uses with
124
202
  per-platform packages. End users then install with no Rust toolchain, and the
125
- runtime is fetched on first use (see above). The guest `agentd` is baked into
203
+ host runtime comes from the `microsandbox-rb-binaries` gem or, without it, is
204
+ fetched on first use (see above). The guest `agentd` is baked into
126
205
  the extension by *target* arch (`filesystem/build.rs` uses
127
206
  `CARGO_CFG_TARGET_ARCH` + `include_bytes!`), so it cross-compiles correctly;
128
207
  the real cross work is linking the *target* native libs — `libcap-ng` on Linux
@@ -133,6 +212,16 @@ git. The override must never be committed — it would break container builds.
133
212
  since CI can't boot a microVM to prove a built gem actually works, gems are
134
213
  promoted to the publish path manually after per-platform validation. Published
135
214
  to RubyGems via Trusted Publishing (OIDC). See [Releasing](README.md#releasing).
215
+ * **Runtime binaries gems** (`microsandbox-rb-binaries`): a distinct artifact
216
+ from the precompiled *extension* gems above — no Ruby code beyond a small
217
+ locator module, just the verified upstream `msb` + `libkrunfw` for one
218
+ platform. CI's `binaries` job vendors and builds all three platforms on every
219
+ run and smoke-tests the host one; the `package` job installs the source gem
220
+ together with the host binaries gem and asserts `Microsandbox.runtime_path`
221
+ resolves into it, and the real-microVM integration job runs twice — once
222
+ against a `~/.microsandbox` provision (the fallback tier) and once booting from
223
+ the gem's vendored runtime. Publishing them is a follow-up (it needs a pending
224
+ trusted publisher on rubygems.org) and lands with the next release.
136
225
 
137
226
  ## Build requirements
138
227
 
data/README.md CHANGED
@@ -58,6 +58,10 @@ them. Our deepest thanks to the maintainers and community. 🙏
58
58
  - A **Rust** toolchain (stable >= 1.91) — needed only when installing the source
59
59
  gem (it compiles the native extension on install). Precompiled per-platform
60
60
  gems, where available, require no Rust toolchain; see [Releasing](#releasing)
61
+ - The **`msb` runtime + `libkrunfw` firmware** — shipped by the optional
62
+ companion gem `microsandbox-rb-binaries`, or downloaded into `~/.microsandbox`
63
+ on first use; see [The runtime binaries](#the-runtime-binaries). Cloud-only
64
+ users (`MSB_BACKEND=cloud`) need no local runtime at all
61
65
 
62
66
  ## Installation
63
67
 
@@ -80,17 +84,49 @@ takes a few minutes and needs a Rust toolchain (`rustc >= 1.91`) on `PATH`. When
80
84
  a **precompiled platform gem** is available for your OS/architecture, RubyGems
81
85
  picks it automatically and no Rust toolchain is required.
82
86
 
83
- Either way the `msb` runtime and `libkrunfw` firmware are provisioned into
84
- `~/.microsandbox` automatically on first use (the first `Sandbox.create`/`start`
85
- downloads them if missing). To provision ahead of time e.g. while baking a
86
- container image, or to avoid the first-call latency call `install` explicitly:
87
+ ### The runtime binaries
88
+
89
+ `microsandbox-rb` is **SDK-only** it wraps the microVM runtime, it doesn't
90
+ carry it. The host-side `msb` runtime and the `libkrunfw` firmware come from an
91
+ optional companion gem, **`microsandbox-rb-binaries`**, published as one gem per
92
+ platform (`arm64-darwin`, `x86_64-linux-gnu`, `aarch64-linux-gnu`) and
93
+ versioned in lockstep with this gem:
94
+
95
+ ```ruby
96
+ # Gemfile — install both gems at the same version
97
+ gem "microsandbox-rb", require: "microsandbox"
98
+ gem "microsandbox-rb-binaries"
99
+ ```
100
+
101
+ That's all the wiring there is: `require "microsandbox"` finds the companion
102
+ gem, checks it is the same version and built for the same upstream runtime, and points the resolver
103
+ at its vendored `msb` — so your bundle carries the runtime and nothing is
104
+ downloaded at install time or on first call. **Recommended whenever you boot
105
+ local microVMs.** Cloud-only users (`MSB_BACKEND=cloud`) should skip it: it is a
106
+ separate, optional gem precisely so nobody has to fetch ~50 MB of binaries they
107
+ won't run. Neither gem depends on the other.
108
+
109
+ > **Availability.** The binaries gems are published on every release tag
110
+ > alongside `microsandbox-rb` (first release to ship them: the one after
111
+ > 0.13.0). On an older SDK version, or a platform without a bundle, use the
112
+ > fallback below (or build them yourself from `binaries/` — see that
113
+ > directory's README).
114
+
115
+ **Fallback — first-use download.** Without the companion gem, the `msb` runtime
116
+ and `libkrunfw` firmware are provisioned into `~/.microsandbox` automatically on
117
+ first use (the first `Sandbox.create`/`start` downloads them if missing). To
118
+ provision ahead of time — e.g. while baking a container image, or to avoid the
119
+ first-call latency — call `install` explicitly:
87
120
 
88
121
  ```ruby
89
122
  Microsandbox.install unless Microsandbox.installed?
90
123
  ```
91
124
 
92
125
  Set `MICROSANDBOX_NO_AUTO_INSTALL` to disable the automatic first-use download
93
- (e.g. on air-gapped hosts that provision the runtime out of band).
126
+ (e.g. on air-gapped hosts that provision the runtime out of band). None of this
127
+ applies when the companion gem supplies the runtime: its binaries are already the
128
+ matching version, so `ensure_runtime!` skips the installer entirely and nothing
129
+ is written to `~/.microsandbox`.
94
130
 
95
131
  ## Quick start
96
132
 
@@ -428,8 +464,9 @@ end
428
464
  ## Runtime configuration
429
465
 
430
466
  The `msb` runtime path is resolved in this order: the `MSB_PATH` environment
431
- variable → an SDK-set override the config file `~/.microsandbox/bin/msb` →
432
- `msb` on `PATH`.
467
+ variable → the `microsandbox-rb-binaries` gem (or another SDK-set override)
468
+ the config file → `~/.microsandbox/bin/msb` → `msb` on `PATH`.
469
+ `Microsandbox.runtime_path` reports the winner.
433
470
 
434
471
  ```ruby
435
472
  Microsandbox.installed? # => true/false
@@ -439,6 +476,16 @@ Microsandbox.runtime_path = "/opt/microsandbox/bin/msb" # override (set-once)
439
476
  Microsandbox.libkrunfw_path = "/opt/microsandbox/lib/libkrunfw.dylib" # override (set-once)
440
477
  ```
441
478
 
479
+ When [`microsandbox-rb-binaries`](#the-runtime-binaries) is installed,
480
+ `require "microsandbox"` claims that SDK-set slot with the gem's vendored `msb`
481
+ (the firmware is found alongside it), and `runtime_path` points into the gem.
482
+ The two gems are versioned in lockstep and the companion gem must be the same
483
+ version **and** built for the same upstream runtime — a mismatch of either is reported with a warning and
484
+ skipped, and the SDK falls back to `~/.microsandbox` rather than driving a
485
+ runtime it doesn't match. Because the slot is **set-once**,
486
+ `Microsandbox.runtime_path=` is then a no-op: use the `MSB_PATH` environment
487
+ variable, which outranks it, to point at a different runtime.
488
+
442
489
  ### Backend routing
443
490
 
444
491
  As of v0.5.8 every operation runs through a backend. The default is the local
@@ -481,6 +528,13 @@ Microsandbox::VERSION # => "0.13.0" (the gem's own version)
481
528
  Microsandbox.runtime_version # => "v0.6.9" (the embedded upstream runtime tag)
482
529
  ```
483
530
 
531
+ The companion [`microsandbox-rb-binaries`](#the-runtime-binaries) gem is
532
+ versioned in **lockstep** with this gem (same number, released together) and
533
+ pins the same upstream runtime — install both at the same version. The gem's
534
+ `Microsandbox::Binaries::VERSION` and `::RUNTIME_VERSION` are asserted against
535
+ this gem's constants by the test suite, so a companion gem can't silently go
536
+ stale.
537
+
484
538
  | Gem version | Upstream runtime | Notes |
485
539
  |-------------|------------------|-------|
486
540
  | `0.5.7` | `v0.5.7` | initial release |
@@ -502,6 +556,7 @@ Microsandbox.runtime_version # => "v0.6.9" (the embedded upstream runtime tag
502
556
  | `0.11.0` | `v0.6.7` | adopts upstream `v0.6.7` (**breaking**): network profiles replace `public_only`/`non_local`, structured `root_disk:` replaces `oci_upper_size:` (deprecated alias kept), snapshot descriptor contract (`create` re-keyed by name, `save`/`load` rename, `snapshot_to` removed, on-disk auto-migration), `Image.load`/`Image.save`, `follow_root_symlinks:`; runtime carries the GHSA-4vq3-cjpp-v7fg `msb copy` fix |
503
557
  | `0.12.0` | `v0.6.8` | adopts upstream `v0.6.8` (**breaking**): `Sandbox.list`/`.list_with` return a cursor-paginated `SandboxPage` (`limit:`/`cursor:` keywords), `UnsupportedError` re-keyed by structured operations with `#operation`/`#hint`; runtime adds a shared log registry for followed streams and cloud exec/ssh reconnects |
504
558
  | `0.13.0` | `v0.6.9` | adopts upstream `v0.6.9` (**breaking**): a bare `MSB_API_KEY` no longer selects the cloud backend (explicit `MSB_BACKEND=cloud` or a cloud profile required; invalid cloud config fails closed with `InvalidConfigError`); snapshot payload integrity becomes opt-in (`record_integrity:`, `verify` can report `:not_recorded`). Parity: default-workload execution (`exec_default`/`exec_default_stream`/`attach_default`, `cmd:`), flat root disks (`RootDisk.flat`), `modify(root_disk_size:)`, `rate_limiter:`, `vsock:`, `default_backend_info`, `Volume.get_default` |
559
+ | `0.14.0` | `v0.6.9` | two-gem split: SDK-only gem (no build-time runtime download) + companion `microsandbox-rb-binaries` platform gems |
505
560
 
506
561
  **Going forward** — the gem version moves on its own semver track and no longer
507
562
  mirrors the upstream tag:
@@ -555,7 +610,11 @@ or credential setup is needed.
555
610
  the number to mirror the upstream tag. If the release also adopts a new upstream
556
611
  runtime, bump the `tag = "vX.Y.Z"` on **both** the `microsandbox` and
557
612
  `microsandbox-network` git deps, update `Microsandbox::RUNTIME_VERSION` to match,
558
- and add a row to the Versioning table. Update `CHANGELOG.md`.
613
+ and add a row to the Versioning table. Also bump
614
+ `Microsandbox::Binaries::VERSION` (and, on a runtime adoption,
615
+ `::RUNTIME_VERSION`) in `binaries/lib/microsandbox/binaries.rb` — the
616
+ companion gem ships in lockstep and the specs assert both. Update
617
+ `CHANGELOG.md`.
559
618
  2. Push a `vX.Y.Z` tag. CI builds the **source gem** and pushes it to RubyGems
560
619
  via `rubygems/configure-rubygems-credentials` (OIDC, `id-token: write`) — no
561
620
  `RUBYGEMS_API_KEY` secret required.
@@ -568,13 +627,29 @@ or credential setup is needed.
568
627
  > prove otherwise — so promotion is manual after validating the artifact on each
569
628
  > platform. A precompiled gem ships the compiled extension (with the guest
570
629
  > `agentd` baked in by *target* arch); the host-side `msb` + `libkrunfw` runtime
571
- > is fetched into `~/.microsandbox` on first use by `Microsandbox.ensure_runtime!`
572
- > (libkrunfw is `dlopen`'d by `msb` at runtime, never linked into the gem). The
630
+ > is **not** in it that comes from the companion `microsandbox-rb-binaries`
631
+ > gem, or, when that isn't installed, is fetched into `~/.microsandbox` on first
632
+ > use by `Microsandbox.ensure_runtime!` (libkrunfw is `dlopen`'d by `msb` at
633
+ > runtime, never linked into the gem). The
573
634
  > real cross-compile work is linking the *target* native libraries — `libcap-ng`
574
635
  > on Linux (handled via Debian multiarch in the workflow) and the Hypervisor +
575
636
  > Security frameworks on macOS (via osxcross; the one platform left to confirm).
576
637
  > Until promoted, users install the source gem (which compiles via `rb_sys`).
577
638
 
639
+ > **Runtime binaries gems** (`microsandbox-rb-binaries`, source in `binaries/`)
640
+ > are a *separate* artifact from the precompiled extension gems above: they carry
641
+ > no Ruby extension, only the upstream `msb` + `libkrunfw` for one platform,
642
+ > verified against the release's published `checksums.sha256` when vendored. CI's
643
+ > `binaries` job vendors and builds all three (`arm64-darwin`,
644
+ > `x86_64-linux-gnu`, `aarch64-linux-gnu`) on every run and smoke-tests the host
645
+ > one; `release.yml`'s `binaries-gems` job does the same on a tag and a separate
646
+ > `publish-binaries` job pushes them after the SDK gem is live (a companion
647
+ > failure is its own red job and never blocks the SDK release).
648
+ > They use their own RubyGems trusted-publisher entry (same repo + workflow,
649
+ > gem name `microsandbox-rb-binaries`). To build them by hand:
650
+ > `rake -C binaries vendor[<platform>]` then `rake -C binaries build[<platform>]`
651
+ > (→ `binaries/pkg/*.gem`).
652
+
578
653
  See [DESIGN.md](DESIGN.md) for the architecture and the implemented-surface
579
654
  section. The binding covers the official-SDK surface: sandbox
580
655
  lifecycle (the live `Sandbox` `stop`/`stop_and_wait`/`kill`/`drain`/`wait`/
@@ -7,7 +7,7 @@ description = "Ruby SDK native extension for microsandbox — secure, fast micro
7
7
  # Must equal Microsandbox::VERSION (lib/microsandbox/version.rb) — Native.version
8
8
  # returns this via env!("CARGO_PKG_VERSION") and version_spec.rb asserts equality.
9
9
  # The core-crate dependency below stays pinned at its own tag (v0.6.9).
10
- version = "0.13.0"
10
+ version = "0.14.0"
11
11
  authors = ["Super Rad Company <development@superrad.company>"]
12
12
  repository = "https://github.com/superradcompany/microsandbox"
13
13
  license = "Apache-2.0"
@@ -32,11 +32,27 @@ rb-sys = "0.9"
32
32
  # gem is self-contained and buildable anywhere (CI, release containers, end-user
33
33
  # source installs) without an adjacent checkout. For local development against a
34
34
  # sibling checkout, use the `paths` override in `.cargo/config.toml` (see
35
- # `.cargo/config.toml.example`). "ssh" matches the feature set the Python/Node
36
- # SDKs ship with; default features add "prebuilt" (provisions msb + libkrunfw at
37
- # build time), "net", and "keyring".
38
- microsandbox = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.9", default-features = true, features = ["ssh"] }
35
+ # `.cargo/config.toml.example`).
36
+ #
37
+ # Features are listed explicitly (default-features = false) so the SDK crate's
38
+ # own "prebuilt" feature stays OFF: that feature makes its build.rs download
39
+ # the msb + libkrunfw *host runtime* into ~/.microsandbox at compile time. This
40
+ # gem is SDK-only — the host runtime comes from the companion
41
+ # `microsandbox-rb-binaries` gem (see binaries/) or, as the lowest tier, the
42
+ # first-use download into ~/.microsandbox (`setup::install`, which is not
43
+ # gated on "prebuilt"). "net"/"ssh"/"keyring" are the features the official
44
+ # SDKs ship with.
45
+ microsandbox = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.9", default-features = false, features = ["keyring", "net", "ssh"] }
39
46
  microsandbox-network = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.9" }
47
+ # The *guest* agent (`agentd`, PID 1 inside every microVM) is embedded into the
48
+ # extension by microsandbox-filesystem's build.rs, which without its "prebuilt"
49
+ # feature demands a locally built `build/agentd` (workspace-only, `just
50
+ # build-deps`). Enable that one sub-feature directly — via microsandbox-runtime,
51
+ # whose "prebuilt" is exactly `microsandbox-filesystem/prebuilt` — so the
52
+ # prebuilt agentd for the target arch is fetched (same behaviour as before)
53
+ # while the SDK-level msb download above stays off. Feature unification means
54
+ # this adds no new crates; keep it on the same tag as the deps above.
55
+ microsandbox-runtime = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.9", default-features = false, features = ["prebuilt"] }
40
56
 
41
57
  # Async core bridged to Ruby's synchronous API via a blocking tokio runtime.
42
58
  tokio = { version = "1", features = ["rt-multi-thread", "sync", "time"] }
@@ -8,7 +8,7 @@ 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.13.0"
11
+ VERSION = "0.14.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
data/lib/microsandbox.rb CHANGED
@@ -64,12 +64,13 @@ module Microsandbox
64
64
  # Download and install the `msb` runtime + `libkrunfw` into
65
65
  # `~/.microsandbox` (idempotent).
66
66
  #
67
- # When the gem is built from source, the native extension provisions the
68
- # runtime at build time, so this is usually a no-op. Precompiled platform
69
- # gems (which skip the local Rust build) do NOT provision it that way, so the
70
- # runtime is fetched on first use see {ensure_runtime!}. Call this
71
- # explicitly to provision ahead of time (e.g. while baking a container
72
- # image) so the first {Sandbox.create} doesn't pay the download.
67
+ # This gem is SDK-only: nothing is provisioned at build/install time. The
68
+ # runtime comes from the companion `microsandbox-rb-binaries` gem when it is
69
+ # installed (see {ensure_runtime!}); otherwise it is fetched into
70
+ # `~/.microsandbox` on first use. Call this explicitly to provision ahead of
71
+ # time (e.g. while baking a container image) so the first {Sandbox.create}
72
+ # doesn't pay the download. Not needed — and not used — when the binaries
73
+ # gem supplies the runtime.
73
74
  # @return [nil]
74
75
  def install
75
76
  Native.install
@@ -102,10 +103,14 @@ module Microsandbox
102
103
 
103
104
  # Ensure the `msb` runtime + `libkrunfw` are present *and version-matched*,
104
105
  # provisioning them on first use if not. Called automatically by
105
- # {Sandbox.create}/{Sandbox.start} so precompiled-gem users (who never ran the
106
- # source build) get a working runtime without a manual {install} step.
106
+ # {Sandbox.create}/{Sandbox.start} so a freshly installed gem gets a working
107
+ # runtime without a manual {install} step.
107
108
  #
108
- # Runs at most once per process. Opt out by setting
109
+ # Runs at most once per process. When the companion `microsandbox-rb-binaries`
110
+ # gem supplies the runtime (it was activated at load time and its `msb` is
111
+ # what the resolver now returns from {runtime_path}), nothing is downloaded
112
+ # or touched in `~/.microsandbox` — the bundled binaries are already the
113
+ # matching version. Opt out of the download by setting
109
114
  # `MICROSANDBOX_NO_AUTO_INSTALL` (e.g. air-gapped hosts that provision the
110
115
  # runtime out of band); the runtime is then left untouched and a missing or
111
116
  # stale one surfaces at the operation itself.
@@ -129,6 +134,14 @@ module Microsandbox
129
134
  # uses the same lazy env/profile/config ladder every operation already
130
135
  # consults, so this adds no work for local hosts (the common case).
131
136
  return if default_backend_kind == :cloud
137
+ # The binaries gem won the resolver: its vendored msb (and the libkrunfw
138
+ # beside it) are exactly the version this gem was built for, so there is
139
+ # nothing to verify or download. (If `MSB_PATH` overrides it, the user owns
140
+ # the runtime and we fall through to the existing behaviour.)
141
+ if bundled_runtime_active?
142
+ @runtime_ready = true
143
+ return
144
+ end
132
145
  # Opted out: the caller manages the runtime out of band, so don't fetch,
133
146
  # verify, or repair it here. Memoize the decision (the env var is stable for
134
147
  # the process); the operation resolves `msb` itself and surfaces any problem.
@@ -139,7 +152,8 @@ module Microsandbox
139
152
 
140
153
  unless installed?
141
154
  warn "[microsandbox] runtime (msb + libkrunfw) not found; " \
142
- "downloading to ~/.microsandbox (set MICROSANDBOX_NO_AUTO_INSTALL to skip)..."
155
+ "downloading to ~/.microsandbox (set MICROSANDBOX_NO_AUTO_INSTALL to skip, " \
156
+ "or install the microsandbox-rb-binaries gem to ship it with your bundle)..."
143
157
  end
144
158
  install
145
159
  @runtime_ready = true
@@ -155,9 +169,17 @@ module Microsandbox
155
169
  # resolver, below only the `MSB_PATH` environment variable). Process-level
156
170
  # and set-once: a second call is silently ignored, and the `MSB_PATH`
157
171
  # environment variable still wins. Mirrors {libkrunfw_path=}.
172
+ #
173
+ # The companion `microsandbox-rb-binaries` gem claims this same slot when
174
+ # `require "microsandbox"` activates it, so with that gem installed this
175
+ # setter is a no-op — use `MSB_PATH` to override a bundled runtime.
158
176
  # @param path [String]
159
177
  # @return [void]
160
178
  def runtime_path=(path)
179
+ if @bundled_msb_path && path.to_s != @bundled_msb_path
180
+ warn "[microsandbox] runtime_path= ignored: the microsandbox-rb-binaries gem already " \
181
+ "claimed the set-once SDK slot (#{@bundled_msb_path}); set MSB_PATH to override it"
182
+ end
161
183
  Native.set_runtime_msb_path(path.to_s)
162
184
  end
163
185
 
@@ -257,5 +279,101 @@ module Microsandbox
257
279
  v = ENV["MICROSANDBOX_NO_AUTO_INSTALL"]
258
280
  !v.nil? && !v.empty? && !%w[0 false no].include?(v.downcase)
259
281
  end
282
+
283
+ # Wire the companion `microsandbox-rb-binaries` gem into the core resolver.
284
+ # Runs once, at `require "microsandbox"` time (like the Node SDK, which pushes
285
+ # its platform package's msb into the same set-once SDK slot at module load)
286
+ # so every entry point that spawns msb — not just {Sandbox.create} — sees it.
287
+ #
288
+ # The gem is optional and has no dependency edge to this one (cloud-only
289
+ # users skip the ~50 MB download; RubyGems has no optional dependencies), so
290
+ # discovery is by require: absent → nothing happens and the resolver's lower
291
+ # tiers (`~/.microsandbox`, then `PATH`) plus the first-use download take
292
+ # over. Present but built for a different upstream runtime → warn and skip
293
+ # it rather than hand the core a mismatched msb (a stale runtime passes an
294
+ # exists-check and then fails every create on a wire-protocol mismatch).
295
+ # Only `MSB_PATH` (env) outranks the slot claimed here.
296
+ #
297
+ # Never raises: a broken companion gem must not take `require "microsandbox"`
298
+ # down with it.
299
+ # @return [String, nil] the activated msb path
300
+ def activate_bundled_runtime!
301
+ @bundled_msb_path = nil
302
+ begin
303
+ # Pin the lockstep version when RubyGems (not Bundler) picks the gem, so
304
+ # a newer/older companion left around doesn't get activated over the
305
+ # matching one. Under Bundler the Gemfile already decides; a companion
306
+ # that isn't in the bundle raises here and `require` then fails below.
307
+ gem "microsandbox-rb-binaries", "= #{VERSION}"
308
+ rescue Gem::LoadError
309
+ # Not installed at this version — `require` settles it.
310
+ end
311
+ begin
312
+ require "microsandbox/binaries"
313
+ rescue LoadError
314
+ return nil
315
+ end
316
+ # A bare require activates the newest gem of ANY name that ships this
317
+ # feature path; only accept the companion gem itself, this gem's own tree
318
+ # (a source checkout, where Bundler's path gem spans the whole repo and so
319
+ # owns binaries/lib too), or a plain load path (RUBYLIB/-I) no gem owns.
320
+ owner = bundled_runtime_owner
321
+ if owner && !TRUSTED_BINARIES_OWNERS.include?(owner)
322
+ warn "[microsandbox] ignoring microsandbox/binaries provided by the #{owner} gem " \
323
+ "(only microsandbox-rb-binaries is trusted for the bundled runtime)"
324
+ return nil
325
+ end
326
+ # Lockstep gate on BOTH constants. The runtime tag is what the wire
327
+ # protocol depends on; the gem version is the documented contract
328
+ # ("install both at the same version") and also covers this file's own
329
+ # API/packaging — the `gem "…", "= VERSION"` pin above is best-effort (it
330
+ # raises under Bundler whenever the bundle picked any other version, and
331
+ # that is swallowed), so it cannot be what enforces it.
332
+ unless Binaries::VERSION == VERSION && Binaries::RUNTIME_VERSION == RUNTIME_VERSION
333
+ warn "[microsandbox] ignoring microsandbox-rb-binaries #{Binaries::VERSION} " \
334
+ "(runtime #{Binaries::RUNTIME_VERSION}): microsandbox-rb #{VERSION} " \
335
+ "(runtime #{RUNTIME_VERSION}) needs the companion gem at the same version. " \
336
+ "Install both gems at the same version; falling back to ~/.microsandbox."
337
+ return nil
338
+ end
339
+ msb = Binaries.msb_path
340
+ unless msb && Binaries.libkrunfw_path
341
+ warn "[microsandbox] microsandbox-rb-binaries #{Binaries::VERSION} is installed but " \
342
+ "carries no runtime under #{Binaries.root}; falling back to ~/.microsandbox."
343
+ return nil
344
+ end
345
+ Native.set_runtime_msb_path(msb)
346
+ @bundled_msb_path = msb
347
+ rescue StandardError, ScriptError => e
348
+ # ScriptError too: a corrupt or newer-syntax binaries.rb raises
349
+ # SyntaxError (not a StandardError) out of `require`, and the optional
350
+ # companion must never take `require "microsandbox"` down with it.
351
+ warn "[microsandbox] could not activate microsandbox-rb-binaries: #{e.class}: #{e.message}"
352
+ nil
353
+ end
354
+
355
+ # Gems allowed to provide `microsandbox/binaries` (see activate_bundled_runtime!).
356
+ TRUSTED_BINARIES_OWNERS = %w[microsandbox-rb-binaries microsandbox-rb].freeze
357
+ private_constant :TRUSTED_BINARIES_OWNERS
358
+
359
+ # Name of the loaded gem whose files define Microsandbox::Binaries, or nil
360
+ # when it came from a bare load path.
361
+ def bundled_runtime_owner
362
+ source = Binaries.method(:msb_path).source_location&.first
363
+ return nil unless source
364
+ spec = Gem.loaded_specs.values.find { |s| source.start_with?(File.join(s.full_gem_path, "")) }
365
+ spec&.name
366
+ end
367
+
368
+ # Whether the binaries gem's msb is what the resolver actually returns — i.e.
369
+ # it was activated and nothing higher in the ladder (`MSB_PATH`) overrides it.
370
+ def bundled_runtime_active?
371
+ return false unless @bundled_msb_path
372
+ runtime_path == @bundled_msb_path
373
+ rescue Microsandbox::Error
374
+ false
375
+ end
260
376
  end
377
+
378
+ send(:activate_bundled_runtime!)
261
379
  end
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.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ya-luotao