diff --git a/.gitignore b/.gitignore index ad67955..e1d44e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# SSH host key (generated on first server run) +host_key + # Generated by Cargo # will have compiled files and executables debug diff --git a/Cargo.lock b/Cargo.lock index 7d4b154..f01fb7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,47 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array 0.14.7", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -17,12 +58,33 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures 0.2.17", + "password-hash", +] + [[package]] name = "atomic" version = "0.6.1" @@ -38,12 +100,64 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" +dependencies = [ + "blowfish", + "pbkdf2", + "sha2 0.10.9", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -71,13 +185,50 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", ] [[package]] @@ -92,6 +243,18 @@ version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +[[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 = "castaway" version = "0.2.4" @@ -101,6 +264,27 @@ dependencies = [ "rustversion", ] +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -113,6 +297,55 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5417da527aa9bf6a1e10a781231effd1edd3ee82f27d5f8529ac9b279babce96" + [[package]] name = "compact_str" version = "0.9.0" @@ -127,6 +360,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "convert_case" version = "0.10.0" @@ -136,6 +381,23 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-models" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444" +dependencies = [ + "hax-lib", + "pastey", + "rand 0.9.2", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -145,6 +407,24 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossterm" version = "0.29.0" @@ -172,16 +452,61 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.7.0-rc.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37387ceb32048ff590f2cbd24d8b05fffe63c3f69a5cfa089d4f722ca4385a19" +dependencies = [ + "ctutils", + "num-traits", + "rand_core 0.10.0-rc-3", + "serdect", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "crypto-primes" +version = "0.7.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79c98a281f9441200b24e3151407a629bfbe720399186e50516da939195e482" +dependencies = [ + "crypto-bigint 0.7.0-rc.18", + "libm", + "rand_core 0.10.0-rc-3", +] + [[package]] name = "csscolorparser" version = "0.6.2" @@ -192,6 +517,51 @@ dependencies = [ "phf", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctutils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758e5ed90be3c8abff7f9a6f37ab7f6d8c59c2210d448b81f3f508134aec84e4" +dependencies = [ + "cmov", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + [[package]] name = "darling" version = "0.23.0" @@ -226,12 +596,51 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + [[package]] name = "deltae" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468 0.7.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +dependencies = [ + "const-oid 0.10.2", + "pem-rfc7468 1.0.0", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.6" @@ -269,8 +678,21 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.1", ] [[package]] @@ -282,12 +704,90 @@ dependencies = [ "litrs", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.10", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.2.0", + "spki 0.7.3", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.2.0", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2 0.10.9", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.115", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -301,7 +801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -323,6 +823,22 @@ dependencies = [ "regex", ] +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filedescriptor" version = "0.8.3" @@ -334,6 +850,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "finl_unicode" version = "1.4.0" @@ -346,6 +868,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -358,6 +890,100 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[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-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[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-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -366,6 +992,31 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "generic-array 0.14.7", + "rustversion", + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", ] [[package]] @@ -380,6 +1031,27 @@ dependencies = [ "wasip2", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -391,6 +1063,43 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hax-lib" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86" +dependencies = [ + "hax-lib-macros", + "num-bigint", + "num-traits", +] + +[[package]] +name = "hax-lib-macros" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1" +dependencies = [ + "hax-lib-macros-types", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "hax-lib-macros-types" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "heck" version = "0.5.0" @@ -403,6 +1112,63 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hybrid-array" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +dependencies = [ + "typenum", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -418,6 +1184,16 @@ dependencies = [ "rustversion", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array 0.14.7", +] + [[package]] name = "instability" version = "0.3.11" @@ -431,6 +1207,37 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "internal-russh-forked-ssh-key" +version = "0.6.16+upstream-0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe44f2bbd99fcb302e246e2d6bcf51aeda346d02a365f80296a07a8c711b6da6" +dependencies = [ + "argon2", + "bcrypt-pbkdf", + "digest 0.11.2", + "ecdsa", + "ed25519-dalek", + "hex", + "hmac", + "num-bigint-dig", + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "sha1 0.10.6", + "sha1 0.11.0", + "sha2 0.10.9", + "signature 2.2.0", + "signature 3.0.0-rc.6", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "itertools" version = "0.14.0" @@ -446,12 +1253,24 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[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.85" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -478,6 +1297,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -485,6 +1307,78 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +[[package]] +name = "libcrux-intrinsics" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ee7ef66569dd7516454fe26de4e401c0c62073929803486b96744594b9632" +dependencies = [ + "core-models", + "hax-lib", +] + +[[package]] +name = "libcrux-ml-kem" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6a88086bf11bd2ec90926c749c4a427f2e59841437dbdede8cde8a96334ab" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-secrets", + "libcrux-sha3", + "libcrux-traits", + "rand 0.9.2", + "tls_codec", +] + +[[package]] +name = "libcrux-platform" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778" +dependencies = [ + "libc", +] + +[[package]] +name = "libcrux-secrets" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4dbbf6bc9f2bc0f20dc3bea3e5c99adff3bdccf6d2a40488963da69e2ec307" +dependencies = [ + "hax-lib", +] + +[[package]] +name = "libcrux-sha3" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2400bec764d1c75b8a496d5747cffe32f1fb864a12577f0aca2f55a92021c962" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-traits", +] + +[[package]] +name = "libcrux-traits" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034" +dependencies = [ + "libcrux-secrets", + "rand 0.9.2", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "line-clipping" version = "0.3.5" @@ -540,6 +1434,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.8.0" @@ -567,6 +1467,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.1.1" @@ -576,7 +1486,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -602,6 +1512,33 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "serde", + "smallvec", +] + [[package]] name = "num-conv" version = "0.2.0" @@ -619,6 +1556,26 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -643,6 +1600,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "ordered-float" version = "4.6.0" @@ -652,6 +1615,63 @@ dependencies = [ "num-traits", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct 0.2.0", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "pageant" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b537f975f6d8dcf48db368d7ec209d583b015713b5df0f5d92d2631e4ff5595" +dependencies = [ + "byteorder", + "bytes", + "delegate", + "futures", + "log", + "rand 0.8.5", + "sha2 0.10.9", + "thiserror 1.0.69", + "tokio", + "windows", + "windows-strings", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -675,6 +1695,51 @@ dependencies = [ "windows-link", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "pest" version = "2.8.6" @@ -715,7 +1780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -745,7 +1810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -770,6 +1835,82 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der 0.8.0", + "spki 0.8.0-rc.4", +] + +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes", + "cbc", + "der 0.7.10", + "pbkdf2", + "scrypt", + "sha2 0.10.9", + "spki 0.7.3", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.10", + "pkcs5", + "rand_core 0.6.4", + "spki 0.7.3", +] + +[[package]] +name = "pkcs8" +version = "0.11.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" +dependencies = [ + "der 0.8.0", + "spki 0.8.0-rc.4", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -782,6 +1923,46 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.115", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -812,7 +1993,39 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_core", + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[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 0.9.5", ] [[package]] @@ -820,6 +2033,24 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[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 = "rand_core" +version = "0.10.0-rc-3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66ee92bc15280519ef199a274fe0cafff4245d31bc39aaa31c011ad56cb1f05" [[package]] name = "ratatui" @@ -944,6 +2175,135 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[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 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.10.0-rc.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2b1eacbc34fbaf77f6f1db1385518446008d49b9f9f59dc9d1340fce4ca9e" +dependencies = [ + "const-oid 0.10.2", + "crypto-bigint 0.7.0-rc.18", + "crypto-primes", + "digest 0.11.2", + "pkcs1", + "pkcs8 0.11.0-rc.11", + "rand_core 0.10.0-rc-3", + "sha2 0.11.0", + "signature 3.0.0-rc.6", + "spki 0.8.0-rc.4", + "zeroize", +] + +[[package]] +name = "russh" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f6ce4f5d5105b934cfb4b8b3028aab4d5dcdff863cb8dda9edd06d39b8c4e8" +dependencies = [ + "aes", + "aws-lc-rs", + "bitflags 2.10.0", + "block-padding", + "byteorder", + "bytes", + "cbc", + "ctr", + "curve25519-dalek", + "data-encoding", + "delegate", + "der 0.7.10", + "digest 0.10.7", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "enum_dispatch", + "flate2", + "futures", + "generic-array 1.3.5", + "getrandom 0.2.17", + "hex-literal", + "hmac", + "inout", + "internal-russh-forked-ssh-key", + "libcrux-ml-kem", + "log", + "md5", + "num-bigint", + "p256", + "p384", + "p521", + "pageant", + "pbkdf2", + "pkcs1", + "pkcs5", + "pkcs8 0.10.2", + "rand 0.9.2", + "rand_core 0.10.0-rc-3", + "ring", + "rsa", + "russh-cryptovec", + "russh-util", + "sec1", + "sha1 0.10.6", + "sha2 0.10.9", + "signature 2.2.0", + "spki 0.7.3", + "ssh-encoding", + "subtle", + "thiserror 2.0.18", + "tokio", + "typenum", + "zeroize", +] + +[[package]] +name = "russh-cryptovec" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc109749696a6853cf2c3fba3537635264f3519a78a9f43c6b08c91edc024384" +dependencies = [ + "log", + "nix", + "ssh-encoding", + "windows-sys 0.59.0", +] + +[[package]] +name = "russh-util" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668424a5dde0bcb45b55ba7de8476b93831b4aa2fa6947e145f3b053e22c60b6" +dependencies = [ + "chrono", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -963,7 +2323,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -978,12 +2338,46 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2 0.10.9", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.10", + "generic-array 0.14.7", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.27" @@ -1010,35 +2404,101 @@ dependencies = [ ] [[package]] -name = "serde_derive" -version = "1.0.228" +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[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 = "serdect" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06" +dependencies = [ + "base16ct 1.0.0", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.2", +] + +[[package]] +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.115", + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", ] [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.3.0", + "digest 0.11.2", ] [[package]] name = "shellshop" version = "0.1.0" dependencies = [ + "anyhow", + "bytes", "crossterm", "ratatui", + "russh", + "tokio", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.18" @@ -1070,18 +2530,115 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "3.0.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a96996ccff7dfa16f052bd995b4cecc72af22c35138738dc029f0ead6608d" +dependencies = [ + "digest 0.11.2", + "rand_core 0.10.0-rc-3", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "siphasher" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.10", +] + +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der 0.8.0", +] + +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "aes", + "aes-gcm", + "cbc", + "chacha20", + "cipher", + "ctr", + "poly1305", + "ssh-encoding", + "subtle", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "bytes", + "pem-rfc7468 0.7.0", + "sha2 0.10.9", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1115,6 +2672,12 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1183,7 +2746,7 @@ dependencies = [ "pest", "pest_derive", "phf", - "sha2", + "sha2 0.10.9", "signal-hook", "siphasher", "terminfo", @@ -1261,6 +2824,55 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + [[package]] name = "typenum" version = "1.19.0" @@ -1302,6 +2914,28 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1315,7 +2949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ "atomic", - "getrandom", + "getrandom 0.3.4", "js-sys", "wasm-bindgen", ] @@ -1352,9 +2986,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -1363,11 +2997,21 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1375,9 +3019,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -1388,9 +3032,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -1411,9 +3055,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" dependencies = [ - "getrandom", + "getrandom 0.3.4", "mac_address", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", "uuid", ] @@ -1489,12 +3133,125 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -1504,8 +3261,127 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index ef46dde..5572693 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,12 @@ description = "A terminal based shopping TUI that runs over SSH and allow paymen edition = "2021" [dependencies] +anyhow = "1" +bytes = "1" crossterm = "0.29.0" ratatui = "0.30.0" +russh = { version = "0.58", features = ["ring"] } +tokio = { version = "1", features = ["full"] } # Optimization for release builds [profile.release] diff --git a/README.md b/README.md index 2e52cae..3e6a9e1 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,34 @@ Think of it as a sovereign, terminal-based alternative to Shopify. No trackers, - SSH-First: No browser required. Customers connect via `ssh shop.yourdomain.com`. - Lightning Payments: Instant payments via Lightning Network. - Templatable: Merchants can customize their shop via a simple `items.yaml`, no coding required to change themes or inventory. -- Sovereign: Run your own node, host your own TUI, and own your own data. \ No newline at end of file +- Sovereign: Run your own node, host your own TUI, and own your own data. + +## Run + +**SSH server** (default): generates `./host_key` if missing, listens on `0.0.0.0:22`. + +```bash +cargo run +# cargo run -- --host 127.0.0.1 --port 2222 --key-path ./host_key +``` + +Connect without typing a username (the SSH client uses your **local OS username** automatically): + +```bash +ssh 127.0.0.1 # or ssh localhost +``` + +If you use a non-default port (e.g. `cargo run -- --port 2222`): + +```bash +ssh -p 2222 127.0.0.1 +``` + +You can still use an explicit username if you want: `ssh user@host`. The server accepts any username in this phase. + +**Local TUI** (no SSH): + +```bash +cargo run -- --local +``` + diff --git a/ROADMAP.md b/ROADMAP.md index e13cc15..eaf517f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -14,11 +14,11 @@ ShellShop is a self-hostable, terminal-first e-commerce platform accessible via ## Phase 2 — SSH Server **Goal**: Accept real SSH connections and spawn a TUI session per client. -- [ ] Embed `russh` SSH server, bind to configurable host/port -- [ ] Generate and persist an Ed25519 host key on first run -- [ ] Spawn an isolated `App` instance per SSH session -- [ ] Pass terminal dimensions from the SSH PTY to ratatui -- [ ] Graceful disconnect on session close or timeout +- [x] Embed `russh` SSH server, bind to configurable host/port +- [x] Generate and persist an Ed25519 host key on first run +- [x] Spawn an isolated `App` instance per SSH session +- [x] Pass terminal dimensions from the SSH PTY to ratatui +- [x] Graceful disconnect on session close or timeout --- diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..b612f23 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,621 @@ +//! Ratatui shopping TUI — shared between SSH sessions and optional local mode. + +use std::io::{self, Write}; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use crossterm::{ + event::{Event, KeyCode, KeyModifiers}, + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, +}; +use ratatui::{ + backend::CrosstermBackend, + layout::{Alignment, Rect}, + style::{Color, Modifier, Style}, + text::{Line, Span}, + widgets::{Block, Borders, Gauge, ListState, Paragraph, Tabs}, + Terminal, TerminalOptions, Viewport, +}; + +const TAB_TITLES: &[&str] = &[" a account ", " c cart ", " s shop ", " h help "]; + +/// Single key event aligned with crossterm for shared handling logic. +#[derive(Debug, Clone, Copy)] +pub struct KeyEvent { + pub code: KeyCode, + pub modifiers: KeyModifiers, +} + +/// Drain one logical key from `buf`, or `None` if more bytes are needed (incomplete escape). +fn pop_key(buf: &mut Vec) -> Option { + if buf.is_empty() { + return None; + } + // Ctrl+C + if buf[0] == 0x03 { + buf.remove(0); + return Some(KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + }); + } + // Escape sequences (arrow keys, etc.) + if buf[0] == 0x1b { + if buf.len() >= 3 && buf[1] == b'[' { + let code = match buf[2] { + b'A' => KeyCode::Up, + b'B' => KeyCode::Down, + b'C' => KeyCode::Right, + b'D' => KeyCode::Left, + _ => { + // Unknown CSI: skip this byte triplet to avoid wedging the buffer + buf.drain(..3.min(buf.len())); + return pop_key(buf); + } + }; + buf.drain(..3); + return Some(KeyEvent { + code, + modifiers: KeyModifiers::NONE, + }); + } + // SS3 arrows: ESC O A / ESC O B / ... + if buf.len() >= 3 && buf[1] == b'O' { + let code = match buf[2] { + b'A' => KeyCode::Up, + b'B' => KeyCode::Down, + b'C' => KeyCode::Right, + b'D' => KeyCode::Left, + _ => { + buf.drain(..3.min(buf.len())); + return pop_key(buf); + } + }; + buf.drain(..3); + return Some(KeyEvent { + code, + modifiers: KeyModifiers::NONE, + }); + } + // Incomplete escape — wait for more input + return None; + } + let b = buf.remove(0); + // Enter + if b == b'\r' || b == b'\n' { + return Some(KeyEvent { + code: KeyCode::Enter, + modifiers: KeyModifiers::NONE, + }); + } + let ch = char::from_u32(u32::from(b)).unwrap_or('\u{fffd}'); + Some(KeyEvent { + code: KeyCode::Char(ch), + modifiers: KeyModifiers::NONE, + }) +} + +pub struct App { + pub cols: u16, + pub rows: u16, + selected_tab: usize, + account_list_state: ListState, + is_light_mode: bool, +} + +impl App { + pub fn new(cols: u16, rows: u16) -> Self { + let mut account_list_state = ListState::default(); + account_list_state.select(Some(0)); + Self { + cols, + rows, + selected_tab: 0, + account_list_state, + is_light_mode: false, + } + } + + /// Run the TUI over stdin/stdout (local development). + pub fn run_local() -> io::Result<()> { + enable_raw_mode()?; + let mut stdout = std::io::stdout(); + execute!(stdout, EnterAlternateScreen)?; + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + + let mut app = App::new(terminal.size()?.width, terminal.size()?.height); + let result = app.run_local_inner(&mut terminal); + + disable_raw_mode()?; + execute!(terminal.backend_mut(), LeaveAlternateScreen)?; + terminal.show_cursor()?; + result + } + + fn run_local_inner(&mut self, terminal: &mut Terminal>) -> io::Result<()> { + // --- Loading Screen Loop --- + let start_time = std::time::Instant::now(); + let loading_duration = Duration::from_secs(3); + + while start_time.elapsed() < loading_duration { + while crossterm::event::poll(Duration::ZERO)? { + if let Event::Key(key) = crossterm::event::read()? { + if (key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c')) + || key.code == KeyCode::Char('q') + { + return Ok(()); + } + } + } + + self.draw_loading(terminal, start_time.elapsed().as_secs_f32())?; + + std::thread::sleep(Duration::from_millis(16)); + } + + // --- Main Loop --- + let main_start_time = std::time::Instant::now(); + loop { + if crossterm::event::poll(Duration::from_millis(16))? { + if let Event::Key(key) = crossterm::event::read()? { + if (key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c')) + || key.code == KeyCode::Char('q') + { + break; + } + self.handle_key(key.code, key.modifiers); + } + } + self.sync_viewport(terminal)?; + self.draw_main(terminal, main_start_time.elapsed().as_secs_f64())?; + } + + Ok(()) + } + + /// Run over an SSH-backed terminal; input arrives as raw bytes from the channel. + pub fn run_over_channel( + &mut self, + terminal: &mut Terminal>, + input: &std::sync::mpsc::Receiver>, + dims: &Arc>, + ) -> io::Result<()> { + let mut pending = Vec::new(); + + // --- Loading Screen Loop --- + let start_time = std::time::Instant::now(); + let loading_duration = Duration::from_secs(3); + + while start_time.elapsed() < loading_duration { + while let Ok(chunk) = input.try_recv() { + pending.extend(chunk); + } + while let Some(ev) = pop_key(&mut pending) { + if (ev.modifiers.contains(KeyModifiers::CONTROL) && ev.code == KeyCode::Char('c')) + || ev.code == KeyCode::Char('q') + { + return Ok(()); + } + } + + self.sync_viewport_from_dims(terminal, dims)?; + self.draw_loading(terminal, start_time.elapsed().as_secs_f32())?; + + std::thread::sleep(Duration::from_millis(16)); + } + + // --- Main Loop --- + let main_start_time = std::time::Instant::now(); + loop { + match input.recv_timeout(Duration::from_millis(16)) { + Ok(chunk) => pending.extend(chunk), + Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {} + Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => break, + } + + while let Some(ev) = pop_key(&mut pending) { + if (ev.modifiers.contains(KeyModifiers::CONTROL) && ev.code == KeyCode::Char('c')) + || ev.code == KeyCode::Char('q') + { + return Ok(()); + } + self.handle_key(ev.code, ev.modifiers); + } + + self.sync_viewport_from_dims(terminal, dims)?; + self.draw_main(terminal, main_start_time.elapsed().as_secs_f64())?; + } + + Ok(()) + } + + fn sync_viewport(&mut self, terminal: &mut Terminal>) -> io::Result<()> { + let s = terminal.size()?; + if s.width != self.cols || s.height != self.rows { + self.cols = s.width; + self.rows = s.height; + terminal.resize(Rect::new(0, 0, self.cols, self.rows))?; + } + Ok(()) + } + + fn sync_viewport_from_dims( + &mut self, + terminal: &mut Terminal>, + dims: &Arc>, + ) -> io::Result<()> { + let (c, r) = *dims.lock().unwrap(); + if c != self.cols || r != self.rows { + self.cols = c; + self.rows = r; + terminal.resize(Rect::new(0, 0, self.cols, self.rows))?; + } + Ok(()) + } + + fn handle_key(&mut self, code: KeyCode, modifiers: KeyModifiers) { + match code { + KeyCode::Right => { + self.selected_tab = (self.selected_tab + 1) % 4; + } + KeyCode::Left => { + self.selected_tab = (self.selected_tab + 3) % 4; + } + KeyCode::Char('d') => { + if self.selected_tab == 0 { + let i = match self.account_list_state.selected() { + Some(i) if i >= 5 => 0, + Some(i) => i + 1, + None => 0, + }; + self.account_list_state.select(Some(i)); + } + } + KeyCode::Char('a') => { + if self.selected_tab == 0 { + let i = match self.account_list_state.selected() { + Some(0) => 5, + Some(i) => i - 1, + None => 0, + }; + self.account_list_state.select(Some(i)); + } else { + self.selected_tab = 0; + } + } + KeyCode::Char('c') if !modifiers.contains(KeyModifiers::CONTROL) => self.selected_tab = 1, + KeyCode::Char('s') => self.selected_tab = 2, + KeyCode::Char('h') => self.selected_tab = 3, + KeyCode::Enter => { + if self.selected_tab == 0 { + if let Some(5) = self.account_list_state.selected() { + self.is_light_mode = !self.is_light_mode; + } + } + } + _ => {} + } + } + + fn draw_loading( + &mut self, + terminal: &mut Terminal>, + elapsed_secs: f32, + ) -> io::Result<()> { + let progress = (elapsed_secs / 3.0).min(1.0); + terminal.draw(|f| { + let size = f.area(); + + let text = Paragraph::new( + " + ▄█████ ▄▄ ▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄█████ ▄▄ ▄▄ ▄▄▄ ▄▄▄▄ + ▀▀▀▄▄▄ ██▄██ ██▄▄ ██ ██ ▀▀▀▄▄▄ ██▄██ ██▀██ ██▄█▀ +█████▀ ██ ██ ██▄▄▄ ██▄▄▄ ██▄▄▄ █████▀ ██ ██ ▀███▀ ██ + ", + ) + .alignment(Alignment::Center) + .style(Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD)); + + let area = centered_rect(40, 25, size); + + let layout = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Vertical) + .constraints([ + ratatui::layout::Constraint::Length(5), + ratatui::layout::Constraint::Length(2), + ratatui::layout::Constraint::Length(1), + ratatui::layout::Constraint::Min(0), + ]) + .split(area); + + f.render_widget(text, layout[0]); + + let gauge = Gauge::default() + .gauge_style(Style::default().fg(Color::Blue).bg(Color::DarkGray)) + .percent((progress * 100.0) as u16); + + f.render_widget(gauge, layout[2]); + })?; + Ok(()) + } + + fn draw_main( + &mut self, + terminal: &mut Terminal>, + main_elapsed: f64, + ) -> io::Result<()> { + let selected_tab = self.selected_tab; + let is_light_mode = self.is_light_mode; + let account_list_state = &self.account_list_state; + + terminal.draw(|f| { + let size = f.area(); + + let bg_color = if is_light_mode { + Color::White + } else { + Color::Reset + }; + let fg_color = if is_light_mode { + Color::Black + } else { + Color::White + }; + + f.render_widget(ratatui::widgets::Clear, size); + f.render_widget( + Block::default().style(Style::default().bg(bg_color).fg(fg_color)), + size, + ); + + let vertical_chunks = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Vertical) + .constraints([ + ratatui::layout::Constraint::Percentage(10), + ratatui::layout::Constraint::Percentage(80), + ratatui::layout::Constraint::Percentage(10), + ]) + .split(size); + + let area = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Horizontal) + .constraints([ + ratatui::layout::Constraint::Percentage(5), + ratatui::layout::Constraint::Percentage(90), + ratatui::layout::Constraint::Percentage(5), + ]) + .split(vertical_chunks[1])[1]; + + let binary_text = "01100011 01111001 01110000 01101000 01100101 01110010 01110000 01110101 01101110 01101011 01110011 01110111 01110010 01101001 01110100 01100101 01100011 01101111 01100100 01100101"; + let block = Block::default() + .title_bottom(binary_text) + .title_alignment(Alignment::Right) + .borders(Borders::ALL) + .border_type(ratatui::widgets::BorderType::Thick) + .style(Style::default().fg(Color::Blue)); + + let inner_area = block.inner(area); + + let t_head = (main_elapsed * 80.0) as usize; + + let w = area.width as usize; + let h = area.height as usize; + let perimeter = (2 * w + 2 * h).saturating_sub(4); + let text_len = binary_text.len(); + + if w > 1 && h > 1 && t_head < perimeter + text_len { + let buf = f.buffer_mut(); + let thick_sym = ratatui::symbols::line::THICK; + let text_bytes = binary_text.as_bytes(); + + for d in 0..perimeter { + if d > t_head { + continue; + } + + let (cx, cy, ch) = if d < w { + let i = d; + let cx = area.x + (w - 1 - i) as u16; + let cy = area.y + (h - 1) as u16; + let ch = if i == 0 { + thick_sym.bottom_right + } else if i == w - 1 { + thick_sym.bottom_left + } else { + thick_sym.horizontal + }; + (cx, cy, ch) + } else if d < w + h - 1 { + let i = d - w + 1; + let cx = area.x; + let cy = area.y + (h - 1 - i) as u16; + let ch = if i == h - 1 { + thick_sym.top_left + } else { + thick_sym.vertical + }; + (cx, cy, ch) + } else if d < 2 * w + h - 2 { + let i = d - (w + h - 1) + 1; + let cx = area.x + i as u16; + let cy = area.y; + let ch = if i == w - 1 { + thick_sym.top_right + } else { + thick_sym.horizontal + }; + (cx, cy, ch) + } else { + let i = d - (2 * w + h - 2) + 1; + let cx = area.x + (w - 1) as u16; + let cy = area.y + i as u16; + (cx, cy, thick_sym.vertical) + }; + + if cx < size.width && cy < size.height { + if let Some(cell) = buf.cell_mut((cx, cy)) { + if t_head >= d && t_head - d < text_len { + cell.set_char(text_bytes[t_head - d] as char) + .set_fg(Color::Blue); + } else { + cell.set_symbol(ch).set_fg(Color::Blue); + } + } + } + } + } else { + f.render_widget(block, area); + } + + let inner_chunks = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Vertical) + .constraints([ + ratatui::layout::Constraint::Length(2), + ratatui::layout::Constraint::Min(0), + ]) + .split(inner_area); + + let titles: Vec = TAB_TITLES + .iter() + .map(|t| Line::from(Span::raw(*t))) + .collect(); + + let tabs_width: u16 = TAB_TITLES + .iter() + .map(|t| t.len() as u16) + .sum::() + + (TAB_TITLES.len() as u16) * 2; + let center_offset = inner_chunks[0].width.saturating_sub(tabs_width) / 2; + + let tabs_layout = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Horizontal) + .constraints([ + ratatui::layout::Constraint::Length(center_offset), + ratatui::layout::Constraint::Min(tabs_width), + ]) + .split(inner_chunks[0]); + + let tabs = Tabs::new(titles) + .block(Block::default().padding(ratatui::widgets::Padding::horizontal(2))) + .select(selected_tab) + .highlight_style( + Style::default() + .fg(fg_color) + .bg(Color::Blue) + .add_modifier(Modifier::BOLD), + ); + + f.render_widget(tabs, tabs_layout[1]); + + if selected_tab == 0 { + let options = [ + "profile", + "payments", + "favorites", + "Option 4", + "Option 5", + "dark/light", + ]; + let sub_titles: Vec = options + .iter() + .map(|t| Line::from(Span::raw(*t))) + .collect(); + + let sub_tabs_width: u16 = options.iter().map(|t| t.len() as u16).sum::() + + (options.len() as u16) * 2; + let sub_center_offset = inner_chunks[1].width.saturating_sub(sub_tabs_width) / 2; + + let sub_tabs = Tabs::new(sub_titles) + .select(account_list_state.selected().unwrap_or(0)) + .highlight_style( + Style::default() + .bg(Color::Blue) + .fg(fg_color) + .add_modifier(Modifier::BOLD), + ) + .divider("|"); + + let sub_tabs_v_layout = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Vertical) + .constraints([ + ratatui::layout::Constraint::Length(0), + ratatui::layout::Constraint::Length(1), + ratatui::layout::Constraint::Min(0), + ]) + .split(inner_chunks[1]); + + let sub_tabs_h_layout = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Horizontal) + .constraints([ + ratatui::layout::Constraint::Length(sub_center_offset), + ratatui::layout::Constraint::Min(sub_tabs_width), + ]) + .split(sub_tabs_v_layout[1]); + + f.render_widget(sub_tabs, sub_tabs_h_layout[1]); + } else { + let text = match selected_tab { + 3 => Paragraph::new( + " + ▄█████ ▄▄ ▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄█████ ▄▄ ▄▄ ▄▄▄ ▄▄▄▄ + ▀▀▀▄▄▄ ██▄██ ██▄▄ ██ ██ ▀▀▀▄▄▄ ██▄██ ██▀██ ██▄█▀ +█████▀ ██ ██ ██▄▄▄ ██▄▄▄ ██▄▄▄ █████▀ ██ ██ ▀███▀ ██ + +←/→ or a/c/s/h to navigate tabs +a/d to select items +enter to select +q to quit + ", + ) + .alignment(Alignment::Center) + .style(Style::default().fg(fg_color)), + 2 => Paragraph::new("\nShop items") + .alignment(Alignment::Center) + .style(Style::default().fg(fg_color).add_modifier(Modifier::BOLD)), + 1 => Paragraph::new("\nCart is empty") + .alignment(Alignment::Center) + .style(Style::default().fg(fg_color).add_modifier(Modifier::BOLD)), + _ => Paragraph::new(""), + }; + + f.render_widget(text, inner_chunks[1]); + } + })?; + Ok(()) + } +} + +fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + let popup_layout = ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Vertical) + .constraints([ + ratatui::layout::Constraint::Percentage((100 - percent_y) / 2), + ratatui::layout::Constraint::Percentage(percent_y), + ratatui::layout::Constraint::Percentage((100 - percent_y) / 2), + ]) + .split(r); + + ratatui::layout::Layout::default() + .direction(ratatui::layout::Direction::Horizontal) + .constraints([ + ratatui::layout::Constraint::Percentage((100 - percent_x) / 2), + ratatui::layout::Constraint::Percentage(percent_x), + ratatui::layout::Constraint::Percentage((100 - percent_x) / 2), + ]) + .split(popup_layout[1])[1] +} + +/// Build a ratatui terminal for an SSH session with the given viewport size. +pub fn terminal_for_ssh( + writer: W, + cols: u16, + rows: u16, +) -> io::Result>> { + let backend = CrosstermBackend::new(writer); + let options = TerminalOptions { + viewport: Viewport::Fixed(Rect::new(0, 0, cols, rows)), + }; + Terminal::with_options(backend, options) +} diff --git a/src/host_key.rs b/src/host_key.rs new file mode 100644 index 0000000..a52aca0 --- /dev/null +++ b/src/host_key.rs @@ -0,0 +1,30 @@ +//! Load or generate a persistent Ed25519 SSH host key. + +use std::fs; +use std::path::Path; + +use anyhow::Context; +use russh::keys::ssh_key::{LineEnding, rand_core::OsRng}; +use russh::keys::{Algorithm, PrivateKey}; + +/// Load an OpenSSH-format private key from `path`, or generate Ed25519 and write it on first run. +pub fn load_or_generate(path: &Path) -> anyhow::Result { + if path.exists() { + let pem = fs::read_to_string(path) + .with_context(|| format!("read host key {}", path.display()))?; + PrivateKey::from_openssh(pem.trim()).map_err(|e| anyhow::anyhow!(e)) + } else { + let key = PrivateKey::random(&mut OsRng, Algorithm::Ed25519) + .map_err(|e| anyhow::anyhow!("generate Ed25519 host key: {e}"))?; + let pem = key + .to_openssh(LineEnding::LF) + .map_err(|e| anyhow::anyhow!("encode host key: {e}"))?; + if let Some(parent) = path.parent() { + fs::create_dir_all(parent) + .with_context(|| format!("create directory {}", parent.display()))?; + } + fs::write(path, pem.as_bytes()) + .with_context(|| format!("write host key {}", path.display()))?; + Ok(key) + } +} diff --git a/src/main.rs b/src/main.rs index d5b3396..7b770fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,371 +1,111 @@ -use crossterm::{ - event::{self, Event, KeyCode, KeyModifiers}, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; -use ratatui::{ - backend::CrosstermBackend, - layout::{Alignment, Rect}, - style::{Color, Modifier, Style}, - text::{Line, Span}, - widgets::{Block, Borders, Paragraph, Tabs, List, ListItem, ListState, Gauge}, - Terminal, -}; -use std::io::{self, stdout}; +//! ShellShop: SSH-first terminal shopping TUI. -fn main() -> Result<(), io::Error> { - // --- Setup Terminal --- - enable_raw_mode()?; - let mut stdout = stdout(); - execute!(stdout, EnterAlternateScreen)?; - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; +mod app; +mod host_key; +mod ssh_server; - let mut selected_tab = 0; // 0 to 3 - let tab_titles = vec![" a account ", " c cart ", " s shop ", " h help "]; - let mut account_list_state = ListState::default(); - account_list_state.select(Some(0)); - let mut is_light_mode = false; +use std::path::PathBuf; +use std::sync::Arc; - // --- Loading Screen Loop --- - let start_time = std::time::Instant::now(); - let loading_duration = std::time::Duration::from_secs(3); +use russh::server::Server; - while start_time.elapsed() < loading_duration { - if event::poll(std::time::Duration::from_millis(16))? { - if let Event::Key(key) = event::read()? { - if (key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c')) - || key.code == KeyCode::Char('q') - { - disable_raw_mode()?; - execute!(terminal.backend_mut(), LeaveAlternateScreen)?; - terminal.show_cursor()?; - return Ok(()); - } - } - } - - terminal.draw(|f| { - let size = f.area(); - - let elapsed = start_time.elapsed().as_secs_f32(); - let progress = (elapsed / 3.0).min(1.0); - - let text = Paragraph::new(" - ▄█████ ▄▄ ▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄█████ ▄▄ ▄▄ ▄▄▄ ▄▄▄▄ - ▀▀▀▄▄▄ ██▄██ ██▄▄ ██ ██ ▀▀▀▄▄▄ ██▄██ ██▀██ ██▄█▀ -█████▀ ██ ██ ██▄▄▄ ██▄▄▄ ██▄▄▄ █████▀ ██ ██ ▀███▀ ██ - ") - .alignment(Alignment::Center) - .style(Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD)); - - let area = centered_rect(40, 25, size); - - let layout = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Vertical) - .constraints([ - ratatui::layout::Constraint::Length(5), // ASCII ART Height - ratatui::layout::Constraint::Length(2), // Spacer - ratatui::layout::Constraint::Length(1), // Progress Bar - ratatui::layout::Constraint::Min(0), - ]) - .split(area); +struct CliConfig { + host: String, + port: u16, + key_path: PathBuf, +} - f.render_widget(text, layout[0]); - - let gauge = Gauge::default() - .gauge_style(Style::default().fg(Color::Blue).bg(Color::DarkGray)) - .percent((progress * 100.0) as u16); - - f.render_widget(gauge, layout[2]); - })?; +impl Default for CliConfig { + fn default() -> Self { + Self { + host: "0.0.0.0".to_string(), + port: 22, + key_path: PathBuf::from("host_key"), + } } +} - // --- Main Loop --- - let main_start_time = std::time::Instant::now(); - loop { - // Handle input events - if event::poll(std::time::Duration::from_millis(16))? { - if let Event::Key(key) = event::read()? { - // Quit on Ctrl+C or 'q' - if (key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c')) - || key.code == KeyCode::Char('q') - { - break; +fn parse_cli(args: &[String]) -> CliConfig { + let mut c = CliConfig::default(); + let mut i = 1; + while i < args.len() { + match args[i].as_str() { + "--help" | "-h" => { + eprintln!( + "\ +ShellShop — SSH server (default) or local TUI + +USAGE: + shellshop [OPTIONS] + shellshop --local + +OPTIONS: + --host Bind address (default: 0.0.0.0) + --port Listen port (default: 22) + --key-path Host private key file (default: ./host_key) + --local, -l Run TUI on this terminal instead of SSH server + -h, --help This help +" + ); + std::process::exit(0); + } + "--local" | "-l" => { + // handled in main before parsing continues + i += 1; + } + "--host" => { + if let Some(v) = args.get(i + 1) { + c.host = v.clone(); + i += 1; } - - // Navigate tabs with Left/Right arrows and letter shortcuts - match key.code { - KeyCode::Right => { - selected_tab = (selected_tab + 1) % 4; - } - KeyCode::Left => { - selected_tab = (selected_tab + 3) % 4; - } - KeyCode::Char('d') => { - if selected_tab == 0 { - let i = match account_list_state.selected() { - Some(i) => if i >= 5 { 0 } else { i + 1 }, - None => 0, - }; - account_list_state.select(Some(i)); - } - } - KeyCode::Char('a') => { - if selected_tab == 0 { - let i = match account_list_state.selected() { - Some(i) => if i == 0 { 5 } else { i - 1 }, - None => 0, - }; - account_list_state.select(Some(i)); - } else { - selected_tab = 0; - } - } - KeyCode::Char('c') => selected_tab = 1, - KeyCode::Char('s') => selected_tab = 2, - KeyCode::Char('h') => selected_tab = 3, - KeyCode::Enter => { - if selected_tab == 0 { - if let Some(5) = account_list_state.selected() { - is_light_mode = !is_light_mode; - } - } - } - _ => {} + i += 1; + } + "--port" => { + if let Some(v) = args.get(i + 1).and_then(|s| s.parse().ok()) { + c.port = v; + i += 1; } + i += 1; } - } - - terminal.draw(|f| { - let size = f.area(); - - let bg_color = if is_light_mode { Color::White } else { Color::Reset }; - let fg_color = if is_light_mode { Color::Black } else { Color::White }; - - f.render_widget(ratatui::widgets::Clear, size); - f.render_widget(Block::default().style(Style::default().bg(bg_color).fg(fg_color)), size); - - // 1. Define the vertical layout for the entire app area - let vertical_chunks = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Vertical) - .constraints([ - ratatui::layout::Constraint::Percentage(10), // Top margin - ratatui::layout::Constraint::Percentage(80), // Main Box Height - ratatui::layout::Constraint::Percentage(10), // Bottom margin - ]) - .split(size); - - // 2. Define the horizontal layout: 5% margin sides, 90% center - let area = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Horizontal) - .constraints([ - ratatui::layout::Constraint::Percentage(5), // Left margin - ratatui::layout::Constraint::Percentage(90), // Main Box Width - ratatui::layout::Constraint::Percentage(5), // Right margin - ]) - .split(vertical_chunks[1])[1]; // Split the middle vertical chunk - - // 3. Create the Large Block (the border blue box) - let binary_text = "01100011 01111001 01110000 01101000 01100101 01110010 01110000 01110101 01101110 01101011 01110011 01110111 01110010 01101001 01110100 01100101 01100011 01101111 01100100 01100101"; - let block = Block::default() - .title_bottom(binary_text) - .title_alignment(Alignment::Right) - .borders(Borders::ALL) - .border_type(ratatui::widgets::BorderType::Thick) // Makes the box look beefier - .style(Style::default().fg(Color::Blue)); - - // Obtain inner area for contents (tabs and text) - let inner_area = block.inner(area); - - let elapsed = main_start_time.elapsed().as_secs_f64(); - let chars_per_sec = 80.0; - let t_head = (elapsed * chars_per_sec) as usize; - - let w = area.width as usize; - let h = area.height as usize; - let perimeter = (2 * w + 2 * h).saturating_sub(4); - let text_len = binary_text.len(); - - if w > 1 && h > 1 && t_head < perimeter + text_len { - let buf = f.buffer_mut(); - let thick_sym = ratatui::symbols::line::THICK; - let text_bytes = binary_text.as_bytes(); - - for d in 0..perimeter { - if d > t_head { continue; } - - let (cx, cy, ch) = if d < w { - let i = d; - let cx = area.x + (w - 1 - i) as u16; - let cy = area.y + (h - 1) as u16; - let ch = if i == 0 { thick_sym.bottom_right } else if i == w - 1 { thick_sym.bottom_left } else { thick_sym.horizontal }; - (cx, cy, ch) - } else if d < w + h - 1 { - let i = d - w + 1; - let cx = area.x; - let cy = area.y + (h - 1 - i) as u16; - let ch = if i == h - 1 { thick_sym.top_left } else { thick_sym.vertical }; - (cx, cy, ch) - } else if d < 2 * w + h - 2 { - let i = d - (w + h - 1) + 1; - let cx = area.x + i as u16; - let cy = area.y; - let ch = if i == w - 1 { thick_sym.top_right } else { thick_sym.horizontal }; - (cx, cy, ch) - } else { - let i = d - (2 * w + h - 2) + 1; - let cx = area.x + (w - 1) as u16; - let cy = area.y + i as u16; - (cx, cy, thick_sym.vertical) - }; - - if cx < size.width && cy < size.height { - if let Some(cell) = buf.cell_mut((cx, cy)) { - if t_head >= d && t_head - d < text_len { - cell.set_char(text_bytes[t_head - d] as char).set_fg(Color::Blue); - } else { - cell.set_symbol(ch).set_fg(Color::Blue); - } - } - } + "--key-path" => { + if let Some(v) = args.get(i + 1) { + c.key_path = PathBuf::from(v); + i += 1; } - } else { - // Render the outer blue box border normally - f.render_widget(block, area); + i += 1; } - - // Main inner chunks: 2 rows for Tabs, the rest for content - let inner_chunks = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Vertical) - .constraints([ - ratatui::layout::Constraint::Length(2), // Tab bar height - ratatui::layout::Constraint::Min(0), // Rest of app - ]) - .split(inner_area); - - // --- Create Tabs --- - let titles: Vec = tab_titles - .iter() - .map(|t| Line::from(Span::raw(*t))) - .collect(); - - // Calculate width needed for tabs to pseudo-center them - let tabs_width: u16 = tab_titles.iter().map(|t| t.len() as u16).sum::() + (tab_titles.len() as u16) * 2; - let center_offset = inner_chunks[0].width.saturating_sub(tabs_width) / 2; - - let tabs_layout = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Horizontal) - .constraints([ - ratatui::layout::Constraint::Length(center_offset), - ratatui::layout::Constraint::Min(tabs_width), - ]) - .split(inner_chunks[0]); - - let tabs = Tabs::new(titles) - // Use a block to add margin if needed, but no borders so it fits nicely - .block(Block::default().padding(ratatui::widgets::Padding::horizontal(2))) - .select(selected_tab) - .highlight_style( - Style::default() - .fg(fg_color) - .bg(Color::Blue) // Highlight/hover color - .add_modifier(Modifier::BOLD), - ); - - f.render_widget(tabs, tabs_layout[1]); - - if selected_tab == 0 { - let options = vec![ - "profile", "payments", "favorites", - "Option 4", "Option 5", "dark/light" - ]; - let sub_titles: Vec = options.iter().map(|t| Line::from(Span::raw(*t))).collect(); - - let sub_tabs_width: u16 = options.iter().map(|t| t.len() as u16).sum::() + (options.len() as u16) * 2; - let sub_center_offset = inner_chunks[1].width.saturating_sub(sub_tabs_width) / 2; - - let sub_tabs = Tabs::new(sub_titles) - .select(account_list_state.selected().unwrap_or(0)) - .highlight_style( - Style::default() - .bg(Color::Blue) - .fg(fg_color) - .add_modifier(Modifier::BOLD), - ) - .divider("|"); - - let sub_tabs_v_layout = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Vertical) - .constraints([ - ratatui::layout::Constraint::Length(0), // slight gap to rest directly below first tab list - ratatui::layout::Constraint::Length(1), // exactly 1 height for horizontal list - ratatui::layout::Constraint::Min(0), - ]) - .split(inner_chunks[1]); - - let sub_tabs_h_layout = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Horizontal) - .constraints([ - ratatui::layout::Constraint::Length(sub_center_offset), - ratatui::layout::Constraint::Min(sub_tabs_width), - ]) - .split(sub_tabs_v_layout[1]); - - f.render_widget(sub_tabs, sub_tabs_h_layout[1]); - } else { - let text = match selected_tab { - 3 => Paragraph::new(" - ▄█████ ▄▄ ▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄█████ ▄▄ ▄▄ ▄▄▄ ▄▄▄▄ - ▀▀▀▄▄▄ ██▄██ ██▄▄ ██ ██ ▀▀▀▄▄▄ ██▄██ ██▀██ ██▄█▀ -█████▀ ██ ██ ██▄▄▄ ██▄▄▄ ██▄▄▄ █████▀ ██ ██ ▀███▀ ██ - -←/→ or a/c/s/h to navigate tabs -a/d to select items -enter to select -q to quit - ") - .alignment(Alignment::Center) - .style(Style::default().fg(fg_color)), - 2 => Paragraph::new("\nShop items") - .alignment(Alignment::Center) - .style(Style::default().fg(fg_color).add_modifier(Modifier::BOLD)), - 1 => Paragraph::new("\nCart is empty") - .alignment(Alignment::Center) - .style(Style::default().fg(fg_color).add_modifier(Modifier::BOLD)), - _ => Paragraph::new(""), - }; - - f.render_widget(text, inner_chunks[1]); + other => { + eprintln!("Unknown argument: {other}"); + i += 1; } - })?; + } + } + c +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let args: Vec = std::env::args().collect(); + if args.iter().any(|a| a == "--local" || a == "-l") { + return app::App::run_local().map_err(|e| anyhow::anyhow!("{e}")); } - // --- Restore Terminal --- - disable_raw_mode()?; - execute!(terminal.backend_mut(), LeaveAlternateScreen)?; - terminal.show_cursor()?; + let config = parse_cli(&args); + let key_path = config.key_path.clone(); - Ok(()) -} + let host_key = host_key::load_or_generate(&key_path)?; + let ssh_config = Arc::new(ssh_server::server_config(host_key)); -/// Helper function to create a centered rectangle -fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { - let popup_layout = ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Vertical) - .constraints([ - ratatui::layout::Constraint::Percentage((100 - percent_y) / 2), - ratatui::layout::Constraint::Percentage(percent_y), - ratatui::layout::Constraint::Percentage((100 - percent_y) / 2), - ]) - .split(r); + eprintln!( + "ShellShop SSH listening on {}:{} (host key: {})", + config.host, + config.port, + key_path.display() + ); - ratatui::layout::Layout::default() - .direction(ratatui::layout::Direction::Horizontal) - .constraints([ - ratatui::layout::Constraint::Percentage((100 - percent_x) / 2), - ratatui::layout::Constraint::Percentage(percent_x), - ratatui::layout::Constraint::Percentage((100 - percent_x) / 2), - ]) - .split(popup_layout[1])[1] + let mut server = ssh_server::ShellShopServer::default(); + server + .run_on_address(ssh_config, (config.host.as_str(), config.port)) + .await?; + Ok(()) } diff --git a/src/ssh_server.rs b/src/ssh_server.rs new file mode 100644 index 0000000..4c7d728 --- /dev/null +++ b/src/ssh_server.rs @@ -0,0 +1,258 @@ +//! Embedded russh SSH server: one isolated TUI per session. + +use std::io::Write; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex, mpsc}; + +use ratatui::Terminal; +use russh::keys::PublicKey; +use russh::server::{Auth, Config, Msg, Server, Session}; +use russh::{Channel, ChannelId, Pty}; +use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; + +use crate::app::{terminal_for_ssh, App}; + +/// Async bridge: crossterm writes are batched on `flush` and sent on the SSH channel. +/// Pattern from russh `examples/ratatui_app.rs`. +struct TerminalHandle { + sender: UnboundedSender>, + sink: Vec, +} + +impl TerminalHandle { + async fn start(handle: russh::server::Handle, channel_id: ChannelId) -> Self { + let (sender, mut receiver) = unbounded_channel::>(); + tokio::spawn(async move { + while let Some(data) = receiver.recv().await { + if handle + .data(channel_id, bytes::Bytes::from(data)) + .await + .is_err() + { + break; + } + } + }); + Self { + sender, + sink: Vec::new(), + } + } +} + +impl Write for TerminalHandle { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.sink.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + if self.sink.is_empty() { + return Ok(()); + } + self.sender + .send(std::mem::take(&mut self.sink)) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::BrokenPipe, e))?; + Ok(()) + } +} + +/// Global listener state: assigns monotonic client ids. +#[derive(Default)] +pub struct ShellShopServer { + next_id: AtomicUsize, +} + +impl Server for ShellShopServer { + type Handler = ClientHandler; + + fn new_client(&mut self, _: Option) -> Self::Handler { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + ClientHandler::new(id) + } + + fn handle_session_error(&mut self, error: ::Error) { + eprintln!("SSH session error: {error:#}"); + } +} + +pub struct ClientHandler { + id: usize, + dims: Arc>, + input_tx: Mutex>>>, + shell_started: AtomicBool, +} + +impl ClientHandler { + fn new(id: usize) -> Self { + Self { + id, + dims: Arc::new(Mutex::new((80, 24))), + input_tx: Mutex::new(None), + shell_started: AtomicBool::new(false), + } + } +} + +impl russh::server::Handler for ClientHandler { + type Error = anyhow::Error; + + async fn auth_none(&mut self, _user: &str) -> Result { + Ok(Auth::Accept) + } + + async fn auth_password(&mut self, _user: &str, _password: &str) -> Result { + Ok(Auth::Accept) + } + + async fn auth_publickey( + &mut self, + _user: &str, + _public_key: &PublicKey, + ) -> Result { + Ok(Auth::Accept) + } + + async fn channel_open_session( + &mut self, + _channel: Channel, + _session: &mut Session, + ) -> Result { + Ok(true) + } + + async fn pty_request( + &mut self, + channel: ChannelId, + _term: &str, + col_width: u32, + row_height: u32, + _pix_width: u32, + _pix_height: u32, + _modes: &[(Pty, u32)], + session: &mut Session, + ) -> Result<(), Self::Error> { + { + let mut d = self.dims.lock().unwrap(); + *d = (col_width as u16, row_height as u16); + } + session.channel_success(channel)?; + Ok(()) + } + + async fn shell_request( + &mut self, + channel: ChannelId, + session: &mut Session, + ) -> Result<(), Self::Error> { + if self.shell_started.swap(true, Ordering::SeqCst) { + session.channel_failure(channel)?; + return Ok(()); + } + + let handle = session.handle(); + let close_handle = handle.clone(); + let terminal_handle = TerminalHandle::start(handle, channel).await; + + let (tx, rx) = mpsc::channel::>(); + *self.input_tx.lock().unwrap() = Some(tx); + + let dims = self.dims.clone(); + let (cols, rows) = *dims.lock().unwrap(); + let client_id = self.id; + let rt = tokio::runtime::Handle::current(); + + std::thread::Builder::new() + .name(format!("shellshop-tui-{client_id}")) + .spawn(move || { + let close_channel = |exit_status: u32| { + rt.block_on(async { + let _ = close_handle.exit_status_request(channel, exit_status).await; + let _ = close_handle.eof(channel).await; + let _ = close_handle.close(channel).await; + }); + }; + + let mut terminal: Terminal<_> = match terminal_for_ssh(terminal_handle, cols, rows) { + Ok(t) => t, + Err(e) => { + eprintln!("terminal init (client {client_id}): {e}"); + close_channel(1); + return; + } + }; + let mut app = App::new(cols, rows); + let exit = match app.run_over_channel(&mut terminal, &rx, &dims) { + Ok(()) => 0, + Err(e) => { + eprintln!("TUI exit (client {client_id}): {e}"); + 1 + } + }; + close_channel(exit); + }) + .map_err(|e| anyhow::anyhow!("spawn TUI thread: {e}"))?; + + session.channel_success(channel)?; + Ok(()) + } + + async fn window_change_request( + &mut self, + channel: ChannelId, + col_width: u32, + row_height: u32, + _pix_width: u32, + _pix_height: u32, + session: &mut Session, + ) -> Result<(), Self::Error> { + { + let mut d = self.dims.lock().unwrap(); + *d = (col_width as u16, row_height as u16); + } + session.channel_success(channel)?; + Ok(()) + } + + async fn data( + &mut self, + _channel: ChannelId, + data: &[u8], + _session: &mut Session, + ) -> Result<(), Self::Error> { + if let Some(tx) = self.input_tx.lock().unwrap().as_ref() { + let _ = tx.send(data.to_vec()); + } + Ok(()) + } + + async fn channel_close( + &mut self, + _channel: ChannelId, + _session: &mut Session, + ) -> Result<(), Self::Error> { + self.input_tx.lock().unwrap().take(); + Ok(()) + } + + async fn channel_eof( + &mut self, + _channel: ChannelId, + _session: &mut Session, + ) -> Result<(), Self::Error> { + self.input_tx.lock().unwrap().take(); + Ok(()) + } +} + +/// Build server config with sensible timeouts for interactive TUI use. +pub fn server_config(host_key: russh::keys::PrivateKey) -> Config { + Config { + inactivity_timeout: Some(std::time::Duration::from_secs(3600)), + auth_rejection_time: std::time::Duration::from_millis(500), + auth_rejection_time_initial: Some(std::time::Duration::from_secs(0)), + keys: vec![host_key], + nodelay: true, + ..Default::default() + } +}