From 5b5c7f323d706f626808c29df946b181105bb3d6 Mon Sep 17 00:00:00 2001 From: ruv Date: Mon, 27 Jul 2026 14:30:51 -0400 Subject: [PATCH] fix(homecore): wire plugin lifecycle and preserve MSRV --- v2/Cargo.lock | 268 ++++++++++++-------- v2/crates/homecore-plugins/Cargo.toml | 3 +- v2/crates/homecore-plugins/src/discovery.rs | 18 +- v2/crates/homecore-plugins/src/tests.rs | 11 +- v2/crates/homecore-server/src/main.rs | 33 +++ v2/crates/homecore-server/src/plugins.rs | 49 ++-- 6 files changed, 248 insertions(+), 134 deletions(-) diff --git a/v2/Cargo.lock b/v2/Cargo.lock index 417f9a75..76f3e3c8 100644 --- a/v2/Cargo.lock +++ b/v2/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "addr2line" -version = "0.26.1" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] @@ -1266,47 +1266,46 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b242b4c3675139f52f0b55624fb92571551a344305c5998f55ad20fa527bc55" +checksum = "d6abf69c884fde2d9d4cc232a585fb18f16af3ae04c634315c84ebe158ded92d" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "499715f19799219f32641b14f2a162f91e50bc1b61c2d2184c2be971716f5c56" +checksum = "263d31fcdf83a10267e8c38b53bc8f7688dfbc331267fd8fdf5b22e0dc47a55b" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebca2ea7c62c56feb88a5b23ec380460fe6d7c18134520f6ddf4bfa35cbea68" +checksum = "d459d5377c01c4472b71029caa2df41afaf47711676aa9b12d7414f15104637b" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe11f154b62d7421d909503a746e89995393b1b71926e6f12b08a2076396d7fb" +checksum = "8283088d5823ba7856ab8d531b7c3654b24984748f9fd99dcf3210701fd1d065" dependencies = [ "serde", "serde_derive", - "wasmtime-internal-core", ] [[package]] name = "cranelift-codegen" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2d0da3d51979dc0183fac3076a535477eab794716b063143ecb16632408664" +checksum = "7d3138316d8dd341d725d6ab1598750545c76ad32892837fde558edd68a01b43" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -1319,7 +1318,6 @@ dependencies = [ "cranelift-isle", "gimli", "hashbrown 0.15.5", - "libm", "log", "pulley-interpreter", "regalloc2", @@ -1327,14 +1325,14 @@ dependencies = [ "serde", "smallvec", "target-lexicon 0.13.5", - "wasmtime-internal-core", + "wasmtime-internal-math", ] [[package]] name = "cranelift-codegen-meta" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483b2c94a1b7f6fba0714387ba34ca56d114b2214a80be018acbb2ed40e09a1e" +checksum = "505cead19304a8dc8689e31b29038775c3f73f9d5ea7a5e33864437a1f46c6b6" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -1345,36 +1343,35 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4aae718c336a52d90d4ebe9a2d8c3cf0906a4bee78f0e6867e777eebbe554fe" +checksum = "ce62ba94f570644ce7de6ed05bd39ca28936665dec10a2a1f6f2c531d6add45c" [[package]] name = "cranelift-control" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18e94519070dc56cddb71906a08cea6a28a1d7c58ed501b88f273fa6b45fa07" +checksum = "db6cfe339689c3926412a7060ab00ef3b2b43d936b537e7a3f696121be9d0eaa" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ab4e0eff1045ff2f5ddd8195bf3c97d7b5ef9b780cb044e0cce76e4d352057" +checksum = "625518090e912bdfe3c41464bf97ae421f6044d4ca0f5c3267dcacdb352b033d" dependencies = [ "cranelift-bitset", "serde", "serde_derive", - "wasmtime-internal-core", ] [[package]] name = "cranelift-frontend" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7645a236e1ec49e660f09ec9fa979a1c5d0b612c419db7610573d4d58a03b7c" +checksum = "17f652d40ddf3afb55be64d8979d312b52b384a8cebbcde1dd1c2e32ebcd4466" dependencies = [ "cranelift-codegen", "log", @@ -1384,15 +1381,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e0b4a1a0ea01cc19084ff01aaeb640dfe22905d47d83037a419b81ba587ed0" +checksum = "9f512767e83015f4baf6e732cabca93cea82907e3ab237f826ef64f7ece75eb6" [[package]] name = "cranelift-native" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bdec40b396eb630ecfb0e7a81766d7287f464a7631b9eb5862f7711f1020012" +checksum = "cb1ca6e4dca568ff988d367e4707be2362cee9782265b0a501eaf467ffd550a8" dependencies = [ "cranelift-codegen", "libc", @@ -1401,9 +1398,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.129.2" +version = "0.127.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a001a9dc4557d9e2be324bc932621c0aa9bf33b74dfefa2338f0bf8913329" +checksum = "97400ad8fbd3a434092fc0b486fa7784150b53187941d818b1087f3ac0a547f0" [[package]] name = "crc" @@ -2185,6 +2182,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "fastbloom" version = "0.14.1" @@ -3102,12 +3105,11 @@ dependencies = [ [[package]] name = "gimli" -version = "0.33.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" dependencies = [ - "fnv", - "hashbrown 0.16.1", + "fallible-iterator", "indexmap 2.13.0", "stable_deref_trait", ] @@ -6402,21 +6404,21 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e59a11b64c166a6e1e990303f46a255a52fb4e84d175dbd5e5ca0428e8c02ce" +checksum = "5de307c194cf6310d486dd5595ffc329c53b4acafd54e214752c1eb2e68be3a9" dependencies = [ "cranelift-bitset", "log", "pulley-macros", - "wasmtime-internal-core", + "wasmtime-internal-math", ] [[package]] name = "pulley-macros" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823a9d8da391be21a5f4d5e11c39d15f45b011076c6825fc2323f7e4753f09ce" +checksum = "99dca2747e910d10bafe911e172a1b35860268421c3ee5ddb7e16c35e0288b4a" dependencies = [ "proc-macro2", "quote", @@ -10475,9 +10477,9 @@ checksum = "cfe29135b180b72b04c74aa97b2b4a2ef275161eff9a6c7955ea9eaedc7e1d4e" [[package]] name = "wasm-compose" -version = "0.244.0" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92cda9c76ca8dcac01a8b497860c2cb15cd6f216dc07060517df5abbe82512ac" +checksum = "af801b6f36459023eaec63fdbaedad2fd5a4ab7dc74ecc110a8b5d375c5775e4" dependencies = [ "anyhow", "heck 0.5.0", @@ -10489,11 +10491,21 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder 0.244.0", - "wasmparser 0.244.0", + "wasm-encoder 0.243.0", + "wasmparser 0.243.0", "wat", ] +[[package]] +name = "wasm-encoder" +version = "0.243.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55db9c896d70bd9fa535ce83cd4e1f2ec3726b0edd2142079f594fc3be1cb35" +dependencies = [ + "leb128fmt", + "wasmparser 0.243.0", +] + [[package]] name = "wasm-encoder" version = "0.244.0" @@ -10571,6 +10583,19 @@ dependencies = [ "shlex 0.1.1", ] +[[package]] +name = "wasmparser" +version = "0.243.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d8db401b0528ec316dfbe579e6ab4152d61739cfe076706d2009127970159d" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", + "serde", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -10581,7 +10606,6 @@ dependencies = [ "hashbrown 0.15.5", "indexmap 2.13.0", "semver", - "serde", ] [[package]] @@ -10597,22 +10621,23 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.244.0" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09390d7b2bd7b938e563e4bff10aa345ef2e27a3bc99135697514ef54495e68f" +checksum = "eb2b6035559e146114c29a909a3232928ee488d6507a1504d8934e8607b36d7b" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.244.0", + "wasmparser 0.243.0", ] [[package]] name = "wasmtime" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66806cf6094768e227f74d209eb017cc967276c94fea478e62a0dffede2b3d0d" +checksum = "0702b64d4c3fe43ae4ce229e06af06a27783e48c519e68586d180717cdd24314" dependencies = [ "addr2line", + "anyhow", "async-trait", "bitflags 2.11.0", "bumpalo", @@ -10622,6 +10647,8 @@ dependencies = [ "futures", "fxprof-processed-profile", "gimli", + "hashbrown 0.15.5", + "indexmap 2.13.0", "ittapi", "libc", "log", @@ -10641,17 +10668,18 @@ dependencies = [ "target-lexicon 0.13.5", "tempfile", "wasm-compose", - "wasm-encoder 0.244.0", - "wasmparser 0.244.0", + "wasm-encoder 0.243.0", + "wasmparser 0.243.0", "wasmtime-environ", "wasmtime-internal-cache", "wasmtime-internal-component-macro", "wasmtime-internal-component-util", - "wasmtime-internal-core", "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", @@ -10661,16 +10689,15 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90d3611be7991cba09f14dbb99fe7a0fbaca9eb995ab5c548456eeda44afe20e" +checksum = "3ffeb777a21965a85e4b1ce7b308c63ba130df91912096b49b95523bf3bdd2c7" dependencies = [ "anyhow", "cpp_demangle", "cranelift-bitset", "cranelift-entity", "gimli", - "hashbrown 0.15.5", "indexmap 2.13.0", "log", "object", @@ -10681,19 +10708,19 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon 0.13.5", - "wasm-encoder 0.244.0", - "wasmparser 0.244.0", + "wasm-encoder 0.243.0", + "wasmparser 0.243.0", "wasmprinter", "wasmtime-internal-component-util", - "wasmtime-internal-core", ] [[package]] name = "wasmtime-internal-cache" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2407af12566ff8d537b1a978eccaa087cc4c6d1f13fa57d21114a8def8bfe8a3" +checksum = "bf419cf9b748d443be7aead0fc85c36dcdfdb4bbbd8203b3cf47179d6de4b0dd" dependencies = [ + "anyhow", "base64 0.22.1", "directories-next", "log", @@ -10703,16 +10730,15 @@ dependencies = [ "serde_derive", "sha2", "toml 0.9.12+spec-1.1.0", - "wasmtime-environ", "windows-sys 0.61.2", "zstd 0.13.3", ] [[package]] name = "wasmtime-internal-component-macro" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3616cebe594e6c4b573ddb908d2703d13b53b2abdaeb73acd1ca8b5a911bc256" +checksum = "935bb8db1e2829bf26b80ed3daeed6cf9fc804f7002c90a8d17dbd5e93c69e0b" dependencies = [ "anyhow", "proc-macro2", @@ -10720,31 +10746,22 @@ dependencies = [ "syn 2.0.117", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", - "wit-parser", + "wit-parser 0.243.0", ] [[package]] name = "wasmtime-internal-component-util" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61571112f9cbf9798e48f3bd6ba5161588a08b99158585153784e3f46f955053" - -[[package]] -name = "wasmtime-internal-core" -version = "42.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7c68311d6220c20cefdf334e0c8021e16a050383c67edc5be42e5661ddf265" -dependencies = [ - "anyhow", - "libm", -] +checksum = "52625d0c8fe2df1d7dd96d45d37dae8818c183c183a82b2368e3741ee5253859" [[package]] name = "wasmtime-internal-cranelift" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5fd90a9113379260508193bab9f4e870d34078fdd181f9fc8dd053b0f7a958c" +checksum = "85da1ba5fee01a3ee21c4d0c8052cc9035388639fa091a969b534d4c6f8449d4" dependencies = [ + "anyhow", "cfg-if", "cranelift-codegen", "cranelift-control", @@ -10759,33 +10776,33 @@ dependencies = [ "smallvec", "target-lexicon 0.13.5", "thiserror 2.0.18", - "wasmparser 0.244.0", + "wasmparser 0.243.0", "wasmtime-environ", - "wasmtime-internal-core", + "wasmtime-internal-math", "wasmtime-internal-unwinder", "wasmtime-internal-versioned-export-macros", ] [[package]] name = "wasmtime-internal-fiber" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd95ecd37e62eaae686256ca9773902b73c0398c2eb8cfbca49fbf950609c22" +checksum = "a4c7de5a0872764c1ca640886af10a70cf7f8526386906245b43cdb345ece0e6" dependencies = [ + "anyhow", "cc", "cfg-if", "libc", "rustix", - "wasmtime-environ", "wasmtime-internal-versioned-export-macros", "windows-sys 0.61.2", ] [[package]] name = "wasmtime-internal-jit-debug" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b875a7727c043a308c81f2de5ce7260b7513cb5baaa2af32937646b8c9019a3f" +checksum = "160acd973d770d62bef1b2697d7fac83a8fe63ef966215e624382b2a9532bd58" dependencies = [ "cc", "object", @@ -10795,34 +10812,49 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52c0779e711777b915d017b3f54049e658057a77df99e0e7958406b3c5d7d07" +checksum = "cc57f590ba7ea967ea9e8c8560175c6926e5b15d11c29bbde3ad0013a29470eb" dependencies = [ + "anyhow", "cfg-if", "libc", - "wasmtime-internal-core", "windows-sys 0.61.2", ] [[package]] -name = "wasmtime-internal-unwinder" -version = "42.0.2" +name = "wasmtime-internal-math" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acb031b1e9700667b3f818235b2846e3babeb30bc340c8233d3fad4c44d80ff" +checksum = "07612904518d47b677e8db67ca47c16d8c8cefb0099020729f886776950cb58b" dependencies = [ + "libm", +] + +[[package]] +name = "wasmtime-internal-slab" +version = "40.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc83ff16531e1e1537e0de2b630af56a0a9e1fab864130c5b7e213da71783a0f" + +[[package]] +name = "wasmtime-internal-unwinder" +version = "40.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47371d697244785e4bbb371229f9a2daa8628d1e03368ec895cf658370e1bf38" +dependencies = [ + "anyhow", "cfg-if", "cranelift-codegen", "log", "object", - "wasmtime-environ", ] [[package]] name = "wasmtime-internal-versioned-export-macros" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbfbbfdb0cfd638145b0de4d3e309901ccc4e29965a33ca1eb18ab6f37057350" +checksum = "ad3cd4aff8f2e7ec658c7a0424b74ad88a6940505303fb0616323592a1c400a6" dependencies = [ "proc-macro2", "quote", @@ -10831,16 +10863,17 @@ dependencies = [ [[package]] name = "wasmtime-internal-winch" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4853af4a25f98c039cc27c7238e40df9ec783fc7981b879a813153d1d3211a" +checksum = "fc4d1e6a37b397aa0a16b3b7f3a48f317d73c81ac7801be1fa9a135f30c55421" dependencies = [ + "anyhow", "cranelift-codegen", "gimli", "log", "object", "target-lexicon 0.13.5", - "wasmparser 0.244.0", + "wasmparser 0.243.0", "wasmtime-environ", "wasmtime-internal-cranelift", "winch-codegen", @@ -10848,15 +10881,15 @@ dependencies = [ [[package]] name = "wasmtime-internal-wit-bindgen" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de1c8eaa54b17e3a64b6c0cfabd065bdbdfd06f5d7c685272b7309117377be0" +checksum = "73a5774737acccdc70ff27bbe9e09c45b156bd7792bb83906735a572ce122247" dependencies = [ "anyhow", "bitflags 2.11.0", "heck 0.5.0", "indexmap 2.13.0", - "wit-parser", + "wit-parser 0.243.0", ] [[package]] @@ -11515,10 +11548,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "42.0.2" +version = "40.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1bc7cbb9103e6847042f0514f911126263173f6e9a18e5cfa257d3b5711c09" +checksum = "dcca3ffe030cc6fe77f2055c19d850bcb2c2589fa6ddc7a8ebb446f7c2b1e715" dependencies = [ + "anyhow", "cranelift-assembler-x64", "cranelift-codegen", "gimli", @@ -11526,10 +11560,10 @@ dependencies = [ "smallvec", "target-lexicon 0.13.5", "thiserror 2.0.18", - "wasmparser 0.244.0", + "wasmparser 0.243.0", "wasmtime-environ", - "wasmtime-internal-core", "wasmtime-internal-cranelift", + "wasmtime-internal-math", ] [[package]] @@ -12135,7 +12169,7 @@ checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser", + "wit-parser 0.244.0", ] [[package]] @@ -12185,7 +12219,25 @@ dependencies = [ "wasm-encoder 0.244.0", "wasm-metadata", "wasmparser 0.244.0", - "wit-parser", + "wit-parser 0.244.0", +] + +[[package]] +name = "wit-parser" +version = "0.243.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df983a8608e513d8997f435bb74207bf0933d0e49ca97aa9d8a6157164b9b7fc" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.243.0", ] [[package]] diff --git a/v2/crates/homecore-plugins/Cargo.toml b/v2/crates/homecore-plugins/Cargo.toml index 7c4b23a1..e20c940a 100644 --- a/v2/crates/homecore-plugins/Cargo.toml +++ b/v2/crates/homecore-plugins/Cargo.toml @@ -62,7 +62,8 @@ base64 = "0.22" # Optional Wasmtime runtime (P2, default-off — 30 MB dep). # Bumped from 25.0.3 → 42 to remediate RUSTSEC-2026-0095 and RUSTSEC-2026-0096 # (Cranelift/Winch sandbox-escape CVEs, CVSS 9.0 — iter-11 security sprint HC-03/04). -wasmtime = { version = "42", optional = true } +# v40 is the newest line compatible with HOMECORE's Rust 1.89 MSRV. +wasmtime = { version = "40.0.4", optional = true } # Optional wasm3 interpretation runtime (P3, default-off). wasm3 = { version = "0.3", optional = true } diff --git a/v2/crates/homecore-plugins/src/discovery.rs b/v2/crates/homecore-plugins/src/discovery.rs index bfa8454d..f8d134d4 100644 --- a/v2/crates/homecore-plugins/src/discovery.rs +++ b/v2/crates/homecore-plugins/src/discovery.rs @@ -115,10 +115,7 @@ pub fn discover_plugins( let manifest_bytes = bounded_regular_file(&manifest_path, limits.max_manifest_bytes, "manifest")?; let manifest_text = std::str::from_utf8(&manifest_bytes).map_err(|e| { - PluginError::InvalidManifest(format!( - "{} is not UTF-8: {e}", - manifest_path.display() - )) + PluginError::InvalidManifest(format!("{} is not UTF-8: {e}", manifest_path.display())) })?; let manifest = PluginManifest::parse_json(manifest_text)?; if !domains.insert(manifest.domain.clone()) { @@ -235,9 +232,13 @@ mod tests { let root = root(); package(&root, "z-last", "zeta", "z.wasm"); package(&root, "a-first", "alpha", "a.wasm"); - let found = discover_plugins(&[root.clone()], DiscoveryLimits::default()).unwrap(); + let found = + discover_plugins(std::slice::from_ref(&root), DiscoveryLimits::default()).unwrap(); assert_eq!( - found.iter().map(|p| p.manifest.domain.as_str()).collect::>(), + found + .iter() + .map(|p| p.manifest.domain.as_str()) + .collect::>(), ["alpha", "zeta"] ); fs::remove_dir_all(root).unwrap(); @@ -247,7 +248,8 @@ mod tests { fn traversal_module_path_is_rejected() { let root = root(); package(&root, "bad", "bad", "../bad.wasm"); - let error = discover_plugins(&[root.clone()], DiscoveryLimits::default()).unwrap_err(); + let error = + discover_plugins(std::slice::from_ref(&root), DiscoveryLimits::default()).unwrap_err(); assert!(matches!(error, PluginError::InvalidManifest(_))); fs::remove_dir_all(root).unwrap(); } @@ -257,7 +259,7 @@ mod tests { let root = root(); package(&root, "large", "large", "large.wasm"); let error = discover_plugins( - &[root.clone()], + std::slice::from_ref(&root), DiscoveryLimits { max_module_bytes: 4, ..DiscoveryLimits::default() diff --git a/v2/crates/homecore-plugins/src/tests.rs b/v2/crates/homecore-plugins/src/tests.rs index de20cd50..2784bf13 100644 --- a/v2/crates/homecore-plugins/src/tests.rs +++ b/v2/crates/homecore-plugins/src/tests.rs @@ -5,6 +5,7 @@ //! and PluginError variants. #[cfg(test)] +#[allow(clippy::module_inception)] mod tests { use std::sync::Arc; @@ -144,7 +145,10 @@ mod tests { .expect("load should succeed"); assert_eq!(id.as_str(), "lights"); - assert!(*plugin.setup_called.lock().await, "setup should have been called"); + assert!( + *plugin.setup_called.lock().await, + "setup should have been called" + ); let listing = registry.list().await; assert_eq!(listing.len(), 1); @@ -163,7 +167,10 @@ mod tests { .expect("load should succeed"); registry.unload(&id).await.expect("unload should succeed"); - assert!(*plugin.unload_called.lock().await, "unload should have been called"); + assert!( + *plugin.unload_called.lock().await, + "unload should have been called" + ); assert_eq!(registry.list().await.len(), 0); } diff --git a/v2/crates/homecore-server/src/main.rs b/v2/crates/homecore-server/src/main.rs index 2396a4f3..0f79faea 100644 --- a/v2/crates/homecore-server/src/main.rs +++ b/v2/crates/homecore-server/src/main.rs @@ -37,6 +37,7 @@ use tower_http::services::ServeDir; use tower_http::trace::TraceLayer; mod gateway; +mod plugins; mod restore; use gateway::{GatewayConfig, GatewayState}; @@ -116,6 +117,26 @@ struct Cli { /// Optional Home Assistant-style automations YAML file to load at boot. #[arg(long, env = "HOMECORE_AUTOMATIONS")] automations: Option, + + /// Explicit directories containing packaged WebAssembly plugins. + #[arg( + long = "plugin-dir", + env = "HOMECORE_PLUGIN_DIRS", + value_delimiter = ',' + )] + plugin_dirs: Vec, + + /// Base64 Ed25519 publisher keys trusted to sign WebAssembly packages. + #[arg( + long = "plugin-trusted-publisher", + env = "HOMECORE_PLUGIN_TRUSTED_PUBLISHERS", + value_delimiter = ',' + )] + plugin_trusted_publishers: Vec, + + /// Permit unsigned WebAssembly plugins. Unsafe; development only. + #[arg(long, env = "HOMECORE_PLUGIN_ALLOW_UNSIGNED", default_value_t = false)] + plugin_allow_unsigned: bool, } #[tokio::main] @@ -206,6 +227,17 @@ async fn main() -> Result<()> { } // ── 3. Plugin runtime ─────────────────────────────────────────── + let server_plugins = plugins::ServerPlugins::start( + hc.clone(), + plugins::PluginConfig { + directories: cli.plugin_dirs.clone(), + trusted_publishers: cli.plugin_trusted_publishers.clone(), + allow_unsigned: cli.plugin_allow_unsigned, + limits: homecore_plugins::DiscoveryLimits::default(), + }, + ) + .await?; + // ── 4. Automation engine ──────────────────────────────────────── // Construct AND start the engine (HC-WS-03, ADR-161). `start()` // spawns the state-change event loop + the 1 Hz wall-clock timer @@ -302,6 +334,7 @@ async fn main() -> Result<()> { info!("Shutdown requested; draining active HTTP connections"); }) .await?; + server_plugins.shutdown().await; Ok(()) } diff --git a/v2/crates/homecore-server/src/plugins.rs b/v2/crates/homecore-server/src/plugins.rs index 5693921f..a336c080 100644 --- a/v2/crates/homecore-server/src/plugins.rs +++ b/v2/crates/homecore-server/src/plugins.rs @@ -10,8 +10,8 @@ use std::sync::Arc; use anyhow::{Context as _, Result}; use homecore::HomeCore; use homecore_plugins::{ - DiscoveryLimits, HomeCorePlugin, InProcessRuntime, PluginError, PluginManifest, - PluginRegistry, StateChangedEventJson, + DiscoveryLimits, HomeCorePlugin, InProcessRuntime, PluginError, PluginManifest, PluginRegistry, + StateChangedEventJson, }; use tokio::task::JoinHandle; use tracing::{error, info, warn}; @@ -22,9 +22,10 @@ use homecore_plugins::{ }; /// Factory entry for a trusted plugin linked into the server at compile time. +type NativePluginFactory = fn() -> Result<(PluginManifest, Arc), PluginError>; + pub struct NativePluginRegistration { - pub create: - fn() -> Result<(PluginManifest, Arc), PluginError>, + pub create: NativePluginFactory, } /// This is intentionally empty in the generic server. Appliance builds can @@ -36,6 +37,7 @@ pub struct PluginConfig { pub directories: Vec, pub trusted_publishers: Vec, pub allow_unsigned: bool, + #[cfg_attr(not(feature = "wasmtime"), allow(dead_code))] pub limits: DiscoveryLimits, } @@ -52,8 +54,8 @@ impl ServerPlugins { pub async fn start(hc: HomeCore, config: PluginConfig) -> Result { let native = Arc::new(PluginRegistry::new(InProcessRuntime)); for registration in NATIVE_PLUGINS { - let (manifest, plugin) = (registration.create)() - .context("compiled-in native plugin factory failed")?; + let (manifest, plugin) = + (registration.create)().context("compiled-in native plugin factory failed")?; native .load(manifest, plugin, hc.clone()) .await @@ -87,7 +89,10 @@ impl ServerPlugins { let change = match receiver.recv().await { Ok(change) => change, Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => { - warn!(skipped, "plugin state dispatcher lagged; events were dropped"); + warn!( + skipped, + "plugin state dispatcher lagged; events were dropped" + ); continue; } Err(tokio::sync::broadcast::error::RecvError::Closed) => break, @@ -106,7 +111,10 @@ impl ServerPlugins { } #[cfg(feature = "wasmtime")] for (id, plugin) in &dispatch_wasm { - if !plugin.subscriptions().iter().any(|entity| entity == change.entity_id.as_str()) + if !plugin + .subscriptions() + .iter() + .any(|entity| entity == change.entity_id.as_str()) { continue; } @@ -117,23 +125,30 @@ impl ServerPlugins { event.new_state.as_deref(), event.attributes.clone(), ); - match tokio::task::spawn_blocking(move || plugin.call_state_changed(&event)).await + match tokio::task::spawn_blocking(move || plugin.call_state_changed(&event)) + .await { Ok(Ok(0)) => {} - Ok(Ok(code)) => error!(plugin = %id, code, "WASM state_changed returned failure"), - Ok(Err(error)) => error!(plugin = %id, %error, "WASM state_changed trapped"), + Ok(Ok(code)) => { + error!(plugin = %id, code, "WASM state_changed returned failure") + } + Ok(Err(error)) => { + error!(plugin = %id, %error, "WASM state_changed trapped") + } Err(error) => error!(plugin = %id, %error, "WASM dispatch task failed"), } } } }); + #[cfg(feature = "wasmtime")] info!( native = NATIVE_PLUGINS.len(), - #[cfg(feature = "wasmtime")] wasm = wasm.len(), "plugin subsystem started" ); + #[cfg(not(feature = "wasmtime"))] + info!(native = NATIVE_PLUGINS.len(), "plugin subsystem started"); Ok(Self { native, dispatcher, @@ -171,11 +186,15 @@ async fn load_wasm_plugins( warn!("INSECURE plugin policy enabled: unsigned plugins may execute"); PluginPolicy::AllowUnsigned } else { - let keys: Vec<_> = config.trusted_publishers.iter().map(String::as_str).collect(); + let keys: Vec<_> = config + .trusted_publishers + .iter() + .map(String::as_str) + .collect(); PluginPolicy::trusted(&keys).context("invalid trusted plugin publisher key")? }; - let discovered = discover_plugins(&config.directories, config.limits) - .context("plugin discovery failed")?; + let discovered = + discover_plugins(&config.directories, config.limits).context("plugin discovery failed")?; let runtime = WasmtimeRuntime::new().context("Wasmtime initialization failed")?; let mut loaded: Vec<(PluginId, WasmPlugin)> = Vec::with_capacity(discovered.len()); for package in discovered {