feat: integrate isolated Plugin Tool executor

This commit is contained in:
2026-10-29 05:01:31 +09:00
13 changed files with 3240 additions and 7 deletions
Generated
+693 -6
View File
@@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
@@ -213,6 +222,12 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "arbitrary"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed"
[[package]]
name = "arc-swap"
version = "1.9.1"
@@ -556,6 +571,9 @@ name = "bumpalo"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
dependencies = [
"allocator-api2",
]
[[package]]
name = "bytecount"
@@ -740,6 +758,15 @@ dependencies = [
"cc",
]
[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror 2.0.18",
]
[[package]]
name = "colorchoice"
version = "1.0.5"
@@ -853,6 +880,144 @@ dependencies = [
"libc",
]
[[package]]
name = "cranelift-assembler-x64"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c088d3406f0c0252efa7445adfd2d05736bfb5218838f64eaf79d567077aed14"
dependencies = [
"cranelift-assembler-x64-meta",
]
[[package]]
name = "cranelift-assembler-x64-meta"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c03f887a763abb9c1dc08f722aa82b69067fda623b6f0273050f45f8b1a6776"
dependencies = [
"cranelift-srcgen",
]
[[package]]
name = "cranelift-bforest"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206887a11a43f507fee320a218dc365980bfc42ec2696792079a9f8c9369e90"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-bitset"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac0790c83cfdab95709c5d0105fd888221e3af9049a7d7ec376ec901ab4e4dba"
dependencies = [
"serde",
"serde_derive",
]
[[package]]
name = "cranelift-codegen"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a98aed2d262eda69310e84bae8e053ee4f17dbdd3347b8d9156aa618ba2de0a"
dependencies = [
"bumpalo",
"cranelift-assembler-x64",
"cranelift-bforest",
"cranelift-bitset",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-control",
"cranelift-entity",
"cranelift-isle",
"gimli",
"hashbrown 0.15.5",
"log",
"pulley-interpreter",
"regalloc2",
"rustc-hash",
"serde",
"smallvec",
"target-lexicon",
"wasmtime-internal-math",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6906852826988563e9b0a9232ad951f53a47aa41ffd02f8ac852d3f41aae836a"
dependencies = [
"cranelift-assembler-x64-meta",
"cranelift-codegen-shared",
"cranelift-srcgen",
"heck",
"pulley-interpreter",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a50105aab667b5cc845f2be37c78475d7cc127cd8ec0a31f7b2b71d526099a7"
[[package]]
name = "cranelift-control"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6adcc7aa7c0bc1727176a6f2d99c28a9e79a541ccd5ca911a0cb352da8befa36"
dependencies = [
"arbitrary",
]
[[package]]
name = "cranelift-entity"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "981b56af777f9a34ea6dcce93255125776d391410c2a68b75bed5941b714fa15"
dependencies = [
"cranelift-bitset",
"serde",
"serde_derive",
]
[[package]]
name = "cranelift-frontend"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea982589684dfb71afecb9fc09555c3a266300a1162a60d7fa39d41a5705b1c"
dependencies = [
"cranelift-codegen",
"log",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-isle"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0422686b22ed6a1f33cc40e3c43eb84b67155788568d1a5cac8439d3dca1783"
[[package]]
name = "cranelift-native"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f697bbbe135c655ea1deb7af0bae4a5c4fae2c88fdfc0fa57b34ae58c91040"
dependencies = [
"cranelift-codegen",
"libc",
"target-lexicon",
]
[[package]]
name = "cranelift-srcgen"
version = "0.125.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718efe674f3df645462677e22a3128e890d88ba55821bb091083d257707be76c"
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -1303,6 +1468,18 @@ dependencies = [
"serde",
]
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]]
name = "encoding_rs"
version = "0.8.35"
@@ -1749,6 +1926,17 @@ dependencies = [
"polyval",
]
[[package]]
name = "gimli"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
dependencies = [
"fallible-iterator",
"indexmap",
"stable_deref_trait",
]
[[package]]
name = "glob"
version = "0.3.3"
@@ -2608,6 +2796,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "mach2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
dependencies = [
"libc",
]
[[package]]
name = "manifest"
version = "0.1.0"
@@ -2631,7 +2828,7 @@ dependencies = [
"tracing",
"wasmparser 0.244.0",
"wit-component",
"wit-parser",
"wit-parser 0.244.0",
]
[[package]]
@@ -2704,6 +2901,15 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "memfd"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57804b2c9b69967f1536a56f86297e367a33b19e98852ed624b84551cdbc0d90"
dependencies = [
"rustix 1.1.4",
]
[[package]]
name = "memmap2"
version = "0.9.10"
@@ -3038,6 +3244,18 @@ dependencies = [
"url",
]
[[package]]
name = "object"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"crc32fast",
"hashbrown 0.15.5",
"indexmap",
"memchr",
]
[[package]]
name = "object_store"
version = "0.13.2"
@@ -3465,6 +3683,18 @@ version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "postcard"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
dependencies = [
"cobs",
"embedded-io 0.4.0",
"embedded-io 0.6.1",
"serde",
]
[[package]]
name = "postscript"
version = "0.14.1"
@@ -3577,6 +3807,29 @@ dependencies = [
"unicase",
]
[[package]]
name = "pulley-interpreter"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beafc309a2d35e16cc390644d88d14dfa45e45e15075ec6a9e37f6dfb43e926f"
dependencies = [
"cranelift-bitset",
"log",
"pulley-macros",
"wasmtime-internal-math",
]
[[package]]
name = "pulley-macros"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885fbb6c07454cfc8725a18a1da3cfc328ee8c53fb8d0671ea313edc8567947"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "quinn"
version = "0.11.9"
@@ -3630,7 +3883,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.59.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -3868,6 +4121,20 @@ dependencies = [
"serde_json",
]
[[package]]
name = "regalloc2"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08effbc1fa53aaebff69521a5c05640523fab037b34a4a2c109506bc938246fa"
dependencies = [
"allocator-api2",
"bumpalo",
"hashbrown 0.15.5",
"log",
"rustc-hash",
"smallvec",
]
[[package]]
name = "regex"
version = "1.12.3"
@@ -4255,6 +4522,10 @@ name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "serde"
@@ -4605,6 +4876,9 @@ name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
dependencies = [
"serde",
]
[[package]]
name = "socket2"
@@ -4864,6 +5138,12 @@ dependencies = [
"xattr",
]
[[package]]
name = "target-lexicon"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
[[package]]
name = "target-triple"
version = "1.0.0"
@@ -5199,6 +5479,7 @@ dependencies = [
"bytes",
"futures-core",
"futures-sink",
"futures-util",
"pin-project-lite",
"tokio",
]
@@ -5822,6 +6103,16 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.239.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c"
dependencies = [
"leb128fmt",
"wasmparser 0.239.0",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
@@ -5867,6 +6158,19 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wasmparser"
version = "0.239.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0"
dependencies = [
"bitflags 2.11.0",
"hashbrown 0.15.5",
"indexmap",
"semver",
"serde",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
@@ -5888,6 +6192,256 @@ checksum = "fe9269dd817e0e7021bf534ccd9761bc2d64778b6708e41228b8439d75eaa0b1"
dependencies = [
"bitflags 2.11.0",
"indexmap",
"semver",
]
[[package]]
name = "wasmprinter"
version = "0.239.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3981f3d51f39f24f5fc90f93049a90f08dbbca8deba602cd46bb8ca67a94718"
dependencies = [
"anyhow",
"termcolor",
"wasmparser 0.239.0",
]
[[package]]
name = "wasmprinter"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09390d7b2bd7b938e563e4bff10aa345ef2e27a3bc99135697514ef54495e68f"
dependencies = [
"anyhow",
"termcolor",
"wasmparser 0.244.0",
]
[[package]]
name = "wasmtime"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f81eafc07c867be94c47e0dc66355d9785e09107a18901f76a20701ba0663ad7"
dependencies = [
"addr2line",
"anyhow",
"async-trait",
"bitflags 2.11.0",
"bumpalo",
"cc",
"cfg-if",
"encoding_rs",
"hashbrown 0.15.5",
"indexmap",
"libc",
"log",
"mach2",
"memfd",
"object",
"once_cell",
"postcard",
"pulley-interpreter",
"rustix 1.1.4",
"semver",
"serde",
"serde_derive",
"smallvec",
"target-lexicon",
"wasmparser 0.239.0",
"wasmtime-environ",
"wasmtime-internal-component-macro",
"wasmtime-internal-component-util",
"wasmtime-internal-cranelift",
"wasmtime-internal-fiber",
"wasmtime-internal-jit-debug",
"wasmtime-internal-jit-icache-coherence",
"wasmtime-internal-math",
"wasmtime-internal-slab",
"wasmtime-internal-unwinder",
"wasmtime-internal-versioned-export-macros",
"wasmtime-internal-winch",
"windows-sys 0.60.2",
]
[[package]]
name = "wasmtime-environ"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78587abe085a44a13c90fa16fea6db014e9883e627a7044d7f0cb397ad08d1da"
dependencies = [
"anyhow",
"cranelift-bitset",
"cranelift-entity",
"gimli",
"indexmap",
"log",
"object",
"postcard",
"semver",
"serde",
"serde_derive",
"smallvec",
"target-lexicon",
"wasm-encoder 0.239.0",
"wasmparser 0.239.0",
"wasmprinter 0.239.0",
"wasmtime-internal-component-util",
]
[[package]]
name = "wasmtime-internal-component-macro"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d843bb444f2d1509ea9304ad749242d1fa5de95cde67665bfcdcafa0f360925c"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn 2.0.117",
"wasmtime-internal-component-util",
"wasmtime-internal-wit-bindgen",
"wit-parser 0.239.0",
]
[[package]]
name = "wasmtime-internal-component-util"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801ee1a80ab66f065a88c6a62f2d495d5540d027b366757c6a53e9c42f153aef"
[[package]]
name = "wasmtime-internal-cranelift"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb50f1c50365c32e557266ca85acdf77696c44a3f98797ba6af58cebc6d6d1e"
dependencies = [
"anyhow",
"cfg-if",
"cranelift-codegen",
"cranelift-control",
"cranelift-entity",
"cranelift-frontend",
"cranelift-native",
"gimli",
"itertools",
"log",
"object",
"pulley-interpreter",
"smallvec",
"target-lexicon",
"thiserror 2.0.18",
"wasmparser 0.239.0",
"wasmtime-environ",
"wasmtime-internal-math",
"wasmtime-internal-unwinder",
"wasmtime-internal-versioned-export-macros",
]
[[package]]
name = "wasmtime-internal-fiber"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9308cdb17f8d51e3164185616d809e28c29a6515c03b9dd95c89436b71f6d154"
dependencies = [
"anyhow",
"cc",
"cfg-if",
"libc",
"rustix 1.1.4",
"wasmtime-internal-versioned-export-macros",
"windows-sys 0.60.2",
]
[[package]]
name = "wasmtime-internal-jit-debug"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c9b63a22bf2a8b6a149a41c6768bc17a8b2e3288a249cb8216987fbd7128e81"
dependencies = [
"cc",
"wasmtime-internal-versioned-export-macros",
]
[[package]]
name = "wasmtime-internal-jit-icache-coherence"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb8e042b6e3de2f3d708279f89f50b4b9aa1b9bab177300cdffb0ffcd2816df5"
dependencies = [
"anyhow",
"cfg-if",
"libc",
"windows-sys 0.60.2",
]
[[package]]
name = "wasmtime-internal-math"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c1f0674f38cd7d014eb1a49ea1d1766cca1a64459e8856ee118a10005302e16"
dependencies = [
"libm",
]
[[package]]
name = "wasmtime-internal-slab"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb24b7535306713e7a250f8b71e35f05b6a5031bf9c3ed7330c308e899cbe7d3"
[[package]]
name = "wasmtime-internal-unwinder"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21d5a80e2623a49cb8e8c419542337b8fe0260b162c40dcc201080a84cbe9b7c"
dependencies = [
"anyhow",
"cfg-if",
"cranelift-codegen",
"log",
"object",
]
[[package]]
name = "wasmtime-internal-versioned-export-macros"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e277f734b9256359b21517c3b0c26a2a9de6c53a51b670ae55cdcde548bf4e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "wasmtime-internal-winch"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4dc9333737142f6ece4369c8bcdda03a11edbd43d8fbd3e15004c194b9b743"
dependencies = [
"anyhow",
"cranelift-codegen",
"gimli",
"log",
"object",
"target-lexicon",
"wasmparser 0.239.0",
"wasmtime-environ",
"wasmtime-internal-cranelift",
"winch-codegen",
]
[[package]]
name = "wasmtime-internal-wit-bindgen"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f758625553fe33fdce0713f63bb7784c4f5fecb7f7cd4813414519ec24b6a4c"
dependencies = [
"anyhow",
"bitflags 2.11.0",
"heck",
"indexmap",
"wit-parser 0.239.0",
]
[[package]]
@@ -6169,6 +6723,26 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winch-codegen"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c0bb17ae9bf89ebc74512150e6ee0a27b1eac5ff3b54d8cec264f4b4255022d"
dependencies = [
"anyhow",
"cranelift-assembler-x64",
"cranelift-codegen",
"gimli",
"regalloc2",
"smallvec",
"target-lexicon",
"thiserror 2.0.18",
"wasmparser 0.239.0",
"wasmtime-environ",
"wasmtime-internal-cranelift",
"wasmtime-internal-math",
]
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -6255,6 +6829,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
@@ -6288,13 +6871,30 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
@@ -6307,6 +6907,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@@ -6319,6 +6925,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@@ -6331,12 +6943,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@@ -6349,6 +6973,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@@ -6361,6 +6991,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@@ -6373,6 +7009,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@@ -6385,6 +7027,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "1.0.1"
@@ -6474,7 +7122,7 @@ checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
"wit-parser 0.244.0",
]
[[package]]
@@ -6525,7 +7173,25 @@ dependencies = [
"wasm-metadata",
"wasmparser 0.244.0",
"wat",
"wit-parser",
"wit-parser 0.244.0",
]
[[package]]
name = "wit-parser"
version = "0.239.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser 0.239.0",
]
[[package]]
@@ -6794,7 +7460,28 @@ dependencies = [
"wasmparser 0.244.0",
"wit-bindgen",
"wit-component",
"wit-parser",
"wit-parser 0.244.0",
]
[[package]]
name = "yoi-plugin-runtime"
version = "0.1.0"
dependencies = [
"async-trait",
"jsonschema",
"manifest",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.18",
"tokio",
"tokio-util",
"wasmparser 0.244.0",
"wasmprinter 0.244.0",
"wasmtime",
"wat",
"wit-component",
"wit-parser 0.244.0",
]
[[package]]
+2
View File
@@ -17,6 +17,7 @@ members = [
"crates/runtime-api",
"crates/server-api",
"crates/plugin-pdk",
"crates/plugin-runtime",
"crates/yoi",
"crates/protocol",
"crates/session-metrics",
@@ -57,6 +58,7 @@ default-members = [
"crates/runtime-api",
"crates/server-api",
"crates/plugin-pdk",
"crates/plugin-runtime",
"crates/yoi",
"crates/protocol",
"crates/session-metrics",
+26
View File
@@ -0,0 +1,26 @@
[package]
name = "yoi-plugin-runtime"
version = "0.1.0"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
[dependencies]
async-trait.workspace = true
manifest.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["rt", "macros", "sync", "time"] }
tokio-util = { version = "0.7", features = ["rt"] }
jsonschema = { version = "0.33.0", default-features = false }
wasmparser = "0.244.0"
wasmtime = { version = "=38.0.4", default-features = false, features = ["async", "component-model", "cranelift", "runtime", "std", "threads"] }
[dev-dependencies]
tempfile.workspace = true
wit-component = { version = "0.244.0", features = ["dummy-module"] }
wit-parser = "0.244.0"
wasmprinter = "0.244.0"
wat = "1"
tokio = { workspace = true, features = ["rt-multi-thread", "test-util"] }
+13
View File
@@ -0,0 +1,13 @@
# yoi-plugin-runtime
Runtime-owned, standalone execution of exact v2 Tool Components. [`Executor`] compiles only artifacts reverified through `manifest::plugin`, then creates a fresh Store and instance for each invocation. It does not resolve catalogs, config, Worker plans, delivery caches or repository paths, and does not register Tools with a Worker.
[`host`] is an explicit asynchronous injection boundary, not an implementation of Server authority or HTTPS. Every invocation (including pure computation) requires live admission and result-publication authorization. Providers must bind their authenticated installation/plan/subject and narrowed grants to [`host::InvocationContext`], authorize every protected operation, and make pending work cancellation-safe. There is no default provider or product allow-all fallback. Dropping a provider future must release temporary resources; an already dispatched external effect is not rolled back. Durable effect receipts, secret resolution, URL/DNS/TLS policy and real network transport belong to the Host provider, not this sandbox.
Component instantiation is not a Tool export: all Host imports, including logging, are rejected until the export context is installed. Service start/stop semantics are outside this crate. Only exact WIT imports are linked, without WASI, filesystem, environment, process, clock or network ambient authority. Call-local connection resources hold opaque provider objects; guest resource representations never identify a Server resource or issue a grant.
A single deadline covers admission, instantiation, guest execution, asynchronous Host waits, post-return and publication. Fuel bounds computation and cooperatively yields; epoch interruption cooperatively yields as well. Store memory/table/instance limits, call-local resource and operation counts, serialized payload and diagnostic budgets complement those bounds. Text/JSON overflows fail rather than returning truncated success; excess diagnostic entries are dropped and counted. Guest diagnostics are untrusted, must not contain secrets, and are separate from model output. Host adapters must not log raw request/response or credentials.
The Tool sandbox accepts only imported Host resources. Wasmtime's Store limiter does not meter guest-defined Component resource tables, so compile rejects private resource definitions and canonical `resource.new` throughout concrete nested Components before any Store exists. This is an additional execution-policy restriction after shared package/ABI verification, not a second package validator. It preserves the WIT connection imports, whose creation is metered by the adapter, without claiming a quota for unmetered internal resource allocators. Unsupported execution policy is distinct from corrupt artifact identity.
Compile results may be shared, but invocation memory, resource tables and imported state are never shared. The caller owns finite aggregate admission/concurrency quotas; per-invocation limits alone do not bound an unbounded number of callers. A validated digest is content identity, never admission authority. This standalone core is not connected to normal Worker launch/restore; product integration must supply the live Server provider.
+713
View File
@@ -0,0 +1,713 @@
use crate::sandbox::SandboxLimits;
use crate::{
bindings,
host::{self, Connection, Host, InvocationContext, Operation, errors, http, logging},
};
use manifest::plugin::{self, PluginPackageLimits, PluginPackageManifest, PluginToolManifest};
use serde::Serialize;
use std::{sync::Arc, time::Duration};
use tokio::time::Instant;
use wasmtime::component::{Component, Linker, Resource, ResourceTable};
use wasmtime::{Config, Engine, Store};
#[derive(Clone, Debug)]
pub struct Limits {
pub payload_bytes: usize,
pub config_bytes: usize,
pub memory_bytes: usize,
pub table_elements: usize,
pub instances: usize,
pub memories: usize,
pub tables: usize,
pub resources: usize,
pub fuel: u64,
pub wall_time: Duration,
pub host_operations: usize,
pub http_body_bytes: usize,
pub http_header_bytes: usize,
pub http_header_pairs: usize,
pub log_bytes: usize,
}
impl Default for Limits {
fn default() -> Self {
Self {
payload_bytes: 64 * 1024,
config_bytes: 64 * 1024,
memory_bytes: 256 * 1024 * 1024,
table_elements: 10_000,
instances: 32,
memories: 4,
tables: 8,
resources: 32,
fuel: 10_000_000,
wall_time: Duration::from_secs(30),
host_operations: 32,
http_body_bytes: 1024 * 1024,
http_header_bytes: 16 * 1024,
http_header_pairs: 64,
log_bytes: 16 * 1024,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ExecutionError {
#[error("invalid artifact: {0}")]
Artifact(String),
#[error("unsupported Component execution policy: {0}")]
UnsupportedComponent(String),
#[error("invalid invocation: {0}")]
InvalidInput(String),
#[error("invalid output: {0}")]
InvalidOutput(String),
#[error("invocation cancelled")]
Cancelled,
#[error("invocation deadline exceeded")]
Deadline,
#[error("Host denied or failed operation: {0:?}")]
Host(errors::HostError),
#[error("guest error {code}: {message}")]
Guest { code: String, message: String },
#[error("Component trap or resource limit: {0}")]
Trap(String),
}
/// Content identity only; both digests are independently verified by the package owner.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ArtifactIdentity {
pub package_digest: String,
pub artifact_digest: String,
pub artifact_size: u64,
}
/// Immutable compiled bytes and declarations, not execution authority.
/// Private fields prevent mutable package metadata from bypassing verification.
pub struct CompiledTool {
component: Component,
manifest: PluginPackageManifest,
identity: ArtifactIdentity,
}
impl CompiledTool {
pub fn identity(&self) -> &ArtifactIdentity {
&self.identity
}
}
#[derive(Debug)]
pub struct InvocationResult {
pub output: bindings::exports::yoi::plugin::tool::Output,
pub dropped_logs: u64,
}
pub struct Executor {
engine: Engine,
limits: Limits,
}
impl Executor {
/// Limits are fixed by the Host, never selected by guest input.
pub fn new(limits: Limits) -> wasmtime::Result<Self> {
let ceiling = Limits::default();
macro_rules! check_ceiling {
($($field:ident),+ $(,)?) => { $(
if limits.$field > ceiling.$field {
return Err(wasmtime::Error::msg(concat!("limit exceeds supported ceiling: ", stringify!($field))));
}
)+ };
}
check_ceiling!(
payload_bytes,
config_bytes,
memory_bytes,
table_elements,
instances,
memories,
tables,
resources,
fuel,
wall_time,
host_operations,
http_body_bytes,
http_header_bytes,
http_header_pairs,
log_bytes
);
let mut config = Config::new();
config
.async_support(true)
.wasm_component_model(true)
.consume_fuel(true)
.epoch_interruption(true)
.wasm_threads(false)
.wasm_shared_everything_threads(false);
Ok(Self {
engine: Engine::new(&config)?,
limits,
})
}
pub fn limits(&self) -> &Limits {
&self.limits
}
/// Reuses the canonical package/digest/ABI validator; no path discovery or
/// duplicate pack/hash implementation is involved. Compilation executes no guest.
pub fn compile(
&self,
artifact: &[u8],
package_digest: &str,
artifact_digest: &str,
artifact_size: u64,
) -> Result<CompiledTool, ExecutionError> {
let verified = plugin::verify_plugin_artifact(
artifact,
package_digest,
artifact_digest,
artifact_size,
&PluginPackageLimits::default(),
)
.map_err(|e| ExecutionError::Artifact(e.message))?;
let manifest = verified.package.manifest;
let component_bytes = &verified.files[&manifest.runtime.component];
crate::sandbox::check_component_resources(component_bytes)
.map_err(|e| ExecutionError::UnsupportedComponent(e.to_string()))?;
let component = Component::new(&self.engine, component_bytes)
.map_err(|e| ExecutionError::Artifact(e.to_string()))?;
Ok(CompiledTool {
component,
manifest,
identity: ArtifactIdentity {
package_digest: package_digest.into(),
artifact_digest: artifact_digest.into(),
artifact_size,
},
})
}
/// All computation and Host waits share the earlier of the caller deadline
/// and local ceiling. Dropping this future also drops its Store and resources.
pub async fn invoke(
&self,
compiled: &CompiledTool,
mut context: InvocationContext,
provider: Arc<dyn Host>,
tool_name: &str,
input_json: &str,
config_json: &str,
) -> Result<InvocationResult, ExecutionError> {
context.deadline = context.deadline.min(Instant::now() + self.limits.wall_time);
context.cancellation = context.cancellation.child_token();
let _lifetime = InvocationLifetime(context.cancellation.clone());
let deadline = context.deadline;
let cancellation = context.cancellation.clone();
let execution = self.run(
compiled,
context,
provider,
tool_name,
input_json,
config_json,
);
tokio::pin!(execution);
let mut tick = tokio::time::interval(Duration::from_millis(10));
loop {
tokio::select! {
biased;
_ = cancellation.cancelled() => return Err(ExecutionError::Cancelled),
_ = tokio::time::sleep_until(deadline) => return Err(ExecutionError::Deadline),
result = &mut execution => {
if cancellation.is_cancelled() { return Err(ExecutionError::Cancelled); }
if Instant::now() >= deadline { return Err(ExecutionError::Deadline); }
return result;
},
_ = tick.tick() => self.engine.increment_epoch(),
}
}
}
async fn run(
&self,
compiled: &CompiledTool,
context: InvocationContext,
provider: Arc<dyn Host>,
tool_name: &str,
input_json: &str,
config_json: &str,
) -> Result<InvocationResult, ExecutionError> {
if !Engine::same(&self.engine, compiled.component.engine()) {
return Err(ExecutionError::Artifact(
"Component belongs to another executor".into(),
));
}
if context.artifact != compiled.identity || context.tool_name != tool_name {
return Err(ExecutionError::InvalidInput(
"artifact/Tool does not match Host-bound context".into(),
));
}
let tool = compiled
.manifest
.tools
.iter()
.find(|t| t.name == tool_name)
.ok_or_else(|| ExecutionError::InvalidInput("undeclared Tool".into()))?;
if input_json.len() > self.limits.payload_bytes
|| config_json.len() > self.limits.config_bytes
|| context.invocation_id.len() > 1024
{
return Err(ExecutionError::InvalidInput(
"input/config/identity exceeds limit".into(),
));
}
validate_json(&tool.input_schema, input_json).map_err(ExecutionError::InvalidInput)?;
serde_json::from_str::<serde_json::Value>(config_json)
.map_err(|e| ExecutionError::InvalidInput(e.to_string()))?;
provider
.authorize(&context, Operation::Admit)
.await
.map_err(ExecutionError::Host)?;
check_current(&context).map_err(ExecutionError::Host)?;
let limits = &self.limits;
let state = State {
context: None,
provider: provider.clone(),
limits: limits.clone(),
manifest: compiled.manifest.clone(),
table: ResourceTable::new(),
operations: 0,
resources: 0,
log_bytes: 0,
dropped_logs: 0,
store_limits: SandboxLimits::new(limits),
};
let mut store = Store::new(&self.engine, state);
store.limiter(|state| &mut state.store_limits);
store.set_fuel(limits.fuel).map_err(trap)?;
store
.fuel_async_yield_interval(Some(10_000))
.map_err(trap)?;
store.set_epoch_deadline(1);
store.epoch_deadline_async_yield_and_update(1);
let mut linker = Linker::new(&self.engine);
bindings::HttpsLoggedToolPlugin::add_to_linker::<_, wasmtime::component::HasSelf<_>>(
&mut linker,
|s| s,
)
.map_err(trap)?;
let instance = bindings::HttpsLoggedToolPlugin::instantiate_async(
&mut store,
&compiled.component,
&linker,
)
.await
.map_err(trap)?;
// No import during instantiate/start may act, including logging. Service
// on-start is a different world and has no adapter here.
check_current(&context).map_err(ExecutionError::Host)?;
store.data_mut().context = Some(context.clone());
let invocation = bindings::exports::yoi::plugin::tool::Invocation {
invocation_id: context.invocation_id.clone(),
tool_name: tool_name.into(),
input_json: input_json.into(),
config_json: config_json.into(),
remaining_ms: context
.deadline
.saturating_duration_since(Instant::now())
.as_millis()
.min(u32::MAX as u128) as u32,
};
let result = instance
.yoi_plugin_tool()
.call_invoke(&mut store, &invocation)
.await
.map_err(trap)?;
check_current(&context).map_err(ExecutionError::Host)?;
provider
.authorize(&context, Operation::Publish)
.await
.map_err(ExecutionError::Host)?;
check_current(&context).map_err(ExecutionError::Host)?;
let output = match result {
Ok(output) => {
validate_output(tool, &output, limits.payload_bytes)?;
output
}
Err(error) => {
if error.message.len().saturating_add(error.code.len()) > limits.payload_bytes {
return Err(ExecutionError::InvalidOutput(
"guest error exceeds limit".into(),
));
}
let code = if tool.error_codes.contains(&error.code) {
error.code
} else {
"plugin-failed".into()
};
#[derive(Serialize)]
struct Failure<'a> {
code: &'a str,
message: &'a str,
}
if serde_json::to_vec(&Failure {
code: &code,
message: &error.message,
})
.unwrap()
.len()
> limits.payload_bytes
{
return Err(ExecutionError::InvalidOutput(
"serialized guest error exceeds limit".into(),
));
}
return Err(ExecutionError::Guest {
code,
message: error.message,
});
}
};
Ok(InvocationResult {
output,
dropped_logs: store.data().dropped_logs,
})
}
}
fn trap(e: wasmtime::Error) -> ExecutionError {
ExecutionError::Trap(format!("{e:#}"))
}
fn validate_json(schema: &serde_json::Value, json: &str) -> Result<(), String> {
let value: serde_json::Value = serde_json::from_str(json).map_err(|e| e.to_string())?;
// Schema admission already forbids external refs/dialects in manifest::plugin.
let validator = jsonschema::options()
.with_draft(jsonschema::Draft::Draft202012)
.should_validate_formats(true)
.build(schema)
.map_err(|e| e.to_string())?;
validator.validate(&value).map_err(|e| e.to_string())
}
fn validate_output(
tool: &PluginToolManifest,
output: &bindings::exports::yoi::plugin::tool::Output,
max: usize,
) -> Result<(), ExecutionError> {
let raw_bytes = output
.summary
.len()
.saturating_add(output.content.as_ref().map_or(0, String::len))
.saturating_add(output.structured_json.as_ref().map_or(0, String::len));
if raw_bytes > max {
return Err(ExecutionError::InvalidOutput("output exceeds limit".into()));
}
// Use the complete serialized envelope budget, not separate per-field limits.
#[derive(Serialize)]
struct Envelope<'a> {
summary: &'a str,
content: &'a Option<String>,
structured_json: &'a Option<String>,
}
let bytes = serde_json::to_vec(&Envelope {
summary: &output.summary,
content: &output.content,
structured_json: &output.structured_json,
})
.unwrap();
if bytes.len() > max {
return Err(ExecutionError::InvalidOutput("output exceeds limit".into()));
}
let json = output
.structured_json
.as_deref()
.ok_or_else(|| ExecutionError::InvalidOutput("missing structured output".into()))?;
validate_json(&tool.output_schema, json).map_err(ExecutionError::InvalidOutput)
}
struct InvocationLifetime(host::CancellationToken);
impl Drop for InvocationLifetime {
fn drop(&mut self) {
self.0.cancel();
}
}
struct State {
context: Option<InvocationContext>,
provider: Arc<dyn Host>,
limits: Limits,
manifest: PluginPackageManifest,
table: ResourceTable,
operations: usize,
resources: usize,
log_bytes: usize,
dropped_logs: u64,
store_limits: SandboxLimits,
}
fn check_current(context: &InvocationContext) -> Result<(), errors::HostError> {
if context.cancellation.is_cancelled() {
return Err(host::error(errors::Code::Cancelled, "invocation cancelled"));
}
if Instant::now() >= context.deadline {
return Err(host::error(
errors::Code::DeadlineExceeded,
"invocation deadline exceeded",
));
}
Ok(())
}
impl State {
fn current(&self) -> wasmtime::Result<InvocationContext> {
let context = self
.context
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Host import outside Tool export context"))?;
check_current(context).map_err(|_| wasmtime::Error::msg("invocation no longer active"))?;
Ok(context.clone())
}
fn operation(&mut self) -> Result<(), errors::HostError> {
if self.operations >= self.limits.host_operations {
return Err(host::error(
errors::Code::QuotaExceeded,
"Host operation limit",
));
}
self.operations += 1;
Ok(())
}
}
impl bindings::yoi::host::errors::Host for State {}
impl bindings::yoi::host::connections::HostConnection for State {
async fn drop(&mut self, resource: Resource<Connection>) -> wasmtime::Result<()> {
self.table.delete(resource)?;
self.resources -= 1;
Ok(())
}
}
impl bindings::yoi::host::connections::Host for State {
async fn open(
&mut self,
slot: String,
) -> wasmtime::Result<Result<Resource<Connection>, errors::HostError>> {
let context = self.current()?;
if let Err(e) = self.operation() {
return Ok(Err(e));
}
let declared = self.manifest.connections.iter().any(|s| s.name == slot);
if !declared || !context.grants.connection_slots.contains(&slot) {
return Ok(Err(host::error(
errors::Code::Denied,
"connection slot not granted",
)));
}
if self.resources >= self.limits.resources {
return Ok(Err(host::error(
errors::Code::QuotaExceeded,
"resource limit",
)));
}
if let Err(e) = self
.provider
.authorize(&context, Operation::Open { slot: &slot })
.await
{
return Ok(Err(e));
}
check_current(&context).map_err(|_| wasmtime::Error::msg("invocation no longer active"))?;
let binding = match self.provider.open(&context, &slot).await {
Ok(v) => v,
Err(e) => return Ok(Err(e)),
};
check_current(&context).map_err(|_| wasmtime::Error::msg("late connection result"))?;
let resource = self.table.push(Connection { slot, binding })?;
self.resources += 1;
Ok(Ok(resource))
}
}
impl bindings::yoi::host::http::Host for State {
async fn send(
&mut self,
target: Resource<Connection>,
mut input: http::Request,
) -> wasmtime::Result<Result<http::Response, errors::HostError>> {
let context = self.current()?;
if let Err(e) = self.operation() {
return Ok(Err(e));
}
let connection = self.table.get(&target)?;
let slot = connection.slot.clone();
let binding = connection.binding.clone();
if !context.grants.connection_slots.contains(&slot) {
return Ok(Err(host::error(
errors::Code::Denied,
"connection slot not granted",
)));
}
let declaration = self
.manifest
.connections
.iter()
.find(|s| s.name == slot)
.unwrap();
let method = match input.method {
http::Method::Get => plugin::PluginHttpMethod::Get,
http::Method::Head => plugin::PluginHttpMethod::Head,
http::Method::Post => plugin::PluginHttpMethod::Post,
http::Method::Put => plugin::PluginHttpMethod::Put,
http::Method::Patch => plugin::PluginHttpMethod::Patch,
http::Method::Delete => plugin::PluginHttpMethod::Delete,
http::Method::Options => plugin::PluginHttpMethod::Options,
};
// Canonical URL/DNS/TLS/secret and narrower binding checks belong to the
// provider. These static ceilings are not a network protection claim.
if !declaration.methods.contains(&method)
|| !declaration.path_prefixes.iter().any(|p| {
input.path == *p
|| input
.path
.strip_prefix(p)
.is_some_and(|rest| p.ends_with('/') || rest.starts_with('/'))
})
{
return Ok(Err(host::error(
errors::Code::Denied,
"request exceeds package ceiling",
)));
}
if !request_bounded(&input, &self.limits) {
return Ok(Err(host::error(
errors::Code::QuotaExceeded,
"request exceeds limit",
)));
}
input.timeout_ms = input.timeout_ms.min(15_000).min(
context
.deadline
.saturating_duration_since(Instant::now())
.as_millis()
.min(u32::MAX as u128) as u32,
);
let send_deadline = context
.deadline
.min(Instant::now() + Duration::from_millis(input.timeout_ms as u64));
match tokio::time::timeout_at(
send_deadline,
self.provider.authorize(
&context,
Operation::Send {
slot: &slot,
binding: binding.as_ref(),
request: &input,
},
),
)
.await
{
Ok(Ok(())) => (),
Ok(Err(error)) => return Ok(Err(error)),
Err(_) => {
return Ok(Err(host::error(
errors::Code::DeadlineExceeded,
"HTTP authorization deadline exceeded",
)));
}
}
check_current(&context).map_err(|_| wasmtime::Error::msg("invocation no longer active"))?;
if Instant::now() >= send_deadline {
return Ok(Err(host::error(
errors::Code::DeadlineExceeded,
"HTTP deadline exceeded before dispatch",
)));
}
let response = match tokio::time::timeout_at(
send_deadline,
self.provider.send(&context, binding.as_ref(), input),
)
.await
{
Ok(response) => response,
Err(_) => Err(errors::HostError {
code: errors::Code::DeadlineExceeded,
message: "HTTP wait deadline exceeded".into(),
diagnostic_id: String::new(),
effect: errors::EffectObservation::Indeterminate,
}),
};
check_current(&context).map_err(|_| wasmtime::Error::msg("late HTTP result"))?;
Ok(response.and_then(|response| {
if response.body.len() > self.limits.http_body_bytes
|| !pairs_bounded(&response.headers, &self.limits)
{
Err(errors::HostError {
code: errors::Code::QuotaExceeded,
message: "response exceeds limit".into(),
diagnostic_id: String::new(),
effect: errors::EffectObservation::ResponseObserved,
})
} else {
Ok(response)
}
}))
}
}
fn pairs_bounded(pairs: &[http::Pair], limits: &Limits) -> bool {
pairs.len() <= limits.http_header_pairs
&& pairs
.iter()
.try_fold(0usize, |n, p| {
n.checked_add(p.name.len())?.checked_add(p.value.len())
})
.is_some_and(|n| n <= limits.http_header_bytes)
}
fn request_bounded(request: &http::Request, limits: &Limits) -> bool {
request.body.len() <= limits.http_body_bytes
&& request.path.len() <= limits.payload_bytes
&& request.effect_key.len() <= 128
&& !request.effect_key.is_empty()
&& pairs_bounded(&request.headers, limits)
&& pairs_bounded(&request.query, limits)
}
impl bindings::yoi::host::logging::Host for State {
async fn log(
&mut self,
input: logging::Entry,
) -> wasmtime::Result<Result<(), errors::HostError>> {
let context = self.current()?;
if !context.grants.logging
|| !self
.manifest
.requested_grants
.contains(&plugin::PluginRequestedGrant::Logging)
{
return Ok(Err(host::error(
errors::Code::Denied,
"logging not granted",
)));
}
let size = input
.event_code
.len()
.saturating_add(input.message.len())
.saturating_add(
input
.fields
.iter()
.map(|f| {
f.name.len().saturating_add(match &f.value {
logging::Scalar::Text(s) => s.len(),
_ => 8,
})
})
.sum::<usize>(),
);
if size > self.limits.log_bytes.saturating_sub(self.log_bytes) {
self.dropped_logs = self.dropped_logs.saturating_add(1);
return Ok(Ok(()));
}
if let Err(e) = self.operation() {
return Ok(Err(e));
}
if let Err(e) = self.provider.authorize(&context, Operation::Log).await {
return Ok(Err(e));
}
check_current(&context).map_err(|_| wasmtime::Error::msg("invocation no longer active"))?;
self.log_bytes += size;
let result = self.provider.log(&context, input).await;
check_current(&context).map_err(|_| wasmtime::Error::msg("late log result"))?;
Ok(result)
}
}
+96
View File
@@ -0,0 +1,96 @@
//! Host-bound identity, grants and asynchronous provider contracts.
//!
//! The provider is the authority adapter: opaque bindings are not credentials
//! supplied by guests. Implementations must live-check authorization, remain
//! cancellation-safe, bound their own allocations and never auto-retry effects.
pub use crate::bindings::yoi::host::{errors, http, logging};
use async_trait::async_trait;
use std::{any::Any, collections::BTreeSet, sync::Arc};
use tokio::time::Instant;
pub use tokio_util::sync::CancellationToken;
/// Constructed by the authenticated Runtime integration, never from guest JSON.
/// All fields are a snapshot upper bound, not a cached live authorization.
#[derive(Clone, Debug)]
pub struct InvocationContext {
pub workspace: String,
pub installation: String,
pub plan: String,
pub subject: String,
pub invocation_id: String,
pub artifact: crate::ArtifactIdentity,
pub tool_name: String,
pub grants: Grants,
pub deadline: Instant,
pub cancellation: CancellationToken,
}
#[derive(Clone, Debug, Default)]
pub struct Grants {
pub logging: bool,
pub connection_slots: BTreeSet<String>,
}
/// Opaque Host-owned capability, stored only within one invocation's Store.
/// The provider must not interpret a guest resource representation as identity.
pub struct Connection {
pub(crate) slot: String,
pub(crate) binding: Arc<dyn Any + Send + Sync>,
}
/// Each operation requires a fresh owner check; admission covers pure Tools too.
pub enum Operation<'a> {
Admit,
Publish,
Open {
slot: &'a str,
},
Send {
slot: &'a str,
binding: &'a (dyn Any + Send + Sync),
request: &'a http::Request,
},
Log,
}
/// No default implementation is supplied. Test providers belong to test targets.
///
/// `authorize` must check the current installation/artifact/plan/subject and
/// operation against live owner policy. `open`/`send` must also enforce concrete
/// binding policy, secret handling and effect receipts at dispatch time. The
/// executor only enforces static package and invocation ceilings. All waits are
/// dropped on cancellation/deadline; providers must not leave detached work.
#[async_trait]
pub trait Host: Send + Sync {
async fn authorize(
&self,
context: &InvocationContext,
operation: Operation<'_>,
) -> Result<(), errors::HostError>;
async fn open(
&self,
context: &InvocationContext,
slot: &str,
) -> Result<Arc<dyn Any + Send + Sync>, errors::HostError>;
async fn send(
&self,
context: &InvocationContext,
binding: &(dyn Any + Send + Sync),
request: http::Request,
) -> Result<http::Response, errors::HostError>;
async fn log(
&self,
context: &InvocationContext,
entry: logging::Entry,
) -> Result<(), errors::HostError>;
}
pub(crate) fn error(code: errors::Code, message: &str) -> errors::HostError {
errors::HostError {
code,
message: message.into(),
diagnostic_id: String::new(),
effect: errors::EffectObservation::NotDispatched,
}
}
+16
View File
@@ -0,0 +1,16 @@
#![doc = include_str!("../README.md")]
pub mod bindings {
wasmtime::component::bindgen!({
path: "../../resources/plugin/wit",
world: "https-logged-tool-plugin",
imports: { default: async | trappable },
exports: { default: async },
with: { "yoi:host/connections/connection": crate::host::Connection },
});
}
mod executor;
pub mod host;
mod sandbox;
pub use executor::*;
+132
View File
@@ -0,0 +1,132 @@
//! Aggregate Store budgets: Wasmtime's built-in memory/table sizes are per
//! object, so count accepted growth across all objects as well. Nothing in a
//! Tool Store outlives that invocation; no cross-Store accounting is retained.
use crate::Limits;
use wasmtime::{ResourceLimiter, StoreLimits, StoreLimitsBuilder};
/// Additional execution-policy check, not a replacement ABI/package validator.
/// Wasmtime's Store limiter does not meter Component-internal resource tables.
/// The Tool ABI needs only imported Host resources, whose creation is metered
/// by our adapter; guest-defined resources and allocators therefore fail closed.
/// `parse_all` visits concrete nested Components as well as the outer one.
pub(crate) fn check_component_resources(bytes: &[u8]) -> wasmtime::Result<()> {
use wasmparser::{CanonicalFunction, ComponentType, Parser, Payload};
for payload in Parser::new(0).parse_all(bytes) {
match payload? {
Payload::ComponentTypeSection(types) => {
for ty in types {
if matches!(ty?, ComponentType::Resource { .. }) {
return Err(wasmtime::Error::msg(
"guest-defined Component resources are unsupported by the Tool sandbox",
));
}
}
}
Payload::ComponentCanonicalSection(functions) => {
for function in functions {
if matches!(function?, CanonicalFunction::ResourceNew { .. }) {
return Err(wasmtime::Error::msg(
"guest Component resource.new is unsupported by the Tool sandbox",
));
}
}
}
_ => (),
}
}
Ok(())
}
pub(crate) struct SandboxLimits {
inner: StoreLimits,
memory: Budget,
table: Budget,
}
struct Budget {
used: usize,
last_growth: usize,
ceiling: usize,
}
impl Budget {
fn new(ceiling: usize) -> Self {
Self {
used: 0,
last_growth: 0,
ceiling,
}
}
fn reserve(&mut self, current: usize, desired: usize, kind: &str) -> wasmtime::Result<()> {
let growth = desired.saturating_sub(current);
if growth > self.ceiling.saturating_sub(self.used) {
return Err(wasmtime::Error::msg(format!(
"aggregate {kind} budget exceeded"
)));
}
self.used += growth;
self.last_growth = growth;
Ok(())
}
fn failed(&mut self) {
self.used -= self.last_growth;
self.last_growth = 0;
}
}
impl SandboxLimits {
pub(crate) fn new(limits: &Limits) -> Self {
Self {
inner: StoreLimitsBuilder::new()
.memory_size(limits.memory_bytes)
.table_elements(limits.table_elements)
.instances(limits.instances)
.memories(limits.memories)
.tables(limits.tables)
.trap_on_grow_failure(true)
.build(),
memory: Budget::new(limits.memory_bytes),
table: Budget::new(limits.table_elements),
}
}
}
impl ResourceLimiter for SandboxLimits {
fn memory_growing(
&mut self,
current: usize,
desired: usize,
maximum: Option<usize>,
) -> wasmtime::Result<bool> {
if !self.inner.memory_growing(current, desired, maximum)? {
return Ok(false);
}
self.memory.reserve(current, desired, "memory")?;
Ok(true)
}
fn table_growing(
&mut self,
current: usize,
desired: usize,
maximum: Option<usize>,
) -> wasmtime::Result<bool> {
if !self.inner.table_growing(current, desired, maximum)? {
return Ok(false);
}
self.table.reserve(current, desired, "table")?;
Ok(true)
}
fn memory_grow_failed(&mut self, error: wasmtime::Error) -> wasmtime::Result<()> {
self.memory.failed();
self.inner.memory_grow_failed(error)
}
fn table_grow_failed(&mut self, error: wasmtime::Error) -> wasmtime::Result<()> {
self.table.failed();
self.inner.table_grow_failed(error)
}
fn instances(&self) -> usize {
self.inner.instances()
}
fn memories(&self) -> usize {
self.inner.memories()
}
fn tables(&self) -> usize {
self.inner.tables()
}
}
File diff suppressed because it is too large Load Diff
+367
View File
@@ -0,0 +1,367 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
[[package]]
name = "bitflags"
version = "2.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
[[package]]
name = "equivalent"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
[[package]]
name = "executor-test-guest"
version = "0.1.0"
dependencies = [
"yoi-plugin-pdk",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "indexmap"
version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
"serde",
"serde_core",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "log"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn 2.0.119",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.9",
]
[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.9",
]
[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn 2.0.119",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn 2.0.119",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "yoi-plugin-pdk"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
"thiserror",
"wit-bindgen",
]
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
@@ -0,0 +1,13 @@
[package]
name = "executor-test-guest"
version = "0.1.0"
edition = "2024"
publish = false
[workspace]
[lib]
crate-type = ["cdylib"]
[dependencies]
yoi-plugin-pdk = { path = "../../../plugin-pdk" }
@@ -0,0 +1,138 @@
use yoi_plugin_pdk::wit_bindgen;
wit_bindgen::generate!({
path: "../../../../resources/plugin/wit",
world: "https-logged-tool-plugin",
generate_all,
runtime_path: "yoi_plugin_pdk::wit_bindgen::rt",
});
use exports::yoi::plugin::tool::{Guest, Invocation, Output, ToolError};
use std::sync::atomic::{AtomicU32, Ordering};
use yoi::host::{connections, http, logging};
static CALLS: AtomicU32 = AtomicU32::new(0);
struct Plugin;
impl Guest for Plugin {
fn invoke(input: Invocation) -> Result<Output, ToolError> {
let count = CALLS.fetch_add(1, Ordering::SeqCst) + 1;
let mode = input.input_json.trim_matches('"');
match mode {
"trap" => panic!("guest trap"),
"loop" => loop {
std::hint::black_box(CALLS.load(Ordering::SeqCst));
},
"wall-loop" => {
logging::log(&logging::Entry {
level: logging::Level::Info,
event_code: "count".into(),
message: "loop entered".into(),
fields: vec![],
})
.map_err(failure)?;
loop {
std::hint::black_box(CALLS.load(Ordering::SeqCst));
}
}
"memory" => {
core::arch::wasm32::memory_grow::<0>(8192);
}
"error-output" => {
return Err(ToolError {
code: "typed".into(),
message: "\"".repeat(40_000),
});
}
"typed" | "unknown" => {
return Err(ToolError {
code: mode.into(),
message: "bounded business failure".into(),
});
}
"logging" => {
for _ in 0..3 {
logging::log(&logging::Entry {
level: logging::Level::Info,
event_code: "count".into(),
message: "safe diagnostic".into(),
fields: vec![logging::Field {
name: "calls".into(),
value: logging::Scalar::Unsigned(count as u64),
}],
})
.map_err(failure)?;
}
}
"http" | "stale" | "foreign" | "resources" => {
if mode == "resources" {
let _resources: Vec<_> =
(0..64).map(|_| connections::open("api").unwrap()).collect();
}
let target = if mode == "foreign" {
// An attacker can fabricate a representation, not a capability.
unsafe { connections::Connection::from_handle(0) }
} else {
connections::open("api").map_err(failure)?
};
let target = if mode == "stale" {
let handle = target.handle();
drop(target);
unsafe { connections::Connection::from_handle(handle) }
} else {
target
};
let response = http::send(
&target,
&http::Request {
method: http::Method::Get,
path: "/status".into(),
query: vec![],
headers: vec![],
body: vec![],
timeout_ms: 15_000,
effect_key: "one".into(),
},
)
.map_err(failure)?;
return Ok(Output {
summary: String::from_utf8(response.body).unwrap(),
content: None,
structured_json: Some(format!("{{\"calls\":{count}}}")),
});
}
_ => (),
}
Ok(Output {
summary: if mode == "output" {
"x".repeat(70_000)
} else {
input.invocation_id
},
content: None,
structured_json: Some(if mode == "bad-json" {
"not-json".into()
} else if mode == "bad-schema" {
"{}".into()
} else {
format!("{{\"calls\":{count}}}")
}),
})
}
}
fn failure(error: yoi::host::errors::HostError) -> ToolError {
ToolError {
code: "host".into(),
message: format!(
"{}:{}",
match error.code {
yoi::host::errors::Code::DeadlineExceeded => "deadline-exceeded",
yoi::host::errors::Code::QuotaExceeded => "quota-exceeded",
_ => "host-rejected",
},
match error.effect {
yoi::host::errors::EffectObservation::NotDispatched => "not-dispatched",
yoi::host::errors::EffectObservation::ResponseObserved => "response-observed",
yoi::host::errors::EffectObservation::Indeterminate => "indeterminate",
}
),
}
}
export!(Plugin);
+1 -1
View File
@@ -44,7 +44,7 @@ rustPlatform.buildRustPackage rec {
};
# All four WIP SDK crates resolve from crates.io 0.2.0 via Cargo.lock checksums.
cargoHash = "sha256-cLDfqFu6H9YjaL5EpTs5NE2GzkeS0qYcj5Ys/6U5u40=";
cargoHash = "sha256-ikCD2DBORUchmnPk6bLZjgPVPbnNh4rVsSz43S164Eo=";
depsExtraArgs = {
# Older fetchCargoVendor utilities used crates.io's API download endpoint,