From 7bc4be6bcef6d420ae89d242edb06cac3498a8fa Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Fri, 05 Jun 2026 03:33:53 +0000 Subject: [PATCH] feat: remowt integration --- --- a/Cargo.lock +++ b/Cargo.lock @@ -3,15 +3,6 @@ version = 4 [[package]] -name = "abort-on-drop" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd6d700ad9af641490c1f7d67980d2de4d1433016e5b12f819448d3c832142a" -dependencies = [ - "tokio", -] - -[[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -23,54 +14,91 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", - "generic-array", + "crypto-common 0.1.7", + "generic-array 0.14.7", ] [[package]] +name = "aead" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef60ac202874e574ce7a7158cc8bca7313dd344322482e4fadee288bf4a306b8" +dependencies = [ + "crypto-common 0.2.2", + "inout 0.2.2", +] + +[[package]] name = "aes" version = "0.8.4" 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", + "aes 0.9.1", + "cipher 0.5.2", + "ctr 0.10.1", + "ghash 0.6.0", + "subtle", + "zeroize", +] + +[[package]] name = "age" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf640be7658959746f1f0f2faab798f6098a9436a8e18e148d18bc9875e13c4b" +checksum = "a07d86e4272c093c88caf7864a2d09af52a5159180848ca4832a3cdbd7d014d5" dependencies = [ - "aes", - "aes-gcm", + "aes 0.8.4", + "aes-gcm 0.10.3", "age-core", "base64 0.21.7", - "bcrypt-pbkdf", + "bcrypt-pbkdf 0.10.0", "bech32", - "cbc", + "cbc 0.1.2", "chacha20poly1305", - "cipher", + "cipher 0.4.4", "cookie-factory", - "ctr", - "curve25519-dalek", - "hmac", + "ctr 0.9.2", + "curve25519-dalek 4.1.3", + "hmac 0.12.1", "i18n-embed", "i18n-embed-fl", "lazy_static", @@ -78,10 +106,10 @@ "num-traits", "pin-project", "rand 0.8.6", - "rsa", + "rsa 0.9.10", "rust-embed", - "scrypt", - "sha2", + "scrypt 0.11.0", + "sha2 0.10.9", "subtle", "which", "wsl", @@ -98,12 +126,12 @@ "base64 0.21.7", "chacha20poly1305", "cookie-factory", - "hkdf", + "hkdf 0.12.4", "io_tee", "nom 7.1.3", "rand 0.8.6", "secrecy", - "sha2", + "sha2 0.10.9", "tempfile", ] @@ -191,34 +219,144 @@ ] [[package]] +name = "argon2" +version = "0.6.0-rc.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures 0.3.0", + "password-hash", +] + +[[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "async-stream" -version = "0.3.6" +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ - "async-stream-impl", + "concurrent-queue", + "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] -name = "async-stream-impl" -version = "0.3.6" +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", ] [[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -226,10 +364,19 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] +name = "async_fn_traits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc58d489c5f2d2c5be31b9004cec7af25a70d23df4d8111715eee736234cf217" +dependencies = [ + "paste", +] + +[[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -237,56 +384,15 @@ [[package]] name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "axum" -version = "0.7.9" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower 0.5.3", - "tower-layer", - "tower-service", -] +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] -name = "axum-core" -version = "0.4.5" +name = "base16ct" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", -] +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" [[package]] name = "base64" @@ -321,9 +427,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" dependencies = [ - "blowfish", - "pbkdf2", - "sha2", + "blowfish 0.9.1", + "pbkdf2 0.12.2", + "sha2 0.10.9", +] + +[[package]] +name = "bcrypt-pbkdf" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144e573728da132683b9488acd528274c790e07fc06ff81ee29f9d8f8b1041e0" +dependencies = [ + "blowfish 0.10.0", + "pbkdf2 0.13.0", + "sha2 0.11.0", ] [[package]] @@ -333,14 +450,45 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] -name = "better-command" -version = "0.1.0" +name = "bifrostlink" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb01af731c11dd31b23783a83a36a29f644cc1972481f6fa4f4fabc709079eb" dependencies = [ - "regex", + "async-trait", + "async_fn_traits", + "bifrostlink-macros", + "bytes", + "derivative", + "futures", "serde", "serde_json", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "bifrostlink-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4b7a5fb38b36bd81910c17ebf369f9296e508d92b1277a768a63c8a2254fdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "bifrostlink-ports" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "977acfcb8ed3c24ab7c2f76fb3eeebff1533c72708733ce6020f2501980b7cf2" +dependencies = [ + "bifrostlink", + "bytes", + "tokio", "tracing", - "tracing-indicatif", ] [[package]] @@ -359,15 +507,24 @@ "quote", "regex", "rustc-hash 2.1.2", - "shlex", - "syn", + "shlex 1.3.0", + "syn 2.0.117", ] [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "blake2" +version = "0.11.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690" +dependencies = [ + "digest 0.11.3", +] [[package]] name = "block-buffer" @@ -375,33 +532,85 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", + "zeroize", +] + +[[package]] name = "block-padding" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "generic-array", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] name = "blowfish" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "blowfish" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ce3946557b35e71d1bbe07ec385073ce9eda05043f95de134eb578fcf1a298" +dependencies = [ + "byteorder", + "cipher 0.5.2", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", ] [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytecount" @@ -422,22 +631,40 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] name = "cbc" 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]] name = "cc" -version = "1.2.60" +version = "1.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -468,7 +695,7 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] @@ -479,8 +706,10 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", + "cipher 0.5.2", "cpufeatures 0.3.0", "rand_core 0.10.1", + "zeroize", ] [[package]] @@ -489,18 +718,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead", + "aead 0.5.2", "chacha20 0.9.1", - "cipher", - "poly1305", + "cipher 0.4.4", + "poly1305 0.8.0", "zeroize", ] [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -516,8 +745,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", - "inout", + "crypto-common 0.1.7", + "inout 0.1.4", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", + "inout 0.2.2", "zeroize", ] @@ -559,9 +800,9 @@ [[package]] name = "clap_complete" -version = "4.6.2" +version = "4.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff7a1dccbdd8b078c2bdebff47e404615151534d5043da397ec50286816f9cb" +checksum = "e0a7a9bfdb35811f9e59832f0f05975114d2251b415fb534108e6f34060fd772" dependencies = [ "clap", ] @@ -575,7 +816,7 @@ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -585,6 +826,12 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] name = "codespan-reporting" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -602,6 +849,15 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] name = "console" version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -615,9 +871,9 @@ [[package]] name = "const-hex" -version = "1.18.1" +version = "1.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -632,6 +888,12 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] name = "cookie-factory" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -657,6 +919,12 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + +[[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -684,23 +952,86 @@ ] [[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-bigint" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3" +dependencies = [ + "cpubits", + "ctutils", + "getrandom 0.4.2", + "hybrid-array", + "num-traits", + "rand_core 0.10.1", + "serdect", + "subtle", + "zeroize", +] + +[[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] [[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "getrandom 0.4.2", + "hybrid-array", + "rand_core 0.10.1", +] + +[[package]] +name = "crypto-primes" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" +dependencies = [ + "crypto-bigint", + "rand_core 0.10.1", +] + +[[package]] name = "ctr" 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.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", + "subtle", ] [[package]] @@ -718,14 +1049,30 @@ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "digest", - "fiat-crypto", + "digest 0.10.7", + "fiat-crypto 0.2.9", "rustc_version", "subtle", "zeroize", ] [[package]] +name = "curve25519-dalek" +version = "5.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bf" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto 0.3.0", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] name = "curve25519-dalek-derive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -733,7 +1080,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -759,11 +1106,11 @@ dependencies = [ "cc", "codespan-reporting", - "indexmap 2.14.0", + "indexmap", "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.117", ] [[package]] @@ -774,10 +1121,10 @@ dependencies = [ "clap", "codespan-reporting", - "indexmap 2.14.0", + "indexmap", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -792,10 +1139,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" dependencies = [ - "indexmap 2.14.0", + "indexmap", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -804,7 +1168,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", + "zeroize", +] + +[[package]] +name = "der" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +dependencies = [ + "const-oid 0.10.2", + "pem-rfc7468", "zeroize", ] @@ -813,9 +1188,25 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "des" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a94e407b54f9034d71dd748234cd1e516ced6284009906ae246f177eafe5a" dependencies = [ - "powerfmt", - "serde_core", + "cipher 0.5.2", ] [[package]] @@ -824,21 +1215,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", "subtle", ] [[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid 0.10.2", + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[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.3", + "elliptic-curve", + "rfc6979", + "signature 3.0.0", + "spki 0.8.0", + "zeroize", ] [[package]] @@ -847,29 +1265,78 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", - "signature", + "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", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "serde", - "sha2", + "sha2 0.10.9", "subtle", "zeroize", ] [[package]] +name = "ed25519-dalek" +version = "3.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60" +dependencies = [ + "curve25519-dalek 5.0.0-rc.0", + "ed25519 3.0.0", + "rand_core 0.10.1", + "serde", + "sha2 0.11.0", + "signature 3.0.0", + "subtle", + "zeroize", +] + +[[package]] name = "either" -version = "1.15.0" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.14.0-rc.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "102d3643d30dd8b559613c5cced68317199597fffb278cdc88daa2ef7fafc935" +dependencies = [ + "base16ct", + "crypto-bigint", + "crypto-common 0.2.2", + "digest 0.11.3", + "ff", + "group", + "hkdf 0.13.0", + "hybrid-array", + "once_cell", + "pem-rfc7468", + "pkcs8 0.11.0", + "rand_core 0.10.1", + "sec1", + "subtle", + "zeroize", +] [[package]] name = "encode_unicode" @@ -878,6 +1345,45 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] name = "env_filter" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -915,18 +1421,55 @@ ] [[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] +name = "ff" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" +dependencies = [ + "rand_core 0.10.1", + "subtle", +] + +[[package]] name = "fiat-crypto" 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 = "find-crate" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -940,12 +1483,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" @@ -961,42 +1498,27 @@ name = "fleet" version = "0.2.0" dependencies = [ - "abort-on-drop", - "age", - "age-core", "anyhow", - "async-trait", - "base64 0.22.1", - "better-command", - "chrono", + "camino", "clap", "clap_complete", "fleet-base", - "fleet-shared", "futures", - "hostname", "human-repr", "indicatif", "itertools 0.14.0", "nix-eval", - "nixlike", - "nom 8.0.0", - "openssh", "opentelemetry", "opentelemetry-appender-tracing", "opentelemetry-exporter-env", "opentelemetry_sdk", - "peg", - "regex", + "remowt-fleet", "serde", "serde_json", - "shlex", + "shlex 1.3.0", "tabled", "tempfile", - "thiserror 2.0.18", - "time", "tokio", - "tokio-util", "tracing", "tracing-indicatif", "tracing-opentelemetry", @@ -1009,7 +1531,7 @@ dependencies = [ "age", "anyhow", - "better-command", + "camino", "chrono", "clap", "fleet-shared", @@ -1020,17 +1542,18 @@ "nix-eval", "nixlike", "nom 8.0.0", - "openssh", "rand 0.10.1", + "remowt-client", + "remowt-endpoints", + "remowt-fleet", + "remowt-link-shared", + "remowt-ui-prompt", "serde", "serde_json", "tabled", "tempfile", - "thiserror 2.0.18", "time", "tokio", - "tokio-util", - "toml_edit", "tracing", ] @@ -1042,13 +1565,13 @@ "anyhow", "base64 0.22.1", "clap", - "ed25519-dalek", + "ed25519-dalek 2.2.0", "fleet-shared", "hex", - "hmac", - "pbkdf2", + "hmac 0.12.1", + "pbkdf2 0.12.2", "rand 0.10.1", - "sha2", + "sha2 0.10.9", "x25519-dalek", ] @@ -1197,6 +1720,19 @@ checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] name = "futures-macro" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1204,7 +1740,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1247,6 +1783,17 @@ ] [[package]] +name = "generic-array" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351" +dependencies = [ + "generic-array 0.14.7", + "rustversion", + "typenum", +] + +[[package]] name = "getrandom" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1280,11 +1827,13 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -1294,20 +1843,53 @@ 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]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "group" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" +dependencies = [ + "ff", + "rand_core 0.10.1", + "subtle", +] + +[[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", @@ -1315,7 +1897,7 @@ "futures-core", "futures-sink", "http", - "indexmap 2.14.0", + "indexmap", "slab", "tokio", "tokio-util", @@ -1324,12 +1906,6 @@ [[package]] name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" @@ -1339,9 +1915,9 @@ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1350,27 +1926,57 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] +name = "hex-literal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" + +[[package]] name = "hkdf" 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]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "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.3", ] [[package]] @@ -1395,9 +2001,9 @@ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1433,22 +2039,28 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "httpdate" -version = "1.0.3" +name = "human-repr" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1" [[package]] -name = "human-repr" -version = "1.1.0" +name = "hybrid-array" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "ctutils", + "subtle", + "typenum", + "zeroize", +] [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -1458,7 +2070,6 @@ "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1512,7 +2123,7 @@ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2", "tokio", "tower-service", "tracing", @@ -1567,7 +2178,7 @@ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.117", "unic-langid", ] @@ -1581,7 +2192,7 @@ "i18n-config", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1709,9 +2320,9 @@ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1719,22 +2330,12 @@ [[package]] name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -1768,11 +2369,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "block-padding", - "generic-array", + "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 = "internal-russh-num-bigint" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8" +dependencies = [ + "num-integer", + "num-traits", + "rand 0.10.1", + "rand_core 0.10.1", +] + +[[package]] name = "intl-memoizer" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1802,16 +2425,6 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] [[package]] name = "is_terminal_polyfill" @@ -1845,17 +2458,36 @@ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] [[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 = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1872,9 +2504,9 @@ [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -1893,6 +2525,18 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "bitflags", + "libc", + "plain", + "redox_syscall 0.8.1", +] + +[[package]] name = "link-cplusplus" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1936,9 +2580,9 @@ [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "lru-slab" @@ -1956,22 +2600,25 @@ ] [[package]] -name = "matchit" -version = "0.7.3" +name = "md5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] -name = "mime" -version = "0.3.17" +name = "memoffset" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] [[package]] name = "minimal-lexical" @@ -1991,9 +2638,9 @@ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -2001,16 +2648,35 @@ ] [[package]] -name = "multimap" -version = "0.10.1" +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 = "module-lattice" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" +checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe" +dependencies = [ + "ctutils", + "hybrid-array", + "num-traits", +] [[package]] name = "nix" -version = "0.31.2" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags", "cfg-if", @@ -2024,15 +2690,16 @@ dependencies = [ "anyhow", "bindgen", + "camino", "cxx", "cxx-build", + "futures", "itertools 0.14.0", "nixlike", "pkg-config", "serde", "serde_json", "test-log", - "thiserror 2.0.18", "tokio", "tracing", "tracing-indicatif", @@ -2040,6 +2707,13 @@ ] [[package]] +name = "nix-plugin-fleet" +version = "0.1.0" +dependencies = [ + "fleet-base", +] + +[[package]] name = "nixlike" version = "0.1.0" dependencies = [ @@ -2047,8 +2721,6 @@ "linked-hash-map", "peg", "serde", - "serde-transcode", - "serde_json", "thiserror 2.0.18", ] @@ -2081,6 +2753,16 @@ ] [[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] name = "num-bigint-dig" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2098,9 +2780,9 @@ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -2151,20 +2833,6 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] -name = "openssh" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d534c4bfecb0ed71dea4db444a5922a294d15cf40e700548f27295e1feb0ef18" -dependencies = [ - "libc", - "once_cell", - "shell-escape", - "tempfile", - "thiserror 2.0.18", - "tokio", -] - -[[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2229,12 +2897,12 @@ "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", - "prost 0.14.3", + "prost", "reqwest", "serde_json", "thiserror 2.0.18", "tokio", - "tonic 0.14.5", + "tonic", "tracing", ] @@ -2248,10 +2916,10 @@ "const-hex", "opentelemetry", "opentelemetry_sdk", - "prost 0.14.3", + "prost", "serde", "serde_json", - "tonic 0.14.5", + "tonic", "tonic-prost", ] @@ -2271,6 +2939,77 @@ ] [[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "p256" +version = "0.14.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41adc63effe99d48837a8cc0e6d7a77e32ae6a07f6000df466178dbc2193093e" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p384" +version = "0.14.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd5333afa5ae0347f39e6a0f2c9c155da431583fd71fe5555bd0521b4ccaf02" +dependencies = [ + "ecdsa", + "elliptic-curve", + "fiat-crypto 0.3.0", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p521" +version = "0.14.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a5297f53dc16d35909060ba3032cff7867e8809f01e273ff325579d5f0ceae" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "pageant" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3a5ae18f65a85c67a77d18d42d3606c07948e3c17c1e5f74852b26589e88a5" +dependencies = [ + "base16ct", + "byteorder", + "bytes", + "delegate", + "futures", + "log", + "rand 0.10.1", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "windows", + "windows-strings", +] + +[[package]] name = "papergrid" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2282,6 +3021,12 @@ ] [[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] name = "parking_lot" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2299,26 +3044,51 @@ dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link", ] [[package]] +name = "password-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b" +dependencies = [ + "phc", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] name = "pbkdf2" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", - "hmac", + "digest 0.10.7", + "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.3", + "hmac 0.13.0", ] [[package]] name = "peg" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477" +checksum = "0aad070be5b63aa72103f2fcdd70a83adbd5e90112ce5b574171ff1c65501773" dependencies = [ "peg-macros", "peg-runtime", @@ -2326,9 +3096,9 @@ [[package]] name = "peg-macros" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71" +checksum = "ddd8ef6825cae95355031ae26a99b616a2a21f22ba2de0197c43dfb05acbe7ee" dependencies = [ "peg-runtime", "proc-macro2", @@ -2337,18 +3107,17 @@ [[package]] name = "peg-runtime" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" +checksum = "7011d97b484a5ebdc4b1fdb3b12d5e4bbbea56e9d22b688f2e79e04b65a7d8a6" [[package]] -name = "pem" -version = "3.0.6" +name = "pem-rfc7468" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" dependencies = [ - "base64 0.22.1", - "serde_core", + "base64ct", ] [[package]] @@ -2358,33 +3127,33 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "petgraph" -version = "0.7.1" +name = "phc" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892" dependencies = [ - "fixedbitset", - "indexmap 2.14.0", + "base64ct", + "ctutils", ] [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2394,33 +3163,102 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] name = "pkcs1" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.10", + "pkcs8 0.10.2", + "spki 0.7.3", ] [[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der 0.8.0", + "spki 0.8.0", +] + +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.10", + "spki 0.7.3", ] [[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der 0.8.0", + "pkcs5", + "rand_core 0.10.1", + "spki 0.8.0", +] + +[[package]] name = "pkg-config" version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] name = "poly1305" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2428,7 +3266,18 @@ dependencies = [ "cpufeatures 0.2.17", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "poly1305" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00baa632505d05512f48a963e16051c54fda9a95cc9acea1a4e3c90991c4a2e" +dependencies = [ + "cpufeatures 0.3.0", + "universal-hash 0.6.1", + "zeroize", ] [[package]] @@ -2440,7 +3289,18 @@ "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]] @@ -2480,10 +3340,42 @@ checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "primefield" +version = "0.14.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d7e42f46a29abc16fb621a3466ee453358ebaae48a9e515f287e0af052ed8f" +dependencies = [ + "crypto-bigint", + "crypto-common 0.2.2", + "ff", + "rand_core 0.10.1", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d2793f22b9b6fd11ef3ac1d59bf003c2573593e4968702341605c2748fd90bf" +dependencies = [ + "elliptic-curve", ] [[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] name = "proc-macro-error-attr2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2502,7 +3394,7 @@ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2531,77 +3423,25 @@ [[package]] name = "prost" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", - "prost-derive 0.13.5", -] - -[[package]] -name = "prost" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" -dependencies = [ - "bytes", - "prost-derive 0.14.3", + "prost-derive", ] [[package]] -name = "prost-build" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" -dependencies = [ - "heck", - "itertools 0.14.0", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost 0.13.5", - "prost-types", - "regex", - "syn", - "tempfile", -] - -[[package]] name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "prost-types" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" -dependencies = [ - "prost 0.13.5", + "syn 2.0.117", ] [[package]] @@ -2617,7 +3457,7 @@ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.6.3", + "socket2", "thiserror 2.0.18", "tokio", "tracing", @@ -2654,7 +3494,7 @@ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2", "tracing", "windows-sys 0.60.2", ] @@ -2766,32 +3606,28 @@ ] [[package]] -name = "rcgen" -version = "0.13.2" +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "pem", - "ring", - "rustls-pki-types", - "time", - "yasna", + "bitflags", ] [[package]] name = "redox_syscall" -version = "0.5.18" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -2812,18 +3648,131 @@ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "remowt-client" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d59c0ce311b4b3c094b0b196a1004aa438b9947c3eace2ca4d2a0ac08fac59" +dependencies = [ + "anyhow", + "bifrostlink", + "bifrostlink-ports", + "bytes", + "camino", + "futures", + "remowt-endpoints", + "remowt-link-shared", + "russh", + "russh-config", + "serde", + "serde_json", + "tempfile", + "tokio", + "tokio-util", + "tracing", + "uuid", +] + +[[package]] +name = "remowt-endpoints" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb203856cd5cff27504bd2023c59195b1f1ce1cf5d8ea836598993894eb1b9c0" +dependencies = [ + "anyhow", + "bifrostlink", + "bifrostlink-macros", + "camino", + "nix", + "serde", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tracing", + "uuid", + "zbus", +] + +[[package]] +name = "remowt-fleet" +version = "0.1.0" +dependencies = [ + "anyhow", + "bifrostlink", + "camino", + "nix-eval", + "remowt-client", + "remowt-endpoints", + "serde", + "thiserror 2.0.18", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "remowt-link-shared" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ebba5be028a2dfb7f1858bb0ff74037262753eaae4dc6e5d9ae241c4567668" +dependencies = [ + "bifrostlink", + "bytes", + "remowt-ui-prompt", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "remowt-plugin" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "ef73ef3eea5bb6152d01019b5062e51ef258a1ac1c7a6f8cdcdf6fe1eb192cdd" +dependencies = [ + "anyhow", + "bifrostlink", + "bifrostlink-ports", + "remowt-link-shared", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] [[package]] -name = "repl-plugin-unstable" +name = "remowt-plugin-fleet" version = "0.1.0" dependencies = [ - "fleet-base", + "anyhow", + "remowt-fleet", + "remowt-plugin", ] [[package]] +name = "remowt-ui-prompt" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf28ba68e6f8087657157695e9ab42b82a21130e77f6dffd27781062109325d" +dependencies = [ + "anyhow", + "bifrostlink", + "bifrostlink-macros", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "zbus", +] + +[[package]] name = "reqwest" version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2854,8 +3803,8 @@ "sync_wrapper", "tokio", "tokio-rustls", - "tower 0.5.3", - "tower-http 0.6.8", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", @@ -2864,6 +3813,16 @@ ] [[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", +] + +[[package]] name = "ring" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2878,45 +3837,155 @@ ] [[package]] -name = "rmp" -version = "0.8.15" +name = "rsa" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ + "const-oid 0.9.6", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", "num-traits", + "pkcs1 0.7.5", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "signature 2.2.0", + "spki 0.7.3", + "subtle", + "zeroize", ] [[package]] -name = "rmp-serde" -version = "1.3.1" +name = "rsa" +version = "0.10.0-rc.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf" dependencies = [ - "rmp", - "serde", + "const-oid 0.10.2", + "crypto-bigint", + "crypto-primes", + "digest 0.11.3", + "pkcs1 0.8.0-rc.4", + "pkcs8 0.11.0", + "rand_core 0.10.1", + "sha2 0.11.0", + "signature 3.0.0", + "spki 0.8.0", + "zeroize", ] [[package]] -name = "rsa" -version = "0.9.10" +name = "russh" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +checksum = "bbf893f64684e58da8a68d56a5e84d1cf0440226274c515770fe267707a7d0b0" dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", + "aes 0.9.1", + "bitflags", + "block-padding 0.4.2", + "byteorder", + "bytes", + "cbc 0.2.1", + "cipher 0.5.2", + "crypto-bigint", + "ctr 0.10.1", + "curve25519-dalek 5.0.0-rc.0", + "data-encoding", + "delegate", + "der 0.8.0", + "digest 0.11.3", + "ecdsa", + "ed25519-dalek 3.0.0-rc.0", + "elliptic-curve", + "enum_dispatch", + "flate2", + "futures", + "generic-array 1.4.3", + "getrandom 0.4.2", + "ghash 0.6.0", + "hex-literal", + "hmac 0.13.0", + "inout 0.2.2", + "internal-russh-num-bigint", + "keccak", + "log", + "md5", + "ml-kem", + "module-lattice", + "num-bigint", + "p256", + "p384", + "p521", + "pageant", + "pbkdf2 0.13.0", + "pkcs1 0.8.0-rc.4", + "pkcs5", + "pkcs8 0.11.0", + "polyval 0.7.1", + "rand 0.10.1", + "rand_core 0.10.1", + "ring", + "rsa 0.10.0-rc.18", + "russh-cryptovec", + "russh-util", + "salsa20 0.11.0", + "scrypt 0.12.0", + "sec1", + "sha1", + "sha2 0.11.0", + "sha3", + "signature 3.0.0", + "spki 0.8.0", + "ssh-encoding", + "ssh-key", "subtle", + "thiserror 2.0.18", + "tokio", + "typenum", + "universal-hash 0.6.1", "zeroize", ] [[package]] +name = "russh-config" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993346101d4741a59c82bafc6b645c52ce3e7b0820f13e4b5d48cdb591e3d955" +dependencies = [ + "futures", + "globset", + "log", + "thiserror 2.0.18", + "tokio", + "whoami", +] + +[[package]] +name = "russh-cryptovec" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443f6bbcfacb34a1aab2b12b99bf08e0c63abdc5a0db261901365df9d57fff51" +dependencies = [ + "log", + "nix", + "ssh-encoding", + "windows-sys 0.61.2", +] + +[[package]] +name = "russh-util" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668424a5dde0bcb45b55ba7de8476b93831b4aa2fa6947e145f3b053e22c60b6" +dependencies = [ + "chrono", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] name = "rust-embed" version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2936,7 +4005,7 @@ "proc-macro2", "quote", "rust-embed-utils", - "syn", + "syn 2.0.117", "walkdir", ] @@ -2946,7 +4015,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" dependencies = [ - "sha2", + "sha2 0.10.9", "walkdir", ] @@ -2999,11 +4068,10 @@ [[package]] name = "rustls" -version = "0.23.38" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ - "log", "once_cell", "ring", "rustls-pki-types", @@ -3014,30 +4082,21 @@ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -3072,7 +4131,17 @@ 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]] @@ -3111,12 +4180,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ - "pbkdf2", - "salsa20", - "sha2", + "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 = "sec1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" +dependencies = [ + "base16ct", + "ctutils", + "der 0.8.0", + "hybrid-array", + "subtle", + "zeroize", +] + +[[package]] name = "secrecy" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3177,28 +4272,9 @@ dependencies = [ "serde_core", "serde_derive", -] - -[[package]] -name = "serde-transcode" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590c0e25c2a5bb6e85bf5c1bce768ceb86b316e7a01bdf07d2cb4ec2271990e2" -dependencies = [ - "serde", ] [[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3215,14 +4291,14 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -3232,6 +4308,17 @@ ] [[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3244,6 +4331,27 @@ ] [[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] name = "sha2" version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3251,10 +4359,31 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", ] [[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak", +] + +[[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3264,16 +4393,16 @@ ] [[package]] -name = "shell-escape" -version = "0.1.5" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook-registry" @@ -3291,11 +4420,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] [[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core 0.10.1", +] + +[[package]] name = "simd-adler32" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3309,25 +4448,15 @@ [[package]] name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.5.10" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -3346,10 +4475,81 @@ checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.10", +] + +[[package]] +name = "spki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" +dependencies = [ + "base64ct", + "der 0.8.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", + "aes 0.9.1", + "aes-gcm 0.11.0-rc.4", + "cbc 0.2.1", + "chacha20 0.10.0", + "cipher 0.5.2", + "ctr 0.10.1", + "ctutils", + "des", + "poly1305 0.9.0", + "ssh-encoding", + "zeroize", ] [[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", + "ctutils", + "digest 0.11.3", + "pem-rfc7468", + "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 0.11.0", + "ctutils", + "ed25519-dalek 3.0.0-rc.0", + "hex", + "hmac 0.13.0", + "p256", + "p384", + "p521", + "rand_core 0.10.1", + "rsa 0.10.0-rc.18", + "sec1", + "sha1", + "sha2 0.11.0", + "signature 3.0.0", + "ssh-cipher", + "ssh-encoding", + "zeroize", +] + +[[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3369,6 +4569,17 @@ [[package]] name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" @@ -3395,7 +4606,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3419,7 +4630,7 @@ "proc-macro-error2", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3452,24 +4663,13 @@ dependencies = [ "rustix 1.1.4", "windows-sys 0.61.2", -] - -[[package]] -name = "terraform-provider-fleet" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "serde", - "tf-provider", - "tokio", ] [[package]] name = "test-log" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f46bf474f0a4afebf92f076d54fd5e63423d9438b8c278a3d2ccb0f47f7cdb3" +checksum = "9b9c218384242b5c89b68303ab6f6fc53a312d923f0c14dc6bb860c6aeee40f1" dependencies = [ "env_logger", "test-log-macros", @@ -3478,22 +4678,22 @@ [[package]] name = "test-log-core" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d4d41320b48bc4a211a9021678fcc0c99569b594ea31c93735b8e517102b4c" +checksum = "c26ef8b00e4d382e59f6a8ddb3cd790b3a5bb29f21a358a9a69ea2f29f13f27b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "test-log-macros" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9beb9249a81e430dffd42400a49019bcf548444f1968ff23080a625de0d4d320" +checksum = "944ad38adcbb71eaa682c56bceeb079e4ca82b4b3edc2a0fde5cb297b77dac8d" dependencies = [ - "syn", + "syn 2.0.117", "test-log-core", ] @@ -3504,34 +4704,6 @@ checksum = "0f8daae29995a24f65619e19d8d31dea5b389f3d853d8bf297bbf607cd0014cc" dependencies = [ "unicode-width", -] - -[[package]] -name = "tf-provider" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80ea2e5f9f54717952d199888aab7e607dc99275ec5221f1259ce7a5f55f5a6" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "base64 0.22.1", - "futures", - "prost 0.13.5", - "rcgen", - "rmp-serde", - "serde", - "serde_bytes", - "serde_json", - "time", - "tokio", - "tokio-stream", - "tokio-util", - "tonic 0.12.3", - "tonic-build", - "tower-http 0.5.2", - "tracing", - "tracing-subscriber", ] [[package]] @@ -3560,7 +4732,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3571,7 +4743,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3585,9 +4757,9 @@ [[package]] name = "time" -version = "0.3.47" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" dependencies = [ "deranged", "num-conv", @@ -3599,15 +4771,15 @@ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" dependencies = [ "num-conv", "time-core", @@ -3641,16 +4813,16 @@ [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -3663,7 +4835,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3685,7 +4857,6 @@ "futures-core", "pin-project-lite", "tokio", - "tokio-util", ] [[package]] @@ -3712,24 +4883,23 @@ [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ - "indexmap 2.14.0", + "indexmap", "toml_datetime", "toml_parser", - "toml_writer", - "winnow 0.7.15", + "winnow", ] [[package]] @@ -3738,52 +4908,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", -] - -[[package]] -name = "toml_writer" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" - -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.22.1", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost 0.13.5", - "rustls-pemfile", - "socket2 0.5.10", - "tokio", - "tokio-rustls", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", + "winnow", ] [[package]] name = "tonic" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "base64 0.22.1", @@ -3800,55 +4932,21 @@ "sync_wrapper", "tokio", "tokio-stream", - "tower 0.5.3", + "tower", "tower-layer", "tower-service", "tracing", ] [[package]] -name = "tonic-build" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build", - "prost-types", - "quote", - "syn", -] - -[[package]] name = "tonic-prost" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" dependencies = [ "bytes", - "prost 0.14.3", - "tonic 0.14.5", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.6", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", + "prost", + "tonic", ] [[package]] @@ -3859,7 +4957,7 @@ dependencies = [ "futures-core", "futures-util", - "indexmap 2.14.0", + "indexmap", "pin-project-lite", "slab", "sync_wrapper", @@ -3872,37 +4970,20 @@ [[package]] name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags", - "bytes", - "http", - "http-body", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", - "tower 0.5.3", + "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -3936,7 +5017,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3989,16 +5070,6 @@ ] [[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] name = "tracing-subscriber" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4008,15 +5079,12 @@ "nu-ansi-term", "once_cell", "regex-automata", - "serde", - "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", - "tracing-serde", ] [[package]] @@ -4036,11 +5104,22 @@ [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4107,11 +5186,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "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 = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4142,6 +5231,18 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] +name = "uuid" +version = "1.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4204,9 +5305,9 @@ [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen 0.57.1", ] @@ -4221,10 +5322,16 @@ ] [[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -4235,9 +5342,9 @@ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ "js-sys", "wasm-bindgen", @@ -4245,9 +5352,9 @@ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4255,22 +5362,22 @@ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] @@ -4292,7 +5399,7 @@ checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.14.0", + "indexmap", "wasm-encoder", "wasmparser", ] @@ -4305,15 +5412,15 @@ dependencies = [ "bitflags", "hashbrown 0.15.5", - "indexmap 2.14.0", + "indexmap", "semver", ] [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -4342,6 +5449,17 @@ ] [[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", + "web-sys", +] + +[[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4351,6 +5469,27 @@ ] [[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] name = "windows-core" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4364,6 +5503,17 @@ ] [[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] name = "windows-implement" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4371,7 +5521,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4382,7 +5532,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4392,6 +5542,16 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4479,6 +5639,15 @@ ] [[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4576,20 +5745,14 @@ [[package]] name = "winnow" -version = "0.7.15" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] [[package]] -name = "winnow" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" - -[[package]] name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4623,9 +5786,9 @@ dependencies = [ "anyhow", "heck", - "indexmap 2.14.0", + "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4641,7 +5804,7 @@ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4654,7 +5817,7 @@ dependencies = [ "anyhow", "bitflags", - "indexmap 2.14.0", + "indexmap", "log", "serde", "serde_derive", @@ -4673,7 +5836,7 @@ dependencies = [ "anyhow", "id-arena", - "indexmap 2.14.0", + "indexmap", "log", "semver", "serde", @@ -4701,26 +5864,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "serde", "zeroize", ] [[package]] -name = "yasna" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" -dependencies = [ - "time", -] - -[[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -4735,35 +5889,96 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] [[package]] +name = "zbus" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix 1.1.4", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +dependencies = [ + "serde", + "winnow", + "zvariant", +] + +[[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -4776,28 +5991,28 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4831,7 +6046,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4839,3 +6054,43 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.117", + "winnow", +] --- a/Cargo.toml +++ b/Cargo.toml @@ -6,22 +6,28 @@ package.rust-version = "1.89.0" [workspace.dependencies] -better-command = { path = "./crates/better-command" } fleet-base = { path = "./crates/fleet-base" } fleet-shared = { path = "./crates/fleet-shared" } nix-eval = { path = "./crates/nix-eval" } nixlike = { path = "./crates/nixlike" } opentelemetry-exporter-env = { path = "./crates/opentelemetry-exporter-env" } +remowt-fleet = { path = "./crates/remowt-fleet" } -abort-on-drop = "0.2" +remowt-client = "0.1.3" +remowt-endpoints = "0.1.3" +remowt-link-shared = "0.1.3" +remowt-plugin = "0.1.3" +remowt-ui-prompt = "0.1.3" + +bifrostlink = "0.2.3" + +uuid = { version = "1", features = ["v4"] } + age = { version = "0.11", features = ["plugin", "ssh"] } -age-core = "0.11" anyhow = "1.0" -async-trait = "0.1.88" -axum = { version = "0.8.7", features = ["http2", "macros", "ws"] } -axum-extra = { version = "0.12.2", features = ["typed-header"] } base64 = "0.22.1" bindgen = "0.72.0" +camino = "1.2.2" chrono = { version = "0.4.41", features = ["serde"] } clap = { version = "4.5", features = ["derive", "env", "unicode", "wrap_help"] } clap_complete = "4.5" @@ -29,19 +35,16 @@ cxx-build = "1.0.168" ed25519-dalek = "2.1" futures = "0.3.31" -futures-util = { version = "0.3.31", features = ["sink"] } hex = "0.4.3" hmac = "0.12" hostname = "0.4.1" human-repr = "1.1" -hyper = "1.8.1" indicatif = "0.18" indoc = "2.0.6" itertools = "0.14.0" linked-hash-map = "0.5.6" nix = { version = "0.31.2", features = ["fs", "user"] } nom = "8.0.0" -openssh = "0.11.5" opentelemetry = "0.31.0" opentelemetry-appender-tracing = "0.31.1" opentelemetry-otlp = { version = "0.31.0", features = ["grpc-tonic", "gzip-tonic", "http-json", "reqwest-rustls"] } @@ -50,21 +53,16 @@ peg = "0.8.5" pkg-config = "0.3.30" rand = "0.10.0" -regex = "1.11" serde = { version = "1.0", features = ["derive"] } -serde-transcode = "1.1.1" serde_json = "1.0" sha2 = "0.10" shlex = "1.3" tabled = "0.20.0" tempfile = "3.20" test-log = { version = "0.2.19", features = ["trace"] } -tf-provider = "0.2.2" thiserror = "2.0.12" time = "0.3.41" tokio = { version = "1.45.1", features = ["fs", "macros", "rt", "rt-multi-thread", "sync", "time"] } -tokio-util = "0.7.17" -toml_edit = "0.23.10" tracing = "0.1" tracing-indicatif = "0.3.13" tracing-opentelemetry = "0.32.1" --- a/cmds/fleet/Cargo.toml +++ b/cmds/fleet/Cargo.toml @@ -8,15 +8,13 @@ default-run = "fleet" [dependencies] -age = { workspace = true, features = ["armor"] } anyhow.workspace = true -better-command.workspace = true +camino.workspace = true clap.workspace = true clap_complete.workspace = true fleet-base.workspace = true -fleet-shared.workspace = true nix-eval.workspace = true -nixlike.workspace = true +remowt-fleet.workspace = true serde.workspace = true serde_json.workspace = true tempfile.workspace = true @@ -24,30 +22,17 @@ tracing.workspace = true tracing-subscriber.workspace = true -abort-on-drop.workspace = true -age-core.workspace = true -async-trait.workspace = true -base64.workspace = true -chrono.workspace = true futures.workspace = true -hostname.workspace = true itertools.workspace = true -openssh.workspace = true -peg.workspace = true -regex.workspace = true shlex.workspace = true tabled.workspace = true -time = { workspace = true, features = ["serde"] } -tokio-util = { workspace = true, features = ["codec"] } human-repr = { workspace = true, optional = true } indicatif = { workspace = true, optional = true } -nom.workspace = true opentelemetry.workspace = true opentelemetry-appender-tracing.workspace = true opentelemetry-exporter-env.workspace = true opentelemetry_sdk.workspace = true -thiserror.workspace = true tracing-indicatif = { workspace = true, optional = true } tracing-opentelemetry.workspace = true @@ -58,6 +43,5 @@ "dep:tracing-indicatif", "dep:indicatif", "dep:human-repr", - "better-command/indicatif", "nix-eval/indicatif", ] --- a/cmds/fleet/src/cmds/build_systems.rs +++ b/cmds/fleet/src/cmds/build_systems.rs @@ -1,6 +1,7 @@ use std::{env::current_dir, os::unix::fs::symlink, path::PathBuf}; use anyhow::Result; +use camino::Utf8PathBuf; use clap::Parser; use fleet_base::{ deploy::{DeployAction, deploy_task, upload_task}, @@ -29,7 +30,7 @@ build_attr: String, } -async fn build_task(config: Config, hostname: String, build_attr: &str) -> Result { +async fn build_task(config: Config, hostname: String, build_attr: &str) -> Result { info!("building"); let host = config.host(&hostname)?; // let action = Action::from(self.subcommand.clone()); @@ -42,17 +43,20 @@ // We already have system profiles for backups. if !host.local { info!("adding gc root"); - let mut cmd = config.local_host().cmd("nix").await?; - cmd.arg("build") - .comparg( - "--profile", - format!( - "/nix/var/nix/profiles/{}-{hostname}", - config.data.gc_root_prefix - ), - ) - .arg(&out_output); - cmd.sudo().run_nix().await?; + let local = config.local_host(); + let plugin_id = local.ensure_nix_plugin().await?; + let nix = local + .remowt() + .await? + .plugin_endpoints::>(plugin_id); + let profile = format!( + "/nix/var/nix/profiles/{}-{hostname}", + config.data.gc_root_prefix + ); + nix.switch_profile(profile, out_output.clone()) + .await + .map_err(|e| anyhow::anyhow!("{e:?}"))? + .map_err(|e| anyhow::anyhow!("{e}"))?; } Ok(out_output) --- a/cmds/fleet/src/cmds/tf.rs +++ b/cmds/fleet/src/cmds/tf.rs @@ -1,6 +1,7 @@ -use std::{collections::BTreeMap, ffi::OsString, path::PathBuf}; +use std::{collections::BTreeMap, ffi::OsString}; use anyhow::{Context, Result}; +use camino::Utf8PathBuf; use clap::Parser; use fleet_base::host::Config; use nix_eval::nix_go; @@ -40,7 +41,7 @@ let system = &config.local_system; let config = &config.flake_outputs; let data = nix_go!(config.tf({ system })); - let data: PathBuf = spawn_blocking(move || data.build("out")) + let data: Utf8PathBuf = spawn_blocking(move || data.build("out")) .await .expect("tf.json derivation should not fail")?; let data = fs::read(&data).await?; --- a/cmds/fleet/src/main.rs +++ b/cmds/fleet/src/main.rs @@ -4,9 +4,10 @@ // pub(crate) mod command; pub(crate) mod extra_args; -use std::{env, ffi::OsString, process::ExitCode, sync::Arc}; +use std::{process::ExitCode, sync::Arc}; -use anyhow::{Result, bail}; +use anyhow::{Context as _, Result, bail}; +use camino::Utf8PathBuf; use clap::{CommandFactory, Parser}; use cmds::{ build_systems::{BuildSystems, Deploy}, @@ -23,7 +24,8 @@ #[cfg(feature = "indicatif")] use indicatif::{ProgressState, ProgressStyle}; use nix_eval::{ - gc_register_my_thread, gc_unregister_my_thread, init_libraries, init_tokio_for_nix, + add_file_to_store, gc_register_my_thread, gc_unregister_my_thread, init_libraries, + init_tokio_for_nix, }; use opentelemetry::trace::TracerProvider; use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; @@ -48,24 +50,21 @@ } let tasks = FuturesUnordered::new(); for entry in std::fs::read_dir(&prefetch_dir)? { - tasks.push(async { - let entry = entry?; - if !entry.metadata()?.is_file() { - bail!("only files should exist in prefetch directory"); - } - let span = info_span!( - "prefetching", - name = entry.file_name().to_string_lossy().as_ref() - ); - let mut path = OsString::new(); - path.push("file://"); - path.push(entry.path()); - - let mut status = config.local_host().cmd("nix").await?; - status.args(&config.nix_args); - status.arg("store").arg("prefetch-file").arg(path); - status.run_nix_string().instrument(span).await?; - Ok(()) + let entry = entry?; + if !entry.metadata()?.is_file() { + bail!("only files should exist in prefetch directory"); + } + let name = entry.file_name().to_string_lossy().into_owned(); + let path = + Utf8PathBuf::try_from(entry.path()).context("prefetch path should be utf8")?; + let span = info_span!("prefetching", name = %name); + tasks.push(async move { + let added = tokio::task::spawn_blocking(move || add_file_to_store(&name, &path)) + .instrument(span.clone()) + .await??; + let _g = span.enter(); + info!("{} -> {}", added.hash, added.store_path); + anyhow::Ok(()) }); } tasks.try_collect::>().await?; @@ -179,9 +178,7 @@ let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); let reg = tracing_subscriber::registry().with({ - let sub = tracing_subscriber::fmt::layer() - .without_time() - .with_target(false); + let sub = tracing_subscriber::fmt::layer().without_time(); #[cfg(feature = "indicatif")] let sub = sub.with_writer(indicatif_layer.get_stderr_writer()); sub.with_filter(filter) // .without, @@ -262,14 +259,10 @@ } async fn main_real(opts: RootOpts) -> Result<()> { - let nix_args = std::env::var_os("NIX_ARGS") - .map(|a| extra_args::parse_os(&a)) - .transpose()? - .unwrap_or_default(); - let config = opts.fleet_opts.build( - nix_args, - matches!(opts.command, Opts::Deploy(_) | Opts::BuildSystems(_)), - )?; + let config = opts.fleet_opts.build(matches!( + opts.command, + Opts::Deploy(_) | Opts::BuildSystems(_) + ))?; match run_command(&config, opts.fleet_opts, opts.command).await { Ok(()) => { --- a/cmds/install-secrets/src/main.rs +++ b/cmds/install-secrets/src/main.rs @@ -227,7 +227,6 @@ .from_env_lossy(), ) .without_time() - .with_target(false) .init(); let opts = Opts::parse(); --- /dev/null +++ b/cmds/nix-plugin-fleet/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "nix-plugin-fleet" +version.workspace = true +edition.workspace = true +rust-version.workspace = true + +[lib] +crate-type = ["cdylib"] + +[dependencies] +fleet-base.workspace = true --- /dev/null +++ b/cmds/nix-plugin-fleet/src/lib.rs @@ -0,0 +1,7 @@ +use fleet_base::primops::init_primops; + +/// SAFETY: expected plugin dynamic library entry point +#[unsafe(no_mangle)] +fn nix_plugin_entry() { + init_primops(); +} --- /dev/null +++ b/cmds/remowt-plugin-fleet/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "remowt-plugin-fleet" +description = "Remowt plugin exposing fleet's Nix endpoint to a running agent" +version.workspace = true +edition = "2021" + +[dependencies] +remowt-fleet.workspace = true +remowt-plugin.workspace = true + +anyhow.workspace = true --- /dev/null +++ b/cmds/remowt-plugin-fleet/src/main.rs @@ -0,0 +1,8 @@ +use remowt_fleet::{Nix, init_libraries}; + +fn main() -> anyhow::Result<()> { + remowt_plugin::run(|rpc| { + init_libraries(); + Nix.register_endpoints(rpc); + }) +} --- a/cmds/repl-plugin-unstable/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "repl-plugin-unstable" -version.workspace = true -edition.workspace = true -rust-version.workspace = true - -[lib] -crate-type = ["cdylib"] - -[dependencies] -fleet-base.workspace = true --- a/cmds/repl-plugin-unstable/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -use fleet_base::primops::init_primops; - -/// SAFETY: expected plugin dynamic library entry point -#[unsafe(no_mangle)] -fn nix_plugin_entry() { - init_primops(); -} --- a/cmds/terraform-provider-fleet/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "terraform-provider-fleet" -version.workspace = true -edition.workspace = true -rust-version.workspace = true - -[dependencies] -anyhow.workspace = true -serde = { workspace = true, features = ["derive"] } -tokio.workspace = true - -async-trait.workspace = true -tf-provider.workspace = true --- a/cmds/terraform-provider-fleet/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - panic!( - "this is a stub, real provider is in development, I just don't want to keep it in different branch." - ) -} --- a/crates/better-command/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "better-command" -version.workspace = true -edition.workspace = true -rust-version.workspace = true - -[dependencies] -regex.workspace = true -serde.workspace = true -serde_json.workspace = true -tracing.workspace = true - -tracing-indicatif = { workspace = true, optional = true } - -[features] -indicatif = ["tracing-indicatif"] --- a/crates/better-command/src/handler.rs +++ /dev/null @@ -1,335 +0,0 @@ -//! Collection of handlers, which transform program-specific stdout format to tracing - -use std::{ - collections::HashMap, - sync::{Arc, LazyLock, Mutex}, -}; - -use regex::Regex; -use serde::Deserialize; -use tracing::{Span, info, info_span, warn}; -#[cfg(feature = "indicatif")] -use tracing_indicatif::span_ext::IndicatifSpanExt as _; - -pub trait Handler: Send { - fn handle_line(&mut self, e: &str); -} - -/// Handler wrapper, which can be cloned. -pub struct ClonableHandler(Arc>); -impl Clone for ClonableHandler { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} -impl ClonableHandler { - pub fn new(inner: H) -> Self { - Self(Arc::new(Mutex::new(inner))) - } -} -impl Handler for ClonableHandler { - fn handle_line(&mut self, e: &str) { - self.0.lock().unwrap().handle_line(e) - } -} - -/// Converts command output to tracing lines -pub struct PlainHandler; -impl Handler for PlainHandler { - fn handle_line(&mut self, e: &str) { - info!(target: "log", "{e}"); - } -} - -/// Ignores output -pub struct NoopHandler; -impl Handler for NoopHandler { - fn handle_line(&mut self, _e: &str) {} -} - -/// Transform nix internal-json logs to tracing spans. -#[derive(Default)] -pub struct NixHandler { - spans: HashMap, -} -#[derive(Deserialize, Debug)] -#[serde(untagged)] -enum LogField { - String(String), - Num(u64), -} - -/// Nix internal-json log line type -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase", tag = "action")] -#[allow(dead_code)] -enum NixLog { - Msg { - level: u32, - msg: String, - raw_msg: Option, - }, - Start { - id: u64, - level: u32, - #[serde(default)] - fields: Vec, - text: String, - #[serde(rename = "type")] - typ: u32, - }, - Stop { - id: u64, - }, - Result { - id: u64, - #[serde(rename = "type")] - typ: u32, - #[serde(default)] - fields: Vec, - }, -} -fn process_message(m: &str) -> String { - // Supposed to remove formatting characters except colors, as some programs try to reset cursor position etc. - static OSC_CLEANER: LazyLock = - LazyLock::new(|| Regex::new(r"\x1B\]([^\x07\x1C]*[\x07\x1C])?|\r").unwrap()); - static DETABBER: LazyLock = LazyLock::new(|| Regex::new(r"\t").unwrap()); - let m = OSC_CLEANER.replace_all(m, ""); - // Indicatif can't format tabs. This is not the correct tab formatting, as correct one should be aligned, - // and not just be replaced with the constant number of spaces, but it's ok for now, as statuses are single-line. - DETABBER.replace_all(m.as_ref(), " ").to_string() -} -impl Handler for NixHandler { - fn handle_line(&mut self, e: &str) { - if let Some(e) = e.strip_prefix("@nix ") { - let log: NixLog = match serde_json::from_str(e) { - Ok(l) => l, - Err(err) => { - warn!("failed to parse nix log line {:?}: {}", e, err); - return; - } - }; - match log { - NixLog::Msg { msg, raw_msg, .. } => { - #[allow(clippy::nonminimal_bool)] - if !(msg.starts_with("\u{1b}[35;1mwarning:\u{1b}[0m Git tree '") - && msg.ends_with("' is dirty")) - && !msg.starts_with( - "\u{1b}[35;1mwarning:\u{1b}[0m not writing modified lock file of flake", - ) && msg - != "\u{1b}[35;1mwarning:\u{1b}[0m \u{1b}[31;1merror:\u{1b}[0m SQLite database '\u{1b}[35;1m/nix/var/nix/db/db.sqlite\u{1b}[0m' is busy" - { - if let Some(raw_msg) = raw_msg { - if !msg.is_empty() { - info!(target: "nix", "{}\n{}", raw_msg.trim_end(), msg.trim_end()) - } else { - info!(target: "nix", "{}", raw_msg.trim_end()) - } - } else { - info!(target: "nix", "{}", msg.trim_end()) - } - } - } - NixLog::Start { - ref fields, - typ, - id, - .. - } if typ == 105 && !fields.is_empty() => { - if let [LogField::String(drv), ..] = &fields[..] { - let mut drv = drv.as_str(); - if let Some(pkg) = drv.strip_prefix("/nix/store/") { - let mut it = pkg.splitn(2, '-'); - it.next(); - if let Some(pkg) = it.next() { - drv = pkg; - } - } - info!(target: "nix","building {}", drv); - let span = info_span!("build", drv); - #[cfg(feature = "indicatif")] - span.pb_start(); - self.spans.insert(id, span); - } else { - warn!("bad build log: {:?}", log) - } - } - NixLog::Start { - ref fields, - typ, - id, - .. - } if typ == 100 && fields.len() >= 3 => { - if let [ - LogField::String(drv), - LogField::String(from), - LogField::String(to), - .., - ] = &fields[..] - { - let mut drv = drv.as_str(); - - if let Some(pkg) = drv.strip_prefix("/nix/store/") { - let mut it = pkg.splitn(2, '-'); - it.next(); - if let Some(pkg) = it.next() { - drv = pkg; - } - } - info!(target: "nix","copying {} {} -> {}", drv, from, to); - let span = info_span!("copy", from, to, drv); - #[cfg(feature = "indicatif")] - span.pb_start(); - self.spans.insert(id, span); - } else { - warn!("bad copy log: {:?}", log) - } - } - NixLog::Start { text, typ, id, .. } - if typ == 0 || typ == 102 || typ == 103 || typ == 104 => - { - if !text.is_empty() - && text != "querying info about missing paths" - && text != "copying 0 paths" - // Too much spam on lazy-trees branch - && !(text.starts_with("copying '") && text.ends_with("' to the store")) - { - let span = info_span!("job"); - #[cfg(feature = "indicatif")] - { - span.pb_start(); - span.pb_set_message(&process_message(text.trim())); - } - self.spans.insert(id, span); - info!(target: "nix", "{}", text); - } - } - NixLog::Start { - text, - level: 0, - typ: 108, - .. - } if text.is_empty() => { - // Cache lookup? Coupled with copy log - } - NixLog::Start { - text, - level: 4, - typ: 109, - .. - } if text.starts_with("querying info about ") => { - // Cache lookup - } - NixLog::Start { - text, - level: 4, - typ: 101, - .. - } if text.starts_with("downloading ") => { - // NAR downloading, coupled with copy log - } - NixLog::Start { - text, - level: 1, - typ: 111, - .. - } if text.starts_with("waiting for a machine to build ") => { - // Useless repeating notification about build - } - NixLog::Start { - text, - level: 3, - typ: 111, - .. - } if text.starts_with("resolved derivation: ") => { - // CA resolved - } - NixLog::Start { - text, - level: 1, - typ: 111, - id, - .. - } if text.starts_with("waiting for lock on ") => { - let mut drv = text.strip_prefix("waiting for lock on ").unwrap(); - if let Some(txt) = drv.strip_prefix("\u{1b}[35;1m'") { - drv = txt; - } - if let Some(txt) = drv.strip_suffix("'\u{1b}[0m") { - drv = txt; - } - if let Some(txt) = drv.split("', '").next() { - drv = txt; - } - if let Some(pkg) = drv.strip_prefix("/nix/store/") { - let mut it = pkg.splitn(2, '-'); - it.next(); - if let Some(pkg) = it.next() { - drv = pkg; - } - } - let span = info_span!("waiting on drv", drv); - #[cfg(feature = "indicatif")] - span.pb_start(); - self.spans.insert(id, span); - // Concurrent build of the same message - } - NixLog::Stop { id, .. } => { - self.spans.remove(&id); - } - NixLog::Result { fields, id, typ } if typ == 101 && !fields.is_empty() => { - if let Some(span) = self.spans.get(&id) { - if let LogField::String(s) = &fields[0] { - #[cfg(feature = "indicatif")] - span.pb_set_message(&process_message(s.trim())); - #[cfg(not(feature = "indicatif"))] - { - let _span = span.enter(); - info!("{}", process_message(s)); - } - } else { - warn!("bad fields: {fields:?}"); - } - } else { - warn!("unknown result id: {id} {typ} {fields:?}"); - } - // dbg!(fields, id, typ); - } - NixLog::Result { fields, id, typ } if typ == 105 && fields.len() >= 4 => { - if let Some(span) = self.spans.get(&id) { - if let [ - LogField::Num(done), - LogField::Num(expected), - LogField::Num(_running), - LogField::Num(_failed), - ] = &fields[..4] - { - #[cfg(feature = "indicatif")] - { - span.pb_set_length(*expected); - span.pb_set_position(*done); - } - let _ = (span, done, expected); - } else { - warn!("bad fields: {fields:?}"); - } - } else { - // warn!("unknown result id: {id} {typ} {fields:?}"); - // Unaccounted progress. - } - // dbg!(fields, id, typ); - } - NixLog::Result { typ, .. } if typ == 104 || typ == 106 => { - // Set phase, expected - } - _ => warn!("unknown log: {:?}", log), - }; - } else { - let e = e.trim(); - if e.starts_with("Failed tcsetattr(TCSADRAIN): ") { - return; - } - info!("{e}") - } - } -} --- a/crates/better-command/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod handler; -pub use handler::{ClonableHandler, Handler, NixHandler, NoopHandler, PlainHandler}; --- a/crates/fleet-base/Cargo.toml +++ b/crates/fleet-base/Cargo.toml @@ -5,9 +5,15 @@ rust-version.workspace = true [dependencies] +remowt-client.workspace = true +remowt-endpoints.workspace = true +remowt-fleet.workspace = true +remowt-link-shared.workspace = true +remowt-ui-prompt.workspace = true + age.workspace = true anyhow.workspace = true -better-command.workspace = true +camino.workspace = true chrono.workspace = true clap = { workspace = true, features = ["derive"] } fleet-shared.workspace = true @@ -18,15 +24,11 @@ nix-eval.workspace = true nixlike.workspace = true nom.workspace = true -openssh.workspace = true rand.workspace = true serde.workspace = true serde_json.workspace = true tabled.workspace = true tempfile.workspace = true -thiserror.workspace = true time = { workspace = true, features = ["parsing"] } tokio.workspace = true -tokio-util = { workspace = true, features = ["codec"] } -toml_edit.workspace = true tracing.workspace = true --- a/crates/fleet-base/src/command.rs +++ /dev/null @@ -1,442 +0,0 @@ -use std::{ffi::OsStr, pin, process::Stdio, sync::Arc, task::Poll}; - -use anyhow::{Result, anyhow}; -use better_command::{Handler, NixHandler, PlainHandler}; -use futures::StreamExt; -use itertools::Either; -use openssh::{OverSsh, OwningCommand, Session}; -use serde::de::DeserializeOwned; -use tokio::{io::AsyncRead, process::Command, select}; -use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec}; -use tracing::debug; - -use crate::host::EscalationStrategy; - -fn escape_bash(input: &str, out: &mut String) { - const TO_ESCAPE: &str = "$ !\"#&'()*,;<>?[\\]^`{|}"; - if input.chars().all(|c| !TO_ESCAPE.contains(c)) { - out.push_str(input); - return; - } - out.push('\''); - for (i, v) in input.split('\'').enumerate() { - if i != 0 { - out.push_str("'\"'\"'"); - } - out.push_str(v); - } - out.push('\''); -} -fn ostoutf8(os: impl AsRef) -> String { - os.as_ref().to_str().expect("non-utf8 data").to_owned() -} - -#[derive(Clone, Debug)] -pub struct MyCommand { - command: String, - args: Vec, - env: Vec<(String, String)>, - ssh_session: Option>, - escalation: EscalationStrategy, - escalate: bool, -} -impl MyCommand { - pub fn new_on( - escalation: EscalationStrategy, - cmd: impl AsRef, - session: Arc, - ) -> Self { - assert!(!cmd.as_ref().is_empty()); - Self { - command: ostoutf8(cmd), - args: vec![], - env: vec![], - ssh_session: Some(session), - escalation, - escalate: false, - } - } - pub fn new(escalation: EscalationStrategy, cmd: impl AsRef) -> Self { - assert!(!cmd.as_ref().is_empty()); - Self { - command: ostoutf8(cmd), - args: vec![], - env: vec![], - ssh_session: None, - escalation, - escalate: false, - } - } - fn new_here(&self, cmd: impl AsRef) -> Self { - match self.ssh_session.clone() { - Some(ssh_session) => Self::new_on(self.escalation, cmd, ssh_session), - _ => Self::new(self.escalation, cmd), - } - } - - fn into_args(self) -> Vec { - let mut out = Vec::new(); - if !self.env.is_empty() { - out.push("env".to_owned()); - for (k, v) in self.env { - assert!(!k.contains('=')); - out.push(format!("{k}={v}")); - } - } - out.push(self.command); - out.extend(self.args); - out - } - - /// Translates environment variables into env command execution. - /// Required for ssh, as ssh don't allow to send environment variables (at least by default). - /// - /// FIXME: Insecure, as arguments might be seen by other users on the same machine. - /// Figure out some way to transfer environment using stdio? - fn translate_env_into_env(self) -> Self { - if self.env.is_empty() { - return self; - } - let mut out = self.new_here("env"); - for (k, v) in self.env { - assert!(!k.contains('=')); - out.arg(format!("{k}={v}")); - } - out.arg(self.command); - out.args(self.args); - - out - } - fn into_string(self) -> String { - let mut out = String::new(); - if !self.env.is_empty() { - out.push_str("env"); - for (k, v) in self.env { - out.push(' '); - assert!(!k.contains('=')); - escape_bash(&k, &mut out); - out.push('='); - escape_bash(&v, &mut out); - } - } - if !out.is_empty() { - out.push(' '); - } - escape_bash(&self.command, &mut out); - for arg in self.args { - out.push(' '); - escape_bash(&arg, &mut out); - } - out - } - fn into_command_unchecked_local(self) -> Command { - let mut out = Command::new(self.command); - out.args(self.args); - for (k, v) in self.env { - out.env(k, v); - } - out - } - fn into_command(self) -> Result>>> { - Ok(match self.ssh_session.clone() { - Some(session) => { - let cmd = self.translate_env_into_env().into_command_unchecked_local(); - Either::Right( - cmd.over_ssh(session) - .map_err(|e| anyhow!("ssh error: {e}"))?, - ) - } - _ => { - let cmd = self.into_command_unchecked_local(); - Either::Left(cmd) - } - }) - } - pub fn arg(&mut self, arg: impl AsRef) -> &mut Self { - let arg = arg.as_ref(); - self.args.push(ostoutf8(arg)); - self - } - pub fn eqarg(&mut self, arg: impl AsRef, value: impl AsRef) -> &mut Self { - let arg = arg.as_ref(); - let value = value.as_ref(); - let arg = ostoutf8(arg); - let value = ostoutf8(value); - self.arg(format!("{arg}={value}")); - self - } - pub fn comparg(&mut self, arg: impl AsRef, value: impl AsRef) -> &mut Self { - self.arg(arg); - self.arg(value); - self - } - pub fn env(&mut self, name: impl AsRef, value: impl AsRef) -> &mut Self { - self.env - .push((name.as_ref().to_owned(), value.as_ref().to_owned())); - self - } - pub fn args>(&mut self, args: impl IntoIterator) -> &mut Self { - for arg in args.into_iter() { - let arg = arg.as_ref(); - self.args.push(ostoutf8(arg)); - } - self - } - pub fn sudo(mut self) -> Self { - self.escalate = true; - self - } - fn wrap_sudo_if_needed(self) -> Self { - if !self.escalate { - return self; - } - match self.escalation { - EscalationStrategy::Su => { - let mut out = self.new_here("su"); - out.arg("-c").arg(self.into_string()); - out - } - EscalationStrategy::Sudo => { - let mut out = self.new_here("sudo"); - out.args(self.into_args()); - out - } - EscalationStrategy::Run0 => { - // run0 wants interactive authentication by default. - let mut run0 = self.new_here("run0"); - let mut out = self.new_here("script"); - - // Red backgrounds messes with fleet formatting - run0.arg("--background="); - run0.args(self.into_args()); - - out.arg("-q"); - out.arg("/dev/null"); - out.arg("-c"); - out.arg(run0.into_string()); - dbg!(&out); - out - } - } - } - - pub async fn run(self) -> Result<()> { - let str = self.clone().into_string(); - let cmd = self.wrap_sudo_if_needed().into_command()?; - match cmd { - Either::Left(cmd) => run_nix_inner(str, cmd, &mut PlainHandler).await?, - Either::Right(cmd) => run_nix_inner_ssh(str, cmd, &mut PlainHandler).await?, - }; - Ok(()) - } - pub async fn run_string(self) -> Result { - let bytes = self.run_bytes().await?; - Ok(String::from_utf8(bytes)?) - } - pub async fn run_value(self) -> Result { - let v = self.run_string().await?; - Ok(serde_json::from_str(&v)?) - } - pub async fn run_bytes(self) -> Result> { - let str = self.clone().into_string(); - let cmd = self.wrap_sudo_if_needed().into_command()?; - let v = match cmd { - Either::Left(cmd) => run_nix_inner_stdout(str, cmd, &mut PlainHandler).await?, - Either::Right(cmd) => run_nix_inner_stdout_ssh(str, cmd, &mut PlainHandler).await?, - }; - Ok(v) - } - - pub async fn run_nix_string(mut self) -> Result { - let str = self.clone().into_string(); - self.arg("--log-format").arg("internal-json"); - let cmd = self.wrap_sudo_if_needed().into_command()?; - let bytes = match cmd { - Either::Left(cmd) => run_nix_inner_stdout(str, cmd, &mut NixHandler::default()).await?, - Either::Right(cmd) => { - run_nix_inner_stdout_ssh(str, cmd, &mut NixHandler::default()).await? - } - }; - Ok(String::from_utf8(bytes)?) - } - pub async fn run_nix(mut self) -> Result<()> { - let str = self.clone().into_string(); - self.arg("--log-format").arg("internal-json"); - let cmd = self.wrap_sudo_if_needed().into_command()?; - match cmd { - Either::Left(mut cmd) => { - cmd.stdout(Stdio::inherit()); - run_nix_inner(str, cmd, &mut NixHandler::default()).await - } - Either::Right(mut cmd) => { - cmd.stdout(openssh::Stdio::inherit()); - run_nix_inner_ssh(str, cmd, &mut NixHandler::default()).await - } - } - } -} - -struct EmptyAsyncRead; -impl AsyncRead for EmptyAsyncRead { - fn poll_read( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - _buf: &mut tokio::io::ReadBuf<'_>, - ) -> Poll> { - Poll::Pending - } -} - -async fn run_nix_inner_stdout( - str: String, - cmd: Command, - handler: &mut dyn Handler, -) -> Result> { - Ok(run_nix_inner_raw(str, cmd, true, handler, None) - .await? - .expect("has out")) -} -async fn run_nix_inner(str: String, cmd: Command, handler: &mut dyn Handler) -> Result<()> { - let v = run_nix_inner_raw(str, cmd, false, handler, None).await?; - assert!(v.is_none()); - Ok(()) -} -async fn run_nix_inner_stdout_ssh( - str: String, - cmd: OwningCommand>, - handler: &mut dyn Handler, -) -> Result> { - Ok(run_nix_inner_raw_ssh(str, cmd, true, handler, None) - .await? - .expect("has out")) -} -async fn run_nix_inner_ssh( - str: String, - cmd: OwningCommand>, - handler: &mut dyn Handler, -) -> Result<()> { - let v = run_nix_inner_raw_ssh(str, cmd, false, handler, None).await?; - assert!(v.is_none()); - Ok(()) -} - -async fn run_nix_inner_raw( - str: String, - mut cmd: Command, - want_stdout: bool, - err_handler: &mut dyn Handler, - mut out_handler: Option<&mut dyn Handler>, -) -> Result>> { - cmd.stderr(Stdio::piped()); - cmd.stdout(Stdio::piped()); - debug!("running command {str:?} on local"); - let mut child = cmd.spawn()?; - let mut stderr = child.stderr.take().unwrap(); - let stdout = child.stdout.take().unwrap(); - let mut err = FramedRead::new(&mut stderr, LinesCodec::new()); - let mut out: Option> = Some(Box::new(stdout)); - let mut ob = want_stdout - .then(|| out.take().unwrap()) - .unwrap_or_else(|| Box::new(EmptyAsyncRead)); - let mut ol = (!want_stdout) - .then(|| out.take().unwrap()) - .unwrap_or_else(|| Box::new(EmptyAsyncRead)); - let mut ob = FramedRead::new(&mut ob, BytesCodec::new()); - let mut ol = FramedRead::new(&mut ol, LinesCodec::new()); - - // while let Some(line) = read.next().await? {} - - let mut out_buf = if want_stdout { Some(vec![]) } else { None }; - loop { - select! { - biased; - - Some(e) = err.next() => { - let e = e?; - err_handler.handle_line(&e); - }, - Some(o) = ob.next() => { - out_buf.as_mut().expect("stdout == wants_stdout").extend_from_slice(&o?); - }, - Some(o) = ol.next() => { - let o = o?; - if let Some(out) = out_handler.as_mut() { - out.handle_line(&o) - } else { - err_handler.handle_line(&o) - } - // out_handler.handle_info(&o); - }, - code = child.wait() => { - let code = code?; - if !code.success() { - anyhow::bail!("command '{str}' failed with status {}", code); - } - break; - } - } - } - - Ok(out_buf) -} -async fn run_nix_inner_raw_ssh( - str: String, - mut cmd: OwningCommand>, - want_stdout: bool, - err_handler: &mut dyn Handler, - mut out_handler: Option<&mut dyn Handler>, -) -> Result>> { - debug!("running command {str:?} over ssh"); - cmd.stderr(openssh::Stdio::piped()); - cmd.stdout(openssh::Stdio::piped()); - let mut child = cmd.spawn().await?; - let mut stderr = child.stderr().take().unwrap(); - let stdout = child.stdout().take().unwrap(); - let mut err = FramedRead::new(&mut stderr, LinesCodec::new()); - let mut out: Option> = Some(Box::new(stdout)); - let mut ob = want_stdout - .then(|| out.take().unwrap()) - .unwrap_or_else(|| Box::new(EmptyAsyncRead)); - let mut ol = (!want_stdout) - .then(|| out.take().unwrap()) - .unwrap_or_else(|| Box::new(EmptyAsyncRead)); - let mut ob = FramedRead::new(&mut ob, BytesCodec::new()); - let mut ol = FramedRead::new(&mut ol, LinesCodec::new()); - - // while let Some(line) = read.next().await? {} - - let mut out_buf = if want_stdout { Some(vec![]) } else { None }; - - let mut wait_future = pin::pin!(child.wait()); - loop { - select! { - biased; - - Some(e) = err.next() => { - let e = e?; - err_handler.handle_line(&e); - }, - Some(o) = ob.next() => { - out_buf.as_mut().expect("stdout == wants_stdout").extend_from_slice(&o?); - }, - Some(o) = ol.next() => { - let o = o?; - if let Some(out) = out_handler.as_mut() { - out.handle_line(&o) - } else { - err_handler.handle_line(&o) - } - // out_handler.handle_info(&o); - }, - code = &mut wait_future => { - let code = code?; - if !code.success() { - anyhow::bail!("command '{str}' failed with status {}", code); - } - break; - } - } - } - - Ok(out_buf) -} --- a/crates/fleet-base/src/deploy.rs +++ b/crates/fleet-base/src/deploy.rs @@ -1,8 +1,13 @@ -use std::{path::PathBuf, time::Duration}; +use std::time::Duration; use anyhow::{Context as _, Result, anyhow, bail}; +use camino::Utf8PathBuf; use clap::ValueEnum; use itertools::Itertools; +use remowt_endpoints::fs::FsClient; +use remowt_endpoints::systemd::SystemdClient; +use remowt_fleet::NixClient; +use remowt_link_shared::BifConfig; use tokio::time::sleep; use tracing::{Instrument as _, error, info, info_span, warn}; @@ -59,10 +64,11 @@ pub async fn deploy_task( action: DeployAction, host: &ConfigHost, - built: PathBuf, + built: Utf8PathBuf, specialisation: Option, disable_rollback: bool, ) -> Result<()> { + let remowt = host.remowt().await?; let deploy_kind = host.deploy_kind().await?; if (deploy_kind == DeployKind::NixosInstall || deploy_kind == DeployKind::NixosLustrate) && !matches!(action, DeployAction::Boot | DeployAction::Upload) @@ -86,7 +92,7 @@ generation.id, generation.datetime ); { - let mut cmd = host.cmd("sh").await?; + let mut cmd = remowt.cmd("sh"); cmd.arg("-c").arg(format!("mark=$(mktemp -p /etc -t fleet_rollback_marker.XXXXX) && echo -n {} > $mark && mv --no-clobber $mark /etc/fleet_rollback_marker", generation.id)); if let Err(e) = cmd.sudo().run().await { error!("failed to set rollback marker: {e}"); @@ -104,7 +110,7 @@ // if we fail to perform generation switch in time, then we will still call the activation script, and this may break something. // Anyway, reboot will still help in this case. if action.should_schedule_rollback_run() { - let mut cmd = host.cmd("systemd-run").await?; + let mut cmd = remowt.cmd("systemd-run"); cmd.comparg("--on-active", "3min") .comparg("--unit", "rollback-watchdog-run") .arg("systemctl") @@ -116,14 +122,20 @@ } } } + + let remowt = host.remowt().await?; + let fs = remowt.endpoints::>(); if deploy_kind == DeployKind::NixosLustrate { // Fleet could also create this file, but as this operation is potentially disruptive, // make user do it themself. - if !host.file_exists("/etc/NIXOS_LUSTRATE").await? { + if !fs + .file_exists(Utf8PathBuf::from("/etc/NIXOS_LUSTRATE")) + .await? + { bail!("/etc/NIXOS_LUSTRATE should be created on remote host"); } // Wanted by NixOS to recognize the system as NixOS. - let mut cmd = host.cmd("touch").await?; + let mut cmd = remowt.cmd("touch"); cmd.arg("/etc/NIXOS"); cmd.sudo().run().await.context("creating /etc/NIXOS")?; } @@ -131,7 +143,7 @@ info!( "running nixos-install to switch profile, install bootloader, and perform activation" ); - let mut cmd = host.cmd("nixos-install").await?; + let mut cmd = remowt.cmd("nixos-install"); cmd.arg("--system").arg(&built).args([ // Channels here aren't fleet host system channels, but channels embedded in installation cd, which might be old. // It is possible to copy host channels, but I would prefer non-flake nix just to be unsupported. @@ -147,32 +159,38 @@ if action.should_switch_profile() && !failed { info!("switching system profile generation"); - // To avoid even more problems, using nixos-install for now. - // // nix build is unable to work with --store argument for some reason, and nix until 2.26 didn't support copy with --profile argument, - // // falling back to using nix-env command - // // After stable NixOS starts using 2.26 - use `nix --store /mnt copy --from /mnt --profile ...` here, and instead of nix build below. - // let mut cmd = host.cmd("nix-env").await?; - // cmd.args([ - // "--store", - // "/mnt", - // "--profile", - // "/mnt/nix/var/nix/profiles/system", - // "--set", - // ]) - // .arg(&built); - // if let Err(e) = cmd.sudo().run_nix().await { - // error!("failed to switch system profile generation: {e}"); - // failed = true; - // } - // It would also be possible to update profile atomically during copy: - // https://github.com/NixOS/nix/pull/11657 - let mut cmd = host.nix_cmd().await?; - cmd.arg("build"); - cmd.comparg("--profile", "/nix/var/nix/profiles/system"); - cmd.arg(&built); - if let Err(e) = cmd.sudo().run_nix().await { - error!("failed to switch system profile generation: {e}"); - failed = true; + match host.ensure_nix_plugin().await { + Ok(plugin_id) => { + let nix_elevated = remowt.plugin_endpoints::>(plugin_id); + // To avoid even more problems, using nixos-install for now. + // // nix build is unable to work with --store argument for some reason, and nix until 2.26 didn't support copy with --profile argument, + // // falling back to using nix-env command + // // After stable NixOS starts using 2.26 - use `nix --store /mnt copy --from /mnt --profile ...` here, and instead of nix build below. + // let mut cmd = host.cmd("nix-env").await?; + // cmd.args([ + // "--store", + // "/mnt", + // "--profile", + // "/mnt/nix/var/nix/profiles/system", + // "--set", + // ]) + // .arg(&built); + // if let Err(e) = cmd.sudo().run_nix().await { + // error!("failed to switch system profile generation: {e}"); + // failed = true; + // } + if let Err(e) = nix_elevated + .switch_profile("/nix/var/nix/profiles/system".to_owned(), built.clone()) + .await + { + error!("failed to switch system profile generation: {e}"); + failed = true; + } + } + Err(e) => { + failed = true; + error!("failed to enable nix plugin: {e:?}"); + } } } @@ -189,7 +207,7 @@ built.clone() }; let switch_script = specialised.join("bin/switch-to-configuration"); - let mut cmd = host.cmd("systemd-run").in_current_span().await?; + let mut cmd = remowt.cmd("systemd-run"); if deploy_kind == DeployKind::NixosLustrate { cmd.arg("--setenv=NIXOS_INSTALL_BOOTLOADER=1"); } @@ -209,12 +227,14 @@ } } if action.should_create_rollback_marker() { + let elevated_systemd = remowt.run0_endpoints::>().await?; + let elevated_fs = remowt.run0_endpoints::>().await?; if !disable_rollback { if failed { if action.should_schedule_rollback_run() { info!("executing rollback"); - if let Err(e) = host - .systemctl_start("rollback-watchdog.service") + if let Err(e) = elevated_systemd + .start("rollback-watchdog.service".to_owned()) .instrument(info_span!("rollback")) .await { @@ -223,8 +243,8 @@ } } else { info!("trying to mark upgrade as successful"); - if let Err(e) = host - .rm_file("/etc/fleet_rollback_marker", true) + if let Err(e) = elevated_fs + .rm_file(Utf8PathBuf::from("/etc/fleet_rollback_marker")) .in_current_span() .await { @@ -234,16 +254,22 @@ } } info!("disarming watchdog, just in case"); - if let Err(_e) = host.systemctl_stop("rollback-watchdog.timer").await { + if let Err(_e) = elevated_systemd + .stop("rollback-watchdog.timer".to_owned()) + .await + { // It is ok, if there was no reboot - then timer might not be running. } if action.should_schedule_rollback_run() { - if let Err(e) = host.systemctl_stop("rollback-watchdog-run.timer").await { + if let Err(e) = elevated_systemd + .stop("rollback-watchdog-run.timer".to_owned()) + .await + { error!("failed to disarm rollback run: {e}"); } } - } else if let Err(_e) = host - .rm_file("/etc/fleet_rollback_marker", true) + } else if let Err(_e) = elevated_fs + .rm_file(Utf8PathBuf::from("/etc/fleet_rollback_marker")) .in_current_span() .await { @@ -257,8 +283,8 @@ config: &Config, host: &ConfigHost, location: GenerationStorage, - generation: PathBuf, -) -> Result { + generation: Utf8PathBuf, +) -> Result { if matches!(location, GenerationStorage::Pusher) { bail!("pusher is not enabled in this version of fleet"); } @@ -273,11 +299,11 @@ } Err(e) if tries < 3 => { tries += 1; - warn!("copy failure ({}/3): {}", tries, e); + warn!("copy failure ({}/3): {:#}", tries, e); sleep(Duration::from_millis(5000)).await; } Err(e) => { - bail!("upload failed: {e}"); + bail!("upload failed: {e:#}"); } } } --- a/crates/fleet-base/src/fleetdata.rs +++ b/crates/fleet-base/src/fleetdata.rs @@ -144,10 +144,6 @@ pub generation_data: Value, } -fn is_false(b: &bool) -> bool { - *b == false -} - #[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] #[repr(transparent)] pub struct SecretOwner(String); @@ -291,7 +287,7 @@ } // Which one is easier to access - return a.owners.len().cmp(&b.owners.len()); + a.owners.len().cmp(&b.owners.len()) } impl FleetSecretDistributions { --- a/crates/fleet-base/src/host.rs +++ b/crates/fleet-base/src/host.rs @@ -1,30 +1,33 @@ use std::{ collections::{BTreeMap, BTreeSet, HashSet}, - ffi::{OsStr, OsString}, - fmt::Display, + future::Future, io::Write, ops::Deref, path::PathBuf, + pin::Pin, str::FromStr, - sync::{Arc, Mutex, MutexGuard, OnceLock}, + sync::{Arc, OnceLock}, }; use anyhow::{Context, Result, anyhow, bail, ensure}; +use camino::{Utf8Path, Utf8PathBuf}; use chrono::{DateTime, Utc}; use fleet_shared::SecretData; -use nix_eval::{Value, nix_go, nix_go_json, util::assert_warn}; -use openssh::{ControlPersist, SessionBuilder}; -use serde::de::DeserializeOwned; +use nix_eval::{Store, Value, nix_go, nix_go_json, util::assert_warn}; +use remowt_client::{AgentBundle, Remowt}; +use remowt_endpoints::fs::FsClient; +use remowt_link_shared::Address; +use remowt_ui_prompt::auto::AutoPrompter; +use remowt_ui_prompt::bifrost::PromptEndpoints; +use remowt_ui_prompt::{PrependSourcePrompter, Source}; use tabled::Tabled; use tempfile::NamedTempFile; -use time::{UtcDateTime, format_description}; -use tracing::warn; +use time::UtcDateTime; +use tokio::task::spawn_blocking; +use tracing::{info, warn}; -use crate::{ - command::MyCommand, - fleetdata::{ - FleetData, FleetSecretData, FleetSecretDistribution, FleetSecretPart, SecretOwner, - }, +use crate::fleetdata::{ + FleetData, FleetSecretData, FleetSecretDistribution, FleetSecretPart, SecretOwner, }; pub struct FleetConfigInternals { @@ -36,7 +39,6 @@ /// builtins.currentSystem pub local_system: String, pub data: Arc, - pub nix_args: Vec, /// fleet_config.config pub config_field: Value, /// flake.output @@ -48,6 +50,8 @@ pub default_pkgs: Value, /// inputs.nixpkgs pub nixpkgs: Value, + + pub local_host: OnceLock>, } // TODO: Make field not pub @@ -62,13 +66,6 @@ } } -#[derive(Clone, Copy, Debug)] -pub enum EscalationStrategy { - Sudo, - Run0, - Su, -} - #[derive(Clone, PartialEq, Copy, Debug)] pub enum DeployKind { /// NixOS => NixOS managed by fleet @@ -115,9 +112,26 @@ // TODO: Move command helpers away with connectivity refactor pub local: bool, - pub session: OnceLock>, + pub remowt: OnceLock, + nix_store: OnceLock>, + nix_plugin: tokio::sync::OnceCell<()>, } +const NIX_PLUGIN_ID: u16 = 2; + +fn agents_dir() -> Result { + std::env::var_os("REMOWT_AGENTS_DIR") + .map(PathBuf::from) + .or_else(|| option_env!("REMOWT_AGENTS_DIR").map(PathBuf::from)) + .ok_or_else(|| { + anyhow!("no remowt-agents bundle; set REMOWT_AGENTS_DIR to a remowt-agents output") + }) +} + +fn agent_bundle() -> Result { + AgentBundle::from_dir(agents_dir()?) +} + #[derive(Debug, Clone, Copy)] pub enum GenerationStorage { Deployer, @@ -143,7 +157,7 @@ #[tabled(rename = "Created at")] pub datetime: UtcDateTime, #[tabled(format = "{:?}")] - pub store_path: PathBuf, + pub store_path: Utf8PathBuf, #[tabled(skip)] pub location: GenerationStorage, } @@ -153,68 +167,36 @@ } } -fn parse_generation_line(g: &str) -> Option { - let mut parts = g.split_whitespace(); - let id = parts.next()?; - let id: u32 = id.parse().ok()?; - let date = parts.next()?; - let time = parts.next()?; - let current = if let Some(current) = parts.next() { - if current == "(current)" { - Some(true) - } else { - None - } - } else { - Some(false) - }; - let current = current?; - if parts.next().is_some() { - warn!("unexpected text after generation: {g}"); - } - - let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]") - .expect("valid format"); - let datetime = UtcDateTime::parse(&format!("{date} {time}"), &format).ok()?; - - Some(Generation { - id, - current, - datetime, - store_path: PathBuf::new(), - location: GenerationStorage::Machine, - }) -} -// TODO: Move command helpers away with connectivity refactor impl ConfigHost { pub async fn list_generations(&self, profile: &str) -> Result> { - let mut cmd = self.cmd("nix-env").await?; - cmd.comparg("--profile", format!("/nix/var/nix/profiles/{profile}")) - .arg("--list-generations") - .env("TZ", "UTC"); - // Sudo is required because --list-generations tries to acquire profile lock - let data = cmd.sudo().run_string().await?; - let mut generations = data - .split('\n') - .map(|e| e.trim()) - .filter(|&l| !l.is_empty()) - .filter_map(|g| { - let generation = parse_generation_line(g); - if generation.is_none() { - warn!("bad generation: {g}"); - }; - generation + let plugin_id = self.ensure_nix_plugin().await?; + let nix = self + .remowt() + .await? + .plugin_endpoints::>(plugin_id); + let raw = nix + .list_generations(profile.to_owned()) + .await + .map_err(|e| anyhow!("{e:?}"))? + .map_err(|e| anyhow!("{e}"))?; + raw.into_iter() + .map(|g| { + let id: u32 = + g.id.try_into() + .with_context(|| format!("generation id {} doesn't fit in u32", g.id))?; + let datetime = UtcDateTime::from_unix_timestamp(g.creation_time_unix) + .with_context(|| { + format!("invalid generation timestamp {}", g.creation_time_unix) + })?; + Ok(Generation { + id, + current: g.current, + datetime, + store_path: g.store_path, + location: GenerationStorage::Machine, + }) }) - .collect::>(); - for ele in generations.iter_mut() { - let mut cmd = self.cmd("readlink").await?; - cmd.arg("--") - .arg(format!("/nix/var/nix/profiles/{profile}-{}-link", ele.id)); - let path = cmd.run_string().await?; - ele.store_path = PathBuf::from(path.trim_end_matches("\n")); - } - - Ok(generations) + .collect() } pub fn set_session_destination(&self, dest: String) { @@ -236,7 +218,9 @@ if let Some(kind) = self.deploy_kind.get() { return Ok(*kind); } - let is_fleet_managed = match self.file_exists("/etc/FLEET_HOST").await { + let remowt = self.remowt().await?; + let fs = remowt.endpoints::>(); + let is_fleet_managed = match fs.file_exists(Utf8PathBuf::from("/etc/FLEET_HOST")).await { Ok(v) => v, Err(e) => { bail!("failed to query remote system kind: {e}"); @@ -252,6 +236,7 @@ 1. manually create /etc/FLEET_HOST file on the target host, 2. use ?deploy_kind=fleet host argument if you're upgrading from older version of fleet 3. use ?deploy_kind=upgrade_to_fleet if you're upgrading from plain nixos to fleet-managed nixos + for installation use ?deploy_kind=nixos_install / ?deploy_kind=nixos_lustrate "} ); } @@ -259,139 +244,101 @@ let _ = self.deploy_kind.set(DeployKind::Fleet); Ok(*self.deploy_kind.get().expect("deploy kind is just set")) } - pub async fn escalation_strategy(&self) -> Result { - // Prefer sudo, as run0 has some gotchas with polkit - // and too many repeating prompts. - if (self.find_in_path("sudo").await).is_ok() { - return Ok(EscalationStrategy::Sudo); - } - if (self.find_in_path("run0").await).is_ok() { - return Ok(EscalationStrategy::Run0); + async fn connection(&self) -> Result { + if let Some(conn) = self.remowt.get() { + return Ok(conn.clone()); } - Ok(EscalationStrategy::Su) - } - async fn open_session(&self) -> Result> { - assert!(!self.local, "do not open ssh connection to local session"); - // FIXME: TOCTOU - if let Some(session) = &self.session.get() { - return Ok((*session).clone()); + let bundle = agent_bundle()?; + let conn = if self.local { + Remowt::connect_local(&bundle) + .await + .context("starting local remowt agent")? + } else { + let dest = self + .session_destination + .get() + .cloned() + .unwrap_or_else(|| self.name.clone()); + Remowt::connect(&dest, &bundle) + .await + .map_err(|e| anyhow!("remowt error while connecting to {}: {e:#?}", self.name))? }; - let mut session = SessionBuilder::default(); - session.control_persist(ControlPersist::ClosedAfterInitialConnection); - - let dest = self.session_destination.get().unwrap_or(&self.name); - let session = session - .connect(&dest) - .await - .map_err(|e| anyhow!("ssh error while connecting to {}: {e:#?}", self.name))?; - let session = Arc::new(session); - self.session.set(session.clone()).expect("TOCTOU happened"); - Ok(session) - } - pub async fn mktemp_dir(&self) -> Result { - let mut cmd = self.cmd("mktemp").await?; - cmd.arg("-d"); - let path = cmd.run_string().await?; - Ok(path.trim_end().to_owned()) - } - pub async fn file_exists(&self, path: impl AsRef) -> Result { - let mut cmd = self.cmd("sh").await?; - cmd.arg("-c") - .arg("test -e \"$1\" && echo true || echo false") - .arg("_") - .arg(path); - cmd.run_value().await - } - pub async fn read_file_bin(&self, path: impl AsRef) -> Result> { - let mut cmd = self.cmd("cat").await?; - cmd.arg(path); - cmd.run_bytes().await - } - pub async fn read_file_text(&self, path: impl AsRef) -> Result { - let mut cmd = self.cmd("cat").await?; - cmd.arg(path); - cmd.run_string().await - } - pub async fn read_dir(&self, path: impl AsRef) -> Result> { - let mut cmd = self.cmd("ls").await?; - cmd.arg(path); - let out = cmd.run_string().await?; - let mut lines = out.split('\n'); - if let Some(last) = lines.next_back() { - ensure!(last.is_empty(), "output of ls should end with newline"); - } - Ok(lines.map(ToOwned::to_owned).collect()) - } - #[allow(dead_code)] - pub async fn read_file_json(&self, path: impl AsRef) -> Result { - let text = self.read_file_text(path).await?; - Ok(serde_json::from_str(&text)?) - } - pub async fn read_env(&self, env: &str) -> Result { - let mut cmd = self.cmd("printenv").await?; - cmd.arg(env); - cmd.run_string().await - } - pub async fn find_in_path(&self, command: &str) -> Result { - // // `which` is not a part of coreutils, and it might not exist on machine. - // let path = self.read_env("PATH").await?; - // // Assuming delimiter is :, we don't work with windows host, this check will be much - // // more sophisticated in remowt backend (and quicker, since actual PATH search will be done on remote machine) - // for ele in path.split(':') { - // let test_path = format!("{ele}/{cmd}"); - // test -x etc - // } - // let mut cmd = self.cmd("printenv").await?; - // cmd.arg(env); - // Ok(cmd.run_string().await?) - // Assuming this is an environment issue if which doesn't exist, will be fixed with remowt. - let mut cmd = self - .cmd_escalation( - // Not used - EscalationStrategy::Su, - "which", - ) - .await?; - cmd.arg(command); - cmd.run_string().await + PromptEndpoints(PrependSourcePrompter { + prompter: AutoPrompter::new().await, + source: if self.local { + vec![] + } else { + vec![Source(std::borrow::Cow::Owned(format!( + "ssh host: {}", + self.name + )))] + }, + description: "".to_owned(), + }) + .register_endpoints(&mut conn.rpc()); + let _ = self.remowt.set(conn); + Ok(self.remowt.get().expect("just set").clone()) } - pub async fn read_file_value(&self, path: impl AsRef) -> Result - where - ::Err: Display, - { - let text = self.read_file_text(path).await?; - D::from_str(&text).map_err(|e| anyhow!("failed to parse value: {e}")) + + /// Client for this host's unprivileged agent. + pub async fn remowt(&self) -> Result { + Ok(self.connection().await?) } - pub async fn cmd(&self, cmd: impl AsRef) -> Result { - self.cmd_escalation(self.escalation_strategy().await?, cmd) - .await + + pub fn ensure_nix_plugin(&self) -> Pin> + Send + '_>> { + Box::pin(async { + self.nix_plugin + .get_or_try_init(|| async { + let pkgs = self.pkgs()?; + let name = "remowt-plugin-fleet"; + let plugin = nix_go!(pkgs[{ name }]); + let built = plugin + .build("out") + .context("failed to build the fleet nix plugin")?; + let copied = self + .remote_derivation(&built) + .await + .context("failed to copy the fleet nix plugin to the host store")?; + let bin = copied.join("bin/remowt-plugin-fleet"); + self.remowt() + .await? + .run0_load_plugin_path(NIX_PLUGIN_ID, bin.as_str()) + .await + .context("failed to load the fleet nix plugin")?; + self.remowt() + .await? + .rpc() + .wait_for_connection_to(Address::Plugin(NIX_PLUGIN_ID)) + .await + .map_err(|e| anyhow!("failed to wait for plugin"))?; + anyhow::Ok(()) + }) + .await?; + Ok(NIX_PLUGIN_ID) + }) } - pub async fn cmd_escalation( - &self, - escalation: EscalationStrategy, - cmd: impl AsRef, - ) -> Result { - if self.local { - Ok(MyCommand::new(escalation, cmd)) - } else { - let session = self.open_session().await?; - Ok(MyCommand::new_on(escalation, cmd, session)) + + async fn nix_store(&self) -> Result> { + if let Some(store) = self.nix_store.get() { + return Ok(store.clone()); } - } - pub async fn nix_cmd(&self) -> Result { - let mut nix = self.cmd("nix").await?; - nix.args([ - "--extra-experimental-features", - "nix-command", - "--extra-experimental-features", - "flakes", - ]); - Ok(nix) + let conn = self.connection().await?; + let socket = match self.deploy_kind().await? { + DeployKind::NixosInstall => { + remowt_fleet::nix_store_socket(conn, "/mnt?require-sigs=false").await? + } + _ => remowt_fleet::nix_store_socket(conn, "auto").await?, + }; + let uri = format!("unix://{}", socket.display()); + let store = Arc::new(Store::open(&uri)?); + let _ = self.nix_store.set(store); + Ok(self.nix_store.get().expect("just set").clone()) } pub async fn decrypt(&self, data: SecretData) -> Result> { ensure!(data.encrypted, "secret is not encrypted"); - let mut cmd = self.cmd("fleet-install-secrets").await?; + let remowt = self.remowt().await?; + let mut cmd = remowt.cmd("fleet-install-secrets"); cmd.arg("decrypt").eqarg("--secret", data.to_string()); let encoded = cmd .sudo() @@ -434,8 +381,9 @@ data: SecretData, targets: BTreeSet, ) -> Result { + let remowt = self.remowt().await?; ensure!(data.encrypted, "secret is not encrypted"); - let mut cmd = self.cmd("fleet-install-secrets").await?; + let mut cmd = remowt.cmd("fleet-install-secrets"); cmd.arg("reencrypt").eqarg("--secret", data.to_string()); for target in targets { let key = self.config.key(&target).await?; @@ -451,74 +399,39 @@ Ok(data) } /// Returns path for futureproofing, as path might change i.e on conversion to CA - pub async fn remote_derivation(&self, path: &PathBuf) -> Result { + pub async fn remote_derivation(&self, path: impl AsRef) -> Result { + let path = path.as_ref().to_owned(); if self.local { // Path is located locally, thus already trusted. - return Ok(path.to_owned()); - } - let mut sign = MyCommand::new( - // TODO: Look at the current escalation strategy. - // ... or switch to run0 right after polkit update - EscalationStrategy::Sudo, - "nix", - ); - sign.arg("store") - .arg("sign") - .comparg("--key-file", "/etc/nix/private-key") - .arg("-r") - .arg(&path); - if let Err(e) = sign.sudo().run_nix().await { - warn!("failed to sign store paths: {e}"); + return Ok(path); } - let mut nix = MyCommand::new( - // Not used - EscalationStrategy::Su, - "nix", - ); - nix.arg("copy").arg("--substitute-on-destination"); - - let proto = if self.legacy_ssh_store.get().cloned().unwrap_or(false) { - "ssh" - } else { - "ssh-ng" + let sign: Pin> + Send>> = { + let path = path.clone(); + Box::pin(async move { + let local = self.config.local_host(); + let plugin_id = local.ensure_nix_plugin().await?; + let nix = local + .remowt() + .await? + .plugin_endpoints::>(plugin_id); + nix.sign_closure(path, Utf8PathBuf::from("/etc/nix/private-key")) + .await + .map_err(|e| anyhow!("{e:?}"))? + .map_err(|e| anyhow!("{e}"))?; + Ok(()) + }) }; - - match self.deploy_kind().await? { - DeployKind::Fleet | DeployKind::UpgradeToFleet | DeployKind::NixosLustrate => { - nix.comparg("--to", format!("{proto}://{}", self.name)); - } - DeployKind::NixosInstall => { - nix - // Signature checking makes no sense with remote-store store argument set, as we're not even interacting with remote nix daemon - .arg("--no-check-sigs") - .comparg( - "--to", - format!("{proto}://root@{}?remote-store=/mnt", self.name), - ); - } + if let Err(e) = sign.await { + warn!("failed to sign store paths: {e}"); } - nix.arg(path); - nix.run_nix().await.context("nix copy")?; - Ok(path.to_owned()) - } - pub async fn systemctl_stop(&self, name: &str) -> Result<()> { - let mut cmd = self.cmd("systemctl").await?; - cmd.arg("stop").arg(name); - cmd.sudo().run().await - } - pub async fn systemctl_start(&self, name: &str) -> Result<()> { - let mut cmd = self.cmd("systemctl").await?; - cmd.arg("start").arg(name); - cmd.sudo().run().await - } - - pub async fn rm_file(&self, path: impl AsRef, sudo: bool) -> Result<()> { - let mut cmd = self.cmd("rm").await?; - cmd.arg("-f").arg(path); - if sudo { - cmd = cmd.sudo() + let store = self.nix_store().await?; + { + let path = path.clone(); + spawn_blocking(move || nix_eval::copy_closure_to(&store, path.as_ref())) + .await? + .context("copying closure to remote store")?; } - cmd.run().await + Ok(path) } } @@ -630,26 +543,32 @@ } Ok(out) } - pub fn local_host(&self) -> ConfigHost { - ConfigHost { - config: self.clone(), - name: "".to_owned(), - host_config: None, - nixos_config: OnceLock::new(), - nixos_unchecked_config: OnceLock::new(), - groups: { - let cell = OnceLock::new(); - let _ = cell.set(vec![]); - cell - }, - pkgs_override: Some(self.default_pkgs.clone()), + pub fn local_host(&self) -> Arc { + self.local_host + .get_or_init(|| { + Arc::new(ConfigHost { + config: self.clone(), + name: "".to_owned(), + host_config: None, + nixos_config: OnceLock::new(), + nixos_unchecked_config: OnceLock::new(), + groups: { + let cell = OnceLock::new(); + let _ = cell.set(vec![]); + cell + }, + pkgs_override: Some(self.default_pkgs.clone()), - local: true, - session: OnceLock::new(), - deploy_kind: OnceLock::new(), - session_destination: OnceLock::new(), - legacy_ssh_store: OnceLock::new(), - } + local: true, + remowt: OnceLock::new(), + nix_store: OnceLock::new(), + nix_plugin: tokio::sync::OnceCell::new(), + deploy_kind: OnceLock::new(), + session_destination: OnceLock::new(), + legacy_ssh_store: OnceLock::new(), + }) + }) + .clone() } pub fn preferred_hosts( @@ -684,7 +603,9 @@ // TODO: Remove with connectivit refactor local: self.localhost == name, - session: OnceLock::new(), + remowt: OnceLock::new(), + nix_store: OnceLock::new(), + nix_plugin: tokio::sync::OnceCell::new(), deploy_kind: OnceLock::new(), session_destination: OnceLock::new(), legacy_ssh_store: OnceLock::new(), --- a/crates/fleet-base/src/keys.rs +++ b/crates/fleet-base/src/keys.rs @@ -31,7 +31,8 @@ } else { warn!("Loading key for {}", host); let host = self.host(host)?; - let mut cmd = host.cmd("cat").await?; + let remowt = host.remowt().await?; + let mut cmd = remowt.cmd("cat"); cmd.arg("/etc/ssh/ssh_host_ed25519_key.pub"); let key = cmd.run_string().await?; self.update_key(&host.name, key.clone()); --- a/crates/fleet-base/src/lib.rs +++ b/crates/fleet-base/src/lib.rs @@ -1,8 +1,6 @@ -pub mod command; pub mod deploy; pub mod fleetdata; pub mod host; mod keys; pub mod opts; pub mod primops; -pub mod secret_storage; --- a/crates/fleet-base/src/opts.rs +++ b/crates/fleet-base/src/opts.rs @@ -1,9 +1,8 @@ use std::{ collections::{BTreeMap, BTreeSet}, env::current_dir, - ffi::OsString, str::FromStr, - sync::{Arc, Mutex}, + sync::{Arc, OnceLock}, }; use anyhow::{Context, Result, bail}; @@ -196,7 +195,7 @@ } // TODO: Config should be detached from opts. - pub fn build(&self, nix_args: Vec, assert: bool) -> Result { + pub fn build(&self, assert: bool) -> Result { let cwd = current_dir()?; let mut directory = cwd.clone(); let mut fleet_data_path = directory.join("fleet.nix"); @@ -274,11 +273,11 @@ data, flake_outputs: flake, local_system: self.local_system.clone(), - nix_args, config_field, default_pkgs, nixpkgs, localhost: self.localhost.to_owned(), + local_host: OnceLock::new(), })); PRIMOPS_DATA --- a/crates/fleet-base/src/primops.rs +++ b/crates/fleet-base/src/primops.rs @@ -1,10 +1,12 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; use anyhow::{Context, bail, ensure}; use fleet_shared::SecretData; use itertools::Itertools; use nix_eval::{NativeFn, Value, await_in_nix, nix_go, nix_go_json}; +use remowt_endpoints::fs::FsClient; +use remowt_link_shared::BifConfig; use serde::Deserialize; use tracing::{info, warn}; @@ -28,7 +30,7 @@ let pkgs = host_on.pkgs()?; let default_mk_secret_generators = nix_go!(pkgs.mkSecretGenerators); let generators = nix_go!(default_mk_secret_generators(Obj { recipients })); - Ok(pkgs.clone().attrs_update(generators)?) + pkgs.clone().attrs_update(generators) } pub fn get_default_pkgs_and_generators(config: &Config) -> Result { let host_on = config.local_host(); @@ -91,12 +93,18 @@ let impure_on: Option = nix_go_json!(default_generator_drv.impureOn); let host_on = if let Some(on) = &impure_on { - config - .host(on) - .context("failed to get secret generation target host")? + Arc::new( + config + .host(on) + .context("failed to get secret generation target host")?, + ) } else { config.local_host() }; + + let remowt = host_on.remowt().await?; + let fs = remowt.endpoints::>(); + let mut recipients = Vec::new(); for owner in &expectations.owners { recipients.push(config.key(owner).await?); @@ -116,12 +124,13 @@ .context("failed to copy generator to target host")?; // TODO: Remove destdir after everything is done - let out_parent = host_on + let out_parent = fs .mktemp_dir() .await + .map_err(|e| anyhow!("{e:?}")) .context("failed to prepare generator output dir on target host")?; - let out = format!("{out_parent}/out"); - let mut generator_cmd = host_on.cmd(generator).await?; + let out = out_parent.join("out"); + let mut generator_cmd = remowt.cmd(generator); generator_cmd.env("out", &out); if impure_on.is_none() { let project_path: String = config @@ -138,7 +147,7 @@ .context("failed to run impure generator")?; { - let marker = host_on.read_file_text(format!("{out}/marker")).await?; + let marker = fs.read_file_text(out.join("marker")).await?; ensure!( marker == "SUCCESS", "impure generator ended prematurely, secret generation failed" @@ -147,15 +156,16 @@ let mut missing_parts = expectations.parts.clone(); let mut parts = BTreeMap::new(); - for part in host_on.read_dir(&out).await? { + for part in fs.read_dir(&out).await? { + let part = part.into_string(); if part == "created_at" || part == "expires_at" || part == "marker" { continue; } let Some(part_def) = missing_parts.remove(&part) else { bail!("secret generator has produced an unexpected part: {part}"); }; - let contents: SecretData = host_on - .read_file_text(format!("{out}/{part}")) + let contents: SecretData = fs + .read_file_text(out.join("part")) .await? .parse() .map_err(|e| anyhow!("failed to decode secret {out:?} part {part:?}: {e}"))?; @@ -178,11 +188,12 @@ ); } - let created_at = host_on.read_file_value(format!("{out}/created_at")).await?; - let expires_at = host_on - .read_file_value(format!("{out}/expires_at")) - .await - .ok(); + let created_at = fs.read_file_value(out.join("created_at")).await??; + let expires_at = match fs.read_file_value(out.join("expires_at")).await { + Ok(v) => Some(v?), + Err(remowt_endpoints::fs::Error::NotFound) => None, + Err(e) => return Err(e.into()), + }; let new_data = FleetSecretData { created_at, --- a/crates/fleet-base/src/secret_storage.rs +++ /dev/null @@ -1,278 +0,0 @@ -use anyhow::{Result, bail, ensure}; -use itertools::Itertools; -use std::fs::{File, metadata}; -use std::io::{self, ErrorKind, Read, Write}; -use std::path::PathBuf; -use std::str::FromStr; -use std::{env, fs}; - -use tempfile::{TempPath, tempfile_in}; -use toml_edit::{Document, DocumentMut, Formatted, Item, Value}; - -struct Name(String); - -fn encode_name(name: &str) -> Name { - assert!( - !name.starts_with(['_', '.']), - "groups should not start with _ or ." - ); - assert!( - !name.chars().any(|c| c == '/'), - "group name should not contain internal slash" - ); - Name(name.to_owned()) -} - -enum RewriteError { - ConcurrentCreate, - ConcurrentDelete, - ConcurrentModify, - ConcurrentWrite, - Io(io::Error), - Persist(tempfile::PersistError), -} - -fn safe_rewrite( - path: &PathBuf, - old_content: Option>, - new_content: Option>, -) -> Result<(), RewriteError> { - let mut f = match (old_content.is_some(), new_content.is_some()) { - (false, true) => match File::create_new(path) { - Ok(v) => v, - Err(e) if e.kind() == ErrorKind::AlreadyExists => { - return Err(RewriteError::ConcurrentCreate); - } - Err(e) => return Err(RewriteError::Io(e)), - }, - (true, _) => match File::open(&path) { - Ok(v) => v, - Err(_e) => return Err(RewriteError::ConcurrentDelete), - }, - (false, false) => match metadata(&path) { - Err(e) if e.kind() == ErrorKind::NotFound => { - return Ok(()); - } - Ok(_) => return Err(RewriteError::ConcurrentCreate), - Err(e) => return Err(RewriteError::Io(e)), - }, - }; - f.lock().map_err(RewriteError::Io)?; - let mut check_content = vec![]; - f.read_to_end(&mut check_content) - .map_err(RewriteError::Io)?; - match &old_content { - Some(old) => { - if old != &check_content { - return Err(RewriteError::ConcurrentModify); - } - } - None => { - if !check_content.is_empty() { - return Err(RewriteError::ConcurrentDelete); - } - } - } - if let Some(new_content) = new_content { - if Some(&new_content) == old_content.as_ref() { - return Ok(()); - } - let dir = path.parent().expect("file is in directory, thus not root"); - let mut tempfile = tempfile::Builder::new() - .prefix(".rewrite-") - .tempfile_in(dir) - .map_err(RewriteError::Io)?; - tempfile.write_all(&new_content).map_err(RewriteError::Io)?; - tempfile.flush().map_err(RewriteError::Io)?; - tempfile.persist(path).map_err(RewriteError::Persist)?; - } else { - fs::remove_file(path).map_err(RewriteError::Io)?; - } - let _ = f.unlock(); - Ok(()) -} -fn update_string(path: PathBuf, modify: impl Fn(&mut Option) -> Result<()>) -> Result<()> { - loop { - let orig = match fs::read_to_string(&path) { - Ok(v) => Some(v), - Err(e) if e.kind() == ErrorKind::NotFound => None, - Err(e) => return Err(e.into()), - }; - let mut edit = orig.clone(); - modify(&mut edit); - - match safe_rewrite(&path, orig.map(String::into), edit.map(String::into)) { - Ok(()) => return Ok(()), - Err( - RewriteError::ConcurrentCreate - | RewriteError::ConcurrentModify - | RewriteError::ConcurrentWrite - | RewriteError::ConcurrentDelete, - ) => { - continue; - } - Err(RewriteError::Io(io)) => return Err(io.into()), - Err(RewriteError::Persist(io)) => return Err(io.into()), - } - } -} -fn update_toml(path: PathBuf, modify: impl Fn(&mut DocumentMut) -> Result<()>) -> Result<()> { - update_string(path, |str| { - let mut doc = match str { - None => DocumentMut::new(), - Some(v) => DocumentMut::from_str(v)?, - }; - modify(&mut doc)?; - if doc.is_empty() { - *str = None - } else { - *str = Some(doc.to_string()) - } - Ok(()) - }) -} -fn update_lines(path: PathBuf, modify: impl Fn(&mut Vec) -> Result<()>) -> Result<()> { - update_string(path, |str| { - let mut list = if let Some(str) = str { - str.split('\n').map(|s| s.to_owned()).collect_vec() - } else { - vec![] - }; - let had_end_newline = if list.last().map(|v| v.as_str()) == Some("") { - list.pop(); - true - } else { - false - }; - modify(&mut list)?; - if list.is_empty() { - *str = None - } else { - if had_end_newline { - list.push("".to_owned()) - } - *str = Some(list.join("\n")); - } - Ok(()) - }) -} -fn update_section( - data: &mut Vec, - start: &str, - end: &str, - modify: impl Fn(&mut Vec) -> Result<()>, -) -> Result<()> { - let first = data - .iter() - .enumerate() - .filter(|(_, v)| *v == start) - .at_most_one() - .map_err(|_| anyhow::anyhow!("there should be at most one section start"))? - .map(|(v, _)| v); - let last = data - .iter() - .enumerate() - .filter(|(_, v)| *v == end) - .at_most_one() - .map_err(|_| anyhow::anyhow!("there should be at most one section end"))? - .map(|(v, _)| v); - - match (first, last) { - (None, None) => { - let mut out = Vec::new(); - modify(&mut out)?; - if out.is_empty() { - return Ok(()); - } - data.push(start.to_owned()); - data.extend(out); - data.push(end.to_owned()); - Ok(()) - } - (None, Some(_)) | (Some(_), None) => { - bail!("mismatched section start/end") - } - (Some(first), Some(last)) => { - ensure!(first < last, "section end should come after start"); - let mut out = data[first + 1..last] - .iter() - .map(|v| v.to_owned()) - .collect_vec(); - modify(&mut out)?; - if out.is_empty() { - data.drain(first..=last); - } else { - data.splice(first + 1..last, out); - } - Ok(()) - } - } -} - -struct Group { - path: PathBuf, -} -impl Group { - fn new(path: PathBuf) -> Self { - Self { path } - } - fn manage(&self, manager: &str) {} - fn ensure_managing(&self, manager: &str) { - if !self.has_stored() { - return; - } - let managed = match fs::read_to_string(self.path.join(".managed_by")) { - Ok(found_manager) => found_manager.lines().any(|line| line == manager), - Err(e) if e.kind() == ErrorKind::NotFound => true, - Err(e) => panic!("{e}"), - }; - assert!(managed); - } - fn has_stored(&self) -> bool { - match fs::metadata(&self.path) { - Ok(d) => d.is_dir(), - Err(e) if e.kind() == ErrorKind::NotFound => false, - Err(e) => panic!("{e}"), - } - } -} - -struct Root { - path: PathBuf, -} -impl Root { - fn new(path: PathBuf) -> Self { - Self { path } - } - fn subgroup(&self, name: &str) -> Group { - Group::new(self.path.join(name)) - } -} - -#[test] -fn test() { - let mut data = vec![ - "a".to_owned(), - "b".to_owned(), - "start".to_owned(), - "c".to_owned(), - "d".to_owned(), - "end".to_owned(), - "e".to_owned(), - "f".to_owned(), - ]; - update_section(&mut data, "start", "end", |a| { - a.push("vv".to_owned()); - Ok(()) - }) - .unwrap(); - dbg!(&data); - // for v in 0..1000 { - // update_toml(PathBuf::from("./test.toml"), |e| { - // e.as_table_mut() - // .insert("hello", Item::Value(Value::Integer(Formatted::new(v)))); - // }) - // .expect("update") - // } - // v.subgroup(name) -} --- a/crates/nix-eval/Cargo.toml +++ b/crates/nix-eval/Cargo.toml @@ -10,10 +10,11 @@ nixlike.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true -thiserror.workspace = true tracing.workspace = true +camino = { workspace = true, features = ["serde1"] } cxx.workspace = true +futures.workspace = true itertools.workspace = true test-log.workspace = true tokio.workspace = true --- a/crates/nix-eval/src/drv.rs +++ b/crates/nix-eval/src/drv.rs @@ -66,7 +66,13 @@ #[derive(Debug, Deserialize)] pub struct DrvParsed { pub inputs: DrvInputs, - pub outputs: HashMap, + pub outputs: HashMap, +} + +#[derive(Debug, Deserialize)] +pub struct DrvParsedOutput { + #[serde(default)] + pub path: Option, } #[derive(Debug, Deserialize)] @@ -82,18 +88,19 @@ pub outputs: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DrvGraph { pub root: String, pub nodes: HashMap, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DrvNode { pub name: String, pub input_drvs: HashMap>, pub input_srcs: Vec, - pub outputs: Vec, + // TODO: CA outputs without a known paths are skipped + pub outputs: HashMap, } impl DrvGraph { @@ -124,19 +131,59 @@ } } + let outputs: HashMap = parsed + .outputs + .into_iter() + .filter_map(|(name, out)| out.path.map(|p| (name, to_absolute_store_path(&sd, &p)))) + .collect(); + nodes.insert( path.clone(), DrvNode { name: extract_drv_name(&path), input_drvs, input_srcs: parsed.inputs.srcs, - outputs: parsed.outputs.into_keys().collect(), + outputs, }, ); } Ok(Self { root, nodes }) } + + pub fn wanted_outputs(&self, root_outputs: &[String]) -> HashMap> { + let mut wanted: HashMap> = HashMap::new(); + wanted.insert(self.root.clone(), root_outputs.iter().cloned().collect()); + + let mut queue: VecDeque = VecDeque::new(); + queue.push_back(self.root.clone()); + while let Some(path) = queue.pop_front() { + let Some(node) = self.nodes.get(&path) else { + continue; + }; + for (dep_path, dep_outputs) in &node.input_drvs { + let entry = wanted.entry(dep_path.clone()).or_default(); + let mut changed = false; + for o in dep_outputs { + if entry.insert(o.clone()) { + changed = true; + } + } + if changed { + queue.push_back(dep_path.clone()); + } + } + } + + wanted + .into_iter() + .map(|(k, v)| { + let mut v: Vec<_> = v.into_iter().collect(); + v.sort(); + (k, v) + }) + .collect() + } } fn extract_drv_name(drv_path: &str) -> String { --- a/crates/nix-eval/src/lib.cc +++ b/crates/nix-eval/src/lib.cc @@ -1,8 +1,25 @@ #include "nix-eval/src/lib.rs" #include "lib.hh" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include struct nix_fetchers_settings { nix::ref settings; @@ -15,3 +32,175 @@ bool result = settings_ref->set(setting, value); } } + +rust::String switch_profile(Store *store, rust::Str profile, + rust::Str store_path) { + try { + auto nixStore = store->ptr; + auto *lfs = dynamic_cast(&*nixStore); + if (!lfs) + return rust::String("destination is not a local-fs store"); + auto path = nixStore->parseStorePath(std::string(store_path)); + std::filesystem::path prof = std::string(profile); + auto gen = nix::createGeneration(*lfs, prof, path); + nix::switchLink(prof, gen); + return rust::String(); + } catch (const std::exception &e) { + return rust::String(e.what()); + } +} + +rust::String sign_closure(Store *store, rust::Str store_path, + rust::Str key_file) { + try { + auto nixStore = store->ptr; + nix::LocalSigner signer( + nix::SecretKey::parse(nix::readFile(std::string(key_file)))); + auto root = nixStore->parseStorePath(std::string(store_path)); + nix::StorePathSet closure; + nixStore->computeFSClosure(root, closure); + for (auto &p : closure) { + auto info = nixStore->queryPathInfo(p); + nix::ValidPathInfo info2(*info); + info2.sign(*nixStore, signer); + nixStore->addSignatures(p, info2.sigs); + } + return rust::String(); + } catch (const std::exception &e) { + return rust::String(e.what()); + } +} + +CxxListGenerationsResult list_generations(rust::Str profile_path) { + CxxListGenerationsResult out{rust::String(), {}}; + try { + auto [gens, current] = + nix::findGenerations(std::filesystem::path(std::string(profile_path))); + for (auto &g : gens) { + CxxProfileGeneration cg{}; + cg.id = g.number; + cg.store_path = rust::String(g.path.string()); + cg.creation_time_unix = static_cast(g.creationTime); + cg.current = current.has_value() && *current == g.number; + out.generations.push_back(cg); + } + } catch (const std::exception &e) { + out.error = rust::String(e.what()); + } + return out; +} + +static std::vector split_lines(rust::Str joined) { + std::vector out; + std::string buf; + std::istringstream iss((std::string(joined))); + while (std::getline(iss, buf)) { + if (!buf.empty()) + out.push_back(buf); + } + return out; +} + +CxxBuildResult build_drv_outputs(Store *store, rust::Str drv_path, + // TODO: Vec + rust::Str output_names_joined) { + CxxBuildResult res{rust::String(), {}}; + try { + auto nixStore = store->ptr; + auto sp = nixStore->parseStorePath(std::string(drv_path)); + + nix::DerivedPath::Built built{ + .drvPath = nix::makeConstantStorePathRef(sp), + .outputs = nix::OutputsSpec{nix::OutputsSpec::All{}}, + }; + auto names = split_lines(output_names_joined); + if (!names.empty()) { + std::set> nameSet; + for (auto &n : names) + nameSet.insert(n); + built.outputs = + nix::OutputsSpec{nix::OutputsSpec::Names{std::move(nameSet)}}; + } + + std::vector reqs; + reqs.push_back(built); + + auto results = nixStore->buildPathsWithResults(reqs); + for (auto &r : results) { + if (auto *failure = r.tryGetFailure()) { + res.error = rust::String(failure->what()); + return res; + } + } + for (auto &r : results) { + if (auto *success = r.tryGetSuccess()) { + for (auto &[name, real] : success->builtOutputs) { + res.outputs.push_back( + rust::String(nixStore->printStorePath(real.outPath))); + } + } + } + } catch (const std::exception &e) { + res.error = rust::String(e.what()); + } + return res; +} + +CxxBuildResult substitute_paths(Store *store, rust::Str paths_joined) { + CxxBuildResult res{rust::String(), {}}; + try { + auto nixStore = store->ptr; + auto paths = split_lines(paths_joined); + + std::vector reqs; + reqs.reserve(paths.size()); + for (auto &p : paths) { + reqs.push_back(nix::DerivedPath::Opaque{nixStore->parseStorePath(p)}); + } + + // Substituter miss will cause scheduler to trigger build + auto results = nixStore->buildPathsWithResults(reqs); + for (auto &r : results) { + if (r.tryGetSuccess() == nullptr) + continue; + if (auto *opaque = std::get_if(&r.path.raw())) { + res.outputs.push_back( + rust::String(nixStore->printStorePath(opaque->path))); + } + } + } catch (const std::exception &e) { + res.error = rust::String(e.what()); + } + return res; +} + +bool is_valid_path(Store *store, rust::Str path) { + try { + auto nixStore = store->ptr; + auto sp = nixStore->parseStorePath(std::string(path)); + return nixStore->isValidPath(sp); + } catch (const std::exception &) { + return false; + } +} + +AddFileToStoreResult add_file_to_store(Store *store, rust::Str name, + rust::Str path) { + AddFileToStoreResult out{rust::String(), rust::String(), rust::String()}; + try { + auto nixStore = store->ptr; + auto src = nix::PosixSourceAccessor::createAtRoot( + std::filesystem::path(std::string(path))); + auto info = nixStore->addToStoreSlow(std::string(name), src, + nix::ContentAddressMethod::Raw::Flat, + nix::HashAlgorithm::SHA256); + out.store_path = rust::String(nixStore->printStorePath(info.path)); + if (info.ca.has_value()) { + out.hash = + rust::String(info.ca->hash.to_string(nix::HashFormat::SRI, true)); + } + } catch (const std::exception &e) { + out.error = rust::String(e.what()); + } + return out; +} --- a/crates/nix-eval/src/lib.hh +++ b/crates/nix-eval/src/lib.hh @@ -1,7 +1,32 @@ #pragma once +#include "rust/cxx.h" #include +struct Store; + +struct AddFileToStoreResult; +struct CxxBuildResult; + extern "C" { void set_fetcher_setting(nix_fetchers_settings *settings, const char *setting, const char *value); } + +rust::String switch_profile(Store *store, rust::Str profile, + rust::Str store_path); + +rust::String sign_closure(Store *store, rust::Str store_path, + rust::Str key_file); + +struct CxxListGenerationsResult; +CxxListGenerationsResult list_generations(rust::Str profile_path); + +AddFileToStoreResult add_file_to_store(Store *store, rust::Str name, + rust::Str path); + +CxxBuildResult build_drv_outputs(Store *store, rust::Str drv_path, + rust::Str output_names_joined); + +CxxBuildResult substitute_paths(Store *store, rust::Str paths_joined); + +bool is_valid_path(Store *store, rust::Str path); --- a/crates/nix-eval/src/lib.rs +++ b/crates/nix-eval/src/lib.rs @@ -1,12 +1,13 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::collections::HashMap; use std::ffi::{CStr, CString, c_char, c_int, c_uint, c_void}; use std::ptr::{null, null_mut}; use std::sync::{Arc, LazyLock, OnceLock}; use std::{array, fmt, slice}; -use std::{collections::HashMap, path::PathBuf}; use anyhow::{Context, anyhow, bail}; +use camino::{Utf8Path, Utf8PathBuf}; use itertools::Itertools; use serde::Serialize; use serde::de::DeserializeOwned; @@ -38,8 +39,9 @@ make_attrs, make_bindings_builder, make_list, make_list_builder, realised_string, realised_string_free, realised_string_get_buffer_size, realised_string_get_buffer_start, realised_string_get_store_path, realised_string_get_store_path_count, register_primop, - set_err_msg, setting_set, state_free, store_open, store_parse_path, store_path_free, - store_path_name, string_realise, value, value_call, value_decref, value_force, value_incref, + set_err_msg, setting_set, state_free, store_copy_closure, store_free, store_open, + store_parse_path, store_path_free, store_path_name, string_realise, value, value_call, + value_decref, value_force, value_incref, }; // Contains macros helpers @@ -47,6 +49,7 @@ pub mod logging; #[doc(hidden)] pub mod macros; +pub mod scheduler; #[doc(hidden)] pub mod __macro_support { @@ -68,8 +71,28 @@ } #[cxx::bridge] pub mod nix_cxx { + struct AddFileToStoreResult { + error: String, + store_path: String, + hash: String, + } + struct CxxProfileGeneration { + id: u64, + store_path: String, + creation_time_unix: i64, + current: bool, + } + struct CxxListGenerationsResult { + error: String, + generations: Vec, + } + struct CxxBuildResult { + error: String, + outputs: Vec, + } unsafe extern "C++" { type nix_fetchers_settings; + type Store; include!("nix-eval/src/lib.hh"); #[allow(clippy::missing_safety_doc)] @@ -78,6 +101,34 @@ setting: *const c_char, value: *const c_char, ); + + #[allow(clippy::missing_safety_doc)] + unsafe fn switch_profile(store: *mut Store, profile: &str, store_path: &str) -> String; + + #[allow(clippy::missing_safety_doc)] + unsafe fn sign_closure(store: *mut Store, store_path: &str, key_file: &str) -> String; + + #[allow(clippy::missing_safety_doc)] + unsafe fn add_file_to_store( + store: *mut Store, + name: &str, + path: &str, + ) -> AddFileToStoreResult; + + fn list_generations(profile_path: &str) -> CxxListGenerationsResult; + + #[allow(clippy::missing_safety_doc)] + unsafe fn build_drv_outputs( + store: *mut Store, + drv_path: &str, + output_names_joined: &str, + ) -> CxxBuildResult; + + #[allow(clippy::missing_safety_doc)] + unsafe fn substitute_paths(store: *mut Store, paths_joined: &str) -> CxxBuildResult; + + #[allow(clippy::missing_safety_doc)] + unsafe fn is_valid_path(store: *mut Store, path: &str) -> bool; } } @@ -186,7 +237,7 @@ unsafe { set_err_msg(self.0, err as c_int, msg.as_ptr()) }; } pub fn set_err(&mut self, err: anyhow::Error) { - let mut fmt = format!("{err:?}").replace("\0", "\\0"); + let fmt = format!("{err:?}").replace("\0", "\\0"); self.set_err_raw( NixErrorKind::Generic, &CString::new(fmt).expect("NUL bytes were just replaced"), @@ -352,6 +403,125 @@ with_default_context(|c, _| unsafe { setting_set(c, s.as_ptr(), v.as_ptr()) }).map(|_| ()) } +#[instrument(skip(dst))] +pub fn copy_closure_to(dst: &Store, path: &Utf8Path) -> Result<()> { + let path_c = CString::new(path.as_str())?; + with_store_context(|c, src_store, _state| -> Result<()> { + let sp = unsafe { store_parse_path(c, src_store, path_c.as_ptr()) }; + if sp.is_null() { + bail!("failed to parse store path {path}"); + } + let rc = unsafe { store_copy_closure(c, src_store, dst.0, sp) }; + unsafe { store_path_free(sp) }; + if rc != nix_raw::err_NIX_OK { + bail!("store_copy_closure failed (code {rc})"); + } + Ok(()) + })? +} + +#[instrument] +pub fn switch_profile(profile: &str, store_path: &Utf8Path) -> Result<()> { + let msg = with_store_context(|_c, store, _state| unsafe { + nix_cxx::switch_profile(store.cast(), profile, store_path.as_str()) + })? + .to_string(); + if msg.is_empty() { + Ok(()) + } else { + bail!("failed to switch profile {profile}: {msg}"); + } +} + +// TODO: fleet operator-managed key file +#[instrument] +pub fn sign_closure(store_path: &str, key_file: &str) -> Result<()> { + let msg = with_store_context(|_c, store, _state| unsafe { + nix_cxx::sign_closure(store.cast(), store_path, key_file) + })? + .to_string(); + if msg.is_empty() { + Ok(()) + } else { + bail!("failed to sign {store_path}: {msg}"); + } +} + +#[derive(Debug)] +pub struct AddedFile { + pub store_path: Utf8PathBuf, + pub hash: String, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct ProfileGeneration { + pub id: u64, + pub store_path: Utf8PathBuf, + pub creation_time_unix: i64, + pub current: bool, +} + +#[instrument] +pub fn list_generations(profile_path: &str) -> Result> { + let res = nix_cxx::list_generations(profile_path); + if !res.error.is_empty() { + bail!( + "failed to list generations at {profile_path}: {}", + res.error + ); + } + Ok(res + .generations + .into_iter() + .map(|g| ProfileGeneration { + id: g.id, + store_path: Utf8PathBuf::from(g.store_path), + creation_time_unix: g.creation_time_unix, + current: g.current, + }) + .collect()) +} + +#[instrument] +pub fn add_file_to_store(name: &str, path: &Utf8Path) -> Result { + let res = with_store_context(|_c, store, _state| unsafe { + nix_cxx::add_file_to_store(store.cast(), name, path.as_str()) + })?; + if !res.error.is_empty() { + bail!("failed to add {path} to store: {}", res.error); + } + Ok(AddedFile { + store_path: Utf8PathBuf::from(res.store_path), + hash: res.hash, + }) +} + +pub fn build_drv_outputs(drv_path: &str, output_names: &[String]) -> Result> { + let joined = output_names.join("\n"); + let res = with_store_context(|_c, store, _state| unsafe { + nix_cxx::build_drv_outputs(store.cast(), drv_path, &joined) + })?; + if !res.error.is_empty() { + bail!("build of {drv_path} failed: {}", res.error); + } + Ok(res.outputs) +} + +pub fn substitute_paths(paths: &[String]) -> Result> { + let joined = paths.join("\n"); + let res = with_store_context(|_c, store, _state| unsafe { + nix_cxx::substitute_paths(store.cast(), &joined) + })?; + if !res.error.is_empty() { + warn!("substitute_paths reported: {}", res.error); + } + Ok(res.outputs) +} + +pub fn is_valid_path(path: &str) -> Result { + with_store_context(|_c, store, _state| unsafe { nix_cxx::is_valid_path(store.cast(), path) }) +} + pub struct FetchSettings(*mut fetchers_settings); impl FetchSettings { pub fn new() -> Self { @@ -450,17 +620,31 @@ unsafe { *user_data.cast::() = s.to_owned() }; } -struct Store(*mut c_store); +pub struct Store(*mut c_store); unsafe impl Send for Store {} unsafe impl Sync for Store {} impl Store { + pub fn open(uri: &str) -> Result { + let uri = CString::new(uri)?; + let ptr = with_default_context(|c, _| unsafe { store_open(c, uri.as_ptr(), null_mut()) })?; + if ptr.is_null() { + bail!("failed to open store"); + } + Ok(Store(ptr)) + } + fn parse_path(&self, path: &CStr) -> Result { with_default_context(|c, _| { StorePath(unsafe { store_parse_path(c, self.0, path.as_ptr()) }) }) } } +impl Drop for Store { + fn drop(&mut self) { + unsafe { store_free(self.0) } + } +} #[repr(transparent)] pub struct EvalState(*mut c_eval_state); @@ -858,7 +1042,7 @@ Ok(out) } #[instrument(name = "build", skip(self), fields(output))] - pub fn build(&self, output: &str) -> Result { + pub fn build(&self, output: &str) -> Result { if !self.is_derivation() { bail!("expected derivation to build") } @@ -880,14 +1064,15 @@ .get_field("drvPath") .context("getting drvPath")? .to_string()?; - let graph = drv::DrvGraph::resolve(&drv_path)?; + let graph = Arc::new(drv::DrvGraph::resolve(&drv_path)?); let _guard = logging::register_build_graph(&Span::current(), &graph); - // to_string here blocks until the path is built + scheduler::build_graph_sync(graph.clone(), vec![output.to_owned()])?; + let s = v.builtin_to_string()?; let rs = s.to_realised_string()?; let out_path = rs.as_str().to_owned(); - Ok(PathBuf::from(out_path)) + Ok(Utf8PathBuf::from(out_path)) } pub fn as_json(&self) -> Result { let to_json = Self::eval("builtins.toJSON")?; --- a/crates/nix-eval/src/logging.cc +++ b/crates/nix-eval/src/logging.cc @@ -96,7 +96,9 @@ extern "C" { void apply_tracing_logger() { - logger = std::make_unique(); + auto old = logger; + logger = new TracingLogger(); + delete old; // verbosity = lvlVomit; } rust::Box --- a/crates/nix-eval/src/logging.rs +++ b/crates/nix-eval/src/logging.rs @@ -499,7 +499,9 @@ } else if level == Level::INFO { info!(target: "nix", "{}", s) } else if level == Level::DEBUG { - debug!(target: "nix", "{}", s) + if s != "querying info about missing paths" { + debug!(target: "nix", "{}", s) + } } else { trace!(target: "nix", "{}", s) } @@ -583,7 +585,9 @@ } else if level == Level::INFO { info!(target: "nix", "{v}") } else if level == Level::DEBUG { - debug!(target: "nix", "{v}") + if v != "querying info about missing paths" { + debug!(target: "nix", "{v}") + } } else { trace!(target: "nix", "{v}") } --- /dev/null +++ b/crates/nix-eval/src/scheduler.rs @@ -0,0 +1,372 @@ +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +use anyhow::{Context, Result, bail}; +use futures::stream::{FuturesUnordered, StreamExt}; +use tokio::sync::{Semaphore, broadcast}; +use tracing::{debug, info, instrument, warn}; + +use crate::drv::DrvGraph; + +#[derive(Clone, Debug)] +pub enum BuildEvent { + SubstitutePrepassStarted { + paths: usize, + }, + SubstitutePrepassFinished { + satisfied: usize, + }, + DrvStarted { + drv_path: String, + name: String, + wanted: Vec, + }, + DrvSkipped { + drv_path: String, + name: String, + }, + DrvFinished { + drv_path: String, + name: String, + }, + DrvFailed { + drv_path: String, + name: String, + error: String, + }, + DrvCancelled { + drv_path: String, + name: String, + failed_dep: String, + }, +} + +pub struct Scheduler { + parallelism: usize, + events: broadcast::Sender, +} + +impl Scheduler { + pub fn new(parallelism: usize) -> Self { + let parallelism = parallelism.max(1); + let (events, _) = broadcast::channel(1024); + Self { + parallelism, + events, + } + } + + pub fn subscribe(&self) -> broadcast::Receiver { + self.events.subscribe() + } + + #[instrument(name = "scheduler", skip(self, graph), fields(root = %graph.root, nodes = graph.nodes.len()))] + pub async fn run(&self, graph: Arc, root_outputs: Vec) -> Result<()> { + let wanted = graph.wanted_outputs(&root_outputs); + + self.substitute_prepass(&graph, &wanted).await?; + self.build_topo(&graph, wanted).await + } + + async fn substitute_prepass( + &self, + graph: &DrvGraph, + wanted: &HashMap>, + ) -> Result<()> { + let paths = collect_substitute_paths(graph, wanted); + if paths.is_empty() { + return Ok(()); + } + let _ = self + .events + .send(BuildEvent::SubstitutePrepassStarted { paths: paths.len() }); + debug!("substitute pre-pass: {} paths", paths.len()); + + let satisfied = tokio::task::spawn_blocking(move || crate::substitute_paths(&paths)) + .await + .expect("substitute pre-pass task should not panic")?; + + let _ = self.events.send(BuildEvent::SubstitutePrepassFinished { + satisfied: satisfied.len(), + }); + Ok(()) + } + + async fn build_topo( + &self, + graph: &Arc, + wanted: HashMap>, + ) -> Result<()> { + let mut indeg: HashMap = graph + .nodes + .iter() + .map(|(k, n)| (k.clone(), n.input_drvs.len())) + .collect(); + let mut dependents: HashMap> = HashMap::new(); + for (path, node) in &graph.nodes { + for dep in node.input_drvs.keys() { + dependents + .entry(dep.clone()) + .or_default() + .push(path.clone()); + } + } + + let sem = Arc::new(Semaphore::new(self.parallelism)); + let mut ready: Vec = indeg + .iter() + .filter(|(_, d)| **d == 0) + .map(|(k, _)| k.clone()) + .collect(); + let mut in_flight = FuturesUnordered::new(); + let mut failed: HashMap = HashMap::new(); + // Tainted = transitively depends on a failed drv + let mut tainted: HashMap = HashMap::new(); + + loop { + let batch: Vec = std::mem::take(&mut ready); + for path in batch { + if let Some(failed_dep) = tainted.get(&path) { + let name = graph + .nodes + .get(&path) + .map(|n| n.name.clone()) + .unwrap_or_default(); + let _ = self.events.send(BuildEvent::DrvCancelled { + drv_path: path.clone(), + name, + failed_dep: failed_dep.clone(), + }); + propagate_done(&dependents, &mut indeg, &mut ready, &path); + continue; + } + + let sem = sem.clone(); + let events = self.events.clone(); + let graph = graph.clone(); + let wanted_here = wanted.get(&path).cloned().unwrap_or_default(); + in_flight.push(tokio::spawn(async move { + let _permit = sem.acquire_owned().await.expect("semaphore not closed"); + let node = graph + .nodes + .get(&path) + .expect("ready node must be in graph") + .clone(); + let name = node.name.clone(); + + let all_valid = !wanted_here.is_empty() + && wanted_here.iter().all(|o| { + node.outputs + .get(o) + .map(|p| crate::is_valid_path(p).unwrap_or(false)) + .unwrap_or(false) + }); + if all_valid { + let _ = events.send(BuildEvent::DrvSkipped { + drv_path: path.clone(), + name: name.clone(), + }); + return (path, name, Ok::<(), anyhow::Error>(())); + } + + let _ = events.send(BuildEvent::DrvStarted { + drv_path: path.clone(), + name: name.clone(), + wanted: wanted_here.clone(), + }); + + let path_for_build = path.clone(); + let res = tokio::task::spawn_blocking(move || { + crate::build_drv_outputs(&path_for_build, &wanted_here) + }) + .await + .expect("build task should not panic"); + + match res { + Ok(_) => { + let _ = events.send(BuildEvent::DrvFinished { + drv_path: path.clone(), + name: name.clone(), + }); + (path, name, Ok(())) + } + Err(e) => { + let msg = format!("{e:#}"); + let _ = events.send(BuildEvent::DrvFailed { + drv_path: path.clone(), + name: name.clone(), + error: msg, + }); + (path, name, Err(e)) + } + } + })); + } + + let Some(joined) = in_flight.next().await else { + break; + }; + let (finished, _name, res) = match joined { + Ok(t) => t, + Err(e) => bail!("scheduler task panicked: {e}"), + }; + match res { + Ok(()) => { + propagate_done(&dependents, &mut indeg, &mut ready, &finished); + } + Err(e) => { + failed.insert(finished.clone(), format!("{e:#}")); + mark_tainted(&dependents, &finished, &mut tainted); + propagate_done(&dependents, &mut indeg, &mut ready, &finished); + } + } + } + + let stuck: Vec<_> = indeg + .iter() + .filter(|(_, d)| **d != 0) + .map(|(k, _)| k.as_str()) + .collect(); + if !stuck.is_empty() { + warn!( + "scheduler finished with {} nodes still pending (loop?)", + stuck.len() + ); + } + + if failed.is_empty() { + info!("scheduler completed"); + Ok(()) + } else { + let mut report = format!("{} drv(s) failed to build:", failed.len()); + let mut sorted: Vec<_> = failed.iter().collect(); + sorted.sort_by(|a, b| a.0.cmp(b.0)); + for (path, err) in sorted { + let name = graph + .nodes + .get(path) + .map(|n| n.name.as_str()) + .unwrap_or("?"); + let chain = path_to_root(graph, path); + report.push_str(&format!( + "\n\n {name} ({path}):\n {err}\n needed by: {}", + chain.join(" => "), + )); + } + Err(anyhow::anyhow!(report)) + } + } +} + +fn propagate_done( + dependents: &HashMap>, + indeg: &mut HashMap, + ready: &mut Vec, + finished: &str, +) { + if let Some(deps) = dependents.get(finished) { + for d in deps { + let entry = indeg.get_mut(d).expect("dependent must have indeg"); + *entry = entry.saturating_sub(1); + if *entry == 0 { + ready.push(d.clone()); + } + } + } +} + +fn mark_tainted( + dependents: &HashMap>, + failed: &str, + tainted: &mut HashMap, +) { + let mut queue: Vec = dependents.get(failed).cloned().unwrap_or_default(); + while let Some(node) = queue.pop() { + if tainted + .entry(node.clone()) + .or_insert_with(|| failed.to_owned()) + == failed + { + if let Some(deps) = dependents.get(&node) { + for d in deps { + if !tainted.contains_key(d) { + queue.push(d.clone()); + } + } + } + } + } +} + +fn path_to_root(graph: &DrvGraph, from: &str) -> Vec { + let mut dependents: HashMap<&str, Vec<&str>> = HashMap::new(); + for (path, node) in &graph.nodes { + for dep in node.input_drvs.keys() { + dependents + .entry(dep.as_str()) + .or_default() + .push(path.as_str()); + } + } + + let mut chain: Vec = vec![node_name(graph, from)]; + let mut cur = from; + let mut seen: HashSet<&str> = HashSet::new(); + seen.insert(cur); + while cur != graph.root.as_str() { + let Some(next) = dependents.get(cur).and_then(|v| v.first().copied()) else { + break; + }; + if !seen.insert(next) { + break; + } + chain.push(node_name(graph, next)); + cur = next; + } + chain +} + +fn node_name(graph: &DrvGraph, path: &str) -> String { + graph + .nodes + .get(path) + .map(|n| n.name.clone()) + .unwrap_or_else(|| path.to_owned()) +} + +fn collect_substitute_paths( + graph: &DrvGraph, + wanted: &HashMap>, +) -> Vec { + let mut paths: HashSet = HashSet::new(); + for node in graph.nodes.values() { + for src in &node.input_srcs { + paths.insert(src.clone()); + } + } + for (path, outs) in wanted { + let Some(node) = graph.nodes.get(path) else { + continue; + }; + for o in outs { + if let Some(p) = node.outputs.get(o) { + paths.insert(p.clone()); + } + } + } + let mut v: Vec<_> = paths.into_iter().collect(); + v.sort(); + v +} + +// TODO: Parallelism as a metric works poorly with multiple machines, but I haven't thought about bringing +// hercy here yet. In case of remote machines - they will handle parallelism on their own, and this one +// will work as a hard cap. +pub fn build_graph_sync(graph: Arc, root_outputs: Vec) -> Result<()> { + let parallelism = std::thread::available_parallelism() + .map(|p| p.get()) + .unwrap_or(4); + let scheduler = Scheduler::new(parallelism); + crate::await_in_nix(async move { scheduler.run(graph, root_outputs).await }) + .context("scheduler run") +} --- a/crates/nixlike/Cargo.toml +++ b/crates/nixlike/Cargo.toml @@ -11,5 +11,3 @@ linked-hash-map.workspace = true peg.workspace = true serde.workspace = true -serde-transcode.workspace = true -serde_json.workspace = true --- /dev/null +++ b/crates/remowt-fleet/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "remowt-fleet" +description = "Remowt endpoints for fleet" +version.workspace = true +edition = "2021" + +[dependencies] +bifrostlink.workspace = true + +remowt-client.workspace = true +remowt-endpoints.workspace = true + +anyhow.workspace = true +camino.workspace = true +nix-eval.workspace = true +serde = { workspace = true } +thiserror.workspace = true +tokio = { workspace = true, features = ["io-util", "net", "process", "rt"] } +tracing.workspace = true +uuid.workspace = true --- /dev/null +++ b/crates/remowt-fleet/src/lib.rs @@ -0,0 +1,120 @@ +use std::path::PathBuf; + +use anyhow::{Context as _, Result}; +use bifrostlink::Config; +use bifrostlink::declarative::endpoints; +use camino::Utf8PathBuf; +use remowt_client::Remowt; +use remowt_endpoints::nix_daemon::NixDaemonClient; +use serde::{Deserialize, Serialize}; +use tokio::net::UnixListener; +use tracing::error; + +pub struct Nix; +pub use nix_eval::{init_libraries, init_tokio_for_nix}; + +#[derive(Serialize, Deserialize, Debug, thiserror::Error)] +pub enum NixError { + #[error("nix daemon unavailable: {0}")] + DaemonUnavailable(String), + #[error("tunnel socket unavailable: {0}")] + Tunnel(String), + #[error("profile switch failed: {0}")] + Profile(String), + #[error("signing failed: {0}")] + Sign(String), + #[error("listing generations failed: {0}")] + ListGenerations(String), +} + +#[endpoints(ns = 91)] +impl Nix { + #[endpoints(id = 3)] + async fn switch_profile( + &self, + profile: String, + store_path: Utf8PathBuf, + ) -> Result<(), NixError> { + tokio::task::spawn_blocking(move || nix_eval::switch_profile(&profile, &store_path)) + .await + .map_err(|e| NixError::Profile(e.to_string()))? + .map_err(|e| NixError::Profile(e.to_string())) + } + + #[endpoints(id = 4)] + async fn sign_closure( + &self, + store_path: Utf8PathBuf, + key_file: Utf8PathBuf, + ) -> Result<(), NixError> { + tokio::task::spawn_blocking(move || { + nix_eval::sign_closure(store_path.as_str(), key_file.as_str()) + }) + .await + .map_err(|e| NixError::Sign(e.to_string()))? + .map_err(|e| NixError::Sign(e.to_string())) + } + + #[endpoints(id = 5)] + async fn list_generations( + &self, + profile: String, + ) -> Result, NixError> { + tokio::task::spawn_blocking(move || { + nix_eval::list_generations(&format!("/nix/var/nix/profiles/{profile}")) + }) + .await + .map_err(|e| NixError::ListGenerations(e.to_string()))? + .map_err(|e| NixError::ListGenerations(e.to_string())) + } +} + +pub async fn nix_store_socket(conn: Remowt, store: &str) -> Result { + let store = store.to_owned(); + let path = std::env::temp_dir().join(format!("fleet-nix-{}.sock", uuid::Uuid::new_v4())); + let _ = std::fs::remove_file(&path); + let listener = UnixListener::bind(&path)?; + tokio::spawn(async move { + if let Err(e) = serve(conn, listener, store).await { + error!("nix daemon proxy failed: {e}"); + } + }); + Ok(path) +} + +async fn serve(conn: Remowt, listener: UnixListener, store: String) -> Result<()> { + let nix = conn.endpoints::>(); + loop { + let (mut local, _) = listener.accept().await?; + + let (rx, remote_sock) = match conn.bind_runtime_unix("nix-daemon").await { + Ok(rx) => rx, + Err(e) => { + error!("streamlocal_forward failed: {e}"); + continue; + } + }; + let sock_str = remote_sock.as_str().to_owned(); + match nix.serve_store(store.clone(), sock_str).await { + Ok(Ok(())) => {} + Ok(Err(e)) => { + error!("nix bridge: {e}"); + continue; + } + Err(e) => { + error!("nix bridge rpc failed: {e}"); + continue; + } + } + + let mut channel = rx + .accept() + .await + .context("failed to accept remote nix connection")?; + tokio::spawn(async move { + if let Err(e) = tokio::io::copy_bidirectional(&mut local, &mut channel).await { + tracing::debug!("nix tunnel ended: {e}"); + } + }); + } +} --- a/flake.lock +++ b/flake.lock @@ -2,10 +2,10 @@ "nodes": { "crane": { "locked": { - "lastModified": 1773189535, + "lastModified": 1780532242, "owner": "ipetkov", "repo": "crane", - "rev": "6fa2fb4cf4a89ba49fc9dd5a3eb6cde99d388269", + "rev": "59a82a1222dd3b2080b5cc52a1a2e8d5f1b77f37", "type": "github" }, "original": { @@ -37,10 +37,10 @@ ] }, "locked": { - "lastModified": 1772408722, + "lastModified": 1778716662, "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", "type": "github" }, "original": { @@ -62,10 +62,10 @@ ] }, "locked": { - "lastModified": 1759080490, + "lastModified": 1780160585, "owner": "CertainLach", "repo": "fleet-tf", - "rev": "878bd8c23933d628bf750378bbe527b841901c3d", + "rev": "aa2200a3f8f93a8c0d3b87bb29f11d9d9b0a23e6", "type": "github" }, "original": { @@ -111,10 +111,10 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1768702010, + "lastModified": 1781448983, "owner": "deltarocks", "repo": "nix", - "rev": "b05b52670b9c7affff5b9be3edb539a1603c39e6", + "rev": "bb8784f285658e77642437409824619f5c34a854", "type": "github" }, "original": { @@ -126,10 +126,10 @@ }, "nixpkgs": { "locked": { - "lastModified": 1773268153, + "lastModified": 1781402740, "owner": "nixos", "repo": "nixpkgs", - "rev": "63ca4f465b79edbaa9ff113ea7ff48fb49ea0db0", + "rev": "49145c5aba740948d6da1265f584826bd27619ad", "type": "github" }, "original": { @@ -171,6 +171,34 @@ "type": "github" } }, + "remowt-agents": { + "inputs": { + "crane": [ + "crane" + ], + "flake-parts": [ + "flake-parts" + ], + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": "rust-overlay", + "shelly": [ + "shelly" + ] + }, + "locked": { + "lastModified": 1781471358, + "rev": "c69dddc33f8e2081ef685ae20a7278f92cfcd32e", + "revCount": 37, + "type": "git", + "url": "file:/home/lach/build/remowt-agents" + }, + "original": { + "type": "git", + "url": "file:/home/lach/build/remowt-agents" + } + }, "root": { "inputs": { "crane": "crane", @@ -178,7 +206,8 @@ "fleet-tf": "fleet-tf", "nix": "nix", "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay", + "remowt-agents": "remowt-agents", + "rust-overlay": "rust-overlay_2", "shelly": "shelly", "treefmt-nix": "treefmt-nix" } @@ -186,14 +215,15 @@ "rust-overlay": { "inputs": { "nixpkgs": [ + "remowt-agents", "nixpkgs" ] }, "locked": { - "lastModified": 1773216618, + "lastModified": 1769309768, "owner": "oxalica", "repo": "rust-overlay", - "rev": "07d7dc6fcc5eae76b4fb0e19d4afd939437bec97", + "rev": "140c9dc582cb73ada2d63a2180524fcaa744fad5", "type": "github" }, "original": { @@ -202,6 +232,25 @@ "type": "github" } }, + "rust-overlay_2": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781320681, + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "5b929d8c854149d926d05ea0cd6469bf4e54db27", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, "shelly": { "locked": { "lastModified": 1756323923, @@ -223,10 +272,10 @@ ] }, "locked": { - "lastModified": 1772660329, + "lastModified": 1780220602, "owner": "numtide", "repo": "treefmt-nix", - "rev": "3710e0e1218041bbad640352a0440114b1e10428", + "rev": "db947814a175b7ca6ded66e21383d938df01c227", "type": "github" }, "original": { --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,13 @@ url = "github:numtide/treefmt-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + remowt-agents = { + url = "git+file:/home/lach/build/remowt-agents"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-parts.follows = "flake-parts"; + inputs.crane.follows = "crane"; + inputs.shelly.follows = "shelly"; + }; # DeterminateSystem's nix fork is controversial, but I don't mind it, # and it has lazy-trees support which is useful for fleet. nix = { @@ -147,7 +154,7 @@ packages = lib.mkIf deployerSystem ( let packages = pkgs.callPackages ./pkgs { - inherit craneLib inputs'; + inherit craneLib inputs; }; in packages // { default = packages.fleet; } --- a/lib/flakePart.nix +++ b/lib/flakePart.nix @@ -57,7 +57,7 @@ modules = (import ../modules/module-list.nix) ++ [ module ( - { inputs', ... }: + { inputs, ... }: { config = { data = if isPath data then import data else data; @@ -66,12 +66,13 @@ (final: prev: { inherit (import ../pkgs { - inherit (prev) callPackage; - inherit inputs'; + inherit (final) callPackage; + inherit inputs; craneLib = crane.mkLib prev; }) fleet-install-secrets fleet-generator-helper + remowt-plugin-fleet ; }) ]; --- a/modules/nixpkgs.nix +++ b/modules/nixpkgs.nix @@ -42,10 +42,12 @@ default = config.nixpkgs.buildUsing; defaultText = literalExpression "config.nixpkgs.buildUsing"; }; - config.nixos = { - inherit _file; - nixpkgs.overlays = config.nixpkgs.overlays; - }; + config.nixos = + { inputs', ... }: + { + inherit _file; + nixpkgs.overlays = config.nixpkgs.overlays; + }; }; }; } --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,10 +1,11 @@ { callPackage, craneLib, - inputs', + inputs, }: { - fleet = callPackage ./fleet.nix { inherit craneLib inputs'; }; + fleet = callPackage ./fleet.nix { inherit craneLib inputs; }; + remowt-plugin-fleet = callPackage ./remowt-plugin-fleet.nix { inherit craneLib inputs; }; fleet-install-secrets = callPackage ./fleet-install-secrets.nix { inherit craneLib; }; fleet-generator-helper = callPackage ./fleet-generator-helper.nix { inherit craneLib; }; } --- a/pkgs/fleet.nix +++ b/pkgs/fleet.nix @@ -2,10 +2,16 @@ lib, craneLib, installShellFiles, - inputs', + inputs, + + stdenv, pkg-config, rustPlatform, + rofi, }: +let + system = stdenv.hostPlatform.system; +in craneLib.buildPackage rec { pname = "fleet"; src = lib.cleanSourceWith { @@ -20,10 +26,15 @@ cargoExtraArgs = "--locked -p ${pname}"; + REMOWT_AGENTS_DIR = "${inputs.remowt-agents.packages.${system}.remowt-agents}"; + # TODO: built-in fleet prompter should be a prodash widget, or it should require + # tty remowt prompter running on host machine idk. + ROFI = "${rofi}/bin/rofi"; + buildInputs = [ - inputs'.nix.packages.nix-expr-c - inputs'.nix.packages.nix-flake-c - inputs'.nix.packages.nix-fetchers-c + inputs.nix.packages.${system}.nix-expr-c + inputs.nix.packages.${system}.nix-flake-c + inputs.nix.packages.${system}.nix-fetchers-c ]; nativeBuildInputs = [ installShellFiles --- /dev/null +++ b/pkgs/remowt-plugin-fleet.nix @@ -0,0 +1,36 @@ +{ + lib, + craneLib, + inputs, + + stdenv, + pkg-config, + rustPlatform, +}: +let + system = stdenv.hostPlatform.system; +in +craneLib.buildPackage rec { + pname = "remowt-plugin-fleet"; + src = lib.cleanSourceWith { + src = ../.; + filter = + path: type: + (lib.hasSuffix "\.cc" path) + || (lib.hasSuffix "\.hh" path) + || (craneLib.filterCargoSources path type); + }; + strictDeps = true; + + cargoExtraArgs = "--locked -p ${pname}"; + + buildInputs = [ + inputs.nix.packages.${system}.nix-expr-c + inputs.nix.packages.${system}.nix-flake-c + inputs.nix.packages.${system}.nix-fetchers-c + ]; + nativeBuildInputs = [ + pkg-config + rustPlatform.bindgenHook + ]; +} -- gitstuff