git.delta.rocks / jrsonnet / refs/commits / 064468b85673

difftreelog

feat batch builds in legacy eval

Yaroslav Bolyukin2024-11-13parent: #dfbdb4a.patch.diff
in: trunk

17 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,18 +13,18 @@
 
 [[package]]
 name = "addr2line"
-version = "0.22.0"
+version = "0.24.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
 dependencies = [
  "gimli",
 ]
 
 [[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
 
 [[package]]
 name = "aead"
@@ -124,8 +124,8 @@
 
 [[package]]
 name = "alejandra"
-version = "3.0.0"
-source = "git+https://github.com/kamadorueda/alejandra#bb688cc2c22e43b3cd710eadc0340399c2de3151"
+version = "3.1.0"
+source = "git+https://github.com/kamadorueda/alejandra#264e23546663a5676a77174cab31340a81aa2cc0"
 dependencies = [
  "mimalloc",
  "rnix",
@@ -149,9 +149,9 @@
 
 [[package]]
 name = "anstream"
-version = "0.6.14"
+version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -164,43 +164,43 @@
 
 [[package]]
 name = "anstyle"
-version = "1.0.7"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.4"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.0.3"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.3"
+version = "3.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
 dependencies = [
  "anstyle",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.86"
+version = "1.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
 
 [[package]]
 name = "arc-swap"
@@ -210,15 +210,15 @@
 
 [[package]]
 name = "arrayvec"
-version = "0.7.4"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
 [[package]]
 name = "async-stream"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
 dependencies = [
  "async-stream-impl",
  "futures-core",
@@ -227,24 +227,24 @@
 
 [[package]]
 name = "async-stream-impl"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "async-trait"
-version = "0.1.81"
+version = "0.1.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
+checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -255,15 +255,15 @@
 
 [[package]]
 name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
 [[package]]
 name = "axum"
-version = "0.7.5"
+version = "0.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
+checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
 dependencies = [
  "async-trait",
  "axum-core",
@@ -281,16 +281,16 @@
  "rustversion",
  "serde",
  "sync_wrapper 1.0.1",
- "tower",
+ "tower 0.5.1",
  "tower-layer",
  "tower-service",
 ]
 
 [[package]]
 name = "axum-core"
-version = "0.4.3"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
 dependencies = [
  "async-trait",
  "bytes",
@@ -301,24 +301,24 @@
  "mime",
  "pin-project-lite",
  "rustversion",
- "sync_wrapper 0.1.2",
+ "sync_wrapper 1.0.1",
  "tower-layer",
  "tower-service",
 ]
 
 [[package]]
 name = "backtrace"
-version = "0.3.72"
+version = "0.3.74"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
 dependencies = [
  "addr2line",
- "cc",
  "cfg-if",
  "libc",
  "miniz_oxide",
  "object",
  "rustc-demangle",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -340,6 +340,15 @@
 checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 
 [[package]]
+name = "basic-toml"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
+dependencies = [
+ "serde",
+]
+
+[[package]]
 name = "bcrypt-pbkdf"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -368,10 +377,33 @@
 ]
 
 [[package]]
+name = "bindgen"
+version = "0.69.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.12.1",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 2.0.87",
+ "which",
+]
+
+[[package]]
 name = "bitflags"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 dependencies = [
  "serde",
 ]
@@ -424,9 +456,9 @@
 
 [[package]]
 name = "bytes"
-version = "1.6.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
 
 [[package]]
 name = "cbc"
@@ -448,9 +480,21 @@
 
 [[package]]
 name = "cc"
-version = "1.0.98"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
 
 [[package]]
 name = "cfg-if"
@@ -500,7 +544,7 @@
  "num-traits",
  "serde",
  "wasm-bindgen",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -515,10 +559,21 @@
 ]
 
 [[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
 name = "clap"
-version = "4.5.4"
+version = "4.5.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -526,9 +581,9 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.5.2"
+version = "4.5.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
 dependencies = [
  "anstream",
  "anstyle",
@@ -536,41 +591,41 @@
  "strsim 0.11.1",
  "terminal_size",
  "unicase",
- "unicode-width",
+ "unicode-width 0.2.0",
 ]
 
 [[package]]
 name = "clap_complete"
-version = "4.5.2"
+version = "4.5.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e"
+checksum = "11611dca53440593f38e6b25ec629de50b14cdfa63adc0fb856115a2c6d97595"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.5.4"
+version = "4.5.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
 dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.7.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.1"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
 
 [[package]]
 name = "console"
@@ -581,7 +636,7 @@
  "encode_unicode",
  "lazy_static",
  "libc",
- "unicode-width",
+ "unicode-width 0.1.14",
  "windows-sys 0.52.0",
 ]
 
@@ -602,9 +657,9 @@
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "countme"
@@ -614,9 +669,9 @@
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6"
 dependencies = [
  "libc",
 ]
@@ -631,7 +686,7 @@
  "crossterm_winapi",
  "filedescriptor",
  "libc",
- "mio",
+ "mio 0.8.11",
  "parking_lot",
  "signal-hook",
  "signal-hook-mio",
@@ -669,16 +724,15 @@
 
 [[package]]
 name = "curve25519-dalek"
-version = "4.1.2"
+version = "4.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
 dependencies = [
  "cfg-if",
  "cpufeatures",
  "curve25519-dalek-derive",
  "digest",
  "fiat-crypto",
- "platforms",
  "rustc_version",
  "subtle",
  "zeroize",
@@ -692,7 +746,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -742,13 +796,13 @@
 
 [[package]]
 name = "displaydoc"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -778,9 +832,9 @@
 
 [[package]]
 name = "either"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
 
 [[package]]
 name = "encode_unicode"
@@ -806,9 +860,9 @@
 
 [[package]]
 name = "fastrand"
-version = "2.1.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
 
 [[package]]
 name = "fiat-crypto"
@@ -823,7 +877,7 @@
 checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e"
 dependencies = [
  "libc",
- "thiserror",
+ "thiserror 1.0.69",
  "winapi",
 ]
 
@@ -833,7 +887,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2"
 dependencies = [
- "toml 0.5.11",
+ "toml",
 ]
 
 [[package]]
@@ -863,11 +917,11 @@
  "hostname",
  "human-repr",
  "indicatif",
- "itertools",
+ "itertools 0.13.0",
  "nix-eval",
  "nixlike",
  "nom",
- "openssh 0.10.4",
+ "openssh 0.10.5",
  "owo-colors",
  "peg",
  "regex",
@@ -896,11 +950,11 @@
  "fleet-shared",
  "futures",
  "hostname",
- "itertools",
+ "itertools 0.13.0",
  "nix-eval",
  "nixlike",
  "nom",
- "openssh 0.11.0",
+ "openssh 0.11.3",
  "serde",
  "serde_json",
  "tempfile",
@@ -991,7 +1045,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d"
 dependencies = [
- "thiserror",
+ "thiserror 1.0.69",
 ]
 
 [[package]]
@@ -1002,9 +1056,9 @@
 
 [[package]]
 name = "futures"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1017,9 +1071,9 @@
 
 [[package]]
 name = "futures-channel"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1027,15 +1081,15 @@
 
 [[package]]
 name = "futures-core"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1044,38 +1098,38 @@
 
 [[package]]
 name = "futures-io"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
 
 [[package]]
 name = "futures-task"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
 
 [[package]]
 name = "futures-util"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1122,9 +1176,15 @@
 
 [[package]]
 name = "gimli"
-version = "0.29.0"
+version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "h2"
@@ -1138,7 +1198,7 @@
  "futures-core",
  "futures-sink",
  "http",
- "indexmap 2.2.6",
+ "indexmap 2.6.0",
  "slab",
  "tokio",
  "tokio-util",
@@ -1164,6 +1224,12 @@
 checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 
 [[package]]
+name = "hashbrown"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
+
+[[package]]
 name = "heck"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1182,6 +1248,12 @@
 checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
 [[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
 name = "hex"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1206,6 +1278,15 @@
 ]
 
 [[package]]
+name = "home"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "hostname"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1252,9 +1333,9 @@
 
 [[package]]
 name = "httparse"
-version = "1.9.4"
+version = "1.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
 
 [[package]]
 name = "httpdate"
@@ -1270,9 +1351,9 @@
 
 [[package]]
 name = "hyper"
-version = "1.4.1"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
+checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -1291,9 +1372,9 @@
 
 [[package]]
 name = "hyper-timeout"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793"
+checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
 dependencies = [
  "hyper",
  "hyper-util",
@@ -1304,9 +1385,9 @@
 
 [[package]]
 name = "hyper-util"
-version = "0.1.7"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -1317,22 +1398,21 @@
  "pin-project-lite",
  "socket2",
  "tokio",
- "tower",
  "tower-service",
  "tracing",
 ]
 
 [[package]]
 name = "i18n-config"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c9ce3c48cbc21fd5b22b9331f32b5b51f6ad85d969b99e793427332e76e7640"
+checksum = "8e88074831c0be5b89181b05e6748c4915f77769ecc9a4c372f88b169a8509c9"
 dependencies = [
+ "basic-toml",
  "log",
  "serde",
  "serde_derive",
- "thiserror",
- "toml 0.8.13",
+ "thiserror 1.0.69",
  "unic-langid",
 ]
 
@@ -1352,7 +1432,7 @@
  "log",
  "parking_lot",
  "rust-embed",
- "thiserror",
+ "thiserror 1.0.69",
  "unic-langid",
  "walkdir",
 ]
@@ -1374,28 +1454,28 @@
  "proc-macro2",
  "quote",
  "strsim 0.10.0",
- "syn 2.0.76",
+ "syn 2.0.87",
  "unic-langid",
 ]
 
 [[package]]
 name = "i18n-embed-impl"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81093c4701672f59416582fe3145676126fd23ba5db910acad0793c1108aaa58"
+checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2"
 dependencies = [
  "find-crate",
  "i18n-config",
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.60"
+version = "0.1.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
@@ -1426,26 +1506,26 @@
 
 [[package]]
 name = "indexmap"
-version = "2.2.6"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
 dependencies = [
  "equivalent",
- "hashbrown 0.14.5",
+ "hashbrown 0.15.1",
 ]
 
 [[package]]
 name = "indicatif"
-version = "0.17.8"
+version = "0.17.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3"
+checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281"
 dependencies = [
  "console",
- "instant",
  "number_prefix",
  "portable-atomic",
- "unicode-width",
+ "unicode-width 0.2.0",
  "vt100",
+ "web-time",
 ]
 
 [[package]]
@@ -1459,15 +1539,6 @@
 ]
 
 [[package]]
-name = "instant"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
 name = "intl-memoizer"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1494,11 +1565,11 @@
 
 [[package]]
 name = "is-terminal"
-version = "0.4.12"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.4.0",
  "libc",
  "windows-sys 0.52.0",
 ]
@@ -1511,9 +1582,18 @@
 
 [[package]]
 name = "is_terminal_polyfill"
-version = "1.70.0"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
 
 [[package]]
 name = "itertools"
@@ -1532,39 +1612,55 @@
 
 [[package]]
 name = "js-sys"
-version = "0.3.69"
+version = "0.3.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
 dependencies = [
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 dependencies = [
- "spin 0.5.2",
+ "spin",
 ]
 
 [[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
 name = "libc"
-version = "0.2.155"
+version = "0.2.162"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
 
 [[package]]
+name = "libloading"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
 name = "libm"
-version = "0.2.8"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
 
 [[package]]
 name = "libmimalloc-sys"
-version = "0.1.38"
+version = "0.1.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6"
+checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
 dependencies = [
  "cc",
  "libc",
@@ -1594,9 +1690,9 @@
 
 [[package]]
 name = "log"
-version = "0.4.21"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
 name = "matchers"
@@ -1615,9 +1711,9 @@
 
 [[package]]
 name = "memchr"
-version = "2.7.2"
+version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
 [[package]]
 name = "memoffset"
@@ -1630,9 +1726,9 @@
 
 [[package]]
 name = "mimalloc"
-version = "0.1.42"
+version = "0.1.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176"
+checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
 dependencies = [
  "libmimalloc-sys",
 ]
@@ -1651,11 +1747,11 @@
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.3"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
 dependencies = [
- "adler",
+ "adler2",
 ]
 
 [[package]]
@@ -1671,6 +1767,18 @@
 ]
 
 [[package]]
+name = "mio"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+ "wasi",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "multimap"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1695,13 +1803,13 @@
  "anyhow",
  "better-command",
  "futures",
- "itertools",
+ "itertools 0.13.0",
  "nixlike",
  "r2d2",
  "regex",
  "serde",
  "serde_json",
- "thiserror",
+ "thiserror 2.0.3",
  "tokio",
  "tokio-util",
  "tracing",
@@ -1709,6 +1817,13 @@
 ]
 
 [[package]]
+name = "nix-native-eval"
+version = "0.1.0"
+dependencies = [
+ "nixrs",
+]
+
+[[package]]
 name = "nixlike"
 version = "0.1.0"
 dependencies = [
@@ -1719,7 +1834,26 @@
  "serde",
  "serde-transcode",
  "serde_json",
- "thiserror",
+ "thiserror 2.0.3",
+]
+
+[[package]]
+name = "nixrs"
+version = "0.1.0"
+source = "git+https://github.com/Anillc/nixrs#740fcf4048cc5b6de8c54d18254f12d53909a867"
+dependencies = [
+ "libc",
+ "nixrs-sys",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "nixrs-sys"
+version = "0.1.0"
+source = "git+https://github.com/Anillc/nixrs#740fcf4048cc5b6de8c54d18254f12d53909a867"
+dependencies = [
+ "bindgen",
+ "pkg-config",
 ]
 
 [[package]]
@@ -1793,16 +1927,6 @@
 dependencies = [
  "autocfg",
  "libm",
-]
-
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi",
- "libc",
 ]
 
 [[package]]
@@ -1813,18 +1937,18 @@
 
 [[package]]
 name = "object"
-version = "0.35.0"
+version = "0.36.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
+checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.19.0"
+version = "1.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 
 [[package]]
 name = "opaque-debug"
@@ -1834,30 +1958,30 @@
 
 [[package]]
 name = "openssh"
-version = "0.10.4"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "432f4a7e4d194272876710557e6b712fc304e7b4711e2063655df1e446b4b8e3"
+checksum = "330f4b61092456dc0aaa0cf9a205d956cae07d8127a69ffeff6760a72549c77f"
 dependencies = [
  "libc",
  "once_cell",
  "shell-escape",
  "tempfile",
- "thiserror",
+ "thiserror 1.0.69",
  "tokio",
  "tokio-pipe",
 ]
 
 [[package]]
 name = "openssh"
-version = "0.11.0"
+version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f27389e5da64700a3efb7f925e442f824f6e3d4b1c27f75e115a92ad3aecbb1"
+checksum = "b52987a10526b8daef7f1946b0aadfc214479f897ba624776327fd3beec2722c"
 dependencies = [
  "libc",
  "once_cell",
  "shell-escape",
  "tempfile",
- "thiserror",
+ "thiserror 2.0.3",
  "tokio",
 ]
 
@@ -1869,11 +1993,12 @@
 
 [[package]]
 name = "owo-colors"
-version = "4.0.0"
+version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
+checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56"
 dependencies = [
- "supports-color",
+ "supports-color 2.1.0",
+ "supports-color 3.0.1",
 ]
 
 [[package]]
@@ -1884,7 +2009,7 @@
 dependencies = [
  "bytecount",
  "fnv",
- "unicode-width",
+ "unicode-width 0.1.14",
 ]
 
 [[package]]
@@ -1907,7 +2032,7 @@
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -1928,9 +2053,9 @@
 
 [[package]]
 name = "peg"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a625d12ad770914cbf7eff6f9314c3ef803bfe364a1b20bc36ddf56673e71e5"
+checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f"
 dependencies = [
  "peg-macros",
  "peg-runtime",
@@ -1938,9 +2063,9 @@
 
 [[package]]
 name = "peg-macros"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f241d42067ed3ab6a4fece1db720838e1418f36d868585a27931f95d6bc03582"
+checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426"
 dependencies = [
  "peg-runtime",
  "proc-macro2",
@@ -1976,34 +2101,34 @@
 checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
 dependencies = [
  "fixedbitset",
- "indexmap 2.2.6",
+ "indexmap 2.6.0",
 ]
 
 [[package]]
 name = "pin-project"
-version = "1.1.5"
+version = "1.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
 dependencies = [
  "pin-project-internal",
 ]
 
 [[package]]
 name = "pin-project-internal"
-version = "1.1.5"
+version = "1.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
 
 [[package]]
 name = "pin-utils"
@@ -2033,10 +2158,10 @@
 ]
 
 [[package]]
-name = "platforms"
-version = "3.4.0"
+name = "pkg-config"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
 
 [[package]]
 name = "poly1305"
@@ -2063,9 +2188,9 @@
 
 [[package]]
 name = "portable-atomic"
-version = "1.6.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
 
 [[package]]
 name = "powerfmt"
@@ -2075,18 +2200,21 @@
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
 name = "prettyplease"
-version = "0.2.22"
+version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
+checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
 dependencies = [
  "proc-macro2",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -2115,18 +2243,18 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.84"
+version = "1.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "prost"
-version = "0.13.1"
+version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc"
+checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
 dependencies = [
  "bytes",
  "prost-derive",
@@ -2134,13 +2262,13 @@
 
 [[package]]
 name = "prost-build"
-version = "0.13.1"
+version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1"
+checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15"
 dependencies = [
  "bytes",
  "heck 0.5.0",
- "itertools",
+ "itertools 0.13.0",
  "log",
  "multimap",
  "once_cell",
@@ -2149,37 +2277,37 @@
  "prost",
  "prost-types",
  "regex",
- "syn 2.0.76",
+ "syn 2.0.87",
  "tempfile",
 ]
 
 [[package]]
 name = "prost-derive"
-version = "0.13.1"
+version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca"
+checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5"
 dependencies = [
  "anyhow",
- "itertools",
+ "itertools 0.13.0",
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "prost-types"
-version = "0.13.1"
+version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2"
+checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
 dependencies = [
  "prost",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.36"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
@@ -2240,23 +2368,23 @@
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.1"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.10.6"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-automata 0.4.6",
- "regex-syntax 0.8.3",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
 ]
 
 [[package]]
@@ -2270,13 +2398,13 @@
 
 [[package]]
 name = "regex-automata"
-version = "0.4.6"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax 0.8.3",
+ "regex-syntax 0.8.5",
 ]
 
 [[package]]
@@ -2287,9 +2415,9 @@
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.3"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "ring"
@@ -2301,7 +2429,7 @@
  "cfg-if",
  "getrandom",
  "libc",
- "spin 0.9.8",
+ "spin",
  "untrusted",
  "windows-sys 0.52.0",
 ]
@@ -2386,9 +2514,9 @@
 
 [[package]]
 name = "rust-embed"
-version = "8.4.0"
+version = "8.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a"
+checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
 dependencies = [
  "rust-embed-impl",
  "rust-embed-utils",
@@ -2397,22 +2525,22 @@
 
 [[package]]
 name = "rust-embed-impl"
-version = "8.4.0"
+version = "8.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb9f96e283ec64401f30d3df8ee2aaeb2561f34c824381efa24a35f79bf40ee4"
+checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
 dependencies = [
  "proc-macro2",
  "quote",
  "rust-embed-utils",
- "syn 2.0.76",
+ "syn 2.0.87",
  "walkdir",
 ]
 
 [[package]]
 name = "rust-embed-utils"
-version = "8.4.0"
+version = "8.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38c74a686185620830701348de757fd36bef4aa9680fd23c49fc539ddcc1af32"
+checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
 dependencies = [
  "sha2",
  "walkdir",
@@ -2432,18 +2560,18 @@
 
 [[package]]
 name = "rustc_version"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
 dependencies = [
  "semver",
 ]
 
 [[package]]
 name = "rustix"
-version = "0.38.34"
+version = "0.38.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
 dependencies = [
  "bitflags",
  "errno",
@@ -2454,9 +2582,9 @@
 
 [[package]]
 name = "rustls"
-version = "0.23.12"
+version = "0.23.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
+checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
 dependencies = [
  "log",
  "once_cell",
@@ -2469,25 +2597,24 @@
 
 [[package]]
 name = "rustls-pemfile"
-version = "2.1.3"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
 dependencies = [
- "base64 0.22.1",
  "rustls-pki-types",
 ]
 
 [[package]]
 name = "rustls-pki-types"
-version = "1.8.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
+checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
 
 [[package]]
 name = "rustls-webpki"
-version = "0.102.7"
+version = "0.102.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56"
+checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
 dependencies = [
  "ring",
  "rustls-pki-types",
@@ -2496,9 +2623,9 @@
 
 [[package]]
 name = "rustversion"
-version = "1.0.17"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
 
 [[package]]
 name = "ryu"
@@ -2582,9 +2709,9 @@
 
 [[package]]
 name = "serde"
-version = "1.0.203"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
 dependencies = [
  "serde_derive",
 ]
@@ -2609,20 +2736,20 @@
 
 [[package]]
 name = "serde_derive"
-version = "1.0.203"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.127"
+version = "1.0.132"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
+checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
 dependencies = [
  "itoa",
  "memchr",
@@ -2631,15 +2758,6 @@
 ]
 
 [[package]]
-name = "serde_spanned"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
-dependencies = [
- "serde",
-]
-
-[[package]]
 name = "sha2"
 version = "0.10.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2683,12 +2801,12 @@
 
 [[package]]
 name = "signal-hook-mio"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
 dependencies = [
  "libc",
- "mio",
+ "mio 0.8.11",
  "signal-hook",
 ]
 
@@ -2744,12 +2862,6 @@
  "libc",
  "windows-sys 0.52.0",
 ]
-
-[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
 
 [[package]]
 name = "spin"
@@ -2781,9 +2893,9 @@
 
 [[package]]
 name = "subtle"
-version = "2.5.0"
+version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "supports-color"
@@ -2796,6 +2908,15 @@
 ]
 
 [[package]]
+name = "supports-color"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77"
+dependencies = [
+ "is_ci",
+]
+
+[[package]]
 name = "syn"
 version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2808,9 +2929,9 @@
 
 [[package]]
 name = "syn"
-version = "2.0.76"
+version = "2.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2837,7 +2958,7 @@
 dependencies = [
  "papergrid",
  "tabled_derive",
- "unicode-width",
+ "unicode-width 0.1.14",
 ]
 
 [[package]]
@@ -2855,24 +2976,25 @@
 
 [[package]]
 name = "tempfile"
-version = "3.10.1"
+version = "3.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
 dependencies = [
  "cfg-if",
  "fastrand",
+ "once_cell",
  "rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "terminal_size"
-version = "0.3.0"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
+checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
 dependencies = [
  "rustix",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -2922,22 +3044,42 @@
 
 [[package]]
 name = "thiserror"
-version = "1.0.61"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
+dependencies = [
+ "thiserror-impl 2.0.3",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
- "thiserror-impl",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.87",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.61"
+version = "2.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -2991,31 +3133,30 @@
 
 [[package]]
 name = "tokio"
-version = "1.38.0"
+version = "1.41.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
+checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
 dependencies = [
  "backtrace",
  "bytes",
  "libc",
- "mio",
- "num_cpus",
+ "mio 1.0.2",
  "pin-project-lite",
  "signal-hook-registry",
  "socket2",
  "tokio-macros",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "tokio-macros"
-version = "2.3.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -3041,9 +3182,9 @@
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.15"
+version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
+checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -3053,9 +3194,9 @@
 
 [[package]]
 name = "tokio-util"
-version = "0.7.11"
+version = "0.7.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
+checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
 dependencies = [
  "bytes",
  "futures-core",
@@ -3074,44 +3215,10 @@
 ]
 
 [[package]]
-name = "toml"
-version = "0.8.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
-dependencies = [
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_edit",
-]
-
-[[package]]
-name = "toml_datetime"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "toml_edit"
-version = "0.22.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
-dependencies = [
- "indexmap 2.2.6",
- "serde",
- "serde_spanned",
- "toml_datetime",
- "winnow",
-]
-
-[[package]]
 name = "tonic"
-version = "0.12.2"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad"
+checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
 dependencies = [
  "async-stream",
  "async-trait",
@@ -3133,7 +3240,7 @@
  "tokio",
  "tokio-rustls",
  "tokio-stream",
- "tower",
+ "tower 0.4.13",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -3141,15 +3248,16 @@
 
 [[package]]
 name = "tonic-build"
-version = "0.12.2"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67"
+checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11"
 dependencies = [
  "prettyplease",
  "proc-macro2",
  "prost-build",
+ "prost-types",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -3173,6 +3281,20 @@
 ]
 
 [[package]]
+name = "tower"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper 0.1.2",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
 name = "tower-http"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3220,7 +3342,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
 
 [[package]]
@@ -3329,24 +3451,27 @@
 
 [[package]]
 name = "unicase"
-version = "2.7.0"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
-dependencies = [
- "version_check",
-]
+checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
 
 [[package]]
 name = "unicode-width"
-version = "0.1.12"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
 
 [[package]]
 name = "unicode_categories"
@@ -3378,9 +3503,9 @@
 
 [[package]]
 name = "utf8parse"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "valuable"
@@ -3390,9 +3515,9 @@
 
 [[package]]
 name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
 [[package]]
 name = "vt100"
@@ -3402,7 +3527,7 @@
 dependencies = [
  "itoa",
  "log",
- "unicode-width",
+ "unicode-width 0.1.14",
  "vte",
 ]
 
@@ -3419,9 +3544,9 @@
 
 [[package]]
 name = "vte_generate_state_changes"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
+checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3454,34 +3579,35 @@
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -3489,24 +3615,46 @@
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
 
 [[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
+
+[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3524,11 +3672,11 @@
 
 [[package]]
 name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -3544,7 +3692,7 @@
 checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
 dependencies = [
  "windows-core",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -3553,7 +3701,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -3571,7 +3719,16 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -3591,18 +3748,18 @@
 
 [[package]]
 name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.5",
- "windows_aarch64_msvc 0.52.5",
- "windows_i686_gnu 0.52.5",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
  "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.5",
- "windows_x86_64_gnu 0.52.5",
- "windows_x86_64_gnullvm 0.52.5",
- "windows_x86_64_msvc 0.52.5",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
 ]
 
 [[package]]
@@ -3613,9 +3770,9 @@
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
@@ -3625,9 +3782,9 @@
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
@@ -3637,15 +3794,15 @@
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
 name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
@@ -3655,9 +3812,9 @@
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
@@ -3667,9 +3824,9 @@
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
@@ -3679,9 +3836,9 @@
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
@@ -3691,20 +3848,11 @@
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
-name = "winnow"
-version = "0.6.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6"
-dependencies = [
- "memchr",
-]
-
-[[package]]
 name = "x25519-dalek"
 version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3732,6 +3880,27 @@
 checksum = "2a599daf1b507819c1121f0bf87fa37eb19daac6aff3aefefd4e6e2e0f2020fc"
 
 [[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.87",
+]
+
+[[package]]
 name = "zeroize"
 version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3748,5 +3917,5 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.76",
+ "syn 2.0.87",
 ]
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,12 +17,8 @@
 	"time",
 	"rt-multi-thread",
 ] }
-clap = { version = "4.5", features = [
-	"derive",
-	"env",
-	"wrap_help",
-	"unicode",
-] }
+tokio-util = { version = "0.7.11", features = ["codec"] }
+clap = { version = "4.5", features = ["derive", "env", "wrap_help", "unicode"] }
 clap_complete = "4.5"
 age = { version = "0.10", features = ["ssh"] }
 anyhow = "1.0"
@@ -32,3 +28,4 @@
 serde_json = "1.0"
 tempfile = "3.10"
 nix = { version = "0.29.0", features = ["user", "fs"] }
+thiserror = "2.0.3"
modifiedcmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth
--- a/cmds/fleet/src/cmds/build_systems.rs
+++ b/cmds/fleet/src/cmds/build_systems.rs
@@ -7,7 +7,7 @@
 	opts::FleetOpts,
 };
 use itertools::Itertools as _;
-use nix_eval::nix_go;
+use nix_eval::{nix_go, NixBuildBatch};
 use tokio::{task::LocalSet, time::sleep};
 use tracing::{error, field, info, info_span, warn, Instrument};
 
@@ -251,18 +251,18 @@
 	Ok(())
 }
 
-async fn build_task(config: Config, host: String, build_attr: &str) -> Result<PathBuf> {
+async fn build_task(
+	config: Config,
+	host: String,
+	build_attr: &str,
+	batch: Option<NixBuildBatch>,
+) -> Result<PathBuf> {
 	info!("building");
 	let host = config.host(&host).await?;
 	// let action = Action::from(self.subcommand.clone());
 	let nixos = host.nixos_config().await?;
 	let drv = nix_go!(nixos.system.build[{ build_attr }]);
-	let outputs = drv.build().await.inspect_err(|_| {
-			if build_attr == "sdImage" {
-				info!("sd-image build failed");
-				info!("Make sure you have imported modulesPath/installer/sd-card/sd-image-<arch>[-installer].nix (For installer, you may want to check config)");
-			}
-		})?;
+	let outputs = drv.build_maybe_batch(batch).await?;
 	let out_output = outputs
 		.get("out")
 		.ok_or_else(|| anyhow!("system build should produce \"out\" output"))?;
@@ -296,7 +296,8 @@
 			// multiple hosts.
 			set.spawn_local(
 				(async move {
-					let built = match build_task(config, hostname.clone(), &build_attr).await {
+					let built = match build_task(config, hostname.clone(), &build_attr, None).await
+					{
 						Ok(path) => path,
 						Err(e) => {
 							error!("failed to deploy host: {}", e);
@@ -324,6 +325,7 @@
 	pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {
 		let hosts = config.list_hosts().await?;
 		let set = LocalSet::new();
+		let batch = Some(config.nix_session.new_build_batch(format!("deploy-hosts")));
 		for host in hosts.into_iter() {
 			if opts.should_skip(&host).await? {
 				continue;
@@ -333,17 +335,19 @@
 			let hostname = host.name.clone();
 			let local_host = config.local_host();
 			let opts = opts.clone();
+			let batch = batch.clone();
 			// FIXME: Fix repl concurrency (see build-systems)
 			set.spawn_local(
 				(async move {
-					let built = match build_task(config.clone(), hostname.clone(), "toplevel").await
-					{
-						Ok(path) => path,
-						Err(e) => {
-							error!("failed to deploy host: {}", e);
-							return;
-						}
-					};
+					let built =
+						match build_task(config.clone(), hostname.clone(), "toplevel", batch).await
+						{
+							Ok(path) => path,
+							Err(e) => {
+								error!("failed to deploy host: {}", e);
+								return;
+							}
+						};
 					if !opts.is_local(&hostname) {
 						info!("uploading system closure");
 						{
@@ -405,6 +409,7 @@
 				.instrument(span),
 			);
 		}
+		drop(batch);
 		set.await;
 		Ok(())
 	}
modifiedcmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth
--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -231,9 +231,7 @@
 		let key = config.key(owner).await?;
 		recipients.push(key);
 	}
-	let generators = nix_go!(mk_secret_generators(Obj {
-		recipients: { recipients },
-	}));
+	let generators = nix_go!(mk_secret_generators(Obj { recipients }));
 
 	let generator = nix_go!(call_package(generator)(generators));
 
@@ -316,7 +314,7 @@
 	// I don't want to make modules always responsible for additional secret data anyway,
 	// so it should be in derivation, and not in the secret data itself.
 	let generators = nix_go!(default_mk_secret_generators(Obj {
-		recipients: { <Vec<String>>::new() },
+		recipients: <Vec<String>>::new(),
 	}));
 	let default_generator = nix_go!(default_call_package(generator)(generators));
 
modifiedcrates/fleet-base/src/host.rsdiffbeforeafterboth
before · crates/fleet-base/src/host.rs
1use std::{2	cell::OnceCell,3	ffi::{OsStr, OsString},4	fmt::Display,5	io::Write,6	ops::Deref,7	path::PathBuf,8	str::FromStr,9	sync::{Arc, Mutex, MutexGuard, OnceLock},10};1112use anyhow::{anyhow, bail, ensure, Context, Result};13use fleet_shared::SecretData;14use nix_eval::{nix_go, nix_go_json, util::assert_warn, Value};15use openssh::SessionBuilder;16use serde::de::DeserializeOwned;17use tempfile::NamedTempFile;1819use crate::{20	command::MyCommand,21	fleetdata::{FleetData, FleetSecret, FleetSharedSecret},22};2324pub struct FleetConfigInternals {25	pub local_system: String,26	pub directory: PathBuf,27	pub data: Mutex<FleetData>,28	pub nix_args: Vec<OsString>,29	/// fleet_config.config30	pub config_field: Value,31	// TODO: Remove with connectivity refactor32	pub localhost: String,3334	/// import nixpkgs {system = local};35	pub default_pkgs: Value,36}3738// TODO: Make field not pub39#[derive(Clone)]40pub struct Config(pub Arc<FleetConfigInternals>);4142impl Deref for Config {43	type Target = FleetConfigInternals;4445	fn deref(&self) -> &Self::Target {46		&self.047	}48}4950#[derive(Clone, Copy, Debug)]51pub enum EscalationStrategy {52	Sudo,53	Run0,54	Su,55}5657pub struct ConfigHost {58	config: Config,59	pub name: String,60	groups: OnceCell<Vec<String>>,6162	pub host_config: Option<Value>,63	pub nixos_config: OnceCell<Value>,64	pub pkgs_override: Option<Value>,6566	// TODO: Move command helpers away with connectivity refactor67	pub local: bool,68	pub session: OnceLock<Arc<openssh::Session>>,69}70// TODO: Move command helpers away with connectivity refactor71impl ConfigHost {72	pub async fn escalation_strategy(&self) -> Result<EscalationStrategy> {73		// Prefer sudo, as run0 has some gotchas with polkit74		// and too many repeating prompts.75		if (self.find_in_path("sudo").await).is_ok() {76			return Ok(EscalationStrategy::Sudo);77		}78		if (self.find_in_path("run0").await).is_ok() {79			return Ok(EscalationStrategy::Run0);80		}81		Ok(EscalationStrategy::Su)82	}83	async fn open_session(&self) -> Result<Arc<openssh::Session>> {84		assert!(!self.local, "do not open ssh connection to local session");85		// FIXME: TOCTOU86		if let Some(session) = &self.session.get() {87			return Ok((*session).clone());88		};89		let session = SessionBuilder::default();90		let session = session91			.connect(&self.name)92			.await93			.map_err(|e| anyhow!("ssh error while connecting to {}: {e}", self.name))?;94		let session = Arc::new(session);95		self.session.set(session.clone()).expect("TOCTOU happened");96		Ok(session)97	}98	pub async fn mktemp_dir(&self) -> Result<String> {99		let mut cmd = self.cmd("mktemp").await?;100		cmd.arg("-d");101		let path = cmd.run_string().await?;102		Ok(path.trim_end().to_owned())103	}104	pub async fn read_file_bin(&self, path: impl AsRef<OsStr>) -> Result<Vec<u8>> {105		let mut cmd = self.cmd("cat").await?;106		cmd.arg(path);107		cmd.run_bytes().await108	}109	pub async fn read_file_text(&self, path: impl AsRef<OsStr>) -> Result<String> {110		let mut cmd = self.cmd("cat").await?;111		cmd.arg(path);112		cmd.run_string().await113	}114	pub async fn read_dir(&self, path: impl AsRef<OsStr>) -> Result<Vec<String>> {115		let mut cmd = self.cmd("ls").await?;116		cmd.arg(path);117		let out = cmd.run_string().await?;118		let mut lines = out.split('\n');119		if let Some(last) = lines.next_back() {120			ensure!(last.is_empty(), "output of ls should end with newline");121		}122		Ok(lines.map(ToOwned::to_owned).collect())123	}124	#[allow(dead_code)]125	pub async fn read_file_json<D: DeserializeOwned>(&self, path: impl AsRef<OsStr>) -> Result<D> {126		let text = self.read_file_text(path).await?;127		Ok(serde_json::from_str(&text)?)128	}129	pub async fn read_env(&self, env: &str) -> Result<String> {130		let mut cmd = self.cmd("printenv").await?;131		cmd.arg(env);132		cmd.run_string().await133	}134	pub async fn find_in_path(&self, command: &str) -> Result<String> {135		// // `which` is not a part of coreutils, and it might not exist on machine.136		// let path = self.read_env("PATH").await?;137		// // Assuming delimiter is :, we don't work with windows host, this check will be much138		// // more sophisticated in remowt backend (and quicker, since actual PATH search will be done on remote machine)139		// for ele in path.split(':') {140		// 	let test_path = format!("{ele}/{cmd}");141		// 	test -x etc142		// }143		// let mut cmd = self.cmd("printenv").await?;144		// cmd.arg(env);145		// Ok(cmd.run_string().await?)146		// Assuming this is an environment issue if which doesn't exist, will be fixed with remowt.147		let mut cmd = self148			.cmd_escalation(149				// Not used150				EscalationStrategy::Su,151				"which",152			)153			.await?;154		cmd.arg(command);155		cmd.run_string().await156	}157	pub async fn read_file_value<D: FromStr>(&self, path: impl AsRef<OsStr>) -> Result<D>158	where159		<D as FromStr>::Err: Display,160	{161		let text = self.read_file_text(path).await?;162		D::from_str(&text).map_err(|e| anyhow!("failed to parse value: {e}"))163	}164	pub async fn cmd(&self, cmd: impl AsRef<OsStr>) -> Result<MyCommand> {165		self.cmd_escalation(self.escalation_strategy().await?, cmd)166			.await167	}168	pub async fn cmd_escalation(169		&self,170		escalation: EscalationStrategy,171		cmd: impl AsRef<OsStr>,172	) -> Result<MyCommand> {173		if self.local {174			Ok(MyCommand::new(escalation, cmd))175		} else {176			let session = self.open_session().await?;177			Ok(MyCommand::new_on(escalation, cmd, session))178		}179	}180181	pub async fn decrypt(&self, data: SecretData) -> Result<Vec<u8>> {182		ensure!(data.encrypted, "secret is not encrypted");183		let mut cmd = self.cmd("fleet-install-secrets").await?;184		cmd.arg("decrypt").eqarg("--secret", data.to_string());185		let encoded = cmd186			.sudo()187			.run_string()188			.await189			.context("failed to call remote host for decrypt")?;190		let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;191		ensure!(!data.encrypted, "secret came out encrypted");192		Ok(data.data)193	}194	pub async fn reencrypt(&self, data: SecretData, targets: Vec<String>) -> Result<SecretData> {195		ensure!(data.encrypted, "secret is not encrypted");196		let mut cmd = self.cmd("fleet-install-secrets").await?;197		cmd.arg("reencrypt").eqarg("--secret", data.to_string());198		for target in targets {199			let key = self.config.key(&target).await?;200			cmd.eqarg("--targets", key);201		}202		let encoded = cmd203			.sudo()204			.run_string()205			.await206			.context("failed to call remote host for decrypt")?;207		let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;208		ensure!(data.encrypted, "secret came out not encrypted");209		Ok(data)210	}211	/// Returns path for futureproofing, as path might change i.e on conversion to CA212	pub async fn remote_derivation(&self, path: &PathBuf) -> Result<PathBuf> {213		if self.local {214			// Path is located locally, thus already trusted.215			return Ok(path.to_owned());216		}217		let mut nix = MyCommand::new(218			// Not used219			EscalationStrategy::Su,220			"nix",221		);222		nix.arg("copy")223			.arg("--substitute-on-destination")224			.comparg("--to", format!("ssh-ng://{}", self.name))225			.arg(path);226		nix.run_nix().await.context("nix copy")?;227		Ok(path.to_owned())228	}229	pub async fn systemctl_stop(&self, name: &str) -> Result<()> {230		let mut cmd = self.cmd("systemctl").await?;231		cmd.arg("stop").arg(name);232		cmd.sudo().run().await233	}234	pub async fn systemctl_start(&self, name: &str) -> Result<()> {235		let mut cmd = self.cmd("systemctl").await?;236		cmd.arg("start").arg(name);237		cmd.sudo().run().await238	}239240	pub async fn rm_file(&self, path: impl AsRef<OsStr>, sudo: bool) -> Result<()> {241		let mut cmd = self.cmd("rm").await?;242		cmd.arg("-f").arg(path);243		if sudo {244			cmd = cmd.sudo()245		}246		cmd.run().await247	}248}249impl ConfigHost {250	// TOCTOU is possible here in case if config is changed, but this case is not handled anywhere anyway,251	// assuming getting tags always returns the same value.252	pub async fn tags(&self) -> Result<Vec<String>> {253		if let Some(v) = self.groups.get() {254			return Ok(v.clone());255		}256		let Some(host_config) = &self.host_config else {257			return Ok(vec![]);258		};259		let tags: Vec<String> = nix_go_json!(host_config.tags);260261		let _ = self.groups.set(tags.clone());262263		Ok(tags)264	}265	pub async fn nixos_config(&self) -> Result<Value> {266		if let Some(v) = self.nixos_config.get() {267			return Ok(v.clone());268		}269		let Some(host_config) = &self.host_config else {270			bail!("local host has no nixos_config");271		};272		let nixos_config = nix_go!(host_config.nixos.config);273		assert_warn("nixos config evaluation", &nixos_config).await?;274275		let _ = self.nixos_config.set(nixos_config.clone());276277		Ok(nixos_config)278	}279280	pub async fn list_configured_secrets(&self) -> Result<Vec<String>> {281		let nixos = self.nixos_config().await?;282		let secrets = nix_go!(nixos.secrets);283		let mut out = Vec::new();284		for name in secrets.list_fields().await? {285			let secret = nix_go!(secrets[{ name }]);286			let is_shared: bool = nix_go_json!(secret.shared);287			if is_shared {288				continue;289			}290			out.push(name);291		}292		Ok(out)293	}294	pub async fn secret_field(&self, name: &str) -> Result<Value> {295		let nixos = self.nixos_config().await?;296		Ok(nix_go!(nixos.secrets[{ name }]))297	}298299	/// Packages for this host, resolved with nixpkgs overlays300	pub async fn pkgs(&self) -> Result<Value> {301		if let Some(value) = &self.pkgs_override {302			return Ok(value.clone());303		}304		let Some(host_config) = &self.host_config else {305			bail!("local host has no host_config");306		};307		// TODO: Should nixos.options be cached?308		Ok(nix_go!(host_config.nixos.options._module.args.value.pkgs))309	}310}311312impl Config {313	pub fn local_host(&self) -> ConfigHost {314		ConfigHost {315			config: self.clone(),316			name: "<virtual localhost>".to_owned(),317			host_config: None,318			nixos_config: OnceCell::new(),319			groups: {320				let cell = OnceCell::new();321				let _ = cell.set(vec![]);322				cell323			},324			pkgs_override: Some(self.default_pkgs.clone()),325326			local: true,327			session: OnceLock::new(),328		}329	}330331	pub async fn host(&self, name: &str) -> Result<ConfigHost> {332		let config = &self.config_field;333		let host_config = nix_go!(config.hosts[{ name }]);334335		Ok(ConfigHost {336			config: self.clone(),337			name: name.to_owned(),338			host_config: Some(host_config),339			nixos_config: OnceCell::new(),340			groups: OnceCell::new(),341			pkgs_override: None,342343			// TODO: Remove with connectivit refactor344			local: self.localhost == name,345			session: OnceLock::new(),346		})347	}348	pub async fn list_hosts(&self) -> Result<Vec<ConfigHost>> {349		let config = &self.config_field;350		let names = nix_go!(config.hosts).list_fields().await?;351		let mut out = vec![];352		for name in names {353			out.push(self.host(&name).await?);354		}355		Ok(out)356	}357	// TODO: Replace usages with .host().nixos_config358	pub async fn system_config(&self, host: &str) -> Result<Value> {359		let fleet_field = &self.config_field;360		Ok(nix_go!(fleet_field.hosts[{ host }].nixos.config))361	}362363	/// Shared secrets configured in fleet.nix or in flake364	pub async fn list_configured_shared(&self) -> Result<Vec<String>> {365		let config_field = &self.config_field;366		Ok(nix_go!(config_field.sharedSecrets).list_fields().await?)367	}368	/// Shared secrets configured in fleet.nix369	pub fn list_shared(&self) -> Vec<String> {370		let data = self.data();371		data.shared_secrets.keys().cloned().collect()372	}373	pub fn has_shared(&self, name: &str) -> bool {374		let data = self.data();375		data.shared_secrets.contains_key(name)376	}377	pub fn replace_shared(&self, name: String, shared: FleetSharedSecret) {378		let mut data = self.data_mut();379		data.shared_secrets.insert(name.to_owned(), shared);380	}381	pub fn remove_shared(&self, secret: &str) {382		let mut data = self.data_mut();383		data.shared_secrets.remove(secret);384	}385386	pub fn list_secrets(&self, host: &str) -> Vec<String> {387		let data = self.data();388		let Some(secrets) = data.host_secrets.get(host) else {389			return Vec::new();390		};391		secrets.keys().cloned().collect()392	}393394	pub fn has_secret(&self, host: &str, secret: &str) -> bool {395		let data = self.data();396		let Some(host_secrets) = data.host_secrets.get(host) else {397			return false;398		};399		host_secrets.contains_key(secret)400	}401	pub fn insert_secret(&self, host: &str, secret: String, value: FleetSecret) {402		let mut data = self.data_mut();403		let host_secrets = data.host_secrets.entry(host.to_owned()).or_default();404		host_secrets.insert(secret, value);405	}406407	pub fn host_secret(&self, host: &str, secret: &str) -> Result<FleetSecret> {408		let data = self.data();409		let Some(host_secrets) = data.host_secrets.get(host) else {410			bail!("no secrets for machine {host}");411		};412		let Some(secret) = host_secrets.get(secret) else {413			bail!("machine {host} has no secret {secret}");414		};415		Ok(secret.clone())416	}417	pub fn shared_secret(&self, secret: &str) -> Result<FleetSharedSecret> {418		let data = self.data();419		let Some(secret) = data.shared_secrets.get(secret) else {420			bail!("no shared secret {secret}");421		};422		Ok(secret.clone())423	}424	pub async fn shared_secret_expected_owners(&self, secret: &str) -> Result<Vec<String>> {425		let config_field = &self.config_field;426		Ok(nix_go_json!(427			config_field.sharedSecrets[{ secret }].expectedOwners428		))429	}430431	// TODO: Should this be something modifiable from other processes?432	// E.g terraform provider might want to update FleetData (e.g secrets),433	// and current implementation assumes only one process holds current fleet.nix434	// Given that it is no longer needs to be a file for nix evaluation,435	// maybe it can be a .nix file for persistence, but accessible only436	// thru some shared state controller? Might it be stored in terraform437	// state provider?438	pub fn data(&self) -> MutexGuard<FleetData> {439		self.data.lock().unwrap()440	}441	pub fn data_mut(&self) -> MutexGuard<FleetData> {442		self.data.lock().unwrap()443	}444	pub fn save(&self) -> Result<()> {445		let mut tempfile = NamedTempFile::new_in(self.directory.clone()).context("failed to create updated version of fleet.nix in the same directory as original.\nDo you have write access to it? Access only to the fleet.nix won't be enough, the directory is used for atomic overwrite operation.\nIt is not recommended to use fleet by root anyway, move fleet project to your home directory.")?;446		let data = nixlike::serialize(&self.data() as &FleetData)?;447		tempfile.write_all(448			format!(449				"# This file contains fleet state and shouldn't be edited by hand\n\n{}\n\n# vim: ts=2 et nowrap\n",450				data451			)452			.as_bytes(),453		)?;454		let mut fleet_data_path = self.directory.clone();455		fleet_data_path.push("fleet.nix");456		tempfile.persist(fleet_data_path)?;457		Ok(())458	}459}
after · crates/fleet-base/src/host.rs
1use std::{2	cell::OnceCell,3	ffi::{OsStr, OsString},4	fmt::Display,5	io::Write,6	ops::Deref,7	path::PathBuf,8	str::FromStr,9	sync::{Arc, Mutex, MutexGuard, OnceLock},10};1112use anyhow::{anyhow, bail, ensure, Context, Result};13use fleet_shared::SecretData;14use nix_eval::{nix_go, nix_go_json, util::assert_warn, NixSession, Value};15use openssh::SessionBuilder;16use serde::de::DeserializeOwned;17use tempfile::NamedTempFile;1819use crate::{20	command::MyCommand,21	fleetdata::{FleetData, FleetSecret, FleetSharedSecret},22};2324pub struct FleetConfigInternals {25	pub local_system: String,26	pub directory: PathBuf,27	pub data: Mutex<FleetData>,28	pub nix_args: Vec<OsString>,29	/// fleet_config.config30	pub config_field: Value,31	// TODO: Remove with connectivity refactor32	pub localhost: String,3334	/// import nixpkgs {system = local};35	pub default_pkgs: Value,3637	pub nix_session: NixSession,38}3940// TODO: Make field not pub41#[derive(Clone)]42pub struct Config(pub Arc<FleetConfigInternals>);4344impl Deref for Config {45	type Target = FleetConfigInternals;4647	fn deref(&self) -> &Self::Target {48		&self.049	}50}5152#[derive(Clone, Copy, Debug)]53pub enum EscalationStrategy {54	Sudo,55	Run0,56	Su,57}5859pub struct ConfigHost {60	config: Config,61	pub name: String,62	groups: OnceCell<Vec<String>>,6364	pub host_config: Option<Value>,65	pub nixos_config: OnceCell<Value>,66	pub pkgs_override: Option<Value>,6768	// TODO: Move command helpers away with connectivity refactor69	pub local: bool,70	pub session: OnceLock<Arc<openssh::Session>>,71}72// TODO: Move command helpers away with connectivity refactor73impl ConfigHost {74	pub async fn escalation_strategy(&self) -> Result<EscalationStrategy> {75		// Prefer sudo, as run0 has some gotchas with polkit76		// and too many repeating prompts.77		if (self.find_in_path("sudo").await).is_ok() {78			return Ok(EscalationStrategy::Sudo);79		}80		if (self.find_in_path("run0").await).is_ok() {81			return Ok(EscalationStrategy::Run0);82		}83		Ok(EscalationStrategy::Su)84	}85	async fn open_session(&self) -> Result<Arc<openssh::Session>> {86		assert!(!self.local, "do not open ssh connection to local session");87		// FIXME: TOCTOU88		if let Some(session) = &self.session.get() {89			return Ok((*session).clone());90		};91		let session = SessionBuilder::default();92		let session = session93			.connect(&self.name)94			.await95			.map_err(|e| anyhow!("ssh error while connecting to {}: {e}", self.name))?;96		let session = Arc::new(session);97		self.session.set(session.clone()).expect("TOCTOU happened");98		Ok(session)99	}100	pub async fn mktemp_dir(&self) -> Result<String> {101		let mut cmd = self.cmd("mktemp").await?;102		cmd.arg("-d");103		let path = cmd.run_string().await?;104		Ok(path.trim_end().to_owned())105	}106	pub async fn read_file_bin(&self, path: impl AsRef<OsStr>) -> Result<Vec<u8>> {107		let mut cmd = self.cmd("cat").await?;108		cmd.arg(path);109		cmd.run_bytes().await110	}111	pub async fn read_file_text(&self, path: impl AsRef<OsStr>) -> Result<String> {112		let mut cmd = self.cmd("cat").await?;113		cmd.arg(path);114		cmd.run_string().await115	}116	pub async fn read_dir(&self, path: impl AsRef<OsStr>) -> Result<Vec<String>> {117		let mut cmd = self.cmd("ls").await?;118		cmd.arg(path);119		let out = cmd.run_string().await?;120		let mut lines = out.split('\n');121		if let Some(last) = lines.next_back() {122			ensure!(last.is_empty(), "output of ls should end with newline");123		}124		Ok(lines.map(ToOwned::to_owned).collect())125	}126	#[allow(dead_code)]127	pub async fn read_file_json<D: DeserializeOwned>(&self, path: impl AsRef<OsStr>) -> Result<D> {128		let text = self.read_file_text(path).await?;129		Ok(serde_json::from_str(&text)?)130	}131	pub async fn read_env(&self, env: &str) -> Result<String> {132		let mut cmd = self.cmd("printenv").await?;133		cmd.arg(env);134		cmd.run_string().await135	}136	pub async fn find_in_path(&self, command: &str) -> Result<String> {137		// // `which` is not a part of coreutils, and it might not exist on machine.138		// let path = self.read_env("PATH").await?;139		// // Assuming delimiter is :, we don't work with windows host, this check will be much140		// // more sophisticated in remowt backend (and quicker, since actual PATH search will be done on remote machine)141		// for ele in path.split(':') {142		// 	let test_path = format!("{ele}/{cmd}");143		// 	test -x etc144		// }145		// let mut cmd = self.cmd("printenv").await?;146		// cmd.arg(env);147		// Ok(cmd.run_string().await?)148		// Assuming this is an environment issue if which doesn't exist, will be fixed with remowt.149		let mut cmd = self150			.cmd_escalation(151				// Not used152				EscalationStrategy::Su,153				"which",154			)155			.await?;156		cmd.arg(command);157		cmd.run_string().await158	}159	pub async fn read_file_value<D: FromStr>(&self, path: impl AsRef<OsStr>) -> Result<D>160	where161		<D as FromStr>::Err: Display,162	{163		let text = self.read_file_text(path).await?;164		D::from_str(&text).map_err(|e| anyhow!("failed to parse value: {e}"))165	}166	pub async fn cmd(&self, cmd: impl AsRef<OsStr>) -> Result<MyCommand> {167		self.cmd_escalation(self.escalation_strategy().await?, cmd)168			.await169	}170	pub async fn cmd_escalation(171		&self,172		escalation: EscalationStrategy,173		cmd: impl AsRef<OsStr>,174	) -> Result<MyCommand> {175		if self.local {176			Ok(MyCommand::new(escalation, cmd))177		} else {178			let session = self.open_session().await?;179			Ok(MyCommand::new_on(escalation, cmd, session))180		}181	}182183	pub async fn decrypt(&self, data: SecretData) -> Result<Vec<u8>> {184		ensure!(data.encrypted, "secret is not encrypted");185		let mut cmd = self.cmd("fleet-install-secrets").await?;186		cmd.arg("decrypt").eqarg("--secret", data.to_string());187		let encoded = cmd188			.sudo()189			.run_string()190			.await191			.context("failed to call remote host for decrypt")?;192		let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;193		ensure!(!data.encrypted, "secret came out encrypted");194		Ok(data.data)195	}196	pub async fn reencrypt(&self, data: SecretData, targets: Vec<String>) -> Result<SecretData> {197		ensure!(data.encrypted, "secret is not encrypted");198		let mut cmd = self.cmd("fleet-install-secrets").await?;199		cmd.arg("reencrypt").eqarg("--secret", data.to_string());200		for target in targets {201			let key = self.config.key(&target).await?;202			cmd.eqarg("--targets", key);203		}204		let encoded = cmd205			.sudo()206			.run_string()207			.await208			.context("failed to call remote host for decrypt")?;209		let data: SecretData = encoded.parse().map_err(|e| anyhow!("{e}"))?;210		ensure!(data.encrypted, "secret came out not encrypted");211		Ok(data)212	}213	/// Returns path for futureproofing, as path might change i.e on conversion to CA214	pub async fn remote_derivation(&self, path: &PathBuf) -> Result<PathBuf> {215		if self.local {216			// Path is located locally, thus already trusted.217			return Ok(path.to_owned());218		}219		let mut nix = MyCommand::new(220			// Not used221			EscalationStrategy::Su,222			"nix",223		);224		nix.arg("copy")225			.arg("--substitute-on-destination")226			.comparg("--to", format!("ssh-ng://{}", self.name))227			.arg(path);228		nix.run_nix().await.context("nix copy")?;229		Ok(path.to_owned())230	}231	pub async fn systemctl_stop(&self, name: &str) -> Result<()> {232		let mut cmd = self.cmd("systemctl").await?;233		cmd.arg("stop").arg(name);234		cmd.sudo().run().await235	}236	pub async fn systemctl_start(&self, name: &str) -> Result<()> {237		let mut cmd = self.cmd("systemctl").await?;238		cmd.arg("start").arg(name);239		cmd.sudo().run().await240	}241242	pub async fn rm_file(&self, path: impl AsRef<OsStr>, sudo: bool) -> Result<()> {243		let mut cmd = self.cmd("rm").await?;244		cmd.arg("-f").arg(path);245		if sudo {246			cmd = cmd.sudo()247		}248		cmd.run().await249	}250}251impl ConfigHost {252	// TOCTOU is possible here in case if config is changed, but this case is not handled anywhere anyway,253	// assuming getting tags always returns the same value.254	pub async fn tags(&self) -> Result<Vec<String>> {255		if let Some(v) = self.groups.get() {256			return Ok(v.clone());257		}258		let Some(host_config) = &self.host_config else {259			return Ok(vec![]);260		};261		let tags: Vec<String> = nix_go_json!(host_config.tags);262263		let _ = self.groups.set(tags.clone());264265		Ok(tags)266	}267	pub async fn nixos_config(&self) -> Result<Value> {268		if let Some(v) = self.nixos_config.get() {269			return Ok(v.clone());270		}271		let Some(host_config) = &self.host_config else {272			bail!("local host has no nixos_config");273		};274		let nixos_config = nix_go!(host_config.nixos.config);275		assert_warn("nixos config evaluation", &nixos_config).await?;276277		let _ = self.nixos_config.set(nixos_config.clone());278279		Ok(nixos_config)280	}281282	pub async fn list_configured_secrets(&self) -> Result<Vec<String>> {283		let nixos = self.nixos_config().await?;284		let secrets = nix_go!(nixos.secrets);285		let mut out = Vec::new();286		for name in secrets.list_fields().await? {287			let secret = nix_go!(secrets[{ name }]);288			let is_shared: bool = nix_go_json!(secret.shared);289			if is_shared {290				continue;291			}292			out.push(name);293		}294		Ok(out)295	}296	pub async fn secret_field(&self, name: &str) -> Result<Value> {297		let nixos = self.nixos_config().await?;298		Ok(nix_go!(nixos.secrets[{ name }]))299	}300301	/// Packages for this host, resolved with nixpkgs overlays302	pub async fn pkgs(&self) -> Result<Value> {303		if let Some(value) = &self.pkgs_override {304			return Ok(value.clone());305		}306		let Some(host_config) = &self.host_config else {307			bail!("local host has no host_config");308		};309		// TODO: Should nixos.options be cached?310		Ok(nix_go!(host_config.nixos.options._module.args.value.pkgs))311	}312}313314impl Config {315	pub fn local_host(&self) -> ConfigHost {316		ConfigHost {317			config: self.clone(),318			name: "<virtual localhost>".to_owned(),319			host_config: None,320			nixos_config: OnceCell::new(),321			groups: {322				let cell = OnceCell::new();323				let _ = cell.set(vec![]);324				cell325			},326			pkgs_override: Some(self.default_pkgs.clone()),327328			local: true,329			session: OnceLock::new(),330		}331	}332333	pub async fn host(&self, name: &str) -> Result<ConfigHost> {334		let config = &self.config_field;335		let host_config = nix_go!(config.hosts[{ name }]);336337		Ok(ConfigHost {338			config: self.clone(),339			name: name.to_owned(),340			host_config: Some(host_config),341			nixos_config: OnceCell::new(),342			groups: OnceCell::new(),343			pkgs_override: None,344345			// TODO: Remove with connectivit refactor346			local: self.localhost == name,347			session: OnceLock::new(),348		})349	}350	pub async fn list_hosts(&self) -> Result<Vec<ConfigHost>> {351		let config = &self.config_field;352		let names = nix_go!(config.hosts).list_fields().await?;353		let mut out = vec![];354		for name in names {355			out.push(self.host(&name).await?);356		}357		Ok(out)358	}359	// TODO: Replace usages with .host().nixos_config360	pub async fn system_config(&self, host: &str) -> Result<Value> {361		let fleet_field = &self.config_field;362		Ok(nix_go!(fleet_field.hosts[{ host }].nixos.config))363	}364365	/// Shared secrets configured in fleet.nix or in flake366	pub async fn list_configured_shared(&self) -> Result<Vec<String>> {367		let config_field = &self.config_field;368		Ok(nix_go!(config_field.sharedSecrets).list_fields().await?)369	}370	/// Shared secrets configured in fleet.nix371	pub fn list_shared(&self) -> Vec<String> {372		let data = self.data();373		data.shared_secrets.keys().cloned().collect()374	}375	pub fn has_shared(&self, name: &str) -> bool {376		let data = self.data();377		data.shared_secrets.contains_key(name)378	}379	pub fn replace_shared(&self, name: String, shared: FleetSharedSecret) {380		let mut data = self.data_mut();381		data.shared_secrets.insert(name.to_owned(), shared);382	}383	pub fn remove_shared(&self, secret: &str) {384		let mut data = self.data_mut();385		data.shared_secrets.remove(secret);386	}387388	pub fn list_secrets(&self, host: &str) -> Vec<String> {389		let data = self.data();390		let Some(secrets) = data.host_secrets.get(host) else {391			return Vec::new();392		};393		secrets.keys().cloned().collect()394	}395396	pub fn has_secret(&self, host: &str, secret: &str) -> bool {397		let data = self.data();398		let Some(host_secrets) = data.host_secrets.get(host) else {399			return false;400		};401		host_secrets.contains_key(secret)402	}403	pub fn insert_secret(&self, host: &str, secret: String, value: FleetSecret) {404		let mut data = self.data_mut();405		let host_secrets = data.host_secrets.entry(host.to_owned()).or_default();406		host_secrets.insert(secret, value);407	}408409	pub fn host_secret(&self, host: &str, secret: &str) -> Result<FleetSecret> {410		let data = self.data();411		let Some(host_secrets) = data.host_secrets.get(host) else {412			bail!("no secrets for machine {host}");413		};414		let Some(secret) = host_secrets.get(secret) else {415			bail!("machine {host} has no secret {secret}");416		};417		Ok(secret.clone())418	}419	pub fn shared_secret(&self, secret: &str) -> Result<FleetSharedSecret> {420		let data = self.data();421		let Some(secret) = data.shared_secrets.get(secret) else {422			bail!("no shared secret {secret}");423		};424		Ok(secret.clone())425	}426	pub async fn shared_secret_expected_owners(&self, secret: &str) -> Result<Vec<String>> {427		let config_field = &self.config_field;428		Ok(nix_go_json!(429			config_field.sharedSecrets[{ secret }].expectedOwners430		))431	}432433	// TODO: Should this be something modifiable from other processes?434	// E.g terraform provider might want to update FleetData (e.g secrets),435	// and current implementation assumes only one process holds current fleet.nix436	// Given that it is no longer needs to be a file for nix evaluation,437	// maybe it can be a .nix file for persistence, but accessible only438	// thru some shared state controller? Might it be stored in terraform439	// state provider?440	pub fn data(&self) -> MutexGuard<FleetData> {441		self.data.lock().unwrap()442	}443	pub fn data_mut(&self) -> MutexGuard<FleetData> {444		self.data.lock().unwrap()445	}446	pub fn save(&self) -> Result<()> {447		let mut tempfile = NamedTempFile::new_in(self.directory.clone()).context("failed to create updated version of fleet.nix in the same directory as original.\nDo you have write access to it? Access only to the fleet.nix won't be enough, the directory is used for atomic overwrite operation.\nIt is not recommended to use fleet by root anyway, move fleet project to your home directory.")?;448		let data = nixlike::serialize(&self.data() as &FleetData)?;449		tempfile.write_all(450			format!(451				"# This file contains fleet state and shouldn't be edited by hand\n\n{}\n\n# vim: ts=2 et nowrap\n",452				data453			)454			.as_bytes(),455		)?;456		let mut fleet_data_path = self.directory.clone();457		fleet_data_path.push("fleet.nix");458		tempfile.persist(fleet_data_path)?;459		Ok(())460	}461}
modifiedcrates/fleet-base/src/opts.rsdiffbeforeafterboth
--- a/crates/fleet-base/src/opts.rs
+++ b/crates/fleet-base/src/opts.rs
@@ -173,9 +173,9 @@
 		let directory = current_dir()?;
 
 		let pool = NixSessionPool::new(directory.as_os_str().to_owned(), nix_args.clone()).await?;
-		let root_field = pool.get().await?;
+		let nix_session = pool.get().await?;
 
-		let builtins_field = Value::binding(root_field.clone(), "builtins").await?;
+		let builtins_field = Value::binding(nix_session.clone(), "builtins").await?;
 		let local_system = if self.local_system == "detect" {
 			nix_go_json!(builtins_field.currentSystem)
 		} else {
@@ -187,7 +187,7 @@
 		let bytes = std::fs::read_to_string(fleet_data_path)?;
 		let data: Mutex<FleetData> = nixlike::parse_str(&bytes)?;
 
-		let fleet_root = Value::binding(root_field, "fleetConfigurations").await?;
+		let fleet_root = Value::binding(nix_session.clone(), "fleetConfigurations").await?;
 		let fleet_field = nix_go!(fleet_root.default({ data }));
 
 		let config_field = nix_go!(fleet_field.config);
@@ -200,10 +200,11 @@
 
 		let default_pkgs = nix_go!(nixpkgs(Obj {
 			overlays,
-			system: { local_system.clone() },
+			system: local_system.clone(),
 		}));
 
 		Ok(Config(Arc::new(FleetConfigInternals {
+			nix_session,
 			directory,
 			data,
 			local_system,
modifiedcrates/nix-eval/Cargo.tomldiffbeforeafterboth
--- a/crates/nix-eval/Cargo.toml
+++ b/crates/nix-eval/Cargo.toml
@@ -14,9 +14,9 @@
 regex = "1.10.6"
 serde = { workspace = true, features = ["derive"] }
 serde_json.workspace = true
-thiserror = "1.0.61"
+thiserror.workspace = true
 tokio = { workspace = true, features = ["process", "io-util"] }
-tokio-util = { version = "0.7.11", features = ["codec"] }
+tokio-util.workspace = true
 tracing.workspace = true
 unindent = "0.2.3"
 
modifiedcrates/nix-eval/src/lib.rsdiffbeforeafterboth
--- a/crates/nix-eval/src/lib.rs
+++ b/crates/nix-eval/src/lib.rs
@@ -3,12 +3,17 @@
 //!
 //! Current api is awful, little effort was put into this implementation.
 
-use std::sync::Arc;
+use std::{collections::HashMap, path::PathBuf, sync::Arc};
 
 pub use pool::NixSessionPool;
 use pool::NixSessionPoolInner;
 use r2d2::PooledConnection;
 pub use session::{Error, Result};
+use tokio::{
+	sync::{mpsc, oneshot},
+	task::AbortHandle,
+};
+use tracing::{info, instrument, Instrument};
 pub use value::{Index, Value};
 
 mod pool;
@@ -30,10 +35,82 @@
 #[derive(Clone)]
 pub struct NixSession(pub(crate) Arc<tokio::sync::Mutex<PooledConnection<NixSessionPoolInner>>>);
 
+struct NixBuildTask(Value, oneshot::Sender<Result<HashMap<String, PathBuf>>>);
+
+#[derive(Clone)]
+pub struct NixBuildBatch {
+	tx: mpsc::UnboundedSender<NixBuildTask>,
+}
+
+#[instrument(skip(session, values))]
+async fn build_multiple(name: String, session: NixSession, values: Vec<Value>) -> Result<()> {
+	let builtins = Value::binding(session, "builtins").await?;
+	let system = nix_go!(builtins.currentSystem);
+	let drv = nix_go!(builtins.derivation(Obj {
+		system,
+		name,
+		builder: "/bin/sh",
+		// we want nothing from this derivation, it is only used to perform multiple builds at once.
+		args: vec!["-c", "echo > $out"],
+		preferLocalBuild: true,
+		allowSubstitutes: false,
+		buildInputs: values,
+	}));
+	drv.build().await?;
+	Ok(())
+}
+
+impl NixBuildBatch {
+	fn new(name: String, session: NixSession) -> Self {
+		let (tx, mut rx) = mpsc::unbounded_channel::<NixBuildTask>();
+
+		tokio::task::spawn(async move {
+			let mut deps = vec![];
+			let mut build_data = vec![];
+			while let Some(task) = rx.recv().await {
+				build_data.push(task.0.clone());
+				deps.push(task);
+			}
+			if deps.is_empty() {
+				return;
+			}
+			match build_multiple(name, session, build_data).await {
+				Ok(_) => {
+					for NixBuildTask(v, o) in deps {
+						let _ = o.send(v.build().await);
+					}
+				}
+				Err(e) => {
+					for NixBuildTask(v, o) in deps {
+						let s = v.to_string_weak().await.expect("drv is string-like");
+						if PathBuf::from(s).exists() {
+							let _ = o.send(v.build().await);
+						} else {
+							let _ = o.send(Err(e.clone()));
+						}
+					}
+				}
+			};
+		});
+		Self { tx }
+	}
+	pub async fn submit(self, task: Value) -> Result<HashMap<String, PathBuf>> {
+		let Self { tx: task_tx } = self;
+		let (tx, rx) = oneshot::channel();
+		let _ = task_tx.send(NixBuildTask(task, tx));
+		drop(task_tx);
+		rx.await.expect("shoudn't be cancelled here")
+	}
+}
+
 impl NixSession {
 	fn ptr_eq(a: &Self, b: &Self) -> bool {
 		Arc::ptr_eq(&a.0, &b.0)
 	}
+
+	pub fn new_build_batch(&self, name: String) -> NixBuildBatch {
+		NixBuildBatch::new(name, self.clone())
+	}
 }
 
 pub fn init_tokio() {
modifiedcrates/nix-eval/src/macros.rsdiffbeforeafterboth
--- a/crates/nix-eval/src/macros.rs
+++ b/crates/nix-eval/src/macros.rs
@@ -7,6 +7,55 @@
 	pub(crate) out: String,
 	used_fields: Vec<Value>,
 }
+trait AttrSetValue {
+	fn to_builder(self) -> NixExprBuilder;
+}
+trait Primitive {}
+
+macro_rules! impl_primitive {
+	($($t:ty),+) => {
+			$(
+				impl Primitive for $t {}
+			)+
+	};
+}
+impl_primitive!(String, bool, &'static str);
+
+impl<T> AttrSetValue for T
+where
+	// Limited by Primitive trait to avoid orphan rules violation with Vec<T: AttrSetValue>
+	T: Serialize + Primitive,
+{
+	fn to_builder(self) -> NixExprBuilder {
+		let serialized = nixlike::serialize(self).expect("invalid value for apply");
+		NixExprBuilder {
+			out: serialized.trim_end().to_owned(),
+			used_fields: Vec::new(),
+		}
+	}
+}
+impl AttrSetValue for Value {
+	fn to_builder(self) -> NixExprBuilder {
+		NixExprBuilder {
+			out: format!("sess_field_{}", self.session_field_id()),
+			used_fields: vec![self],
+		}
+	}
+}
+impl<T> AttrSetValue for Vec<T>
+where
+	T: AttrSetValue,
+{
+	fn to_builder(self) -> NixExprBuilder {
+		let mut builder = NixExprBuilder::list();
+		for v in self {
+			builder.list_value(NixExprBuilder::attrset_value(v));
+		}
+		builder.list_end();
+		builder
+	}
+}
+
 impl NixExprBuilder {
 	pub fn object() -> Self {
 		NixExprBuilder {
@@ -14,6 +63,12 @@
 			used_fields: Vec::new(),
 		}
 	}
+	pub fn list() -> Self {
+		NixExprBuilder {
+			out: "[".to_owned(),
+			used_fields: Vec::new(),
+		}
+	}
 	pub fn string(s: &str) -> Self {
 		NixExprBuilder {
 			out: nixlike::serialize(s)
@@ -23,6 +78,9 @@
 			used_fields: Vec::new(),
 		}
 	}
+	pub fn attrset_value(v: impl AttrSetValue) -> Self {
+		v.to_builder()
+	}
 	pub fn serialized(v: impl Serialize) -> Self {
 		let serialized = nixlike::serialize(v).expect("invalid value for apply");
 		Self {
@@ -46,6 +104,13 @@
 		self.extend(value);
 		self.out.push_str("; ");
 	}
+	pub fn list_value(&mut self, value: Self) {
+		self.extend(value);
+		self.out.push(' ');
+	}
+	pub fn list_end(&mut self) {
+		self.out.push(']');
+	}
 
 	pub fn extend(&mut self, e: Self) {
 		self.out.push_str(&e.out);
@@ -80,19 +145,19 @@
 #[macro_export]
 macro_rules! nix_expr_inner {
 	//(@munch_object FIXME: value should be arbitrary nix_expr_inner input... Time to write proc-macro?
-	(@obj($o:ident) $field:ident, $($tt:tt)*) => {{
+	(@obj($o:ident) $field:ident$(, $($tt:tt)*)?) => {{
 		$o.obj_key(
 			NixExprBuilder::string(stringify!($field)),
-			NixExprBuilder::value($field),
+			NixExprBuilder::attrset_value($field),
 		);
-		nix_expr_inner!(@obj($o) $($tt)*);
+		$(nix_expr_inner!(@obj($o) $($tt)*);)?
 	}};
-	(@obj($o:ident) $field:ident: $v:block, $($tt:tt)*) => {{
+	(@obj($o:ident) $field:ident: $v:expr$(, $($tt:tt)*)?) => {{
 		$o.obj_key(
 			NixExprBuilder::string(stringify!($field)),
-			NixExprBuilder::serialized(&$v),
+			NixExprBuilder::attrset_value($v),
 		);
-		nix_expr_inner!(@obj($o) $($tt)*);
+		$(nix_expr_inner!(@obj($o) $($tt)*);)?
 	}};
 	(@obj($o:ident)) => {{}};
 	(Obj { $($tt:tt)* }) => {{
modifiedcrates/nix-eval/src/session.rsdiffbeforeafterboth
--- a/crates/nix-eval/src/session.rs
+++ b/crates/nix-eval/src/session.rs
@@ -14,7 +14,7 @@
 use tokio_util::codec::{FramedRead, LinesCodec};
 use tracing::{debug, error, warn, Level};
 
-#[derive(Error, Debug)]
+#[derive(Error, Debug, Clone)]
 pub enum Error {
 	#[error("failed to create nix repl session: {0}")]
 	SessionInit(&'static str),
@@ -33,15 +33,15 @@
 	BuildFailed { attribute: String, error: String },
 
 	#[error("output: {0}")]
-	Json(#[from] serde_json::Error),
+	Json(Arc<serde_json::Error>),
 	// int outputs are too specific, and should not be used,
 	// thus error is ok to be not informative.
 	#[error("int output: {0}")]
 	Int(ParseIntError),
 	#[error("pool: {0}")]
-	Pool(#[from] r2d2::Error),
+	Pool(Arc<r2d2::Error>),
 	#[error("io: {0}")]
-	Io(#[from] std::io::Error),
+	Io(Arc<std::io::Error>),
 
 	// TODO: Should be done by wrapper/in different type.
 	#[error("at {0}: {1}")]
@@ -50,6 +50,21 @@
 	#[error("error: {0}")]
 	NixError(String),
 }
+impl From<r2d2::Error> for Error {
+	fn from(value: r2d2::Error) -> Self {
+		Self::Pool(Arc::new(value))
+	}
+}
+impl From<std::io::Error> for Error {
+	fn from(value: std::io::Error) -> Self {
+		Self::Io(Arc::new(value))
+	}
+}
+impl From<serde_json::Error> for Error {
+	fn from(value: serde_json::Error) -> Self {
+		Self::Json(Arc::new(value))
+	}
+}
 impl Error {
 	pub(crate) fn context(self, context: String) -> Self {
 		Self::InContext(context, Box::new(self))
modifiedcrates/nix-eval/src/value.rsdiffbeforeafterboth
--- a/crates/nix-eval/src/value.rs
+++ b/crates/nix-eval/src/value.rs
@@ -3,7 +3,7 @@
 use better_command::NixHandler;
 use serde::{de::DeserializeOwned, Serialize};
 
-use crate::{macros::NixExprBuilder, nix_go, Error, NixSession, Result};
+use crate::{macros::NixExprBuilder, nix_go, Error, NixBuildBatch, NixSession, Result};
 
 #[derive(Clone)]
 pub enum Index {
@@ -230,6 +230,16 @@
 		let import = Self::new(self.0.session.clone(), "import").await?;
 		Ok(nix_go!(self | import))
 	}
+	pub async fn build_maybe_batch(
+		&self,
+		batch: Option<NixBuildBatch>,
+	) -> Result<HashMap<String, PathBuf>> {
+		if let Some(batch) = batch {
+			batch.submit(self.clone()).await
+		} else {
+			self.build().await
+		}
+	}
 	pub async fn build(&self) -> Result<HashMap<String, PathBuf>> {
 		let id = self.0.value.expect("can't use build on not-value");
 		let query = format!(":b sess_field_{id}");
@@ -262,6 +272,20 @@
 			.collect();
 		Ok(outputs)
 	}
+	/// Weakly convert string-like types (derivation/path/string) to string
+	pub async fn to_string_weak(&self) -> Result<String> {
+		let id = self.0.value.expect("can't use build on not-value");
+		let query = format!("\"${{sess_field_{id}}}\"");
+		let vid: String = self
+			.0
+			.session
+			.0
+			.lock()
+			.await
+			.execute_expression_to_json(&query)
+			.await?;
+		Ok(vid)
+	}
 
 	fn attribute(&self) -> String {
 		if let Some(full_path) = &self.0.full_path {
addedcrates/nix-native-eval/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/crates/nix-native-eval/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "nix-native-eval"
+edition = "2021"
+version.workspace = true
+
+[dependencies]
+nixrs = { git = "https://github.com/Anillc/nixrs", version = "0.1.0" }
addedcrates/nix-native-eval/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/nix-native-eval/src/lib.rs
@@ -0,0 +1,7 @@
+use nixrs::{State, Store};
+
+fn init() {
+	nixrs::init();
+	let store = Store::new("daemon")?;
+	let state = State::new(store)
+}
modifiedcrates/nixlike/Cargo.tomldiffbeforeafterboth
--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -4,11 +4,11 @@
 edition = "2021"
 
 [dependencies]
+thiserror.workspace = true
 alejandra = { git = "https://github.com/kamadorueda/alejandra" }
 linked-hash-map = "0.5.6"
 peg = "0.8.2"
 serde = "1.0.196"
-thiserror = "1.0.57"
 serde_json = "1.0.113"
 ron = "0.8.1"
 serde-transcode = "1.1.1"
modifiedflake.lockdiffbeforeafterboth
--- a/flake.lock
+++ b/flake.lock
@@ -1,17 +1,12 @@
 {
   "nodes": {
     "crane": {
-      "inputs": {
-        "nixpkgs": [
-          "nixpkgs"
-        ]
-      },
       "locked": {
-        "lastModified": 1721699339,
-        "narHash": "sha256-UqtSwU13vpzzM6w8tGghEbA7ObM3NCDzSpz19QQo9XE=",
+        "lastModified": 1731098351,
+        "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=",
         "owner": "ipetkov",
         "repo": "crane",
-        "rev": "0081e9c447f3b70822c142908f08ceeb436982b8",
+        "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28",
         "type": "github"
       },
       "original": {
@@ -22,14 +17,16 @@
     },
     "flake-parts": {
       "inputs": {
-        "nixpkgs-lib": "nixpkgs-lib"
+        "nixpkgs-lib": [
+          "nixpkgs"
+        ]
       },
       "locked": {
-        "lastModified": 1719994518,
-        "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
+        "lastModified": 1730504689,
+        "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=",
         "owner": "hercules-ci",
         "repo": "flake-parts",
-        "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
+        "rev": "506278e768c2a08bec68eb62932193e341f55c90",
         "type": "github"
       },
       "original": {
@@ -40,11 +37,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1721814637,
-        "narHash": "sha256-L3QkCvxeByJfW45wLkdZ9pL5h9PezOwwfx7G2sRfjiU=",
+        "lastModified": 1731514040,
+        "narHash": "sha256-4VkY8gwyR83N6MPT7ipXTOSBXpVL2Hrwh898UAR3HZ8=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "e0c444a0b8413a31df199052f5714d409dc4c1d0",
+        "rev": "155168226cb666d242306e13d7dbdaa8a76d20e1",
         "type": "github"
       },
       "original": {
@@ -52,18 +49,6 @@
         "ref": "master",
         "repo": "nixpkgs",
         "type": "github"
-      }
-    },
-    "nixpkgs-lib": {
-      "locked": {
-        "lastModified": 1719876945,
-        "narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=",
-        "type": "tarball",
-        "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
-      },
-      "original": {
-        "type": "tarball",
-        "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
       }
     },
     "root": {
@@ -81,11 +66,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1721810656,
-        "narHash": "sha256-33UCMmgPL+sz06+iupNkl99hcBABP56ENcxSoKqr0TY=",
+        "lastModified": 1731464916,
+        "narHash": "sha256-WZ5rpjr/wCt7yBOUsvDE2i22hYz9g8W921jlwVktRQ4=",
         "owner": "oxalica",
         "repo": "rust-overlay",
-        "rev": "a6afdaab4a47d6ecf647a74968e92a51c4a18e5a",
+        "rev": "2c19bad6e881b5a154cafb7f9106879b5b356d1f",
         "type": "github"
       },
       "original": {
modifiedflake.nixdiffbeforeafterboth
--- a/flake.nix
+++ b/flake.nix
@@ -5,23 +5,16 @@
     nixpkgs.url = "github:nixos/nixpkgs/master";
     rust-overlay = {
       url = "github:oxalica/rust-overlay";
-      inputs = {
-        nixpkgs.follows = "nixpkgs";
-      };
+      inputs.nixpkgs.follows = "nixpkgs";
     };
-    flake-parts.url = "github:hercules-ci/flake-parts";
-    crane = {
-      url = "github:ipetkov/crane";
-      inputs.nixpkgs.follows = "nixpkgs";
+    flake-parts = {
+      url = "github:hercules-ci/flake-parts";
+      inputs.nixpkgs-lib.follows = "nixpkgs";
     };
+    crane.url = "github:ipetkov/crane";
   };
-  outputs = inputs @ {
-    self,
-    flake-parts,
-    crane,
-    ...
-  }:
-    flake-parts.lib.mkFlake {
+  outputs = inputs:
+    inputs.flake-parts.lib.mkFlake {
       inherit inputs;
     } {
       flake = rec {
@@ -33,7 +26,7 @@
             fleetConfiguration = throw "function-based interface is deprecated, use flake-parts syntax instead";
           };
         flakeModules.default = import ./lib/flakePart.nix {
-          inherit crane;
+          inherit (inputs) crane;
         };
         flakeModule = flakeModules.default;
 
@@ -85,7 +78,7 @@
         deployerSystem = elem system deployerSystems;
         lib = pkgs.lib;
         rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
-        craneLib = (crane.mkLib pkgs).overrideToolchain rust;
+        craneLib = (inputs.crane.mkLib pkgs).overrideToolchain rust;
       in {
         _module.args.pkgs = import inputs.nixpkgs {
           inherit system;
@@ -132,6 +125,8 @@
               openssl
               bacon
               nil
+              rustPlatform.bindgenHook
+              nixVersions.nix_2_22
             ];
             env.PROTOC = "${pkgs.protobuf}/bin/protoc";
           };
modifiedrust-toolchain.tomldiffbeforeafterboth
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-07-08"
+channel = "nightly-2024-11-12"
 components = ["rustfmt", "clippy", "rust-analyzer", "rust-src"]