kobako 0.29.0 → 0.30.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.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +217 -10
  4. data/Cargo.lock +139 -141
  5. data/README.md +96 -574
  6. data/ROADMAP.md +2 -1
  7. data/crates/kobako-runtime/CHANGELOG.md +269 -1
  8. data/crates/kobako-runtime/Cargo.toml +25 -15
  9. data/crates/kobako-runtime/README.md +7 -3
  10. data/crates/kobako-runtime/src/dispatch.rs +1 -1
  11. data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
  12. data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
  13. data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
  14. data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
  15. data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
  16. data/crates/kobako-runtime/src/endpoint.rs +166 -0
  17. data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
  18. data/crates/kobako-runtime/src/filesystem/{directory/spelling.rs → bind/lookup.rs} +91 -49
  19. data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
  20. data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
  21. data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
  22. data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
  23. data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
  24. data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
  25. data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
  26. data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
  27. data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
  28. data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +227 -734
  29. data/crates/kobako-runtime/src/filesystem/overlay.rs +2649 -1135
  30. data/crates/kobako-runtime/src/filesystem/path.rs +2 -41
  31. data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
  32. data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
  33. data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
  34. data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
  35. data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
  36. data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
  37. data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
  38. data/crates/kobako-runtime/src/filesystem.rs +21 -387
  39. data/crates/kobako-runtime/src/hashtable.rs +44 -0
  40. data/crates/kobako-runtime/src/lib.rs +11 -6
  41. data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
  42. data/crates/kobako-runtime/src/locking.rs +218 -0
  43. data/crates/kobako-runtime/src/mount.rs +31 -104
  44. data/crates/kobako-runtime/src/profile.rs +137 -1
  45. data/crates/kobako-runtime/src/sched.rs +210 -0
  46. data/crates/kobako-runtime/src/security.rs +696 -0
  47. data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
  48. data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
  49. data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
  50. data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
  51. data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
  52. data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
  53. data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
  54. data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
  55. data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
  56. data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
  57. data/crates/kobako-runtime/src/vfs/path.rs +64 -0
  58. data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
  59. data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
  60. data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
  61. data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
  62. data/crates/kobako-runtime/src/vfs.rs +60 -0
  63. data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
  64. data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
  65. data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
  66. data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
  67. data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
  68. data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
  69. data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
  70. data/crates/kobako-transport/CHANGELOG.md +8 -2
  71. data/crates/kobako-transport/Cargo.toml +3 -3
  72. data/crates/kobako-transport/README.md +6 -5
  73. data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
  74. data/crates/kobako-transport/src/envelope/call.rs +5 -5
  75. data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
  76. data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
  77. data/crates/kobako-transport/src/envelope/fault.rs +2 -2
  78. data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
  79. data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
  80. data/crates/kobako-transport/src/envelope/reply.rs +2 -2
  81. data/crates/kobako-transport/src/envelope/request.rs +2 -2
  82. data/crates/kobako-transport/src/envelope/run.rs +1 -1
  83. data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
  84. data/crates/kobako-transport/src/envelope.rs +2 -2
  85. data/crates/kobako-wasmtime/CHANGELOG.md +154 -8
  86. data/crates/kobako-wasmtime/Cargo.toml +15 -14
  87. data/crates/kobako-wasmtime/README.md +9 -5
  88. data/crates/kobako-wasmtime/src/abi.rs +12 -20
  89. data/crates/kobako-wasmtime/src/ambient.rs +1 -2
  90. data/crates/kobako-wasmtime/src/cache.rs +5 -5
  91. data/crates/kobako-wasmtime/src/config.rs +22 -7
  92. data/crates/kobako-wasmtime/src/descriptors.rs +133 -100
  93. data/crates/kobako-wasmtime/src/driver.rs +29 -96
  94. data/crates/kobako-wasmtime/src/exports.rs +5 -16
  95. data/crates/kobako-wasmtime/src/guest.rs +38 -0
  96. data/crates/kobako-wasmtime/src/imports.rs +14 -8
  97. data/crates/kobako-wasmtime/src/instance_pre.rs +3 -3
  98. data/crates/kobako-wasmtime/src/invocation.rs +26 -46
  99. data/crates/kobako-wasmtime/src/lib.rs +1 -4
  100. data/crates/kobako-wasmtime/src/limiter.rs +6 -230
  101. data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +69 -46
  102. data/crates/kobako-wasmtime/src/preview1/differential.rs +121 -7
  103. data/crates/kobako-wasmtime/src/preview1/errno.rs +74 -206
  104. data/crates/kobako-wasmtime/src/preview1/fd.rs +1248 -500
  105. data/crates/kobako-wasmtime/src/preview1/memory.rs +117 -150
  106. data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
  107. data/crates/kobako-wasmtime/src/preview1/mount_view.rs +1939 -672
  108. data/crates/kobako-wasmtime/src/preview1/path.rs +230 -349
  109. data/crates/kobako-wasmtime/src/preview1/poll.rs +148 -100
  110. data/crates/kobako-wasmtime/src/preview1/sock.rs +6 -23
  111. data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
  112. data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
  113. data/crates/kobako-wasmtime/src/preview1.rs +464 -413
  114. data/crates/kobako-wasmtime/src/trap.rs +9 -9
  115. data/crates/kobako-wasmtime/src/wasi.rs +53 -37
  116. data/crates/kobako-wasmtime/tests/{guest_file.rs → integration/guest_file.rs} +232 -201
  117. data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
  118. data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +1 -1
  119. data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +1 -1
  120. data/crates/kobako-wasmtime/tests/{unanswered_call.rs → integration/unanswered_call.rs} +1 -1
  121. data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +1 -1
  122. data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
  123. data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
  124. data/data/kobako.wasm +0 -0
  125. data/ext/kobako/Cargo.toml +1 -1
  126. data/ext/kobako/src/runtime/bridge.rs +1 -1
  127. data/ext/kobako/src/runtime/errors.rs +1 -1
  128. data/ext/kobako/src/runtime/escape.rs +28 -14
  129. data/ext/kobako/src/runtime/filesystem/grants.rs +21 -5
  130. data/ext/kobako/src/runtime/filesystem/ruby.rs +464 -156
  131. data/ext/kobako/src/runtime/filesystem.rs +109 -137
  132. data/ext/kobako/src/runtime/gvl.rs +43 -8
  133. data/ext/kobako/src/runtime/snapshot.rs +1 -1
  134. data/ext/kobako/src/runtime.rs +6 -1
  135. data/lib/kobako/catalog/extensions.rb +5 -5
  136. data/lib/kobako/catalog/flags.rb +2 -2
  137. data/lib/kobako/catalog/handles.rb +6 -6
  138. data/lib/kobako/catalog/mount.rb +2 -10
  139. data/lib/kobako/catalog/services.rb +2 -2
  140. data/lib/kobako/catalog/snippets.rb +7 -7
  141. data/lib/kobako/catalog.rb +1 -1
  142. data/lib/kobako/codec/decoder.rb +10 -10
  143. data/lib/kobako/codec/ext_types.rb +3 -5
  144. data/lib/kobako/codec/handle_walk.rb +4 -6
  145. data/lib/kobako/codec/nesting.rb +7 -7
  146. data/lib/kobako/codec/utils.rb +1 -1
  147. data/lib/kobako/codec/value_budget.rb +3 -3
  148. data/lib/kobako/context.rb +5 -5
  149. data/lib/kobako/filesystem.rb +44 -13
  150. data/lib/kobako/payload/arguments.rb +2 -2
  151. data/lib/kobako/pool.rb +4 -4
  152. data/lib/kobako/sandbox.rb +28 -28
  153. data/lib/kobako/sandbox_options.rb +2 -3
  154. data/lib/kobako/snippet/bytecode.rb +1 -1
  155. data/lib/kobako/snippet/source.rb +1 -1
  156. data/lib/kobako/transport/dispatcher.rb +5 -5
  157. data/lib/kobako/transport/exposure.rb +2 -2
  158. data/lib/kobako/transport/reflection.rb +7 -7
  159. data/lib/kobako/transport/run.rb +5 -5
  160. data/lib/kobako/transport/yielder.rb +1 -1
  161. data/lib/kobako/version.rb +1 -1
  162. data/lib/kobako.rb +34 -0
  163. data/release-please-config.json +1 -0
  164. data/sig/kobako/catalog/extensions.rbs +3 -3
  165. data/sig/kobako/catalog/handles.rbs +3 -3
  166. data/sig/kobako/catalog/mount.rbs +0 -4
  167. data/sig/kobako/catalog/services.rbs +1 -1
  168. data/sig/kobako/catalog/snippets.rbs +3 -3
  169. data/sig/kobako/catalog.rbs +1 -1
  170. data/sig/kobako/codec/decoder.rbs +1 -1
  171. data/sig/kobako/codec/handle_walk.rbs +1 -1
  172. data/sig/kobako/codec/nesting.rbs +2 -2
  173. data/sig/kobako/codec/utils.rbs +1 -1
  174. data/sig/kobako/codec/value_budget.rbs +1 -1
  175. data/sig/kobako/codec.rbs +1 -2
  176. data/sig/kobako/context.rbs +1 -1
  177. data/sig/kobako/filesystem.rbs +19 -14
  178. data/sig/kobako/guest_yielder.rbs +1 -1
  179. data/sig/kobako/payload/arguments.rbs +1 -1
  180. data/sig/kobako/pool.rbs +2 -2
  181. data/sig/kobako/runtime.rbs +1 -1
  182. data/sig/kobako/sandbox.rbs +3 -5
  183. data/sig/kobako/sandbox_options.rbs +1 -1
  184. data/sig/kobako/transport/dispatcher.rbs +2 -2
  185. data/sig/kobako/transport/exposure.rbs +1 -1
  186. data/sig/kobako/transport/reflection.rbs +2 -2
  187. data/sig/kobako/transport/run.rbs +3 -3
  188. data/sig/kobako/transport.rbs +1 -1
  189. metadata +67 -19
  190. data/crates/kobako-runtime/src/filesystem/directory.rs +0 -1082
  191. data/crates/kobako-runtime/src/filesystem/memory.rs +0 -1100
  192. data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +0 -88
  193. data/crates/kobako-runtime/src/filesystem/temporary.rs +0 -167
  194. data/crates/kobako-wasmtime/src/capture.rs +0 -96
  195. data/crates/kobako-wasmtime/src/dispatch.rs +0 -125
  196. data/crates/kobako-wasmtime/src/guest_mem.rs +0 -271
  197. data/crates/kobako-wasmtime/src/io.rs +0 -89
  198. data/crates/kobako-wasmtime/src/preview1/args.rs +0 -60
  199. data/crates/kobako-wasmtime/src/preview1/beneath.rs +0 -188
  200. /data/crates/kobako-wasmtime/tests/{compile_posture.rs → integration/compile_posture.rs} +0 -0
  201. /data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +0 -0
@@ -18,7 +18,7 @@ pub struct Run {
18
18
  impl Run {
19
19
  pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
20
20
  let mut reader = Reader::new(bytes);
21
- let entrypoint = reader.text()?.to_owned();
21
+ let entrypoint = reader.read_text()?.to_owned();
22
22
  Ok(Run {
23
23
  entrypoint,
24
24
  payload: reader.remaining().to_vec(),
@@ -38,8 +38,8 @@ impl Unsupported {
38
38
  }
39
39
 
40
40
  pub(crate) fn read(reader: &mut Reader<'_>) -> Result<Self, DecodeError> {
41
- reader.u8()?;
42
- let message = reader.text()?.to_owned();
41
+ reader.read_u8()?;
42
+ let message = reader.read_text()?.to_owned();
43
43
  // The last thing its answer carries, so whatever follows belongs
44
44
  // to a later version of these fields, as for a Fault.
45
45
  reader.skip_unknown_fields();
@@ -1,4 +1,4 @@
1
- //! The core envelope — the fixed-layout outer frame of every host↔guest
1
+ //! The Envelope — the fixed-layout outer frame of every host↔guest
2
2
  //! message.
3
3
  //!
4
4
  //! Routing and outcome attribution live here; everything a resolved method
@@ -50,7 +50,7 @@ pub use unsupported::Unsupported;
50
50
 
51
51
  use std::fmt;
52
52
 
53
- /// A message that does not conform to the core envelope layout. Only
53
+ /// A message that does not conform to the Envelope layout. Only
54
54
  /// decoding produces one — every `encode` is infallible — so the name says
55
55
  /// which direction it comes from.
56
56
  ///
@@ -1,22 +1,168 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.20.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.19.0...kobako-wasmtime-v0.20.0) (2026-11-07)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **io:** a path holding a NUL byte now raises ArgumentError from every File and Dir call, including the exist?, file?, and directory? questions that answered false before.
9
+ * **runtime:** Policy::allows is asked Access::ReadWrite before an open that may create finds what stands, so an open of an existing file asking to create is asked ReadWrite and then the access it opens for, where it was asked that access alone.
10
+ * **runtime:** Kstat's len, accessed, modified, and changed fields are now size, atime, mtime, and ctime, and Profile::guest_metadata is now
11
+ * **runtime:** spell each file type as Linux's FT_* names it
12
+ * **runtime:** name what a path reports Kstat, as Linux's struct kstat
13
+ * **runtime:** build an open's flags once and hand them to do_file_open
14
+ * **runtime:** answer EAGAIN to a `..` a move landed before
15
+ * **runtime:** judge a path through a link where the link leads
16
+ * **runtime:** kobako_runtime::Denial is gone. A Denial::ReadOnly is now
17
+ * **runtime:** name a Filesystem's make-and-open atomic_open
18
+ * **runtime:** kobako_runtime::filesystem::Access moved to kobako_runtime::mount::Access. kobako::Access and kobako_runtime::Access are unchanged.
19
+ * **runtime:** a mount's block or Policy no longer allows writing beneath a read-only Mount, and access defaults to read. Pass access: :read_write in Ruby, or Access::ReadWrite in Rust, where the block should decide about changes. A change beneath a read-only Mount answers EROFS without asking it.
20
+ * **runtime:** File::metadata and File::set_len are gone from what an open file answers. Implement InodeOperations::getattr and setattr, answering from what the file holds where the entry must still answer once its name is gone.
21
+ * **runtime:** File::read_at is FileOperations::read_iter, and File::write_at and File::append are FileOperations::write_iter, which take a Kiocb for the offset. A write finding Kiocb::append set lands at the end and answers Unsupported if it cannot take that as one step; each moves Kiocb::pos past what it took.
22
+ * **runtime:** File::close is FileOperations::flush and takes &mut self. Move what a close reports there, and what a file lets go of to Drop.
23
+ * **runtime:** File::sync is FileOperations::fsync, and a file that does not implement it now answers EINVAL instead of success. Implement fsync returning Ok where a file has nothing to flush.
24
+ * **runtime:** the File trait is FileOperations. Rename it where a Filesystem implements it.
25
+ * **runtime:** the Filesystem trait is InodeOperations, whatever earlier notes call it. Each entry answers through its own: a lookup returns a Lookup, whose Inode holds them, and an operation an entry lacks answers None.
26
+ * Inode::stored_name is replaced by Inode::lookup, which the runtime asks before keeping a name's dentry. A rename that changes only a name's case on a host folding case leaves it as it was, as a rename of an entry onto itself does.
27
+ * Inode::rename takes the source, the target directory, and the target, without a Rename; Rename and Rename::check are removed. A Filesystem written in Ruby answers rename(from, to) instead of rename(from, to, directory:).
28
+ * Overlay::new() and Overlay::layer are replaced by Overlay::new(lower, upper), which refuses an upper that is no Tmpfs. In Ruby, Overlay.new.layer(fs, access:) is replaced by Overlay.new(lower: [...], upper: tmpfs).
29
+ * a Filesystem is the instance a Mount places, made with Filesystem::new from its root Inode. Mount::new, the Rust Sandbox's and Context's mount and mount_with_policy, and Overlay::layer take one, and
30
+ * Kobako::Filesystem::Memory is Kobako::Filesystem::Tmpfs with size: and nr_inodes: in place of limit: and inodes:, and Kobako::Filesystem::Directory is Kobako::Filesystem::Bind. In Rust, Memory is Tmpfs with with_size, with_nr_inodes, and DEFAULT_SIZE; Directory is Bind; and the Filesystem trait is Inode.
31
+ * Overlay::layer takes the instance a Filesystem is mounted by instead of the Filesystem itself.
32
+ * the Filesystem trait's open, open_dir, symlink_metadata, stored_name, read_link, create_dir, remove_dir, remove_file, and rename take a &Dentry instead of a name; open_dir_nofollow is now open_dir, and metadata() has no default. The kobako crate re-exports Dentry.
33
+ * the Filesystem trait's metadata(path) is removed; metadata() reports the directory itself and defaults to what symlink_metadata answers for the empty path.
34
+ * the Filesystem trait's open_dir is removed; open_dir_nofollow opens a directory by name and refuses a link.
35
+ * Mount::new, and the Rust Sandbox's and Context's mount and mount_with_policy, take a shared instance made from the Filesystem instead of the Filesystem itself.
36
+ * Mount's check, guest_path, moves_directories, and follows_links are no longer public; an engine calls the vfs operations, which ask the Mount itself.
37
+ * the Filesystem trait's canonicalize_beneath and for_policy are removed and read_link is added; a Filesystem holding no links answers read_link with InvalidInput. The Mount's for_invocation is removed, and a Directory no longer has a mode following no link.
38
+ * the Filesystem trait's hold_directory and the Hold type are removed; a directory a Filesystem opens holds what it needs open itself. Beneath a Policy, a directory opened on a Filesystem that reports no inode number now answers ENOENT.
39
+ * **runtime:** a Filesystem implementation answers open_dir, open_dir_nofollow, entries and stored_name in place of read_dir and stored_path, and its rename takes the target directory before the target path.
40
+ * Kobako::Filesystem::Temporary and the Rust Temporary are removed; mount a Memory, and mount a new one through the Context where each run should start empty. A Filesystem implementation drops its for_invocation method.
41
+
42
+ ### Features
43
+
44
+ * answer every path call from the runtime's VFS ([39d0292](https://github.com/elct9620/kobako/commit/39d0292c3b5d3ec8ea563fe4720c8449e92ae86a))
45
+ * ask a Filesystem about a directory itself, never through a link ([5881b87](https://github.com/elct9620/kobako/commit/5881b87d8e06cdabd12fe958c65f5bc67b24375d))
46
+ * check what a rename moves in the VFS, as Linux does ([dd5e267](https://github.com/elct9620/kobako/commit/dd5e267b7d3f09591864575ce8b16aaf2f703a07))
47
+ * hand a Filesystem the dentry the VFS made for each name ([5a2f85a](https://github.com/elct9620/kobako/commit/5a2f85a030918f329b98874af843385ea5ba12a8))
48
+ * hand every invocation the Filesystem its Mount holds ([17bb576](https://github.com/elct9620/kobako/commit/17bb576803f8366a073993efa17df788949f8a69))
49
+ * key each dentry by the name its directory stores, as Linux's d_add_ci ([fc0267b](https://github.com/elct9620/kobako/commit/fc0267b9df310482842e0695d0350b7dc7f3a65f))
50
+ * layer an Overlay over the instance its Filesystem is mounted by ([c321548](https://github.com/elct9620/kobako/commit/c321548d294698d4b8f16336c0b035f0ee909511))
51
+ * let no Filesystem follow a link to open a directory ([79a6a0b](https://github.com/elct9620/kobako/commit/79a6a0bbec78e273f218e863e2c22a3bb960b4de))
52
+ * mount a Filesystem instance whose Mounts share what moves ([bead3f8](https://github.com/elct9620/kobako/commit/bead3f84ebd9f4a8b240afc52aa7e0967a3a400a))
53
+ * name the Filesystems kobako ships after the Linux ones they mirror ([4fb367b](https://github.com/elct9620/kobako/commit/4fb367beefedf89ee424ff32b33837c000760f4b))
54
+ * name the instance a Mount places a Filesystem ([50ca78e](https://github.com/elct9620/kobako/commit/50ca78e405e3cd567095b49c77d3b26b2d104c57))
55
+ * reach an opened directory wherever it moves ([34522df](https://github.com/elct9620/kobako/commit/34522df97bd625f298d1a280fa73adf091879f59))
56
+ * report a file's link count and times to the guest ([5e95113](https://github.com/elct9620/kobako/commit/5e95113a056bd676bd5736f6ffa79f210a036647))
57
+ * **runtime:** answer every Filesystem call from a directory handle ([5a4376c](https://github.com/elct9620/kobako/commit/5a4376c6a8d3ede3272f8d6393ab27589f733eeb))
58
+ * **runtime:** ask a Filesystem whether a held dentry still names its entry ([13f56a4](https://github.com/elct9620/kobako/commit/13f56a40bd9622ba15ee6b34dc93393c92b3e270))
59
+ * **runtime:** judge a path through a link where the link leads ([7407a71](https://github.com/elct9620/kobako/commit/7407a71cfbcb9c4f5faa3ef954c592d0687174cc))
60
+ * **runtime:** keep the times a caller sets on a Tmpfs, a Bind, and an Overlay ([d53d547](https://github.com/elct9620/kobako/commit/d53d5479c48e4d423addf02b7d373784409d1604))
61
+ * stack an Overlay as overlayfs does, under an upper Tmpfs ([2b59780](https://github.com/elct9620/kobako/commit/2b597806809d7a810d29265e9e377d7d648d77f9))
62
+ * walk a guest path one name at a time and follow links there ([2e57fdc](https://github.com/elct9620/kobako/commit/2e57fdc11c5cbeb00697e9b287f59c15f0c91e2d))
63
+ * **wasmtime:** judge every invocation by the security modules a Driver is built with ([ba42b85](https://github.com/elct9620/kobako/commit/ba42b85f43ba8eb4bacba807a4c5af1788a614ae))
64
+
65
+
66
+ ### Bug Fixes
67
+
68
+ * **io:** answer Dir, NUL paths, pread, and advise as CRuby does ([fd9accd](https://github.com/elct9620/kobako/commit/fd9accd485d3aad2f0f36a20a292d27b81f4777b))
69
+ * keep an opened directory's identity through the directory itself ([28ee9e0](https://github.com/elct9620/kobako/commit/28ee9e003a30fb9159ff3f6c47dce2da25586eb2))
70
+ * **runtime:** answer a dot path or a slash create through a link as Linux ([8aafe42](https://github.com/elct9620/kobako/commit/8aafe42cd517cdc9730ac41b092000570c065511))
71
+ * **runtime:** answer a link call by what stands, as filename_linkat does ([5a0ac25](https://github.com/elct9620/kobako/commit/5a0ac257a260f8638caf794df50d95ad11fb139d))
72
+ * **runtime:** answer a move's flags and a link's source where Linux does ([be7af48](https://github.com/elct9620/kobako/commit/be7af48aa4dce202f9a2d1342a3cd0e314240cd4))
73
+ * **runtime:** answer about a name through DentryOperations ([ae6ca8a](https://github.com/elct9620/kobako/commit/ae6ca8abaebdbfd289378ba9299a6446bb611e02))
74
+ * **runtime:** answer EAGAIN to a `..` a move landed before ([1aa66dd](https://github.com/elct9620/kobako/commit/1aa66dda78bd63aff91a78bfe8d6bff6306a9c19))
75
+ * **runtime:** answer ENODEV to an allocation on a device ([0ba099b](https://github.com/elct9620/kobako/commit/0ba099b474890179e3c4c4e8a13837b9be5b5ead))
76
+ * **runtime:** ask a walk's start whether it still stands under its directory's lock ([4b14e4e](https://github.com/elct9620/kobako/commit/4b14e4e91aaf1a91d2815789ed41c2da9c50e97d))
77
+ * **runtime:** ask an entry about itself however a guest reached it, as fstat and ftruncate do ([1166426](https://github.com/elct9620/kobako/commit/1166426d3f4bc27d9c2d0d3a8f2b7f1a6a23f59e))
78
+ * **runtime:** ask atomic_open for an open's last name no walk looked up ([9860ae9](https://github.com/elct9620/kobako/commit/9860ae9391874cf27bf63345b4672458f696a8a8))
79
+ * **runtime:** ask to make a file before an open finds what stands ([0b3ecf5](https://github.com/elct9620/kobako/commit/0b3ecf54c6975576dee13decb1904ccb371f30be))
80
+ * **runtime:** build an open's flags once and hand them to do_file_open ([6672734](https://github.com/elct9620/kobako/commit/6672734fc42af8c2040402c8ed2b14d562c31328))
81
+ * **runtime:** empty a file an open truncates through setattr ([0ee006f](https://github.com/elct9620/kobako/commit/0ee006f4683ed3059a9f3251d450bf3de0eb365d))
82
+ * **runtime:** hand a listed entry where it stands, as dir_emit does ([804f8b2](https://github.com/elct9620/kobako/commit/804f8b2ce93a8d5b935d400d295ba5f2ce7c5a32))
83
+ * **runtime:** hold a directory the guest opens from the moment it opens ([1dcca08](https://github.com/elct9620/kobako/commit/1dcca089ffc5ce8459940565999ca09b20bee44a))
84
+ * **runtime:** hold what a guest's listings keep within its memory limit ([e4d74a5](https://github.com/elct9620/kobako/commit/e4d74a5ad2d5554e1b509ee606c1911811f66be8))
85
+ * **runtime:** keep a Bind's walk and allocation answers true under a race ([cce1a2a](https://github.com/elct9620/kobako/commit/cce1a2a0e64bf44648e01caf14e901f1cb04fad3))
86
+ * **runtime:** keep a Cred without kobako's rules inside the runtime ([775bb5c](https://github.com/elct9620/kobako/commit/775bb5c09c1fb8ce4412208df8113e26260e89c9))
87
+ * **runtime:** keep pipes among the Filesystems, reading nothing into no room ([79a2240](https://github.com/elct9620/kobako/commit/79a22401f9c8ab3c8fe777955a4b39e880418686))
88
+ * **runtime:** leave the descriptor table to the Driver, as FD_ADD reserves first ([12e6b65](https://github.com/elct9620/kobako/commit/12e6b65b4b47bdd4e73465f904c9cef92c27e1bd))
89
+ * **runtime:** let a descriptor's stat and listing ask no hook ([48d265a](https://github.com/elct9620/kobako/commit/48d265ab83773e1ee436f06d4f822bf57f372ba2))
90
+ * **runtime:** let a directory answer as one open to read does ([3919be4](https://github.com/elct9620/kobako/commit/3919be4a542fc0d63fcc18422c3465b06fd6c6d1))
91
+ * **runtime:** let a link leading out escape as a `..` does ([47d7ec7](https://github.com/elct9620/kobako/commit/47d7ec7dfa97bd1d3d1a9bfee0c536372e42108d))
92
+ * **runtime:** let a listing hand its own dots and answer for a removed directory ([88a3fc7](https://github.com/elct9620/kobako/commit/88a3fc7e491206b23a3eab2b4c91bf9aa0f00786))
93
+ * **runtime:** let a permissive guest tell entries apart by inode number ([e651562](https://github.com/elct9620/kobako/commit/e651562d40fd55028415975f4305f6f3f512ca28))
94
+ * **runtime:** let a Policy only refuse more than a Mount's access allows ([f1502b5](https://github.com/elct9620/kobako/commit/f1502b522bcb9d8fc8fffd29a7714b5d777bb793))
95
+ * **runtime:** let an entry's setattr decide whether it keeps a time the guest sets ([fdfa3fb](https://github.com/elct9620/kobako/commit/fdfa3fb197909c1f027db5e5c540bc5cbfd5f013))
96
+ * **runtime:** let an open file answer for its mode and the largest offset ([a737685](https://github.com/elct9620/kobako/commit/a7376851ba17aa5e9c84a5158424d2ce5d367eaf))
97
+ * **runtime:** list a directory as its Filesystem says, through dir_emit ([c7447a1](https://github.com/elct9620/kobako/commit/c7447a137a5237b2e071ba6500fddd736abe5092))
98
+ * **runtime:** merge only directory over directory in an Overlay ([6473956](https://github.com/elct9620/kobako/commit/6473956fd773a858ea34880b6a1d7b8e60018f3f))
99
+ * **runtime:** move seeking, flags, hints, allocation and readiness into File ([5d1cd6b](https://github.com/elct9620/kobako/commit/5d1cd6b1d1d626945b5f5c70a5342a8576a017f8))
100
+ * **runtime:** name a listing's dots as Linux's drivers do ([ef26d61](https://github.com/elct9620/kobako/commit/ef26d61318ad3f489f5d1379db690854d46a2900))
101
+ * **runtime:** open a file made by create alone after the security modules ([5f8d30c](https://github.com/elct9620/kobako/commit/5f8d30c48e08450fa3b1065954fad8ea8e8e5a26))
102
+ * **runtime:** open a made file once its directory is unlocked ([1a61088](https://github.com/elct9620/kobako/commit/1a61088b4adbbeaef052e83ad6d8ac19b79d7523))
103
+ * **runtime:** open a standard stream as a pipe's end, as create_pipe_files does ([58b3f68](https://github.com/elct9620/kobako/commit/58b3f686db485bd39f44e69bca25675acdad215a))
104
+ * **runtime:** open each Mount's root before its Policy applies ([4580697](https://github.com/elct9620/kobako/commit/45806973ab0b99f977b7d90253f8dafa9b1f8e8b))
105
+ * **runtime:** read a call's flags and path before where it starts ([70bce0f](https://github.com/elct9620/kobako/commit/70bce0f0d5b282e1324ab042d5c29b2e523cb0ae))
106
+ * **runtime:** read a link at an empty path from the descriptor itself ([c59f6c2](https://github.com/elct9620/kobako/commit/c59f6c2a9c69d40ec08afd961bc6f71ea66cc020))
107
+ * **runtime:** refuse a change at an Overlay's super block when it has no upper ([7f4c31e](https://github.com/elct9620/kobako/commit/7f4c31e8db91d09fc6a133708e14f28a26d60675))
108
+ * **runtime:** refuse a path no call can take in the VFS ([e752648](https://github.com/elct9620/kobako/commit/e7526485f91ca2acbfe59ad4431d2385c91caa19))
109
+ * **runtime:** refuse a sync of a file without one, as vfs_fsync_range does ([0f26730](https://github.com/elct9620/kobako/commit/0f267304ef9966fb797194817ccfc21134389570))
110
+ * **runtime:** refuse an advice from past the largest signed offset ([3ff54a2](https://github.com/elct9620/kobako/commit/3ff54a2f0859c694f012a2ab228295050a500e76))
111
+ * **runtime:** refuse removing a path ending in .. as ENOTEMPTY, as Linux does ([a02bac9](https://github.com/elct9620/kobako/commit/a02bac9e9ea300013e90ab396226fe03fbf45f1d))
112
+ * **runtime:** report the kind a host gives a special file ([b421805](https://github.com/elct9620/kobako/commit/b421805dc4b76cc76f541d1b9e0579329e72a502))
113
+ * **runtime:** reserve the room a guest allocates in a Tmpfs and a Bind ([dd94317](https://github.com/elct9620/kobako/commit/dd94317257c3bbbbfd2a2a170a9d5a3783801f43))
114
+ * **runtime:** sync only as far as asked, cap one call, and let a writer through ([5e2902f](https://github.com/elct9620/kobako/commit/5e2902f95312c985f78053cacf6374db893e174f))
115
+ * **runtime:** take O_SYNC and O_DIRECTORY's other flags as Linux does ([ba6bc57](https://github.com/elct9620/kobako/commit/ba6bc57e1ad8264e9035d9808098efe5b846f32f))
116
+ * **runtime:** walk a path's last name as Linux's namei does ([591435b](https://github.com/elct9620/kobako/commit/591435b5abedf30c62c00fc634bbafe7f1dc68fc))
117
+ * **runtime:** walk out of a link and create through one as Linux does ([c6e4054](https://github.com/elct9620/kobako/commit/c6e40547e52421bb7f8a1f86e549c40f365002ad))
118
+ * **wasmtime:** answer a call setting no time before its descriptor ([31a58a3](https://github.com/elct9620/kobako/commit/31a58a3ccd541c27120c31d5f42fcaee4be5201d))
119
+ * **wasmtime:** answer a path call's flags and name before its descriptor ([d8771ff](https://github.com/elct9620/kobako/commit/d8771ff42a967c04452e74a1f1f35d47ecf0b985))
120
+ * **wasmtime:** answer a path call's names and descriptors in Linux's order ([a2f2db4](https://github.com/elct9620/kobako/commit/a2f2db47be863037f904367af9a5cc9750e0111d))
121
+ * **wasmtime:** answer the entries a listing wrote before it failed ([67364af](https://github.com/elct9620/kobako/commit/67364afb2bed53a9b3685e299a82d8a61801a4e9))
122
+ * **wasmtime:** answer what a listing wrote before a name it stops at ([89974ff](https://github.com/elct9620/kobako/commit/89974fff2fc5fefad99ce8ff26befe91e92130aa))
123
+ * **wasmtime:** encode ESTALE as preview1's stale ([625371e](https://github.com/elct9620/kobako/commit/625371e961c370e73f49aae771ed3b9f5253d51b))
124
+ * **wasmtime:** hand down the right to write from a directory a guest opens ([c3e2297](https://github.com/elct9620/kobako/commit/c3e229780ecb666f18b7c9b9d85c35bb14cb96c3))
125
+ * **wasmtime:** let a directory renamed onto its own path stay ([5fa99ab](https://github.com/elct9620/kobako/commit/5fa99ab7052cf4dbbd6be34bc2d79351d42e6311))
126
+ * **wasmtime:** make a path call again a few times on EAGAIN ([a47b33e](https://github.com/elct9620/kobako/commit/a47b33e39c24eb77b193cfef7f57ab3d1cd17790))
127
+ * **wasmtime:** make the start's check part of the call made again ([858643a](https://github.com/elct9620/kobako/commit/858643a471d372df99b0eb92bb49d4d8d78fef7d))
128
+ * **wasmtime:** move a renumbered descriptor before closing what it replaces ([bcf5f46](https://github.com/elct9620/kobako/commit/bcf5f464566788b41aca771287908bfd01bf347e))
129
+ * **wasmtime:** open to read a file made or emptied without a right ([7e86943](https://github.com/elct9620/kobako/commit/7e869434bd93db4760ee6c6a04edbd6b8bb3a622))
130
+ * **wasmtime:** refuse a descriptor that stands only for its path as fdget does ([52be6ed](https://github.com/elct9620/kobako/commit/52be6edc630fe8db5537d0910a9a28eb2324c0ee))
131
+ * **wasmtime:** refuse a link to an absolute path through preview1 ([3477e84](https://github.com/elct9620/kobako/commit/3477e840cd15ac4bd6f06105e4ee9842717a05bc))
132
+ * **wasmtime:** refuse a readlink into no room before its path ([37f8c13](https://github.com/elct9620/kobako/commit/37f8c1360920712490a9c86409cc427560b7f5c3))
133
+ * **wasmtime:** report a descriptor's kind from its open file ([18c4c7e](https://github.com/elct9620/kobako/commit/18c4c7eecc96a8588574ad8a80853c31d93466fc))
134
+ * **wasmtime:** report a stream's flags and rights as it holds them ([cfa3475](https://github.com/elct9620/kobako/commit/cfa3475c65be7ba730fb0bb6e6e490c22078c544))
135
+ * **wasmtime:** report a wait on a descriptor not open in its event ([479b545](https://github.com/elct9620/kobako/commit/479b5459940ae62645ee62737363383679e22571))
136
+ * **wasmtime:** reserve an open's descriptor before reading its name ([a85dfe6](https://github.com/elct9620/kobako/commit/a85dfe65763a55c6993f5420c3b76cf068ee693e))
137
+ * **wasmtime:** stop a guest's waits for a lock at its deadline ([e102796](https://github.com/elct9620/kobako/commit/e102796b34b63098f3cce0050fb5ed1f16349b94))
138
+ * **wasmtime:** take a socket call's descriptor as fdget does ([5e5205f](https://github.com/elct9620/kobako/commit/5e5205fd3403f1cbbd167a9e87879f88797d8421))
139
+
140
+
141
+ ### Code Refactoring
142
+
143
+ * **runtime:** answer a close through flush, as close(2) reads f_op-&gt;flush ([c34eb58](https://github.com/elct9620/kobako/commit/c34eb58b297e5be6ef249af03c6d7655cfd94729))
144
+ * **runtime:** answer every VFS call with an errno ([84a0a38](https://github.com/elct9620/kobako/commit/84a0a385149f5a9194fcab9598849cf7c92c46ae))
145
+ * **runtime:** hang each entry's operations on its inode, as Linux does ([e21470b](https://github.com/elct9620/kobako/commit/e21470b625255eb85b4b5ff868872f2db5a7967e))
146
+ * **runtime:** land each read and write through a Kiocb, as read_iter and write_iter ([bc35dfd](https://github.com/elct9620/kobako/commit/bc35dfd13085b6aee517748996647bd7083cdbc8))
147
+ * **runtime:** let the mount layer own what a Guest may do beneath a Mount ([0dcde21](https://github.com/elct9620/kobako/commit/0dcde2143d7935a8e4b205bb03d664974af7edfb))
148
+ * **runtime:** name a Filesystem's make-and-open atomic_open ([57a6e10](https://github.com/elct9620/kobako/commit/57a6e10cb14e7a7f6ff02d2a7a88b5e18e459165))
149
+ * **runtime:** name what a path reports Kstat, as Linux's struct kstat ([0f43bed](https://github.com/elct9620/kobako/commit/0f43bed4242c2236e02c88d00470d1d31180219e))
150
+ * **runtime:** name what an open file answers as Linux's file_operations ([ca835e9](https://github.com/elct9620/kobako/commit/ca835e99b821845bcfa56648ff7336c67242bbd7))
151
+ * **runtime:** spell a Kstat's fields as Linux's struct kstat does ([ec73cac](https://github.com/elct9620/kobako/commit/ec73cac38e9b7e8b9162ea59ebc35de7e9394c8d))
152
+ * **runtime:** spell each file type as Linux's FT_* names it ([796fac4](https://github.com/elct9620/kobako/commit/796fac412cae9c70b6f80e7f33946fe960ed520e))
153
+
3
154
  ## [0.19.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.18.0...kobako-wasmtime-v0.19.0) (2026-10-26)
4
155
 
5
156
 
6
157
  ### ⚠ BREAKING CHANGES
7
158
 
8
- * **io:** File.open raises ArgumentError for a mode such as bw, where b leads, or wbt, holding both b and t, as CRuby does, instead of opening the file.
9
- * **wasmtime:** a call through a directory descriptor answers ENOENT when the directory at its path is not the one it opened, where it used to reach that directory.
10
159
  * **wasmtime:** a struct literal no longer builds the wasmtime driver's Config outside its crate; start from `Config::default()` and assign each field.
11
160
  * **runtime:** a struct literal no longer builds OpenOptions, Metadata, DirEntry, Snapshot, Capture, or Usage outside kobako-runtime; use their `new` or `Default` and assign each field.
12
- * the wasmtime Config and the SDK Options gain a setup_timeout field, so a struct literal that names every field must add it; one built from Options::default() is unaffected.
13
161
  * **wasmtime:** a Service answer past 16 MiB now raises a Service failure in the guest where it used to raise the transport error, and an answer the host cannot write into guest memory now traps the invocation.
14
- * the Guest ABI is version 5, so a Guest Binary built against version 4 is refused at Sandbox construction until rebuilt. The Yielder trait of kobako-runtime gains frame and takes the frame in yield_to_block; the Guest trait of kobako-core and the MrbGuest trait of kobako-mruby take the frame in yield_to_block; Downcall::Yield carries frame and args.
162
+ * **wasmtime:** a Guest Binary stalling while an invocation sets it up ends in the deadline's trap, and a cap tripped there keeps its own kind, where it used to hang or trap as a generic failure.
163
+ * the Guest ABI is version 5, so a Guest Binary built against version 4 is refused at Sandbox construction until rebuilt. In kobako-transport, Reply gains a Break arm and YieldReply::Break carries no payload. The Yielder trait of kobako-runtime gains frame and takes the frame in yield_to_block; the Guest trait of kobako-core and the MrbGuest trait of kobako-mruby take the frame in yield_to_block; Downcall::Yield carries frame and args.
15
164
  * **io:** putc, IO#read and SystemCallError.new raise RangeError for those numbers instead of writing, refusing or ignoring them.
16
- * **guest:** the default memory_limit is now 4 MiB. JSON.parse in the guest answers an Integer for every integer within 64 bits, where it used to answer a Float past 32 bits. An integer past 64 bits now raises
17
- * **wasmtime:** a struct literal building the wasmtime driver's Config must set the new `open_file_limit` field.
18
- * **io:** a script writing to a file through `IO#write` beneath a Mount now raises on a failed write where it used to go on.
19
- * **io:** a script rescuing IOError for a failed read or write beneath a Mount must rescue the EIO error class, or SystemCallError, instead.
165
+ * **guest:** the default memory_limit is now 4 MiB. JSON.parse in the guest answers an Integer of any width exactly, where it used to answer a Float past 32 bits.
20
166
  * **guest:** a Service or Extension backend bound at the path File fails its first invocation, naming the segment the Guest Binary now defines.
21
167
  * **runtime:** the Runtime trait's invoke takes the invocation's Grants as a fourth argument, so every engine implementing it must accept them.
22
168
 
@@ -15,10 +15,11 @@
15
15
 
16
16
  [package]
17
17
  name = "kobako-wasmtime"
18
- version = "0.19.0"
18
+ version = "0.20.0"
19
19
  edition = "2021"
20
- # wasmtime's own floor.
21
- rust-version = "1.96"
20
+ # The toolchain this crate is built and tested on, the first whose stripped
21
+ # Apple binary macOS 26 loads.
22
+ rust-version = "1.98"
22
23
  description = "wasmtime implementation of the kobako host runtime contract."
23
24
  license = "Apache-2.0"
24
25
  repository = "https://github.com/elct9620/kobako"
@@ -30,10 +31,10 @@ categories = ["wasm", "virtualization"]
30
31
  # The engine-neutral contract this crate implements. The version pin
31
32
  # rides the linked release group; the path keeps in-tree builds (and
32
33
  # the Ruby gem, which ships both crates) resolving locally.
33
- kobako-runtime = { version = "0.19.0", path = "../kobako-runtime" }
34
- # The fixed tier: the core envelope this driver shuttles and the ABI
34
+ kobako-runtime = { version = "0.20.0", path = "../kobako-runtime" }
35
+ # The fixed tier: the Envelope this driver shuttles and the ABI
35
36
  # values it drives the exports against.
36
- kobako-transport = { version = "0.19.0", path = "../kobako-transport" }
37
+ kobako-transport = { version = "0.20.0", path = "../kobako-transport" }
37
38
  # wasmtime — host-side embedder for kobako.wasm. We disable default-features
38
39
  # and opt back in only what kobako needs: a Cranelift-backed runtime that can
39
40
  # compile a pre-built wasm32-wasip1 module on the host triple, plus the `wat`
@@ -42,7 +43,7 @@ kobako-transport = { version = "0.19.0", path = "../kobako-transport" }
42
43
  # kobako runs short-lived synchronous sandboxes. `component-model` and
43
44
  # `async` still arrive through wasmtime-wasi's `p1`, which builds on `p2`;
44
45
  # kobako loads only core Modules, so none of that code is reached.
45
- wasmtime = { version = "49.0.4", default-features = false, features = [
46
+ wasmtime = { version = "50.0.0", default-features = false, features = [
46
47
  "cranelift",
47
48
  "runtime",
48
49
  "gc",
@@ -52,14 +53,14 @@ wasmtime = { version = "49.0.4", default-features = false, features = [
52
53
  "wat",
53
54
  ] }
54
55
  # wasmtime-wasi answers the preview1 calls that read no descriptor
55
- # (args, environ, clocks, random) and supplies the memory pipes the
56
- # capture channels write into; kobako answers every call that reads its
57
- # own descriptor table. `p1` is the only feature kobako asks for; it
56
+ # (args, environ, clocks, random); kobako answers every call that reads
57
+ # its own descriptor table. `p1` is the only feature kobako asks for; it
58
58
  # enables `p2` itself, while `p0` and `p3` stay off.
59
- wasmtime-wasi = { version = "49.0.4", default-features = false, features = ["p1"] }
60
- # bytes is the buffer type wasmtime-wasi's stream traits take, for the
61
- # capture pipes kobako's own preview1 imports write into.
62
- bytes = "1.11"
59
+ wasmtime-wasi = { version = "50.0.0", default-features = false, features = ["p1"] }
60
+ # wiggle generates the preview1 calls kobako answers from `witx/`, as it
61
+ # generates wasmtime-wasi's, so each takes its arguments and writes its answer
62
+ # as wasmtime-wasi does. It rides wasmtime's release train.
63
+ wiggle = { version = "50.0.0", default-features = false, features = ["wasmtime"] }
63
64
  # sha2 keys the on-disk compiled-module cache by Guest Binary content
64
65
  # (see cache.rs); a collision would load the wrong artifact, so the
65
66
  # hash must be cryptographic.
@@ -12,21 +12,25 @@ contract surface.
12
12
  |---|---|
13
13
  | Compilation | process-wide Engine and compiled-Module caches |
14
14
  | Disk cache | AOT `.cwasm` artifacts on Unix, keyed by Guest Binary content and engine |
15
- | Instantiation | a pre-linked `InstancePre` per guest path; every invocation runs on a fresh instance and discards its Store afterwards |
15
+ | Instantiation | a pre-linked `InstancePre` per guest path, a fresh instance per invocation |
16
16
  | Bounds | epoch-based wall-clock deadlines on setup and on each invocation, and the per-invocation linear-memory cap |
17
17
  | Ambient denial | under the default hermetic profile, frozen clocks and a constant RNG |
18
18
  | Filesystems | a preopen per Mount, and its own capped descriptor table |
19
19
 
20
- The kobako Ruby gem's native ext is the first frontend; a Rust host
21
- SDK consumes the same surface.
20
+ Each invocation discards its Store once it ends, so nothing one leaves
21
+ behind reaches the next. The kobako Ruby gem's native ext is the first
22
+ frontend; a Rust host SDK consumes the same surface.
22
23
 
23
24
  ## Usage
24
25
 
26
+ Add the crate beside `kobako-runtime` to run a Guest Binary through its
27
+ `Driver`.
28
+
25
29
  ```toml
26
30
  [dependencies]
27
- kobako-wasmtime = "0.19.0" # x-release-please-version
31
+ kobako-wasmtime = "0.20.0" # x-release-please-version
28
32
  ```
29
33
 
30
34
  ## License
31
35
 
32
- Apache-2.0
36
+ Licensed under [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0).
@@ -1,4 +1,5 @@
1
- //! The Guest Binary's declared ABI version check.
1
+ //! Reading the Guest Binary's declared ABI version for the endpoint to
2
+ //! accept or refuse.
2
3
  //!
3
4
  //! An artifact reports one ABI version, so the answer belongs to the
4
5
  //! module and not to the sandbox asking for it. The check therefore runs
@@ -9,12 +10,12 @@ use std::time::Duration;
9
10
 
10
11
  use wasmtime::{AsContextMut, InstancePre, Store as WtStore};
11
12
 
12
- use crate::cache::shared_engine;
13
+ use crate::cache::load_engine;
13
14
  use crate::invocation::Invocation;
14
15
  use crate::trap;
15
16
  use crate::wasi::Wasi;
17
+ use kobako_runtime::endpoint;
16
18
  use kobako_runtime::error::{SetupError, Trap};
17
- use kobako_transport::abi::ABI_VERSION;
18
19
 
19
20
  /// An absent or unequal version is a deterministic artifact fault, so
20
21
  /// every failure is a `SetupError`. `setup_timeout` bounds instantiating
@@ -27,7 +28,7 @@ pub(crate) fn verify(
27
28
  let wasi = Wasi::for_probe().map_err(|t| SetupError::Dead(t.to_string()))?;
28
29
  // The memory cap stays off: it arms per invocation, and the probe is
29
30
  // none.
30
- let mut store = WtStore::new(shared_engine()?, Invocation::new(None, wasi));
31
+ let mut store = WtStore::new(load_engine()?, Invocation::new(None, wasi));
31
32
  trap::arm_deadline(&mut store, setup_timeout);
32
33
  let past_deadline = |err: &wasmtime::Error| {
33
34
  matches!(trap::cap_trap(err), Some(Trap::Timeout(_))).then(|| {
@@ -40,14 +41,11 @@ pub(crate) fn verify(
40
41
  let instance = pre
41
42
  .instantiate(store.as_context_mut())
42
43
  .map_err(|e| past_deadline(&e).unwrap_or_else(|| trap::instantiate_err(e)))?;
43
- let probe = instance
44
- .get_typed_func::<(), u32>(store.as_context_mut(), "__kobako_abi_version")
45
- .map_err(|_| {
46
- SetupError::Dead(format!(
47
- "the Guest Binary does not export __kobako_abi_version; \
48
- rebuild it against ABI version {ABI_VERSION}"
49
- ))
50
- })?;
44
+ let Ok(probe) =
45
+ instance.get_typed_func::<(), u32>(store.as_context_mut(), "__kobako_abi_version")
46
+ else {
47
+ return endpoint::verify_abi_version(None);
48
+ };
51
49
  let reported = probe.call(store.as_context_mut(), ()).map_err(|e| {
52
50
  past_deadline(&e).unwrap_or_else(|| {
53
51
  SetupError::Dead(format!(
@@ -55,14 +53,7 @@ pub(crate) fn verify(
55
53
  ))
56
54
  })
57
55
  })?;
58
- if reported != ABI_VERSION {
59
- return Err(SetupError::Dead(format!(
60
- "the Guest Binary reports ABI version {reported}, but this host \
61
- implements ABI version {ABI_VERSION}; rebuild the Guest Binary \
62
- against the host's version"
63
- )));
64
- }
65
- Ok(())
56
+ endpoint::verify_abi_version(Some(reported))
66
57
  }
67
58
 
68
59
  #[cfg(test)]
@@ -90,6 +81,7 @@ mod tests {
90
81
  stderr_limit: None,
91
82
  open_file_limit: None,
92
83
  profile: Profile::Hermetic,
84
+ security: Vec::new(),
93
85
  };
94
86
  let built = Driver::new(Path::new(FIXTURE), config);
95
87
  assert!(
@@ -8,8 +8,7 @@
8
8
  //! seed. No allowlisted mrbgem reaches these preview1 imports today
9
9
  //! (`build_config/wasi.rb`), but a future libc-backed gem would silently
10
10
  //! obtain real time and host entropy — a covert timing channel and a
11
- //! nondeterminism source the hermetic posture deliberately excludes
12
- //! (docs/guides/security.md).
11
+ //! nondeterminism source the hermetic posture deliberately excludes.
13
12
  //! Pinning the clocks to the Unix epoch and the RNG to a constant stream
14
13
  //! makes that denial a property of the host, not merely of the gem allowlist.
15
14
  //!
@@ -48,7 +48,7 @@ const EPOCH_TICK: Duration = Duration::from_millis(10);
48
48
  /// new-style exception handling (`-mllvm -wasm-use-legacy-eh=false`) for
49
49
  /// its setjmp path. Epoch interruption backs the wall-clock cap, driven by
50
50
  /// one process-wide ticker thread.
51
- pub(crate) fn shared_engine() -> Result<&'static WtEngine, SetupError> {
51
+ pub(crate) fn load_engine() -> Result<&'static WtEngine, SetupError> {
52
52
  if let Some(engine) = SHARED_ENGINE.get() {
53
53
  return Ok(engine);
54
54
  }
@@ -77,7 +77,7 @@ fn spawn_epoch_ticker(engine: WtEngine) {
77
77
 
78
78
  /// A missing file answers `SetupError::ModuleNotBuilt`, the common state of
79
79
  /// a fresh clone before `rake compile`.
80
- pub(crate) fn cached_module(path: &Path) -> Result<WtModule, SetupError> {
80
+ pub(crate) fn load_module(path: &Path) -> Result<WtModule, SetupError> {
81
81
  let cache = MODULE_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
82
82
 
83
83
  if let Some(module) = cache
@@ -102,7 +102,7 @@ pub(crate) fn cached_module(path: &Path) -> Result<WtModule, SetupError> {
102
102
  path.display()
103
103
  ))
104
104
  })?;
105
- let engine = shared_engine()?;
105
+ let engine = load_engine()?;
106
106
  let artifact = artifact_path(engine, &bytes);
107
107
  let module = match artifact.as_deref().and_then(|p| load_artifact(engine, p)) {
108
108
  Some(module) => module,
@@ -276,7 +276,7 @@ fn prune_stale(dir: &rustix::fd::OwnedFd, keep: &std::ffi::OsStr) {
276
276
  };
277
277
  while let Some(Ok(entry)) = entries.read() {
278
278
  let name = entry.file_name();
279
- if name.to_bytes() == keep.as_bytes() || !name.to_str().is_ok_and(cache_entry_name) {
279
+ if name.to_bytes() == keep.as_bytes() || !name.to_str().is_ok_and(is_cache_entry_name) {
280
280
  continue;
281
281
  }
282
282
  let stale = statat(dir, name, AtFlags::SYMLINK_NOFOLLOW)
@@ -295,7 +295,7 @@ fn prune_stale(dir: &rustix::fd::OwnedFd, keep: &std::ffi::OsStr) {
295
295
  }
296
296
 
297
297
  #[cfg(unix)]
298
- fn cache_entry_name(name: &str) -> bool {
298
+ fn is_cache_entry_name(name: &str) -> bool {
299
299
  name.ends_with(".cwasm") || name.contains(".tmp")
300
300
  }
301
301
 
@@ -6,14 +6,17 @@
6
6
  //! per-invocation `crate::invocation::Invocation` (which the wasm engine
7
7
  //! mutates from inside a run).
8
8
  //!
9
- //! Field-for-field the same shape as the SDK's `kobako::Options`, and
10
- //! deliberately not shared with it: this struct is free to name a wasmtime
11
- //! type, and `Options` is not — an engine-free host must still be able to
12
- //! build the SDK.
9
+ //! Its caps are field-for-field the SDK's `kobako::Options`, and deliberately
10
+ //! not shared with it: this struct is free to name a wasmtime type, and
11
+ //! `Options` is not — an engine-free host must still be able to build the SDK.
12
+ //! The security modules are the Driver's own, as Linux's LSMs are the
13
+ //! kernel's: whoever builds the Driver hands them here.
13
14
 
15
+ use std::sync::Arc;
14
16
  use std::time::Duration;
15
17
 
16
18
  use kobako_runtime::profile::Profile;
19
+ use kobako_runtime::vfs::SecurityOperations;
17
20
 
18
21
  /// The caps and requested isolation profile for one `Driver`. `None` on
19
22
  /// any cap field disables that cap.
@@ -35,12 +38,20 @@ pub struct Config {
35
38
  pub stderr_limit: Option<usize>,
36
39
  /// Descriptors one invocation may hold open, the standard ones and
37
40
  /// each Mount's preopen included; an open past it answers `MFILE`.
41
+ /// Only the guest's descriptors count, as `RLIMIT_NOFILE` counts a
42
+ /// process's: the host descriptors a Filesystem holds behind one are
43
+ /// its own, up to two for a file and one for each Layer holding a
44
+ /// directory, Directory Layers alone holding any.
38
45
  pub open_file_limit: Option<usize>,
39
46
  /// Isolation posture the frontend requested. `Hermetic` freezes
40
47
  /// ambient time and entropy, `Permissive` leaves the live WASI
41
48
  /// sources, and `Driver::profile` declares it back as the built
42
49
  /// posture.
43
50
  pub profile: Profile,
51
+ /// Security modules every invocation is judged by, each asked in order
52
+ /// after kobako's own rules and before any Mount's Policy, as Linux
53
+ /// stacks the LSMs it was booted with.
54
+ pub security: Vec<Arc<dyn SecurityOperations>>,
44
55
  }
45
56
 
46
57
  impl Default for Config {
@@ -56,6 +67,7 @@ impl Default for Config {
56
67
  stderr_limit: Some(1 << 20),
57
68
  open_file_limit: Some(1024),
58
69
  profile: Profile::Hermetic,
70
+ security: Vec::new(),
59
71
  }
60
72
  }
61
73
  }
@@ -77,7 +89,8 @@ mod tests {
77
89
  config.stdout_limit,
78
90
  config.stderr_limit,
79
91
  config.open_file_limit,
80
- config.profile
92
+ config.profile,
93
+ config.security.len()
81
94
  ),
82
95
  (
83
96
  Some(Duration::from_secs(60)),
@@ -86,9 +99,11 @@ mod tests {
86
99
  Some(1024 * 1024),
87
100
  Some(1024 * 1024),
88
101
  Some(1024),
89
- Profile::Hermetic
102
+ Profile::Hermetic,
103
+ 0
90
104
  ),
91
- "Config::default must carry the caps every frontend defaults to"
105
+ "Config::default must carry the caps every frontend defaults to, and no security \
106
+ module beyond kobako's own"
92
107
  );
93
108
  }
94
109
  }