From 8ab234ce098735fc1ce78430bc50be71ab0c12b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 08:55:39 +0000 Subject: [PATCH 01/19] build(deps): bump rustls-webpki from 0.103.10 to 0.103.13 Bumps [rustls-webpki](https://github.com/rustls/webpki) from 0.103.10 to 0.103.13. - [Release notes](https://github.com/rustls/webpki/releases) - [Commits](https://github.com/rustls/webpki/compare/v/0.103.10...v/0.103.13) --- updated-dependencies: - dependency-name: rustls-webpki dependency-version: 0.103.13 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47096e0..f8ec223 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2835,9 +2835,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", From f84162ab742727e7cb18ce4025632d54437cb64b Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:35:09 +0000 Subject: [PATCH 02/19] Update ws to version 8.21.0 --- stacker/stacker/package-lock.json | 9 ++++----- stacker/stacker/package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/stacker/stacker/package-lock.json b/stacker/stacker/package-lock.json index ec9e263..7dcc24e 100644 --- a/stacker/stacker/package-lock.json +++ b/stacker/stacker/package-lock.json @@ -5,14 +5,13 @@ "packages": { "": { "dependencies": { - "ws": "^8.18.3" + "ws": "^8.21.0" } }, "node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", - "license": "MIT", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "engines": { "node": ">=10.0.0" }, diff --git a/stacker/stacker/package.json b/stacker/stacker/package.json index 31fef03..40058be 100644 --- a/stacker/stacker/package.json +++ b/stacker/stacker/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "ws": "^8.18.3" + "ws": "^8.21.0" } } From b668dcc01f84527dabb99c79fd9924c2fe0553d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:35:10 +0000 Subject: [PATCH 03/19] build(deps): bump openssl from 0.10.76 to 0.10.80 in /stacker/stacker Bumps [openssl](https://github.com/rust-openssl/rust-openssl) from 0.10.76 to 0.10.80. - [Release notes](https://github.com/rust-openssl/rust-openssl/releases) - [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.76...openssl-v0.10.80) --- updated-dependencies: - dependency-name: openssl dependency-version: 0.10.80 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- stacker/stacker/Cargo.lock | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stacker/stacker/Cargo.lock b/stacker/stacker/Cargo.lock index 0776871..01c1825 100644 --- a/stacker/stacker/Cargo.lock +++ b/stacker/stacker/Cargo.lock @@ -4253,15 +4253,14 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.76" +version = "0.10.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" dependencies = [ "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -4291,9 +4290,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.112" +version = "0.9.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" dependencies = [ "cc", "libc", From 56ecfe78fb8619462167345d059b6832af97b44e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:35:17 +0000 Subject: [PATCH 04/19] build(deps): bump russh from 0.58.0 to 0.61.1 in /stacker/stacker Bumps [russh](https://github.com/warp-tech/russh) from 0.58.0 to 0.61.1. - [Release notes](https://github.com/warp-tech/russh/releases) - [Commits](https://github.com/warp-tech/russh/compare/v0.58.0...v0.61.1) --- updated-dependencies: - dependency-name: russh dependency-version: 0.61.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- stacker/stacker/Cargo.lock | 1081 ++++++++++++++++++++++++------------ stacker/stacker/Cargo.toml | 2 +- 2 files changed, 712 insertions(+), 371 deletions(-) diff --git a/stacker/stacker/Cargo.lock b/stacker/stacker/Cargo.lock index 0776871..7a8a525 100644 --- a/stacker/stacker/Cargo.lock +++ b/stacker/stacker/Cargo.lock @@ -305,6 +305,16 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "aead" +version = "0.6.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" +dependencies = [ + "crypto-common 0.2.2", + "inout 0.2.2", +] + [[package]] name = "aes" version = "0.8.4" @@ -312,24 +322,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] +[[package]] +name = "aes" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" +dependencies = [ + "cipher 0.5.2", + "cpubits", + "cpufeatures 0.3.0", + "zeroize", +] + [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", + "aead 0.5.2", + "aes 0.8.4", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.1", "subtle", ] +[[package]] +name = "aes-gcm" +version = "0.11.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da8c919c118108f144adecad74b425b804ad075580d605d9b33c2d6d1c62a2f8" +dependencies = [ + "aead 0.6.0-rc.10", + "aes 0.9.1", + "cipher 0.5.2", + "ctr 0.10.1", + "ghash 0.6.0", + "subtle", + "zeroize", +] + [[package]] name = "ahash" version = "0.7.8" @@ -509,13 +546,13 @@ dependencies = [ [[package]] name = "argon2" -version = "0.5.3" +version = "0.6.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5" dependencies = [ "base64ct", "blake2", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "password-hash", ] @@ -1042,13 +1079,13 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bcrypt-pbkdf" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" +checksum = "144e573728da132683b9488acd528274c790e07fc06ff81ee29f9d8f8b1041e0" dependencies = [ "blowfish", - "pbkdf2", - "sha2 0.10.9", + "pbkdf2 0.13.0", + "sha2 0.11.0", ] [[package]] @@ -1068,11 +1105,11 @@ dependencies = [ [[package]] name = "blake2" -version = "0.10.6" +version = "0.11.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690" dependencies = [ - "digest 0.10.7", + "digest 0.11.2", ] [[package]] @@ -1091,6 +1128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ "hybrid-array", + "zeroize", ] [[package]] @@ -1102,6 +1140,15 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "block-padding" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" +dependencies = [ + "hybrid-array", +] + [[package]] name = "blocking" version = "1.6.2" @@ -1117,12 +1164,12 @@ dependencies = [ [[package]] name = "blowfish" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +checksum = "62ce3946557b35e71d1bbe07ec385073ce9eda05043f95de134eb578fcf1a298" dependencies = [ "byteorder", - "cipher", + "cipher 0.5.2", ] [[package]] @@ -1280,7 +1327,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "cbc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896" +dependencies = [ + "cipher 0.5.2", ] [[package]] @@ -1309,13 +1365,15 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", - "cipher", - "cpufeatures 0.2.17", + "cipher 0.5.2", + "cpufeatures 0.3.0", + "rand_core 0.10.1", + "zeroize", ] [[package]] @@ -1371,7 +1429,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", - "inout", + "inout 0.1.4", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.2", + "inout 0.2.2", + "zeroize", ] [[package]] @@ -1435,9 +1505,9 @@ dependencies = [ [[package]] name = "cmov" -version = "0.5.0-pre.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417da527aa9bf6a1e10a781231effd1edd3ee82f27d5f8529ac9b279babce96" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" [[package]] name = "cms" @@ -1634,15 +1704,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "core-models" -version = "0.0.4" +name = "cpubits" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444" -dependencies = [ - "hax-lib", - "pastey", - "rand 0.9.2", -] +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" [[package]] name = "cpufeatures" @@ -1749,14 +1814,18 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.7.0-rc.18" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37387ceb32048ff590f2cbd24d8b05fffe63c3f69a5cfa089d4f722ca4385a19" +checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3" dependencies = [ + "cpubits", "ctutils", + "getrandom 0.4.2", + "hybrid-array", "num-traits", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.1", "serdect", + "subtle", "zeroize", ] @@ -1773,22 +1842,24 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ + "getrandom 0.4.2", "hybrid-array", + "rand_core 0.10.1", ] [[package]] name = "crypto-primes" -version = "0.7.0-pre.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79c98a281f9441200b24e3151407a629bfbe720399186e50516da939195e482" +checksum = "21f41f23de7d24cdbda7f0c4d9c0351f99a4ceb258ef30e5c1927af8987ffe5a" dependencies = [ - "crypto-bigint 0.7.0-rc.18", + "crypto-bigint 0.7.3", "libm", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.1", ] [[package]] @@ -1797,16 +1868,26 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "ctr" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21" +dependencies = [ + "cipher 0.5.2", ] [[package]] name = "ctutils" -version = "0.3.2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758e5ed90be3c8abff7f9a6f37ab7f6d8c59c2210d448b81f3f508134aec84e4" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" dependencies = [ "cmov", + "subtle", ] [[package]] @@ -1876,7 +1957,22 @@ dependencies = [ "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.10.7", - "fiat-crypto", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.11.2", + "fiat-crypto 0.3.0", "rustc_version", "subtle", "zeroize", @@ -2152,7 +2248,16 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "des" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a94e407b54f9034d71dd748234cd1e516ced6284009906ae246f177eafe5a" +dependencies = [ + "cipher 0.5.2", ] [[package]] @@ -2201,7 +2306,8 @@ checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" dependencies = [ "block-buffer 0.12.0", "const-oid 0.10.2", - "crypto-common 0.2.1", + "crypto-common 0.2.2", + "ctutils", ] [[package]] @@ -2259,34 +2365,71 @@ checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der 0.7.10", "digest 0.10.7", - "elliptic-curve", - "rfc6979", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", "signature 2.2.0", "spki 0.7.3", ] +[[package]] +name = "ecdsa" +version = "0.17.0-rc.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54fb064faabbee66e1fc8e5c5a9458d4269dc2d8b638fe86a425adb2510d1a96" +dependencies = [ + "der 0.8.0", + "digest 0.11.2", + "elliptic-curve 0.14.0-rc.32", + "rfc6979 0.5.0", + "signature 3.0.0", + "spki 0.8.0", + "zeroize", +] + [[package]] name = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8 0.10.2", "signature 2.2.0", ] +[[package]] +name = "ed25519" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" +dependencies = [ + "pkcs8 0.11.0", + "signature 3.0.0", +] + [[package]] name = "ed25519-dalek" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core 0.6.4", - "serde", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "sha2 0.10.9", "subtle", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0-pre.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20449acd54b660981ae5caa2bcb56d1fe7f25f2e37a38ec507400fab034d4bb6" +dependencies = [ + "curve25519-dalek 5.0.0-pre.6", + "ed25519 3.0.0", + "rand_core 0.10.1", + "serde", + "sha2 0.11.0", + "signature 3.0.0", + "subtle", "zeroize", ] @@ -2311,11 +2454,32 @@ dependencies = [ "ff", "generic-array 0.14.7", "group", - "hkdf", - "pem-rfc7468 0.7.0", "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.14.0-rc.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda94f31325c4275e9706adecbb6f0650dee2f904c915a98e3d81adaaaa757aa" +dependencies = [ + "base16ct 1.0.0", + "crypto-bigint 0.7.3", + "crypto-common 0.2.2", + "digest 0.11.2", + "hkdf 0.13.0", + "hybrid-array", + "once_cell", + "pem-rfc7468 1.0.0", + "pkcs8 0.11.0", + "rand_core 0.10.1", + "rustcrypto-ff", + "rustcrypto-group", + "sec1 0.8.1", "subtle", "zeroize", ] @@ -2475,6 +2639,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + [[package]] name = "filetime" version = "0.2.27" @@ -2797,6 +2967,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -2808,7 +2979,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", - "polyval", + "polyval 0.6.2", +] + +[[package]] +name = "ghash" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" +dependencies = [ + "polyval 0.7.1", ] [[package]] @@ -2972,43 +3152,6 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "hax-lib" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86" -dependencies = [ - "hax-lib-macros", - "num-bigint", - "num-traits", -] - -[[package]] -name = "hax-lib-macros" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1" -dependencies = [ - "hax-lib-macros-types", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "hax-lib-macros-types" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_json", - "uuid", -] - [[package]] name = "heck" version = "0.5.0" @@ -3035,9 +3178,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-literal" -version = "0.4.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" [[package]] name = "hkdf" @@ -3045,7 +3188,16 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", +] + +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac 0.13.0", ] [[package]] @@ -3057,6 +3209,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.2", +] + [[package]] name = "home" version = "0.5.12" @@ -3181,7 +3342,10 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" dependencies = [ + "ctutils", + "subtle", "typenum", + "zeroize", ] [[package]] @@ -3491,10 +3655,20 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "block-padding", + "block-padding 0.3.3", "generic-array 0.14.7", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "block-padding 0.4.2", + "hybrid-array", +] + [[package]] name = "instant" version = "0.1.13" @@ -3505,34 +3679,15 @@ dependencies = [ ] [[package]] -name = "internal-russh-forked-ssh-key" -version = "0.6.16+upstream-0.6.7" +name = "internal-russh-num-bigint" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe44f2bbd99fcb302e246e2d6bcf51aeda346d02a365f80296a07a8c711b6da6" +checksum = "ae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8" dependencies = [ - "argon2", - "bcrypt-pbkdf", - "digest 0.11.2", - "ecdsa", - "ed25519-dalek", - "hex", - "hmac", - "num-bigint-dig", - "p256", - "p384", - "p521", - "rand_core 0.6.4", - "rsa 0.10.0-rc.12", - "sec1", - "sha1 0.10.6", - "sha1 0.11.0", - "sha2 0.10.9", - "signature 2.2.0", - "signature 3.0.0-rc.6", - "ssh-cipher", - "ssh-encoding", - "subtle", - "zeroize", + "num-integer", + "num-traits", + "rand 0.10.1", + "rand_core 0.10.1", ] [[package]] @@ -3653,6 +3808,26 @@ dependencies = [ "serde", ] +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + +[[package]] +name = "kem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd" +dependencies = [ + "crypto-common 0.2.2", + "rand_core 0.10.1", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -3735,75 +3910,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.184" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" - -[[package]] -name = "libcrux-intrinsics" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9ee7ef66569dd7516454fe26de4e401c0c62073929803486b96744594b9632" -dependencies = [ - "core-models", - "hax-lib", -] - -[[package]] -name = "libcrux-ml-kem" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6a88086bf11bd2ec90926c749c4a427f2e59841437dbdede8cde8a96334ab" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-secrets", - "libcrux-sha3", - "libcrux-traits", - "rand 0.9.2", - "tls_codec", -] - -[[package]] -name = "libcrux-platform" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778" -dependencies = [ - "libc", -] - -[[package]] -name = "libcrux-secrets" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4dbbf6bc9f2bc0f20dc3bea3e5c99adff3bdccf6d2a40488963da69e2ec307" -dependencies = [ - "hax-lib", -] - -[[package]] -name = "libcrux-sha3" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2400bec764d1c75b8a496d5747cffe32f1fb864a12577f0aca2f55a92021c962" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-traits", -] - -[[package]] -name = "libcrux-traits" -version = "0.0.4" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034" -dependencies = [ - "libcrux-secrets", - "rand 0.9.2", -] +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -4015,6 +4124,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ml-kem" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e15f3e5b957493873e396a66914e83e616b6afe335cdef7efe5c6e1216aba66" +dependencies = [ + "hybrid-array", + "kem", + "module-lattice", + "pkcs8 0.11.0", + "rand_core 0.10.1", + "sha3", +] + [[package]] name = "mockito" version = "1.7.2" @@ -4040,6 +4163,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "module-lattice" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe" +dependencies = [ + "ctutils", + "hybrid-array", + "num-traits", +] + [[package]] name = "multimap" version = "0.10.1" @@ -4071,9 +4205,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags 2.11.0", "cfg-if", @@ -4143,7 +4277,6 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand 0.8.5", ] [[package]] @@ -4158,7 +4291,6 @@ dependencies = [ "num-iter", "num-traits", "rand 0.8.5", - "serde", "smallvec", "zeroize", ] @@ -4317,14 +4449,14 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cae83056e7cb770211494a0ecf66d9fa7eba7d00977e5bb91f0e925b40b937f" dependencies = [ - "cbc", + "cbc 0.1.2", "cms", "der 0.7.10", - "des", + "des 0.8.1", "hex", - "hmac", + "hmac 0.12.1", "pkcs12", - "pkcs5", + "pkcs5 0.7.1", "rand 0.9.2", "rc2", "sha1 0.10.6", @@ -4339,24 +4471,51 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder 0.13.6", "sha2 0.10.9", ] +[[package]] +name = "p256" +version = "0.14.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b97e3bf0465157ae90975ff52dbeb1362ba618924878c9f74c25baa27a65f9a" +dependencies = [ + "ecdsa 0.17.0-rc.18", + "elliptic-curve 0.14.0-rc.32", + "primefield", + "primeorder 0.14.0-rc.9", + "sha2 0.11.0", +] + [[package]] name = "p384" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder 0.13.6", "sha2 0.10.9", ] +[[package]] +name = "p384" +version = "0.14.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437f30ebcb1e16ff48acead5f08bd69fbcdbc82421687bb48af5c315a0bfab03" +dependencies = [ + "ecdsa 0.17.0-rc.18", + "elliptic-curve 0.14.0-rc.32", + "fiat-crypto 0.3.0", + "primefield", + "primeorder 0.14.0-rc.9", + "sha2 0.11.0", +] + [[package]] name = "p521" version = "0.13.3" @@ -4364,13 +4523,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" dependencies = [ "base16ct 0.2.0", - "ecdsa", - "elliptic-curve", - "primeorder", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder 0.13.6", "rand_core 0.6.4", "sha2 0.10.9", ] +[[package]] +name = "p521" +version = "0.14.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9fd792bab86ecf6249561752fb5a413511f999887107dd054bbda5143743d7" +dependencies = [ + "base16ct 1.0.0", + "ecdsa 0.17.0-rc.18", + "elliptic-curve 0.14.0-rc.32", + "primefield", + "primeorder 0.14.0-rc.9", + "sha2 0.11.0", +] + [[package]] name = "pageant" version = "0.2.0" @@ -4430,13 +4603,11 @@ dependencies = [ [[package]] name = "password-hash" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b" dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", + "phc", ] [[package]] @@ -4445,12 +4616,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - [[package]] name = "pathdiff" version = "0.2.3" @@ -4464,7 +4629,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", - "hmac", + "hmac 0.12.1", +] + +[[package]] +name = "pbkdf2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" +dependencies = [ + "digest 0.11.2", + "hmac 0.13.0", ] [[package]] @@ -4571,6 +4746,16 @@ dependencies = [ "indexmap 2.14.0", ] +[[package]] +name = "phc" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892" +dependencies = [ + "base64ct", + "ctutils", +] + [[package]] name = "phf" version = "0.11.3" @@ -4735,15 +4920,31 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" dependencies = [ - "aes", - "cbc", + "aes 0.8.4", + "cbc 0.1.2", "der 0.7.10", - "pbkdf2", - "scrypt", + "pbkdf2 0.12.2", + "scrypt 0.11.0", "sha2 0.10.9", "spki 0.7.3", ] +[[package]] +name = "pkcs5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279a91971a1d8eb1260a30938eae3be9cb67b472dffecb222fbbbe2fd2dc1453" +dependencies = [ + "aes 0.9.1", + "cbc 0.2.1", + "der 0.8.0", + "pbkdf2 0.13.0", + "rand_core 0.10.1", + "scrypt 0.12.0", + "sha2 0.11.0", + "spki 0.8.0", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -4751,18 +4952,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der 0.7.10", - "pkcs5", - "rand_core 0.6.4", "spki 0.7.3", ] [[package]] name = "pkcs8" -version = "0.11.0-rc.11" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" dependencies = [ "der 0.8.0", + "pkcs5 0.8.0", + "rand_core 0.10.1", "spki 0.8.0", ] @@ -4810,13 +5011,13 @@ dependencies = [ [[package]] name = "poly1305" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +checksum = "a00baa632505d05512f48a963e16051c54fda9a95cc9acea1a4e3c90991c4a2e" dependencies = [ - "cpufeatures 0.2.17", - "opaque-debug", - "universal-hash", + "cpufeatures 0.3.0", + "universal-hash 0.6.1", + "zeroize", ] [[package]] @@ -4828,7 +5029,18 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "polyval" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfc63250416fea14f5749b90725916a6c903f599d51cb635aa7a52bfd03eede" +dependencies = [ + "cpubits", + "cpufeatures 0.3.0", + "universal-hash 0.6.1", ] [[package]] @@ -4901,13 +5113,36 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primefield" +version = "0.14.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b52e6ee42db392378a95622b463c9740631171d1efce43fa445a569c1600cb6" +dependencies = [ + "crypto-bigint 0.7.3", + "crypto-common 0.2.2", + "rand_core 0.10.1", + "rustcrypto-ff", + "subtle", + "zeroize", +] + [[package]] name = "primeorder" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "elliptic-curve", + "elliptic-curve 0.13.8", +] + +[[package]] +name = "primeorder" +version = "0.14.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0556580e42c19833f5d232aca11a7687a503ee41f937b54f5ae1d50fc2a6a36a" +dependencies = [ + "elliptic-curve 0.14.0-rc.32", ] [[package]] @@ -4934,28 +5169,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -5136,6 +5349,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -5195,9 +5419,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0-rc-3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66ee92bc15280519ef199a274fe0cafff4245d31bc39aaa31c011ad56cb1f05" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_hc" @@ -5214,7 +5438,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -5381,7 +5605,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", + "subtle", +] + +[[package]] +name = "rfc6979" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5236ce872cac07e0fb3969b0cbf468c7d2f37d432f1b627dcb7b8d34563fb0c3" +dependencies = [ + "hmac 0.13.0", "subtle", ] @@ -5463,94 +5697,106 @@ dependencies = [ [[package]] name = "rsa" -version = "0.10.0-rc.12" +version = "0.10.0-rc.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a2b1eacbc34fbaf77f6f1db1385518446008d49b9f9f59dc9d1340fce4ca9e" +checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf" dependencies = [ "const-oid 0.10.2", - "crypto-bigint 0.7.0-rc.18", + "crypto-bigint 0.7.3", "crypto-primes", "digest 0.11.2", "pkcs1 0.8.0-rc.4", - "pkcs8 0.11.0-rc.11", - "rand_core 0.10.0-rc-3", + "pkcs8 0.11.0", + "rand_core 0.10.1", "sha2 0.11.0", - "signature 3.0.0-rc.6", + "signature 3.0.0", "spki 0.8.0", "zeroize", ] [[package]] name = "russh" -version = "0.58.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30f6ce4f5d5105b934cfb4b8b3028aab4d5dcdff863cb8dda9edd06d39b8c4e8" +checksum = "f67013f080c226e5a34db1c71f2567f44d95a6300005bb6cd4e2c8fe3c326d1b" dependencies = [ - "aes", + "aes 0.9.1", "aws-lc-rs", "bitflags 2.11.0", - "block-padding", + "block-padding 0.4.2", "byteorder", "bytes", - "cbc", - "ctr", - "curve25519-dalek", + "cbc 0.2.1", + "cipher 0.5.2", + "crypto-bigint 0.7.3", + "ctr 0.10.1", + "curve25519-dalek 5.0.0-pre.6", "data-encoding", "delegate", - "der 0.7.10", - "digest 0.10.7", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", + "der 0.8.0", + "digest 0.11.2", + "ecdsa 0.17.0-rc.18", + "ed25519-dalek 3.0.0-pre.7", + "elliptic-curve 0.14.0-rc.32", "enum_dispatch", "flate2", "futures", "generic-array 1.3.5", "getrandom 0.2.17", + "ghash 0.6.0", "hex-literal", - "hmac", - "inout", - "internal-russh-forked-ssh-key", - "libcrux-ml-kem", + "hkdf 0.13.0", + "hmac 0.13.0", + "inout 0.1.4", + "internal-russh-num-bigint", + "keccak", "log", "md5", + "ml-kem", + "module-lattice", "num-bigint", - "p256", - "p384", - "p521", + "p256 0.14.0-rc.9", + "p384 0.14.0-rc.9", + "p521 0.14.0-rc.9", "pageant", - "pbkdf2", + "pbkdf2 0.13.0", "pkcs1 0.8.0-rc.4", - "pkcs5", - "pkcs8 0.10.2", - "rand 0.9.2", - "rand_core 0.10.0-rc-3", - "rsa 0.10.0-rc.12", + "pkcs5 0.8.0", + "pkcs8 0.11.0", + "polyval 0.7.1", + "rand 0.10.1", + "rand_core 0.10.1", + "rsa 0.10.0-rc.18", "russh-cryptovec", "russh-util", - "sec1", - "sha1 0.10.6", - "sha2 0.10.9", - "signature 2.2.0", - "spki 0.7.3", - "ssh-encoding", + "salsa20 0.11.0", + "scrypt 0.12.0", + "sec1 0.8.1", + "sha1 0.11.0", + "sha2 0.11.0", + "sha3", + "signature 3.0.0", + "spki 0.8.0", + "ssh-encoding 0.3.0-rc.9", + "ssh-key 0.7.0-rc.10", "subtle", "thiserror 2.0.18", "tokio", "typenum", + "universal-hash 0.6.1", "zeroize", ] [[package]] name = "russh-cryptovec" -version = "0.58.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc109749696a6853cf2c3fba3537635264f3519a78a9f43c6b08c91edc024384" +checksum = "443f6bbcfacb34a1aab2b12b99bf08e0c63abdc5a0db261901365df9d57fff51" dependencies = [ "log", "nix", - "ssh-encoding", - "windows-sys 0.59.0", + "ssh-encoding 0.3.0-rc.9", + "windows-sys 0.61.2", ] [[package]] @@ -5584,6 +5830,27 @@ dependencies = [ "semver", ] +[[package]] +name = "rustcrypto-ff" +version = "0.14.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6" +dependencies = [ + "rand_core 0.10.1", + "subtle", +] + +[[package]] +name = "rustcrypto-group" +version = "0.14.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369f9b61aa45933c062c9f6b5c3c50ab710687eca83dd3802653b140b43f85ed" +dependencies = [ + "rand_core 0.10.1", + "rustcrypto-ff", + "subtle", +] + [[package]] name = "rusticata-macros" version = "4.1.0" @@ -5755,7 +6022,17 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "salsa20" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f874456e72520ff1375a06c588eaf074b0f01f9e9e1aada45bd9b7954a6e42c" +dependencies = [ + "cfg-if", + "cipher 0.5.2", ] [[package]] @@ -5788,11 +6065,23 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ - "pbkdf2", - "salsa20", + "pbkdf2 0.12.2", + "salsa20 0.10.2", "sha2 0.10.9", ] +[[package]] +name = "scrypt" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87af57419b594aa23fa95f09f0e06d80d84ba01c26148c43844cad6ff4485f0" +dependencies = [ + "cfg-if", + "pbkdf2 0.13.0", + "salsa20 0.11.0", + "sha2 0.11.0", +] + [[package]] name = "sealed" version = "0.6.0" @@ -5818,6 +6107,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sec1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" +dependencies = [ + "base16ct 1.0.0", + "ctutils", + "der 0.8.0", + "hybrid-array", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -6064,6 +6367,16 @@ dependencies = [ "digest 0.11.2", ] +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.2", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -6107,12 +6420,12 @@ dependencies = [ [[package]] name = "signature" -version = "3.0.0-rc.6" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a96996ccff7dfa16f052bd995b4cecc72af22c35138738dc029f0ead6608d" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" dependencies = [ "digest 0.11.2", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.1", ] [[package]] @@ -6430,8 +6743,8 @@ dependencies = [ "futures-util", "generic-array 0.14.7", "hex", - "hkdf", - "hmac", + "hkdf 0.12.4", + "hmac 0.12.1", "itoa", "log", "md-5", @@ -6470,8 +6783,8 @@ dependencies = [ "futures-core", "futures-util", "hex", - "hkdf", - "hmac", + "hkdf 0.12.4", + "hmac 0.12.1", "home", "ipnetwork", "itoa", @@ -6524,15 +6837,28 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" dependencies = [ - "aes", - "aes-gcm", - "cbc", + "cipher 0.4.4", + "ssh-encoding 0.2.0", +] + +[[package]] +name = "ssh-cipher" +version = "0.3.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10db6f219196a8528f9ec904d9d45cdad692d65b0e57e72be4dedd1c5fddce36" +dependencies = [ + "aead 0.6.0-rc.10", + "aes 0.9.1", + "aes-gcm 0.11.0-rc.4", + "cbc 0.2.1", "chacha20", - "cipher", - "ctr", + "cipher 0.5.2", + "ctr 0.10.1", + "ctutils", + "des 0.9.0", "poly1305", - "ssh-encoding", - "subtle", + "ssh-encoding 0.3.0-rc.9", + "zeroize", ] [[package]] @@ -6542,32 +6868,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" dependencies = [ "base64ct", - "bytes", "pem-rfc7468 0.7.0", "sha2 0.10.9", ] +[[package]] +name = "ssh-encoding" +version = "0.3.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abf34aa716da5d5b4c496936d042ea282ab392092cd68a72ef6a8863ff8c96a" +dependencies = [ + "base64ct", + "bytes", + "crypto-bigint 0.7.3", + "ctutils", + "digest 0.11.2", + "pem-rfc7468 1.0.0", + "zeroize", +] + [[package]] name = "ssh-key" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" dependencies = [ - "ed25519-dalek", - "p256", - "p384", - "p521", + "ed25519-dalek 2.2.0", + "p256 0.13.2", + "p384 0.13.1", + "p521 0.13.3", "rand_core 0.6.4", "rsa 0.9.10", - "sec1", + "sec1 0.7.3", "sha2 0.10.9", "signature 2.2.0", - "ssh-cipher", - "ssh-encoding", + "ssh-cipher 0.2.0", + "ssh-encoding 0.2.0", "subtle", "zeroize", ] +[[package]] +name = "ssh-key" +version = "0.7.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45735ce3dea95690e4a9e414c4cfde7f79835063c3dcd35881df85a84118e74b" +dependencies = [ + "argon2", + "bcrypt-pbkdf", + "ctutils", + "ed25519-dalek 3.0.0-pre.7", + "hex", + "hmac 0.13.0", + "p256 0.14.0-rc.9", + "p384 0.14.0-rc.9", + "p521 0.14.0-rc.9", + "rand_core 0.10.1", + "rsa 0.10.0-rc.18", + "sec1 0.8.1", + "sha1 0.11.0", + "sha2 0.11.0", + "signature 3.0.0", + "ssh-cipher 0.3.0-rc.9", + "ssh-encoding 0.3.0-rc.9", + "zeroize", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -6585,7 +6951,7 @@ dependencies = [ "actix-http", "actix-web", "actix-web-actors", - "aes-gcm", + "aes-gcm 0.10.3", "anyhow", "assert_cmd", "async-trait", @@ -6607,7 +6973,7 @@ dependencies = [ "futures-lite 2.6.1", "futures-util", "glob", - "hmac", + "hmac 0.12.1", "indexmap 2.14.0", "indicatif", "lapin", @@ -6633,7 +6999,7 @@ dependencies = [ "sha2 0.10.9", "sqlx", "sqlx-adapter", - "ssh-key", + "ssh-key 0.6.7", "tar", "tempfile", "tera", @@ -7023,27 +7389,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tls_codec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" -dependencies = [ - "tls_codec_derive", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "tokio" version = "1.51.1" @@ -7481,6 +7826,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" +dependencies = [ + "crypto-common 0.2.2", + "ctutils", +] + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -8377,20 +8732,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] [[package]] name = "zerotrie" diff --git a/stacker/stacker/Cargo.toml b/stacker/stacker/Cargo.toml index 71795e0..a37b8b4 100644 --- a/stacker/stacker/Cargo.toml +++ b/stacker/stacker/Cargo.toml @@ -59,7 +59,7 @@ tempfile = "3" flate2 = "1.0" tar = "0.4" ssh-key = { version = "0.6", features = ["ed25519", "rand_core"] } -russh = "0.58" +russh = "0.61" futures-util = "0.3.29" futures = "0.3.29" tokio-stream = "0.1.14" From e4254a7ec9257365788ba981288c663b56076cb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:35:20 +0000 Subject: [PATCH 05/19] build(deps): bump tar from 0.4.45 to 0.4.46 in /stacker/stacker Bumps [tar](https://github.com/composefs/tar-rs) from 0.4.45 to 0.4.46. - [Release notes](https://github.com/composefs/tar-rs/releases) - [Commits](https://github.com/composefs/tar-rs/compare/0.4.45...0.4.46) --- updated-dependencies: - dependency-name: tar dependency-version: 0.4.46 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- stacker/stacker/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stacker/stacker/Cargo.lock b/stacker/stacker/Cargo.lock index 0776871..42183e1 100644 --- a/stacker/stacker/Cargo.lock +++ b/stacker/stacker/Cargo.lock @@ -6808,9 +6808,9 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tar" -version = "0.4.45" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", From 0e4f4cc900993500cc70356b91b85ed6f77a5401 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Thu, 16 Jul 2026 15:59:55 +0300 Subject: [PATCH 06/19] label_matches --- src/agent/docker.rs | 64 +++++++++++++++++++++++++++++++++++------ src/commands/stacker.rs | 18 ++++++++++++ 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/agent/docker.rs b/src/agent/docker.rs index ba22542..06d7d6c 100644 --- a/src/agent/docker.rs +++ b/src/agent/docker.rs @@ -119,6 +119,26 @@ fn name_matches(container_name: &str, app_code: &str) -> bool { false } +/// Stacker's stable service-identity label, set by the control plane on every +/// project service. Preferred over Docker Compose's `com.docker.compose.service` +/// because it carries the app code the control plane resolves by and survives +/// compose service renames (the generated main service is named `app`, but its +/// `my.stacker.service` label is the project code). +const STACKER_SERVICE_LABEL: &str = "my.stacker.service"; +const COMPOSE_SERVICE_LABEL: &str = "com.docker.compose.service"; + +/// If a container's labels identify it as `app_code`, return which label +/// matched (for logging). Prefers the stacker-owned label over Compose's. +fn label_matches_app(labels: &HashMap, app_code: &str) -> Option<&'static str> { + if labels.get(STACKER_SERVICE_LABEL).map(String::as_str) == Some(app_code) { + return Some(STACKER_SERVICE_LABEL); + } + if labels.get(COMPOSE_SERVICE_LABEL).map(String::as_str) == Some(app_code) { + return Some(COMPOSE_SERVICE_LABEL); + } + None +} + pub async fn resolve_container_name(name: &str) -> Result { let docker = docker_client()?; let opts: Option = @@ -143,15 +163,14 @@ pub async fn resolve_container_name(name: &str) -> Result { available_containers.push(normalized.to_string()); if let Some(labels) = container.labels.as_ref() { - if let Some(service) = labels.get("com.docker.compose.service") { - if service == name { - tracing::info!( - app_code = name, - resolved_name = normalized, - "Container name resolved via compose service label" - ); - return Ok(normalized.to_string()); - } + if let Some(matched_label) = label_matches_app(labels, name) { + tracing::info!( + app_code = name, + resolved_name = normalized, + matched_label, + "Container name resolved via service label" + ); + return Ok(normalized.to_string()); } } @@ -874,6 +893,33 @@ mod tests { assert!(name_matches("/komodo", "komodo")); } + #[test] + fn label_matches_prefers_stacker_service_over_compose() { + let mut labels = HashMap::new(); + // Generated main service: compose service name is "app", but the + // stacker label carries the project code. + labels.insert(COMPOSE_SERVICE_LABEL.to_string(), "app".to_string()); + labels.insert(STACKER_SERVICE_LABEL.to_string(), "wordpress-matomo".to_string()); + + // Resolves by the stacker label even though the compose service is "app". + assert_eq!( + label_matches_app(&labels, "wordpress-matomo"), + Some(STACKER_SERVICE_LABEL) + ); + // The Docker Compose service name still resolves. + assert_eq!(label_matches_app(&labels, "app"), Some(COMPOSE_SERVICE_LABEL)); + // No spurious match. + assert_eq!(label_matches_app(&labels, "matomo"), None); + } + + #[test] + fn label_matches_compose_only_still_works() { + let mut labels = HashMap::new(); + labels.insert(COMPOSE_SERVICE_LABEL.to_string(), "matomo".to_string()); + assert_eq!(label_matches_app(&labels, "matomo"), Some(COMPOSE_SERVICE_LABEL)); + assert_eq!(label_matches_app(&labels, "app"), None); + } + #[test] fn test_name_matches_replica_suffix() { assert!(name_matches("komodo_1", "komodo")); diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 910b16e..e6bcc79 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -8466,11 +8466,25 @@ fn build_probe_result_payload( .map(|protocol| probe_issue_for_protocol(protocol)) .collect::>(); + // Surface the resolved container in the report so callers can act on it — + // e.g. the CLI's direct-container retry when an app-scoped probe finds + // nothing. Without this the report always carried an empty `containers` + // list even after the agent resolved the container internally. + let containers = if container_resolved.is_empty() { + Vec::new() + } else { + vec![json!({ + "name": container_resolved, + "ports": ports.iter().map(|port| port.to_string()).collect::>(), + })] + }; + json!({ "type": "probe_endpoints", "deployment_hash": deployment_hash, "app_code": app_code, "protocols_detected": protocols_detected, + "containers": containers, "endpoints": endpoints, "forms": forms, "diagnostics": ProbeDiagnostics { @@ -12759,6 +12773,10 @@ mod probe_endpoints_command_tests { assert_eq!(payload["type"], "probe_endpoints"); assert_eq!(payload["protocols_detected"], json!([])); + // The resolved container is surfaced so callers (e.g. the CLI's + // direct-container retry) can act on an otherwise-empty probe. + assert_eq!(payload["containers"][0]["name"], "status-panel-web-1"); + assert_eq!(payload["containers"][0]["ports"], json!(["3000"])); assert_eq!( payload["diagnostics"]["protocols_requested"], json!(["html_forms", "rest"]) From a1b6e309a8a2fb420ec9bbd9401a1834df989fa0 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Thu, 16 Jul 2026 16:16:34 +0300 Subject: [PATCH 07/19] cargo fmt all --- src/agent/docker.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/agent/docker.rs b/src/agent/docker.rs index 06d7d6c..e0b882c 100644 --- a/src/agent/docker.rs +++ b/src/agent/docker.rs @@ -899,7 +899,10 @@ mod tests { // Generated main service: compose service name is "app", but the // stacker label carries the project code. labels.insert(COMPOSE_SERVICE_LABEL.to_string(), "app".to_string()); - labels.insert(STACKER_SERVICE_LABEL.to_string(), "wordpress-matomo".to_string()); + labels.insert( + STACKER_SERVICE_LABEL.to_string(), + "wordpress-matomo".to_string(), + ); // Resolves by the stacker label even though the compose service is "app". assert_eq!( @@ -907,7 +910,10 @@ mod tests { Some(STACKER_SERVICE_LABEL) ); // The Docker Compose service name still resolves. - assert_eq!(label_matches_app(&labels, "app"), Some(COMPOSE_SERVICE_LABEL)); + assert_eq!( + label_matches_app(&labels, "app"), + Some(COMPOSE_SERVICE_LABEL) + ); // No spurious match. assert_eq!(label_matches_app(&labels, "matomo"), None); } @@ -916,7 +922,10 @@ mod tests { fn label_matches_compose_only_still_works() { let mut labels = HashMap::new(); labels.insert(COMPOSE_SERVICE_LABEL.to_string(), "matomo".to_string()); - assert_eq!(label_matches_app(&labels, "matomo"), Some(COMPOSE_SERVICE_LABEL)); + assert_eq!( + label_matches_app(&labels, "matomo"), + Some(COMPOSE_SERVICE_LABEL) + ); assert_eq!(label_matches_app(&labels, "app"), None); } From 80941102affafc735b5034874a2b0f805df4438f Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Thu, 16 Jul 2026 22:12:15 +0300 Subject: [PATCH 08/19] probe timeout --- src/agent/docker.rs | 67 ++++++++++++----- src/commands/stacker.rs | 163 ++++++++++++++++++++++------------------ 2 files changed, 137 insertions(+), 93 deletions(-) diff --git a/src/agent/docker.rs b/src/agent/docker.rs index e0b882c..399b973 100644 --- a/src/agent/docker.rs +++ b/src/agent/docker.rs @@ -10,8 +10,35 @@ use bollard::query_parameters::{ use bollard::Docker; use serde::Serialize; use std::collections::HashMap; +use std::time::Duration; use tracing::{debug, error}; +/// Upper bound for a single Docker control-plane operation (list/inspect/exec +/// setup). Wraps bollard calls so an unresponsive daemon can never hang a +/// resolver — it covers both connection and execution, since the timeout spans +/// the whole request including connect. +const DOCKER_OP_TIMEOUT: Duration = Duration::from_secs(10); + +/// Run a Docker control-plane future with [`DOCKER_OP_TIMEOUT`], mapping an +/// elapsed timeout into an error rather than hanging. Accepts any error type +/// bollard returns (it maps into `anyhow`). +async fn with_docker_timeout( + what: &str, + fut: impl std::future::Future>, +) -> Result +where + E: std::error::Error + Send + Sync + 'static, +{ + match tokio::time::timeout(DOCKER_OP_TIMEOUT, fut).await { + Ok(Ok(value)) => Ok(value), + Ok(Err(err)) => Err(anyhow::Error::new(err).context(format!("docker {what}"))), + Err(_) => Err(anyhow::anyhow!( + "docker {what} timed out after {}s", + DOCKER_OP_TIMEOUT.as_secs() + )), + } +} + #[derive(Serialize, Clone, Debug)] pub struct ContainerInfo { pub name: String, @@ -143,10 +170,7 @@ pub async fn resolve_container_name(name: &str) -> Result { let docker = docker_client()?; let opts: Option = Some(ListContainersOptionsBuilder::default().all(true).build()); - let list = docker - .list_containers(opts) - .await - .context("list containers")?; + let list = with_docker_timeout("list_containers", docker.list_containers(opts)).await?; tracing::debug!( app_code = name, @@ -200,10 +224,7 @@ pub async fn list_containers() -> Result> { let docker = docker_client()?; let opts: Option = Some(ListContainersOptionsBuilder::default().all(true).build()); - let list = docker - .list_containers(opts) - .await - .context("list containers")?; + let list = with_docker_timeout("list_containers", docker.list_containers(opts)).await?; Ok(list .into_iter() .map(|c| { @@ -234,10 +255,7 @@ pub async fn list_containers_with_logs(tail: &str) -> Result> let docker = docker_client()?; let opts: Option = Some(ListContainersOptionsBuilder::default().all(true).build()); - let list = docker - .list_containers(opts) - .await - .context("list containers")?; + let list = with_docker_timeout("list_containers", docker.list_containers(opts)).await?; let mut result = Vec::with_capacity(list.len()); @@ -391,10 +409,7 @@ pub async fn list_container_health() -> Result> { let docker = docker_client()?; let opts: Option = Some(ListContainersOptionsBuilder::default().all(true).build()); - let list = docker - .list_containers(opts) - .await - .context("list containers")?; + let list = with_docker_timeout("list_containers", docker.list_containers(opts)).await?; let mut health = Vec::with_capacity(list.len()); @@ -807,18 +822,30 @@ pub async fn exec_in_container_argv(name: &str, argv: Vec) -> Result<()> /// Execute a shell command inside a running container and return output. /// Returns (exit_code, stdout, stderr) tuple. pub async fn exec_in_container_with_output(name: &str, cmd: &str) -> Result<(i64, String, String)> { + let resolved_name = resolve_container_name(name) + .await + .unwrap_or_else(|_| name.to_string()); + exec_in_container_with_output_resolved(&resolved_name, cmd).await +} + +/// Like [`exec_in_container_with_output`] but assumes `name` is already a real, +/// resolved container name and skips the `resolve_container_name` lookup. Use +/// this in hot paths (e.g. endpoint probing) that resolve the container once up +/// front, to avoid a full `list_containers` API call on every invocation. +pub async fn exec_in_container_with_output_resolved( + resolved_name: &str, + cmd: &str, +) -> Result<(i64, String, String)> { use bollard::exec::StartExecResults; use futures_util::StreamExt; let docker = docker_client()?; - let resolved_name = resolve_container_name(name) - .await - .unwrap_or_else(|_| name.to_string()); + let name = resolved_name; // Create exec instance let exec = docker .create_exec( - &resolved_name, + resolved_name, CreateExecOptions { attach_stdout: Some(true), attach_stderr: Some(true), diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index e6bcc79..3021e0d 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -8349,66 +8349,71 @@ fn derive_health_status(container_state: &str, has_errors: bool) -> &'static str } #[cfg(feature = "docker")] -async fn get_container_ports(container_name: &str) -> Result> { - use tokio::process::Command; - - let output = Command::new("docker") - .args([ - "inspect", - "--format", - "{{json .Config.ExposedPorts}}", - container_name, - ]) - .output() - .await - .context("docker inspect for ports")?; +/// Hard timeout for a single `docker` CLI subprocess so an unresponsive daemon +/// can't hang a probe. Covers spawn + execution. +#[cfg(feature = "docker")] +const DOCKER_CLI_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); - let mut ports = Vec::new(); +/// Run a `docker` CLI subprocess with [`DOCKER_CLI_TIMEOUT`]. Returns `None` on +/// spawn error or timeout (callers treat that as "no data"). +#[cfg(feature = "docker")] +async fn docker_cli_output(args: &[&str]) -> Option { + use tokio::process::Command; + match tokio::time::timeout( + DOCKER_CLI_TIMEOUT, + Command::new("docker").args(args).output(), + ) + .await + { + Ok(Ok(output)) => Some(output), + _ => None, + } +} - if output.status.success() { - let stdout = String::from_utf8_lossy(&output.stdout); - let trimmed_output = stdout.trim(); +#[cfg(feature = "docker")] +fn parse_ports_json(stdout: &[u8], ports: &mut Vec) { + let stdout = String::from_utf8_lossy(stdout); + if let Ok(map) = serde_json::from_str::>(stdout.trim()) { // Parse JSON like {"80/tcp":{},"8080/tcp":{}} - if let Ok(map) = serde_json::from_str::>(trimmed_output) { - for port_str in map.keys() { - if let Some(port_num) = port_str.split('/').next() { - if let Ok(port) = port_num.parse::() { - if !ports.contains(&port) { - ports.push(port); - } + for port_str in map.keys() { + if let Some(port_num) = port_str.split('/').next() { + if let Ok(port) = port_num.parse::() { + if !ports.contains(&port) { + ports.push(port); } } } } } +} - // Also check network settings port bindings - let output2 = Command::new("docker") - .args([ - "inspect", - "--format", - "{{json .NetworkSettings.Ports}}", - container_name, - ]) - .output() - .await; +async fn get_container_ports(container_name: &str) -> Result> { + let mut ports = Vec::new(); + + if let Some(output) = docker_cli_output(&[ + "inspect", + "--format", + "{{json .Config.ExposedPorts}}", + container_name, + ]) + .await + { + if output.status.success() { + parse_ports_json(&output.stdout, &mut ports); + } + } - if let Ok(output2) = output2 { + // Also check network settings port bindings + if let Some(output2) = docker_cli_output(&[ + "inspect", + "--format", + "{{json .NetworkSettings.Ports}}", + container_name, + ]) + .await + { if output2.status.success() { - let stdout = String::from_utf8_lossy(&output2.stdout); - let trimmed_output = stdout.trim(); - if let Ok(map) = serde_json::from_str::>(trimmed_output) - { - for port_str in map.keys() { - if let Some(port_num) = port_str.split('/').next() { - if let Ok(port) = port_num.parse::() { - if !ports.contains(&port) { - ports.push(port); - } - } - } - } - } + parse_ports_json(&output2.stdout, &mut ports); } } @@ -8507,15 +8512,20 @@ async fn execute_http_body_probe( path: &str, timeout_secs: u32, ) -> HttpProbeExecution { + // Exec-first: `docker exec … curl http://localhost:` reaches the app + // from inside its own network namespace — no shared network, and it avoids + // the per-IP connect timeouts of the agent HTTP path when the agent is not + // on the project's network. Fall back to the agent HTTP client (reqwest to + // the container DNS name / IP) only when the container has no HTTP client + // or no shell. let mut execution = - execute_http_body_probe_from_agent(app_code, container_name, port, path, timeout_secs) - .await; + execute_http_body_probe_from_container_exec(container_name, port, path, timeout_secs).await; if execution.response.is_none() { - let container_execution = - execute_http_body_probe_from_container_exec(container_name, port, path, timeout_secs) + let agent_execution = + execute_http_body_probe_from_agent(app_code, container_name, port, path, timeout_secs) .await; - execution.attempts.extend(container_execution.attempts); - execution.response = container_execution.response; + execution.attempts.extend(agent_execution.attempts); + execution.response = agent_execution.response; } execution } @@ -8528,15 +8538,21 @@ async fn execute_http_status_probe( path: &str, timeout_secs: u32, ) -> HttpProbeExecution { + // Exec-first (see `execute_http_body_probe` for rationale). let mut execution = - execute_http_status_probe_from_agent(app_code, container_name, port, path, timeout_secs) + execute_http_status_probe_from_container_exec(container_name, port, path, timeout_secs) .await; if execution.response.is_none() { - let container_execution = - execute_http_status_probe_from_container_exec(container_name, port, path, timeout_secs) - .await; - execution.attempts.extend(container_execution.attempts); - execution.response = container_execution.response; + let agent_execution = execute_http_status_probe_from_agent( + app_code, + container_name, + port, + path, + timeout_secs, + ) + .await; + execution.attempts.extend(agent_execution.attempts); + execution.response = agent_execution.response; } execution } @@ -8712,7 +8728,7 @@ async fn execute_http_body_probe_from_container_exec( let command = build_http_body_probe_command(&url, timeout_secs); let result = tokio::time::timeout( std::time::Duration::from_secs((timeout_secs + 2) as u64), - docker::exec_in_container_with_output(container_name, &command), + docker::exec_in_container_with_output_resolved(container_name, &command), ) .await; let probe_result = match result { @@ -8799,7 +8815,7 @@ async fn execute_http_status_probe_from_container_exec( let command = build_http_status_probe_command(&url, timeout_secs); let result = tokio::time::timeout( std::time::Duration::from_secs((timeout_secs + 2) as u64), - docker::exec_in_container_with_output(container_name, &command), + docker::exec_in_container_with_output_resolved(container_name, &command), ) .await; let probe_result = match result { @@ -8867,6 +8883,9 @@ async fn execute_http_status_probe_from_container_exec( fn probe_http_client(timeout_secs: u32) -> Option { reqwest::Client::builder() .timeout(std::time::Duration::from_secs(timeout_secs as u64)) + // Fail fast on unreachable container IPs (agent not on the project + // network) instead of blocking for the full request timeout per URL. + .connect_timeout(std::time::Duration::from_secs(1)) .build() .ok() } @@ -8909,16 +8928,14 @@ fn push_probe_base_url(urls: &mut Vec, host: &str, port: u16, app_ #[cfg(feature = "docker")] async fn get_container_ip_addresses(container_name: &str) -> Vec { - let output = Command::new("docker") - .args([ - "inspect", - "--format", - "{{json .NetworkSettings.Networks}}", - container_name, - ]) - .output() - .await; - let Ok(output) = output else { + let Some(output) = docker_cli_output(&[ + "inspect", + "--format", + "{{json .NetworkSettings.Networks}}", + container_name, + ]) + .await + else { return Vec::new(); }; if !output.status.success() { From 3b7ab67cbfc471d0b7e2bad48c71b039bda7ab14 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Fri, 17 Jul 2026 10:02:18 +0300 Subject: [PATCH 09/19] docker_cli_output fix --- src/commands/stacker.rs | 2 +- tests/probe_exec_integration.rs | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/probe_exec_integration.rs diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 3021e0d..1e85e92 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -8348,7 +8348,6 @@ fn derive_health_status(container_state: &str, has_errors: bool) -> &'static str } } -#[cfg(feature = "docker")] /// Hard timeout for a single `docker` CLI subprocess so an unresponsive daemon /// can't hang a probe. Covers spawn + execution. #[cfg(feature = "docker")] @@ -8387,6 +8386,7 @@ fn parse_ports_json(stdout: &[u8], ports: &mut Vec) { } } +#[cfg(feature = "docker")] async fn get_container_ports(container_name: &str) -> Result> { let mut ports = Vec::new(); diff --git a/tests/probe_exec_integration.rs b/tests/probe_exec_integration.rs new file mode 100644 index 0000000..b9146fa --- /dev/null +++ b/tests/probe_exec_integration.rs @@ -0,0 +1,78 @@ +//! Integration test for the exec-first probe building blocks against a real +//! container. Requires a running Docker daemon, so it is `#[ignore]` by default. +//! +//! Run manually with: +//! cargo test --test probe_exec_integration -- --ignored --nocapture +#![cfg(feature = "docker")] + +use std::process::Command; + +fn docker(args: &[&str]) -> std::process::Output { + Command::new("docker") + .args(args) + .output() + .expect("failed to run docker") +} + +/// Removes the test container on drop, even if an assertion panics. +struct Cleanup(&'static str); +impl Drop for Cleanup { + fn drop(&mut self) { + let _ = docker(&["rm", "-f", self.0]); + } +} + +#[tokio::test] +#[ignore = "requires a running Docker daemon"] +async fn exec_first_probe_resolves_by_label_and_reaches_localhost() { + const CONTAINER: &str = "stacker-probe-exec-it"; + let _ = docker(&["rm", "-f", CONTAINER]); + + // A container whose Docker Compose service name (== container name) does NOT + // equal the app code, but carries the stacker label with the app code — + // exactly the generated-`app`-service situation the fix targets. + let start = docker(&[ + "run", + "-d", + "--name", + CONTAINER, + "-l", + "my.stacker.service=probeapp", + "nginx:alpine", + ]); + assert!( + start.status.success(), + "failed to start container: {}", + String::from_utf8_lossy(&start.stderr) + ); + let _cleanup = Cleanup(CONTAINER); + + // Let nginx come up. + tokio::time::sleep(std::time::Duration::from_millis(800)).await; + + // 1) Resolution: app_code "probeapp" resolves to the container via the + // my.stacker.service label, even though the container name differs. + let resolved = status_panel::agent::docker::resolve_container_name("probeapp") + .await + .expect("resolve_container_name"); + assert_eq!( + resolved, CONTAINER, + "app code should resolve to the container via my.stacker.service label" + ); + + // 2) Exec-first probe: the same command shape the probe builds, run inside + // the container's own namespace — reaches the app on localhost with no + // shared network. + let cmd = "if command -v curl >/dev/null 2>&1; then curl -sf -m 5 'http://localhost:80/' 2>/dev/null || true; \ + elif command -v wget >/dev/null 2>&1; then wget -q -T 5 -O - 'http://localhost:80/' 2>/dev/null || true; fi"; + let (exit_code, stdout, stderr) = + status_panel::agent::docker::exec_in_container_with_output_resolved(&resolved, cmd) + .await + .expect("exec_in_container_with_output_resolved"); + + assert_eq!(exit_code, 0, "exec exit code (stderr: {stderr})"); + assert!( + stdout.to_lowercase().contains("nginx"), + "expected the nginx welcome page from localhost, got: {stdout}" + ); +} From f9282b0b4014c1a4aa62c1e645c86fc604aa9ab3 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 09:27:06 +0300 Subject: [PATCH 10/19] source_url: Option added, optional external source URL (agent fetches via HTTP, no curl needed in container) --- src/commands/stacker.rs | 151 +++++++++++++++++++++++++++++++--------- 1 file changed, 120 insertions(+), 31 deletions(-) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 1e85e92..ed6356f 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -884,6 +884,8 @@ pub struct ActivatePipeCommand { source_adapter: Option, #[serde(default)] source_container: Option, + #[serde(default)] + source_url: Option, #[serde(default = "default_pipe_source_endpoint")] source_endpoint: String, #[serde(default = "default_pipe_source_method")] @@ -932,6 +934,8 @@ pub struct TriggerPipeCommand { source_adapter: Option, #[serde(default)] source_container: Option, + #[serde(default)] + source_url: Option, #[serde(default = "default_pipe_source_endpoint")] source_endpoint: String, #[serde(default = "default_pipe_source_method")] @@ -1040,6 +1044,7 @@ struct PipeLifecycleSnapshot { struct PipeRegistration { source_adapter: Option, source_container: Option, + source_url: Option, source_endpoint: String, source_method: String, source_broker_url: Option, @@ -1446,6 +1451,7 @@ impl PipeRuntime { source_adapter: None, input_data: Some(payload), source_container: None, + source_url: None, source_endpoint: default_pipe_source_endpoint(), source_method: default_pipe_source_method(), target_adapter: None, @@ -1480,6 +1486,7 @@ impl From for PipeRegistration { Self { source_adapter: value.source_adapter, source_container: value.source_container, + source_url: value.source_url, source_endpoint: value.source_endpoint, source_method: value.source_method, source_broker_url: value.source_broker_url, @@ -3680,6 +3687,10 @@ fn redact_persisted_registration(registration: &PipeRegistration) -> PipeRegistr .source_broker_url .as_deref() .map(redact_url_credentials); + registration.source_url = registration + .source_url + .as_deref() + .map(redact_url_credentials); registration.target_adapter = registration .target_adapter .take() @@ -3786,6 +3797,14 @@ fn merge_trigger_with_registration( { merged.source_container = registration.source_container.clone(); } + if merged + .source_url + .as_deref() + .filter(|value| !value.is_empty()) + .is_none() + { + merged.source_url = registration.source_url.clone(); + } if merged.source_endpoint == default_pipe_source_endpoint() { merged.source_endpoint = registration.source_endpoint.clone(); } @@ -4559,13 +4578,12 @@ async fn handle_trigger_pipe( let source_data = match resolved.input_data.clone() { Some(value) => value, None => match resolved - .source_container + .source_url .as_deref() .filter(|value| !value.is_empty()) { - Some(container) => match fetch_trigger_pipe_source_request( - container, - &resolved.source_endpoint, + Some(url) => match fetch_external_pipe_source_request( + url, &resolved.source_method, ) .await @@ -4628,33 +4646,104 @@ async fn handle_trigger_pipe( return Ok(result); } }, - None => { - let error = "trigger_pipe requires input_data or source_container"; - pipe_runtime - .mark_failed( - &data.deployment_hash, - &data.pipe_instance_id, - now_timestamp(), - error.to_string(), - ) - .await; - result.status = "failed".into(); - result.result = Some(json!({ - "type": "trigger_pipe", - "deployment_hash": data.deployment_hash, - "pipe_instance_id": data.pipe_instance_id, - "success": false, - "source_data": Value::Null, - "mapped_data": Value::Null, - "target_response": Value::Null, - "error": error, - "triggered_at": now_timestamp(), - "trigger_type": resolved.trigger_type, - "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, - })); - result.error = Some(error.into()); - return Ok(result); - } + None => match resolved + .source_container + .as_deref() + .filter(|value| !value.is_empty()) + { + Some(container) => match fetch_trigger_pipe_source_request( + container, + &resolved.source_endpoint, + &resolved.source_method, + ) + .await + { + Ok((status_code, response_body)) if (200..300).contains(&status_code) => { + response_body + } + Ok((status_code, response_body)) => { + let error = format!("source fetch failed with status {}", status_code); + pipe_runtime + .mark_failed( + &data.deployment_hash, + &data.pipe_instance_id, + now_timestamp(), + error.clone(), + ) + .await; + result.status = "failed".into(); + result.result = Some(json!({ + "type": "trigger_pipe", + "deployment_hash": data.deployment_hash, + "pipe_instance_id": data.pipe_instance_id, + "success": false, + "source_data": response_body, + "mapped_data": Value::Null, + "target_response": Value::Null, + "error": error, + "triggered_at": now_timestamp(), + "trigger_type": resolved.trigger_type, + "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, + })); + result.error = Some(error); + return Ok(result); + } + Err(err) => { + let error = format!("failed to fetch trigger_pipe source: {}", err); + pipe_runtime + .mark_failed( + &data.deployment_hash, + &data.pipe_instance_id, + now_timestamp(), + error.clone(), + ) + .await; + result.status = "failed".into(); + result.result = Some(json!({ + "type": "trigger_pipe", + "deployment_hash": data.deployment_hash, + "pipe_instance_id": data.pipe_instance_id, + "success": false, + "source_data": Value::Null, + "mapped_data": Value::Null, + "target_response": Value::Null, + "error": error, + "triggered_at": now_timestamp(), + "trigger_type": resolved.trigger_type, + "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, + })); + result.error = Some(error); + return Ok(result); + } + }, + None => { + let error = "trigger_pipe requires input_data, source_url, or source_container"; + pipe_runtime + .mark_failed( + &data.deployment_hash, + &data.pipe_instance_id, + now_timestamp(), + error.to_string(), + ) + .await; + result.status = "failed".into(); + result.result = Some(json!({ + "type": "trigger_pipe", + "deployment_hash": data.deployment_hash, + "pipe_instance_id": data.pipe_instance_id, + "success": false, + "source_data": Value::Null, + "mapped_data": Value::Null, + "target_response": Value::Null, + "error": error, + "triggered_at": now_timestamp(), + "trigger_type": resolved.trigger_type, + "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, + })); + result.error = Some(error.into()); + return Ok(result); + } + }, }, }; From d6c5287b3cd659d70c68711ad94fe740ea54e4f8 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 09:29:01 +0300 Subject: [PATCH 11/19] fmt --- src/commands/stacker.rs | 123 ++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 63 deletions(-) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index ed6356f..15449e7 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -4582,70 +4582,67 @@ async fn handle_trigger_pipe( .as_deref() .filter(|value| !value.is_empty()) { - Some(url) => match fetch_external_pipe_source_request( - url, - &resolved.source_method, - ) - .await - { - Ok((status_code, response_body)) if (200..300).contains(&status_code) => { - response_body - } - Ok((status_code, response_body)) => { - let error = format!("source fetch failed with status {}", status_code); - pipe_runtime - .mark_failed( - &data.deployment_hash, - &data.pipe_instance_id, - now_timestamp(), - error.clone(), - ) - .await; - result.status = "failed".into(); - result.result = Some(json!({ - "type": "trigger_pipe", - "deployment_hash": data.deployment_hash, - "pipe_instance_id": data.pipe_instance_id, - "success": false, - "source_data": response_body, - "mapped_data": Value::Null, - "target_response": Value::Null, - "error": error, - "triggered_at": now_timestamp(), - "trigger_type": resolved.trigger_type, - "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, - })); - result.error = Some(error); - return Ok(result); - } - Err(err) => { - let error = format!("failed to fetch trigger_pipe source: {}", err); - pipe_runtime - .mark_failed( - &data.deployment_hash, - &data.pipe_instance_id, - now_timestamp(), - error.clone(), - ) - .await; - result.status = "failed".into(); - result.result = Some(json!({ - "type": "trigger_pipe", - "deployment_hash": data.deployment_hash, - "pipe_instance_id": data.pipe_instance_id, - "success": false, - "source_data": Value::Null, - "mapped_data": Value::Null, - "target_response": Value::Null, - "error": error, - "triggered_at": now_timestamp(), - "trigger_type": resolved.trigger_type, - "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, - })); - result.error = Some(error); - return Ok(result); + Some(url) => { + match fetch_external_pipe_source_request(url, &resolved.source_method).await { + Ok((status_code, response_body)) if (200..300).contains(&status_code) => { + response_body + } + Ok((status_code, response_body)) => { + let error = format!("source fetch failed with status {}", status_code); + pipe_runtime + .mark_failed( + &data.deployment_hash, + &data.pipe_instance_id, + now_timestamp(), + error.clone(), + ) + .await; + result.status = "failed".into(); + result.result = Some(json!({ + "type": "trigger_pipe", + "deployment_hash": data.deployment_hash, + "pipe_instance_id": data.pipe_instance_id, + "success": false, + "source_data": response_body, + "mapped_data": Value::Null, + "target_response": Value::Null, + "error": error, + "triggered_at": now_timestamp(), + "trigger_type": resolved.trigger_type, + "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, + })); + result.error = Some(error); + return Ok(result); + } + Err(err) => { + let error = format!("failed to fetch trigger_pipe source: {}", err); + pipe_runtime + .mark_failed( + &data.deployment_hash, + &data.pipe_instance_id, + now_timestamp(), + error.clone(), + ) + .await; + result.status = "failed".into(); + result.result = Some(json!({ + "type": "trigger_pipe", + "deployment_hash": data.deployment_hash, + "pipe_instance_id": data.pipe_instance_id, + "success": false, + "source_data": Value::Null, + "mapped_data": Value::Null, + "target_response": Value::Null, + "error": error, + "triggered_at": now_timestamp(), + "trigger_type": resolved.trigger_type, + "lifecycle": pipe_runtime.snapshot(&data.deployment_hash, &data.pipe_instance_id).await, + })); + result.error = Some(error); + return Ok(result); + } } - }, + } None => match resolved .source_container .as_deref() From f1f3cb899f7fb420cff853b1e14eae242b27caa9 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 09:37:44 +0300 Subject: [PATCH 12/19] feat(pipe): add source_url for external HTTP source fetch Add source_url field to TriggerPipeCommand, ActivatePipeCommand, and PipeRegistration. When source_url is set, the agent fetches source data via reqwest HTTP client instead of exec'ing curl inside a container. This eliminates the need for curl/wget in source containers. --- src/commands/stacker.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 15449e7..ed7f4d1 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -95,6 +95,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "user": { "email": "dev@try.direct" } })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -134,6 +135,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "user": { "email": "dev@try.direct" } })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -187,6 +189,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "subject": "Deployment ready", "body_text": "done" })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: Some(PipeAdapterReference::new("smtp").with_config(json!({ @@ -305,6 +308,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: None, source_container: None, + source_url: None, source_endpoint: "/source/data".into(), source_method: "GET".into(), target_adapter: None, @@ -338,6 +342,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "key": "value" })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -376,6 +381,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "key": "value" })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -414,6 +420,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "key": "value" })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -452,6 +459,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "key": "value" })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -494,6 +502,7 @@ mod trigger_pipe_handler_tests { pipe_instance_id: "pipe-runtime-1".into(), source_adapter: None, source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), source_broker_url: None, @@ -528,6 +537,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "user": { "email": "runtime@try.direct" } })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -574,6 +584,7 @@ mod trigger_pipe_handler_tests { pipe_instance_id: "pipe-runtime-2".into(), source_adapter: None, source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), source_broker_url: None, @@ -632,6 +643,7 @@ mod trigger_pipe_handler_tests { pipe_instance_id: "pipe-runtime-3".into(), source_adapter: None, source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), source_broker_url: None, @@ -657,6 +669,7 @@ mod trigger_pipe_handler_tests { source_adapter: None, input_data: Some(json!({ "user": { "email": "runtime@try.direct" } })), source_container: None, + source_url: None, source_endpoint: "/".into(), source_method: "GET".into(), target_adapter: None, @@ -10119,6 +10132,7 @@ mod tests { pipe_instance_id: "pipe-poll-1".into(), source_adapter: None, source_container: None, + source_url: None, source_endpoint: "http://127.0.0.1:1/source".into(), source_method: "GET".into(), source_broker_url: None, @@ -10237,6 +10251,7 @@ mod tests { "password": "pop3-secret" }))), source_container: None, + source_url: None, source_endpoint: "/source".into(), source_method: "GET".into(), source_broker_url: Some("amqp://guest:guest@localhost:5672/%2f".into()), @@ -10295,6 +10310,7 @@ mod tests { "mailbox": "INBOX" }))), source_container: None, + source_url: None, source_endpoint: "/source".into(), source_method: "GET".into(), source_broker_url: None, @@ -10318,6 +10334,7 @@ mod tests { source_adapter: None, input_data: Some(json!({ "subject": "hello" })), source_container: None, + source_url: None, source_endpoint: default_pipe_source_endpoint(), source_method: default_pipe_source_method(), target_adapter: None, From c1c4a9bbe1bf58205adaf7ccbecd536c72f14598 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 09:46:51 +0300 Subject: [PATCH 13/19] fix: add source_url to remaining test constructors --- src/commands/stacker.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index ed7f4d1..566f21c 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -10008,6 +10008,7 @@ mod tests { pipe_instance_id: "pipe-restore-1".into(), source_adapter: None, source_container: Some("source-app".into()), + source_url: None, source_endpoint: "/source".into(), source_method: "GET".into(), source_broker_url: None, @@ -10072,6 +10073,7 @@ mod tests { pipe_instance_id: "pipe-deactivate-1".into(), source_adapter: None, source_container: Some("source-app".into()), + source_url: None, source_endpoint: "/source".into(), source_method: "GET".into(), source_broker_url: None, From 935d87c7f05ae3fc0a244eabdaed5450e11dfcf0 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 10:30:57 +0300 Subject: [PATCH 14/19] fix: update test assertion to match new source_url error message --- src/commands/stacker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 566f21c..9dd1295 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -327,7 +327,7 @@ mod trigger_pipe_handler_tests { assert_eq!(result.status, "failed"); assert_eq!( result.error.as_deref(), - Some("trigger_pipe requires input_data or source_container") + Some("trigger_pipe requires input_data, source_url, or source_container") ); } From 25a4d6ea5fdbbb53595e8a719756e6e901cca23c Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 14:14:22 +0300 Subject: [PATCH 15/19] target_headers added --- src/commands/stacker.rs | 61 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 9dd1295..46ac8fa 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -103,6 +103,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/webhook/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: Some(json!({ "email": "$.user.email" })), trigger_type: "manual".into(), }; @@ -143,6 +144,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/webhook/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: Some(json!({ "email": "$.user.email" })), trigger_type: "manual".into(), }; @@ -200,6 +202,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/".into(), target_method: "POST".into(), + target_headers: None, field_mapping: Some(json!({})), trigger_type: "manual".into(), }; @@ -316,6 +319,7 @@ mod trigger_pipe_handler_tests { target_container: Some("target-app".into()), target_endpoint: "/webhook/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: Some(json!({ "email": "$.user.email" })), trigger_type: "manual".into(), }; @@ -350,6 +354,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/ws-target".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -389,6 +394,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/grpc-target".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -428,6 +434,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -467,6 +474,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -514,6 +522,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/runtime/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: Some(json!({ "email": "$.user.email" })), trigger_type: "manual".into(), }; @@ -545,6 +554,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -596,6 +606,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/runtime/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -655,6 +666,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/runtime/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -677,6 +689,7 @@ mod trigger_pipe_handler_tests { target_container: None, target_endpoint: "/".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "manual".into(), }; @@ -922,6 +935,8 @@ pub struct ActivatePipeCommand { #[serde(default = "default_pipe_target_method")] target_method: String, #[serde(default)] + target_headers: Option>, + #[serde(default)] field_mapping: Option, #[serde(default = "default_activate_pipe_trigger_type")] trigger_type: String, @@ -964,6 +979,8 @@ pub struct TriggerPipeCommand { #[serde(default = "default_pipe_target_method")] target_method: String, #[serde(default)] + target_headers: Option>, + #[serde(default)] field_mapping: Option, #[serde(default = "default_pipe_trigger_type")] trigger_type: String, @@ -1069,6 +1086,7 @@ struct PipeRegistration { target_container: Option, target_endpoint: String, target_method: String, + target_headers: Option>, field_mapping: Option, trigger_type: String, lifecycle: PipeLifecycleSnapshot, @@ -1472,6 +1490,7 @@ impl PipeRuntime { target_container: None, target_endpoint: default_pipe_target_endpoint(), target_method: default_pipe_target_method(), + target_headers: None, field_mapping: None, trigger_type: trigger_type.to_string(), }; @@ -1511,6 +1530,7 @@ impl From for PipeRegistration { target_container: value.target_container, target_endpoint: value.target_endpoint, target_method: value.target_method, + target_headers: value.target_headers, field_mapping: value.field_mapping, trigger_type: value.trigger_type, lifecycle: PipeLifecycleSnapshot::active(String::new()), @@ -3712,6 +3732,13 @@ fn redact_persisted_registration(registration: &PipeRegistration) -> PipeRegistr .target_url .as_deref() .map(redact_url_credentials); + if let Some(ref mut headers) = registration.target_headers { + for value in headers.values_mut() { + if is_sensitive_header_value(value) { + *value = "[REDACTED]".into(); + } + } + } registration } @@ -3720,6 +3747,11 @@ fn redact_pipe_adapter_reference(mut adapter: PipeAdapterReference) -> PipeAdapt adapter } +fn is_sensitive_header_value(value: &str) -> bool { + let lower = value.to_lowercase(); + lower.starts_with("bearer ") || lower.starts_with("basic ") || value.len() > 20 +} + fn redact_json_secrets(value: Value) -> Value { match value { Value::Array(values) => Value::Array(values.into_iter().map(redact_json_secrets).collect()), @@ -3849,6 +3881,9 @@ fn merge_trigger_with_registration( if merged.target_method == default_pipe_target_method() { merged.target_method = registration.target_method.clone(); } + if merged.target_headers.is_none() { + merged.target_headers = registration.target_headers.clone(); + } if merged.field_mapping.is_none() { merged.field_mapping = registration.field_mapping.clone(); } @@ -3908,6 +3943,7 @@ async fn send_trigger_pipe_request( url: &str, method: &str, payload: &Value, + headers: &Option>, ) -> Result<(u16, Value)> { let method = reqwest::Method::from_bytes(method.as_bytes()) .with_context(|| format!("invalid target_method '{}'", method))?; @@ -3916,9 +3952,14 @@ async fn send_trigger_pipe_request( .build() .context("building trigger_pipe http client")?; - let response = client - .request(method, url) - .json(payload) + let mut request = client.request(method, url).json(payload); + if let Some(ref hdrs) = headers { + for (key, value) in hdrs { + request = request.header(key.as_str(), value.as_str()); + } + } + + let response = request .send() .await .with_context(|| format!("sending trigger_pipe request to {}", url))?; @@ -4890,8 +4931,13 @@ async fn handle_trigger_pipe( .map_err(|e| anyhow::anyhow!(e)) } } else { - send_trigger_pipe_request(&target_value, &resolved.target_method, &mapped_data) - .await + send_trigger_pipe_request( + &target_value, + &resolved.target_method, + &mapped_data, + &resolved.target_headers, + ) + .await } } "container" => { @@ -10020,6 +10066,7 @@ mod tests { target_container: None, target_endpoint: "/runtime/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: Some(json!({ "email": "$.user.email" })), trigger_type: "webhook".into(), }); @@ -10085,6 +10132,7 @@ mod tests { target_container: None, target_endpoint: "/runtime/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "webhook".into(), }); @@ -10146,6 +10194,7 @@ mod tests { target_container: None, target_endpoint: "/runtime/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "poll".into(), }); @@ -10269,6 +10318,7 @@ mod tests { target_container: None, target_endpoint: "/runtime/pipe".into(), target_method: "POST".into(), + target_headers: None, field_mapping: None, trigger_type: "rabbitmq".into(), }); @@ -10326,6 +10376,7 @@ mod tests { target_container: None, target_endpoint: "/target".into(), target_method: "POST".into(), + target_headers: None, field_mapping: Some(json!({ "subject": "$.subject" })), trigger_type: "manual".into(), }); From 8b5dba7e71429f5efdb38a60035e2c3ba2ab01f4 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 14:19:48 +0300 Subject: [PATCH 16/19] feat: add target_headers support for authenticated pipe delivery --- src/commands/stacker.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 46ac8fa..157724c 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -10395,6 +10395,7 @@ mod tests { target_container: None, target_endpoint: default_pipe_target_endpoint(), target_method: default_pipe_target_method(), + target_headers: None, field_mapping: None, trigger_type: default_pipe_trigger_type(), }; From 3a414137e3876ed1fb2148eac8e359e111f79f73 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Mon, 20 Jul 2026 16:31:28 +0300 Subject: [PATCH 17/19] probe with curl --- src/agent/docker.rs | 30 ++++++++++- src/commands/stacker.rs | 108 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/src/agent/docker.rs b/src/agent/docker.rs index 399b973..c200d5a 100644 --- a/src/agent/docker.rs +++ b/src/agent/docker.rs @@ -1,5 +1,5 @@ #![cfg(feature = "docker")] -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use bollard::container::LogOutput; use bollard::exec::CreateExecOptions; use bollard::models::{ContainerStatsResponse, ContainerSummaryStateEnum}; @@ -220,6 +220,34 @@ pub async fn resolve_container_name(name: &str) -> Result { Ok(name.to_string()) } +pub async fn get_container_port(name: &str) -> Result { + let docker = docker_client()?; + let opts: Option = + Some(ListContainersOptionsBuilder::default().all(true).build()); + let list = with_docker_timeout("list_containers", docker.list_containers(opts)).await?; + + let resolved = resolve_container_name(name) + .await + .unwrap_or_else(|_| name.to_string()); + + for container in &list { + if let Some(names) = &container.names { + for entry in names { + let normalized = entry.trim_start_matches('/'); + if normalized == resolved { + if let Some(ports) = &container.ports { + for port in ports { + return Ok(port.private_port); + } + } + } + } + } + } + + bail!("no exposed port found for container '{}'", name) +} + pub async fn list_containers() -> Result> { let docker = docker_client()?; let opts: Option = diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 157724c..13c8656 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -708,6 +708,64 @@ mod trigger_pipe_handler_tests { Some(&json!("failed")) ); } + + #[tokio::test] + async fn send_trigger_pipe_container_request_via_http_delivers_payload() { + let mut server = Server::new_async().await; + let mock = server + .mock("POST", "/webhook/pipe") + .match_body(Matcher::Exact(r#"{"email":"dev@try.direct"}"#.into())) + .with_status(200) + .with_header("content-type", "application/json") + .with_body(r#"{"accepted":true}"#) + .create_async() + .await; + + let url = format!("{}/webhook/pipe", server.url()); + let (status, body) = super::send_trigger_pipe_container_request_via_http( + &url, + "POST", + &json!({ "email": "dev@try.direct" }), + &None, + ) + .await + .expect("container_http request should succeed"); + + mock.assert_async().await; + assert_eq!(status, 200); + assert_eq!(body, json!({ "accepted": true })); + } + + #[tokio::test] + async fn send_trigger_pipe_container_request_via_http_sends_custom_headers() { + let mut server = Server::new_async().await; + let mock = server + .mock("POST", "/api/v1/conversations") + .match_header("X-Custom", "test-value") + .match_header("Content-Type", "application/json") + .with_status(201) + .with_header("content-type", "application/json") + .with_body(r#"{"id":1}"#) + .create_async() + .await; + + let url = format!("{}/api/v1/conversations", server.url()); + let mut headers = std::collections::HashMap::new(); + headers.insert("X-Custom".to_string(), "test-value".to_string()); + + let (status, body) = super::send_trigger_pipe_container_request_via_http( + &url, + "POST", + &json!({ "message": "hello" }), + &Some(headers), + ) + .await + .expect("container_http request with headers should succeed"); + + mock.assert_async().await; + assert_eq!(status, 201); + assert_eq!(body, json!({ "id": 1 })); + } } impl std::fmt::Display for ContainerRuntime { @@ -4100,6 +4158,45 @@ async fn send_trigger_pipe_container_request( bail!("target_container requires docker feature") } +async fn send_trigger_pipe_container_request_via_http( + url: &str, + method: &str, + payload: &Value, + headers: &Option>, +) -> Result<(u16, Value)> { + let method = reqwest::Method::from_bytes(method.as_bytes()) + .with_context(|| format!("invalid target_method '{}'", method))?; + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .build() + .context("building container_http client")?; + + let mut request = client.request(method, url).json(payload); + if let Some(ref hdrs) = headers { + for (key, value) in hdrs { + request = request.header(key.as_str(), value.as_str()); + } + } + + let response = request + .send() + .await + .with_context(|| format!("sending container_http request to {}", url))?; + + let status = response.status().as_u16(); + let body_text = response + .text() + .await + .context("reading container_http response body")?; + let body = if body_text.trim().is_empty() { + Value::Null + } else { + serde_json::from_str(&body_text).unwrap_or(Value::String(body_text)) + }; + + Ok((status, body)) +} + async fn run_poll_source_worker( runtime: PipeRuntime, key: PipeRuntimeKey, @@ -4941,11 +5038,18 @@ async fn handle_trigger_pipe( } } "container" => { - send_trigger_pipe_container_request( - &target_value, + let port = crate::agent::docker::get_container_port(&target_value) + .await + .unwrap_or(80); + let container_url = build_pipe_target_url( + &format!("http://{}:{}", target_value, port), &resolved.target_endpoint, + ); + send_trigger_pipe_container_request_via_http( + &container_url, &resolved.target_method, &mapped_data, + &resolved.target_headers, ) .await } From 2c93f5b40274ad636fb2291a8fee5bff877b3f96 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Sat, 1 Aug 2026 15:40:29 +0200 Subject: [PATCH 18/19] clippy error fix --- src/agent/docker.rs | 2 +- src/commands/stacker.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/agent/docker.rs b/src/agent/docker.rs index c200d5a..ecae4ff 100644 --- a/src/agent/docker.rs +++ b/src/agent/docker.rs @@ -236,7 +236,7 @@ pub async fn get_container_port(name: &str) -> Result { let normalized = entry.trim_start_matches('/'); if normalized == resolved { if let Some(ports) = &container.ports { - for port in ports { + if let Some(port) = ports.iter().next() { return Ok(port.private_port); } } diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 13c8656..5505e25 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -3974,6 +3974,7 @@ fn build_http_status_probe_command(url: &str, timeout_secs: u32) -> String { } #[cfg(feature = "docker")] +#[allow(dead_code)] fn build_trigger_pipe_container_command(endpoint: &str, method: &str, payload: &Value) -> String { let json_payload = serde_json::to_string(payload).unwrap_or_else(|_| "{}".to_string()); let escaped_payload = shell_escape_single_quotes(&json_payload); @@ -4111,6 +4112,7 @@ async fn fetch_trigger_pipe_source_request( } #[cfg(feature = "docker")] +#[allow(dead_code)] async fn send_trigger_pipe_container_request( container: &str, endpoint: &str, @@ -4149,6 +4151,7 @@ async fn send_trigger_pipe_container_request( } #[cfg(not(feature = "docker"))] +#[allow(dead_code)] async fn send_trigger_pipe_container_request( _container: &str, _endpoint: &str, From 345ec12bd3c650734499c23fd0ac2208f3273295 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Sat, 1 Aug 2026 21:28:41 +0200 Subject: [PATCH 19/19] fix: gate docker-only container target behind cfg(feature = "docker") --- src/commands/stacker.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/commands/stacker.rs b/src/commands/stacker.rs index 5505e25..b69081e 100644 --- a/src/commands/stacker.rs +++ b/src/commands/stacker.rs @@ -4161,6 +4161,7 @@ async fn send_trigger_pipe_container_request( bail!("target_container requires docker feature") } +#[cfg(feature = "docker")] async fn send_trigger_pipe_container_request_via_http( url: &str, method: &str, @@ -5040,6 +5041,7 @@ async fn handle_trigger_pipe( .await } } + #[cfg(feature = "docker")] "container" => { let port = crate::agent::docker::get_container_port(&target_value) .await @@ -5056,6 +5058,8 @@ async fn handle_trigger_pipe( ) .await } + #[cfg(not(feature = "docker"))] + "container" => Err(anyhow::anyhow!("container target requires docker feature")), _ => unreachable!(), };