kobako 0.28.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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +377 -0
  4. data/Cargo.lock +181 -154
  5. data/README.md +99 -459
  6. data/ROADMAP.md +7 -1
  7. data/SECURITY.md +1 -1
  8. data/crates/kobako-runtime/CHANGELOG.md +354 -0
  9. data/crates/kobako-runtime/Cargo.toml +38 -7
  10. data/crates/kobako-runtime/README.md +17 -8
  11. data/crates/kobako-runtime/src/dispatch.rs +9 -1
  12. data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
  13. data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
  14. data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
  15. data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
  16. data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
  17. data/crates/kobako-runtime/src/endpoint.rs +166 -0
  18. data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
  19. data/crates/kobako-runtime/src/filesystem/bind/lookup.rs +236 -0
  20. data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
  21. data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
  22. data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
  23. data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
  24. data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
  25. data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
  26. data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
  27. data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
  28. data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
  29. data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +301 -0
  30. data/crates/kobako-runtime/src/filesystem/overlay.rs +3469 -0
  31. data/crates/kobako-runtime/src/filesystem/path.rs +21 -0
  32. data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
  33. data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
  34. data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
  35. data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
  36. data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
  37. data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
  38. data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
  39. data/crates/kobako-runtime/src/filesystem.rs +26 -0
  40. data/crates/kobako-runtime/src/grant.rs +20 -0
  41. data/crates/kobako-runtime/src/hashtable.rs +44 -0
  42. data/crates/kobako-runtime/src/lib.rs +16 -1
  43. data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
  44. data/crates/kobako-runtime/src/locking.rs +218 -0
  45. data/crates/kobako-runtime/src/mount.rs +105 -0
  46. data/crates/kobako-runtime/src/profile.rs +140 -4
  47. data/crates/kobako-runtime/src/runtime.rs +12 -0
  48. data/crates/kobako-runtime/src/sched.rs +210 -0
  49. data/crates/kobako-runtime/src/security.rs +696 -0
  50. data/crates/kobako-runtime/src/snapshot.rs +20 -5
  51. data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
  52. data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
  53. data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
  54. data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
  55. data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
  56. data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
  57. data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
  58. data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
  59. data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
  60. data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
  61. data/crates/kobako-runtime/src/vfs/path.rs +64 -0
  62. data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
  63. data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
  64. data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
  65. data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
  66. data/crates/kobako-runtime/src/vfs.rs +60 -0
  67. data/crates/kobako-runtime/src/yielder.rs +11 -6
  68. data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
  69. data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
  70. data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
  71. data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
  72. data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
  73. data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
  74. data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
  75. data/crates/kobako-transport/CHANGELOG.md +23 -0
  76. data/crates/kobako-transport/Cargo.toml +3 -3
  77. data/crates/kobako-transport/README.md +6 -5
  78. data/crates/kobako-transport/src/abi.rs +1 -1
  79. data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
  80. data/crates/kobako-transport/src/envelope/call.rs +5 -5
  81. data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
  82. data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
  83. data/crates/kobako-transport/src/envelope/fault.rs +4 -3
  84. data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
  85. data/crates/kobako-transport/src/envelope/preamble.rs +24 -24
  86. data/crates/kobako-transport/src/envelope/reply.rs +34 -16
  87. data/crates/kobako-transport/src/envelope/request.rs +19 -9
  88. data/crates/kobako-transport/src/envelope/run.rs +1 -1
  89. data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
  90. data/crates/kobako-transport/src/envelope.rs +2 -2
  91. data/crates/kobako-wasmtime/CHANGELOG.md +265 -0
  92. data/crates/kobako-wasmtime/Cargo.toml +25 -19
  93. data/crates/kobako-wasmtime/README.md +13 -7
  94. data/crates/kobako-wasmtime/src/abi.rs +75 -49
  95. data/crates/kobako-wasmtime/src/ambient.rs +64 -6
  96. data/crates/kobako-wasmtime/src/cache.rs +188 -77
  97. data/crates/kobako-wasmtime/src/config.rs +77 -5
  98. data/crates/kobako-wasmtime/src/descriptors.rs +200 -0
  99. data/crates/kobako-wasmtime/src/driver.rs +70 -135
  100. data/crates/kobako-wasmtime/src/exports.rs +5 -16
  101. data/crates/kobako-wasmtime/src/guest.rs +38 -0
  102. data/crates/kobako-wasmtime/src/imports.rs +52 -0
  103. data/crates/kobako-wasmtime/src/instance_pre.rs +18 -46
  104. data/crates/kobako-wasmtime/src/invocation.rs +43 -74
  105. data/crates/kobako-wasmtime/src/lib.rs +5 -4
  106. data/crates/kobako-wasmtime/src/limiter.rs +6 -230
  107. data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +632 -0
  108. data/crates/kobako-wasmtime/src/preview1/differential.rs +486 -0
  109. data/crates/kobako-wasmtime/src/preview1/errno.rs +135 -0
  110. data/crates/kobako-wasmtime/src/preview1/fd.rs +1420 -0
  111. data/crates/kobako-wasmtime/src/preview1/memory.rs +172 -0
  112. data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
  113. data/crates/kobako-wasmtime/src/preview1/mount_view.rs +3766 -0
  114. data/crates/kobako-wasmtime/src/preview1/path.rs +302 -0
  115. data/crates/kobako-wasmtime/src/preview1/poll.rs +381 -0
  116. data/crates/kobako-wasmtime/src/preview1/sock.rs +13 -0
  117. data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
  118. data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
  119. data/crates/kobako-wasmtime/src/preview1.rs +512 -0
  120. data/crates/kobako-wasmtime/src/trap.rs +45 -25
  121. data/crates/kobako-wasmtime/src/wasi.rs +333 -0
  122. data/crates/kobako-wasmtime/tests/integration/compile_posture.rs +43 -0
  123. data/crates/kobako-wasmtime/tests/integration/guest_file.rs +1085 -0
  124. data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
  125. data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +4 -3
  126. data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +5 -4
  127. data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +9 -13
  128. data/crates/kobako-wasmtime/tests/integration/unanswered_call.rs +86 -0
  129. data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +2 -1
  130. data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
  131. data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
  132. data/data/kobako.wasm +0 -0
  133. data/ext/kobako/Cargo.toml +4 -2
  134. data/ext/kobako/src/runtime/bridge.rs +154 -81
  135. data/ext/kobako/src/runtime/errors.rs +1 -1
  136. data/ext/kobako/src/runtime/escape.rs +67 -0
  137. data/ext/kobako/src/runtime/filesystem/grants.rs +107 -0
  138. data/ext/kobako/src/runtime/filesystem/ruby.rs +708 -0
  139. data/ext/kobako/src/runtime/filesystem.rs +208 -0
  140. data/ext/kobako/src/runtime/gvl.rs +49 -8
  141. data/ext/kobako/src/runtime/pinned_value.rs +31 -0
  142. data/ext/kobako/src/runtime/snapshot.rs +127 -0
  143. data/ext/kobako/src/runtime.rs +125 -199
  144. data/lib/kobako/catalog/extensions.rb +5 -5
  145. data/lib/kobako/catalog/flags.rb +2 -14
  146. data/lib/kobako/catalog/grants.rb +30 -0
  147. data/lib/kobako/catalog/handles.rb +6 -6
  148. data/lib/kobako/catalog/mount.rb +19 -0
  149. data/lib/kobako/catalog/services.rb +13 -2
  150. data/lib/kobako/catalog/snippets.rb +11 -15
  151. data/lib/kobako/catalog.rb +11 -0
  152. data/lib/kobako/codec/decoder.rb +10 -9
  153. data/lib/kobako/codec/ext_types.rb +4 -6
  154. data/lib/kobako/codec/handle_walk.rb +4 -6
  155. data/lib/kobako/codec/nesting.rb +7 -7
  156. data/lib/kobako/codec/state.rb +19 -3
  157. data/lib/kobako/codec/utils.rb +1 -1
  158. data/lib/kobako/codec/value_budget.rb +86 -0
  159. data/lib/kobako/codec.rb +7 -0
  160. data/lib/kobako/context.rb +40 -13
  161. data/lib/kobako/errors.rb +15 -14
  162. data/lib/kobako/filesystem.rb +69 -0
  163. data/lib/kobako/payload/arguments.rb +2 -2
  164. data/lib/kobako/pool.rb +6 -6
  165. data/lib/kobako/sandbox.rb +55 -27
  166. data/lib/kobako/sandbox_options.rb +39 -13
  167. data/lib/kobako/snippet/{binary.rb → bytecode.rb} +2 -2
  168. data/lib/kobako/snippet/source.rb +1 -1
  169. data/lib/kobako/snippet.rb +1 -1
  170. data/lib/kobako/transport/block_failure.rb +35 -0
  171. data/lib/kobako/transport/dispatcher.rb +33 -29
  172. data/lib/kobako/transport/exposure.rb +7 -9
  173. data/lib/kobako/transport/reflection.rb +79 -23
  174. data/lib/kobako/transport/run.rb +5 -5
  175. data/lib/kobako/transport/yielder.rb +9 -31
  176. data/lib/kobako/transport.rb +1 -0
  177. data/lib/kobako/version.rb +1 -1
  178. data/lib/kobako.rb +35 -0
  179. data/release-please-config.json +1 -0
  180. data/sig/kobako/catalog/extensions.rbs +3 -3
  181. data/sig/kobako/catalog/flags.rbs +0 -4
  182. data/sig/kobako/catalog/grants.rbs +13 -0
  183. data/sig/kobako/catalog/handles.rbs +3 -3
  184. data/sig/kobako/catalog/mount.rbs +24 -0
  185. data/sig/kobako/catalog/services.rbs +5 -1
  186. data/sig/kobako/catalog/snippets.rbs +4 -4
  187. data/sig/kobako/catalog.rbs +1 -0
  188. data/sig/kobako/codec/decoder.rbs +1 -1
  189. data/sig/kobako/codec/handle_walk.rbs +1 -1
  190. data/sig/kobako/codec/nesting.rbs +2 -2
  191. data/sig/kobako/codec/state.rbs +7 -1
  192. data/sig/kobako/codec/utils.rbs +1 -1
  193. data/sig/kobako/codec/value_budget.rbs +23 -0
  194. data/sig/kobako/codec.rbs +3 -2
  195. data/sig/kobako/context.rbs +14 -6
  196. data/sig/kobako/errors.rbs +4 -4
  197. data/sig/kobako/filesystem.rbs +93 -0
  198. data/sig/kobako/guest_yielder.rbs +9 -0
  199. data/sig/kobako/payload/arguments.rbs +1 -1
  200. data/sig/kobako/pool.rbs +3 -3
  201. data/sig/kobako/runtime.rbs +21 -49
  202. data/sig/kobako/sandbox.rbs +9 -5
  203. data/sig/kobako/sandbox_options.rbs +25 -3
  204. data/sig/kobako/snapshot.rbs +20 -0
  205. data/sig/kobako/snippet/bytecode.rbs +12 -0
  206. data/sig/kobako/transport/block_failure.rbs +13 -0
  207. data/sig/kobako/transport/dispatcher.rbs +4 -4
  208. data/sig/kobako/transport/exposure.rbs +2 -2
  209. data/sig/kobako/transport/reflection.rbs +14 -1
  210. data/sig/kobako/transport/run.rbs +3 -3
  211. data/sig/kobako/transport/yielder.rbs +3 -7
  212. data/sig/kobako/transport.rbs +3 -3
  213. metadata +116 -15
  214. data/crates/kobako-wasmtime/src/capture.rs +0 -96
  215. data/crates/kobako-wasmtime/src/dispatch.rs +0 -107
  216. data/crates/kobako-wasmtime/src/guest_mem.rs +0 -265
  217. data/crates/kobako-wasmtime/src/io.rs +0 -286
  218. data/sig/kobako/snippet/binary.rbs +0 -12
@@ -1,5 +1,270 @@
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->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
+
154
+ ## [0.19.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.18.0...kobako-wasmtime-v0.19.0) (2026-10-26)
155
+
156
+
157
+ ### ⚠ BREAKING CHANGES
158
+
159
+ * **wasmtime:** a struct literal no longer builds the wasmtime driver's Config outside its crate; start from `Config::default()` and assign each field.
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.
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.
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.
164
+ * **io:** putc, IO#read and SystemCallError.new raise RangeError for those numbers instead of writing, refusing or ignoring them.
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.
166
+ * **guest:** a Service or Extension backend bound at the path File fails its first invocation, naming the segment the Guest Binary now defines.
167
+ * **runtime:** the Runtime trait's invoke takes the invocation's Grants as a fourth argument, so every engine implementing it must accept them.
168
+
169
+ ### Features
170
+
171
+ * check what a rename moves as it moves, refusing a directory beneath a Policy ([23794bf](https://github.com/elct9620/kobako/commit/23794bfcb54e148126e29873fa9b52d9e3de8d8c))
172
+ * **guest:** carry mruby-bigint so an Integer has no width of its own ([a74266d](https://github.com/elct9620/kobako/commit/a74266d3e7b1295b75770b62894b232195f9b3bb))
173
+ * **guest:** ship File in the default Guest Binary over the filesystem Grants ([535527b](https://github.com/elct9620/kobako/commit/535527b2c6beb82fe37758df62c3804ce5c7191a)), closes [#78](https://github.com/elct9620/kobako/issues/78)
174
+ * **guest:** widen the guest Integer to 64 bits ([7236a26](https://github.com/elct9620/kobako/commit/7236a26285dfdef175048f659c1094dd3bbb5b4d))
175
+ * **io:** create a file only where nothing is through File.open's wx ([56c5665](https://github.com/elct9620/kobako/commit/56c5665e4c87ac30b8035aaed207ce840efad25c))
176
+ * **io:** open a file by an Integer mode of File::Constants flags ([e1f484c](https://github.com/elct9620/kobako/commit/e1f484c713df4d894744833836cf9a29ba9677ee))
177
+ * **io:** open a file through wasi-libc so a create needs no write ([6d50ca4](https://github.com/elct9620/kobako/commit/6d50ca4d79b70956d113f0798255300aa8a7fe92))
178
+ * let a Host App bound how long a Guest Binary takes to be set up ([2addab0](https://github.com/elct9620/kobako/commit/2addab002550539e3ebb24bdd599dd424c2dc3d9))
179
+ * **runtime:** hand each invocation its Grants and let an engine declare the kinds it supports ([cba90ee](https://github.com/elct9620/kobako/commit/cba90eed09c8afcb23080cef1c9cbb3650648472)), closes [#78](https://github.com/elct9620/kobako/issues/78)
180
+ * **runtime:** keep a directory descriptor's identity its own while open ([6267642](https://github.com/elct9620/kobako/commit/6267642ea831917be76a0101e8d206ccfa8834c1))
181
+ * **runtime:** let a dispatch handler abandon its invocation ([179da9e](https://github.com/elct9620/kobako/commit/179da9ee1f31fed9e6d0c149faee0ad55f8db487))
182
+ * **runtime:** let a Filesystem's file refuse its close ([af16007](https://github.com/elct9620/kobako/commit/af1600797b098796dd6f329672c7d04e2e1cd2fa)), closes [#78](https://github.com/elct9620/kobako/issues/78)
183
+ * **runtime:** let a Mount decide its own access as the one kind of Grant ([5c9189e](https://github.com/elct9620/kobako/commit/5c9189eea8ac61cf165dca1b38e459c4f14acdd0)), closes [#78](https://github.com/elct9620/kobako/issues/78)
184
+ * **runtime:** let a Mount's Policy judge the spelling the host stores ([5281057](https://github.com/elct9620/kobako/commit/52810573f22d2a3edfae9b34b7767a5f440fba15))
185
+ * **runtime:** let the records a Filesystem or an engine builds gain fields ([c0ef88d](https://github.com/elct9620/kobako/commit/c0ef88d0b6df16d651e4c49226e41667307d772f))
186
+ * **runtime:** name a file grown past what a Filesystem keeps ([7bac3e6](https://github.com/elct9620/kobako/commit/7bac3e65534097b85d93f5967b6c73a979869a78))
187
+ * **runtime:** name each code a Filesystem may fail with ([1d980fa](https://github.com/elct9620/kobako/commit/1d980fa4328bad42e8a8665adfec9fc844690e97))
188
+ * **runtime:** refuse a Mount at a path that is no absolute guest path ([a525758](https://github.com/elct9620/kobako/commit/a5257589f9d831b9a3b6d1952df6e5c8469630e1))
189
+ * **wasmtime:** answer a Filesystem out of space as ENOSPC ([c648ebf](https://github.com/elct9620/kobako/commit/c648ebf63a871b7d4417110de00decf1738a19d6))
190
+ * **wasmtime:** cap the descriptors one invocation holds open ([03409c1](https://github.com/elct9620/kobako/commit/03409c1437f71312c584a92e22054b90241535a3))
191
+ * **wasmtime:** let Config gain a cap without breaking a frontend ([55d8093](https://github.com/elct9620/kobako/commit/55d80933b958bab67f1b94deb7633272dc56f972))
192
+ * **wasmtime:** present each filesystem Grant to the guest as a preopen ([68149a7](https://github.com/elct9620/kobako/commit/68149a7adc912172b72e7a34bd39082a3b2a9e95)), closes [#78](https://github.com/elct9620/kobako/issues/78)
193
+
194
+
195
+ ### Bug Fixes
196
+
197
+ * append through a Filesystem's file only in one step ([0509ca7](https://github.com/elct9620/kobako/commit/0509ca70d2d5a832303973ba0f7280be0731fd3d))
198
+ * ask a mount's block once for a read ([f89bd16](https://github.com/elct9620/kobako/commit/f89bd16c1a71ed1d385ab9d3ac5974ac3f0a9103))
199
+ * follow no link swapped in after a Policy judges a path ([ed93213](https://github.com/elct9620/kobako/commit/ed9321303dd45e113fc413e99e1240118cc4bbe4))
200
+ * hold an Overlay's lock across each call, waiting without the GVL ([b8e7342](https://github.com/elct9620/kobako/commit/b8e734273fdc577a40be97c43c46d9548d66c625))
201
+ * **io:** build Errno and IOError as CRuby does ([da77c47](https://github.com/elct9620/kobako/commit/da77c4725d3701ba982f5425b1fcf2fb0038ac89))
202
+ * **io:** give File::Constants CRuby's Linux values ([14cadd8](https://github.com/elct9620/kobako/commit/14cadd88ac5a5dcb8f73b252b68c493488b3e78a))
203
+ * **io:** keep the number a SystemCallError is built with ([5fd327a](https://github.com/elct9620/kobako/commit/5fd327add59173cd5756fecb41d97aebcba7b881))
204
+ * **io:** raise each code the engine answers as its own Errno ([eb6382e](https://github.com/elct9620/kobako/commit/eb6382e506a30e82576fa12ab9859e7d35be2b17))
205
+ * **io:** raise Errno::EIO for a file failure no other code names ([8a0447d](https://github.com/elct9620/kobako/commit/8a0447d2d09ea83e6f7771cf0411bfd6deec3d1a)), closes [#78](https://github.com/elct9620/kobako/issues/78)
206
+ * **io:** raise RangeError for a number past what putc, read and errno take ([6f87cc0](https://github.com/elct9620/kobako/commit/6f87cc08dff7146415f80ca25cd75ddbec4391c0))
207
+ * **io:** raise the error a failed file write names ([bcc8e00](https://github.com/elct9620/kobako/commit/bcc8e00662741fcac3b91f58c0b60f4a79d2ab9f))
208
+ * **io:** write a file to the end when its Filesystem takes it in parts ([029b439](https://github.com/elct9620/kobako/commit/029b43939a3a48bc2925057df69bfcb85b1a0548))
209
+ * keep a link within the directory a descriptor opened ([952dd05](https://github.com/elct9620/kobako/commit/952dd0556969683f44798da94f9fc7a17c1f6d55))
210
+ * run a stored block as the block of the Call that carried it ([bf33dc1](https://github.com/elct9620/kobako/commit/bf33dc1fd40c83498c6c12b0be52b3a835a144cf))
211
+ * **runtime:** make every Filesystem answer the methods that keep a guarantee ([00f5af6](https://github.com/elct9620/kobako/commit/00f5af63f34ac7722249746d0b0eb566da618803))
212
+ * **runtime:** report a merged directory's identity alike through an Overlay's lstat ([ded3b4a](https://github.com/elct9620/kobako/commit/ded3b4aba78efa678318c38b139b5c9c76b5d3fd))
213
+ * **wasmtime:** answer a Call the host cannot serve with the reason ([cf60dcc](https://github.com/elct9620/kobako/commit/cf60dccc63a0d0cfc959f701b7212c78beafa528))
214
+ * **wasmtime:** answer a path past Linux's limits as NAMETOOLONG ([af43917](https://github.com/elct9620/kobako/commit/af43917e30a634d6c1bd70813044967bd1057ba3))
215
+ * **wasmtime:** answer a stat that follows no link with the link itself ([ad0072e](https://github.com/elct9620/kobako/commit/ad0072e03ecc363487598a507de20a8f7aec2927))
216
+ * **wasmtime:** answer a wait to write a file by what its Mount permits ([bf863db](https://github.com/elct9620/kobako/commit/bf863db998edeab60f9276fc53e8adcf8cf98364))
217
+ * **wasmtime:** answer an offset past the largest signed offset as EINVAL ([b94788e](https://github.com/elct9620/kobako/commit/b94788e1c1fc9d86f647da4396bcba3eaec16242))
218
+ * **wasmtime:** answer an unlink with a trailing slash by the name itself ([94e7250](https://github.com/elct9620/kobako/commit/94e72505cfc027319c2139e15488eb2682268d49))
219
+ * **wasmtime:** answer descriptor flags, advice, and clock waits as preview1 does ([c1aec63](https://github.com/elct9620/kobako/commit/c1aec632445c3bf216be6375552c799d7877670f))
220
+ * **wasmtime:** answer dot paths, creates, and far seeks as Linux does ([68c234e](https://github.com/elct9620/kobako/commit/68c234e06859b93fb986bdbab92cca944ce39700))
221
+ * **wasmtime:** answer EINVAL for a guest path holding a NUL ([1c681d6](https://github.com/elct9620/kobako/commit/1c681d6e723f259ab2b3acc5e3a33d85499a2ef5))
222
+ * **wasmtime:** answer ENOENT for an open of a missing path that makes nothing ([48e3944](https://github.com/elct9620/kobako/commit/48e3944c64d7ef410fcc4595d80ba0284f257a09))
223
+ * **wasmtime:** answer every preview1 call that reads a descriptor from kobako's own table ([d0e39ae](https://github.com/elct9620/kobako/commit/d0e39aebd6d729d49111340af7f2af4e37aa7909)), closes [#78](https://github.com/elct9620/kobako/issues/78)
224
+ * **wasmtime:** answer fd_allocate beneath a Mount as unsupported ([e3d2071](https://github.com/elct9620/kobako/commit/e3d20713e4474cd637dacfece99e52b80d7ad2f6))
225
+ * **wasmtime:** answer what stands at a path before a Mount refuses an open ([81e02dc](https://github.com/elct9620/kobako/commit/81e02dc5e7d500b2971ab5d4a670d38601f89389))
226
+ * **wasmtime:** ask a Policy once for an open that may create ([a4613cb](https://github.com/elct9620/kobako/commit/a4613cb8cff89af51f55ce090433c61268105789))
227
+ * **wasmtime:** bound a buffer outside memory before a file's access is checked ([8b76f76](https://github.com/elct9620/kobako/commit/8b76f764a6336d2e77baa2ca6fe63dfd7663ce19))
228
+ * **wasmtime:** bound reads, writable waits, and a FIFO's names as preview1 hosts do ([6d89e55](https://github.com/elct9620/kobako/commit/6d89e55541f3366815dd7ed271e813a6ba41bcb3))
229
+ * **wasmtime:** bound what one preview1 call copies to wasmtime's hostcall fuel ([097c554](https://github.com/elct9620/kobako/commit/097c55426c1e71467bb1e372abc190331dd3e601))
230
+ * **wasmtime:** check path_open's flags as wasmtime-wasi does ([22eb494](https://github.com/elct9620/kobako/commit/22eb4944117260831e92fe616b02888078c02b0c))
231
+ * **wasmtime:** close the file fd_renumber moves a descriptor onto ([0f6148d](https://github.com/elct9620/kobako/commit/0f6148d536ed3e9e96749c2e4a60bf6275332655))
232
+ * **wasmtime:** give each writer of a cached artifact its own temp file ([995a46e](https://github.com/elct9620/kobako/commit/995a46e99dc1c485a303bb7f023b4e668a2801a3))
233
+ * **wasmtime:** grant the compile instance no ambient authority ([c3edc71](https://github.com/elct9620/kobako/commit/c3edc71ab99dac57cbabfc685a7cc60a5a5c06ac))
234
+ * **wasmtime:** hold the whole invocation to its deadline ([231e325](https://github.com/elct9620/kobako/commit/231e32592699008adf3105050bee8b5528ea27a8))
235
+ * **wasmtime:** judge the cache directory itself, not where a link leads ([6baf6fd](https://github.com/elct9620/kobako/commit/6baf6fd2ad845cea83049c4b2bb51174aa205cd0))
236
+ * **wasmtime:** keep a write and its file position within the largest signed offset ([29d4c34](https://github.com/elct9620/kobako/commit/29d4c341fc5a47ee43fdd249918e1f3e68120f86))
237
+ * **wasmtime:** key the compiled-artifact cache by the engine that compiles it ([b43bad2](https://github.com/elct9620/kobako/commit/b43bad2fe176c153ed82a3a8825698414df78dfa))
238
+ * **wasmtime:** leave a link named with a trailing slash in place ([9b3cdea](https://github.com/elct9620/kobako/commit/9b3cdea6dc00353c5c4896ec29b88eebdaddc8ab))
239
+ * **wasmtime:** list a directory once for each listing a guest reads ([c333001](https://github.com/elct9620/kobako/commit/c3330011725c3dba01ea248ed7d1dfea31085af0))
240
+ * **wasmtime:** look up what stands before a dot path as Linux does ([0898eaf](https://github.com/elct9620/kobako/commit/0898eafb55397c84ca9007561ddb35dd007ed2b8))
241
+ * **wasmtime:** place an append at the end as the file stands when it lands ([d7291c4](https://github.com/elct9620/kobako/commit/d7291c4f32fb1f5f2494c69c76796476bbcf4d43))
242
+ * **wasmtime:** reach the artifact cache through the directory it checked ([462fe91](https://github.com/elct9620/kobako/commit/462fe918d516cd639e405b06a5e280a9e58624df))
243
+ * **wasmtime:** refuse a `..` that steps out of a host link ([4599386](https://github.com/elct9620/kobako/commit/45993868c7334376f5d1a455afbd15fe4a2146bc))
244
+ * **wasmtime:** refuse a directory made in an opened directory's place ([5e67d4c](https://github.com/elct9620/kobako/commit/5e67d4cd2a52e3aa37bdd615fe1ae101a618db88))
245
+ * **wasmtime:** refuse a link an open asks not to follow ([123a01b](https://github.com/elct9620/kobako/commit/123a01b01f59cc69c0e3a23f9aeb01aa89132c07))
246
+ * **wasmtime:** refuse a link beneath a Policy before the Policy judges the path ([0e08e7b](https://github.com/elct9620/kobako/commit/0e08e7b463344ae34b5feb1787f8af8a6c719063))
247
+ * **wasmtime:** refuse a link to a directory an open asks not to follow ([eec218f](https://github.com/elct9620/kobako/commit/eec218fdd5584f641070032cf1110ef1683eaae5))
248
+ * **wasmtime:** refuse a path climbing above the directory it is opened through ([ae52746](https://github.com/elct9620/kobako/commit/ae52746761624249b7ff1e1fa09ece4ea8130ced))
249
+ * **wasmtime:** refuse a path through a link beneath a Mount with a Policy ([3da1641](https://github.com/elct9620/kobako/commit/3da16416692ade01db2d9cf366d54d3ec5f50605))
250
+ * **wasmtime:** refuse a rename of a file onto a path ending in a slash ([486a0ab](https://github.com/elct9620/kobako/commit/486a0ab3584772b58e181d5de57ef505e610eb78))
251
+ * **wasmtime:** refuse an argument preview1 cannot hold in wasmtime-wasi's order ([9c76e21](https://github.com/elct9620/kobako/commit/9c76e2181a0dabd81f02723154cc4290215fcf70))
252
+ * **wasmtime:** refuse to remove or move a Mount's root ([9f2e000](https://github.com/elct9620/kobako/commit/9f2e000115a4dd4aecfcc4b1cd39b23f6f20fe05))
253
+ * **wasmtime:** report a host FIFO or device as of an unknown filetype ([5a212e7](https://github.com/elct9620/kobako/commit/5a212e7a7c0f069ffdcc56b850fab3778863d9ee))
254
+ * **wasmtime:** require wasmtime 49.0.4 for the aarch64 br_table fix ([390e30b](https://github.com/elct9620/kobako/commit/390e30ba51ed4175820f2bb230e3ad5b0ad1e9a5))
255
+ * **wasmtime:** resolve a `..` and a trailing slash as a host does ([d2d04c1](https://github.com/elct9620/kobako/commit/d2d04c1fff24d8829b2cf4fcbf628565f35eeadc))
256
+ * **wasmtime:** resolve a final dot through the name before it ([7a0d3d8](https://github.com/elct9620/kobako/commit/7a0d3d8b23d47d4ba5c942682841434e74d6bf6a))
257
+ * **wasmtime:** resolve a link a stat names with a trailing slash ([6da98fa](https://github.com/elct9620/kobako/commit/6da98facbccaa9bf6d8f8280a56841e19a7c879b))
258
+ * **wasmtime:** size a file opened only to read as wasmtime-wasi and Linux answer ([810f5c8](https://github.com/elct9620/kobako/commit/810f5c8e863dc5bdbe82f8a19c7559fd7eee03f9))
259
+ * **wasmtime:** sync a cached artifact before renaming it into place ([097efc7](https://github.com/elct9620/kobako/commit/097efc77599545bb2a5e729c75d23082470ed8f4))
260
+ * **wasmtime:** take wasmtime 49.0.3 for its sandbox advisories ([7672592](https://github.com/elct9620/kobako/commit/76725923cac50473c7e3a21cc5121e3cbba0cba5))
261
+ * **wasmtime:** trap a guest field address past the address space ([de5d47e](https://github.com/elct9620/kobako/commit/de5d47e10aa5d978573e46a7cb3453ff248d006e))
262
+
263
+
264
+ ### Performance Improvements
265
+
266
+ * **io:** answer IO#read with a length in one read the file holds ([e9161a2](https://github.com/elct9620/kobako/commit/e9161a2018c1617157025de275f77890e865b7d1)), closes [#78](https://github.com/elct9620/kobako/issues/78)
267
+
3
268
  ## [0.18.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.17.0...kobako-wasmtime-v0.18.0) (2026-10-13)
4
269
 
5
270
 
@@ -15,10 +15,11 @@
15
15
 
16
16
  [package]
17
17
  name = "kobako-wasmtime"
18
- version = "0.18.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,17 +31,19 @@ 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.18.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.18.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`
40
41
  # feature so test fixtures can be expressed as text.
41
- # `cache` / `parallel-compilation` / `pooling` / `component-model` / `async`
42
- # are intentionally off — kobako runs short-lived synchronous sandboxes.
43
- wasmtime = { version = "49.0.2", default-features = false, features = [
42
+ # `cache` / `parallel-compilation` / `pooling` are intentionally off —
43
+ # kobako runs short-lived synchronous sandboxes. `component-model` and
44
+ # `async` still arrive through wasmtime-wasi's `p1`, which builds on `p2`;
45
+ # kobako loads only core Modules, so none of that code is reached.
46
+ wasmtime = { version = "50.0.0", default-features = false, features = [
44
47
  "cranelift",
45
48
  "runtime",
46
49
  "gc",
@@ -49,19 +52,22 @@ wasmtime = { version = "49.0.2", default-features = false, features = [
49
52
  "demangle",
50
53
  "wat",
51
54
  ] }
52
- # wasmtime-wasi provides WASI preview1 support for routing guest stdout/stderr
53
- # into in-memory buffers. The `p1` feature enables the
54
- # WasiCtxBuilder + preview1 adapter which wires fd 1/2 to pipes. We omit
55
- # `p2` (component-model) and `p0`/`p3` (async) because kobako runs
56
- # synchronous sandboxes only.
57
- wasmtime-wasi = { version = "49.0.2", default-features = false, features = ["p1"] }
55
+ # wasmtime-wasi answers the preview1 calls that read no descriptor
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
+ # enables `p2` itself, while `p0` and `p3` stay off.
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"] }
58
64
  # sha2 keys the on-disk compiled-module cache by Guest Binary content
59
65
  # (see cache.rs); a collision would load the wrong artifact, so the
60
66
  # hash must be cryptographic.
61
67
  sha2 = "0.11"
62
68
 
63
- # libc supplies geteuid for the cache-directory ownership check gating
64
- # the unsafe artifact deserialize (see cache.rs); std exposes a file's
65
- # owner but not the process's effective uid.
69
+ # rustix reaches the cache directory through one descriptor, checked by
70
+ # owner, so the unsafe artifact deserialize reads only what that
71
+ # directory holds (see cache.rs); std has no `*at` calls.
66
72
  [target.'cfg(unix)'.dependencies]
67
- libc = "0.2"
73
+ rustix = { version = "1.1.4", features = ["fs", "process"] }
@@ -10,21 +10,27 @@ contract surface.
10
10
 
11
11
  | Mechanic | What the driver holds |
12
12
  |---|---|
13
- | Compilation | process-wide Engine and compiled-Module caches, with an on-disk AOT (`.cwasm`) artifact cache keyed by Guest Binary content |
14
- | Instantiation | a pre-linked `InstancePre` per guest path; every invocation runs on a fresh instance and discards its Store afterwards |
15
- | Bounds | the epoch-based wall-clock timeout and the per-invocation linear-memory cap |
13
+ | Compilation | process-wide Engine and compiled-Module caches |
14
+ | Disk cache | AOT `.cwasm` artifacts on Unix, keyed by Guest Binary content and engine |
15
+ | Instantiation | a pre-linked `InstancePre` per guest path, a fresh instance per invocation |
16
+ | Bounds | epoch-based wall-clock deadlines on setup and on each invocation, and the per-invocation linear-memory cap |
16
17
  | Ambient denial | under the default hermetic profile, frozen clocks and a constant RNG |
18
+ | Filesystems | a preopen per Mount, and its own capped descriptor table |
17
19
 
18
- The kobako Ruby gem's native ext is the first frontend; a Rust host
19
- 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.
20
23
 
21
24
  ## Usage
22
25
 
26
+ Add the crate beside `kobako-runtime` to run a Guest Binary through its
27
+ `Driver`.
28
+
23
29
  ```toml
24
30
  [dependencies]
25
- kobako-wasmtime = "0.18.0" # x-release-please-version
31
+ kobako-wasmtime = "0.20.0" # x-release-please-version
26
32
  ```
27
33
 
28
34
  ## License
29
35
 
30
- Apache-2.0
36
+ Licensed under [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0).
@@ -1,67 +1,93 @@
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
5
6
  //! alongside the `InstancePre` it guards (`crate::instance_pre`) and is
6
7
  //! amortised with it, rather than once per `Driver::new`.
7
8
 
9
+ use std::time::Duration;
10
+
8
11
  use wasmtime::{AsContextMut, InstancePre, Store as WtStore};
9
12
 
10
- use crate::cache::shared_engine;
11
- use crate::config::Config;
13
+ use crate::cache::load_engine;
12
14
  use crate::invocation::Invocation;
13
- use crate::{io, trap};
14
- use kobako_runtime::error::SetupError;
15
- use kobako_runtime::profile::Profile;
16
- use kobako_transport::abi::ABI_VERSION;
17
-
18
- /// Caps for the throwaway probe instance. Instantiation runs the
19
- /// artifact's start section, so the capture pipes are sized to keep
20
- /// nothing — the probe reads neither — and the strongest isolation rung
21
- /// is taken, since reading an artifact's version grants no ambient
22
- /// authority. The timeout and memory caps stay off because both arm per
23
- /// invocation and there is none here.
24
- const PROBE_CONFIG: Config = Config {
25
- timeout: None,
26
- memory_limit: None,
27
- stdout_limit: Some(0),
28
- stderr_limit: Some(0),
29
- profile: Profile::Hermetic,
30
- };
15
+ use crate::trap;
16
+ use crate::wasi::Wasi;
17
+ use kobako_runtime::endpoint;
18
+ use kobako_runtime::error::{SetupError, Trap};
31
19
 
32
20
  /// An absent or unequal version is a deterministic artifact fault, so
33
- /// every failure is a `SetupError`. The probe's own WASI context keeps a
34
- /// guest whose start section touches WASI on that path instead of
35
- /// panicking in `Invocation::wasi_mut`.
36
- pub(crate) fn verify(pre: &InstancePre<Invocation>) -> Result<(), SetupError> {
37
- let mut store = WtStore::new(shared_engine()?, Invocation::new(None));
38
- // Epoch interruption is on engine-wide and a fresh Store's deadline
39
- // has already elapsed, so the probe must name one it cannot reach.
40
- store.set_epoch_deadline(trap::NO_TIMEOUT_EPOCH_DELTA);
41
- io::install_wasi(&mut store, &PROBE_CONFIG, &[])
42
- .map_err(|t| SetupError::Dead(t.to_string()))?;
21
+ /// every failure is a `SetupError`. `setup_timeout` bounds instantiating
22
+ /// the artifact and reading its version, so a start function that never
23
+ /// returns fails construction instead of hanging it.
24
+ pub(crate) fn verify(
25
+ pre: &InstancePre<Invocation>,
26
+ setup_timeout: Option<Duration>,
27
+ ) -> Result<(), SetupError> {
28
+ let wasi = Wasi::for_probe().map_err(|t| SetupError::Dead(t.to_string()))?;
29
+ // The memory cap stays off: it arms per invocation, and the probe is
30
+ // none.
31
+ let mut store = WtStore::new(load_engine()?, Invocation::new(None, wasi));
32
+ trap::arm_deadline(&mut store, setup_timeout);
33
+ let past_deadline = |err: &wasmtime::Error| {
34
+ matches!(trap::cap_trap(err), Some(Trap::Timeout(_))).then(|| {
35
+ SetupError::Dead(format!(
36
+ "the Guest Binary did not finish being set up within {:?}",
37
+ setup_timeout.unwrap_or_default()
38
+ ))
39
+ })
40
+ };
43
41
  let instance = pre
44
42
  .instantiate(store.as_context_mut())
45
- .map_err(trap::instantiate_err)?;
46
- let probe = instance
47
- .get_typed_func::<(), u32>(store.as_context_mut(), "__kobako_abi_version")
48
- .map_err(|_| {
43
+ .map_err(|e| past_deadline(&e).unwrap_or_else(|| trap::instantiate_err(e)))?;
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
+ };
49
+ let reported = probe.call(store.as_context_mut(), ()).map_err(|e| {
50
+ past_deadline(&e).unwrap_or_else(|| {
49
51
  SetupError::Dead(format!(
50
- "the Guest Binary does not export __kobako_abi_version; \
51
- rebuild it against ABI version {ABI_VERSION}"
52
+ "failed to read the Guest Binary's ABI version: {e}"
52
53
  ))
53
- })?;
54
- let reported = probe.call(store.as_context_mut(), ()).map_err(|e| {
55
- SetupError::Dead(format!(
56
- "failed to read the Guest Binary's ABI version: {e}"
57
- ))
54
+ })
58
55
  })?;
59
- if reported != ABI_VERSION {
60
- return Err(SetupError::Dead(format!(
61
- "the Guest Binary reports ABI version {reported}, but this host \
62
- implements ABI version {ABI_VERSION}; rebuild the Guest Binary \
63
- against the host's version"
64
- )));
56
+ endpoint::verify_abi_version(Some(reported))
57
+ }
58
+
59
+ #[cfg(test)]
60
+ mod tests {
61
+ use std::path::Path;
62
+ use std::time::Duration;
63
+
64
+ use kobako_runtime::error::SetupError;
65
+ use kobako_runtime::profile::Profile;
66
+
67
+ use crate::{Config, Driver};
68
+
69
+ // @behavior RT-112
70
+ #[test]
71
+ fn a_guest_binary_that_never_finishes_instantiating_fails_construction() {
72
+ const FIXTURE: &str = concat!(
73
+ env!("CARGO_MANIFEST_DIR"),
74
+ "/../../test/fixtures/minimal_spinning_start.wat"
75
+ );
76
+ let config = Config {
77
+ timeout: None,
78
+ setup_timeout: Some(Duration::from_millis(300)),
79
+ memory_limit: None,
80
+ stdout_limit: None,
81
+ stderr_limit: None,
82
+ open_file_limit: None,
83
+ profile: Profile::Hermetic,
84
+ security: Vec::new(),
85
+ };
86
+ let built = Driver::new(Path::new(FIXTURE), config);
87
+ assert!(
88
+ matches!(&built, Err(SetupError::Dead(message)) if message.contains("within 300ms")),
89
+ "a Guest Binary whose start never returns through Driver::new must fail construction \
90
+ once the setup deadline passes, saying so"
91
+ );
65
92
  }
66
- Ok(())
67
93
  }