kobako 0.27.0 → 0.28.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/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +71 -0
- data/Cargo.lock +117 -140
- data/README.md +100 -125
- data/ROADMAP.md +23 -22
- data/SECURITY.md +1 -1
- data/crates/kobako-runtime/CHANGELOG.md +31 -0
- data/crates/kobako-runtime/Cargo.toml +2 -2
- data/crates/kobako-runtime/README.md +4 -3
- data/crates/kobako-runtime/src/error.rs +8 -9
- data/crates/kobako-runtime/src/lib.rs +1 -1
- data/crates/kobako-runtime/src/profile.rs +1 -1
- data/crates/kobako-runtime/src/runtime.rs +31 -17
- data/crates/kobako-runtime/src/snapshot.rs +13 -10
- data/crates/kobako-runtime/src/yielder.rs +9 -6
- data/crates/kobako-transport/CHANGELOG.md +28 -0
- data/crates/kobako-transport/Cargo.toml +1 -1
- data/crates/kobako-transport/README.md +21 -12
- data/crates/kobako-transport/src/abi.rs +11 -14
- data/crates/kobako-transport/src/envelope/bytes.rs +18 -6
- data/crates/kobako-transport/src/envelope/compile_reply.rs +139 -0
- data/crates/kobako-transport/src/envelope/outcome.rs +17 -16
- data/crates/kobako-transport/src/envelope/preamble.rs +513 -0
- data/crates/kobako-transport/src/envelope/reply.rs +77 -53
- data/crates/kobako-transport/src/envelope/request.rs +295 -0
- data/crates/kobako-transport/src/envelope/run.rs +1 -2
- data/crates/kobako-transport/src/envelope/tag.rs +49 -0
- data/crates/kobako-transport/src/envelope/unsupported.rs +105 -0
- data/crates/kobako-transport/src/envelope.rs +19 -12
- data/crates/kobako-transport/src/lib.rs +0 -2
- data/crates/kobako-wasmtime/CHANGELOG.md +44 -0
- data/crates/kobako-wasmtime/Cargo.toml +7 -5
- data/crates/kobako-wasmtime/README.md +2 -2
- data/crates/kobako-wasmtime/src/abi.rs +5 -8
- data/crates/kobako-wasmtime/src/ambient.rs +2 -2
- data/crates/kobako-wasmtime/src/cache.rs +25 -59
- data/crates/kobako-wasmtime/src/capture.rs +4 -11
- data/crates/kobako-wasmtime/src/config.rs +11 -14
- data/crates/kobako-wasmtime/src/dispatch.rs +70 -80
- data/crates/kobako-wasmtime/src/driver.rs +133 -86
- data/crates/kobako-wasmtime/src/exports.rs +88 -34
- data/crates/kobako-wasmtime/src/guest_mem.rs +145 -138
- data/crates/kobako-wasmtime/src/instance_pre.rs +11 -22
- data/crates/kobako-wasmtime/src/invocation.rs +55 -372
- data/crates/kobako-wasmtime/src/io.rs +286 -0
- data/crates/kobako-wasmtime/src/lib.rs +3 -24
- data/crates/kobako-wasmtime/src/limiter.rs +253 -0
- data/crates/kobako-wasmtime/src/trap.rs +32 -45
- data/crates/kobako-wasmtime/tests/preamble_flags.rs +69 -0
- data/crates/kobako-wasmtime/tests/snippet_compile.rs +94 -0
- data/crates/kobako-wasmtime/tests/support/mod.rs +34 -0
- data/crates/kobako-wasmtime/tests/unsupported_call.rs +50 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +2 -2
- data/ext/kobako/src/runtime/bridge.rs +38 -47
- data/ext/kobako/src/runtime/errors.rs +26 -32
- data/ext/kobako/src/runtime/gvl.rs +4 -12
- data/ext/kobako/src/runtime.rs +169 -212
- data/lib/kobako/capture.rb +1 -4
- data/lib/kobako/catalog/extensions.rb +21 -59
- data/lib/kobako/catalog/flags.rb +41 -0
- data/lib/kobako/catalog/handles.rb +7 -54
- data/lib/kobako/catalog/services.rb +7 -61
- data/lib/kobako/catalog/snippets.rb +42 -66
- data/lib/kobako/catalog.rb +2 -10
- data/lib/kobako/codec/decoder.rb +9 -28
- data/lib/kobako/codec/encoder.rb +3 -22
- data/lib/kobako/codec/error.rb +6 -27
- data/lib/kobako/codec/ext_types.rb +72 -94
- data/lib/kobako/codec/handle_walk.rb +65 -130
- data/lib/kobako/codec/nesting.rb +3 -8
- data/lib/kobako/codec/state.rb +4 -10
- data/lib/kobako/codec/utils.rb +6 -34
- data/lib/kobako/codec.rb +3 -22
- data/lib/kobako/constant_name.rb +8 -0
- data/lib/kobako/context.rb +51 -112
- data/lib/kobako/errors.rb +51 -127
- data/lib/kobako/execution.rb +16 -21
- data/lib/kobako/extension.rb +23 -40
- data/lib/kobako/handle.rb +7 -12
- data/lib/kobako/outcome.rb +51 -80
- data/lib/kobako/payload/arguments.rb +1 -16
- data/lib/kobako/payload.rb +4 -12
- data/lib/kobako/pool.rb +23 -53
- data/lib/kobako/runtime.rb +7 -21
- data/lib/kobako/sandbox.rb +96 -133
- data/lib/kobako/sandbox_options.rb +38 -84
- data/lib/kobako/snippet/binary.rb +1 -16
- data/lib/kobako/snippet/source.rb +1 -16
- data/lib/kobako/snippet.rb +1 -9
- data/lib/kobako/transport/call.rb +1 -14
- data/lib/kobako/transport/dispatcher.rb +112 -195
- data/lib/kobako/transport/error.rb +6 -20
- data/lib/kobako/transport/exposure.rb +5 -10
- data/lib/kobako/transport/reflection.rb +39 -50
- data/lib/kobako/transport/run.rb +17 -71
- data/lib/kobako/transport/yielder.rb +21 -73
- data/lib/kobako/transport.rb +3 -15
- data/lib/kobako/unresolved.rb +4 -11
- data/lib/kobako/usage.rb +2 -6
- data/lib/kobako/version.rb +1 -1
- data/sig/kobako/catalog/extensions.rbs +2 -2
- data/sig/kobako/catalog/flags.rbs +17 -0
- data/sig/kobako/catalog/services.rbs +0 -6
- data/sig/kobako/catalog/snippets.rbs +4 -4
- data/sig/kobako/codec/ext_types.rbs +10 -8
- data/sig/kobako/codec/handle_walk.rbs +9 -7
- data/sig/kobako/codec/utils.rbs +2 -2
- data/sig/kobako/codec.rbs +2 -2
- data/sig/kobako/constant_name.rbs +3 -0
- data/sig/kobako/context.rbs +2 -3
- data/sig/kobako/errors.rbs +6 -0
- data/sig/kobako/outcome.rbs +6 -8
- data/sig/kobako/runtime.rbs +12 -8
- data/sig/kobako/sandbox.rbs +4 -0
- data/sig/kobako/sandbox_options.rbs +2 -6
- data/sig/kobako/transport/dispatcher.rbs +14 -18
- data/sig/kobako/transport/reflection.rbs +5 -3
- data/sig/kobako/transport/run.rbs +1 -3
- data/sig/kobako/transport/yielder.rbs +3 -3
- data/sig/kobako/transport.rbs +7 -6
- metadata +16 -4
- data/crates/kobako-transport/src/envelope/invocation_frames.rs +0 -241
- data/crates/kobako-wasmtime/src/frames.rs +0 -273
- data/rust-toolchain.toml +0 -15
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.18.0](https://github.com/elct9620/kobako/compare/kobako-runtime-v0.17.0...kobako-runtime-v0.18.0) (2026-10-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* **transport:** the stdin layout a Guest Binary reads changed within ABI 4. kobako_transport::envelope::Frames is replaced by Preamble, which is #[non_exhaustive] and built from Default; Bindings and Snippets no longer have their own decode and encode. Guest::eval and Guest::run, MrbGuest::eval and MrbGuest::run, and Runtime::invoke take a Preamble in place of Frames. kobako_core re-exports Preamble and Flag in place of Frames.
|
|
9
|
+
* **transport:** the ABI version is 4, so a Guest Binary built for ABI 3 is refused at Sandbox construction. kobako_core::export_guest! emits __kobako_downcall in place of __kobako_eval, __kobako_run, __kobako_yield_to_block and __kobako_take_outcome, and the host import is __kobako_upcall. kobako_core::abi::{take_outcome, write_outcome, write_reply, decode_run} are replaced by serve and answer, and the frames module is no longer public. Reply, YieldReply and Outcome gain an Unsupported arm and take their tags from the shared registry, and kobako_core::DispatchError and kobako::YieldError gain an Unsupported arm. A yield to a guest without block support raises Kobako::UnsupportedError at the Service's yield instead of failing the invocation as a trap.
|
|
10
|
+
* **runtime:** Runtime::invoke takes &kobako_transport::envelope::Frames in place of kobako_runtime::Frames, whose byte fields are gone. Guest::eval and Guest::run (and their MrbGuest flows) receive the decoded Frames, eval also its source; kobako_core::frames::read_frame is replaced by read_eval_frames and read_frames.
|
|
11
|
+
* **runtime:** kobako_runtime::snapshot::Completion::Outcome holds a decoded kobako_transport::envelope::Outcome instead of Vec<u8>; an engine decodes the result itself and reports an unframed one as
|
|
12
|
+
* **runtime:** kobako_runtime::runtime::Entry::Run holds { entrypoint: &str, payload: Vec<u8> } instead of { envelope: &[u8] }; a frontend passes the fields, and an engine encodes the Run envelope itself.
|
|
13
|
+
* **runtime:** kobako_runtime::yielder::Yielder::yield_to_block returns Result<YieldReply, Trap> instead of the encoded bytes; an engine implementing it decodes the reply itself.
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **runtime:** compile a Preamble's snippets on an instance of their own ([303432d](https://github.com/elct9620/kobako/commit/303432dd90ed62dc378391515e68e784e1b473a4)), closes [#75](https://github.com/elct9620/kobako/issues/75)
|
|
18
|
+
* **transport:** carry each operation as a tag on one channel per direction ([c7d574b](https://github.com/elct9620/kobako/commit/c7d574b59a42e58632f2bdc46cc9a8e4c7ac6121)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
19
|
+
* **transport:** set an invocation up from a Preamble of tagged records ([84d4498](https://github.com/elct9620/kobako/commit/84d4498464f94d7312f26931ddd4ec43894a6654))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* keep a compile's bytecode only when the Preamble still fits it ([765db08](https://github.com/elct9620/kobako/commit/765db08ad31c8f5163794c7e144355f3237af93b))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Code Refactoring
|
|
28
|
+
|
|
29
|
+
* **runtime:** hand a frontend the decoded Outcome ([0da6aac](https://github.com/elct9620/kobako/commit/0da6aac0f7943d694a5a4ab9439b2d6c527ae1b0)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
30
|
+
* **runtime:** hand a frontend the decoded Yield Reply ([e80ab54](https://github.com/elct9620/kobako/commit/e80ab54e36c2a80b0f746c675f85bd1238fd4331)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
31
|
+
* **runtime:** let the ABI endpoints frame and read the Frames ([dea069a](https://github.com/elct9620/kobako/commit/dea069ac49a59c12a6f7102ed3cf2925f316cbff)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
32
|
+
* **runtime:** let the engine frame a run's entrypoint and payload ([62f9c94](https://github.com/elct9620/kobako/commit/62f9c9407ff3ce690723625bea075fa49d2dda47)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
33
|
+
|
|
3
34
|
## [0.17.0](https://github.com/elct9620/kobako/compare/kobako-runtime-v0.16.0...kobako-runtime-v0.17.0) (2026-09-20)
|
|
4
35
|
|
|
5
36
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
[package]
|
|
15
15
|
name = "kobako-runtime"
|
|
16
|
-
version = "0.
|
|
16
|
+
version = "0.18.0"
|
|
17
17
|
edition = "2021"
|
|
18
18
|
description = "Engine-neutral host runtime contract for embedding kobako Wasm guests."
|
|
19
19
|
license = "Apache-2.0"
|
|
@@ -27,4 +27,4 @@ categories = ["wasm", "virtualization"]
|
|
|
27
27
|
# in-tree builds (and the Ruby gem, which ships both crates) resolving
|
|
28
28
|
# locally.
|
|
29
29
|
[dependencies]
|
|
30
|
-
kobako-transport = { version = "0.
|
|
30
|
+
kobako-transport = { version = "0.18.0", path = "../kobako-transport" }
|
|
@@ -11,8 +11,9 @@ type, so the engine stays swappable.
|
|
|
11
11
|
| Module | What it declares |
|
|
12
12
|
|---|---|
|
|
13
13
|
| `runtime` | the `Runtime` trait: one guest invocation on a fresh instance in, its observable `Snapshot` out |
|
|
14
|
-
| `snapshot` |
|
|
15
|
-
| `error` | the neutral failure
|
|
14
|
+
| `snapshot` | each invocation's `Completion` (outcome or trap), both output `Capture`s, and `Usage` |
|
|
15
|
+
| `error` | the neutral failure types: `Trap`, `SetupError`, and `InvokeError` over both |
|
|
16
|
+
| `profile` | the isolation `Profile` a runtime declares |
|
|
16
17
|
| `dispatch` / `yielder` | the `DispatchHandler` and `Yielder` traits a frontend supplies for guest→host dispatch and block-yield re-entry |
|
|
17
18
|
|
|
18
19
|
Engine implementations (such as `kobako-wasmtime`) implement
|
|
@@ -23,7 +24,7 @@ map the neutral types onto their own language surface.
|
|
|
23
24
|
|
|
24
25
|
```toml
|
|
25
26
|
[dependencies]
|
|
26
|
-
kobako-runtime = "0.
|
|
27
|
+
kobako-runtime = "0.18.0" # x-release-please-version
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
## License
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
//! Engine-neutral host error
|
|
1
|
+
//! Engine-neutral host error types, free of any frontend dependency.
|
|
2
2
|
//!
|
|
3
3
|
//! The run path produces these instead of constructing host-language
|
|
4
4
|
//! exceptions directly; each frontend's boundary is the single place that
|
|
5
5
|
//! maps them onto its own error classes (the Ruby ext does so in its error
|
|
6
|
-
//! mapper). Keeping the
|
|
6
|
+
//! mapper). Keeping the types frontend-free lets any engine
|
|
7
7
|
//! implementation produce them unchanged.
|
|
8
8
|
|
|
9
9
|
use std::fmt;
|
|
@@ -26,11 +26,10 @@ pub enum Trap {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/// A failure that yields no invocation outcome. The discriminant records
|
|
29
|
-
/// the runtime's state so a frontend can attribute the failure
|
|
29
|
+
/// the runtime's state so a frontend can attribute the failure:
|
|
30
30
|
/// `ModuleNotBuilt` (the guest artifact is absent), `Dead` (the runtime
|
|
31
31
|
/// could not be constructed), and `Intact` (the runtime is live but a
|
|
32
|
-
/// host-side pre-call step failed
|
|
33
|
-
/// owed).
|
|
32
|
+
/// host-side pre-call step failed before the engine ran).
|
|
34
33
|
///
|
|
35
34
|
/// Non-exhaustive, as `Trap`: a frontend maps each state onto its own
|
|
36
35
|
/// class, and a later state must not break that match.
|
|
@@ -45,14 +44,14 @@ pub enum SetupError {
|
|
|
45
44
|
/// A failure that prevented the invocation from starting — a pre-call
|
|
46
45
|
/// engine fault (`Trap`) or a host-side setup fault (`Setup`) — unified so
|
|
47
46
|
/// the run mechanics can propagate both with `?`; a frontend destructures
|
|
48
|
-
/// it back into the two
|
|
47
|
+
/// it back into the two. Faults after the guest export starts
|
|
49
48
|
/// ride in `Completion::Trap` instead, so captures and usage survive them.
|
|
50
49
|
///
|
|
51
50
|
/// Named for where it comes from — the `Err` of `Runtime::invoke` — so a
|
|
52
51
|
/// host that also has its own `Error` in scope can hold both.
|
|
53
52
|
///
|
|
54
|
-
/// Non-exhaustive
|
|
55
|
-
///
|
|
53
|
+
/// Non-exhaustive: a later way an invocation can fail to start must not
|
|
54
|
+
/// break the frontends already destructuring it.
|
|
56
55
|
#[derive(Debug)]
|
|
57
56
|
#[non_exhaustive]
|
|
58
57
|
pub enum InvokeError {
|
|
@@ -96,7 +95,7 @@ impl fmt::Display for SetupError {
|
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
// Both
|
|
98
|
+
// Both types sit on the seam a third-party engine implements against,
|
|
100
99
|
// where a caller wants to lift them into `Box<dyn Error>` or `anyhow` with
|
|
101
100
|
// `?`. `Trap` carries no source of its own, so neither reports one.
|
|
102
101
|
impl std::error::Error for SetupError {}
|
|
@@ -24,6 +24,6 @@ pub mod yielder;
|
|
|
24
24
|
pub use dispatch::DispatchHandler;
|
|
25
25
|
pub use error::{InvokeError, SetupError, Trap};
|
|
26
26
|
pub use profile::Profile;
|
|
27
|
-
pub use runtime::{Entry,
|
|
27
|
+
pub use runtime::{Entry, Runtime};
|
|
28
28
|
pub use snapshot::{Capture, Completion, Snapshot, Usage};
|
|
29
29
|
pub use yielder::Yielder;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
/// `Hermetic` is the full ambient-denial posture: ambient time and
|
|
12
12
|
/// entropy denied at the WASI boundary, no filesystem / environment /
|
|
13
13
|
/// network reachability, and no host import beyond the wire ABI's
|
|
14
|
-
/// `
|
|
14
|
+
/// `__kobako_upcall`. `Permissive` differs in exactly one grant —
|
|
15
15
|
/// live ambient time and entropy at the WASI boundary. Ordering
|
|
16
16
|
/// follows strength (`Permissive < Hermetic`), so a floor check is a
|
|
17
17
|
/// plain comparison.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! Engine-neutral host runtime contract.
|
|
2
2
|
//!
|
|
3
3
|
//! The contract a wasm engine must satisfy to drive a kobako guest: take a
|
|
4
|
-
//! per-invocation entry plus its
|
|
4
|
+
//! per-invocation entry plus its Preamble, run one invocation on a
|
|
5
5
|
//! fresh instance, and return the observable `Snapshot` — `Ok` iff the
|
|
6
6
|
//! guest export ran, `Err` when the invocation never started. Nothing here
|
|
7
7
|
//! mentions `magnus` or a Ruby type — a frontend supplies the dispatch
|
|
@@ -9,29 +9,31 @@
|
|
|
9
9
|
|
|
10
10
|
use std::sync::Arc;
|
|
11
11
|
|
|
12
|
+
use kobako_transport::envelope::Preamble;
|
|
13
|
+
|
|
12
14
|
use crate::dispatch::DispatchHandler;
|
|
13
15
|
use crate::error::InvokeError;
|
|
14
16
|
use crate::profile::Profile;
|
|
15
17
|
use crate::snapshot::Snapshot;
|
|
16
18
|
|
|
17
19
|
/// The per-invocation entry: a one-shot mruby source (`Eval`) or an
|
|
18
|
-
/// entrypoint
|
|
19
|
-
/// `
|
|
20
|
+
/// entrypoint and its payload (`Run`). Both ride alongside the Sandbox's
|
|
21
|
+
/// `Preamble`; the runtime frames `Run` and the `Preamble` onto the wire,
|
|
22
|
+
/// so a frontend never encodes either. The payload is owned so that framing it needs no
|
|
23
|
+
/// second copy.
|
|
20
24
|
///
|
|
21
|
-
/// Closed rather than `#[non_exhaustive]`:
|
|
22
|
-
///
|
|
23
|
-
///
|
|
24
|
-
///
|
|
25
|
+
/// Closed rather than `#[non_exhaustive]`: each arm is an invocation the
|
|
26
|
+
/// downcall carries under its own tag, and a runtime that has not
|
|
27
|
+
/// learned a new one should fail to compile rather than fall into a
|
|
28
|
+
/// wildcard.
|
|
25
29
|
pub enum Entry<'a> {
|
|
26
|
-
Eval {
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
pub preamble: &'a [u8],
|
|
34
|
-
pub snippets: &'a [u8],
|
|
30
|
+
Eval {
|
|
31
|
+
source: &'a [u8],
|
|
32
|
+
},
|
|
33
|
+
Run {
|
|
34
|
+
entrypoint: &'a str,
|
|
35
|
+
payload: Vec<u8>,
|
|
36
|
+
},
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
/// Engine-neutral runtime: drives one guest invocation on a fresh instance
|
|
@@ -50,7 +52,7 @@ pub trait Runtime {
|
|
|
50
52
|
fn invoke(
|
|
51
53
|
&self,
|
|
52
54
|
entry: Entry<'_>,
|
|
53
|
-
|
|
55
|
+
preamble: &Preamble,
|
|
54
56
|
handler: Option<Arc<dyn DispatchHandler>>,
|
|
55
57
|
) -> Result<Snapshot, InvokeError>;
|
|
56
58
|
|
|
@@ -59,4 +61,16 @@ pub trait Runtime {
|
|
|
59
61
|
/// and a frontend refuses construction when the declaration falls
|
|
60
62
|
/// below the floor its host application requested.
|
|
61
63
|
fn profile(&self) -> Profile;
|
|
64
|
+
|
|
65
|
+
/// The bytecode of the Preamble's source Snippets, one per snippet in
|
|
66
|
+
/// order, compiled on a fresh instance that runs none of them. A
|
|
67
|
+
/// frontend hands over the Preamble an invocation sends and replays the
|
|
68
|
+
/// answer in its place, so every invocation after skips the compiler.
|
|
69
|
+
/// `None` whenever there is nothing to replay, an answer that would
|
|
70
|
+
/// carry that Preamble past the payload cap included: the frontend
|
|
71
|
+
/// keeps the source. The default compiles nothing, so an engine that
|
|
72
|
+
/// does not offer it still serves every invocation.
|
|
73
|
+
fn compile(&self, _preamble: &Preamble) -> Option<Vec<Vec<u8>>> {
|
|
74
|
+
None
|
|
75
|
+
}
|
|
62
76
|
}
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
//! frontend type and uniform across success and trap: how the invocation
|
|
5
5
|
//! completed, the two captured output channels, and the resource usage.
|
|
6
6
|
//! A `Snapshot` exists iff the guest export ran — a failure to even start
|
|
7
|
-
//! the invocation
|
|
7
|
+
//! the invocation comes back as the `Err` of `invoke` instead.
|
|
8
8
|
//!
|
|
9
9
|
//! `Usage` and `Capture` are shared with the tier above rather than
|
|
10
10
|
//! translated at its boundary: what they carry is already frontend-free,
|
|
11
11
|
//! so the Rust host SDK re-exports these types themselves.
|
|
12
12
|
|
|
13
13
|
use crate::error::Trap;
|
|
14
|
+
use kobako_transport::envelope::Outcome;
|
|
14
15
|
|
|
15
16
|
/// One captured output channel: the bytes the guest wrote (already clipped
|
|
16
17
|
/// to the channel's cap) and whether the cap was reached. `Default` is the
|
|
@@ -22,17 +23,19 @@ pub struct Capture {
|
|
|
22
23
|
pub truncated: bool,
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
/// How the guest invocation completed: `Outcome`
|
|
26
|
-
///
|
|
27
|
-
///
|
|
28
|
-
/// survives it.
|
|
26
|
+
/// How the guest invocation completed: `Outcome` is the result the guest
|
|
27
|
+
/// left, already framed; `Trap` is an engine fault after the export call
|
|
28
|
+
/// started, kept as a value so the rest of the `Snapshot` survives it.
|
|
29
29
|
///
|
|
30
|
-
///
|
|
31
|
-
///
|
|
32
|
-
///
|
|
33
|
-
///
|
|
30
|
+
/// A guest that returned without an Outcome the envelope can frame —
|
|
31
|
+
/// nothing written included — ends as a `Trap`: with no record to read,
|
|
32
|
+
/// there is nothing a frontend could attribute.
|
|
33
|
+
///
|
|
34
|
+
/// Closed rather than `#[non_exhaustive]`: an invocation either returned
|
|
35
|
+
/// a result or failed to, and there is no third way for one to end. A
|
|
36
|
+
/// wildcard would only hide a frontend that had not been told otherwise.
|
|
34
37
|
pub enum Completion {
|
|
35
|
-
Outcome(
|
|
38
|
+
Outcome(Outcome),
|
|
36
39
|
Trap(Trap),
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -4,19 +4,22 @@
|
|
|
4
4
|
//! During a guest→host dispatch, a Service method may yield to a guest
|
|
5
5
|
//! block. The host drives that re-entry through a `Yielder`: it ships the
|
|
6
6
|
//! yield-arguments payload into the in-flight guest and returns the
|
|
7
|
-
//! Yield Reply
|
|
7
|
+
//! decoded Yield Reply. What backs the re-entry — a wasmtime `Caller`, some
|
|
8
8
|
//! other engine handle — is the implementer's concern; the dispatch
|
|
9
9
|
//! contract sees only this trait.
|
|
10
10
|
|
|
11
|
+
use kobako_transport::envelope::YieldReply;
|
|
12
|
+
|
|
11
13
|
use crate::error::Trap;
|
|
12
14
|
|
|
13
15
|
/// Host-initiated re-entry into the in-flight guest instance to run a
|
|
14
16
|
/// yielded block.
|
|
15
17
|
///
|
|
16
|
-
/// `yield_to_block` ships `args`
|
|
17
|
-
///
|
|
18
|
-
/// trap-error mapping — when the re-entry traps,
|
|
19
|
-
/// result, or a payload exceeds
|
|
18
|
+
/// `yield_to_block` ships `args` as a `yield` downcall and returns the
|
|
19
|
+
/// decoded Yield Reply, `Unsupported` arm included, or a `Trap` — surfaced
|
|
20
|
+
/// through the frontend's trap-error mapping — when the re-entry traps,
|
|
21
|
+
/// the guest returns an empty or unframeable result, or a payload exceeds
|
|
22
|
+
/// the 16 MiB cap.
|
|
20
23
|
pub trait Yielder {
|
|
21
|
-
fn yield_to_block(&mut self, args: &[u8]) -> Result<
|
|
24
|
+
fn yield_to_block(&mut self, args: &[u8]) -> Result<YieldReply, Trap>;
|
|
22
25
|
}
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.18.0](https://github.com/elct9620/kobako/compare/kobako-transport-v0.17.0...kobako-transport-v0.18.0) (2026-10-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* **transport:** the stdin layout a Guest Binary reads changed within ABI 4. kobako_transport::envelope::Frames is replaced by Preamble, which is #[non_exhaustive] and built from Default; Bindings and Snippets no longer have their own decode and encode. Guest::eval and Guest::run, MrbGuest::eval and MrbGuest::run, and Runtime::invoke take a Preamble in place of Frames. kobako_core re-exports Preamble and Flag in place of Frames.
|
|
9
|
+
* **mruby:** CodecError::Unsupported is renamed to CodecError::Unserved; a codec of one's own that refuses a position returns the new name.
|
|
10
|
+
* **transport:** the ABI version is 4, so a Guest Binary built for ABI 3 is refused at Sandbox construction. kobako_core::export_guest! emits __kobako_downcall in place of __kobako_eval, __kobako_run, __kobako_yield_to_block and __kobako_take_outcome, and the host import is __kobako_upcall. kobako_core::abi::{take_outcome, write_outcome, write_reply, decode_run} are replaced by serve and answer, and the frames module is no longer public. Reply, YieldReply and Outcome gain an Unsupported arm and take their tags from the shared registry, and kobako_core::DispatchError and kobako::YieldError gain an Unsupported arm. A yield to a guest without block support raises Kobako::UnsupportedError at the Service's yield instead of failing the invocation as a trap.
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* **transport:** carry a compile request and its answer ([f30958e](https://github.com/elct9620/kobako/commit/f30958e7d61c95667c32cfee7d9b2d2f74550a8e)), closes [#75](https://github.com/elct9620/kobako/issues/75)
|
|
15
|
+
* **transport:** carry each operation as a tag on one channel per direction ([c7d574b](https://github.com/elct9620/kobako/commit/c7d574b59a42e58632f2bdc46cc9a8e4c7ac6121)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
16
|
+
* **transport:** keep an unknown request's body, and refuse tag 0x00 ([edb64df](https://github.com/elct9620/kobako/commit/edb64df8f87694d17c5b6fd71ac63329023306ec)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
17
|
+
* **transport:** name the Frames a Sandbox is set up with ([52b25aa](https://github.com/elct9620/kobako/commit/52b25aa48d321ce8ea7dc9532cfade062118c64f)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
18
|
+
* **transport:** set an invocation up from a Preamble of tagged records ([84d4498](https://github.com/elct9620/kobako/commit/84d4498464f94d7312f26931ddd4ec43894a6654))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* **transport:** read only an unassigned tag as an unknown request ([fcb81b5](https://github.com/elct9620/kobako/commit/fcb81b5c38d001b729a10287443c5012281b76b6)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
24
|
+
* **transport:** size a counted list by what its bytes can hold ([cfcc9fb](https://github.com/elct9620/kobako/commit/cfcc9fb6e79ec0bd1e6bf3a15659220aab0f6914))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Code Refactoring
|
|
28
|
+
|
|
29
|
+
* **mruby:** name a codec's refused position Unserved ([91979f9](https://github.com/elct9620/kobako/commit/91979f9546a3fa2d2dec8c136ed6affc521ba6fb)), closes [#73](https://github.com/elct9620/kobako/issues/73)
|
|
30
|
+
|
|
3
31
|
## [0.17.0](https://github.com/elct9620/kobako/compare/kobako-transport-v0.16.0...kobako-transport-v0.17.0) (2026-09-20)
|
|
4
32
|
|
|
5
33
|
|
|
@@ -9,22 +9,29 @@ same in every assembly. This crate is both of them, and nothing else.
|
|
|
9
9
|
|
|
10
10
|
| Module | What it fixes |
|
|
11
11
|
|---|---|
|
|
12
|
-
| `envelope` | the **core envelope**, the outer frame each message rides in
|
|
13
|
-
| `abi` | the values
|
|
12
|
+
| `envelope` | the **core envelope**, the outer frame each message rides in |
|
|
13
|
+
| `abi` | the values both sides agree on before exchanging a byte |
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The envelope covers the downcall's and the upcall's requests under one
|
|
16
|
+
tag registry. It also covers a dispatch round-trip, how an invocation
|
|
17
|
+
ended, and what an invocation is handed, and re-exports every message
|
|
18
|
+
type. The ABI values are the version, the packed `(ptr, len)` return
|
|
19
|
+
layout, the length prefix the Preamble rides stdin behind, and the
|
|
20
|
+
message size cap.
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
## Opaque Payload
|
|
23
|
+
|
|
24
|
+
The envelope reads a message's routing fields and its answer's tag
|
|
18
25
|
without decoding a payload byte. Everything the resolved method consumes
|
|
19
|
-
rides through opaque
|
|
20
|
-
|
|
26
|
+
rides through opaque. A decoded envelope borrows the buffer it came from,
|
|
27
|
+
so that payload reaches its reader as a view rather than a copy.
|
|
21
28
|
|
|
22
29
|
```text
|
|
23
30
|
Call { method, payload }
|
|
24
31
|
└── opaque here; read by whoever owns the schema
|
|
25
32
|
```
|
|
26
33
|
|
|
27
|
-
##
|
|
34
|
+
## Dependency Position
|
|
28
35
|
|
|
29
36
|
This crate depends on no other, and every kobako tier depends on it —
|
|
30
37
|
including any third-party engine, codec, or guest composing against them.
|
|
@@ -34,17 +41,19 @@ host kobako-runtime · kobako-wasmtime ──┐
|
|
|
34
41
|
guest kobako-core · kobako-mruby ────────┴──> kobako-transport
|
|
35
42
|
```
|
|
36
43
|
|
|
37
|
-
The
|
|
38
|
-
|
|
39
|
-
the golden vectors in this crate are derived from that document.
|
|
44
|
+
The golden vectors in this crate are derived from the kobako repository's
|
|
45
|
+
byte-layout document, not from this code.
|
|
40
46
|
|
|
41
47
|
## Usage
|
|
42
48
|
|
|
49
|
+
Depend on it directly only to build a tier of your own; a guest shell
|
|
50
|
+
reaches the same types through `kobako-core`.
|
|
51
|
+
|
|
43
52
|
```toml
|
|
44
53
|
[dependencies]
|
|
45
|
-
kobako-transport = "0.
|
|
54
|
+
kobako-transport = "0.18.0" # x-release-please-version
|
|
46
55
|
```
|
|
47
56
|
|
|
48
57
|
## License
|
|
49
58
|
|
|
50
|
-
Apache-2.0
|
|
59
|
+
Licensed under [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0).
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
//! The ABI's fixed values — the numbers and layouts a host and a guest
|
|
2
2
|
//! must already agree on before either can read the other's bytes.
|
|
3
3
|
//!
|
|
4
|
-
//! The
|
|
5
|
-
//!
|
|
6
|
-
//!
|
|
4
|
+
//! The symbols that carry the ABI are declared where they are
|
|
5
|
+
//! implemented: the guest contract crate emits the exports, the driver
|
|
6
|
+
//! resolves them and provides the import. What lives here is what both sides must spell the
|
|
7
7
|
//! same way, and each value is spelled once.
|
|
8
|
-
//!
|
|
9
|
-
//! [ABI signatures]: ../../../docs/wire-codec.md
|
|
10
8
|
|
|
11
9
|
/// The Guest ABI version. A host accepts a Guest Binary only when the
|
|
12
|
-
/// version it reports equals this one, so a
|
|
13
|
-
/// rather than a negotiation.
|
|
14
|
-
|
|
10
|
+
/// version it reports equals this one, so a framing or symbol change is
|
|
11
|
+
/// an increment rather than a negotiation. A new request tag needs
|
|
12
|
+
/// neither, but a new answer arm is an increment.
|
|
13
|
+
pub const ABI_VERSION: u32 = 4;
|
|
15
14
|
|
|
16
|
-
/// Width in bytes of the length prefix that
|
|
17
|
-
///
|
|
15
|
+
/// Width in bytes of the length prefix that frames a message on WASI
|
|
16
|
+
/// stdin, such as the Preamble.
|
|
18
17
|
pub const FRAME_LEN_SIZE: usize = 4;
|
|
19
18
|
|
|
20
19
|
/// Single-dispatch size cap: 16 MiB in either direction, applied to the
|
|
@@ -26,16 +25,14 @@ pub const MAX_DISPATCH_PAYLOAD: usize = 16 * 1024 * 1024;
|
|
|
26
25
|
/// length before it can check anything else about it, so this bounds what a
|
|
27
26
|
/// declared length may make it allocate. It sits above
|
|
28
27
|
/// `MAX_DISPATCH_PAYLOAD` because it guards the read rather than enforcing
|
|
29
|
-
/// the contract:
|
|
30
|
-
/// its merits.
|
|
28
|
+
/// the contract: the host enforces the 16 MiB cap before it sends a frame.
|
|
31
29
|
pub const MAX_FRAME_LEN: usize = 64 * 1024 * 1024;
|
|
32
30
|
|
|
33
31
|
// The read guard must admit every frame the contract allows, so a refusal
|
|
34
32
|
// names the cap rather than the allocation ceiling.
|
|
35
33
|
const _: () = assert!(MAX_FRAME_LEN > MAX_DISPATCH_PAYLOAD);
|
|
36
34
|
|
|
37
|
-
/// Pack `(ptr, len)` into the u64 the
|
|
38
|
-
/// functions answer with: the high 32 bits carry the linear-memory pointer,
|
|
35
|
+
/// Pack `(ptr, len)` into the u64 the downcall and the upcall answer with: the high 32 bits carry the linear-memory pointer,
|
|
39
36
|
/// the low 32 the byte length. A `len` of zero is the failure signal.
|
|
40
37
|
///
|
|
41
38
|
/// ```text
|
|
@@ -54,21 +54,33 @@ impl<'a> Reader<'a> {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/// A `u32` count followed by that many byte strings, each carrying
|
|
57
|
-
/// UTF-8.
|
|
58
|
-
/// is the only list shape a reader needs.
|
|
57
|
+
/// UTF-8.
|
|
59
58
|
///
|
|
60
59
|
/// The elements are copied rather than borrowed: a binding's path and
|
|
61
60
|
/// a panic's `available` names outlive the frame buffer they arrive in.
|
|
62
61
|
pub(crate) fn text_list(&mut self) -> Result<Vec<String>, DecodeError> {
|
|
62
|
+
self.list(|reader| Ok(reader.text()?.to_owned()))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// A `u32` count followed by that many byte strings of any content,
|
|
66
|
+
/// copied for the same reason as `text_list`.
|
|
67
|
+
pub(crate) fn bytes_list(&mut self) -> Result<Vec<Vec<u8>>, DecodeError> {
|
|
68
|
+
self.list(|reader| Ok(reader.bytes()?.to_vec()))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn list<T>(
|
|
72
|
+
&mut self,
|
|
73
|
+
mut element: impl FnMut(&mut Self) -> Result<T, DecodeError>,
|
|
74
|
+
) -> Result<Vec<T>, DecodeError> {
|
|
63
75
|
let count = self.u32()? as usize;
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
if count > self.remaining().len() {
|
|
76
|
+
// Every element opens with a `u32` length, so a count past what the
|
|
77
|
+
// bytes left can hold is refused before it sizes the allocation.
|
|
78
|
+
if count > self.remaining().len() / size_of::<u32>() {
|
|
67
79
|
return Err(MALFORMED);
|
|
68
80
|
}
|
|
69
81
|
let mut out = Vec::with_capacity(count);
|
|
70
82
|
for _ in 0..count {
|
|
71
|
-
out.push(self
|
|
83
|
+
out.push(element(self)?);
|
|
72
84
|
}
|
|
73
85
|
Ok(out)
|
|
74
86
|
}
|