microsandbox-rb 0.13.0 → 0.15.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 +4 -4
- data/CHANGELOG.md +102 -1
- data/Cargo.lock +230 -258
- data/DESIGN.md +107 -18
- data/README.md +88 -12
- data/ext/microsandbox/Cargo.toml +23 -7
- data/ext/microsandbox/src/sandbox.rs +15 -2
- data/lib/microsandbox/ssh.rb +15 -2
- data/lib/microsandbox/version.rb +2 -2
- data/lib/microsandbox.rb +128 -10
- data/sig/microsandbox.rbs +3 -2
- metadata +1 -1
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
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
`
|
|
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.
|
|
187
|
+
`Microsandbox::RUNTIME_VERSION` — currently `v0.6.14`), 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
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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 →
|
|
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
|
|
@@ -477,10 +524,17 @@ change diverged the two numbers — the gem version is **not** a reliable indica
|
|
|
477
524
|
of the embedded runtime version. To learn which runtime a build wraps, ask it:
|
|
478
525
|
|
|
479
526
|
```ruby
|
|
480
|
-
Microsandbox::VERSION # => "0.
|
|
481
|
-
Microsandbox.runtime_version # => "v0.6.
|
|
527
|
+
Microsandbox::VERSION # => "0.15.0" (the gem's own version)
|
|
528
|
+
Microsandbox.runtime_version # => "v0.6.14" (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,8 @@ 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 |
|
|
560
|
+
| `0.15.0` | `v0.6.14` | adopts upstream `v0.6.10`–`v0.6.14` step by step: bind-mount correctness, guest bootstrap off the kernel command line, DNS pins for deferred domain allows, Linux glibc 2.28 baseline for the prebuilt runtime, legacy ext4 upper-disk resize, `msb_krun` 0.1.32. Parity: `ssh.open_client`/`prepare_server` accept `inactivity_timeout:` (seconds; `0` disables, `nil` inherits the 600s global default) |
|
|
505
561
|
|
|
506
562
|
**Going forward** — the gem version moves on its own semver track and no longer
|
|
507
563
|
mirrors the upstream tag:
|
|
@@ -555,7 +611,11 @@ or credential setup is needed.
|
|
|
555
611
|
the number to mirror the upstream tag. If the release also adopts a new upstream
|
|
556
612
|
runtime, bump the `tag = "vX.Y.Z"` on **both** the `microsandbox` and
|
|
557
613
|
`microsandbox-network` git deps, update `Microsandbox::RUNTIME_VERSION` to match,
|
|
558
|
-
and add a row to the Versioning table.
|
|
614
|
+
and add a row to the Versioning table. Also bump
|
|
615
|
+
`Microsandbox::Binaries::VERSION` (and, on a runtime adoption,
|
|
616
|
+
`::RUNTIME_VERSION`) in `binaries/lib/microsandbox/binaries.rb` — the
|
|
617
|
+
companion gem ships in lockstep and the specs assert both. Update
|
|
618
|
+
`CHANGELOG.md`.
|
|
559
619
|
2. Push a `vX.Y.Z` tag. CI builds the **source gem** and pushes it to RubyGems
|
|
560
620
|
via `rubygems/configure-rubygems-credentials` (OIDC, `id-token: write`) — no
|
|
561
621
|
`RUBYGEMS_API_KEY` secret required.
|
|
@@ -568,13 +628,29 @@ or credential setup is needed.
|
|
|
568
628
|
> prove otherwise — so promotion is manual after validating the artifact on each
|
|
569
629
|
> platform. A precompiled gem ships the compiled extension (with the guest
|
|
570
630
|
> `agentd` baked in by *target* arch); the host-side `msb` + `libkrunfw` runtime
|
|
571
|
-
> is
|
|
572
|
-
>
|
|
631
|
+
> is **not** in it — that comes from the companion `microsandbox-rb-binaries`
|
|
632
|
+
> gem, or, when that isn't installed, is fetched into `~/.microsandbox` on first
|
|
633
|
+
> use by `Microsandbox.ensure_runtime!` (libkrunfw is `dlopen`'d by `msb` at
|
|
634
|
+
> runtime, never linked into the gem). The
|
|
573
635
|
> real cross-compile work is linking the *target* native libraries — `libcap-ng`
|
|
574
636
|
> on Linux (handled via Debian multiarch in the workflow) and the Hypervisor +
|
|
575
637
|
> Security frameworks on macOS (via osxcross; the one platform left to confirm).
|
|
576
638
|
> Until promoted, users install the source gem (which compiles via `rb_sys`).
|
|
577
639
|
|
|
640
|
+
> **Runtime binaries gems** (`microsandbox-rb-binaries`, source in `binaries/`)
|
|
641
|
+
> are a *separate* artifact from the precompiled extension gems above: they carry
|
|
642
|
+
> no Ruby extension, only the upstream `msb` + `libkrunfw` for one platform,
|
|
643
|
+
> verified against the release's published `checksums.sha256` when vendored. CI's
|
|
644
|
+
> `binaries` job vendors and builds all three (`arm64-darwin`,
|
|
645
|
+
> `x86_64-linux-gnu`, `aarch64-linux-gnu`) on every run and smoke-tests the host
|
|
646
|
+
> one; `release.yml`'s `binaries-gems` job does the same on a tag and a separate
|
|
647
|
+
> `publish-binaries` job pushes them after the SDK gem is live (a companion
|
|
648
|
+
> failure is its own red job and never blocks the SDK release).
|
|
649
|
+
> They use their own RubyGems trusted-publisher entry (same repo + workflow,
|
|
650
|
+
> gem name `microsandbox-rb-binaries`). To build them by hand:
|
|
651
|
+
> `rake -C binaries vendor[<platform>]` then `rake -C binaries build[<platform>]`
|
|
652
|
+
> (→ `binaries/pkg/*.gem`).
|
|
653
|
+
|
|
578
654
|
See [DESIGN.md](DESIGN.md) for the architecture and the implemented-surface
|
|
579
655
|
section. The binding covers the official-SDK surface: sandbox
|
|
580
656
|
lifecycle (the live `Sandbox` `stop`/`stop_and_wait`/`kill`/`drain`/`wait`/
|
data/ext/microsandbox/Cargo.toml
CHANGED
|
@@ -6,8 +6,8 @@ name = "microsandbox_rb"
|
|
|
6
6
|
description = "Ruby SDK native extension for microsandbox — secure, fast microVM-based sandboxing."
|
|
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
|
-
# The core-crate dependency below stays pinned at its own tag (v0.6.
|
|
10
|
-
version = "0.
|
|
9
|
+
# The core-crate dependency below stays pinned at its own tag (v0.6.14).
|
|
10
|
+
version = "0.15.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`).
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
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.14", default-features = false, features = ["keyring", "net", "ssh"] }
|
|
46
|
+
microsandbox-network = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.14" }
|
|
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.14", 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"] }
|
|
@@ -866,11 +866,14 @@ impl Sandbox {
|
|
|
866
866
|
//----------------------------------------------------------------------
|
|
867
867
|
|
|
868
868
|
/// Open a native in-process SSH client to this sandbox. `opts`: user, term,
|
|
869
|
-
/// sftp (bool, default true).
|
|
869
|
+
/// sftp (bool, default true), inactivity_timeout (seconds, f64; 0 disables).
|
|
870
870
|
fn ssh_open_client(&self, opts: RHash) -> Result<crate::ssh::SshClient, Error> {
|
|
871
871
|
let user = conv::opt_string(opts, "user")?;
|
|
872
872
|
let term = conv::opt_string(opts, "term")?;
|
|
873
873
|
let sftp = conv::opt::<bool>(opts, "sftp")?.unwrap_or(true);
|
|
874
|
+
let inactivity_timeout = conv::opt_f64(opts, "inactivity_timeout")?
|
|
875
|
+
.map(secs_to_duration)
|
|
876
|
+
.transpose()?;
|
|
874
877
|
let ssh = self.inner.ssh();
|
|
875
878
|
let client = block_on(ssh.open_client_with(move |mut b| {
|
|
876
879
|
if let Some(u) = user {
|
|
@@ -879,6 +882,9 @@ impl Sandbox {
|
|
|
879
882
|
if let Some(t) = term {
|
|
880
883
|
b = b.term(t);
|
|
881
884
|
}
|
|
885
|
+
if let Some(t) = inactivity_timeout {
|
|
886
|
+
b = b.inactivity_timeout(t);
|
|
887
|
+
}
|
|
882
888
|
b.sftp(sftp)
|
|
883
889
|
}))
|
|
884
890
|
.map_err(error::to_ruby)?;
|
|
@@ -886,12 +892,16 @@ impl Sandbox {
|
|
|
886
892
|
}
|
|
887
893
|
|
|
888
894
|
/// Prepare a reusable SSH server endpoint. `opts`: host_key_path,
|
|
889
|
-
/// authorized_keys_path, user, sftp (bool, default true)
|
|
895
|
+
/// authorized_keys_path, user, sftp (bool, default true),
|
|
896
|
+
/// inactivity_timeout (seconds, f64; 0 disables).
|
|
890
897
|
fn ssh_prepare_server(&self, opts: RHash) -> Result<crate::ssh::SshServer, Error> {
|
|
891
898
|
let host_key_path = conv::opt_string(opts, "host_key_path")?;
|
|
892
899
|
let authorized_keys_path = conv::opt_string(opts, "authorized_keys_path")?;
|
|
893
900
|
let user = conv::opt_string(opts, "user")?;
|
|
894
901
|
let sftp = conv::opt::<bool>(opts, "sftp")?.unwrap_or(true);
|
|
902
|
+
let inactivity_timeout = conv::opt_f64(opts, "inactivity_timeout")?
|
|
903
|
+
.map(secs_to_duration)
|
|
904
|
+
.transpose()?;
|
|
895
905
|
let ssh = self.inner.ssh();
|
|
896
906
|
let server = block_on(ssh.prepare_server_with(move |mut b| {
|
|
897
907
|
if let Some(p) = host_key_path {
|
|
@@ -903,6 +913,9 @@ impl Sandbox {
|
|
|
903
913
|
if let Some(u) = user {
|
|
904
914
|
b = b.user(u);
|
|
905
915
|
}
|
|
916
|
+
if let Some(t) = inactivity_timeout {
|
|
917
|
+
b = b.inactivity_timeout(t);
|
|
918
|
+
}
|
|
906
919
|
b.sftp(sftp)
|
|
907
920
|
}))
|
|
908
921
|
.map_err(error::to_ruby)?;
|
data/lib/microsandbox/ssh.rb
CHANGED
|
@@ -231,11 +231,17 @@ module Microsandbox
|
|
|
231
231
|
# @param user [String] guest user to authenticate as (default "root")
|
|
232
232
|
# @param term [String, nil] TERM value for the session
|
|
233
233
|
# @param sftp [Boolean] enable the SFTP subsystem (default true)
|
|
234
|
+
# @param inactivity_timeout [Numeric, nil] per-session inactivity timeout in
|
|
235
|
+
# seconds; `nil` inherits the global config (default 600s), `0` disables it
|
|
234
236
|
# @yieldparam client [SshClient]
|
|
235
237
|
# @return [SshClient, Object]
|
|
236
|
-
def open_client(user: "root", term: nil, sftp: true)
|
|
238
|
+
def open_client(user: "root", term: nil, sftp: true, inactivity_timeout: nil)
|
|
237
239
|
opts = {"user" => user.to_s, "sftp" => sftp ? true : false}
|
|
238
240
|
opts["term"] = term.to_s if term
|
|
241
|
+
unless inactivity_timeout.nil?
|
|
242
|
+
opts["inactivity_timeout"] =
|
|
243
|
+
Sandbox.send(:coerce_duration, inactivity_timeout, "inactivity_timeout")
|
|
244
|
+
end
|
|
239
245
|
client = SshClient.new(@native.ssh_open_client(opts))
|
|
240
246
|
return client unless block_given?
|
|
241
247
|
|
|
@@ -251,12 +257,19 @@ module Microsandbox
|
|
|
251
257
|
# @param authorized_keys_path [String, nil] authorized_keys file path
|
|
252
258
|
# @param user [String, nil] guest user connections run as
|
|
253
259
|
# @param sftp [Boolean] enable the SFTP subsystem (default true)
|
|
260
|
+
# @param inactivity_timeout [Numeric, nil] per-session inactivity timeout in
|
|
261
|
+
# seconds; `nil` inherits the global config (default 600s), `0` disables it
|
|
254
262
|
# @return [SshServer]
|
|
255
|
-
def prepare_server(host_key_path: nil, authorized_keys_path: nil, user: nil, sftp: true
|
|
263
|
+
def prepare_server(host_key_path: nil, authorized_keys_path: nil, user: nil, sftp: true,
|
|
264
|
+
inactivity_timeout: nil)
|
|
256
265
|
opts = {"sftp" => sftp ? true : false}
|
|
257
266
|
opts["host_key_path"] = host_key_path.to_s if host_key_path
|
|
258
267
|
opts["authorized_keys_path"] = authorized_keys_path.to_s if authorized_keys_path
|
|
259
268
|
opts["user"] = user.to_s if user
|
|
269
|
+
unless inactivity_timeout.nil?
|
|
270
|
+
opts["inactivity_timeout"] =
|
|
271
|
+
Sandbox.send(:coerce_duration, inactivity_timeout, "inactivity_timeout")
|
|
272
|
+
end
|
|
260
273
|
SshServer.new(@native.ssh_prepare_server(opts))
|
|
261
274
|
end
|
|
262
275
|
end
|
data/lib/microsandbox/version.rb
CHANGED
|
@@ -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.
|
|
11
|
+
VERSION = "0.15.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.
|
|
18
|
+
RUNTIME_VERSION = "v0.6.14"
|
|
19
19
|
end
|