diff --git a/.editorconfig b/.editorconfig index 5fad59f..cbacbab 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,10 +3,10 @@ root = true [*] indent_style = tab +[*.rs] +indent_style = space +indent_size = 4 + [*.yaml] indent_style = space indent_size = 2 - -[{*.py,tools/render}] -indent_style = space -indent_size = 4 diff --git a/.gitattributes b/.gitattributes index 9fc5447..b7d3991 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,2 @@ -_secrets.yaml filter=git-crypt diff=git-crypt +*.key filter=git-crypt diff=git-crypt secrets.yaml filter=git-crypt diff=git-crypt -*.agekey filter=git-crypt diff=git-crypt diff --git a/.python-version b/.python-version deleted file mode 100644 index 24ee5b1..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.13 diff --git a/.secretsignore b/.secretsignore index a12d653..2f1b74c 100644 --- a/.secretsignore +++ b/.secretsignore @@ -1,3 +1,2 @@ -_secrets.yaml secrets.yaml -*.agekey +*.key diff --git a/config.yaml b/config.yaml deleted file mode 100644 index a2f7db7..0000000 --- a/config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -server: - tftpIp: 192.168.1.1 - httpUrl: http://192.168.1.1:8000 - -tailscale: - loginServer: https://headscale.huizinga.dev diff --git a/crete/.cargo/config.toml b/crete/.cargo/config.toml new file mode 100644 index 0000000..d29d6c3 --- /dev/null +++ b/crete/.cargo/config.toml @@ -0,0 +1,3 @@ +[target.x86_64-unknown-linux-gnu] +linker = "clang" +rustflags = ["-C", "link-arg=-fuse-ld=mold"] diff --git a/crete/.gitignore b/crete/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/crete/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/crete/Cargo.lock b/crete/Cargo.lock new file mode 100644 index 0000000..ab43359 --- /dev/null +++ b/crete/Cargo.lock @@ -0,0 +1,2659 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "aws-lc-rs" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crete" +version = "0.1.0" +dependencies = [ + "clap", + "gix-discover", + "minijinja", + "optional_struct", + "regress", + "reqwest", + "schemars", + "semver", + "serde", + "serde_context", + "serde_json", + "serde_yaml", + "walkdir", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "gix-actor" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e5e5b518339d5e6718af108fd064d4e9ba33caf728cf487352873d76411df35" +dependencies = [ + "bstr", + "gix-date", + "gix-error", + "winnow", +] + +[[package]] +name = "gix-date" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2f2155782090fd947c2f7904166b9f3c3da0d91358adb011f753ea3a55c0ff" +dependencies = [ + "bstr", + "gix-error", + "itoa", + "jiff", + "smallvec", +] + +[[package]] +name = "gix-discover" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "810764b92e8cb95e4d91b7adfc5a14666434fd32ace02900dfb66aae71f845df" +dependencies = [ + "bstr", + "dunce", + "gix-fs", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dfe8025209bf2a72d97a6f2dff105b93e5ebcf131ffa3d3f1728ce4ac3767b" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-features" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a83a5fe8927de3bb02b0cfb87165dbfb49f04d4c297767443f2e1011ecc15bdd" +dependencies = [ + "gix-path", + "gix-trace", + "gix-utils", + "libc", + "prodash", + "walkdir", +] + +[[package]] +name = "gix-fs" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de4bd0d8e6c6ef03485205f8eecc0359042a866d26dba569075db1ebcc005970" +dependencies = [ + "bstr", + "fastrand", + "gix-features", + "gix-path", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-hash" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ced05d2d7b13bff08b2f7eb4e47cfeaf00b974c2ddce08377c4fe1f706b3eb" +dependencies = [ + "faster-hex", + "gix-features", + "sha1-checked", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-hashtable" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f1eecdd006390cbed81f105417dbf82a6fe40842022006550f2e32484101da" +dependencies = [ + "gix-hash", + "hashbrown", + "parking_lot", +] + +[[package]] +name = "gix-lock" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbe09cf05ba7c679bba189acc29eeea137f643e7fff1b5dff879dfd45248be31" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-object" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "013eae8e072c6155191ac266950dfbc8d162408642571b32e2c6b3e4b03740fb" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-path", + "gix-utils", + "gix-validate", + "itoa", + "smallvec", + "thiserror 2.0.18", + "winnow", +] + +[[package]] +name = "gix-path" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7163b1633d35846a52ef8093f390cec240e2d55da99b60151883035e5169cd85" +dependencies = [ + "bstr", + "gix-trace", + "gix-validate", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-ref" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc7b230945f02d706a49bcf823b671785ecd9e88e713b8bd2ca5db104c97add" +dependencies = [ + "gix-actor", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "thiserror 2.0.18", + "winnow", +] + +[[package]] +name = "gix-sec" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e014df75f3d7f5c98b18b45c202422da6236a1c0c0a50997c3f41e601f3ad511" +dependencies = [ + "bitflags", + "gix-path", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "gix-tempfile" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d9ab2c89fe4bfd4f1d8700aa4516534c170d8a21ae2c554167374607c2eaf16" +dependencies = [ + "gix-fs", + "libc", + "parking_lot", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f69a13643b8437d4ca6845e08143e847a36ca82903eed13303475d0ae8b162e0" + +[[package]] +name = "gix-utils" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "befcdbdfb1238d2854591f760a48711bed85e72d80a10e8f2f93f656746ef7c5" +dependencies = [ + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec1eff98d91941f47766367cba1be746bab662bad761d9891ae6f7882f7840b" +dependencies = [ + "bstr", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jiff" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minijinja" +version = "2.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea5ea1e90055f200af6b8e52a4a34e05e77e7fee953a9fb40c631efdc43cab1" +dependencies = [ + "memo-map", + "self_cell", + "serde", + "serde_json", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "optional_struct" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14199f59efce6ed2c5854f0abc725c32eedfbd02c6ef82c9733c726f3fc6dc91" +dependencies = [ + "optional_struct_macro", + "serde", +] + +[[package]] +name = "optional_struct_macro" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5eba042d9efe5e108e0df9ce2f85c540fc4f94f41c6821cbcf70ed47c1221da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prodash" +version = "31.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" + +[[package]] +name = "regress" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07948de9abc2e83adbeb7543c061a5ddaf7d944afcafbdd6e6b39aeacd40504b" +dependencies = [ + "hashbrown", + "memchr", +] + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "semver", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_context" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf91b7f6d20809cb97ed1aa944243e8e2e18991298a129aba35441f6bcdb404" +dependencies = [ + "rustc-hash", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1-checked" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" +dependencies = [ + "digest", + "sha1", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/crete/Cargo.toml b/crete/Cargo.toml new file mode 100644 index 0000000..a7029ba --- /dev/null +++ b/crete/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "crete" +version = "0.1.0" +edition = "2024" +default-run = "crete" + +[dependencies] +clap = { version = "4.5.60", features = ["derive"] } +gix-discover = "0.48.0" +minijinja = { version = "2.17.1", features = ["json", "loader"] } +optional_struct = "0.5.2" +regress = "0.11.0" +reqwest = { version = "0.13.2", features = ["blocking"] } +schemars = { version = "1.2.1", features = ["semver1"] } +semver = { version = "1.0.27", features = ["serde"] } +serde = { version = "1.0.228", features = ["derive"] } +serde_context = "0.1.0" +serde_json = "1.0.149" +serde_yaml = "0.9.34" +walkdir = "2.5.0" diff --git a/crete/rust-toolchain.toml b/crete/rust-toolchain.toml new file mode 100644 index 0000000..063315e --- /dev/null +++ b/crete/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2026-03-12" +components = ["rustfmt", "clippy", "rust-analyzer"] +profile = "minimal" diff --git a/crete/src/bin/schemas.rs b/crete/src/bin/schemas.rs new file mode 100644 index 0000000..956b716 --- /dev/null +++ b/crete/src/bin/schemas.rs @@ -0,0 +1,23 @@ +use std::{fs::File, io::Write}; + +use crete::{cluster::Cluster, node::OptionalNode}; +use repo_path_lib::repo_dir; +use schemars::{JsonSchema, schema_for}; + +fn write(name: &str) +where + T: JsonSchema, +{ + let mut path = repo_dir().join("schemas").join(name); + path.add_extension("json"); + let mut file = File::create(path).unwrap(); + + let schema = serde_json::to_string_pretty(&schema_for!(T)).unwrap(); + file.write_all(schema.as_bytes()).unwrap(); +} + +// TODO: Create directory if it does not exist +fn main() { + write::("cluster"); + write::("node"); +} diff --git a/crete/src/cluster.rs b/crete/src/cluster.rs new file mode 100644 index 0000000..1c03b78 --- /dev/null +++ b/crete/src/cluster.rs @@ -0,0 +1,71 @@ +use std::net::Ipv4Addr; + +use minijinja::Environment; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{ + get_talos_config_path, + node::{Node, OptionalNode}, + patch::Patches, +}; + +#[derive(Debug, Deserialize, JsonSchema, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Base { + #[serde(default)] + pub(crate) kernel_args: Vec, + #[serde(default)] + pub(crate) patches: Patches, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Version { + kubernetes: semver::Version, + talos: semver::Version, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub enum ClusterEnv { + Production, + Staging, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Cluster { + #[serde(skip_deserializing)] + name: String, + version: Version, + nodes: Vec, + cluster_env: ClusterEnv, + control_plane_ip: Ipv4Addr, + secrets_file: String, + #[serde(default, skip_serializing)] + pub(crate) default: OptionalNode, + #[serde(skip_serializing)] + pub(crate) base: Base, + // pub secrets_file: PathBuf, +} + +impl Cluster { + pub fn get(cluster_name: &str) -> Self { + let mut path = get_talos_config_path().join("clusters").join(cluster_name); + path.add_extension("yaml"); + let content = std::fs::read_to_string(path).unwrap(); + + let mut cluster: Self = serde_yaml::from_str(&content).unwrap(); + cluster.name = cluster_name.to_string(); + + cluster + } + + pub fn nodes(&self, env: &Environment) -> Vec { + self.nodes + .iter() + .map(|node_name| Node::get(node_name, env, self)) + .collect() + } +} diff --git a/crete/src/environment.rs b/crete/src/environment.rs new file mode 100644 index 0000000..53d0aba --- /dev/null +++ b/crete/src/environment.rs @@ -0,0 +1,96 @@ +use std::{ + net::Ipv4Addr, + ops::Deref, + path::{Path, PathBuf}, + str::FromStr, +}; + +use minijinja::{AutoEscape, Environment, path_loader}; +use walkdir::WalkDir; + +use crate::{get_repo_path, get_talos_config_path}; + +/// Transparent wrapper around minijinja::Environment that loads templates from a path and +/// configures better defaults. It also implements IntoIter, making it possible to iterate over all +/// the templates. +pub struct PathEnvironment<'a> { + env: Environment<'a>, + path: PathBuf, +} + +impl<'a> PathEnvironment<'a> { + pub fn new(path: &Path) -> Self { + let mut env = Environment::new(); + + // Configure jinja + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); + env.set_undefined_behavior(minijinja::UndefinedBehavior::Strict); + env.set_auto_escape_callback(|_| AutoEscape::None); + + // Add filters + env.add_filter("to_prefix", |netmask: String| { + let netmask = Ipv4Addr::from_str(&netmask).map_err(|err| { + minijinja::Error::new(minijinja::ErrorKind::InvalidOperation, err.to_string()) + })?; + let mask = netmask.to_bits(); + let prefix = mask.leading_ones(); + + if mask.checked_shl(prefix).unwrap_or(0) == 0 { + Ok(prefix as u8) + } else { + Err(minijinja::Error::new( + minijinja::ErrorKind::InvalidOperation, + "invalid IP prefix length", + )) + } + }); + + // Add path loader + env.set_loader(path_loader(&path)); + + Self { + path: path.into(), + env, + } + } + + pub fn new_patches() -> Self { + Self::new(&get_talos_config_path().join("patches")) + } + + pub fn new_templates() -> Self { + Self::new(&get_repo_path().join("templates")) + } +} + +// Make PathEnvironment act like a normal Environment transparently +impl<'a> Deref for PathEnvironment<'a> { + type Target = Environment<'a>; + + fn deref(&self) -> &Self::Target { + &self.env + } +} + +// Iterate over all the files in the path +impl<'a, 'b> IntoIterator for &'b PathEnvironment<'a> { + type Item = String; + + type IntoIter = impl Iterator; + + fn into_iter(self) -> Self::IntoIter { + // Find all templates in path + WalkDir::new(&self.path) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.metadata().unwrap().is_file()) + .map(|e| { + e.path() + .strip_prefix(&self.path) + .expect("All paths should have prefix") + .to_string_lossy() + .to_string() + }) + } +} diff --git a/crete/src/lib.rs b/crete/src/lib.rs new file mode 100644 index 0000000..6cfca4c --- /dev/null +++ b/crete/src/lib.rs @@ -0,0 +1,28 @@ +#![feature(impl_trait_in_assoc_type)] +use std::{ + path::{Path, PathBuf}, + sync::OnceLock, +}; + +pub mod cluster; +pub mod environment; +pub mod node; +pub mod patch; +pub mod schematic; +pub mod secret; + +pub(crate) static REPO_PATH: OnceLock = OnceLock::new(); + +pub fn set_repo_path(path: impl Into) { + REPO_PATH + .set(path.into()) + .expect("Repo path already initialized"); +} + +fn get_repo_path() -> &'static Path { + REPO_PATH.get().expect("Repo path not initialized") +} + +fn get_talos_config_path() -> PathBuf { + get_repo_path().join("talos") +} diff --git a/crete/src/main.rs b/crete/src/main.rs new file mode 100644 index 0000000..9e6741f --- /dev/null +++ b/crete/src/main.rs @@ -0,0 +1,86 @@ +use std::path::PathBuf; + +use clap::{Args, Parser, Subcommand}; +use crete::{cluster::Cluster, environment::PathEnvironment, set_repo_path}; +use gix_discover::repository::Path; +use minijinja::context; + +#[derive(Debug, Parser)] +#[command(version, about)] +#[command(propagate_version = true)] +struct Cli { + #[command(flatten)] + global_opts: GlobalOpts, + + #[command(subcommand)] + command: Commands, +} + +#[derive(Debug, Args)] +struct GlobalOpts { + #[arg(global = true, short, long, action = clap::ArgAction::Count)] + /// Use verbose output + verbose: u8, + + #[arg(global = true, short, long, value_parser = path_is_repo, default_value_os_t = PathBuf::from("."))] + /// Path to the repo containing the config files + repo: PathBuf, +} + +fn path_is_repo(path: &str) -> Result { + let path = PathBuf::from(path); + let (repo, _trust) = gix_discover::upwards(&path).map_err(|err| err.to_string())?; + + let work_dir = match repo { + Path::LinkedWorkTree { work_dir, .. } => work_dir, + Path::WorkTree(work_dir) => work_dir, + Path::Repository(git_dir) => { + return Err(format!( + "Repo '{}' has no working directory", + git_dir.to_string_lossy() + )); + } + }; + + Ok(work_dir) +} + +#[derive(Debug, Subcommand)] +enum Commands { + /// Generate talos config file + Generate, +} + +fn generate(opts: &GlobalOpts) { + set_repo_path(&opts.repo); + + let cluster = Cluster::get("testing"); + + let patch_env = PathEnvironment::new_patches(); + let nodes = cluster.nodes(&patch_env); + + let template_env = PathEnvironment::new_templates(); + + for template_name in &template_env { + let template = template_env.get_template(&template_name).unwrap(); + + let result = template + .render(context! { + cluster, + nodes, + root => opts.repo + }) + .unwrap(); + + println!("###### {template_name} ######"); + println!("{result}"); + } +} + +fn main() { + let cli = Cli::parse(); + + match cli.command { + Commands::Generate => generate(&cli.global_opts), + } +} diff --git a/crete/src/node.rs b/crete/src/node.rs new file mode 100644 index 0000000..18d3868 --- /dev/null +++ b/crete/src/node.rs @@ -0,0 +1,140 @@ +use std::net::Ipv4Addr; + +use minijinja::Environment; +use optional_struct::{Applicable, optional_struct}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{ + cluster::Cluster, + get_talos_config_path, + patch::{OptionalPatches, Patches}, + schematic::Schematic, + secret::Secret, +}; + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +enum NodeType { + Worker, + #[serde(rename(serialize = "controlplane"))] + ControlPlane, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +enum NodeArch { + Amd64, +} + +#[optional_struct] +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Tailscale { + auth_key: Secret, + advertise_routes: bool, + #[serde(default)] + server: Option, +} + +#[optional_struct] +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Network { + interface: String, + ip: Ipv4Addr, + netmask: Ipv4Addr, + gateway: Ipv4Addr, + dns: [Ipv4Addr; 2], + #[optional_rename(OptionalTailscale)] + #[optional_wrap] + tailscale: Tailscale, +} + +#[optional_struct] +#[derive(Debug, Serialize, Deserialize, JsonSchema, Default)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Install { + auto: bool, + disk: String, + serial: Option, +} + +#[optional_struct] +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Node { + #[serde(skip_deserializing)] + hostname: String, + arch: NodeArch, + schematic: Schematic, + r#type: NodeType, + #[optional_rename(OptionalNetwork)] + #[optional_wrap] + network: Network, + ntp: String, + #[optional_rename(OptionalInstall)] + #[optional_wrap] + install: Install, + kernel_args: Vec, + #[optional_rename(OptionalPatches)] + #[optional_wrap] + pub(crate) patches: Patches, + sops: Secret, +} + +impl Node { + pub fn get(node_name: &str, env: &Environment, cluster: &Cluster) -> Self { + let mut path = get_talos_config_path().join("nodes").join(node_name); + let named = OptionalNode { + hostname: Some( + path.file_name() + .expect("Path should be valid") + .to_string_lossy() + .to_string(), + ), + ..OptionalNode::default() + }; + + path.add_extension("yaml"); + let content = std::fs::read_to_string(path).unwrap(); + + let node: OptionalNode = serde_yaml::from_str(&content).unwrap(); + + // We want all vectors to be empty vectors by default + // Sadly we have to this manually + // TODO: Find a better way of doing this + let default = OptionalNode { + patches: Some(OptionalPatches { + all: Some(vec![]), + control_plane: Some(vec![]), + }), + kernel_args: vec![].into(), + ..Default::default() + }; + + // Combine all the optional node parts into complete struct + let mut node: Node = default + // Apply cluster default settings + .apply(cluster.default.clone()) + // Apply hostname based on filename + .apply(named) + // Override node specific settings + .apply(node) + .try_into() + .unwrap(); + + // Prepend the cluster base values + let mut kernel_args = cluster.base.kernel_args.clone(); + kernel_args.extend(node.kernel_args); + node.kernel_args = kernel_args; + + let patches = cluster.base.patches.clone().extend(node.patches); + node.patches = patches; + + // Render patches + node.patches = node.patches.clone().render(env, cluster, &node); + + node + } +} diff --git a/crete/src/patch.rs b/crete/src/patch.rs new file mode 100644 index 0000000..c531bee --- /dev/null +++ b/crete/src/patch.rs @@ -0,0 +1,64 @@ +use minijinja::{Environment, context}; +use optional_struct::optional_struct; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{cluster::Cluster, node::Node}; + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)] +#[serde(untagged)] +pub(crate) enum Patch { + Name(String), + #[serde(skip_deserializing)] + #[schemars(with = "serde_json::Value")] + Resolved(serde_yaml::Value), +} + +#[optional_struct] +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq, Default)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Patches { + pub(crate) all: Vec, + pub(crate) control_plane: Vec, +} + +fn render(patches: Vec, env: &Environment, cluster: &Cluster, node: &Node) -> Vec { + patches + .into_iter() + .map(|patch| { + if let Patch::Name(name) = patch { + let content = env + .get_template(&name) + .unwrap() + .render(context! { + node, + cluster + }) + .unwrap(); + + Patch::Resolved(serde_yaml::from_str(&content).unwrap()) + } else { + patch + } + }) + .collect() +} + +impl Patches { + pub(crate) fn extend(mut self, other: Self) -> Self { + self.all.extend(other.all); + self.control_plane.extend(other.control_plane); + + Self { + all: self.all, + control_plane: self.control_plane, + } + } + + pub(crate) fn render(self, env: &Environment, cluster: &Cluster, node: &Node) -> Self { + Self { + all: render(self.all.clone(), env, cluster, node), + control_plane: render(self.control_plane.clone(), env, cluster, node), + } + } +} diff --git a/crete/src/schematic.rs b/crete/src/schematic.rs new file mode 100644 index 0000000..2f557a9 --- /dev/null +++ b/crete/src/schematic.rs @@ -0,0 +1,33 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Deserializer, Serialize}; + +use crate::get_talos_config_path; + +fn deserialize_schematic<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let name: String = Deserialize::deserialize(deserializer)?; + let path = get_talos_config_path().join("schematics").join(name); + let content = std::fs::read_to_string(path).unwrap().trim().to_owned(); + + let client = reqwest::blocking::Client::new(); + let res = client + .post("https://factory.talos.dev/schematics") + .body(content) + .send() + .map_err(serde::de::Error::custom)?; + + #[derive(Debug, Deserialize)] + struct Response { + id: String, + } + + let response: Response = serde_json::from_str(&res.text().map_err(serde::de::Error::custom)?) + .map_err(serde::de::Error::custom)?; + + Ok(response.id) +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)] +pub(crate) struct Schematic(#[serde(deserialize_with = "deserialize_schematic")] String); diff --git a/crete/src/secret.rs b/crete/src/secret.rs new file mode 100644 index 0000000..cd78c39 --- /dev/null +++ b/crete/src/secret.rs @@ -0,0 +1,34 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Deserializer, Serialize}; + +use crate::get_talos_config_path; + +#[derive(Debug, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase", untagged)] +enum SecretHelper { + String(String), + File { file: String }, +} + +pub(crate) fn deserialize_secret<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let secret: SecretHelper = Deserialize::deserialize(deserializer)?; + let value = match secret { + SecretHelper::String(value) => value, + SecretHelper::File { file } => { + let path = get_talos_config_path().join("secrets").join(file); + std::fs::read_to_string(path).unwrap().trim().to_owned() + } + }; + + Ok(value) +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)] +pub(crate) struct Secret( + #[serde(deserialize_with = "deserialize_secret")] + #[schemars(with = "SecretHelper")] + String, +); diff --git a/nodes/_defaults.yaml b/nodes/_defaults.yaml deleted file mode 100644 index 91302db..0000000 --- a/nodes/_defaults.yaml +++ /dev/null @@ -1,43 +0,0 @@ -schematicId: !schematic default -arch: amd64 -talosVersion: 1.11.3 -kubernetesVersion: 1.34.1 -kernelArgs: - - talos.platform=metal - - console=tty0 - - init_on_alloc=1 - - init_on_free=1 - - slab_nomerge - - pti=on - - consoleblank=0 - - nvme_core.io_timeout=4294967295 - - printk.devkmsg=on - - selinux=1 - - lockdown=confidentiality -extraKernelArgs: [] -dns: - - 1.1.1.1 - - 8.8.8.8 -ntp: nl.pool.ntp.org -install: true -autoInstall: false -advertiseRoutes: true -patches: - - !patch hostname - - !patch install-disk - - !patch network - - !patch vip - - !patch tailscale - - !patch cilium - - !patch spegel - - !patch longhorn - - !patch longhorn-user-volume - - !patch local-path-provisioner-volume - - !patch limit-ephemeral - - !patch metrics -patchesControlPlane: - - !patch allow-control-plane-workloads - - !patch sops - - !patch cluster-variables - - !patch metrics-cluster - - !patch gateway-api diff --git a/nodes/testing/_defaults.yaml b/nodes/testing/_defaults.yaml deleted file mode 100644 index fcd8eb1..0000000 --- a/nodes/testing/_defaults.yaml +++ /dev/null @@ -1,10 +0,0 @@ -netmask: 255.255.255.0 -gateway: 192.168.1.1 -installDisk: /dev/vda -autoInstall: true -cluster: - name: testing - production: false - controlPlaneIp: 192.168.1.100 - secretsFile: !realpath _secrets.yaml - sopsKeyFile: !realpath _age.agekey diff --git a/nodes/testing/talos-vm.yaml b/nodes/testing/talos-vm.yaml deleted file mode 100644 index f9a922d..0000000 --- a/nodes/testing/talos-vm.yaml +++ /dev/null @@ -1,4 +0,0 @@ -serial: talos-vm -interface: enp1s0 -ip: 192.168.1.2 -type: "controlplane" diff --git a/nodes/titan/_age.agekey b/nodes/titan/_age.agekey deleted file mode 100644 index ee154d0..0000000 Binary files a/nodes/titan/_age.agekey and /dev/null differ diff --git a/nodes/titan/_defaults.yaml b/nodes/titan/_defaults.yaml deleted file mode 100644 index 7e30017..0000000 --- a/nodes/titan/_defaults.yaml +++ /dev/null @@ -1,9 +0,0 @@ -netmask: 255.255.252.0 -gateway: 10.0.0.1 -installDisk: /dev/sda -cluster: - name: titan - production: true - controlPlaneIp: 10.0.2.1 - secretsFile: !realpath _secrets.yaml - sopsKeyFile: !realpath _age.agekey diff --git a/nodes/titan/_secrets.yaml b/nodes/titan/_secrets.yaml deleted file mode 100644 index 7690821..0000000 Binary files a/nodes/titan/_secrets.yaml and /dev/null differ diff --git a/nodes/titan/helios.yaml b/nodes/titan/helios.yaml deleted file mode 100644 index eedd4f9..0000000 --- a/nodes/titan/helios.yaml +++ /dev/null @@ -1,4 +0,0 @@ -serial: 5CZ7NX2 -interface: enp2s0 -ip: 10.0.0.202 -type: "controlplane" diff --git a/nodes/titan/hyperion.yaml b/nodes/titan/hyperion.yaml deleted file mode 100644 index 2a869c1..0000000 --- a/nodes/titan/hyperion.yaml +++ /dev/null @@ -1,4 +0,0 @@ -serial: F3PKRH2 -interface: enp3s0 -ip: 10.0.0.201 -type: "controlplane" diff --git a/nodes/titan/selene.yaml b/nodes/titan/selene.yaml deleted file mode 100644 index 194b798..0000000 --- a/nodes/titan/selene.yaml +++ /dev/null @@ -1,4 +0,0 @@ -serial: J33CHY2 -interface: enp2s0 -ip: 10.0.0.203 -type: "controlplane" diff --git a/patches/tailscale.yaml b/patches/tailscale.yaml deleted file mode 100644 index 698bdaf..0000000 --- a/patches/tailscale.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json -apiVersion: v1alpha1 -kind: ExtensionServiceConfig -name: tailscale -environment: - - TS_AUTHKEY={{ config.tailscale.authKey }} - - TS_EXTRA_ARGS=--login-server {{ config.tailscale.loginServer }} --advertise-tags=tag:cluster-{{ node.cluster.name }} - - TS_ROUTES={% if node.advertiseRoutes -%} {{ helper.tailscale_subnet(node.gateway, node.netmask) }} {%- endif %} diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 40d6ec1..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,17 +0,0 @@ -[project] -name = "bootstrap" -version = "0.1.0" -description = "Add your description here" -# readme = "README.md" -requires-python = ">=3.13" -dependencies = [ - "gitpython==3.1.45", - "jinja2==3.1.6", - "mergedeep==1.3.4", - "netaddr==1.3.0", - "pydantic>=2.12.5", - "pydantic-extra-types>=2.11.0", - "pyyaml==6.0.3", - "requests==2.32.5", - "semver>=3.0.4", -] diff --git a/schemas/cluster.json b/schemas/cluster.json new file mode 100644 index 0000000..a3427fe --- /dev/null +++ b/schemas/cluster.json @@ -0,0 +1,381 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Cluster", + "type": "object", + "properties": { + "base": { + "$ref": "#/$defs/Base", + "writeOnly": true + }, + "clusterEnv": { + "$ref": "#/$defs/ClusterEnv" + }, + "controlPlaneIp": { + "type": "string", + "format": "ipv4" + }, + "default": { + "$ref": "#/$defs/OptionalNode", + "writeOnly": true + }, + "nodes": { + "type": "array", + "items": { + "type": "string" + } + }, + "secretsFile": { + "type": "string" + }, + "version": { + "$ref": "#/$defs/Version" + } + }, + "additionalProperties": false, + "required": [ + "version", + "nodes", + "clusterEnv", + "controlPlaneIp", + "secretsFile", + "base" + ], + "$defs": { + "Base": { + "type": "object", + "properties": { + "kernelArgs": { + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "patches": { + "$ref": "#/$defs/Patches", + "default": { + "all": [], + "controlPlane": [] + } + } + }, + "additionalProperties": false + }, + "ClusterEnv": { + "type": "string", + "enum": [ + "production", + "staging" + ] + }, + "NodeArch": { + "type": "string", + "enum": [ + "amd64" + ] + }, + "NodeType": { + "type": "string", + "enum": [ + "worker", + "controlPlane" + ] + }, + "OptionalInstall": { + "type": "object", + "properties": { + "auto": { + "type": [ + "boolean", + "null" + ] + }, + "disk": { + "type": [ + "string", + "null" + ] + }, + "serial": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "OptionalNetwork": { + "type": "object", + "properties": { + "dns": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "format": "ipv4" + }, + "maxItems": 2, + "minItems": 2 + }, + "gateway": { + "type": [ + "string", + "null" + ], + "format": "ipv4" + }, + "interface": { + "type": [ + "string", + "null" + ] + }, + "ip": { + "type": [ + "string", + "null" + ], + "format": "ipv4" + }, + "netmask": { + "type": [ + "string", + "null" + ], + "format": "ipv4" + }, + "tailscale": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalTailscale" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "OptionalNode": { + "type": "object", + "properties": { + "arch": { + "anyOf": [ + { + "$ref": "#/$defs/NodeArch" + }, + { + "type": "null" + } + ] + }, + "install": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalInstall" + }, + { + "type": "null" + } + ] + }, + "kernelArgs": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "network": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalNetwork" + }, + { + "type": "null" + } + ] + }, + "ntp": { + "type": [ + "string", + "null" + ] + }, + "patches": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalPatches" + }, + { + "type": "null" + } + ] + }, + "schematic": { + "anyOf": [ + { + "$ref": "#/$defs/Schematic" + }, + { + "type": "null" + } + ] + }, + "sops": { + "anyOf": [ + { + "$ref": "#/$defs/Secret" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "$ref": "#/$defs/NodeType" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "OptionalPatches": { + "type": "object", + "properties": { + "all": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/$defs/Patch" + } + }, + "controlPlane": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/$defs/Patch" + } + } + }, + "additionalProperties": false + }, + "OptionalTailscale": { + "type": "object", + "properties": { + "advertiseRoutes": { + "type": [ + "boolean", + "null" + ] + }, + "authKey": { + "anyOf": [ + { + "$ref": "#/$defs/Secret" + }, + { + "type": "null" + } + ] + }, + "server": { + "type": [ + "string", + "null" + ], + "default": null + } + }, + "additionalProperties": false + }, + "Patch": { + "anyOf": [ + { + "type": "string" + } + ] + }, + "Patches": { + "type": "object", + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/$defs/Patch" + } + }, + "controlPlane": { + "type": "array", + "items": { + "$ref": "#/$defs/Patch" + } + } + }, + "additionalProperties": false, + "required": [ + "all", + "controlPlane" + ] + }, + "Schematic": { + "type": "string" + }, + "Secret": { + "$ref": "#/$defs/SecretHelper" + }, + "SecretHelper": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "file": { + "type": "string" + } + }, + "required": [ + "file" + ] + } + ] + }, + "SemVer": { + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + }, + "Version": { + "type": "object", + "properties": { + "kubernetes": { + "$ref": "#/$defs/SemVer" + }, + "talos": { + "$ref": "#/$defs/SemVer" + } + }, + "additionalProperties": false, + "required": [ + "kubernetes", + "talos" + ] + } + } +} diff --git a/schemas/node.json b/schemas/node.json new file mode 100644 index 0000000..a69fd22 --- /dev/null +++ b/schemas/node.json @@ -0,0 +1,271 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "OptionalNode", + "type": "object", + "properties": { + "arch": { + "anyOf": [ + { + "$ref": "#/$defs/NodeArch" + }, + { + "type": "null" + } + ] + }, + "install": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalInstall" + }, + { + "type": "null" + } + ] + }, + "kernelArgs": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "network": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalNetwork" + }, + { + "type": "null" + } + ] + }, + "ntp": { + "type": [ + "string", + "null" + ] + }, + "patches": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalPatches" + }, + { + "type": "null" + } + ] + }, + "schematic": { + "anyOf": [ + { + "$ref": "#/$defs/Schematic" + }, + { + "type": "null" + } + ] + }, + "sops": { + "anyOf": [ + { + "$ref": "#/$defs/Secret" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "$ref": "#/$defs/NodeType" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "$defs": { + "NodeArch": { + "type": "string", + "enum": [ + "amd64" + ] + }, + "NodeType": { + "type": "string", + "enum": [ + "worker", + "controlPlane" + ] + }, + "OptionalInstall": { + "type": "object", + "properties": { + "auto": { + "type": [ + "boolean", + "null" + ] + }, + "disk": { + "type": [ + "string", + "null" + ] + }, + "serial": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "OptionalNetwork": { + "type": "object", + "properties": { + "dns": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "format": "ipv4" + }, + "maxItems": 2, + "minItems": 2 + }, + "gateway": { + "type": [ + "string", + "null" + ], + "format": "ipv4" + }, + "interface": { + "type": [ + "string", + "null" + ] + }, + "ip": { + "type": [ + "string", + "null" + ], + "format": "ipv4" + }, + "netmask": { + "type": [ + "string", + "null" + ], + "format": "ipv4" + }, + "tailscale": { + "anyOf": [ + { + "$ref": "#/$defs/OptionalTailscale" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "OptionalPatches": { + "type": "object", + "properties": { + "all": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/$defs/Patch" + } + }, + "controlPlane": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/$defs/Patch" + } + } + }, + "additionalProperties": false + }, + "OptionalTailscale": { + "type": "object", + "properties": { + "advertiseRoutes": { + "type": [ + "boolean", + "null" + ] + }, + "authKey": { + "anyOf": [ + { + "$ref": "#/$defs/Secret" + }, + { + "type": "null" + } + ] + }, + "server": { + "type": [ + "string", + "null" + ], + "default": null + } + }, + "additionalProperties": false + }, + "Patch": { + "anyOf": [ + { + "type": "string" + } + ] + }, + "Schematic": { + "type": "string" + }, + "Secret": { + "$ref": "#/$defs/SecretHelper" + }, + "SecretHelper": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "file": { + "type": "string" + } + }, + "required": [ + "file" + ] + } + ] + } + } +} diff --git a/schematics/default.yaml b/schematics/default.yaml deleted file mode 100644 index c12043f..0000000 --- a/schematics/default.yaml +++ /dev/null @@ -1,8 +0,0 @@ -customization: - systemExtensions: - officialExtensions: - - siderolabs/iscsi-tools - - siderolabs/util-linux-tools - - siderolabs/intel-ucode - - siderolabs/i915 - - siderolabs/tailscale diff --git a/secrets.yaml b/secrets.yaml deleted file mode 100644 index 5ff3544..0000000 Binary files a/secrets.yaml and /dev/null differ diff --git a/src/models/__init__.py b/src/models/__init__.py deleted file mode 100644 index 77c44c5..0000000 --- a/src/models/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from pydantic import RootModel - -from . import ( - block, - extensions, - hardware, - network, - runtime, - security, - siderolink, - v1alpha1, -) - - -class Model( - RootModel[ - block.ExistingVolumeConfigV1Alpha1 - | block.RawVolumeConfigV1Alpha1 - | block.SwapVolumeConfigV1Alpha1 - | block.UserVolumeConfigV1Alpha1 - | block.VolumeConfigV1Alpha1 - | block.ZswapConfigV1Alpha1 - | extensions.ServiceConfigV1Alpha1 - | hardware.PCIDriverRebindConfigV1Alpha1 - | network.DefaultActionConfigV1Alpha1 - | network.EthernetConfigV1Alpha1 - | network.KubespanEndpointsConfigV1Alpha1 - | network.RuleConfigV1Alpha1 - | runtime.EventSinkV1Alpha1 - | runtime.KmsgLogV1Alpha1 - | runtime.WatchdogTimerV1Alpha1 - | security.TrustedRootsConfigV1Alpha1 - | siderolink.ConfigV1Alpha1 - | v1alpha1.Config - ] -): - root: ( - block.ExistingVolumeConfigV1Alpha1 - | block.RawVolumeConfigV1Alpha1 - | block.SwapVolumeConfigV1Alpha1 - | block.UserVolumeConfigV1Alpha1 - | block.VolumeConfigV1Alpha1 - | block.ZswapConfigV1Alpha1 - | extensions.ServiceConfigV1Alpha1 - | hardware.PCIDriverRebindConfigV1Alpha1 - | network.DefaultActionConfigV1Alpha1 - | network.EthernetConfigV1Alpha1 - | network.KubespanEndpointsConfigV1Alpha1 - | network.RuleConfigV1Alpha1 - | runtime.EventSinkV1Alpha1 - | runtime.KmsgLogV1Alpha1 - | runtime.WatchdogTimerV1Alpha1 - | security.TrustedRootsConfigV1Alpha1 - | siderolink.ConfigV1Alpha1 - | v1alpha1.Config - ) diff --git a/src/models/block.py b/src/models/block.py deleted file mode 100644 index 9cb9704..0000000 --- a/src/models/block.py +++ /dev/null @@ -1,403 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum - -from pydantic import BaseModel, ConfigDict, Field - - -class DiskSelector(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - match: str | None = Field( - None, - description='The Common Expression Language (CEL) expression to match the disk.\n', - title='match', - ) - - -class EncryptionKeyKMS(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - endpoint: str | None = Field( - None, description='KMS endpoint to Seal/Unseal the key.\n', title='endpoint' - ) - - -class EncryptionKeyNodeID(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - - -class EncryptionKeyStatic(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - passphrase: str | None = Field( - None, description='Defines the static passphrase value.\n', title='passphrase' - ) - - -class EncryptionKeyTPM(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - checkSecurebootStatusOnEnroll: bool | None = Field( - None, - description='Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\n', - title='checkSecurebootStatusOnEnroll', - ) - - -class Provider(Enum): - luks2 = 'luks2' - - -class Cipher(Enum): - aes_xts_plain64 = 'aes-xts-plain64' - xchacha12_aes_adiantum_plain64 = 'xchacha12,aes-adiantum-plain64' - xchacha20_aes_adiantum_plain64 = 'xchacha20,aes-adiantum-plain64' - - -class Options(Enum): - no_read_workqueue = 'no_read_workqueue' - no_write_workqueue = 'no_write_workqueue' - same_cpu_crypt = 'same_cpu_crypt' - - -class ApiVersion(Enum): - v1alpha1 = 'v1alpha1' - - -class Kind(Enum): - ExistingVolumeConfig = 'ExistingVolumeConfig' - - -class Type(Enum): - ext4 = 'ext4' - xfs = 'xfs' - - -class FilesystemSpec(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - type: Type | None = Field( - None, description='Filesystem type. Default is xfs.\n', title='type' - ) - projectQuotaSupport: bool | None = Field( - None, - description='Enables project quota support, valid only for ‘xfs’ filesystem.\n\nNote: changing this value might require a full remount of the filesystem.\n', - title='projectQuotaSupport', - ) - - -class MountSpec(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - readOnly: bool | None = Field( - None, description='Mount the volume read-only.\n', title='readOnly' - ) - - -class ProvisioningSpec(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - diskSelector: DiskSelector | None = Field( - None, description='The disk selector expression.\n', title='diskSelector' - ) - grow: bool | None = Field( - None, - description='Should the volume grow to the size of the disk (if possible).\n', - title='grow', - ) - minSize: str | None = Field( - None, - description='The minimum size of the volume.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\n', - title='minSize', - ) - maxSize: str | None = Field( - None, - description='The maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\n', - title='maxSize', - ) - - -class KindModel(Enum): - RawVolumeConfig = 'RawVolumeConfig' - - -class KindModel1(Enum): - SwapVolumeConfig = 'SwapVolumeConfig' - - -class KindModel2(Enum): - UserVolumeConfig = 'UserVolumeConfig' - - -class KindModel3(Enum): - VolumeConfig = 'VolumeConfig' - - -class VolumeSelector(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - match: str | None = Field( - None, - description='The Common Expression Language (CEL) expression to match the volume.\n', - title='match', - ) - - -class KindModel4(Enum): - ZswapConfig = 'ZswapConfig' - - -class ZswapConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel4 = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - maxPoolPercent: int | None = Field( - None, - description='The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\n', - title='maxPoolPercent', - ) - shrinkerEnabled: bool | None = Field( - None, - description='Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\n', - title='shrinkerEnabled', - ) - - -class EncryptionKey(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - slot: int | None = Field( - None, description='Key slot number for LUKS2 encryption.\n', title='slot' - ) - static: EncryptionKeyStatic | None = Field( - None, - description='Key which value is stored in the configuration file.\n', - title='static', - ) - nodeID: EncryptionKeyNodeID | None = Field( - None, - description='Deterministically generated key from the node UUID and PartitionLabel.\n', - title='nodeID', - ) - kms: EncryptionKeyKMS | None = Field( - None, description='KMS managed encryption key.\n', title='kms' - ) - tpm: EncryptionKeyTPM | None = Field( - None, description='Enable TPM based disk encryption.\n', title='tpm' - ) - lockToState: bool | None = Field( - None, - description='Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\n', - title='lockToState', - ) - - -class EncryptionSpec(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - provider: Provider | None = Field( - None, - description='Encryption provider to use for the encryption.\n', - title='provider', - ) - keys: list[EncryptionKey] | None = Field( - None, - description='Defines the encryption keys generation and storage method.\n', - title='keys', - ) - cipher: Cipher | None = Field( - None, - description='Cipher to use for the encryption. Depends on the encryption provider.\n', - title='cipher', - ) - keySize: int | None = Field( - None, description='Defines the encryption key length.\n', title='keySize' - ) - blockSize: int | None = Field( - None, description='Defines the encryption sector size.\n', title='blockSize' - ) - options: Options | None = Field( - None, - description='Additional –perf parameters for the LUKS2 encryption.\n', - title='options', - ) - - -class RawVolumeConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str | None = Field( - None, - description='Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n', - title='name', - ) - provisioning: ProvisioningSpec | None = Field( - None, - description='The provisioning describes how the volume is provisioned.\n', - title='provisioning', - ) - encryption: EncryptionSpec | None = Field( - None, - description='The encryption describes how the volume is encrypted.\n', - title='encryption', - ) - - -class SwapVolumeConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel1 = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str | None = Field( - None, - description='Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n', - title='name', - ) - provisioning: ProvisioningSpec | None = Field( - None, - description='The provisioning describes how the volume is provisioned.\n', - title='provisioning', - ) - encryption: EncryptionSpec | None = Field( - None, - description='The encryption describes how the volume is encrypted.\n', - title='encryption', - ) - - -class UserVolumeConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel2 = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str | None = Field( - None, - description='Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n', - title='name', - ) - provisioning: ProvisioningSpec | None = Field( - None, - description='The provisioning describes how the volume is provisioned.\n', - title='provisioning', - ) - filesystem: FilesystemSpec | None = Field( - None, - description='The filesystem describes how the volume is formatted.\n', - title='filesystem', - ) - encryption: EncryptionSpec | None = Field( - None, - description='The encryption describes how the volume is encrypted.\n', - title='encryption', - ) - - -class VolumeConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel3 = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str | None = Field(None, description='Name of the volume.\n', title='name') - provisioning: ProvisioningSpec | None = Field( - None, - description='The provisioning describes how the volume is provisioned.\n', - title='provisioning', - ) - encryption: EncryptionSpec | None = Field( - None, - description='The encryption describes how the volume is encrypted.\n', - title='encryption', - ) - - -class VolumeDiscoverySpec(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - volumeSelector: VolumeSelector | None = Field( - None, description='The volume selector expression.\n', title='volumeSelector' - ) - - -class ExistingVolumeConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: Kind = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str | None = Field( - None, - description='Name of the volume.\n\nName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n', - title='name', - ) - discovery: VolumeDiscoverySpec | None = Field( - None, - description='The discovery describes how to find a volume.\n', - title='discovery', - ) - mount: MountSpec | None = Field( - None, - description='The mount describes additional mount options.\n', - title='mount', - ) diff --git a/src/models/extensions.py b/src/models/extensions.py deleted file mode 100644 index a3d5369..0000000 --- a/src/models/extensions.py +++ /dev/null @@ -1,58 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum - -from pydantic import BaseModel, ConfigDict, Field - - -class ConfigFile(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - content: str | None = Field( - None, - description='The content of the extension service config file.\n', - title='content', - ) - mountPath: str | None = Field( - None, - description='The mount path of the extension service config file.\n', - title='mountPath', - ) - - -class ApiVersion(Enum): - v1alpha1 = 'v1alpha1' - - -class Kind(Enum): - ExtensionServiceConfig = 'ExtensionServiceConfig' - - -class ServiceConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: Kind = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str = Field(..., description='Name of the extension service.\n', title='name') - configFiles: list[ConfigFile] | None = Field( - None, - description='The config files for the extension service.\n', - title='configFiles', - ) - environment: list[str] | None = Field( - None, - description='The environment for the extension service.\n', - title='environment', - ) diff --git a/src/models/hardware.py b/src/models/hardware.py deleted file mode 100644 index 1f6e483..0000000 --- a/src/models/hardware.py +++ /dev/null @@ -1,37 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum - -from pydantic import BaseModel, ConfigDict, Field - - -class ApiVersion(Enum): - v1alpha1 = 'v1alpha1' - - -class Kind(Enum): - PCIDriverRebindConfig = 'PCIDriverRebindConfig' - - -class PCIDriverRebindConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: Kind = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str = Field(..., description='PCI device id\n', title='name') - targetDriver: str = Field( - ..., - description='Target driver to rebind the PCI device to.\n', - title='targetDriver', - ) diff --git a/src/models/network.py b/src/models/network.py deleted file mode 100644 index fa270bb..0000000 --- a/src/models/network.py +++ /dev/null @@ -1,220 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum - -from pydantic import BaseModel, ConfigDict, Field, constr - - -class ApiVersion(Enum): - v1alpha1 = 'v1alpha1' - - -class Kind(Enum): - NetworkDefaultActionConfig = 'NetworkDefaultActionConfig' - - -class Ingress(Enum): - accept = 'accept' - block = 'block' - - -class DefaultActionConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: Kind = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - ingress: Ingress | None = Field( - None, - description='Default action for all not explicitly configured ingress traffic: accept or block.\n', - title='ingress', - ) - - -class EthernetChannelsConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - rx: int | None = Field(None, description='Number of RX channels.\n', title='rx') - tx: int | None = Field(None, description='Number of TX channels.\n', title='tx') - other: int | None = Field( - None, description='Number of other channels.\n', title='other' - ) - combined: int | None = Field( - None, description='Number of combined channels.\n', title='combined' - ) - - -class KindModel(Enum): - EthernetConfig = 'EthernetConfig' - - -class EthernetRingsConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - rx: int | None = Field(None, description='Number of RX rings.\n', title='rx') - tx: int | None = Field(None, description='Number of TX rings.\n', title='tx') - rx_mini: int | None = Field( - None, alias='rx-mini', description='Number of RX mini rings.\n', title='rx-mini' - ) - rx_jumbo: int | None = Field( - None, - alias='rx-jumbo', - description='Number of RX jumbo rings.\n', - title='rx-jumbo', - ) - rx_buf_len: int | None = Field( - None, alias='rx-buf-len', description='RX buffer length.\n', title='rx-buf-len' - ) - cqe_size: int | None = Field( - None, alias='cqe-size', description='CQE size.\n', title='cqe-size' - ) - tx_push: bool | None = Field( - None, alias='tx-push', description='TX push enabled.\n', title='tx-push' - ) - rx_push: bool | None = Field( - None, alias='rx-push', description='RX push enabled.\n', title='rx-push' - ) - tx_push_buf_len: int | None = Field( - None, - alias='tx-push-buf-len', - description='TX push buffer length.\n', - title='tx-push-buf-len', - ) - tcp_data_split: bool | None = Field( - None, - alias='tcp-data-split', - description='TCP data split enabled.\n', - title='tcp-data-split', - ) - - -class IngressRule(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - subnet: constr(pattern=r'^[0-9a-f.:]+/\d{1,3}$') | None = Field( - None, description='Subnet defines a source subnet.\n', title='subnet' - ) - except_: constr(pattern=r'^[0-9a-f.:]+/\d{1,3}$') | None = Field( - None, - alias='except', - description='Except defines a source subnet to exclude from the rule, it gets excluded from the subnet.\n', - title='except', - ) - - -class KindModel1(Enum): - KubeSpanEndpoints = 'KubeSpanEndpoints' - - -class KubespanEndpointsConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel1 = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - extraAnnouncedEndpoints: list[str] | None = Field( - None, - description='A list of extra Wireguard endpoints to announce from this machine.\n\nTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.\n', - title='extraAnnouncedEndpoints', - ) - - -class KindModel2(Enum): - NetworkRuleConfig = 'NetworkRuleConfig' - - -class Protocol(Enum): - tcp = 'tcp' - udp = 'udp' - icmp = 'icmp' - icmpv6 = 'icmpv6' - - -class RulePortSelector(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - ports: list[int | str] | None = Field( - None, - description='Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.\n', - title='ports', - ) - protocol: Protocol | None = Field( - None, - description='Protocol defines traffic protocol (e.g. TCP or UDP).\n', - title='protocol', - ) - - -class EthernetConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str = Field(..., description='Name of the link (interface).\n', title='name') - features: dict[constr(pattern=r'.*'), bool] | None = Field( - None, - description='Configuration for Ethernet features.\n\nSet of features available and whether they can be enabled or disabled is driver specific.\nUse talosctl get ethernetstatus <link> -o yaml to get the list of available features and\ntheir current status.\n', - title='features', - ) - rings: EthernetRingsConfig | None = Field( - None, - description='Configuration for Ethernet link rings.\n\nThis is similar to ethtool -G command.\n', - title='rings', - ) - channels: EthernetChannelsConfig | None = Field( - None, - description='Configuration for Ethernet link channels.\n\nThis is similar to ethtool -L command.\n', - title='channels', - ) - - -class RuleConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel2 = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str = Field(..., description='Name of the config document.\n', title='name') - portSelector: RulePortSelector | None = Field( - None, - description='Port selector defines which ports and protocols on the host are affected by the rule.\n', - title='portSelector', - ) - ingress: list[IngressRule] | None = Field( - None, - description='Ingress defines which source subnets are allowed to access the host ports/protocols defined by the portSelector.\n', - title='ingress', - ) diff --git a/src/models/runtime.py b/src/models/runtime.py deleted file mode 100644 index 4dcb753..0000000 --- a/src/models/runtime.py +++ /dev/null @@ -1,90 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum - -from pydantic import BaseModel, ConfigDict, Field, constr - - -class ApiVersion(Enum): - v1alpha1 = 'v1alpha1' - - -class Kind(Enum): - EventSinkConfig = 'EventSinkConfig' - - -class EventSinkV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: Kind = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - endpoint: str | None = Field( - None, - description='The endpoint for the event sink as ‘host:port’.\n', - title='endpoint', - ) - - -class KindModel(Enum): - KmsgLogConfig = 'KmsgLogConfig' - - -class KmsgLogV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str | None = Field( - None, description='Name of the config document.\n', title='name' - ) - url: constr(pattern=r'^(tcp|udp)://') | None = Field( - None, - description='The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.\n', - title='url', - ) - - -class KindModel1(Enum): - WatchdogTimerConfig = 'WatchdogTimerConfig' - - -class WatchdogTimerV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: KindModel1 = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - device: str | None = Field( - None, description='Path to the watchdog device.\n', title='device' - ) - timeout: ( - constr(pattern=r'^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$') | None - ) = Field( - None, - description='Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.\n', - title='timeout', - ) diff --git a/src/models/security.py b/src/models/security.py deleted file mode 100644 index 859f87c..0000000 --- a/src/models/security.py +++ /dev/null @@ -1,37 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum - -from pydantic import BaseModel, ConfigDict, Field - - -class ApiVersion(Enum): - v1alpha1 = 'v1alpha1' - - -class Kind(Enum): - TrustedRootsConfig = 'TrustedRootsConfig' - - -class TrustedRootsConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: Kind = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - name: str = Field(..., description='Name of the config document.\n', title='name') - certificates: str | None = Field( - None, - description='List of additional trusted certificate authorities (as PEM-encoded certificates).\n\nMultiple certificates can be provided in a single config document, separated by newline characters.\n', - title='certificates', - ) diff --git a/src/models/siderolink.py b/src/models/siderolink.py deleted file mode 100644 index e26ca75..0000000 --- a/src/models/siderolink.py +++ /dev/null @@ -1,39 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum - -from pydantic import BaseModel, ConfigDict, Field, constr - - -class ApiVersion(Enum): - v1alpha1 = 'v1alpha1' - - -class Kind(Enum): - SideroLinkConfig = 'SideroLinkConfig' - - -class ConfigV1Alpha1(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiVersion: ApiVersion = Field( - ..., - description='apiVersion is the API version of the resource.\n', - title='apiVersion', - ) - kind: Kind = Field( - ..., description='kind is the kind of the resource.\n', title='kind' - ) - apiUrl: constr(pattern=r'^(https|grpc)://') | None = Field( - None, description='SideroLink API URL to connect to.\n', title='apiUrl' - ) - uniqueToken: str | None = Field( - None, - description='SideroLink unique token to use for the connection (optional).\n\nThis value is overridden with META key UniqueMachineToken.\n', - title='uniqueToken', - ) diff --git a/src/models/v1alpha1.py b/src/models/v1alpha1.py deleted file mode 100644 index 5275aef..0000000 --- a/src/models/v1alpha1.py +++ /dev/null @@ -1,1873 +0,0 @@ -# generated by datamodel-codegen: -# filename: schema.tRo.json -# timestamp: 2026-02-20T04:31:38+00:00 - -from __future__ import annotations - -from enum import Enum -from typing import Any - -from pydantic import AnyUrl, BaseModel, ConfigDict, Field, constr - - -class AdminKubeconfigConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - certLifetime: ( - constr(pattern=r'^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$') | None - ) = Field( - None, - description='Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n', - title='certLifetime', - ) - - -class AdmissionPluginConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - name: str | None = Field( - None, - description='Name is the name of the admission controller.\nIt must match the registered admission plugin name.\n', - title='name', - ) - configuration: dict[str, Any] | None = Field( - None, - description='Configuration is an embedded configuration object to be used as the plugin’s\nconfiguration.\n', - title='configuration', - ) - - -class AuthorizationConfigAuthorizerConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - type: str | None = Field( - None, - description='Type is the name of the authorizer. Allowed values are Node, RBAC, and Webhook.\n', - title='type', - ) - name: str | None = Field( - None, description='Name is used to describe the authorizer.\n', title='name' - ) - webhook: dict[str, Any] | None = Field( - None, - description='webhook is the configuration for the webhook authorizer.\n', - title='webhook', - ) - - -class BridgePort(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - master: str | None = Field( - None, description='The name of the bridge master interface\n', title='master' - ) - - -class BridgeVLAN(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - vlanFiltering: bool | None = Field( - None, description='Whether VLAN filtering is enabled.\n', title='vlanFiltering' - ) - - -class Name(Enum): - flannel = 'flannel' - custom = 'custom' - none = 'none' - - -class Ca(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - crt: str | None = None - key: str | None = None - - -class AcceptedCAs(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - crt: str | None = None - - -class AggregatorCA(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - crt: str | None = None - key: str | None = None - - -class ServiceAccount(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - key: str | None = None - - -class ClusterInlineManifest(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - name: str | None = Field( - None, - description='Name of the manifest.\nName should be unique.\n', - title='name', - ) - contents: str | None = Field( - None, description='Manifest contents as a string.\n', title='contents' - ) - - -class Version(Enum): - v1alpha1 = 'v1alpha1' - - -class ControlPlaneConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - endpoint: AnyUrl | None = Field( - None, - description='Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\n', - title='endpoint', - ) - localAPIServerPort: int | None = Field( - None, - description='The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is 6443.\n', - title='localAPIServerPort', - ) - - -class CoreDNS(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disabled: bool | None = Field( - None, - description='Disable coredns deployment on cluster bootstrap.\n', - title='disabled', - ) - image: str | None = Field( - None, - description='The image field is an override to the default coredns image.\n', - title='image', - ) - - -class DHCPOptions(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - routeMetric: int | None = Field( - None, - description='The priority of all routes received via DHCP.\n', - title='routeMetric', - ) - ipv4: bool | None = Field( - None, - description='Enables DHCPv4 protocol for the interface (default is enabled).\n', - title='ipv4', - ) - ipv6: bool | None = Field( - None, - description='Enables DHCPv6 protocol for the interface (default is disabled).\n', - title='ipv6', - ) - duidv6: str | None = Field( - None, description='Set client DUID (hex string).\n', title='duidv6' - ) - - -class DeviceWireguardPeer(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - publicKey: str | None = Field( - None, - description='Specifies the public key of this peer.\nCan be extracted from private key by running wg pubkey < private.key > public.key && cat public.key.\n', - title='publicKey', - ) - endpoint: str | None = Field( - None, - description='Specifies the endpoint of this peer entry.\n', - title='endpoint', - ) - persistentKeepaliveInterval: ( - constr(pattern=r'^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$') | None - ) = Field( - None, - description='Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n', - title='persistentKeepaliveInterval', - ) - allowedIPs: list[str] | None = Field( - None, - description='AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\n', - title='allowedIPs', - ) - - -class Endpoint(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - - -class EtcdConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - image: str | None = Field( - None, - description='The container image used to create the etcd service.\n', - title='image', - ) - ca: Ca | None = Field( - None, - description='The ca is the root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n', - title='ca', - ) - extraArgs: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n\nname\ndata-dir\ninitial-cluster-state\nlisten-peer-urls\nlisten-client-urls\ncert-file\nkey-file\ntrusted-ca-file\npeer-client-cert-auth\npeer-cert-file\npeer-trusted-ca-file\npeer-key-file\n\n', - title='extraArgs', - ) - advertisedSubnets: list[str] | None = Field( - None, - description='The advertisedSubnets field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n', - title='advertisedSubnets', - ) - listenSubnets: list[str] | None = Field( - None, - description='The listenSubnets field configures the networks for the etcd to listen for peer and client connections.\n\nIf listenSubnets is not set, but advertisedSubnets is set, listenSubnets defaults to\nadvertisedSubnets.\n\nIf neither advertisedSubnets nor listenSubnets is set, listenSubnets defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n', - title='listenSubnets', - ) - - -class ExternalCloudProviderConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - enabled: bool | None = Field( - None, description='Enable external cloud provider.\n', title='enabled' - ) - manifests: list[str] | None = Field( - None, - description='A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\n', - title='manifests', - ) - - -class ExtraHost(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - ip: str | None = Field(None, description='The IP of the host.\n', title='ip') - aliases: list[str] | None = Field( - None, description='The host alias.\n', title='aliases' - ) - - -class FlannelCNIConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - extraArgs: list[str] | None = Field( - None, description='Extra arguments for ‘flanneld’.\n', title='extraArgs' - ) - - -class HostDNSConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - enabled: bool | None = Field( - None, description='Enable host DNS caching resolver.\n', title='enabled' - ) - forwardKubeDNSToHost: bool | None = Field( - None, - description='Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\n', - title='forwardKubeDNSToHost', - ) - resolveMemberNames: bool | None = Field( - None, - description='Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\n', - title='resolveMemberNames', - ) - - -class ImageCacheConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - localEnabled: bool | None = Field( - None, description='Enable local image cache.\n', title='localEnabled' - ) - - -class Type(Enum): - ssd = 'ssd' - hdd = 'hdd' - nvme = 'nvme' - sd = 'sd' - - -class InstallDiskSelector(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - size: str | None = Field(None, description='Disk size.\n', title='size') - name: str | None = Field( - None, - description='Disk name /sys/block/<dev>/device/name.\n', - title='name', - ) - model: str | None = Field( - None, - description='Disk model /sys/block/<dev>/device/model.\n', - title='model', - ) - serial: str | None = Field( - None, - description='Disk serial number /sys/block/<dev>/serial.\n', - title='serial', - ) - modalias: str | None = Field( - None, - description='Disk modalias /sys/block/<dev>/device/modalias.\n', - title='modalias', - ) - uuid: str | None = Field( - None, description='Disk UUID /sys/block/<dev>/uuid.\n', title='uuid' - ) - wwid: str | None = Field( - None, description='Disk WWID /sys/block/<dev>/wwid.\n', title='wwid' - ) - type: Type | None = Field(None, description='Disk Type.\n', title='type') - busPath: str | None = Field(None, description='Disk bus path.\n', title='busPath') - - -class KernelModuleConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - name: str | None = Field(None, description='Module name.\n', title='name') - parameters: list[str] | None = Field( - None, - description='Module parameters, changes applied after reboot.\n', - title='parameters', - ) - - -class KubePrism(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - enabled: bool | None = Field( - None, - description='Enable KubePrism support - will start local load balancing proxy.\n', - title='enabled', - ) - port: int | None = Field(None, description='KubePrism port.\n', title='port') - - -class KubeSpanFilters(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - endpoints: list[str] | None = Field( - None, - description='Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.\n', - title='endpoints', - ) - - -class KubeletNodeIPConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - validSubnets: list[str] | None = Field( - None, - description='The validSubnets field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\n', - title='validSubnets', - ) - - -class KubernetesTalosAPIAccessConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - enabled: bool | None = Field( - None, - description='Enable Talos API access from Kubernetes pods.\n', - title='enabled', - ) - allowedRoles: list[str] | None = Field( - None, - description='The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.\n', - title='allowedRoles', - ) - allowedKubernetesNamespaces: list[str] | None = Field( - None, - description='The list of Kubernetes namespaces Talos API access is available from.\n', - title='allowedKubernetesNamespaces', - ) - - -class LinuxIDMapping(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - containerID: int | None = Field( - None, - description='ContainerID is the starting UID/GID in the container.\n', - title='containerID', - ) - hostID: int | None = Field( - None, - description='HostID is the starting UID/GID on the host to be mapped to ‘ContainerID’.\n', - title='hostID', - ) - size: int | None = Field( - None, description='Size is the number of IDs to be mapped.\n', title='size' - ) - - -class Format(Enum): - json_lines = 'json_lines' - - -class LoggingDestination(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - endpoint: Endpoint | None = Field( - None, - description='Where to send logs. Supported protocols are “tcp” and “udp”.\n', - title='endpoint', - ) - format: Format | None = Field(None, description='Logs format.\n', title='format') - extraTags: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Extra tags (key-value) pairs to attach to every log message sent.\n', - title='extraTags', - ) - - -class TypeModel(Enum): - controlplane = 'controlplane' - worker = 'worker' - - -class MachineControllerManagerConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disabled: bool | None = Field( - None, - description='Disable kube-controller-manager on the node.\n', - title='disabled', - ) - - -class Op(Enum): - create = 'create' - append = 'append' - overwrite = 'overwrite' - - -class MachineFile(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - content: str | None = Field( - None, description='The contents of the file.\n', title='content' - ) - permissions: int | None = Field( - None, description='The file’s permissions in octal.\n', title='permissions' - ) - path: str | None = Field(None, description='The path of the file.\n', title='path') - op: Op | None = Field(None, description='The operation to use\n', title='op') - - -class MachineSchedulerConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disabled: bool | None = Field( - None, description='Disable kube-scheduler on the node.\n', title='disabled' - ) - - -class MachineSeccompProfile(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - name: str | None = Field( - None, - description='The name field is used to provide the file name of the seccomp profile.\n', - title='name', - ) - value: dict[str, Any] | None = Field( - None, - description='The value field is used to provide the seccomp profile.\n', - title='value', - ) - - -class NetworkDeviceSelector(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - busPath: str | None = Field( - None, - description='PCI, USB bus prefix, supports matching by wildcard.\n', - title='busPath', - ) - hardwareAddr: str | None = Field( - None, - description='Device hardware (MAC) address, supports matching by wildcard.\n', - title='hardwareAddr', - ) - permanentAddr: str | None = Field( - None, - description='Device permanent hardware address, supports matching by wildcard.\nThe permanent address doesn’t change when the link is enslaved to a bond,\nso it’s recommended to use this field for bond members.\n', - title='permanentAddr', - ) - pciID: str | None = Field( - None, - description='PCI ID (vendor ID, product ID), supports matching by wildcard.\n', - title='pciID', - ) - driver: str | None = Field( - None, - description='Kernel driver, supports matching by wildcard.\n', - title='driver', - ) - physical: bool | None = Field( - None, description='Select only physical devices.\n', title='physical' - ) - - -class NetworkKubeSpan(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - enabled: bool | None = Field( - None, - description='Enable the KubeSpan feature.\nCluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled.\n', - title='enabled', - ) - advertiseKubernetesNetworks: bool | None = Field( - None, - description='Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel,\nand KubeSpan handles the node-to-node traffic.\nIf enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly.\nWhen enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which\nis not always the case with CNIs not relying on Kubernetes for IPAM.\n', - title='advertiseKubernetesNetworks', - ) - allowDownPeerBypass: bool | None = Field( - None, - description='Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security: either traffic is always\nforced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly\nto the peer if Wireguard connection can’t be established.\n', - title='allowDownPeerBypass', - ) - harvestExtraEndpoints: bool | None = Field( - None, - description='KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nThis feature is disabled by default, don’t enable it\nwith high number of peers (>50) in the KubeSpan network (performance issues).\n', - title='harvestExtraEndpoints', - ) - mtu: int | None = Field( - None, - description='KubeSpan link MTU size.\nDefault value is 1420.\n', - title='mtu', - ) - filters: KubeSpanFilters | None = Field( - None, - description='KubeSpan advanced filtering of network addresses .\n\nSettings in this section are optional, and settings apply only to the node.\n', - title='filters', - ) - - -class ProxyConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disabled: bool | None = Field( - None, - description='Disable kube-proxy deployment on cluster bootstrap.\n', - title='disabled', - ) - image: str | None = Field( - None, - description='The container image used in the kube-proxy manifest.\n', - title='image', - ) - mode: str | None = Field( - None, - description='proxy mode of kube-proxy.\nThe default is ‘iptables’.\n', - title='mode', - ) - extraArgs: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Extra arguments to supply to kube-proxy.\n', - title='extraArgs', - ) - - -class RegistryAuthConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - username: str | None = Field( - None, - description='Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n', - title='username', - ) - password: str | None = Field( - None, - description='Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n', - title='password', - ) - auth: str | None = Field( - None, - description='Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n', - title='auth', - ) - identityToken: str | None = Field( - None, - description='Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n', - title='identityToken', - ) - - -class RegistryKubernetesConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disabled: bool | None = Field( - None, description='Disable Kubernetes discovery registry.\n', title='disabled' - ) - - -class RegistryMirrorConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - endpoints: list[str] | None = Field( - None, - description='List of endpoints (URLs) for registry mirrors to use.\nEndpoint configures HTTP/HTTPS access mode, host name,\nport and path (if path is not set, it defaults to /v2).\n', - title='endpoints', - ) - overridePath: bool | None = Field( - None, - description='Use the exact path specified for the endpoint (don’t append /v2/).\nThis setting is often required for setting up multiple mirrors\non a single instance of a registry.\n', - title='overridePath', - ) - skipFallback: bool | None = Field( - None, - description='Skip fallback to the upstream endpoint, for example the mirror configuration\nfor docker.io will not fallback to registry-1.docker.io.\n', - title='skipFallback', - ) - - -class RegistryServiceConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disabled: bool | None = Field( - None, - description='Disable external service discovery registry.\n', - title='disabled', - ) - endpoint: str | None = Field( - None, description='External service endpoint.\n', title='endpoint' - ) - - -class ClientIdentity(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - crt: str | None = None - key: str | None = None - - -class RegistryTLSConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - clientIdentity: ClientIdentity | None = Field( - None, - description='Enable mutual TLS authentication with the registry.\nClient certificate and key should be base64-encoded.\n', - title='clientIdentity', - ) - ca: str | None = Field( - None, - description='CA registry certificate to add the list of trusted certificates.\nCertificate should be base64-encoded.\n', - title='ca', - ) - insecureSkipVerify: bool | None = Field( - None, - description='Skip TLS server certificate verification (not recommended).\n', - title='insecureSkipVerify', - ) - - -class ResourcesConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - requests: dict[str, Any] | None = Field( - None, - description='Requests configures the reserved cpu/memory resources.\n', - title='requests', - ) - limits: dict[str, Any] | None = Field( - None, - description='Limits configures the maximum cpu/memory resources a container can use.\n', - title='limits', - ) - - -class Route(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - network: str | None = Field( - None, description='The route’s network (destination).\n', title='network' - ) - gateway: str | None = Field( - None, - description='The route’s gateway (if empty, creates link scope route).\n', - title='gateway', - ) - source: str | None = Field( - None, description='The route’s source address (optional).\n', title='source' - ) - metric: int | None = Field( - None, description='The optional metric for the route.\n', title='metric' - ) - mtu: int | None = Field( - None, description='The optional MTU for the route.\n', title='mtu' - ) - - -class STP(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - enabled: bool | None = Field( - None, - description='Whether Spanning Tree Protocol (STP) is enabled.\n', - title='enabled', - ) - - -class TimeConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disabled: bool | None = Field( - None, - description='Indicates if the time service is disabled for the machine.\nDefaults to false.\n', - title='disabled', - ) - servers: list[str] | None = Field( - None, - description='description: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to time.cloudflare.com.\n\nTalos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as “/dev/ptp0” or “/dev/ptp_kvm”.\n', - title='servers', - ) - bootTimeout: ( - constr(pattern=r'^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$') | None - ) = Field( - None, - description='Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n', - title='bootTimeout', - ) - - -class UdevConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - rules: list[str] | None = Field( - None, - description='List of udev rules to apply to the udev system\n', - title='rules', - ) - - -class VIPEquinixMetalConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiToken: str | None = Field( - None, description='Specifies the Equinix Metal API Token.\n', title='apiToken' - ) - - -class VIPHCloudConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - apiToken: str | None = Field( - None, description='Specifies the Hetzner Cloud API Token.\n', title='apiToken' - ) - - -class VolumeMountConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - hostPath: str | None = Field( - None, description='Path on the host.\n', title='hostPath' - ) - mountPath: str | None = Field( - None, description='Path in the container.\n', title='mountPath' - ) - readonly: bool | None = Field( - None, description='Mount the volume read only.\n', title='readonly' - ) - - -class APIServerConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - image: str | None = Field( - None, - description='The container image used in the API server manifest.\n', - title='image', - ) - extraArgs: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Extra arguments to supply to the API server.\n', - title='extraArgs', - ) - extraVolumes: list[VolumeMountConfig] | None = Field( - None, - description='Extra volumes to mount to the API server static pod.\n', - title='extraVolumes', - ) - env: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='The env field allows for the addition of environment variables for the control plane component.\n', - title='env', - ) - certSANs: list[str] | None = Field( - None, - description='Extra certificate subject alternative names for the API server’s certificate.\n', - title='certSANs', - ) - disablePodSecurityPolicy: bool | None = Field( - None, - description='Disable PodSecurityPolicy in the API server and default manifests.\n', - title='disablePodSecurityPolicy', - ) - admissionControl: list[AdmissionPluginConfig] | None = Field( - None, - description='Configure the API server admission plugins.\n', - title='admissionControl', - ) - auditPolicy: dict[str, Any] | None = Field( - None, - description='Configure the API server audit policy.\n', - title='auditPolicy', - ) - resources: dict[str, Any] | None = Field( - None, description='Configure the API server resources.\n', title='resources' - ) - authorizationConfig: list[AuthorizationConfigAuthorizerConfig] | None = Field( - None, - description='Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.\n', - title='authorizationConfig', - ) - - -class Bond(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - interfaces: list[str] | None = Field( - None, description='The interfaces that make up the bond.\n', title='interfaces' - ) - deviceSelectors: list[NetworkDeviceSelector] | None = Field( - None, - description='Picks a network device using the selector.\nMutually exclusive with interfaces.\nSupports partial match using wildcard syntax.\n', - title='deviceSelectors', - ) - arpIPTarget: list[str] | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\n', - title='arpIPTarget', - ) - mode: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='mode', - ) - xmitHashPolicy: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='xmitHashPolicy', - ) - lacpRate: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='lacpRate', - ) - adActorSystem: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\n', - title='adActorSystem', - ) - arpValidate: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='arpValidate', - ) - arpAllTargets: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='arpAllTargets', - ) - primary: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='primary', - ) - primaryReselect: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='primaryReselect', - ) - failOverMac: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='failOverMac', - ) - adSelect: str | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='adSelect', - ) - miimon: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='miimon', - ) - updelay: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='updelay', - ) - downdelay: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='downdelay', - ) - arpInterval: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='arpInterval', - ) - resendIgmp: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='resendIgmp', - ) - minLinks: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='minLinks', - ) - lpInterval: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='lpInterval', - ) - packetsPerSlave: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='packetsPerSlave', - ) - numPeerNotif: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='numPeerNotif', - ) - tlbDynamicLb: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='tlbDynamicLb', - ) - allSlavesActive: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='allSlavesActive', - ) - useCarrier: bool | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='useCarrier', - ) - adActorSysPrio: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='adActorSysPrio', - ) - adUserPortKey: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='adUserPortKey', - ) - peerNotifyDelay: int | None = Field( - None, - description='A bond option.\nPlease see the official kernel documentation.\n', - title='peerNotifyDelay', - ) - - -class Bridge(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - interfaces: list[str] | None = Field( - None, - description='The interfaces that make up the bridge.\n', - title='interfaces', - ) - stp: STP | None = Field( - None, - description='Enable STP on this bridge.\nPlease see the official kernel documentation.\n', - title='stp', - ) - vlan: BridgeVLAN | None = Field( - None, - description='Enable VLAN-awareness on this bridge.\nPlease see the official kernel documentation.\n', - title='vlan', - ) - - -class CNIConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - name: Name | None = Field(None, description='Name of CNI to use.\n', title='name') - urls: list[str] | None = Field( - None, - description='URLs containing manifests to apply for the CNI.\nShould be present for “custom”, must be empty for “flannel” and “none”.\n', - title='urls', - ) - flannel: FlannelCNIConfig | None = Field( - None, - description='description: |\nFlannel configuration options.\n', - title='flannel', - ) - - -class ClusterNetworkConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - cni: CNIConfig | None = Field( - None, - description='The CNI used.\nComposed of “name” and “urls”.\nThe “name” key supports the following options: “flannel”, “custom”, and “none”.\n“flannel” uses Talos-managed Flannel CNI, and that’s the default option.\n“custom” uses custom manifests that should be provided in “urls”.\n“none” indicates that Talos will not manage any CNI installation.\n', - title='cni', - ) - dnsDomain: str | None = Field( - None, - description='The domain used by Kubernetes DNS.\nThe default is cluster.local\n', - title='dnsDomain', - ) - podSubnets: list[str] | None = Field( - None, description='The pod subnet CIDR.\n', title='podSubnets' - ) - serviceSubnets: list[str] | None = Field( - None, description='The service subnet CIDR.\n', title='serviceSubnets' - ) - - -class ControllerManagerConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - image: str | None = Field( - None, - description='The container image used in the controller manager manifest.\n', - title='image', - ) - extraArgs: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Extra arguments to supply to the controller manager.\n', - title='extraArgs', - ) - extraVolumes: list[VolumeMountConfig] | None = Field( - None, - description='Extra volumes to mount to the controller manager static pod.\n', - title='extraVolumes', - ) - env: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='The env field allows for the addition of environment variables for the control plane component.\n', - title='env', - ) - resources: dict[str, Any] | None = Field( - None, - description='Configure the controller manager resources.\n', - title='resources', - ) - - -class DeviceVIPConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - ip: str | None = Field( - None, description='Specifies the IP address to be used.\n', title='ip' - ) - equinixMetal: VIPEquinixMetalConfig | None = Field( - None, - description='Specifies the Equinix Metal API settings to assign VIP to the node.\n', - title='equinixMetal', - ) - hcloud: VIPHCloudConfig | None = Field( - None, - description='Specifies the Hetzner Cloud API settings to assign VIP to the node.\n', - title='hcloud', - ) - - -class DeviceWireguardConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - privateKey: str | None = Field( - None, - description='Specifies a private key configuration (base64 encoded).\nCan be generated by wg genkey.\n', - title='privateKey', - ) - listenPort: int | None = Field( - None, description='Specifies a device’s listening port.\n', title='listenPort' - ) - firewallMark: int | None = Field( - None, description='Specifies a device’s firewall mark.\n', title='firewallMark' - ) - peers: list[DeviceWireguardPeer] | None = Field( - None, - description='Specifies a list of peer configurations to apply to a device.\n', - title='peers', - ) - - -class DiscoveryRegistriesConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - kubernetes: RegistryKubernetesConfig | None = Field( - None, - description='Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n\nThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee https://github.com/siderolabs/talos/issues/9980 for more information.\n', - title='kubernetes', - ) - service: RegistryServiceConfig | None = Field( - None, - description='Service registry is using an external service to push and pull information about cluster members.\n', - title='service', - ) - - -class ExtraMount(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - destination: str | None = Field( - None, - description='Destination is the absolute path where the mount will be placed in the container.\n', - title='destination', - ) - type: str | None = Field( - None, description='Type specifies the mount kind.\n', title='type' - ) - source: str | None = Field( - None, - description='Source specifies the source path of the mount.\n', - title='source', - ) - options: list[str] | None = Field( - None, description='Options are fstab style mount options.\n', title='options' - ) - uidMappings: list[LinuxIDMapping] | None = Field( - None, - description='UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n', - title='uidMappings', - ) - gidMappings: list[LinuxIDMapping] | None = Field( - None, - description='UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n', - title='gidMappings', - ) - - -class FeaturesConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - rbac: bool | None = Field( - None, description='Enable role-based access control (RBAC).\n', title='rbac' - ) - stableHostname: bool | None = Field( - None, description='Enable stable default hostname.\n', title='stableHostname' - ) - kubernetesTalosAPIAccess: KubernetesTalosAPIAccessConfig | None = Field( - None, - description='Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.\n', - title='kubernetesTalosAPIAccess', - ) - apidCheckExtKeyUsage: bool | None = Field( - None, - description='Enable checks for extended key usage of client certificates in apid.\n', - title='apidCheckExtKeyUsage', - ) - diskQuotaSupport: bool | None = Field( - None, - description='Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\n', - title='diskQuotaSupport', - ) - kubePrism: KubePrism | None = Field( - None, - description='KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\n', - title='kubePrism', - ) - hostDNS: HostDNSConfig | None = Field( - None, description='Configures host DNS caching resolver.\n', title='hostDNS' - ) - imageCache: ImageCacheConfig | None = Field( - None, description='Enable Image Cache feature.\n', title='imageCache' - ) - nodeAddressSortAlgorithm: str | None = Field( - None, - description='Select the node address sort algorithm.\nThe ‘v1’ algorithm sorts addresses by the address itself.\nThe ‘v2’ algorithm prefers more specific prefixes.\nIf unset, defaults to ‘v1’.\n', - title='nodeAddressSortAlgorithm', - ) - - -class InstallConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - disk: str | None = Field( - None, description='The disk used for installations.\n', title='disk' - ) - diskSelector: InstallDiskSelector | None = Field( - None, - description='Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over disk.\n', - title='diskSelector', - ) - extraKernelArgs: list[str] | None = Field( - None, - description='Allows for supplying extra kernel args via the bootloader.\nExisting kernel args can be removed by prefixing the argument with a -.\nFor example -console removes all console=<value> arguments, whereas -console=tty0 removes the console=tty0 default argument.\nIf Talos is using systemd-boot as a bootloader (default for UEFI) this setting will be ignored.\n', - title='extraKernelArgs', - ) - image: str | None = Field( - None, - description='Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\nGitHub releases page.\n', - title='image', - ) - wipe: bool | None = Field( - None, - description='Indicates if the installation disk should be wiped at installation time.\nDefaults to true.\n', - title='wipe', - ) - legacyBIOSSupport: bool | None = Field( - None, - description='Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn’t support GPT partitioning scheme.\n', - title='legacyBIOSSupport', - ) - - -class KernelConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - modules: list[KernelModuleConfig] | None = Field( - None, description='Kernel modules to load.\n', title='modules' - ) - - -class KubeletConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - image: str | None = Field( - None, - description='The image field is an optional reference to an alternative kubelet image.\n', - title='image', - ) - clusterDNS: list[str] | None = Field( - None, - description='The ClusterDNS field is an optional reference to an alternative kubelet clusterDNS ip list.\n', - title='clusterDNS', - ) - extraArgs: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='The extraArgs field is used to provide additional flags to the kubelet.\n', - title='extraArgs', - ) - extraMounts: list[ExtraMount] | None = Field( - None, - description='The extraMounts field is used to add additional mounts to the kubelet container.\nNote that either bind or rbind are required in the options.\n', - title='extraMounts', - ) - extraConfig: dict[str, Any] | None = Field( - None, - description='The extraConfig field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\n', - title='extraConfig', - ) - credentialProviderConfig: dict[str, Any] | None = Field( - None, - description='The KubeletCredentialProviderConfig field is used to provide kubelet credential configuration.\n', - title='credentialProviderConfig', - ) - defaultRuntimeSeccompProfileEnabled: bool | None = Field( - None, - description='Enable container runtime default Seccomp profile.\n', - title='defaultRuntimeSeccompProfileEnabled', - ) - registerWithFQDN: bool | None = Field( - None, - description='The registerWithFQDN field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\n', - title='registerWithFQDN', - ) - nodeIP: KubeletNodeIPConfig | None = Field( - None, - description='The nodeIP field is used to configure --node-ip flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\n', - title='nodeIP', - ) - skipNodeRegistration: bool | None = Field( - None, - description='The skipNodeRegistration is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\n', - title='skipNodeRegistration', - ) - disableManifestsDirectory: bool | None = Field( - None, - description='The disableManifestsDirectory field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt’s recommended to configure static pods with the “pods” key instead.\n', - title='disableManifestsDirectory', - ) - - -class LoggingConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - destinations: list[LoggingDestination] | None = Field( - None, description='Logging destination.\n', title='destinations' - ) - - -class MachineControlPlaneConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - controllerManager: MachineControllerManagerConfig | None = Field( - None, - description='Controller manager machine specific configuration options.\n', - title='controllerManager', - ) - scheduler: MachineSchedulerConfig | None = Field( - None, - description='Scheduler machine specific configuration options.\n', - title='scheduler', - ) - - -class RegistryConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - tls: RegistryTLSConfig | None = Field( - None, description='The TLS configuration for the registry.\n', title='tls' - ) - auth: RegistryAuthConfig | None = Field( - None, - description='The auth configuration for this registry.\nNote: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot.\n', - title='auth', - ) - - -class SchedulerConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - image: str | None = Field( - None, - description='The container image used in the scheduler manifest.\n', - title='image', - ) - extraArgs: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Extra arguments to supply to the scheduler.\n', - title='extraArgs', - ) - extraVolumes: list[VolumeMountConfig] | None = Field( - None, - description='Extra volumes to mount to the scheduler static pod.\n', - title='extraVolumes', - ) - env: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='The env field allows for the addition of environment variables for the control plane component.\n', - title='env', - ) - resources: dict[str, Any] | None = Field( - None, description='Configure the scheduler resources.\n', title='resources' - ) - config: dict[str, Any] | None = Field( - None, - description='Specify custom kube-scheduler configuration.\n', - title='config', - ) - - -class Vlan(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - addresses: list[str] | None = Field( - None, - description='The addresses in CIDR notation or as plain IPs to use.\n', - title='addresses', - ) - routes: list[Route] | None = Field( - None, description='A list of routes associated with the VLAN.\n', title='routes' - ) - dhcp: bool | None = Field( - None, description='Indicates if DHCP should be used.\n', title='dhcp' - ) - vlanId: int | None = Field(None, description='The VLAN’s ID.\n', title='vlanId') - mtu: int | None = Field(None, description='The VLAN’s MTU.\n', title='mtu') - vip: DeviceVIPConfig | None = Field( - None, description='The VLAN’s virtual IP address configuration.\n', title='vip' - ) - dhcpOptions: DHCPOptions | None = Field( - None, - description='DHCP specific options.\ndhcp must be set to true for these to take effect.\n', - title='dhcpOptions', - ) - - -class ClusterDiscoveryConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - enabled: bool | None = Field( - None, - description='Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\n', - title='enabled', - ) - registries: DiscoveryRegistriesConfig | None = Field( - None, - description='Configure registries used for cluster member discovery.\n', - title='registries', - ) - - -class Device(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - interface: str | None = Field( - None, - description='The interface name.\nMutually exclusive with deviceSelector.\n', - title='interface', - ) - deviceSelector: NetworkDeviceSelector | None = Field( - None, - description='Picks a network device using the selector.\nMutually exclusive with interface.\nSupports partial match using wildcard syntax.\n', - title='deviceSelector', - ) - addresses: list[str] | None = Field( - None, - description='Assigns static IP addresses to the interface.\nAn address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed).\n', - title='addresses', - ) - routes: list[Route] | None = Field( - None, - description='A list of routes associated with the interface.\nIf used in combination with DHCP, these routes will be appended to routes returned by DHCP server.\n', - title='routes', - ) - bond: Bond | None = Field( - None, description='Bond specific options.\n', title='bond' - ) - bridge: Bridge | None = Field( - None, description='Bridge specific options.\n', title='bridge' - ) - bridgePort: BridgePort | None = Field( - None, - description='Configure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.\n', - title='bridgePort', - ) - vlans: list[Vlan] | None = Field( - None, description='VLAN specific options.\n', title='vlans' - ) - mtu: int | None = Field( - None, - description='The interface’s MTU.\nIf used in combination with DHCP, this will override any MTU settings returned from DHCP server.\n', - title='mtu', - ) - dhcp: bool | None = Field( - None, - description='Indicates if DHCP should be used to configure the interface.\nThe following DHCP options are supported:\n\n\nOptionClasslessStaticRoute\nOptionDomainNameServer\nOptionDNSDomainSearchList\nOptionHostName\n\n', - title='dhcp', - ) - ignore: bool | None = Field( - None, - description='Indicates if the interface should be ignored (skips configuration).\n', - title='ignore', - ) - dummy: bool | None = Field( - None, - description='Indicates if the interface is a dummy interface.\ndummy is used to specify that this interface should be a virtual-only, dummy interface.\n', - title='dummy', - ) - dhcpOptions: DHCPOptions | None = Field( - None, - description='DHCP specific options.\ndhcp must be set to true for these to take effect.\n', - title='dhcpOptions', - ) - wireguard: DeviceWireguardConfig | None = Field( - None, - description='Wireguard specific configuration.\nIncludes things like private key, listen port, peers.\n', - title='wireguard', - ) - vip: DeviceVIPConfig | None = Field( - None, description='Virtual (shared) IP address configuration.\n', title='vip' - ) - - -class NetworkConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - hostname: str | None = Field( - None, - description='Used to statically set the hostname for the machine.\n', - title='hostname', - ) - interfaces: list[Device] | None = Field( - None, - description='interfaces is used to define the network interface configuration.\nBy default all network interfaces will attempt a DHCP discovery.\nThis can be further tuned through this configuration parameter.\n', - title='interfaces', - ) - nameservers: list[str] | None = Field( - None, - description='Used to statically set the nameservers for the machine.\nDefaults to 1.1.1.1 and 8.8.8.8\n', - title='nameservers', - ) - searchDomains: list[str] | None = Field( - None, - description='Used to statically set arbitrary search domains.\n', - title='searchDomains', - ) - extraHostEntries: list[ExtraHost] | None = Field( - None, - description='Allows for extra entries to be added to the /etc/hosts file\n', - title='extraHostEntries', - ) - kubespan: NetworkKubeSpan | None = Field( - None, description='Configures KubeSpan feature.\n', title='kubespan' - ) - disableSearchDomain: bool | None = Field( - None, - description='Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to false.\n', - title='disableSearchDomain', - ) - - -class RegistriesConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - mirrors: dict[constr(pattern=r'.*'), RegistryMirrorConfig] | None = Field( - None, - description='Specifies mirror configuration for each registry host namespace.\nThis setting allows to configure local pull-through caching registires,\nair-gapped installations, etc.\n\nFor example, when pulling an image with the reference example.com:123/image:v1,\nthe example.com:123 key will be used to lookup the mirror configuration.\n\nOptionally the * key can be used to configure a fallback mirror.\n\nRegistry name is the first segment of image identifier, with ‘docker.io’\nbeing default one.\n', - title='mirrors', - ) - config: dict[constr(pattern=r'.*'), RegistryConfig] | None = Field( - None, - description='Specifies TLS & auth configuration for HTTPS image registries.\nMutual TLS can be enabled with ‘clientIdentity’ option.\n\nThe full hostname and port (if not using a default port 443)\nshould be used as the key.\nThe fallback key * can’t be used for TLS configuration.\n\nTLS configuration can be skipped if registry has trusted\nserver certificate.\n', - title='config', - ) - - -class ClusterConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - id: str | None = Field( - None, - description='Globally unique identifier for this cluster (base64 encoded random 32 bytes).\n', - title='id', - ) - secret: str | None = Field( - None, - description='Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\n', - title='secret', - ) - controlPlane: ControlPlaneConfig | None = Field( - None, - description='Provides control plane specific configuration options.\n', - title='controlPlane', - ) - clusterName: str | None = Field( - None, description='Configures the cluster’s name.\n', title='clusterName' - ) - network: ClusterNetworkConfig | None = Field( - None, - description='Provides cluster specific network configuration options.\n', - title='network', - ) - token: str | None = Field( - None, - description='The bootstrap token used to join the cluster.\n', - title='token', - ) - aescbcEncryptionSecret: str | None = Field( - None, - description='A key used for the encryption of secret data at rest.\nEnables encryption with AESCBC.\n', - title='aescbcEncryptionSecret', - ) - secretboxEncryptionSecret: str | None = Field( - None, - description='A key used for the encryption of secret data at rest.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\n', - title='secretboxEncryptionSecret', - ) - ca: Ca | None = Field( - None, - description='The base64 encoded root certificate authority used by Kubernetes.\n', - title='ca', - ) - acceptedCAs: AcceptedCAs | None = Field( - None, - description='The list of base64 encoded accepted certificate authorities used by Kubernetes.\n', - title='acceptedCAs', - ) - aggregatorCA: AggregatorCA | None = Field( - None, - description='The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.\n', - title='aggregatorCA', - ) - serviceAccount: ServiceAccount | None = Field( - None, - description='The base64 encoded private key for service account token generation.\n', - title='serviceAccount', - ) - apiServer: APIServerConfig | None = Field( - None, - description='API server specific configuration options.\n', - title='apiServer', - ) - controllerManager: ControllerManagerConfig | None = Field( - None, - description='Controller manager server specific configuration options.\n', - title='controllerManager', - ) - proxy: ProxyConfig | None = Field( - None, - description='Kube-proxy server-specific configuration options\n', - title='proxy', - ) - scheduler: SchedulerConfig | None = Field( - None, - description='Scheduler server specific configuration options.\n', - title='scheduler', - ) - discovery: ClusterDiscoveryConfig | None = Field( - None, description='Configures cluster member discovery.\n', title='discovery' - ) - etcd: EtcdConfig | None = Field( - None, description='Etcd specific configuration options.\n', title='etcd' - ) - coreDNS: CoreDNS | None = Field( - None, description='Core DNS specific configuration options.\n', title='coreDNS' - ) - externalCloudProvider: ExternalCloudProviderConfig | None = Field( - None, - description='External cloud provider configuration.\n', - title='externalCloudProvider', - ) - extraManifests: list[str] | None = Field( - None, - description='A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\n', - title='extraManifests', - ) - extraManifestHeaders: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='A map of key value pairs that will be added while fetching the extraManifests.\n', - title='extraManifestHeaders', - ) - inlineManifests: list[ClusterInlineManifest] | None = Field( - None, - description='A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\n', - title='inlineManifests', - ) - adminKubeconfig: AdminKubeconfigConfig | None = Field( - None, - description='Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.\n', - title='adminKubeconfig', - ) - allowSchedulingOnControlPlanes: bool | None = Field( - None, - description='Allows running workload on control-plane nodes.\n', - title='allowSchedulingOnControlPlanes', - ) - - -class MachineConfig(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - type: TypeModel | None = Field( - None, - description='Defines the role of the machine within the cluster.\n\nControl Plane\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\nWorker\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as “join”; that value is still supported but deprecated.\n', - title='type', - ) - token: str | None = Field( - None, - description='The token is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its’ identity.\n', - title='token', - ) - ca: Ca | None = Field( - None, - description='The root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n', - title='ca', - ) - acceptedCAs: AcceptedCAs | None = Field( - None, - description='The certificates issued by certificate authorities are accepted in addition to issuing ‘ca’.\nIt is composed of a base64 encoded crt`.\n', - title='acceptedCAs', - ) - certSANs: list[str] | None = Field( - None, - description='Extra certificate subject alternative names for the machine’s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate’s SANs.\n', - title='certSANs', - ) - controlPlane: MachineControlPlaneConfig | None = Field( - None, - description='Provides machine specific control plane configuration options.\n', - title='controlPlane', - ) - kubelet: KubeletConfig | None = Field( - None, - description='Used to provide additional options to the kubelet.\n', - title='kubelet', - ) - pods: list[dict[str, Any]] | None = Field( - None, - description='Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn’t validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.\n', - title='pods', - ) - network: NetworkConfig | None = Field( - None, - description='Provides machine specific network configuration options.\n', - title='network', - ) - install: InstallConfig | None = Field( - None, - description='Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\n', - title='install', - ) - files: list[MachineFile] | None = Field( - None, - description='Allows the addition of user specified files.\nThe value of op can be create, overwrite, or append.\nIn the case of create, path must not exist.\nIn the case of overwrite, and append, path must be a valid file.\nIf an op value of append is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\n', - title='files', - ) - env: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='The env field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\n', - title='env', - ) - time: TimeConfig | None = Field( - None, - description='Used to configure the machine’s time settings.\n', - title='time', - ) - sysctls: dict[constr(pattern=r'.*'), str] | None = Field( - None, description='Used to configure the machine’s sysctls.\n', title='sysctls' - ) - sysfs: dict[constr(pattern=r'.*'), str] | None = Field( - None, description='Used to configure the machine’s sysfs.\n', title='sysfs' - ) - registries: RegistriesConfig | None = Field( - None, - description='Used to configure the machine’s container image registry mirrors.\n\nAutomatically generates matching CRI configuration for registry mirrors.\n\nThe mirrors section allows to redirect requests for images to a non-default registry,\nwhich might be a local registry or a caching mirror.\n\nThe config section provides a way to authenticate to the registry with TLS client\nidentity, provide registry CA, or authentication information.\nAuthentication information has same meaning with the corresponding field in .docker/config.json.\n\nSee also matching configuration for CRI containerd plugin.\n', - title='registries', - ) - features: FeaturesConfig | None = Field( - None, - description='Features describe individual Talos features that can be switched on or off.\n', - title='features', - ) - udev: UdevConfig | None = Field( - None, description='Configures the udev system.\n', title='udev' - ) - logging: LoggingConfig | None = Field( - None, description='Configures the logging system.\n', title='logging' - ) - kernel: KernelConfig | None = Field( - None, description='Configures the kernel.\n', title='kernel' - ) - seccompProfiles: list[MachineSeccompProfile] | None = Field( - None, - description='Configures the seccomp profiles for the machine.\n', - title='seccompProfiles', - ) - baseRuntimeSpecOverrides: dict[str, Any] | None = Field( - None, - description='Override (patch) settings in the default OCI runtime spec for CRI containers.\n\nIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.\n', - title='baseRuntimeSpecOverrides', - ) - nodeLabels: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Configures the node labels for the machine.\n\nNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see NodeRestriction admission plugin).\n', - title='nodeLabels', - ) - nodeAnnotations: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Configures the node annotations for the machine.\n', - title='nodeAnnotations', - ) - nodeTaints: dict[constr(pattern=r'.*'), str] | None = Field( - None, - description='Configures the node taints for the machine. Effect is optional.\n\nNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see NodeRestriction admission plugin).\n', - title='nodeTaints', - ) - - -class Config(BaseModel): - model_config = ConfigDict( - extra='forbid', - ) - version: Version | None = Field( - None, - description='Indicates the schema used to decode the contents.\n', - title='version', - ) - debug: bool | None = Field( - None, - description='Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\nNote: To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\n', - title='debug', - ) - machine: MachineConfig | None = Field( - None, - description='Provides machine specific configuration options.\n', - title='machine', - ) - cluster: ClusterConfig | None = Field( - None, - description='Provides cluster specific configuration options.\n', - title='cluster', - ) diff --git a/talos/clusters/testing.yaml b/talos/clusters/testing.yaml new file mode 100644 index 0000000..f0b82c3 --- /dev/null +++ b/talos/clusters/testing.yaml @@ -0,0 +1,64 @@ +# yaml-language-server: $schema=../../schemas/cluster.json +version: + kubernetes: 1.34.1 + talos: 1.11.3 +clusterEnv: staging +controlPlaneIp: 192.168.1.100 +secretsFile: testing/secrets.yaml +nodes: + - testing/talos-vm + +base: + kernelArgs: + - talos.platform=metal + - console=tty0 + - init_on_alloc=1 + - init_on_free=1 + - slab_nomerge + - pti=on + - consoleblank=0 + - nvme_core.io_timeout=4294967295 + - printk.devkmsg=on + - selinux=1 + - lockdown=confidentiality + patches: + all: + - system/hostname.yaml + - system/install-disk.yaml + - system/network.yaml + - networking/vip.yaml + - networking/tailscale.yaml + - networking/cilium.yaml + - spegel.yaml + - storage/longhorn.yaml + - storage/longhorn/user-volume.yaml + - storage/local-path-provisioner/user-volume.yaml + - storage/limit-ephemeral.yaml + - metrics/all.yaml + controlPlane: + - system/allow-control-plane-workloads.yaml + - sops.yaml + - flux/cluster-variables.yaml + - metrics/control-plane.yaml + - networking/gateway-api.yaml +default: + arch: amd64 + schematic: default.yaml + network: + interface: enp1s0 + netmask: 255.255.252.0 + gateway: 192.168.1.1 + dns: + - 1.1.1.1 + - 8.8.8.8 + tailscale: + server: https://headscale.huizinga.dev + authKey: + file: tailscale.key + advertiseRoutes: true + ntp: nl.pool.ntp.org + sops: + file: testing/age.key + install: + auto: true + disk: /dev/vda diff --git a/talos/nodes/testing/talos-vm.yaml b/talos/nodes/testing/talos-vm.yaml new file mode 100644 index 0000000..74895de --- /dev/null +++ b/talos/nodes/testing/talos-vm.yaml @@ -0,0 +1,6 @@ +# yaml-language-server: $schema=../../../schemas/node.json +type: controlPlane +install: + serial: talos-vm +network: + ip: 192.168.1.2 diff --git a/patches/cluster-variables.yaml b/talos/patches/flux/cluster-variables.yaml similarity index 53% rename from patches/cluster-variables.yaml rename to talos/patches/flux/cluster-variables.yaml index 63cd3de..ca48e14 100644 --- a/patches/cluster-variables.yaml +++ b/talos/patches/flux/cluster-variables.yaml @@ -4,17 +4,15 @@ cluster: - name: cluster-variables contents: | --- - # yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/refs/heads/master/v1.34.1-standalone-strict/namespace.json apiVersion: v1 kind: Namespace metadata: name: flux-system --- - # yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/refs/heads/master/v1.34.1-standalone-strict/configmap.json apiVersion: v1 kind: ConfigMap metadata: name: cluster-variables namespace: flux-system data: - cluster_env: "{%- if node.cluster.production %} production {%- else %} staging {%- endif %}" + cluster_env: {{ cluster.clusterEnv }} diff --git a/patches/metrics.yaml b/talos/patches/metrics/all.yaml similarity index 100% rename from patches/metrics.yaml rename to talos/patches/metrics/all.yaml diff --git a/patches/metrics-cluster.yaml b/talos/patches/metrics/control-plane.yaml similarity index 100% rename from patches/metrics-cluster.yaml rename to talos/patches/metrics/control-plane.yaml diff --git a/patches/cilium.yaml b/talos/patches/networking/cilium.yaml similarity index 100% rename from patches/cilium.yaml rename to talos/patches/networking/cilium.yaml diff --git a/patches/gateway-api.yaml b/talos/patches/networking/gateway-api.yaml similarity index 100% rename from patches/gateway-api.yaml rename to talos/patches/networking/gateway-api.yaml diff --git a/talos/patches/networking/tailscale.yaml b/talos/patches/networking/tailscale.yaml new file mode 100644 index 0000000..d52e94d --- /dev/null +++ b/talos/patches/networking/tailscale.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json +apiVersion: v1alpha1 +kind: ExtensionServiceConfig +name: tailscale +environment: + - TS_AUTHKEY={{ node.network.tailscale.authKey }} + - TS_EXTRA_ARGS={% if node.network.tailscale.server %}--login-server {{ node.network.tailscale.server }} {% endif %}--advertise-tags=tag:cluster-{{ cluster.name }} + - TS_ROUTES={% if node.network.tailscale.advertiseRoutes %}{{node.network.ip}}/{{ node.network.netmask | to_prefix }}{% endif %} diff --git a/patches/vip.yaml b/talos/patches/networking/vip.yaml similarity index 69% rename from patches/vip.yaml rename to talos/patches/networking/vip.yaml index bf19547..c9db9b9 100644 --- a/patches/vip.yaml +++ b/talos/patches/networking/vip.yaml @@ -2,6 +2,6 @@ machine: network: interfaces: - - interface: "{{node.interface}}" + - interface: "{{node.network.interface}}" vip: - ip: "{{node.cluster.controlPlaneIp}}" + ip: "{{cluster.controlPlaneIp}}" diff --git a/patches/sops.yaml b/talos/patches/sops.yaml similarity index 88% rename from patches/sops.yaml rename to talos/patches/sops.yaml index 7449275..ffd8613 100644 --- a/patches/sops.yaml +++ b/talos/patches/sops.yaml @@ -15,4 +15,4 @@ cluster: namespace: flux-system data: age.agekey: | - {{ helper.load_secret(node.cluster.sopsKeyFile) }} + {{ node.sops | indent(6*2) }} diff --git a/patches/spegel.yaml b/talos/patches/spegel.yaml similarity index 100% rename from patches/spegel.yaml rename to talos/patches/spegel.yaml diff --git a/patches/limit-ephemeral.yaml b/talos/patches/storage/limit-ephemeral.yaml similarity index 100% rename from patches/limit-ephemeral.yaml rename to talos/patches/storage/limit-ephemeral.yaml diff --git a/patches/local-path-provisioner-volume.yaml b/talos/patches/storage/local-path-provisioner/user-volume.yaml similarity index 100% rename from patches/local-path-provisioner-volume.yaml rename to talos/patches/storage/local-path-provisioner/user-volume.yaml diff --git a/patches/longhorn.yaml b/talos/patches/storage/longhorn.yaml similarity index 100% rename from patches/longhorn.yaml rename to talos/patches/storage/longhorn.yaml diff --git a/patches/longhorn-user-volume.yaml b/talos/patches/storage/longhorn/user-volume.yaml similarity index 100% rename from patches/longhorn-user-volume.yaml rename to talos/patches/storage/longhorn/user-volume.yaml diff --git a/patches/openebs.yaml b/talos/patches/storage/openebs.yaml similarity index 100% rename from patches/openebs.yaml rename to talos/patches/storage/openebs.yaml diff --git a/patches/allow-control-plane-workloads.yaml b/talos/patches/system/allow-control-plane-workloads.yaml similarity index 100% rename from patches/allow-control-plane-workloads.yaml rename to talos/patches/system/allow-control-plane-workloads.yaml diff --git a/patches/hostname.yaml b/talos/patches/system/hostname.yaml similarity index 100% rename from patches/hostname.yaml rename to talos/patches/system/hostname.yaml diff --git a/patches/install-disk.yaml b/talos/patches/system/install-disk.yaml similarity index 83% rename from patches/install-disk.yaml rename to talos/patches/system/install-disk.yaml index d9d983a..227efb7 100644 --- a/patches/install-disk.yaml +++ b/talos/patches/system/install-disk.yaml @@ -1,4 +1,4 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json machine: install: - disk: "{{node.installDisk}}" + disk: "{{node.install.disk}}" diff --git a/patches/network.yaml b/talos/patches/system/network.yaml similarity index 68% rename from patches/network.yaml rename to talos/patches/system/network.yaml index ab9d458..db0f37c 100644 --- a/patches/network.yaml +++ b/talos/patches/system/network.yaml @@ -2,10 +2,10 @@ machine: network: interfaces: - - interface: "{{node.interface}}" + - interface: "{{node.network.interface}}" dhcp: false addresses: - - "{{node.ip}}" + - "{{node.network.ip}}" routes: - network: 0.0.0.0/0 - gateway: "{{node.gateway}}" + gateway: "{{node.network.gateway}}" diff --git a/talos/schematics/default.yaml b/talos/schematics/default.yaml new file mode 100644 index 0000000..1744e40 --- /dev/null +++ b/talos/schematics/default.yaml @@ -0,0 +1,8 @@ +customization: + systemExtensions: + officialExtensions: + - siderolabs/iscsi-tools + - siderolabs/util-linux-tools + - siderolabs/intel-ucode + - siderolabs/i915 + - siderolabs/tailscale diff --git a/talos/secrets/tailscale.key b/talos/secrets/tailscale.key new file mode 100644 index 0000000..89f0b3a Binary files /dev/null and b/talos/secrets/tailscale.key differ diff --git a/nodes/testing/_age.agekey b/talos/secrets/testing/age.key similarity index 100% rename from nodes/testing/_age.agekey rename to talos/secrets/testing/age.key diff --git a/nodes/testing/_secrets.yaml b/talos/secrets/testing/secrets.yaml similarity index 100% rename from nodes/testing/_secrets.yaml rename to talos/secrets/testing/secrets.yaml diff --git a/templates/boot.ipxe b/templates/boot.ipxe index 48d1263..ef71685 100644 --- a/templates/boot.ipxe +++ b/templates/boot.ipxe @@ -1,3 +1,4 @@ +{% set httpUrl = "http://192.168.1.1:8000" -%} #!ipxe dhcp @@ -8,17 +9,13 @@ goto node_${serial} || exit # Default behavior (non install mode) is to exit iPXE script {% for node in nodes %} -{%- if node.install -%} -# {{ node.filename }} -:node_{{ node.serial }} -{% set ipArg = "ip=" ~ [node.ip, "" , node.gateway, node.netmask, node.hostname, node.interface, "", node.dns[0], node.dns[1], node.ntp]|join(":") -%} -{% set kernelArgs = [ipArg, node.kernelArgs|join(" "), node.extraKernelArgs|join(" ")] -%} -{% if node.autoInstall %} -{% do kernelArgs.append("talos.config=" ~ config.server.httpUrl ~ "/configs/" ~ node.filename ~ ".yaml") %} -{% endif %} +{%- if node.install.serial -%} +# {{ cluster.name }}/{{ node.hostname }} +:node_{{ node.install.serial }} +{% set ipArg = "ip=" ~ [node.network.ip, "" , node.network.gateway, node.network.netmask, node.hostname, node.network.interface, "", node.network.dns[0], node.network.dns[1], node.ntp]|join(":") -%} imgfree -kernel https://pxe.factory.talos.dev/image/{{ node.schematicId }}/v{{ node.talosVersion }}/kernel-{{ node.arch }} {{ kernelArgs|join(" ") }} -initrd https://pxe.factory.talos.dev/image/{{ node.schematicId }}/v{{ node.talosVersion }}/initramfs-{{ node.arch }}.xz +kernel https://pxe.factory.talos.dev/image/{{ node.schematic }}/v{{ cluster.version.talos }}/kernel-{{ node.arch }} {{ ipArg }} {{ node.kernelArgs|join(" ") }} {% if node.install.auto %}talos.config={{httpUrl}}/configs/{{cluster.name}}/{{node.hostname}}.yaml{% endif +%} +initrd https://pxe.factory.talos.dev/image/{{ node.schematic }}/v{{ cluster.version.talos }}/initramfs-{{ node.arch }}.xz boot {% endif %} {% endfor %} diff --git a/templates/dnsmasq.conf b/templates/dnsmasq.conf index 0f39dc0..8c8e082 100644 --- a/templates/dnsmasq.conf +++ b/templates/dnsmasq.conf @@ -1,4 +1,4 @@ -{% set tftpIp = config.server.tftpIp -%} +{% set tftpIp = "192.168.1.1" -%} enable-tftp tftp-root=/tftproot diff --git a/templates/generate_configs.sh b/templates/generate_configs.sh index fb5a3d8..774e304 100644 --- a/templates/generate_configs.sh +++ b/templates/generate_configs.sh @@ -4,36 +4,32 @@ CONFIGS={{ root }}/configs # Generate the configuration for each node {% for node in nodes -%} -talosctl gen config {{ node.cluster.name }} https://{{ node.cluster.controlPlaneIp }}:6443 -f \ - --with-secrets {{ node.cluster.secretsFile }} \ - --talos-version v{{ node.talosVersion }} \ - --kubernetes-version v{{ node.kubernetesVersion }} \ +talosctl gen config {{ cluster.name }} https://{{ cluster.controlPlaneIp }}:6443 -f \ + --with-secrets {{ cluster.secretsFile }} \ + --talos-version v{{ cluster.version.talos }} \ + --kubernetes-version v{{ cluster.version.kubernetes }} \ --output-types {{ node.type }} \ - --install-image factory.talos.dev/metal-installer/{{ node.schematicId }}:v{{ node.talosVersion }} \ - {% for patch in node.patches -%} + --install-image factory.talos.dev/metal-installer/{{ node.schematic }}:v{{ cluster.version.talos }} \ + {% for patch in node.patches.all -%} {# The double call to tojson is needed to properly escape the patch (object -> json -> string) -#} - --config-patch {{ helper.model_dump_json(patch)|tojson }} \ + --config-patch {{ patch|tojson|tojson }} \ {% endfor -%} - {% for patch in node.patchesControlPlane -%} - --config-patch-control-plane {{ helper.model_dump_json(patch)|tojson }} \ + {% for patch in node.patches.controlPlane -%} + --config-patch-control-plane {{ patch|tojson|tojson }} \ {% endfor -%} --with-docs=false \ --with-examples=false \ - -o ${CONFIGS}/{{ node.filename }}.yaml + -o ${CONFIGS}/{{ cluster.name }}/{{ node.hostname }}.yaml {% endfor %} # Generate the talosconfig file for each cluster -{% for cluster in clusters -%} talosctl gen config {{ cluster.name }} https://{{ cluster.controlPlaneIp }}:6443 -f \ --with-secrets {{ cluster.secretsFile }} \ --output-types talosconfig \ -o ${CONFIGS}/{{ cluster.name }}/talosconfig -{% endfor %} # Create merged talosconfig TALOSCONFIG=${CONFIGS}/talosconfig rm -f ${TALOSCONFIG} -{% for cluster in clusters -%} talosctl config --talosconfig=${CONFIGS}/{{ cluster.name }}/talosconfig endpoint {{ cluster.controlPlaneIp }} talosctl config --talosconfig=${TALOSCONFIG} merge ${CONFIGS}/{{ cluster.name }}/talosconfig -{% endfor %} diff --git a/templates/source.sh b/templates/source.sh index 19faf02..1e41351 100644 --- a/templates/source.sh +++ b/templates/source.sh @@ -1,6 +1,2 @@ export TALOSCONFIG={{ root }}/configs/talosconfig -{% set paths = [] %} -{%- for cluster in clusters -%} - {%- do paths.append(root ~ "/configs/" ~ cluster.name ~ "/kubeconfig") -%} -{% endfor -%} -export KUBECONFIG={{ paths|join(":") }} +export KUBECONFIG={{ root }}/configs/{{ cluster.name }}/kubeconfig diff --git a/tools/render b/tools/render deleted file mode 100755 index fbfc9c5..0000000 --- a/tools/render +++ /dev/null @@ -1,298 +0,0 @@ -#!/usr/bin/env -S uv run --script -# vim: set filetype=python : - -# Adapted from: https://enix.io/en/blog/pxe-talos/ - -import base64 -import functools -import pathlib -import sys -from typing import Annotated, Any, List, Literal - -import git -import requests -import yaml -from jinja2 import Environment, FileSystemLoader, StrictUndefined, Template -from mergedeep import Strategy, merge -from netaddr import IPAddress -from pydantic import ( - BaseModel, - BeforeValidator, - ConfigDict, - HttpUrl, - IPvAnyAddress, - ValidationInfo, -) -from pydantic_extra_types.semantic_version import SemanticVersion - -from models import Model as TalosModel - -REPO = git.Repo(sys.path[0], search_parent_directories=True) -assert REPO.working_dir is not None - -ROOT = pathlib.Path(REPO.working_dir) - -NODES = ROOT.joinpath("nodes") -SCHEMATICS = ROOT.joinpath("schematics") -RENDERED = ROOT.joinpath("rendered") - -EXTENSIONS = ["jinja2.ext.do"] - -PATCHES = Environment( - loader=FileSystemLoader(ROOT.joinpath("patches")), - undefined=StrictUndefined, - extensions=EXTENSIONS, -) -TEMPLATES = Environment( - loader=FileSystemLoader(ROOT.joinpath("templates")), - undefined=StrictUndefined, - extensions=EXTENSIONS, -) - - -class ServerConfig(BaseModel): - model_config = ConfigDict(strict=True, extra="forbid") - - tftpIp: IPvAnyAddress - httpUrl: HttpUrl - - -class TailscaleConfig(BaseModel): - model_config = ConfigDict(strict=True, extra="forbid") - - loginServer: HttpUrl - authKey: str - - -class Config(BaseModel): - model_config = ConfigDict(strict=True, extra="forbid") - - server: ServerConfig - tailscale: TailscaleConfig - - -class Cluster(BaseModel): - model_config = ConfigDict(strict=True, extra="forbid") - - name: str - production: bool - controlPlaneIp: IPvAnyAddress - # TODO: Path - secretsFile: str - sopsKeyFile: str - - -# When we try to make a deep copy of the nodes dict it fails as the Template -# does not implement __deepcopy__, so this wrapper type facilitates that -class TemplateWrapper: - def __init__(self, template: Template): - self.template = template - - def __deepcopy__(self, memo): - # NOTE: This is not a true deepcopy, but since we know we won't modify - # the template this is fine. - return self - - -def render_patch(wrapper: Any, info: ValidationInfo): - if not isinstance(wrapper, TemplateWrapper): - raise RuntimeError("Expected TemplateWrapper") - args = (info.context or {}) | {"node": info.data} - try: - rendered = wrapper.template.render(args) - except Exception as e: - e.add_note(f"While rendering for: {args['node']['hostname']}") - raise e - # Parse the rendered yaml - return yaml.safe_load(rendered) - - -class Node(BaseModel): - model_config = ConfigDict(strict=True, extra="forbid") - - schematicId: str - arch: Literal["amd64"] - talosVersion: SemanticVersion - kubernetesVersion: SemanticVersion - - kernelArgs: List[str] - extraKernelArgs: List[str] - dns: List[IPvAnyAddress] - # TODO: Validation - ntp: str - - install: bool - advertiseRoutes: bool - - serial: str - interface: str - ip: IPvAnyAddress - netmask: IPvAnyAddress - gateway: IPvAnyAddress - # TODO: Extra validation - installDisk: str - autoInstall: bool - cluster: Cluster - hostname: str - filename: str - - type: Literal["controlplane", "worker"] - - patches: List[Annotated[TalosModel, BeforeValidator(render_patch)]] - patchesControlPlane: List[Annotated[TalosModel, BeforeValidator(render_patch)]] - - -def tailscale_subnet(gateway: IPvAnyAddress, netmask: IPvAnyAddress): - netmask_bits = IPAddress(netmask.exploded).netmask_bits() - return f"{IPAddress(gateway.exploded) & IPAddress(netmask.exploded)}/{netmask_bits}" - - -def load_secret(path: str): - with open(path) as f: - return base64.b64encode(f.read().encode()).decode() - - -def model_dump_json(model: BaseModel): - return model.model_dump_json(exclude_none=True) - - -@functools.cache -def get_schematic_id(schematic: str): - """Lookup the schematic id associated with a given schematic""" - r = requests.post("https://factory.talos.dev/schematics", data=schematic) - r.raise_for_status() - data = r.json() - return data["id"] - - -def schematic_constructor(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode): - """Load specified schematic file and get the assocatied schematic id""" - schematic_name = loader.construct_yaml_str(node) - try: - schematic = SCHEMATICS.joinpath(schematic_name).with_suffix(".yaml").read_text() - return get_schematic_id(schematic) - except Exception: - raise yaml.MarkedYAMLError("Failed to load schematic", node.start_mark) - - -def template_constructor(environment: Environment): - def inner(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode): - patch_name = loader.construct_scalar(node) - try: - template = environment.get_template(f"{patch_name}.yaml") - return TemplateWrapper(template) - except Exception: - raise yaml.MarkedYAMLError("Failed to load patch", node.start_mark) - - return inner - - -def realpath_constructor(directory: pathlib.Path): - def inner(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode): - try: - realpath = directory.joinpath(loader.construct_scalar(node)).resolve( - strict=True - ) - return str(realpath) - except Exception: - raise yaml.MarkedYAMLError("Failed to get real path", node.start_mark) - - return inner - - -def get_loader(directory: pathlib.Path): - """Add special constructors to yaml loader""" - loader = yaml.SafeLoader - loader.add_constructor("!realpath", realpath_constructor(directory)) - loader.add_constructor("!schematic", schematic_constructor) - loader.add_constructor("!patch", template_constructor(PATCHES)) - - return loader - - -@functools.cache -def get_defaults(directory: pathlib.Path, root: pathlib.Path): - """Compute the defaults from the provided directory and parents.""" - try: - with open(directory.joinpath("_defaults.yaml")) as fyaml: - yml_data = yaml.load(fyaml, Loader=get_loader(directory)) - except OSError: - yml_data = {} - - # Stop recursion when reaching root directory - if directory != root: - return merge( - {}, - get_defaults(directory.parent, root), - yml_data, - strategy=Strategy.TYPESAFE_REPLACE, - ) - else: - return yml_data - - -def walk_files(root: pathlib.Path): - """Get all files that do not start with and underscore""" - for dirpath, _dirnames, filenames in root.walk(): - for fn in filenames: - if not fn.startswith("_"): - yield dirpath.joinpath(fn) - - -def main(): - with open(ROOT.joinpath("config.yaml")) as fyaml: - config = yaml.safe_load(fyaml) - - with open(ROOT.joinpath("secrets.yaml")) as fyaml: - merge(config, yaml.safe_load(fyaml), strategy=Strategy.TYPESAFE_REPLACE) - - config = Config(**config) - - template_args = { - "config": config, - "root": ROOT, - "helper": { - "tailscale_subnet": tailscale_subnet, - "load_secret": load_secret, - "model_dump_json": model_dump_json, - }, - } - - nodes: List[Node] = [] - for fullname in walk_files(NODES): - filename = str(fullname.relative_to(NODES).parent) + "/" + fullname.stem - - with open(fullname) as fyaml: - yml_data = yaml.load(fyaml, Loader=get_loader(fullname.parent)) - yml_data = merge( - {}, - get_defaults(fullname.parent, NODES), - yml_data, - strategy=Strategy.TYPESAFE_REPLACE, - ) - yml_data["hostname"] = fullname.stem - yml_data["filename"] = filename - node = Node.model_validate(yml_data, context=template_args) - nodes.append(node) - - # HACK: We can't hash a dict, so we first convert it to json, the use set - # to get all the unique entries, and then convert it back - # NOTE: This assumes that all nodes in the cluster use the same definition for the cluster - clusters = list( - Cluster.model_validate_json(cluster) - for cluster in set(node.cluster.model_dump_json() for node in nodes) - ) - - template_args |= {"nodes": nodes, "clusters": clusters} - - RENDERED.mkdir(exist_ok=True) - for template_name in TEMPLATES.list_templates(): - template = TEMPLATES.get_template(template_name) - - rendered = template.render(template_args) - with open(RENDERED.joinpath(template_name), "w") as f: - f.write(rendered) - - -if __name__ == "__main__": - main() diff --git a/tools/update_models b/tools/update_models deleted file mode 100755 index d01c4fe..0000000 --- a/tools/update_models +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -ROOT=$(git rev-parse --show-toplevel) - -MODELS_DIR=${ROOT}/src/models -rm -rf ${MODELS_DIR} - -SCHEMA_FILE=$(mktemp schema.XXX.json) -function cleanup() { - rm -rf ${SCHEMA_FILE} -} -trap cleanup EXIT - -curl https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json > ${SCHEMA_FILE} -uvx --from datamodel-code-generator datamodel-codegen --input ${SCHEMA_FILE} --input-file-type jsonschema --output ${MODELS_DIR} --output-model pydantic_v2.BaseModel diff --git a/uv.lock b/uv.lock deleted file mode 100644 index af1f4fe..0000000 --- a/uv.lock +++ /dev/null @@ -1,386 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.13" - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, -] - -[[package]] -name = "bootstrap" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "gitpython" }, - { name = "jinja2" }, - { name = "mergedeep" }, - { name = "netaddr" }, - { name = "pydantic" }, - { name = "pydantic-extra-types" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "semver" }, -] - -[package.metadata] -requires-dist = [ - { name = "gitpython", specifier = "==3.1.45" }, - { name = "jinja2", specifier = "==3.1.6" }, - { name = "mergedeep", specifier = "==1.3.4" }, - { name = "netaddr", specifier = "==1.3.0" }, - { name = "pydantic", specifier = ">=2.12.5" }, - { name = "pydantic-extra-types", specifier = ">=2.11.0" }, - { name = "pyyaml", specifier = "==6.0.3" }, - { name = "requests", specifier = "==2.32.5" }, - { name = "semver", specifier = ">=3.0.4" }, -] - -[[package]] -name = "certifi" -version = "2025.10.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, -] - -[[package]] -name = "gitpython" -version = "3.1.45" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076, upload-time = "2025-07-24T03:45:54.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "mergedeep" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, -] - -[[package]] -name = "netaddr" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/90/188b2a69654f27b221fba92fda7217778208532c962509e959a9cee5229d/netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a", size = 2260504, upload-time = "2024-05-28T21:30:37.743Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/cc/f4fe2c7ce68b92cbf5b2d379ca366e1edae38cccaad00f69f529b460c3ef/netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe", size = 2262023, upload-time = "2024-05-28T21:30:34.191Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, -] - -[[package]] -name = "pydantic-extra-types" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "requests" -version = "2.32.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, -] - -[[package]] -name = "semver" -version = "3.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730, upload-time = "2025-01-24T13:19:27.617Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912, upload-time = "2025-01-24T13:19:24.949Z" }, -] - -[[package]] -name = "smmap" -version = "5.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, -]