kobako 0.27.0-aarch64-linux → 0.28.0-aarch64-linux

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +71 -0
  4. data/README.md +100 -125
  5. data/ROADMAP.md +23 -22
  6. data/SECURITY.md +1 -1
  7. data/crates/kobako-runtime/CHANGELOG.md +31 -0
  8. data/crates/kobako-runtime/README.md +4 -3
  9. data/crates/kobako-transport/CHANGELOG.md +28 -0
  10. data/crates/kobako-transport/README.md +21 -12
  11. data/crates/kobako-wasmtime/CHANGELOG.md +44 -0
  12. data/crates/kobako-wasmtime/README.md +2 -2
  13. data/data/kobako.wasm +0 -0
  14. data/lib/kobako/3.3/kobako.so +0 -0
  15. data/lib/kobako/3.4/kobako.so +0 -0
  16. data/lib/kobako/4.0/kobako.so +0 -0
  17. data/lib/kobako/capture.rb +1 -4
  18. data/lib/kobako/catalog/extensions.rb +21 -59
  19. data/lib/kobako/catalog/flags.rb +41 -0
  20. data/lib/kobako/catalog/handles.rb +7 -54
  21. data/lib/kobako/catalog/services.rb +7 -61
  22. data/lib/kobako/catalog/snippets.rb +42 -66
  23. data/lib/kobako/catalog.rb +2 -10
  24. data/lib/kobako/codec/decoder.rb +9 -28
  25. data/lib/kobako/codec/encoder.rb +3 -22
  26. data/lib/kobako/codec/error.rb +6 -27
  27. data/lib/kobako/codec/ext_types.rb +72 -94
  28. data/lib/kobako/codec/handle_walk.rb +65 -130
  29. data/lib/kobako/codec/nesting.rb +3 -8
  30. data/lib/kobako/codec/state.rb +4 -10
  31. data/lib/kobako/codec/utils.rb +6 -34
  32. data/lib/kobako/codec.rb +3 -22
  33. data/lib/kobako/constant_name.rb +8 -0
  34. data/lib/kobako/context.rb +51 -112
  35. data/lib/kobako/errors.rb +51 -127
  36. data/lib/kobako/execution.rb +16 -21
  37. data/lib/kobako/extension.rb +23 -40
  38. data/lib/kobako/handle.rb +7 -12
  39. data/lib/kobako/outcome.rb +51 -80
  40. data/lib/kobako/payload/arguments.rb +1 -16
  41. data/lib/kobako/payload.rb +4 -12
  42. data/lib/kobako/pool.rb +23 -53
  43. data/lib/kobako/runtime.rb +7 -21
  44. data/lib/kobako/sandbox.rb +96 -133
  45. data/lib/kobako/sandbox_options.rb +38 -84
  46. data/lib/kobako/snippet/binary.rb +1 -16
  47. data/lib/kobako/snippet/source.rb +1 -16
  48. data/lib/kobako/snippet.rb +1 -9
  49. data/lib/kobako/transport/call.rb +1 -14
  50. data/lib/kobako/transport/dispatcher.rb +112 -195
  51. data/lib/kobako/transport/error.rb +6 -20
  52. data/lib/kobako/transport/exposure.rb +5 -10
  53. data/lib/kobako/transport/reflection.rb +39 -50
  54. data/lib/kobako/transport/run.rb +17 -71
  55. data/lib/kobako/transport/yielder.rb +21 -73
  56. data/lib/kobako/transport.rb +3 -15
  57. data/lib/kobako/unresolved.rb +4 -11
  58. data/lib/kobako/usage.rb +2 -6
  59. data/lib/kobako/version.rb +1 -1
  60. data/sig/kobako/catalog/extensions.rbs +2 -2
  61. data/sig/kobako/catalog/flags.rbs +17 -0
  62. data/sig/kobako/catalog/services.rbs +0 -6
  63. data/sig/kobako/catalog/snippets.rbs +4 -4
  64. data/sig/kobako/codec/ext_types.rbs +10 -8
  65. data/sig/kobako/codec/handle_walk.rbs +9 -7
  66. data/sig/kobako/codec/utils.rbs +2 -2
  67. data/sig/kobako/codec.rbs +2 -2
  68. data/sig/kobako/constant_name.rbs +3 -0
  69. data/sig/kobako/context.rbs +2 -3
  70. data/sig/kobako/errors.rbs +6 -0
  71. data/sig/kobako/outcome.rbs +6 -8
  72. data/sig/kobako/runtime.rbs +12 -8
  73. data/sig/kobako/sandbox.rbs +4 -0
  74. data/sig/kobako/sandbox_options.rbs +2 -6
  75. data/sig/kobako/transport/dispatcher.rbs +14 -18
  76. data/sig/kobako/transport/reflection.rbs +5 -3
  77. data/sig/kobako/transport/run.rbs +1 -3
  78. data/sig/kobako/transport/yielder.rbs +3 -3
  79. data/sig/kobako/transport.rbs +7 -6
  80. metadata +6 -3
  81. 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
 
@@ -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` | the per-invocation observables — `Completion` (outcome or trap), the two output `Capture`s, and resource `Usage`, uniform across success and trap |
15
- | `error` | the neutral failure channels: `Trap` (engine fault) and `SetupError` (the invocation never started) |
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.17.0" # x-release-please-version
27
+ kobako-runtime = "0.18.0" # x-release-please-version
27
28
  ```
28
29
 
29
30
  ## License
@@ -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: `Call` / `Reply` / `YieldReply` for a dispatch round-trip, `Outcome` / `Panic` for how an invocation ended, `Run` / `Preamble` / `Snippets` for what an invocation is handed, and the `ErrorRecord` a guest failure carries |
13
- | `abi` | the values a host and a guest must already agree on to exchange a byte: the version, the packed `(ptr, len)` return layout, the invocation-channel frame prefix, and the message size cap |
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
- ## What the envelope does not read
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
- The envelope reads a message's routing fields and its ok-versus-fault tag
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, and a decoded envelope borrows the buffer it came
20
- from, so that payload reaches its reader as a view rather than a copy.
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
- ## Where it sits
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 byte layout is specified in
38
- [`docs/wire/envelope.md`](https://github.com/elct9620/kobako/blob/main/docs/wire/envelope.md);
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.17.0" # x-release-please-version
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,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.18.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.17.0...kobako-wasmtime-v0.18.0) (2026-10-13)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * the kobako and kobako-wasmtime crates declare a minimum supported Rust version of 1.96.
9
+ * **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.
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
+ * **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.
12
+ * **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
13
+ * **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.
14
+ * **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.
15
+ * a memory budget or deadline reached during a Service answer or a yielded block now raises Kobako::MemoryLimitError or
16
+
17
+ ### Features
18
+
19
+ * **guest:** raise Kobako::UnsupportedError when a host does not serve a call ([db003af](https://github.com/elct9620/kobako/commit/db003af75cf2d11b5f644986aa77c0408f5f59ba)), closes [#73](https://github.com/elct9620/kobako/issues/73)
20
+ * **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)
21
+ * **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)
22
+ * **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)
23
+ * **transport:** set an invocation up from a Preamble of tagged records ([84d4498](https://github.com/elct9620/kobako/commit/84d4498464f94d7312f26931ddd4ec43894a6654))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * end the invocation as the trap a host callback into the guest hits ([b8f47d3](https://github.com/elct9620/kobako/commit/b8f47d3699a19adcfd5596af93136401a286c761))
29
+ * keep a compile's bytecode only when the Preamble still fits it ([765db08](https://github.com/elct9620/kobako/commit/765db08ad31c8f5163794c7e144355f3237af93b))
30
+ * **wasmtime:** keep a compile answer only when it covers every source snippet ([93e9ddd](https://github.com/elct9620/kobako/commit/93e9ddd79f714446ae713a3145c59d3d2f4145dc))
31
+ * **wasmtime:** tell every frontend how to recover, not only the gem ([cab805a](https://github.com/elct9620/kobako/commit/cab805ab62441a0ca2c78775b7a0d7ce9cc05c43))
32
+ * **wasmtime:** word a missing or mistyped export one way ([8ab0aee](https://github.com/elct9620/kobako/commit/8ab0aeec8bda2960b6cab0502ef51860ea9093b8))
33
+
34
+
35
+ ### Code Refactoring
36
+
37
+ * **runtime:** hand a frontend the decoded Outcome ([0da6aac](https://github.com/elct9620/kobako/commit/0da6aac0f7943d694a5a4ab9439b2d6c527ae1b0)), closes [#73](https://github.com/elct9620/kobako/issues/73)
38
+ * **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)
39
+ * **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)
40
+ * **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)
41
+
42
+
43
+ ### Build System
44
+
45
+ * declare the Rust 1.96 floor wasmtime 49 sets ([f49a2df](https://github.com/elct9620/kobako/commit/f49a2df88fa51d3b0eecb619cab1e98d79bb4245))
46
+
3
47
  ## [0.17.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.16.0...kobako-wasmtime-v0.17.0) (2026-09-20)
4
48
 
5
49
 
@@ -13,7 +13,7 @@ contract surface.
13
13
  | Compilation | process-wide Engine and compiled-Module caches, with an on-disk AOT (`.cwasm`) artifact cache keyed by Guest Binary content |
14
14
  | Instantiation | a pre-linked `InstancePre` per guest path; every invocation runs on a fresh instance and discards its Store afterwards |
15
15
  | Bounds | the epoch-based wall-clock timeout and the per-invocation linear-memory cap |
16
- | Ambient denial | frozen WASI clocks and a constant RNG, so a guest observes no real time and no real entropy |
16
+ | Ambient denial | under the default hermetic profile, frozen clocks and a constant RNG |
17
17
 
18
18
  The kobako Ruby gem's native ext is the first frontend; a Rust host
19
19
  SDK consumes the same surface.
@@ -22,7 +22,7 @@ SDK consumes the same surface.
22
22
 
23
23
  ```toml
24
24
  [dependencies]
25
- kobako-wasmtime = "0.17.0" # x-release-please-version
25
+ kobako-wasmtime = "0.18.0" # x-release-please-version
26
26
  ```
27
27
 
28
28
  ## License
data/data/kobako.wasm CHANGED
Binary file
Binary file
Binary file
Binary file
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- # What one invocation wrote to stdout or stderr, up to that channel's
5
- # cap, together with whether it wrote past the cap. Frozen, so the bytes
6
- # and the flag always travel together.
7
- class Capture
4
+ class Capture # :nodoc:
8
5
  attr_reader :bytes
9
6
 
10
7
  # The bytes read as UTF-8 when they are valid UTF-8, and as binary
@@ -4,37 +4,17 @@ require_relative "../unresolved"
4
4
 
5
5
  module Kobako
6
6
  module Catalog
7
- # Kobako::Catalog::Extensions — per-Sandbox registry of installed
8
- # Extensions. Composes each Extension onto the sibling registries at
9
- # install time (its +source+ into +Catalog::Snippets+, its +backend+
10
- # path into +Catalog::Services+), asserts declared dependencies are
11
- # present when the Sandbox seals, and resolves each callable-backed
12
- # path to a fresh object for every invocation — returning that map to the
13
- # driving +Context+ rather than mutating the shared registry, so
14
- # concurrent invocations stay shared-nothing.
15
- #
16
- # Sealing and the reject-after-install guard are governed by the owning
17
- # Sandbox through +Catalog::Services#sealed?+, the shared seal signal;
18
- # this registry only records entries and enforces the Extension-shape
19
- # and dependency rules.
20
- class Extensions
7
+ # The installed Extensions, composed onto the Service and snippet
8
+ # registries. Providers resolve afresh for each invocation and are handed
9
+ # back rather than stored, so concurrent invocations share nothing.
10
+ class Extensions # :nodoc:
21
11
  def initialize
22
12
  @entries = [] # : Array[untyped]
23
13
  @asserted = false
24
14
  end
25
15
 
26
- # Install +extension+: validate its shape, register its +source+ as a
27
- # preloaded snippet, and — when it carries a +backend+ — reserve the
28
- # backend path in +services+. A static +object:+ binds directly; a
29
- # per-invocation +provider:+ or a fillable backend reserves the path with
30
- # +Kobako::Unresolved+ (the per-invocation result is layered onto the
31
- # Context, a fillable stays Unresolved until filled). Raises
32
- # +ArgumentError+ for the two shapes +#validate!+ owns — +source+ not a
33
- # String, or a present +backend+ that omits +path+ / +object+ /
34
- # +provider+. The Extension readers are duck-typed, so an object missing
35
- # them surfaces the underlying +NoMethodError+, and a malformed +name+ or
36
- # +backend.path+ surfaces through the +snippets+ / +services+
37
- # registration it routes to.
16
+ # The Extension readers are duck-typed, so a malformed +name+ or
17
+ # +backend.path+ surfaces through the registry it routes to.
38
18
  def install(extension, snippets:, services:)
39
19
  validate!(extension)
40
20
  snippets.register(code: extension.source, name: extension.name)
@@ -44,27 +24,24 @@ module Kobako
44
24
  self
45
25
  end
46
26
 
47
- # Assert every installed Extension's +depends_on+ names a fellow
48
- # installed Extension. Runs once, when the Sandbox first seals
49
- # successfully; an unmet dependency raises +ArgumentError+ naming the
50
- # gap, before the guest runs. The asserted flag flips only on success,
51
- # so a seal that failed re-checks on the next attempt rather than
52
- # silently passing a broken Sandbox. Idempotent across later invocations.
53
- def seal!
27
+ # The asserted flag flips only on success, so a check that failed
28
+ # re-runs on the next invocation rather than silently passing a broken
29
+ # Sandbox.
30
+ def assert_dependencies!
54
31
  return self if @asserted
55
32
 
56
- assert_dependencies!
33
+ extension, dependency = unmet_dependency
34
+ if extension
35
+ raise ArgumentError,
36
+ "Extension #{extension.name.inspect} depends on #{dependency.inspect}, which is not installed"
37
+ end
38
+
57
39
  @asserted = true
58
40
  self
59
41
  end
60
42
 
61
- # Resolve each +provider:+-backed path to this invocation's object and
62
- # return the +path => object+ map the driving +Context+ layers over the
63
- # static base bindings. Distinct providers yield distinct objects; one
64
- # provider shared by several Extensions is invoked once and its result
65
- # shared, so provider identity is resource identity. Static and fillable
66
- # backends are omitted — they stay the object bound at install in the
67
- # base registry (+Kobako::Unresolved+ for a fillable).
43
+ # One provider shared by several Extensions is invoked once and its
44
+ # result shared, so provider identity is resource identity.
68
45
  def resolve
69
46
  resolved = {} # : Hash[String, untyped]
70
47
  by_provider = {} # : Hash[untyped, untyped]
@@ -75,15 +52,8 @@ module Kobako
75
52
 
76
53
  private
77
54
 
78
- # The object a backend binds at install: a static +object:+ directly, or
79
- # +Kobako::Unresolved+ as the placeholder for a per-invocation +provider:+
80
- # (layered onto the Context) or a fillable (neither keyword).
81
55
  def install_object(backend) = backend.object.nil? ? Kobako::Unresolved : backend.object
82
56
 
83
- # Resolve one Extension's +provider:+-backed path against the shared
84
- # per-invocation identity cache (keyed by provider identity) and record
85
- # it in +resolved+. A static or fillable backend is skipped — its object
86
- # stays as bound at install in the base registry.
87
57
  def resolve_backend(extension, resolved, by_provider)
88
58
  backend = extension.backend
89
59
  return unless backend
@@ -94,9 +64,6 @@ module Kobako
94
64
  resolved[backend.path.to_s] = by_provider.fetch(provider) { by_provider[provider] = provider.call }
95
65
  end
96
66
 
97
- # Enforce the Extension-shape checks +#preload+ / +#bind+ do not: a
98
- # mandatory String +source+ (the install/bind boundary) and a
99
- # +backend+ that, when present, exposes +path+ / +object+ / +provider+.
100
67
  def validate!(extension)
101
68
  source = extension.source
102
69
  raise ArgumentError, "Extension #source must be a String, got #{source.class}" unless source.is_a?(String)
@@ -108,21 +75,16 @@ module Kobako
108
75
  raise ArgumentError, "Extension #backend must expose #path, #object, and #provider"
109
76
  end
110
77
 
111
- def assert_dependencies!
78
+ def unmet_dependency
112
79
  names = @entries.map { |extension| symbolize(extension.name) }
113
80
  @entries.each do |extension|
114
81
  (extension.depends_on || []).each do |dependency|
115
- next if names.include?(symbolize(dependency))
116
-
117
- raise ArgumentError,
118
- "Extension #{extension.name.inspect} depends on #{dependency.inspect}, which is not installed"
82
+ return [extension, dependency] unless names.include?(symbolize(dependency))
119
83
  end
120
84
  end
85
+ nil
121
86
  end
122
87
 
123
- # Match names and dependencies by Symbol so the Symbol-or-String forms
124
- # of a constant token are interchangeable; a value that is neither
125
- # falls through unchanged to the not-installed path.
126
88
  def symbolize(name) = name.is_a?(String) ? name.to_sym : name
127
89
  end
128
90
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kobako
4
+ module Catalog
5
+ class Flags # :nodoc:
6
+ def initialize
7
+ @entries = {} # : Hash[String, [String, bool]]
8
+ end
9
+
10
+ # Each entry is the name, the value, and whether the flag is critical.
11
+ def entries
12
+ @entries.map { |name, (value, critical)| [name, value, critical] }
13
+ end
14
+
15
+ def register(name, value, advisory:)
16
+ unless name.is_a?(Symbol) || name.is_a?(String)
17
+ raise ArgumentError, "flag name must be a Symbol or String, got #{name.class}"
18
+ end
19
+ raise ArgumentError, "flag value must be a String, got #{value.class}" unless value.is_a?(String)
20
+
21
+ key = utf8!(name.to_s, "flag name")
22
+ raise ArgumentError, "flag #{key.inspect} already set" if @entries.key?(key)
23
+
24
+ @entries[key] = [utf8!(value, "flag value"), !advisory]
25
+ nil
26
+ end
27
+
28
+ private
29
+
30
+ # The native side carries a flag only as UTF-8 text, so text read as
31
+ # bytes is re-tagged, and bytes that are not UTF-8 are refused here
32
+ # rather than failing every invocation.
33
+ def utf8!(text, what)
34
+ utf8 = text.dup.force_encoding(Encoding::UTF_8)
35
+ raise ArgumentError, "#{what} must be UTF-8 text" unless utf8.valid_encoding?
36
+
37
+ utf8
38
+ end
39
+ end
40
+ end
41
+ end
@@ -7,30 +7,11 @@ require_relative "../transport/exposure"
7
7
 
8
8
  module Kobako
9
9
  module Catalog
10
- # Host-side mapping from opaque integer Handle IDs to Ruby objects.
11
- # Each invocation's +Kobako::Context+ mints its own table and hands it to
12
- # the dispatcher, so guest→host dispatch resolves Handle targets and
13
- # arguments against the same table that host→guest wire encoding allocates
14
- # into for that run.
15
- #
16
- # Lifecycle invariants:
17
- #
18
- # - Handle IDs are allocated by a monotonically increasing counter
19
- # scoped to a single invocation. The first ID issued in an
20
- # invocation is 1; ID 0 is reserved as the invalid sentinel and is
21
- # never returned by +#alloc+.
22
- #
23
- # - A fresh table backs each invocation, so a Handle issued in one
24
- # invocation resolves in no other — uniformly regardless of
25
- # allocation source (Service return or host-injected argument).
26
- #
27
- # - The cap is +0x7fff_ffff+ (2³¹ − 1). Allocation beyond the cap
28
- # raises immediately — no silent truncation, no wrap, no ID reuse.
29
- class Handles
30
- # Build a fresh, empty table. +next_id+ is an internal seam that
31
- # sets the starting value of the monotonic counter (defaults to 1);
32
- # tests pass a value near +Kobako::Handle::MAX_ID+ to exercise
33
- # the cap-exhaustion path without 2³¹ allocations.
10
+ # One invocation's Handle table, mapping opaque ids to host objects. Each
11
+ # invocation mints its own, so a Handle resolves only where it was issued.
12
+ class Handles # :nodoc:
13
+ # +next_id+ lets tests start near +Kobako::Handle::MAX_ID+ to reach
14
+ # exhaustion without 2³¹ allocations.
34
15
  def initialize(next_id: 1)
35
16
  @entries = {} # : Hash[Integer, Kobako::Transport::Exposure]
36
17
  surfaces = {} # : Hash[Module, Set[Symbol]]
@@ -38,19 +19,6 @@ module Kobako
38
19
  @next_id = next_id
39
20
  end
40
21
 
41
- # Bind +object+ in the table and return a +Kobako::Handle+ token
42
- # for it. +object+ is any host-side Ruby object to bind. Returns a
43
- # freshly-allocated +Kobako::Handle+ whose +#id+ falls in
44
- # +[Kobako::Handle::MIN_ID, Kobako::Handle::MAX_ID]+. Raises
45
- # +Kobako::HandleExhaustedError+ if the next ID would exceed the
46
- # cap. The cap is anchored on +Kobako::Handle+ — the wire codec
47
- # and the allocator share the same invariant.
48
- #
49
- # Returning a Handle (rather than a bare Integer id) keeps the
50
- # allocator's output a domain entity. An id is the Handle's only
51
- # content, so the same internal +Kobako::Handle.restore+ constructor
52
- # serves both this allocator and the codec's wire-decode path.
53
- #
54
22
  # The entry records the object's Exposure as it stands at mint, so a
55
23
  # call made through this Handle is authorized against the reference
56
24
  # the guest was given.
@@ -63,27 +31,17 @@ module Kobako
63
31
  Kobako::Handle.restore(id)
64
32
  end
65
33
 
66
- # Resolve a Handle ID to its bound object — the very object +#alloc+
67
- # received, so a Handle crossing back restores to it. +id+ is a Handle
68
- # ID previously returned by +#alloc+. Raises +Kobako::SandboxError+ if
69
- # +id+ is not currently bound.
70
34
  def fetch(id)
71
35
  exposure(id).object
72
36
  end
73
37
 
74
- # The Exposure +id+ was minted with, which authorizes a call the guest
75
- # makes through that Handle. Raises +Kobako::SandboxError+ if +id+ is
76
- # not currently bound.
77
38
  def exposure(id)
78
39
  require_bound!(id)
79
40
  @entries[id]
80
41
  end
81
42
 
82
- # Number of currently-bound entries. Used by tests of the Dispatcher
83
- # and Codec::HandleWalk#deep_wrap to observe whether each path allocates
84
- # exactly the Handle entries it should — the +Handles+ table itself never
85
- # consults its own size, but the surrounding code's allocation
86
- # contract is part of the observable boundary.
43
+ # The table never consults its own size; tests read it to pin how many
44
+ # Handles each path allocates.
87
45
  def size
88
46
  @entries.size
89
47
  end
@@ -113,8 +71,6 @@ module Kobako
113
71
  end
114
72
  end
115
73
 
116
- # Guard #alloc against issuing an ID past the cap. Returns +nil+
117
- # on success; raises +Kobako::HandleExhaustedError+ at exhaustion.
118
74
  def ensure_capacity!
119
75
  cap = Kobako::Handle::MAX_ID
120
76
  return unless @next_id > cap
@@ -124,9 +80,6 @@ module Kobako
124
80
  "in a single invocation (limit #{cap})"
125
81
  end
126
82
 
127
- # Single source of truth for the "unknown Handle id" raise used by
128
- # #fetch. Returns +nil+ on success; raises +Kobako::SandboxError+
129
- # when +id+ is not currently bound.
130
83
  def require_bound!(id)
131
84
  return if @entries.key?(id)
132
85