difftreelog
refactor temporarly break cross, but greatly cleanup data
in: trunk
16 files changed
Cargo.lockdiffbeforeafterboth117117118[[package]]118[[package]]119name = "aho-corasick"119name = "aho-corasick"120version = "1.1.1"120version = "1.1.2"121source = "registry+https://github.com/rust-lang/crates.io-index"121source = "registry+https://github.com/rust-lang/crates.io-index"122checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"122checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"123dependencies = [123dependencies = [124 "memchr",124 "memchr",125]125]170170171[[package]]171[[package]]172name = "anstream"172name = "anstream"173version = "0.6.4"173version = "0.6.5"174source = "registry+https://github.com/rust-lang/crates.io-index"174source = "registry+https://github.com/rust-lang/crates.io-index"175checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"175checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"176dependencies = [176dependencies = [177 "anstyle",177 "anstyle",178 "anstyle-parse",178 "anstyle-parse",190190191[[package]]191[[package]]192name = "anstyle-parse"192name = "anstyle-parse"193version = "0.2.2"193version = "0.2.3"194source = "registry+https://github.com/rust-lang/crates.io-index"194source = "registry+https://github.com/rust-lang/crates.io-index"195checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"195checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"196dependencies = [196dependencies = [197 "utf8parse",197 "utf8parse",198]198]199199200[[package]]200[[package]]201name = "anstyle-query"201name = "anstyle-query"202version = "1.0.0"202version = "1.0.2"203source = "registry+https://github.com/rust-lang/crates.io-index"203source = "registry+https://github.com/rust-lang/crates.io-index"204checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"204checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"205dependencies = [205dependencies = [206 "windows-sys 0.48.0",206 "windows-sys 0.52.0",207]207]208208209[[package]]209[[package]]210name = "anstyle-wincon"210name = "anstyle-wincon"211version = "3.0.1"211version = "3.0.2"212source = "registry+https://github.com/rust-lang/crates.io-index"212source = "registry+https://github.com/rust-lang/crates.io-index"213checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"213checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"214dependencies = [214dependencies = [215 "anstyle",215 "anstyle",216 "windows-sys 0.48.0",216 "windows-sys 0.52.0",217]217]218218219[[package]]219[[package]]220name = "anyhow"220name = "anyhow"221version = "1.0.75"221version = "1.0.77"222source = "registry+https://github.com/rust-lang/crates.io-index"222source = "registry+https://github.com/rust-lang/crates.io-index"223checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"223checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9"224224225[[package]]225[[package]]226name = "arc-swap"226name = "arc-swap"242242243[[package]]243[[package]]244name = "async-trait"244name = "async-trait"245version = "0.1.74"245version = "0.1.75"246source = "registry+https://github.com/rust-lang/crates.io-index"246source = "registry+https://github.com/rust-lang/crates.io-index"247checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"247checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98"248dependencies = [248dependencies = [249 "proc-macro2",249 "proc-macro2",250 "quote",250 "quote",251 "syn 2.0.38",251 "syn 2.0.43",252]252]253253254[[package]]254[[package]]387387388[[package]]388[[package]]389name = "byteorder"389name = "byteorder"390version = "1.4.3"390version = "1.5.0"391source = "registry+https://github.com/rust-lang/crates.io-index"391source = "registry+https://github.com/rust-lang/crates.io-index"392checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"392checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"393393394[[package]]394[[package]]395name = "bytes"395name = "bytes"482482483[[package]]483[[package]]484name = "clap"484name = "clap"485version = "4.4.7"485version = "4.4.12"486source = "registry+https://github.com/rust-lang/crates.io-index"486source = "registry+https://github.com/rust-lang/crates.io-index"487checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"487checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d"488dependencies = [488dependencies = [489 "clap_builder",489 "clap_builder",490 "clap_derive",490 "clap_derive",491]491]492492493[[package]]493[[package]]494name = "clap_builder"494name = "clap_builder"495version = "4.4.7"495version = "4.4.12"496source = "registry+https://github.com/rust-lang/crates.io-index"496source = "registry+https://github.com/rust-lang/crates.io-index"497checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"497checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"498dependencies = [498dependencies = [499 "anstream",499 "anstream",500 "anstyle",500 "anstyle",514 "heck",514 "heck",515 "proc-macro2",515 "proc-macro2",516 "quote",516 "quote",517 "syn 2.0.38",517 "syn 2.0.43",518]518]519519520[[package]]520[[package]]544544545[[package]]545[[package]]546name = "const-oid"546name = "const-oid"547version = "0.9.5"547version = "0.9.6"548source = "registry+https://github.com/rust-lang/crates.io-index"548source = "registry+https://github.com/rust-lang/crates.io-index"549checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"549checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"550550551[[package]]551[[package]]552name = "cookie-factory"552name = "cookie-factory"556556557[[package]]557[[package]]558name = "core-foundation-sys"558name = "core-foundation-sys"559version = "0.8.4"559version = "0.8.6"560source = "registry+https://github.com/rust-lang/crates.io-index"560source = "registry+https://github.com/rust-lang/crates.io-index"561checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"561checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"562562563[[package]]563[[package]]564name = "countme"564name = "countme"568568569[[package]]569[[package]]570name = "cpufeatures"570name = "cpufeatures"571version = "0.2.9"571version = "0.2.11"572source = "registry+https://github.com/rust-lang/crates.io-index"572source = "registry+https://github.com/rust-lang/crates.io-index"573checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"573checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"574dependencies = [574dependencies = [575 "libc",575 "libc",576]576]615checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"615checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"616dependencies = [616dependencies = [617 "cfg-if",617 "cfg-if",618 "hashbrown 0.14.1",618 "hashbrown 0.14.3",619 "lock_api",619 "lock_api",620 "once_cell",620 "once_cell",621 "parking_lot_core",621 "parking_lot_core",633633634[[package]]634[[package]]635name = "deranged"635name = "deranged"636version = "0.3.9"636version = "0.3.11"637source = "registry+https://github.com/rust-lang/crates.io-index"637source = "registry+https://github.com/rust-lang/crates.io-index"638checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"638checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"639dependencies = [639dependencies = [640 "powerfmt",640 "powerfmt",641 "serde",641 "serde",691dependencies = [691dependencies = [692 "proc-macro2",692 "proc-macro2",693 "quote",693 "quote",694 "syn 2.0.38",694 "syn 2.0.43",695]695]696696697[[package]]697[[package]]708708709[[package]]709[[package]]710name = "env_logger"710name = "env_logger"711version = "0.10.0"711version = "0.10.1"712source = "registry+https://github.com/rust-lang/crates.io-index"712source = "registry+https://github.com/rust-lang/crates.io-index"713checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"713checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"714dependencies = [714dependencies = [715 "humantime",715 "humantime",716 "is-terminal",716 "is-terminal",727727728[[package]]728[[package]]729name = "errno"729name = "errno"730version = "0.3.5"730version = "0.3.8"731source = "registry+https://github.com/rust-lang/crates.io-index"731source = "registry+https://github.com/rust-lang/crates.io-index"732checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"732checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"733dependencies = [733dependencies = [734 "libc",734 "libc",735 "windows-sys 0.48.0",735 "windows-sys 0.52.0",736]736]737737738[[package]]738[[package]]752752753[[package]]753[[package]]754name = "fleet"754name = "fleet"755version = "0.1.0"755version = "0.2.0"756dependencies = [756dependencies = [757 "abort-on-drop",757 "abort-on-drop",758 "age",758 "age",827 "intl-memoizer",827 "intl-memoizer",828 "intl_pluralrules",828 "intl_pluralrules",829 "rustc-hash",829 "rustc-hash",830 "self_cell",830 "self_cell 0.10.3",831 "smallvec",831 "smallvec",832 "unic-langid",832 "unic-langid",833]833]858858859[[package]]859[[package]]860name = "futures"860name = "futures"861version = "0.3.29"861version = "0.3.30"862source = "registry+https://github.com/rust-lang/crates.io-index"862source = "registry+https://github.com/rust-lang/crates.io-index"863checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"863checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"864dependencies = [864dependencies = [865 "futures-channel",865 "futures-channel",866 "futures-core",866 "futures-core",873873874[[package]]874[[package]]875name = "futures-channel"875name = "futures-channel"876version = "0.3.29"876version = "0.3.30"877source = "registry+https://github.com/rust-lang/crates.io-index"877source = "registry+https://github.com/rust-lang/crates.io-index"878checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"878checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"879dependencies = [879dependencies = [880 "futures-core",880 "futures-core",881 "futures-sink",881 "futures-sink",882]882]883883884[[package]]884[[package]]885name = "futures-core"885name = "futures-core"886version = "0.3.29"886version = "0.3.30"887source = "registry+https://github.com/rust-lang/crates.io-index"887source = "registry+https://github.com/rust-lang/crates.io-index"888checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"888checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"889889890[[package]]890[[package]]891name = "futures-executor"891name = "futures-executor"892version = "0.3.29"892version = "0.3.30"893source = "registry+https://github.com/rust-lang/crates.io-index"893source = "registry+https://github.com/rust-lang/crates.io-index"894checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"894checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"895dependencies = [895dependencies = [896 "futures-core",896 "futures-core",897 "futures-task",897 "futures-task",900900901[[package]]901[[package]]902name = "futures-io"902name = "futures-io"903version = "0.3.29"903version = "0.3.30"904source = "registry+https://github.com/rust-lang/crates.io-index"904source = "registry+https://github.com/rust-lang/crates.io-index"905checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"905checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"906906907[[package]]907[[package]]908name = "futures-macro"908name = "futures-macro"909version = "0.3.29"909version = "0.3.30"910source = "registry+https://github.com/rust-lang/crates.io-index"910source = "registry+https://github.com/rust-lang/crates.io-index"911checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"911checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"912dependencies = [912dependencies = [913 "proc-macro2",913 "proc-macro2",914 "quote",914 "quote",915 "syn 2.0.38",915 "syn 2.0.43",916]916]917917918[[package]]918[[package]]919name = "futures-sink"919name = "futures-sink"920version = "0.3.29"920version = "0.3.30"921source = "registry+https://github.com/rust-lang/crates.io-index"921source = "registry+https://github.com/rust-lang/crates.io-index"922checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"922checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"923923924[[package]]924[[package]]925name = "futures-task"925name = "futures-task"926version = "0.3.29"926version = "0.3.30"927source = "registry+https://github.com/rust-lang/crates.io-index"927source = "registry+https://github.com/rust-lang/crates.io-index"928checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"928checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"929929930[[package]]930[[package]]931name = "futures-util"931name = "futures-util"932version = "0.3.29"932version = "0.3.30"933source = "registry+https://github.com/rust-lang/crates.io-index"933source = "registry+https://github.com/rust-lang/crates.io-index"934checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"934checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"935dependencies = [935dependencies = [936 "futures-channel",936 "futures-channel",937 "futures-core",937 "futures-core",968968969[[package]]969[[package]]970name = "getrandom"970name = "getrandom"971version = "0.2.10"971version = "0.2.11"972source = "registry+https://github.com/rust-lang/crates.io-index"972source = "registry+https://github.com/rust-lang/crates.io-index"973checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"973checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"974dependencies = [974dependencies = [975 "cfg-if",975 "cfg-if",976 "libc",976 "libc",989989990[[package]]990[[package]]991name = "gimli"991name = "gimli"992version = "0.28.0"992version = "0.28.1"993source = "registry+https://github.com/rust-lang/crates.io-index"993source = "registry+https://github.com/rust-lang/crates.io-index"994checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"994checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"995995996[[package]]996[[package]]997name = "hashbrown"997name = "hashbrown"100110011002[[package]]1002[[package]]1003name = "hashbrown"1003name = "hashbrown"1004version = "0.14.1"1004version = "0.14.3"1005source = "registry+https://github.com/rust-lang/crates.io-index"1005source = "registry+https://github.com/rust-lang/crates.io-index"1006checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"1006checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"100710071008[[package]]1008[[package]]1009name = "heck"1009name = "heck"102810281029[[package]]1029[[package]]1030name = "hkdf"1030name = "hkdf"1031version = "0.12.3"1031version = "0.12.4"1032source = "registry+https://github.com/rust-lang/crates.io-index"1032source = "registry+https://github.com/rust-lang/crates.io-index"1033checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"1033checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"1034dependencies = [1034dependencies = [1035 "hmac",1035 "hmac",1036]1036]1077 "serde",1077 "serde",1078 "serde_derive",1078 "serde_derive",1079 "thiserror",1079 "thiserror",1080 "toml 0.8.0",1080 "toml 0.8.8",1081 "unic-langid",1081 "unic-langid",1082]1082]108310831119 "proc-macro2",1119 "proc-macro2",1120 "quote",1120 "quote",1121 "strsim",1121 "strsim",1122 "syn 2.0.38",1122 "syn 2.0.43",1123 "unic-langid",1123 "unic-langid",1124]1124]112511251126[[package]]1126[[package]]1127name = "i18n-embed-impl"1127name = "i18n-embed-impl"1128version = "0.8.2"1128version = "0.8.3"1129source = "registry+https://github.com/rust-lang/crates.io-index"1129source = "registry+https://github.com/rust-lang/crates.io-index"1130checksum = "a2a4d5bff745c9a6e1459c490059281b353a4ab0a4e1e58b3eeeaef71f97d07b"1130checksum = "81093c4701672f59416582fe3145676126fd23ba5db910acad0793c1108aaa58"1131dependencies = [1131dependencies = [1132 "find-crate",1132 "find-crate",1133 "i18n-config",1133 "i18n-config",1134 "proc-macro2",1134 "proc-macro2",1135 "quote",1135 "quote",1136 "syn 2.0.38",1136 "syn 2.0.43",1137]1137]113811381139[[package]]1139[[package]]1140name = "iana-time-zone"1140name = "iana-time-zone"1141version = "0.1.57"1141version = "0.1.59"1142source = "registry+https://github.com/rust-lang/crates.io-index"1142source = "registry+https://github.com/rust-lang/crates.io-index"1143checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"1143checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"1144dependencies = [1144dependencies = [1145 "android_system_properties",1145 "android_system_properties",1146 "core-foundation-sys",1146 "core-foundation-sys",1147 "iana-time-zone-haiku",1147 "iana-time-zone-haiku",1148 "js-sys",1148 "js-sys",1149 "wasm-bindgen",1149 "wasm-bindgen",1150 "windows",1150 "windows-core",1151]1151]115211521153[[package]]1153[[package]]116111611162[[package]]1162[[package]]1163name = "indexmap"1163name = "indexmap"1164version = "2.0.2"1164version = "2.1.0"1165source = "registry+https://github.com/rust-lang/crates.io-index"1165source = "registry+https://github.com/rust-lang/crates.io-index"1166checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"1166checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"1167dependencies = [1167dependencies = [1168 "equivalent",1168 "equivalent",1169 "hashbrown 0.14.1",1169 "hashbrown 0.14.3",1170]1170]117111711172[[package]]1172[[package]]122912291230[[package]]1230[[package]]1231name = "is-terminal"1231name = "is-terminal"1232version = "0.4.9"1232version = "0.4.10"1233source = "registry+https://github.com/rust-lang/crates.io-index"1233source = "registry+https://github.com/rust-lang/crates.io-index"1234checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"1234checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"1235dependencies = [1235dependencies = [1236 "hermit-abi 0.3.3",1236 "hermit-abi 0.3.3",1237 "rustix",1237 "rustix",1238 "windows-sys 0.48.0",1238 "windows-sys 0.52.0",1239]1239]124012401241[[package]]1241[[package]]125512551256[[package]]1256[[package]]1257name = "itoa"1257name = "itoa"1258version = "1.0.9"1258version = "1.0.10"1259source = "registry+https://github.com/rust-lang/crates.io-index"1259source = "registry+https://github.com/rust-lang/crates.io-index"1260checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"1260checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"126112611262[[package]]1262[[package]]1263name = "js-sys"1263name = "js-sys"1264version = "0.3.64"1264version = "0.3.66"1265source = "registry+https://github.com/rust-lang/crates.io-index"1265source = "registry+https://github.com/rust-lang/crates.io-index"1266checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"1266checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"1267dependencies = [1267dependencies = [1268 "wasm-bindgen",1268 "wasm-bindgen",1269]1269]127912791280[[package]]1280[[package]]1281name = "libc"1281name = "libc"1282version = "0.2.149"1282version = "0.2.151"1283source = "registry+https://github.com/rust-lang/crates.io-index"1283source = "registry+https://github.com/rust-lang/crates.io-index"1284checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"1284checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"128512851286[[package]]1286[[package]]1287name = "libm"1287name = "libm"1288version = "0.2.7"1288version = "0.2.8"1289source = "registry+https://github.com/rust-lang/crates.io-index"1289source = "registry+https://github.com/rust-lang/crates.io-index"1290checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"1290checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"129112911292[[package]]1292[[package]]1293name = "libmimalloc-sys"1293name = "libmimalloc-sys"1307dependencies = [1307dependencies = [1308 "bitflags 2.4.1",1308 "bitflags 2.4.1",1309 "libc",1309 "libc",1310 "redox_syscall 0.4.1",1310 "redox_syscall",1311]1311]131213121313[[package]]1313[[package]]131813181319[[package]]1319[[package]]1320name = "linux-raw-sys"1320name = "linux-raw-sys"1321version = "0.4.10"1321version = "0.4.12"1322source = "registry+https://github.com/rust-lang/crates.io-index"1322source = "registry+https://github.com/rust-lang/crates.io-index"1323checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"1323checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"132413241325[[package]]1325[[package]]1326name = "lock_api"1326name = "lock_api"1327version = "0.4.10"1327version = "0.4.11"1328source = "registry+https://github.com/rust-lang/crates.io-index"1328source = "registry+https://github.com/rust-lang/crates.io-index"1329checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"1329checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"1330dependencies = [1330dependencies = [1331 "autocfg",1331 "autocfg",1332 "scopeguard",1332 "scopeguard",135513551356[[package]]1356[[package]]1357name = "memchr"1357name = "memchr"1358version = "2.6.4"1358version = "2.7.1"1359source = "registry+https://github.com/rust-lang/crates.io-index"1359source = "registry+https://github.com/rust-lang/crates.io-index"1360checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"1360checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"136113611362[[package]]1362[[package]]1363name = "memoffset"1363name = "memoffset"139413941395[[package]]1395[[package]]1396name = "mio"1396name = "mio"1397version = "0.8.8"1397version = "0.8.10"1398source = "registry+https://github.com/rust-lang/crates.io-index"1398source = "registry+https://github.com/rust-lang/crates.io-index"1399checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"1399checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"1400dependencies = [1400dependencies = [1401 "libc",1401 "libc",1402 "wasi 0.11.0+wasi-snapshot-preview1",1402 "wasi 0.11.0+wasi-snapshot-preview1",151415141515[[package]]1515[[package]]1516name = "object"1516name = "object"1517version = "0.32.1"1517version = "0.32.2"1518source = "registry+https://github.com/rust-lang/crates.io-index"1518source = "registry+https://github.com/rust-lang/crates.io-index"1519checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"1519checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"1520dependencies = [1520dependencies = [1521 "memchr",1521 "memchr",1522]1522]153515351536[[package]]1536[[package]]1537name = "openssh"1537name = "openssh"1538version = "0.10.1"1538version = "0.10.2"1539source = "registry+https://github.com/rust-lang/crates.io-index"1539source = "registry+https://github.com/rust-lang/crates.io-index"1540checksum = "3dfe68c42d6ee6bd9de175b7a5d9bb86aa99d4e2fa7cf2f2a44e97f60b6d2759"1540checksum = "8274f2bf1fc3785406a3ff07c92c15590c00e84efb883da77b671562ca9a6115"1541dependencies = [1541dependencies = [1542 "dirs",1542 "dirs",1543 "libc",1543 "libc",159515951596[[package]]1596[[package]]1597name = "parking_lot_core"1597name = "parking_lot_core"1598version = "0.9.8"1598version = "0.9.9"1599source = "registry+https://github.com/rust-lang/crates.io-index"1599source = "registry+https://github.com/rust-lang/crates.io-index"1600checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"1600checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"1601dependencies = [1601dependencies = [1602 "cfg-if",1602 "cfg-if",1603 "libc",1603 "libc",1604 "redox_syscall 0.3.5",1604 "redox_syscall",1605 "smallvec",1605 "smallvec",1606 "windows-targets 0.48.5",1606 "windows-targets 0.48.5",1607]1607]1659dependencies = [1659dependencies = [1660 "proc-macro2",1660 "proc-macro2",1661 "quote",1661 "quote",1662 "syn 2.0.38",1662 "syn 2.0.43",1663]1663]166416641665[[package]]1665[[package]]172117211722[[package]]1722[[package]]1723name = "portable-atomic"1723name = "portable-atomic"1724version = "1.4.3"1724version = "1.6.0"1725source = "registry+https://github.com/rust-lang/crates.io-index"1725source = "registry+https://github.com/rust-lang/crates.io-index"1726checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b"1726checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"172717271728[[package]]1728[[package]]1729name = "powerfmt"1729name = "powerfmt"176317631764[[package]]1764[[package]]1765name = "proc-macro2"1765name = "proc-macro2"1766version = "1.0.69"1766version = "1.0.71"1767source = "registry+https://github.com/rust-lang/crates.io-index"1767source = "registry+https://github.com/rust-lang/crates.io-index"1768checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"1768checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"1769dependencies = [1769dependencies = [1770 "unicode-ident",1770 "unicode-ident",1771]1771]1849source = "registry+https://github.com/rust-lang/crates.io-index"1849source = "registry+https://github.com/rust-lang/crates.io-index"1850checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"1850checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"1851dependencies = [1851dependencies = [1852 "getrandom 0.2.10",1852 "getrandom 0.2.11",1853]1853]185418541855[[package]]1855[[package]]1861 "rand_core 0.5.1",1861 "rand_core 0.5.1",1862]1862]18631864[[package]]1865name = "redox_syscall"1866version = "0.3.5"1867source = "registry+https://github.com/rust-lang/crates.io-index"1868checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"1869dependencies = [1870 "bitflags 1.3.2",1871]187218631873[[package]]1864[[package]]1874name = "redox_syscall"1865name = "redox_syscall"1885source = "registry+https://github.com/rust-lang/crates.io-index"1876source = "registry+https://github.com/rust-lang/crates.io-index"1886checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"1877checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"1887dependencies = [1878dependencies = [1888 "getrandom 0.2.10",1879 "getrandom 0.2.11",1889 "libredox",1880 "libredox",1890 "thiserror",1881 "thiserror",1891]1882]2015 "proc-macro2",2006 "proc-macro2",2016 "quote",2007 "quote",2017 "rust-embed-utils",2008 "rust-embed-utils",2018 "syn 2.0.38",2009 "syn 2.0.43",2019 "walkdir",2010 "walkdir",2020]2011]20212012204320342044[[package]]2035[[package]]2045name = "rustix"2036name = "rustix"2046version = "0.38.21"2037version = "0.38.28"2047source = "registry+https://github.com/rust-lang/crates.io-index"2038source = "registry+https://github.com/rust-lang/crates.io-index"2048checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"2039checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"2049dependencies = [2040dependencies = [2050 "bitflags 2.4.1",2041 "bitflags 2.4.1",2051 "errno",2042 "errno",2052 "libc",2043 "libc",2053 "linux-raw-sys",2044 "linux-raw-sys",2054 "windows-sys 0.48.0",2045 "windows-sys 0.52.0",2055]2046]205620472057[[package]]2048[[package]]2058name = "ryu"2049name = "ryu"2059version = "1.0.15"2050version = "1.0.16"2060source = "registry+https://github.com/rust-lang/crates.io-index"2051source = "registry+https://github.com/rust-lang/crates.io-index"2061checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"2052checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"206220532063[[package]]2054[[package]]2064name = "salsa20"2055name = "salsa20"211621072117[[package]]2108[[package]]2118name = "self_cell"2109name = "self_cell"2119version = "0.10.2"2110version = "0.10.3"2120source = "registry+https://github.com/rust-lang/crates.io-index"2111source = "registry+https://github.com/rust-lang/crates.io-index"2121checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"2112checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"2113dependencies = [2114 "self_cell 1.0.3",2115]21162117[[package]]2118name = "self_cell"2119version = "1.0.3"2120source = "registry+https://github.com/rust-lang/crates.io-index"2121checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"212221222123[[package]]2123[[package]]2124name = "serde"2124name = "serde"2146dependencies = [2146dependencies = [2147 "proc-macro2",2147 "proc-macro2",2148 "quote",2148 "quote",2149 "syn 2.0.38",2149 "syn 2.0.43",2150]2150]215121512152[[package]]2152[[package]]216221622163[[package]]2163[[package]]2164name = "serde_spanned"2164name = "serde_spanned"2165version = "0.6.3"2165version = "0.6.5"2166source = "registry+https://github.com/rust-lang/crates.io-index"2166source = "registry+https://github.com/rust-lang/crates.io-index"2167checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"2167checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"2168dependencies = [2168dependencies = [2169 "serde",2169 "serde",2170]2170]218221822183[[package]]2183[[package]]2184name = "sharded-slab"2184name = "sharded-slab"2185version = "0.1.4"2185version = "0.1.7"2186source = "registry+https://github.com/rust-lang/crates.io-index"2186source = "registry+https://github.com/rust-lang/crates.io-index"2187checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"2187checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"2188dependencies = [2188dependencies = [2189 "lazy_static",2189 "lazy_static",2190]2190]223122312232[[package]]2232[[package]]2233name = "smallvec"2233name = "smallvec"2234version = "1.11.1"2234version = "1.11.2"2235source = "registry+https://github.com/rust-lang/crates.io-index"2235source = "registry+https://github.com/rust-lang/crates.io-index"2236checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"2236checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"223722372238[[package]]2238[[package]]2239name = "smol_str"2239name = "smol_str"224622462247[[package]]2247[[package]]2248name = "socket2"2248name = "socket2"2249version = "0.5.4"2249version = "0.5.5"2250source = "registry+https://github.com/rust-lang/crates.io-index"2250source = "registry+https://github.com/rust-lang/crates.io-index"2251checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"2251checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"2252dependencies = [2252dependencies = [2253 "libc",2253 "libc",2254 "windows-sys 0.48.0",2254 "windows-sys 0.48.0",230523052306[[package]]2306[[package]]2307name = "syn"2307name = "syn"2308version = "2.0.38"2308version = "2.0.43"2309source = "registry+https://github.com/rust-lang/crates.io-index"2309source = "registry+https://github.com/rust-lang/crates.io-index"2310checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"2310checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53"2311dependencies = [2311dependencies = [2312 "proc-macro2",2312 "proc-macro2",2313 "quote",2313 "quote",234223422343[[package]]2343[[package]]2344name = "tempfile"2344name = "tempfile"2345version = "3.8.1"2345version = "3.9.0"2346source = "registry+https://github.com/rust-lang/crates.io-index"2346source = "registry+https://github.com/rust-lang/crates.io-index"2347checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"2347checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"2348dependencies = [2348dependencies = [2349 "cfg-if",2349 "cfg-if",2350 "fastrand",2350 "fastrand",2351 "redox_syscall 0.4.1",2351 "redox_syscall",2352 "rustix",2352 "rustix",2353 "windows-sys 0.48.0",2353 "windows-sys 0.52.0",2354]2354]235523552356[[package]]2356[[package]]2357name = "termcolor"2357name = "termcolor"2358version = "1.3.0"2358version = "1.4.0"2359source = "registry+https://github.com/rust-lang/crates.io-index"2359source = "registry+https://github.com/rust-lang/crates.io-index"2360checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"2360checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"2361dependencies = [2361dependencies = [2362 "winapi-util",2362 "winapi-util",2363]2363]238023802381[[package]]2381[[package]]2382name = "thiserror"2382name = "thiserror"2383version = "1.0.50"2383version = "1.0.53"2384source = "registry+https://github.com/rust-lang/crates.io-index"2384source = "registry+https://github.com/rust-lang/crates.io-index"2385checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"2385checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09"2386dependencies = [2386dependencies = [2387 "thiserror-impl",2387 "thiserror-impl",2388]2388]238923892390[[package]]2390[[package]]2391name = "thiserror-impl"2391name = "thiserror-impl"2392version = "1.0.50"2392version = "1.0.53"2393source = "registry+https://github.com/rust-lang/crates.io-index"2393source = "registry+https://github.com/rust-lang/crates.io-index"2394checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"2394checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19"2395dependencies = [2395dependencies = [2396 "proc-macro2",2396 "proc-macro2",2397 "quote",2397 "quote",2398 "syn 2.0.38",2398 "syn 2.0.43",2399]2399]240024002401[[package]]2401[[package]]241024102411[[package]]2411[[package]]2412name = "time"2412name = "time"2413version = "0.3.30"2413version = "0.3.31"2414source = "registry+https://github.com/rust-lang/crates.io-index"2414source = "registry+https://github.com/rust-lang/crates.io-index"2415checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"2415checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"2416dependencies = [2416dependencies = [2417 "deranged",2417 "deranged",2418 "powerfmt",2418 "powerfmt",242924292430[[package]]2430[[package]]2431name = "time-macros"2431name = "time-macros"2432version = "0.2.15"2432version = "0.2.16"2433source = "registry+https://github.com/rust-lang/crates.io-index"2433source = "registry+https://github.com/rust-lang/crates.io-index"2434checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"2434checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"2435dependencies = [2435dependencies = [2436 "time-core",2436 "time-core",2437]2437]243824382439[[package]]2439[[package]]2440name = "tinystr"2440name = "tinystr"2441version = "0.7.2"2441version = "0.7.5"2442source = "registry+https://github.com/rust-lang/crates.io-index"2442source = "registry+https://github.com/rust-lang/crates.io-index"2443checksum = "8faa444297615a4e020acb64146b0603c9c395c03a97c17fd9028816d3b4d63e"2443checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece"2444dependencies = [2444dependencies = [2445 "displaydoc",2445 "displaydoc",2446]2446]244724472448[[package]]2448[[package]]2449name = "tokio"2449name = "tokio"2450version = "1.33.0"2450version = "1.35.1"2451source = "registry+https://github.com/rust-lang/crates.io-index"2451source = "registry+https://github.com/rust-lang/crates.io-index"2452checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"2452checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"2453dependencies = [2453dependencies = [2454 "backtrace",2454 "backtrace",2455 "bytes",2455 "bytes",246624662467[[package]]2467[[package]]2468name = "tokio-macros"2468name = "tokio-macros"2469version = "2.1.0"2469version = "2.2.0"2470source = "registry+https://github.com/rust-lang/crates.io-index"2470source = "registry+https://github.com/rust-lang/crates.io-index"2471checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"2471checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"2472dependencies = [2472dependencies = [2473 "proc-macro2",2473 "proc-macro2",2474 "quote",2474 "quote",2475 "syn 2.0.38",2475 "syn 2.0.43",2476]2476]247724772478[[package]]2478[[package]]251025102511[[package]]2511[[package]]2512name = "toml"2512name = "toml"2513version = "0.8.0"2513version = "0.8.8"2514source = "registry+https://github.com/rust-lang/crates.io-index"2514source = "registry+https://github.com/rust-lang/crates.io-index"2515checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e"2515checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"2516dependencies = [2516dependencies = [2517 "serde",2517 "serde",2518 "serde_spanned",2518 "serde_spanned",252225222523[[package]]2523[[package]]2524name = "toml_datetime"2524name = "toml_datetime"2525version = "0.6.3"2525version = "0.6.5"2526source = "registry+https://github.com/rust-lang/crates.io-index"2526source = "registry+https://github.com/rust-lang/crates.io-index"2527checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"2527checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"2528dependencies = [2528dependencies = [2529 "serde",2529 "serde",2530]2530]253125312532[[package]]2532[[package]]2533name = "toml_edit"2533name = "toml_edit"2534version = "0.20.0"2534version = "0.21.0"2535source = "registry+https://github.com/rust-lang/crates.io-index"2535source = "registry+https://github.com/rust-lang/crates.io-index"2536checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95"2536checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"2537dependencies = [2537dependencies = [2538 "indexmap",2538 "indexmap",2539 "serde",2539 "serde",2561dependencies = [2561dependencies = [2562 "proc-macro2",2562 "proc-macro2",2563 "quote",2563 "quote",2564 "syn 2.0.38",2564 "syn 2.0.43",2565]2565]256625662567[[package]]2567[[package]]258825882589[[package]]2589[[package]]2590name = "tracing-log"2590name = "tracing-log"2591version = "0.1.3"2591version = "0.2.0"2592source = "registry+https://github.com/rust-lang/crates.io-index"2592source = "registry+https://github.com/rust-lang/crates.io-index"2593checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"2593checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"2594dependencies = [2594dependencies = [2595 "lazy_static",2596 "log",2595 "log",2596 "once_cell",2597 "tracing-core",2597 "tracing-core",2598]2598]259925992600[[package]]2600[[package]]2601name = "tracing-subscriber"2601name = "tracing-subscriber"2602version = "0.3.17"2602version = "0.3.18"2603source = "registry+https://github.com/rust-lang/crates.io-index"2603source = "registry+https://github.com/rust-lang/crates.io-index"2604checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"2604checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"2605dependencies = [2605dependencies = [2606 "matchers",2606 "matchers",2607 "nu-ansi-term",2607 "nu-ansi-term",263226322633[[package]]2633[[package]]2634name = "unic-langid"2634name = "unic-langid"2635version = "0.9.1"2635version = "0.9.4"2636source = "registry+https://github.com/rust-lang/crates.io-index"2636source = "registry+https://github.com/rust-lang/crates.io-index"2637checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f"2637checksum = "238722e6d794ed130f91f4ea33e01fcff4f188d92337a21297892521c72df516"2638dependencies = [2638dependencies = [2639 "unic-langid-impl",2639 "unic-langid-impl",2640]2640]264126412642[[package]]2642[[package]]2643name = "unic-langid-impl"2643name = "unic-langid-impl"2644version = "0.9.1"2644version = "0.9.4"2645source = "registry+https://github.com/rust-lang/crates.io-index"2645source = "registry+https://github.com/rust-lang/crates.io-index"2646checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff"2646checksum = "4bd55a2063fdea4ef1f8633243a7b0524cbeef1905ae04c31a1c9b9775c55bc6"2647dependencies = [2647dependencies = [2648 "serde",2648 "serde",2649 "tinystr",2649 "tinystr",277227722773[[package]]2773[[package]]2774name = "wasm-bindgen"2774name = "wasm-bindgen"2775version = "0.2.87"2775version = "0.2.89"2776source = "registry+https://github.com/rust-lang/crates.io-index"2776source = "registry+https://github.com/rust-lang/crates.io-index"2777checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"2777checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"2778dependencies = [2778dependencies = [2779 "cfg-if",2779 "cfg-if",2780 "wasm-bindgen-macro",2780 "wasm-bindgen-macro",2781]2781]278227822783[[package]]2783[[package]]2784name = "wasm-bindgen-backend"2784name = "wasm-bindgen-backend"2785version = "0.2.87"2785version = "0.2.89"2786source = "registry+https://github.com/rust-lang/crates.io-index"2786source = "registry+https://github.com/rust-lang/crates.io-index"2787checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"2787checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"2788dependencies = [2788dependencies = [2789 "bumpalo",2789 "bumpalo",2790 "log",2790 "log",2791 "once_cell",2791 "once_cell",2792 "proc-macro2",2792 "proc-macro2",2793 "quote",2793 "quote",2794 "syn 2.0.38",2794 "syn 2.0.43",2795 "wasm-bindgen-shared",2795 "wasm-bindgen-shared",2796]2796]279727972798[[package]]2798[[package]]2799name = "wasm-bindgen-macro"2799name = "wasm-bindgen-macro"2800version = "0.2.87"2800version = "0.2.89"2801source = "registry+https://github.com/rust-lang/crates.io-index"2801source = "registry+https://github.com/rust-lang/crates.io-index"2802checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"2802checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"2803dependencies = [2803dependencies = [2804 "quote",2804 "quote",2805 "wasm-bindgen-macro-support",2805 "wasm-bindgen-macro-support",2806]2806]280728072808[[package]]2808[[package]]2809name = "wasm-bindgen-macro-support"2809name = "wasm-bindgen-macro-support"2810version = "0.2.87"2810version = "0.2.89"2811source = "registry+https://github.com/rust-lang/crates.io-index"2811source = "registry+https://github.com/rust-lang/crates.io-index"2812checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"2812checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"2813dependencies = [2813dependencies = [2814 "proc-macro2",2814 "proc-macro2",2815 "quote",2815 "quote",2816 "syn 2.0.38",2816 "syn 2.0.43",2817 "wasm-bindgen-backend",2817 "wasm-bindgen-backend",2818 "wasm-bindgen-shared",2818 "wasm-bindgen-shared",2819]2819]282028202821[[package]]2821[[package]]2822name = "wasm-bindgen-shared"2822name = "wasm-bindgen-shared"2823version = "0.2.87"2823version = "0.2.89"2824source = "registry+https://github.com/rust-lang/crates.io-index"2824source = "registry+https://github.com/rust-lang/crates.io-index"2825checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"2825checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"282628262827[[package]]2827[[package]]2828name = "winapi"2828name = "winapi"2856checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"2856checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"285728572858[[package]]2858[[package]]2859name = "windows"2859name = "windows-core"2860version = "0.48.0"2860version = "0.52.0"2861source = "registry+https://github.com/rust-lang/crates.io-index"2861source = "registry+https://github.com/rust-lang/crates.io-index"2862checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"2862checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"2863dependencies = [2863dependencies = [2864 "windows-targets 0.48.5",2864 "windows-targets 0.52.0",2865]2865]286628662867[[package]]2867[[package]]2882 "windows-targets 0.48.5",2882 "windows-targets 0.48.5",2883]2883]28842885[[package]]2886name = "windows-sys"2887version = "0.52.0"2888source = "registry+https://github.com/rust-lang/crates.io-index"2889checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"2890dependencies = [2891 "windows-targets 0.52.0",2892]288428932885[[package]]2894[[package]]2886name = "windows-targets"2895name = "windows-targets"2912 "windows_x86_64_msvc 0.48.5",2921 "windows_x86_64_msvc 0.48.5",2913]2922]29232924[[package]]2925name = "windows-targets"2926version = "0.52.0"2927source = "registry+https://github.com/rust-lang/crates.io-index"2928checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"2929dependencies = [2930 "windows_aarch64_gnullvm 0.52.0",2931 "windows_aarch64_msvc 0.52.0",2932 "windows_i686_gnu 0.52.0",2933 "windows_i686_msvc 0.52.0",2934 "windows_x86_64_gnu 0.52.0",2935 "windows_x86_64_gnullvm 0.52.0",2936 "windows_x86_64_msvc 0.52.0",2937]291429382915[[package]]2939[[package]]2916name = "windows_aarch64_gnullvm"2940name = "windows_aarch64_gnullvm"2924source = "registry+https://github.com/rust-lang/crates.io-index"2948source = "registry+https://github.com/rust-lang/crates.io-index"2925checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"2949checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"29502951[[package]]2952name = "windows_aarch64_gnullvm"2953version = "0.52.0"2954source = "registry+https://github.com/rust-lang/crates.io-index"2955checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"292629562927[[package]]2957[[package]]2928name = "windows_aarch64_msvc"2958name = "windows_aarch64_msvc"2936source = "registry+https://github.com/rust-lang/crates.io-index"2966source = "registry+https://github.com/rust-lang/crates.io-index"2937checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"2967checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"29682969[[package]]2970name = "windows_aarch64_msvc"2971version = "0.52.0"2972source = "registry+https://github.com/rust-lang/crates.io-index"2973checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"293829742939[[package]]2975[[package]]2940name = "windows_i686_gnu"2976name = "windows_i686_gnu"2948source = "registry+https://github.com/rust-lang/crates.io-index"2984source = "registry+https://github.com/rust-lang/crates.io-index"2949checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"2985checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"29862987[[package]]2988name = "windows_i686_gnu"2989version = "0.52.0"2990source = "registry+https://github.com/rust-lang/crates.io-index"2991checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"295029922951[[package]]2993[[package]]2952name = "windows_i686_msvc"2994name = "windows_i686_msvc"2960source = "registry+https://github.com/rust-lang/crates.io-index"3002source = "registry+https://github.com/rust-lang/crates.io-index"2961checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"3003checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"30043005[[package]]3006name = "windows_i686_msvc"3007version = "0.52.0"3008source = "registry+https://github.com/rust-lang/crates.io-index"3009checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"296230102963[[package]]3011[[package]]2964name = "windows_x86_64_gnu"3012name = "windows_x86_64_gnu"2972source = "registry+https://github.com/rust-lang/crates.io-index"3020source = "registry+https://github.com/rust-lang/crates.io-index"2973checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"3021checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"30223023[[package]]3024name = "windows_x86_64_gnu"3025version = "0.52.0"3026source = "registry+https://github.com/rust-lang/crates.io-index"3027checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"297430282975[[package]]3029[[package]]2976name = "windows_x86_64_gnullvm"3030name = "windows_x86_64_gnullvm"2984source = "registry+https://github.com/rust-lang/crates.io-index"3038source = "registry+https://github.com/rust-lang/crates.io-index"2985checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"3039checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"30403041[[package]]3042name = "windows_x86_64_gnullvm"3043version = "0.52.0"3044source = "registry+https://github.com/rust-lang/crates.io-index"3045checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"298630462987[[package]]3047[[package]]2988name = "windows_x86_64_msvc"3048name = "windows_x86_64_msvc"2996source = "registry+https://github.com/rust-lang/crates.io-index"3056source = "registry+https://github.com/rust-lang/crates.io-index"2997checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"3057checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"30583059[[package]]3060name = "windows_x86_64_msvc"3061version = "0.52.0"3062source = "registry+https://github.com/rust-lang/crates.io-index"3063checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"299830642999[[package]]3065[[package]]3000name = "winnow"3066name = "winnow"3001version = "0.5.15"3067version = "0.5.31"3002source = "registry+https://github.com/rust-lang/crates.io-index"3068source = "registry+https://github.com/rust-lang/crates.io-index"3003checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"3069checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"3004dependencies = [3070dependencies = [3005 "memchr",3071 "memchr",3006]3072]302430903025[[package]]3091[[package]]3026name = "zeroize"3092name = "zeroize"3027version = "1.6.0"3093version = "1.7.0"3028source = "registry+https://github.com/rust-lang/crates.io-index"3094source = "registry+https://github.com/rust-lang/crates.io-index"3029checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"3095checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"3030dependencies = [3096dependencies = [3031 "zeroize_derive",3097 "zeroize_derive",3032]3098]3039dependencies = [3105dependencies = [3040 "proc-macro2",3106 "proc-macro2",3041 "quote",3107 "quote",3042 "syn 2.0.38",3108 "syn 2.0.43",3043]3109]30443110cmds/fleet/Cargo.tomldiffbeforeafterboth1[package]1[package]2name = "fleet"2name = "fleet"3description = "NixOS configuration management"3description = "NixOS configuration management"4version = "0.1.0"4version = "0.2.0"5authors = ["Yaroslav Bolyukin <iam@lach.pw>"]5authors = ["Yaroslav Bolyukin <iam@lach.pw>"]6edition = "2021"6edition = "2021"77cmds/fleet/src/better_nix_eval.rsdiffbeforeafterboth9use std::sync::{Arc, OnceLock};9use std::sync::{Arc, OnceLock};101011use anyhow::{anyhow, bail, ensure, Context, Result};11use anyhow::{anyhow, bail, ensure, Context, Result};12use better_command::{ClonableHandler, NixHandler, Handler, NoopHandler};12use better_command::{ClonableHandler, Handler, NixHandler, NoopHandler};13use futures::StreamExt;13use futures::StreamExt;14use itertools::Itertools;14use itertools::Itertools;15use r2d2::{Pool, PooledConnection};15use r2d2::{Pool, PooledConnection};302 let v = self.execute_expression_string(fexpr).await?;302 let v = self303 .execute_expression_string(fexpr)304 .await305 .context("string expression")?;303 Ok(serde_json::from_str(&v)?)306 serde_json::from_str(&v).context("json parse")304 }307 }305 async fn execute_expression_wrapping(308 async fn execute_expression_wrapping(306 &mut self,309 &mut self,450453451#[macro_export]454#[macro_export]452macro_rules! nix_expr_inner {455macro_rules! nix_expr_inner {456 //(@munch_object FIXME: value should be arbitrary nix_expr_inner input... Time to write proc-macro?457 (@obj($o:ident) $field:ident, $($tt:tt)*) => {{458 $o.obj_key(459 NixExprBuilder::string(stringify!($field)),460 NixExprBuilder::field($field),461 );462 nix_expr_inner!(@obj($o) $($tt)*);463 }};464 (@obj($o:ident) $field:ident: $v:block, $($tt:tt)*) => {{465 $o.obj_key(466 NixExprBuilder::string(stringify!($field)),467 NixExprBuilder::serialized(&$v),468 );469 nix_expr_inner!(@obj($o) $($tt)*);470 }};471 (@obj($o:ident)) => {{}};453 (Obj { $($ident:ident: $($val:tt)+),* $(,)? }) => {{472 (Obj { $($tt:tt)* }) => {{454 use $crate::better_nix_eval::NixExprBuilder;473 use $crate::{better_nix_eval::NixExprBuilder, nix_expr_inner};455 let mut out = NixExprBuilder::object();474 let mut out = NixExprBuilder::object();456 $(457 out.obj_key(458 NixExprBuilder::string(stringify!($ident)),459 $crate::nix_expr_inner!($($val)+),475 nix_expr_inner!(@obj(out) $($tt)*);460 );461 )*462 out.end_obj();476 out.end_obj();463 out477 out464 }};478 }};522 $o.push(Index::ExprApply($crate::nix_expr_inner!($($var)+)));536 $o.push(Index::ExprApply($crate::nix_expr_inner!($($var)+)));523 nix_go!(@o($o) $($tt)*);537 nix_go!(@o($o) $($tt)*);524 };538 };539 (@o($o:ident) | $($var:tt)*) => {540 $o.push(Index::Pipe($crate::nix_expr_inner!($($var)+)));541 };525 (@o($o:ident)) => {};542 (@o($o:ident)) => {};526 ($field:ident $($tt:tt)+) => {{543 ($field:ident $($tt:tt)+) => {{527 use $crate::{nix_go, better_nix_eval::Index};544 use $crate::{nix_go, better_nix_eval::Index};545 Apply(String),562 Apply(String),546 Expr(NixExprBuilder),563 Expr(NixExprBuilder),547 ExprApply(NixExprBuilder),564 ExprApply(NixExprBuilder),565 Pipe(NixExprBuilder),548}566}549impl Index {567impl Index {550 pub fn var(v: impl AsRef<str>) -> Self {568 pub fn var(v: impl AsRef<str>) -> Self {582 Index::ExprApply(e) => {600 Index::ExprApply(e) => {583 write!(f, "<apply>({})", e.out)601 write!(f, "<apply>({})", e.out)584 }602 }603 Index::Pipe(e) => {604 write!(f, "<map>({})", e.out)605 }585 }606 }586 }607 }587}608}604 session: NixSession,625 session: NixSession,605 value: Option<u32>,626 value: Option<u32>,606}627}607fn context(full_path: Option<&[Index]>, query: &str) -> String {628fn context(op: &str, full_path: Option<&[Index]>, query: &str) -> String {608 if let Some(full_path) = &full_path {629 if let Some(full_path) = &full_path {609 format!("full path: {}", PathDisplay(full_path))630 format!("on {op}, full path: {}", PathDisplay(full_path))610 } else {631 } else {611 format!("query: {query:?}")632 format!("query: {query:?}")612 }633 }628 .await649 .await629 .execute_assign(query)650 .execute_assign(query)630 .await651 .await631 .with_context(|| context(None, query))?;652 .with_context(|| context("new root", None, query))?;632 Ok(Self(Arc::new(FieldInner {653 Ok(Self(Arc::new(FieldInner {633 full_path: None,654 full_path: None,634 session,655 session,686 query.push_str(&index);707 query.push_str(&index);687 query = format!("({query})");708 query = format!("({query})");688 }709 }710 Index::Pipe(v) => {711 let index = Field::new(self.0.session.clone(), &v.out).await?;712 used_fields.push(index.clone());713 let index = format!("sess_field_{}", index.0.value.expect("value"));714 query = format!("({index} {query})");715 }689 }716 }690 }717 }691718720 .await747 .await721 .execute_expression_to_json(&query)748 .execute_expression_to_json(&query)722 .await749 .await723 .with_context(|| context(self.0.full_path.as_deref(), &query))750 .with_context(|| context("as_json", self.0.full_path.as_deref(), &query))724 }751 }725 pub async fn has_field(&self, name: &str) -> Result<bool> {752 pub async fn has_field(&self, name: &str) -> Result<bool> {726 let id = self.0.value.expect("can't list root fields");753 let id = self.0.value.expect("can't list root fields");733 .await760 .await734 .execute_expression_to_json(&query)761 .execute_expression_to_json(&query)735 .await762 .await736 .with_context(|| context(self.0.full_path.as_deref(), &query))763 .with_context(|| context("has_field", self.0.full_path.as_deref(), &query))737 }764 }738 pub async fn list_fields(&self) -> Result<Vec<String>> {765 pub async fn list_fields(&self) -> Result<Vec<String>> {739 let id = self.0.value.expect("can't list root fields");766 let id = self.0.value.expect("can't list root fields");745 .await772 .await746 .execute_expression_to_json(&query)773 .execute_expression_to_json(&query)747 .await774 .await748 .with_context(|| context(self.0.full_path.as_deref(), &query))775 .with_context(|| context("list field", self.0.full_path.as_deref(), &query))749 }776 }750 pub async fn type_of(&self) -> Result<String> {777 pub async fn type_of(&self) -> Result<String> {751 let id = self.0.value.expect("can't list root fields");778 let id = self.0.value.expect("can't list root fields");757 .await784 .await758 .execute_expression_to_json(&query)785 .execute_expression_to_json(&query)759 .await786 .await760 .with_context(|| context(self.0.full_path.as_deref(), &query))787 .with_context(|| context("type_of", self.0.full_path.as_deref(), &query))761 }788 }789 pub async fn import(&self) -> Result<Self> {790 let import = Self::new(self.0.session.clone(), "import").await?;791 Ok(nix_go!(self | import))792 }762 pub async fn build(&self) -> Result<HashMap<String, PathBuf>> {793 pub async fn build(&self) -> Result<HashMap<String, PathBuf>> {763 let id = self.0.value.expect("can't use build on not-value");794 let id = self.0.value.expect("can't use build on not-value");764 let query = format!(":b sess_field_{id}");795 let query = format!(":b sess_field_{id}");773 ensure!(804 ensure!(774 !vid.is_empty(),805 !vid.is_empty(),775 "build failed: {}",806 "build failed: {}",776 context(self.0.full_path.as_deref(), &query),807 context("build", self.0.full_path.as_deref(), &query),777 );808 );778 let Some(vid) = vid.strip_prefix("This derivation produced the following outputs:\n")809 let Some(vid) = vid.strip_prefix("This derivation produced the following outputs:\n")779 else {810 else {cmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth53 fn build_attr(&self) -> String {53 fn build_attr(&self) -> String {54 match self {54 match self {55 PackageAction::SdImage => "sdImage".to_owned(),55 PackageAction::SdImage => "sdImage".to_owned(),56 PackageAction::InstallationCd => "installationCd".to_owned(),56 PackageAction::InstallationCd => "isoImage".to_owned(),57 }57 }58 }58 }59}59}178 if !build.disable_rollback {178 if !build.disable_rollback {179 let _span = info_span!("preparing").entered();179 let _span = info_span!("preparing").entered();180 info!("preparing for rollback");180 info!("preparing for rollback");181 let generation = get_current_generation(&host).await?;181 let generation = get_current_generation(host).await?;182 info!(182 info!(183 "rollback target would be {} {}",183 "rollback target would be {} {}",184 generation.id, generation.datetime184 generation.id, generation.datetime234 let mut switch_script = built.clone();234 let mut switch_script = built.clone();235 switch_script.push("bin");235 switch_script.push("bin");236 switch_script.push("switch-to-configuration");236 switch_script.push("switch-to-configuration");237 let mut cmd = host.cmd(switch_script).await?;237 let mut cmd = host.cmd(switch_script).in_current_span().await?;238 cmd.arg(action.name());238 cmd.arg(action.name());239 if let Err(e) = cmd.sudo().run().in_current_span().await {239 if let Err(e) = cmd.sudo().run().in_current_span().await {240 error!("failed to activate: {e}");240 error!("failed to activate: {e}");285 info!("building");285 info!("building");286 let host = config.host(&host).await?;286 let host = config.host(&host).await?;287 let action = Action::from(self.subcommand.clone());287 let action = Action::from(self.subcommand.clone());288 let fleet_field = &config.fleet_field;288 let fleet_config = &config.config_field;289 let drv = nix_go!(289 let drv = nix_go!(290 fleet_field.buildSystems(Obj {290 fleet_config.hosts[{ &host.name }].nixosSystem.config.system.build[{ action.build_attr() }]291 localSystem: { config.local_system.clone() }292 })[{ action.build_attr() }][{ &host.name }]293 );291 );294 let outputs = drv.build().await.map_err(|e| {292 let outputs = drv.build().await.map_err(|e| {295 if action.build_attr() == "sdImage" {293 if action.build_attr() == "sdImage" {cmds/fleet/src/cmds/info.rsdiffbeforeafterboth37 InfoCmd::ListHosts { ref tagged } => {37 InfoCmd::ListHosts { ref tagged } => {38 'host: for host in config.list_hosts().await? {38 'host: for host in config.list_hosts().await? {39 if !tagged.is_empty() {39 if !tagged.is_empty() {40 let fleet_field = &config.fleet_field;40 let config = &config.config_unchecked_field;41 let tags: Vec<String> =41 let tags: Vec<String> =42 nix_go_json!(fleet_field.configuredSystems[{ host.name }].config.tags);42 nix_go_json!(config.hosts[{ host.name }].nixosSystem.config.tags);43 for tag in tagged {43 for tag in tagged {44 if !tags.contains(tag) {44 if !tags.contains(tag) {45 continue 'host;45 continue 'host;cmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth10use futures::StreamExt;10use futures::StreamExt;11use itertools::Itertools;11use itertools::Itertools;12use owo_colors::OwoColorize;12use owo_colors::OwoColorize;13use serde::Deserialize;13use std::{14use std::{14 collections::HashSet,15 collections::{BTreeSet, HashSet},15 io::{self, Cursor, Read},16 io::{self, Cursor, Read},16 path::PathBuf,17 path::PathBuf,17};18};18use tabled::{Table, Tabled};19use tabled::{Table, Tabled};19use tokio::fs::read_to_string;20use tokio::fs::read_to_string;20use tracing::{info, info_span, warn};21use tracing::{error, info, info_span, warn, Instrument};212222#[derive(Parser)]23#[derive(Parser)]23pub enum Secret {24pub enum Secret {92 List {},93 List {},93}94}949596#[tracing::instrument(skip(config, secret, field, prefer_identities))]95async fn generate_shared(97async fn update_owner_set(98 secret_name: &str,99 config: &Config,100 mut secret: FleetSharedSecret,101 field: Field,102 updated_set: &[String],103 prefer_identities: &[String],104) -> Result<FleetSharedSecret> {105 let original_set = secret.owners.clone();106107 let set = original_set.iter().collect::<BTreeSet<_>>();108 let expected_set = updated_set.iter().collect::<BTreeSet<_>>();109110 if set == expected_set {111 info!("no need to update owner list, it is already correct");112 return Ok(secret);113 }114115 let should_regenerate = if set.difference(&expected_set).next().is_some() {116 // TODO: Remove this warning for revokable secrets.117 warn!("host was removed from secret owners, but until this host rebuild, the secret will still be stored on it.");118 nix_go_json!(field.regenerateOnOwnerRemoved)119 } else if expected_set.difference(&set).next().is_some() {120 nix_go_json!(field.regenerateOnOwnerAdded)121 } else {122 false123 };124125 if should_regenerate {126 info!("secret is owner-dependent, will regenerate");127 let generated = generate_shared(config, secret_name, field, updated_set.to_vec()).await?;128 Ok(generated)129 } else {130 let identity_holder = if !prefer_identities.is_empty() {131 prefer_identities132 .iter()133 .find(|i| original_set.iter().any(|s| s == *i))134 } else {135 secret.owners.first()136 };137 let Some(identity_holder) = identity_holder else {138 bail!("no available holder found");139 };140141 if let Some(data) = secret.secret.secret {142 let host = config.host(identity_holder).await?;143 let encrypted = host.reencrypt(data, updated_set.to_vec()).await?;144 secret.secret.secret = Some(encrypted);145 }146147 secret.owners = updated_set.to_vec();148 Ok(secret)149 }150}151152#[derive(Deserialize)]153#[serde(rename_all = "camelCase")]154enum GeneratorKind {155 Impure,156}157158async fn generate_impure(96 config: &Config,159 config: &Config,97 display_name: &str,160 _display_name: &str,98 secret: Field,161 secret: Field,162 default_generator: Field,163 owners: &[String],99) -> Result<FleetSharedSecret> {164) -> Result<FleetSecret> {100 Ok(if secret.has_field("generateImpure").await? {101 let config_field = &config.config_unchecked_field;165 let config_field = &config.config_unchecked_field;102 let generate = nix_go!(secret.generateImpure);166 let generator = nix_go!(secret.generator);103 let owners: Vec<String> = nix_go_json!(secret.expectedOwners);104167105 let on: String = nix_go_json!(generate.on);168 let on: String = nix_go_json!(default_generator.impureOn);106 let call_package = nix_go!(169 let call_package = nix_go!(107 config_field.buildableSystems(Obj {170 config_field.buildableSystems(Obj {108 localSystem: { config.local_system.clone() }171 localSystem: { config.local_system.clone() },115178116 let host = config.host(&on).await?;179 let host = config.host(&on).await?;117180118 let generator = nix_go!(call_package(generate.generator)(Obj {}));181 let generator = nix_go!(call_package(generator)(Obj {}));119 let generator = generator.build().await?;182 let generator = generator.build().await?;120 let generator = generator183 let generator = generator121 .get("out")184 .get("out")122 .ok_or_else(|| anyhow!("missing generateImpure out"))?;185 .ok_or_else(|| anyhow!("missing generateImpure out"))?;123 let generator = host.remote_derivation(generator).await?;186 let generator = host.remote_derivation(generator).await?;124187125 let mut recipients = String::new();188 let mut recipients = String::new();126 for owner in &owners {189 for owner in owners {127 let key = config.key(owner).await?;190 let key = config.key(owner).await?;128 recipients.push_str(&format!("-r \"{key}\" "));191 recipients.push_str(&format!("-r \"{key}\" "));129 }192 }133196134 let mut gen = host.cmd(generator).await?;197 let mut gen = host.cmd(generator).await?;135 gen.env("rageArgs", recipients).env("out", &out);198 gen.env("rageArgs", recipients).env("out", &out);136 gen.run().await?;199 gen.run().await.context("impure generator")?;137200138 {201 {139 let marker = host.read_file_text(format!("{out}/marker")).await?;202 let marker = host.read_file_text(format!("{out}/marker")).await?;145 if let Some(secret) = &secret {208 if let Some(secret) = &secret {146 ensure!(209 ensure!(147 age::Decryptor::new(Cursor::new(&secret)).is_ok(),210 age::Decryptor::new(Cursor::new(&secret)).is_ok(),148 "builder produced non-encrypted value as secret, this is highly insecure"211 "builder produced non-encrypted value as secret, this is highly insecure, and not allowed."149 );212 );150 }213 }151214152 let created_at = host.read_file_value(format!("{out}/created_at")).await?;215 let created_at = host.read_file_value(format!("{out}/created_at")).await?;153 let expires_at = host.read_file_value(format!("{out}/expires_at")).await.ok();216 let expires_at = host.read_file_value(format!("{out}/expires_at")).await.ok();154217155 FleetSharedSecret {156 owners,157 secret: FleetSecret {218 Ok(FleetSecret {158 created_at,219 created_at,159 expires_at,220 expires_at,160 public,221 public,161 secret: secret.map(SecretData),222 secret: secret.map(SecretData),162 },223 })163 }164 } else {165 bail!("no generator defined for {display_name}")166 })167}224}225async fn generate(226 config: &Config,227 display_name: &str,228 secret: Field,229 owners: &[String],230) -> Result<FleetSecret> {231 let generator = nix_go!(secret.generator);232 // Can't properly check on nix module system level233 {234 let gen_ty = generator.type_of().await?;235 if gen_ty == "null" {236 bail!("secret has no generator defined, can't automatically generate it.");237 }238 if gen_ty != "lambda" {239 bail!("generator should be lambda, got {gen_ty}");240 }241 }242 let default_pkgs = &config.default_pkgs;243 let default_call_package = nix_go!(default_pkgs.callPackage);244 // Generators provide additional information in passthru, to access245 // passthru we should call generator, but information about where this generator is supposed to build246 // is located in passthru... Thus evaluating generator on host.247 //248 // Maybe it is also possible to do some magic with __functor?249 //250 // I don't want to make modules always responsible for additional secret data anyway,251 // so it should be in derivation, and not in the secret data itself.252 let default_generator = nix_go!(default_call_package(generator)(Obj {}));253254 let kind: GeneratorKind = nix_go_json!(default_generator.generatorKind);255256 match kind {257 GeneratorKind::Impure => {258 generate_impure(config, display_name, secret, default_generator, owners).await259 }260 }261}262async fn generate_shared(263 config: &Config,264 display_name: &str,265 secret: Field,266 expected_owners: Vec<String>,267) -> Result<FleetSharedSecret> {268 // let owners: Vec<String> = nix_go_json!(secret.expectedOwners);269 Ok(FleetSharedSecret {270 secret: generate(config, display_name, secret, &expected_owners).await?,271 owners: expected_owners,272 })273}168274169async fn parse_public(275async fn parse_public(170 public: Option<String>,276 public: Option<String>,270 machines = shared.owners;376 machines = shared.owners;271 }377 }272378273 let recipients = config379 let recipients = config.recipients(machines.clone()).await?;274 .recipients(&machines.iter().map(String::as_str).collect_vec())275 .await?;276380277 let secret = {381 let secret = {362 remove_machines,466 remove_machines,363 prefer_identities,467 prefer_identities,364 } => {468 } => {365 let mut secret = config.shared_secret(&name)?;469 let secret = config.shared_secret(&name)?;366 if secret.secret.secret.is_none() {470 if secret.secret.secret.is_none() {367 bail!("no secret");471 bail!("no secret");368 }472 }381 return Ok(());485 return Ok(());382 }486 }383384 if target_machines == initial_machines {385 warn!("secret owners are already correct");386 return Ok(());387 }388487389 let identity_holder = if !prefer_identities.is_empty() {488 let config_field = &config.config_unchecked_field;390 prefer_identities391 .iter()392 .find(|i| initial_machines.iter().any(|s| s == *i))393 } else {394 secret.owners.first()395 };396 let Some(identity_holder) = identity_holder else {489 let config_field = nix_go!(config_field.configUnchecked);397 bail!("no available holder found");398 };399 let target_recipients = futures::stream::iter(&target_machines)490 let field = nix_go!(config_field.sharedSecrets[{ name }]);400 .then(|m| async { config.key(m).await })401 .collect::<Vec<_>>()402 .await;403 let target_recipients =404 target_recipients.into_iter().collect::<Result<Vec<_>>>()?;405491406 if let Some(data) = secret.secret.secret {492 let updated = update_owner_set(493 &name,494 config,495 secret,496 field,497 &target_machines,407 let host = config.host(&identity_holder).await?;498 &prefer_identities,499 )408 let encrypted = host.reencrypt(data, target_recipients).await?;500 .await?;409 secret.secret.secret = Some(encrypted);410 }411412 secret.owners = target_machines;413 config.replace_shared(name, secret);501 config.replace_shared(name, updated);414 }502 }415 Secret::Regenerate { prefer_identities } => {503 Secret::Regenerate { prefer_identities } => {504 info!("checking for secrets to regenerate");416 {505 {506 let _span = info_span!("shared").entered();417 let expected_shared_set = config507 let expected_shared_set = config418 .list_configured_shared()508 .list_configured_shared()419 .await?509 .await?420 .into_iter()510 .into_iter()421 .collect::<HashSet<_>>();511 .collect::<HashSet<_>>();422 let shared_set = config.list_shared().into_iter().collect::<HashSet<_>>();512 let shared_set = config.list_shared().into_iter().collect::<HashSet<_>>();423 for removed in expected_shared_set.difference(&shared_set) {513 for missing in expected_shared_set.difference(&shared_set) {424 info!("generating secret: {removed}");425 let config_field = &config.config_unchecked_field;514 let config_field = &config.config_unchecked_field;426 let config_field = nix_go!(config_field.configUnchecked);515 let config_field = nix_go!(config_field.configUnchecked);427 let secret = nix_go!(config_field.sharedSecrets[{ removed }]);516 let secret = nix_go!(config_field.sharedSecrets[{ missing }]);517 let expected_owners: Option<Vec<String>> =518 nix_go_json!(secret.expectedOwners);519 let Some(expected_owners) = expected_owners else {520 // TODO: Might still need to regenerate521 continue;522 };523 info!("generating secret: {missing}");428 let shared = generate_shared(config, removed, secret).await?;524 let shared = generate_shared(config, missing, secret, expected_owners)525 .in_current_span()526 .await?;429 config.replace_shared(removed.to_string(), shared)527 config.replace_shared(missing.to_string(), shared)430 }528 }431 }529 }530 for host in config.list_hosts().await? {531 let _span = info_span!("host", host = host.name).entered();532 let expected_set = host533 .list_configured_secrets()534 .in_current_span()535 .await?536 .into_iter()537 .collect::<HashSet<_>>();538 let stored_set = config539 .list_secrets(&host.name)540 .into_iter()541 .collect::<HashSet<_>>();542 for missing in expected_set.difference(&stored_set) {543 info!("generating secret: {missing}");544 let secret = host.secret_field(missing).in_current_span().await?;545 let generated =546 match generate(config, missing, secret, &[host.name.clone()])547 .in_current_span()548 .await549 {550 Ok(v) => v,551 Err(e) => {552 error!("{e}");553 continue;554 }555 };556 config.insert_secret(&host.name, missing.to_string(), generated)557 }558 }432 let mut to_remove = Vec::new();559 let mut to_remove = Vec::new();433 for name in &config.list_shared() {560 for name in &config.list_shared() {434 info!("updating secret: {name}");561 info!("updating secret: {name}");435 let mut data = config.shared_secret(name)?;562 let data = config.shared_secret(name)?;436 let config_field = &config.config_unchecked_field;563 let config_field = &config.config_unchecked_field;437 let config_field = nix_go!(config_field.configUnchecked);564 let config_field = nix_go!(config_field.configUnchecked);438 let expected_owners: Vec<String> =565 let expected_owners: Vec<String> =442 to_remove.push(name.to_string());569 to_remove.push(name.to_string());443 continue;570 continue;444 }571 }445 let set = data.owners.iter().collect::<HashSet<_>>();446 let expected_set = expected_owners.iter().collect::<HashSet<_>>();447 let should_remove = set.difference(&expected_set).next().is_some();448 if set == expected_set {449 info!("secret data is ok");450 continue;451 }452572453 let secret = nix_go!(config_field.sharedSecrets[{ name }]);573 let secret = nix_go!(config_field.sharedSecrets[{ name }]);454 let owner_dependent: bool = nix_go_json!(secret.ownerDependent);455 let regenerate_on_remove: bool = nix_go_json!(secret.regenerateOnOwnerRemoved);456 #[allow(clippy::nonminimal_bool)]457 if !owner_dependent && !(should_remove && regenerate_on_remove) {458 warn!("reencrypting secret '{name}' for new owner set");459 // TODO: force regeneration460 if should_remove {461 warn!("secret will not be regenerated for removed machines, and until host rebuild, they will still possess the ability to decode secret");462 }463464 let identity_holder = if !prefer_identities.is_empty() {465 prefer_identities466 .iter()467 .find(|i| data.owners.iter().any(|s| s == *i))468 } else {469 data.owners.first()470 };471 let Some(identity_holder) = identity_holder else {472 bail!("no available holder found");473 };474475 let target_recipients = futures::stream::iter(&expected_owners)476 .then(|m| async { config.key(m).await })477 .collect::<Vec<_>>()478 .await;479 let target_recipients =480 target_recipients.into_iter().collect::<Result<Vec<_>>>()?;481482 if let Some(secret) = data.secret.secret {483 let host = config.host(identity_holder).await?;484 let encrypted = host.reencrypt(secret, target_recipients).await?;485486 data.secret.secret = Some(encrypted);487 }488 data.owners = expected_owners;489 config.replace_shared(name.to_owned(), data);574 config.replace_shared(490 } else {575 name.to_owned(),576 update_owner_set(577 &name,491 let shared = generate_shared(config, name, secret).await?;578 config,492 config.replace_shared(name.to_owned(), shared)579 data,493 }580 secret,581 &expected_owners,582 &prefer_identities,583 )584 .await?,585 );494 }586 }495 for k in to_remove {587 for k in to_remove {496 config.remove_shared(&k);588 config.remove_shared(&k);cmds/fleet/src/command.rsdiffbeforeafterboth1use std::thread::sleep;2use std::time::Duration;3use std::{ffi::OsStr, pin, process::Stdio, sync::Arc, task::Poll};1use std::{ffi::OsStr, pin, process::Stdio, sync::Arc, task::Poll};425use anyhow::{anyhow, Result};3use anyhow::{anyhow, Result};9use openssh::{OverSsh, OwningCommand, Session};7use openssh::{OverSsh, OwningCommand, Session};10use tokio::{io::AsyncRead, process::Command, select};8use tokio::{io::AsyncRead, process::Command, select};11use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec};9use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec};12use tracing::{info, debug};10use tracing::debug;131114fn escape_bash(input: &str, out: &mut String) {12fn escape_bash(input: &str, out: &mut String) {15 const TO_ESCAPE: &str = "$ !\"#&'()*,;<>?[\\]^`{|}";13 const TO_ESCAPE: &str = "$ !\"#&'()*,;<>?[\\]^`{|}";162 self160 self163 }161 }164 pub fn sudo(mut self) -> Self {162 pub fn sudo(mut self) -> Self {163 // TODO: Multiple escalation strategies.164 // Maybe escalation should be moved to ConfigHost, to also support cases165 // when there is no sudo on remote machine, but instead we can reconnect166 // as root using ssh?165 if std::env::var_os("NO_SUDO").is_some() {167 if std::env::var_os("NO_SUDO").is_some() {166 let mut out = Self::new("su");168 let mut out = Self::new("su");167 out.ssh_session = self.ssh_session.take();169 out.ssh_session = self.ssh_session.take();267) -> Result<Option<Vec<u8>>> {269) -> Result<Option<Vec<u8>>> {268 cmd.stderr(Stdio::piped());270 cmd.stderr(Stdio::piped());269 cmd.stdout(Stdio::piped());271 cmd.stdout(Stdio::piped());270 debug!("running command {cmd:?} on local");272 debug!("running command {str:?} on local");271 let mut child = cmd.spawn()?;273 let mut child = cmd.spawn()?;272 let mut stderr = child.stderr.take().unwrap();274 let mut stderr = child.stderr.take().unwrap();273 let stdout = child.stdout.take().unwrap();275 let stdout = child.stdout.take().unwrap();328 err_handler: &mut dyn Handler,330 err_handler: &mut dyn Handler,329 mut out_handler: Option<&mut dyn Handler>,331 mut out_handler: Option<&mut dyn Handler>,330) -> Result<Option<Vec<u8>>> {332) -> Result<Option<Vec<u8>>> {331 debug!("running command {cmd:?} over ssh");333 debug!("running command {str:?} over ssh");332 cmd.stderr(openssh::Stdio::piped());334 cmd.stderr(openssh::Stdio::piped());333 cmd.stdout(openssh::Stdio::piped());335 cmd.stdout(openssh::Stdio::piped());334 let mut child = cmd.spawn().await?;336 let mut child = cmd.spawn().await?;cmds/fleet/src/host.rsdiffbeforeafterboth14use openssh::SessionBuilder;14use openssh::SessionBuilder;15use serde::de::DeserializeOwned;15use serde::de::DeserializeOwned;16use tempfile::NamedTempFile;16use tempfile::NamedTempFile;17use tracing::instrument;171818use crate::{19use crate::{19 better_nix_eval::{Field, NixSessionPool},20 better_nix_eval::{Field, NixSessionPool},28 pub opts: FleetOpts,29 pub opts: FleetOpts,29 pub data: Mutex<FleetData>,30 pub data: Mutex<FleetData>,30 pub nix_args: Vec<OsString>,31 pub nix_args: Vec<OsString>,31 /// fleetConfigurations.<name>.<localSystem>32 /// fleet_config.config32 pub fleet_field: Field,33 pub config_field: Field,33 /// fleet_config.configUnchecked34 /// fleet_config.unchecked.config34 pub config_field: Field,35 pub config_unchecked_field: Field,35 /// fleet_config.unchecked3637 /// import nixpkgs {system = local};36 pub config_unchecked_field: Field,38 pub default_pkgs: Field,37}39}384039#[derive(Clone)]41#[derive(Clone)]48}50}495150pub struct ConfigHost {52pub struct ConfigHost {53 config: Config,51 pub name: String,54 pub name: String,52 pub local: bool,55 pub local: bool,53 pub session: OnceLock<Arc<openssh::Session>>,56 pub session: OnceLock<Arc<openssh::Session>>,5758 pub nixos_config: Field,54}59}55impl ConfigHost {60impl ConfigHost {56 async fn open_session(&self) -> Result<Arc<openssh::Session>> {61 async fn open_session(&self) -> Result<Arc<openssh::Session>> {64 let session = session69 let session = session65 .connect(&self.name)70 .connect(&self.name)66 .await71 .await67 .map_err(|e| anyhow!("ssh error: {e}"))?;72 .map_err(|e| anyhow!("ssh error while connecting to {}: {e}", self.name))?;68 let session = Arc::new(session);73 let session = Arc::new(session);69 self.session.set(session.clone()).expect("TOCTOU happened");74 self.session.set(session.clone()).expect("TOCTOU happened");70 Ok(session)75 Ok(session)119 let mut cmd = self.cmd("fleet-install-secrets").await?;124 let mut cmd = self.cmd("fleet-install-secrets").await?;120 cmd.arg("reencrypt").eqarg("--secret", data.encode_z85());125 cmd.arg("reencrypt").eqarg("--secret", data.encode_z85());121 for target in targets {126 for target in targets {127 let key = self.config.key(&target).await?;122 cmd.eqarg("--targets", target);128 cmd.eqarg("--targets", key);123 }129 }124 let encoded = cmd130 let encoded = cmd125 .sudo()131 .sudo()139 .arg("--substitute-on-destination")145 .arg("--substitute-on-destination")140 .comparg("--to", format!("ssh-ng://{}", self.name))146 .comparg("--to", format!("ssh-ng://{}", self.name))141 .arg(path);147 .arg(path);142 nix.run_nix().await?;148 nix.run_nix().await.context("nix copy")?;143 Ok(path.to_owned())149 Ok(path.to_owned())144 }150 }145 pub async fn systemctl_stop(&self, name: &str) -> Result<()> {151 pub async fn systemctl_stop(&self, name: &str) -> Result<()> {162 cmd.run().await168 cmd.run().await163 }169 }170171 pub async fn list_configured_secrets(&self) -> Result<Vec<String>> {172 let nixos = &self.nixos_config;173 let secrets = nix_go!(nixos.secrets);174 let mut out = Vec::new();175 for name in secrets.list_fields().await? {176 let secret = nix_go!(secrets[{ name }]);177 let is_shared: bool = nix_go_json!(secret.shared);178 if is_shared {179 continue;180 }181 out.push(name);182 }183 Ok(out)184 }185 pub async fn secret_field(&self, name: &str) -> Result<Field> {186 let nixos = &self.nixos_config;187 Ok(nix_go!(nixos.secrets[{ name }]))188 }164}189}165190166impl Config {191impl Config {178 }203 }179204180 pub async fn host(&self, name: &str) -> Result<ConfigHost> {205 pub async fn host(&self, name: &str) -> Result<ConfigHost> {206 let config = &self.config_unchecked_field;207 let nixos_config = nix_go!(config.configuredSystems[{ name }].config);181 Ok(ConfigHost {208 Ok(ConfigHost {209 config: self.clone(),182 name: name.to_owned(),210 name: name.to_owned(),183 local: self.is_local(name),211 local: self.is_local(name),184 session: OnceLock::new(),212 session: OnceLock::new(),213 nixos_config,185 })214 })186 }215 }187 pub async fn list_hosts(&self) -> Result<Vec<ConfigHost>> {216 pub async fn list_hosts(&self) -> Result<Vec<ConfigHost>> {188 let fleet_field = &self.fleet_field;217 let config = &self.config_unchecked_field;189 let names = nix_go!(fleet_field.configuredHosts).list_fields().await?;218 let names = nix_go!(config.hosts).list_fields().await?;190 let mut out = vec![];219 let mut out = vec![];191 for name in names {220 for name in names {192 out.push(ConfigHost {221 out.push(self.host(&name).await?);193 local: self.is_local(&name),194 name,195 session: OnceLock::new(),196 })197 }222 }198 Ok(out)223 Ok(out)199 }224 }200 pub async fn system_config(&self, host: &str) -> Result<Field> {225 pub async fn system_config(&self, host: &str) -> Result<Field> {201 let fleet_field = &self.fleet_field;226 let fleet_field = &self.config_unchecked_field;202 Ok(nix_go!(fleet_field.configuredSystems[{ host }].config))227 Ok(nix_go!(fleet_field.hosts[{ host }].nixosSystem.config))203 }228 }204229205 pub(super) fn data(&self) -> MutexGuard<FleetData> {230 pub(super) fn data(&self) -> MutexGuard<FleetData> {233 data.shared_secrets.remove(secret);258 data.shared_secrets.remove(secret);234 }259 }260261 pub fn list_secrets(&self, host: &str) -> Vec<String> {262 let data = self.data();263 let Some(secrets) = data.host_secrets.get(host) else {264 return Vec::new();265 };266 secrets.keys().cloned().collect()267 }235268236 pub fn has_secret(&self, host: &str, secret: &str) -> bool {269 pub fn has_secret(&self, host: &str, secret: &str) -> bool {237 let data = self.data();270 let data = self.data();319 let pool = NixSessionPool::new(directory.as_os_str().to_owned(), nix_args.clone()).await?;352 let pool = NixSessionPool::new(directory.as_os_str().to_owned(), nix_args.clone()).await?;320 let root_field = pool.get().await?;353 let root_field = pool.get().await?;321354322 if self.local_system == "detect" {323 let builtins_field = Field::field(root_field.clone(), "builtins").await?;355 let builtins_field = Field::field(root_field.clone(), "builtins").await?;356 if self.local_system == "detect" {324 self.local_system = nix_go_json!(builtins_field.currentSystem);357 self.local_system = nix_go_json!(builtins_field.currentSystem);325 }358 }326 let local_system = self.local_system.clone();359 let local_system = self.local_system.clone();327360328 let fleet_root = Field::field(root_field, "fleetConfigurations").await?;361 let fleet_root = Field::field(root_field, "fleetConfigurations").await?;329330 let fleet_field = nix_go!(fleet_root.default);362 let fleet_field = nix_go!(fleet_root.default);363331 let config_field = nix_go!(fleet_field.configUnchecked);364 let config_field = nix_go!(fleet_field.config);332 let config_unchecked_field = nix_go!(fleet_field.unchecked);365 let config_unchecked_field = nix_go!(fleet_field.unchecked.config);366367 let import = nix_go!(builtins_field.import);368 let overlays = nix_go!(fleet_field.overlays);369 let nixpkgs = nix_go!(fleet_field.nixpkgs | import);370371 let default_pkgs = nix_go!(nixpkgs(Obj {372 overlays,373 system: { self.local_system.clone() },374 }));333375334 let mut fleet_data_path = directory.clone();376 let mut fleet_data_path = directory.clone();335 fleet_data_path.push("fleet.nix");377 fleet_data_path.push("fleet.nix");342 data,384 data,343 local_system,385 local_system,344 nix_args,386 nix_args,345 fleet_field,346 config_field,387 config_field,347 config_unchecked_field,388 config_unchecked_field,389 default_pkgs,348 })))390 })))349 }391 }350}392}cmds/fleet/src/keys.rsdiffbeforeafterboth43 age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e))43 age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e))44 }44 }454546 pub async fn recipients(&self, hosts: &[&str]) -> Result<Vec<impl Recipient>> {46 pub async fn recipients(&self, hosts: Vec<String>) -> Result<Vec<impl Recipient>> {47 futures::stream::iter(hosts.iter())47 futures::stream::iter(hosts.iter())48 .then(|m| self.recipient(m))48 .then(|m| self.recipient(m.as_ref()))49 .try_collect::<Vec<_>>()49 .try_collect::<Vec<_>>()50 .await50 .await51 }51 }cmds/fleet/src/main.rsdiffbeforeafterboth12mod fleetdata;12mod fleetdata;131314use std::ffi::OsString;14use std::ffi::OsString;15use std::io::{stderr, stdout, Write};16use std::process::exit;15use std::time::Duration;17use std::time::Duration;161817use anyhow::{bail, Result};19use anyhow::{bail, Result};24use host::{Config, FleetOpts};26use host::{Config, FleetOpts};25use human_repr::HumanCount;27use human_repr::HumanCount;26use indicatif::{ProgressState, ProgressStyle};28use indicatif::{ProgressState, ProgressStyle};27use tracing::info;29use tracing::{error, info};28use tracing::{info_span, Instrument};30use tracing::{info_span, Instrument};29use tracing_indicatif::IndicatifLayer;31use tracing_indicatif::IndicatifLayer;30use tracing_subscriber::{prelude::*, EnvFilter};32use tracing_subscriber::{prelude::*, EnvFilter};81}83}828483#[derive(Parser)]85#[derive(Parser)]84#[clap(version = "1.0", author)]86#[clap(version, author)]85struct RootOpts {87struct RootOpts {86 #[clap(flatten)]88 #[clap(flatten)]87 fleet_opts: FleetOpts,89 fleet_opts: FleetOpts,136 ),138 ),137 );139 );138140139 let filter = EnvFilter::from_default_env();141 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));140142141 tracing_subscriber::registry()143 tracing_subscriber::registry()142 .with(144 .with(143 tracing_subscriber::fmt::layer()145 tracing_subscriber::fmt::layer()144 .without_time()146 .without_time()145 .with_target(false)147 .with_target(true)146 .with_writer(indicatif_layer.get_stderr_writer())148 .with_writer(indicatif_layer.get_stderr_writer())147 .with_filter(filter), // .withou,149 .with_filter(filter), // .withou,148 )150 )149 .with(indicatif_layer)151 .with(indicatif_layer)150 .init();152 .init();151}153}152154153#[tokio::main]155#[tokio::main]156async fn main() {157 setup_logging();158 if let Err(e) = main_real().await {159 error!("{e:#}");160 exit(1);161 }162}163154async fn main() -> Result<()> {164async fn main_real() -> Result<()> {155 setup_logging();156 let _ = better_nix_eval::TOKIO_RUNTIME.set(tokio::runtime::Handle::current());165 let _ = better_nix_eval::TOKIO_RUNTIME.set(tokio::runtime::Handle::current());157166158 let nix_args = std::env::var_os("NIX_ARGS")167 let nix_args = std::env::var_os("NIX_ARGS")flake.lockdiffbeforeafterboth38 },38 },39 "nixpkgs": {39 "nixpkgs": {40 "locked": {40 "locked": {41 "lastModified": 1703705939,41 "lastModified": 1703974965,42 "narHash": "sha256-9s2Ep3NyRDj9HUgfv2TQUwQEanRUAmeXkvKIr/o1XbY=",42 "narHash": "sha256-dvZjLuAcLnv25bqStTL2ZICC5YSs8aynF5amRM+I6UM=",43 "owner": "nixos",43 "owner": "nixos",44 "repo": "nixpkgs",44 "repo": "nixpkgs",45 "rev": "1ada32da4ba24d7310653c9ac54888bee463f455",45 "rev": "9f434bd436e2bb5615827469ed651e30c26daada",46 "type": "github"46 "type": "github"47 },47 },48 "original": {48 "original": {67 ]67 ]68 },68 },69 "locked": {69 "locked": {70 "lastModified": 1703643208,70 "lastModified": 1703902408,71 "narHash": "sha256-UL4KO8JxnD5rOycwHqBAf84lExF1/VnYMDC7b/wpPDU=",71 "narHash": "sha256-qXdWvu+tlgNjeoz8yQMRKSom6QyRROfgpmeOhwbujqw=",72 "owner": "oxalica",72 "owner": "oxalica",73 "repo": "rust-overlay",73 "repo": "rust-overlay",74 "rev": "ce117f3e0de8262be8cd324ee6357775228687cf",74 "rev": "319f57cd2c34348c55970a4bf2b35afe82088681",75 "type": "github"75 "type": "github"76 },76 },77 "original": {77 "original": {flake.nixdiffbeforeafterboth29 llvmPkgs = pkgs.buildPackages.llvmPackages_11;29 llvmPkgs = pkgs.buildPackages.llvmPackages_11;30 rust =30 rust =31 (pkgs.rustChannelOf {31 (pkgs.rustChannelOf {32 date = "2023-12-26";32 date = "2023-12-29";33 channel = "nightly";33 channel = "nightly";34 })34 })35 .default35 .defaultlib/default.nixdiffbeforeafterboth1{flake-utils}: {1{flake-utils}: {2 fleetConfiguration = {2 fleetConfiguration = {3 # TODO: Provide by fleet, instead of requesting user to provide it.4 # This is not good that user needs to provide it, as it becomes a flake data, and fleet arbitrarily rewriting it5 # always dirnets the flake. Instead, fleetConfiguration should return function, parameters of which should be filled6 # by fleet itself, which is possible since fleet moving to nix repl execution.3 data,7 data,4 nixpkgs,8 nixpkgs,9 overlays ? [],5 hosts,10 hosts,6 ...11 modules,12 globalModules ? [],7 } @ allConfig: let13 }: let8 hostNames = nixpkgs.lib.attrNames hosts;14 hostNames = nixpkgs.lib.attrNames hosts;9 config = builtins.removeAttrs allConfig ["nixpkgs" "data"];10 fleetLib = import ./fleetLib.nix {15 fleetLib = import ./fleetLib.nix {11 inherit nixpkgs hostNames;16 inherit nixpkgs hostNames;12 };17 };13 in let18 in let14 root = nixpkgs.lib.evalModules {19 root = nixpkgs.lib.evalModules {15 modules = (import ../modules/fleet/_modules.nix) ++ [config data];20 modules =21 (import ../modules/fleet/_modules.nix)22 ++ [23 data24 ({...}: {25 inherit globalModules hosts;26 })27 ]28 ++ modules;16 specialArgs = {29 specialArgs = {17 inherit nixpkgs fleetLib;30 inherit nixpkgs fleetLib;18 };31 };25 withData = {38 withData = {26 root,39 root,27 data,40 data,28 }: rec {41 }: {29 configuredHosts = root.config.hosts;42 configuredHosts = root.config.hosts;30 configuredUncheckedHosts = root.config.hosts;31 configuredSystems = configuredSystemsWithExtraModules [];32 configuredSystemsWithExtraModules = extraModules:33 nixpkgs.lib.listToAttrs (34 map35 (36 name: {37 inherit name;38 value = nixpkgs.lib.nixosSystem {39 system = configuredHosts.${name}.system;40 modules = configuredHosts.${name}.modules ++ extraModules;41 specialArgs = {42 inherit fleetLib;43 fleet = fleetLib.hostsToAttrs (host: configuredSystems.${host}.config);44 };45 };46 }47 )48 (builtins.attrNames root.config.hosts)49 );50 buildableSystems = {localSystem}: let51 buildConfigurationModule = {config, ...}: {52 # Equivalent to nixpkgs.localSystem53 # nixpkgs.system = localSystem;54 nixpkgs.buildPlatform.system = localSystem;55 };56 in57 configuredSystemsWithExtraModules [58 buildConfigurationModule59 ];60 buildSystems = {localSystem}: let61 buildConfigurationModule = {config, ...}: {62 # Equivalent to nixpkgs.localSystem63 # nixpkgs.system = localSystem;64 nixpkgs.buildPlatform.system = localSystem;65 };66 in {67 toplevel = builtins.mapAttrs (_name: value: value.config.system.build.toplevel) (configuredSystemsWithExtraModules [68 buildConfigurationModule69 ({...}: {70 buildTarget = "toplevel";71 })72 ]);73 sdImage = builtins.mapAttrs (_name: value: value.config.system.build.sdImage) (configuredSystemsWithExtraModules [74 buildConfigurationModule75 #(nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix")76 ({...}: {77 buildTarget = "sd-image";78 })79 ]);80 installationCd = builtins.mapAttrs (_name: value: value.config.system.build.isoImage) (configuredSystemsWithExtraModules [81 buildConfigurationModule82 (nixpkgs + "/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix")83 ({lib, ...}: {84 buildTarget = "installation-cd";85 # Needed for https://github.com/NixOS/nixpkgs/issues/5895986 boot.supportedFilesystems = lib.mkForce ["btrfs" "reiserfs" "vfat" "f2fs" "xfs" "ntfs" "cifs"];87 })88 ]);89 };90 configUnchecked = root.config;43 config = root.config;91 };44 };92 defaultData = withData {45 defaultData = withData {93 inherit data;46 inherit data;94 root = checkedRoot;47 root = checkedRoot;95 };48 };96 uncheckedData = withData {inherit data root;};49 uncheckedData = withData {inherit data root;};97 in rec {50 in {98 inherit (defaultData) configuredHosts configuredSystems buildSystems configUnchecked buildableSystems;51 inherit nixpkgs overlays;99 unchecked = {100 inherit (uncheckedData) configuredHosts configuredSystems buildSystems configUnchecked buildableSystems;52 inherit (defaultData) configuredHosts configuredSystems config buildableSystems;101 };102 injectData = data: let53 unchecked = {103 injectedData = withData data;104 in {105 inherit (injectedData) configuredHosts configuredSystems buildSystems configUnchecked;54 inherit (uncheckedData) configuredHosts configuredSystems config buildableSystems;106 };55 };107 };56 };108}57}modules/fleet/meta.nixdiffbeforeafterboth2 lib,3 fleetLib,4 config,5 nixpkgs,6 ...7}:2let8with lib; let3 host = with types; {9 hostModule = with types;10 {...} @ hostConfig: {4 options = {11 options = {5 modules = mkOption {12 modules = mkOption {6 type = listOf (mkOptionType {13 type = listOf (mkOptionType {20 type = str;27 type = str;21 description = "Encryption key";28 description = "Encryption key";22 };29 };30 nixosSystem = mkOption {31 type = unspecified;32 description = "Nixos configuration";33 };23 };34 };35 config.nixosSystem = nixpkgs.lib.nixosSystem {36 inherit (hostConfig.config) system modules;37 specialArgs = {38 inherit fleetLib;39 fleet = fleetLib.hostsToAttrs (host: config.hosts.${host}.nixosSystem.config);40 };41 };24 };42 };43 overlayType = mkOptionType {44 name = "nixpkgs-overlay";45 description = "nixpkgs overlay";46 check = lib.isFunction;47 merge = lib.mergeOneOption;48 };25in49in {26{27 options = with types; {50 options = with types; {28 hosts = mkOption {51 hosts = mkOption {29 type = attrsOf (submodule host);52 type = attrsOf (submodule hostModule);30 default = { };53 default = {};31 description = "Configurations of individual hosts";54 description = "Configurations of individual hosts";32 };55 };40 description = "Modules, which should be added to every system";63 description = "Modules, which should be added to every system";41 default = [ ];64 default = [];42 };65 };66 overlays = mkOption {67 default = [];68 type = listOf overlayType;69 };43 };70 };44 config = {71 config = {45 hosts = fleetLib.hostsToAttrs (host: {72 hosts = fleetLib.hostsToAttrs (host: {46 modules = config.globalModules;73 modules =74 config.globalModules75 ++ [76 ({...}: {77 nixpkgs.overlays = config.overlays;78 })79 ];47 });80 });48 globalModules = import ../../nixos/modules/module-list.nix;81 globalModules = import ../../nixos/modules/module-list.nix;49 };82 };modules/fleet/secrets.nixdiffbeforeafterboth1{ lib, fleetLib, config, ... }: with lib; with fleetLib;1{ lib, fleetLib, config, ... }: with lib; with fleetLib;2let2let3 sharedSecret = with types; {3 sharedSecret = with types; ({config, ...}: {4 options = {4 options = {5 expectedOwners = mkOption {5 expectedOwners = mkOption {6 type = listOf str;6 type = nullOr (listOf str);7 description = ''7 description = ''8 List of hosts to encrypt secret for8 List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix)9910 Secrets would be decrypted and stored to /run/secrets/$\{name} on owners10 Secrets would be decrypted and stored to /run/secrets/$\{name} on owners11 '';11 '';12 default = [ ];13 };12 };13 # TODO: Aren't those options may be just desugared to data/expectedData?14 ownerDependent = mkOption {14 regenerateOnOwnerAdded = mkOption {15 type = bool;15 type = bool;16 description = "Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted";16 description = ''17 Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.18 19 You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.20 '';17 };21 };18 generateImpure = mkOption {19 type = unspecified;20 };21 generator = mkOption {22 regenerateOnOwnerRemoved = mkOption {22 type = nullOr (submodule {23 packages = mkOption {24 type = attrsOf package;25 description = ''23 default = config.regenerateOnOwnerAdded;26 Derivation to execute for shared secret generation (key = system).27 This derivation should produce directory, with exactly two files:28 - publicData29 - encryptedSecretData3031 If null - secret value may only be created manually.32 '';33 };34 expectedData = mkOption {35 type = types.unspecified;24 type = bool;36 description = "Data expected to be used for secret generation, if doesn't match specified - secret should be regenerated";37 };38 dependencies = mkOption {39 type = listOf str;40 description = ''25 description = ''41 List of secrets, on which this secret depends.26 Should this secret be removed on owner removal, or it may be just reencrypted4227 43 During generation, generator command will be ran on host, which already has specified secrets generated.28 Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.44 '';29 Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked45 default = [];30 in some other way than by this secret ownership, I.e by firewall/etc.46 };31 '';47 data = mkOption {48 type = types.unspecified;49 description = "Data used for secret generation. Imported from fleet.nix";50 default = null;51 internal = true;52 };53 });54 default = null;55 };32 };56 expireIn = mkOption {33 generator = mkOption {57 type = nullOr int;34 type = nullOr unspecified;58 description = "Time in hours, in which this secret should be regenerated";35 description = "Derivation to evaluate for secret generation";59 default = null;36 default = null;60 };37 };61 createdAt = mkOption {38 createdAt = mkOption {62 type = nullOr str;39 type = nullOr str;40 description = "When this secret was (re)generated";63 default = null;41 default = null;64 };42 };65 expiresAt = mkOption {43 expiresAt = mkOption {66 type = nullOr str;44 type = nullOr str;45 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";67 default = null;46 default = null;68 };47 };694878 '';57 '';79 default = [ ];58 default = [ ];80 };59 };60 # TODO: Make secret generator generate arbitrary number of secret/public parts?61 # Make it generate a folder, where all files except suffixed by .enc are public, and the rest are secret?62 # How should modules refer to those files then?81 public = mkOption {63 public = mkOption {82 type = nullOr str;64 type = nullOr str;83 description = "Secret public data. Imported from fleet.nix";65 description = "Secret public data. Imported from fleet.nix";90 internal = true;72 internal = true;91 };73 };92 };74 };93 };75 });94 hostSecret = with types; {76 hostSecret = with types; {95 options = {77 options = {96 createdAt = mkOption {78 createdAt = mkOption {132 config = {114 config = {133 assertions = mapAttrsToList115 assertions = mapAttrsToList134 (name: secret: {116 (name: secret: {135 assertion = builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners;117 assertion = secret.expectedOwners == null || builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners;136 message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix";118 message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix";137 })119 })138 config.sharedSecrets;120 config.sharedSecrets;141 let123 let142 cleanupSecret = (secretName: v: {124 cleanupSecret = (secretName: v: {143 inherit (v) public secret;125 inherit (v) public secret;126 shared = true;144 });127 });145 in128 in146 [129 [nixos/secrets.nixdiffbeforeafterboth5let5let6 sysConfig = config;6 sysConfig = config;7 secretType = types.submodule ({ config, ... }: {7 secretType = types.submodule ({ config, ... }: {8 config = let secretName = config._module.args.name; in rec {8 config = let secretName = config._module.args.name; in {9 stableSecretPath = mkOptionDefault "/run/secrets/secret-stable-${secretName}";9 stableSecretPath = mkOptionDefault "/run/secrets/secret-stable-${secretName}";10 secretPath = mkOptionDefault "/run/secrets/secret-${config.secretHash}-${secretName}";10 secretPath = mkOptionDefault "/run/secrets/secret-${config.secretHash}-${secretName}";11 secretHash = mkOptionDefault (if config.secret != null then (builtins.hashString "sha1" config.secret) else throw "secret is not defined for secret ${secretName}");11 secretHash = mkOptionDefault (if config.secret != null then (builtins.hashString "sha1" config.secret) else throw "secret is not defined for secret ${secretName}");14 publicPath = mkOptionDefault "/run/secrets/public-${config.publicHash}-${secretName}";14 publicPath = mkOptionDefault "/run/secrets/public-${config.publicHash}-${secretName}";15 publicHash = mkOptionDefault (if config.public != null then (builtins.hashString "sha1" config.public) else throw "public is not defined for secret ${secretName}");15 publicHash = mkOptionDefault (if config.public != null then (builtins.hashString "sha1" config.public) else throw "public is not defined for secret ${secretName}");16 };16 };17 options = {17 options = with types; {18 shared = mkOption {19 description = "Is this secret owned by this machine, or propagated from shared secrets";20 default = false;21 };2223 generator = mkOption {24 type = nullOr unspecified;25 description = "Derivation to evaluate for secret generation";26 default = null;27 };2818 public = mkOption {29 public = mkOption {19 type = types.nullOr types.str;30 type = nullOr str;20 description = "Secret public data";31 description = "Secret public data";21 default = null;32 default = null;22 };33 };23 secret = mkOption {34 secret = mkOption {24 type = types.nullOr types.str;35 type = nullOr str;25 description = "Encrypted secret data";36 description = "Encrypted secret data";26 default = null;37 default = null;27 };38 };28 mode = mkOption {39 mode = mkOption {29 type = types.str;40 type = str;30 description = "Secret mode";41 description = "Secret mode";31 default = "0440";42 default = "0440";32 };43 };33 owner = mkOption {44 owner = mkOption {34 type = types.str;45 type = str;35 description = "Owner of the secret";46 description = "Owner of the secret";36 default = "root";47 default = "root";37 };48 };38 group = mkOption {49 group = mkOption {39 type = types.str;50 type = str;40 description = "Group of the secret";51 description = "Group of the secret";41 default = sysConfig.users.users.${config.owner}.group;52 default = sysConfig.users.users.${config.owner}.group;42 };53 };435444 secretHash = mkOption {55 secretHash = mkOption {45 type = types.str;56 type = str;46 description = "Hash of .secret field";57 description = "Hash of .secret field";47 };58 };48 publicHash = mkOption {59 publicHash = mkOption {49 type = types.str;60 type = str;50 description = "Hash of .public field";61 description = "Hash of .public field";51 };62 };526353 stableSecretPath = mkOption {64 stableSecretPath = mkOption {54 type = types.str;65 type = str;55 description = ''66 description = ''56 Use this, if target process supports re-reading of secret from disk,67 Use this, if target process supports re-reading of secret from disk,57 and doesn't needs to be restarted when secret is updated in file68 and doesn't needs to be restarted when secret is updated in file58 '';69 '';59 };70 };60 secretPath = mkOption {71 secretPath = mkOption {61 type = types.str;72 type = str;62 description = "Path to decrypted secret, suffixed with contents hash";73 description = "Path to decrypted secret, suffixed with contents hash";63 };74 };647565 stablePublicPath = mkOption {76 stablePublicPath = mkOption {66 type = types.str;77 type = str;67 description = ''78 description = ''68 Use this, if target process supports re-reading of secret from disk,79 Use this, if target process supports re-reading of secret from disk,69 and doesn't needs to be restarted when secret is updated in file80 and doesn't needs to be restarted when secret is updated in file70 '';81 '';71 };82 };72 publicPath = mkOption {83 publicPath = mkOption {73 type = types.str;84 type = str;74 description = "Path to the public part of secret";85 description = "Path to the public part of secret";75 };86 };76 };87 };