From 0f13a5c8d178207dcc56a2830700a3b7febe0fb2 Mon Sep 17 00:00:00 2001 From: csd113 Date: Fri, 20 Mar 2026 12:38:00 -0700 Subject: [PATCH] replaced tor with arti --- CHANGELOG.md | 20 +- Cargo.lock | 6000 ++++++++++++++++++++++++++------ Cargo.toml | 9 +- README.md | 25 +- audit.toml | 3 + deny.toml | 181 +- ffmpeg-migration.md | 1233 +++++++ src/config.rs | 8 +- src/detect.rs | 543 +-- src/handlers/admin/mod.rs | 26 +- src/handlers/admin/settings.rs | 26 +- src/handlers/board.rs | 14 +- src/middleware/mod.rs | 3 + src/server/server.rs | 18 +- 14 files changed, 6580 insertions(+), 1529 deletions(-) create mode 100644 audit.toml create mode 100644 ffmpeg-migration.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f712c7..3b5f9e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,19 +79,19 @@ All notable changes to RustChan will be documented in this file. --- -## Tor Detection & Panic Safety (`src/detect.rs`) +## Tor β€” Arti In-Process Migration -- Removed all `.expect()` usage on shared mutexes -- Replaced with poison-tolerant locking strategies -- Prevented cascade crashes caused by mutex poisoning +Replaced the subprocess-based C Tor launcher with **[Arti](https://gitlab.torproject.org/tpo/core/arti)** running fully in-process. **No system `tor` installation is required.** -- Hardened global panic hook: - - Eliminated potential for double-panic aborts - - Replaced blocking locks with `try_lock()` where required - - Ensured panic hook never panics under any condition +**How it works:** at startup a single Tokio task bootstraps Arti, derives a `.onion` address from a persistent Ed25519 keypair, launches the hidden service, and proxies inbound onion connections to the local HTTP port β€” all without spawning a child process, writing a `torrc`, or polling a `hostname` file. -- Restored original panic hook after Tor lifecycle completes -- Reduced global side effects of detection logic +- Bootstrap takes ~30 s on first run (Arti downloads ~2 MB of directory data) and ~5 s on subsequent runs (consensus cached in `arti_cache/`) +- The onion address is published to `AppState` the moment the service is ready; handlers read it from memory with zero filesystem I/O per request +- Service keypair lives in `rustchan-data/arti_state/keys/` β€” back this directory up to preserve your `.onion` address; delete it to rotate to a new one +- Old `rustchan-data/tor_data/`, `rustchan-data/tor_hidden_service/`, and `rustchan-data/torrc` are no longer created and can be safely deleted after migration +- **Note:** the keypair location changed on migration (`tor_hidden_service/` β†’ `arti_state/keys/`), so a new `.onion` address is generated on first run unless the old Ed25519 key is manually imported via Arti's key management tooling + +**Files changed:** `Cargo.toml` (+6 deps: `arti-client`, `tor-hsservice`, `tor-cell`, `futures`, `sha3`, `data-encoding`), `src/detect.rs`, `src/middleware/mod.rs`, `src/server/server.rs`, `src/handlers/board.rs`, `src/handlers/admin/mod.rs`, `src/handlers/admin/settings.rs`, `src/config.rs` --- diff --git a/Cargo.lock b/Cargo.lock index b91e553..7435085 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,18 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[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", + "zeroize", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -32,6 +44,61 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "amplify" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f7fb4ac7c881e54a8e7015e399b6112a2a5bc958b6c89ac510840ff20273b31" +dependencies = [ + "amplify_derive", + "amplify_num", + "ascii", + "getrandom 0.2.17", + "getrandom 0.3.4", + "wasm-bindgen", +] + +[[package]] +name = "amplify_derive" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a6309e6b8d89b36b9f959b7a8fa093583b94922a0f6438a24fb08936de4d428" +dependencies = [ + "amplify_syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "amplify_num" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99bcb75a2982047f733547042fc3968c0f460dfcf7d90b90dea3b2744580e9ad" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "amplify_syn" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -41,6 +108,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "1.0.0" @@ -109,6 +182,114 @@ dependencies = [ "password-hash", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "arti-client" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89842cae6e3bda0fd128a5c66eb3392ed412065dc698c77d9fcc4b77e4159f2" +dependencies = [ + "async-trait", + "cfg-if", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "educe", + "fs-mistrust", + "futures", + "hostname-validator", + "humantime", + "humantime-serde", + "libc", + "once_cell", + "postage", + "rand 0.9.2", + "safelog", + "serde", + "thiserror 2.0.18", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-chanmgr", + "tor-circmgr", + "tor-config", + "tor-config-path", + "tor-dircommon", + "tor-dirmgr", + "tor-error", + "tor-guardmgr", + "tor-hsclient", + "tor-hscrypto", + "tor-hsservice", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-rtcompat", + "tracing", + "void", +] + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.18", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "async-compression" version = "0.4.41" @@ -117,10 +298,77 @@ checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" dependencies = [ "compression-codecs", "compression-core", + "futures-io", "pin-project-lite", "tokio", ] +[[package]] +name = "async-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" +dependencies = [ + "futures-util", + "native-tls", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async_executors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" +dependencies = [ + "blanket", + "futures-core", + "futures-task", + "futures-util", + "pin-project", + "rustc_version", + "tokio", +] + +[[package]] +name = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -208,6 +456,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" @@ -220,12 +474,40 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "serde", + "unty", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.10.6" @@ -235,6 +517,17 @@ dependencies = [ "digest", ] +[[package]] +name = "blanket" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -265,18 +558,41 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + [[package]] name = "bytemuck" 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 = "byteorder-lite" version = "0.1.0" @@ -289,6 +605,18 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "caret" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae2cb9f60bc3f21effaaf9c64e51f6627edd54eedc9199ba07f519ef2a2101" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.56" @@ -335,7 +663,45 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", ] [[package]] @@ -357,7 +723,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] @@ -369,7 +735,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -378,6 +744,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "coarsetime" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58eb270476aa4fc7843849f8a35063e8743b4dbcdf6dd0f8ea0886980c204c2" +dependencies = [ + "libc", + "wasix", + "wasm-bindgen", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -399,6 +776,7 @@ dependencies = [ "brotli", "compression-core", "flate2", + "liblzma", "memchr", "zstd", "zstd-safe", @@ -410,6 +788,30 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -421,6 +823,25 @@ dependencies = [ "version_check", ] +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -455,14 +876,87 @@ dependencies = [ ] [[package]] -name = "crossbeam-channel" -version = "0.5.15" +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-cycles-per-byte" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5396de42a52e9e5d8f67ef0702dae30451f310a9ba1c3094dcf228f0be0e54bc" +dependencies = [ + "cfg-if", + "criterion", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools 0.13.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -476,1790 +970,4889 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] -name = "crypto-common" -version = "0.1.7" +name = "crypto-bigint" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "typenum", + "rand_core 0.6.4", + "subtle", + "zeroize", ] [[package]] -name = "dashmap" -version = "6.1.0" +name = "crypto-common" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", + "generic-array", + "typenum", ] [[package]] -name = "deranged" -version = "0.5.8" +name = "ctr" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "powerfmt", + "cipher", ] [[package]] -name = "digest" -version = "0.10.7" +name = "curve25519-dalek" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "block-buffer", - "crypto-common", + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", "subtle", + "zeroize", ] [[package]] -name = "displaydoc" -version = "0.2.5" +name = "curve25519-dalek-derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "darling" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "cfg-if", + "darling_core 0.14.4", + "darling_macro 0.14.4", ] [[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" +name = "darling" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ - "libc", - "windows-sys 0.61.2", + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] -name = "fallible-iterator" -version = "0.3.0" +name = "darling" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] [[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" +name = "darling_core" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] [[package]] -name = "fastrand" -version = "2.3.0" +name = "darling_core" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] -name = "fax" -version = "0.2.6" +name = "darling_core" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fax_derive", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.117", ] [[package]] -name = "fax_derive" -version = "0.2.0" +name = "darling_macro" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "proc-macro2", + "darling_core 0.14.4", "quote", - "syn", + "syn 1.0.109", ] [[package]] -name = "fdeflate" -version = "0.3.7" +name = "darling_macro" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ - "simd-adler32", + "darling_core 0.21.3", + "quote", + "syn 2.0.117", ] [[package]] -name = "find-msvc-tools" -version = "0.1.9" +name = "darling_macro" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.117", +] [[package]] -name = "flate2" -version = "1.1.9" +name = "dashmap" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ - "crc32fast", - "miniz_oxide", - "zlib-rs", + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] -name = "foldhash" -version = "0.1.5" +name = "data-encoding" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] -name = "foldhash" -version = "0.2.0" +name = "der" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] [[package]] -name = "form_urlencoded" -version = "1.2.2" +name = "der-parser" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ - "percent-encoding", + "asn1-rs", + "cookie-factory", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", ] [[package]] -name = "futures-channel" -version = "0.3.32" +name = "deranged" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "futures-core", + "powerfmt", + "serde_core", ] [[package]] -name = "futures-core" -version = "0.3.32" +name = "derive-deftly" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "284db66a66f03c3dafbe17360d959eb76b83f77cfe191677e2a7899c0da291f3" +dependencies = [ + "derive-deftly-macros", + "heck", +] [[package]] -name = "futures-sink" -version = "0.3.32" +name = "derive-deftly-macros" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "caef6056a5788d05d173cdc3c562ac28ae093828f851f69378b74e4e3d578e41" +dependencies = [ + "heck", + "indexmap 2.13.0", + "itertools 0.14.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "sha3", + "strum", + "syn 2.0.117", + "void", +] [[package]] -name = "futures-task" -version = "0.3.32" +name = "derive_builder_core_fork_arti" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "futures-util" -version = "0.3.32" +name = "derive_builder_fork_arti" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "c3eae24d595f4d0ecc90a9a5a6d11c2bd8dafe2375ec4a1ec63250e5ade7d228" dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", + "derive_builder_macro_fork_arti", ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "derive_builder_macro_fork_arti" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "69887769a2489cd946bf782eb2b1bb2cb7bc88551440c94a765d4f040c08ebf3" dependencies = [ - "typenum", - "version_check", + "derive_builder_core_fork_arti", + "syn 1.0.109", ] [[package]] -name = "getrandom" -version = "0.2.17" +name = "derive_more" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", + "derive_more-impl", ] [[package]] -name = "getrandom" -version = "0.3.4" +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", + "unicode-xid", ] [[package]] -name = "getrandom" -version = "0.4.2" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "rand_core 0.10.0", - "wasip2", - "wasip3", + "block-buffer", + "const-oid", + "crypto-common", + "subtle", ] [[package]] -name = "gif" -version = "0.14.1" +name = "directories" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "color_quant", - "weezl", + "dirs-sys", ] [[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "hashbrown" -version = "0.15.5" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "foldhash 0.1.5", + "dirs-sys", ] [[package]] -name = "hashbrown" -version = "0.16.1" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ - "foldhash 0.2.0", + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", ] [[package]] -name = "hashlink" -version = "0.11.0" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "hashbrown 0.16.1", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "heck" -version = "0.5.0" +name = "downcast-rs" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" [[package]] -name = "hex" -version = "0.4.3" +name = "dyn-clone" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] -name = "http" -version = "1.4.0" +name = "ecdsa" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "bytes", - "itoa", + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] -name = "http-body" -version = "1.0.1" +name = "ed25519" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "bytes", - "http", + "pkcs8", + "signature", ] [[package]] -name = "http-body-util" -version = "0.1.3" +name = "ed25519-dalek" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", + "curve25519-dalek", + "ed25519", + "merlin", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", ] [[package]] -name = "http-range-header" -version = "0.4.2" +name = "educe" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "httparse" -version = "1.10.1" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "httpdate" -version = "1.0.3" +name = "elliptic-curve" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] [[package]] -name = "hyper" -version = "1.8.1" +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", + "cfg-if", ] [[package]] -name = "hyper-rustls" -version = "0.27.7" +name = "enum-ordinalize" +version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "hyper-util" -version = "0.1.20" +name = "enum_dispatch" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "iana-time-zone" -version = "0.1.65" +name = "enumset" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", + "enumset_derive", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "enumset_derive" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" dependencies = [ - "cc", + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "icu_collections" -version = "2.1.1" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "icu_locale_core" -version = "2.1.1" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "icu_normalizer" -version = "2.1.1" +name = "event-listener" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", + "concurrent-queue", + "parking", + "pin-project-lite", ] [[package]] -name = "icu_normalizer_data" -version = "2.1.1" +name = "fallible-iterator" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] -name = "icu_properties" -version = "2.1.2" +name = "fallible-streaming-iterator" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] -name = "icu_properties_data" -version = "2.1.2" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "icu_provider" -version = "2.1.1" +name = "fax" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", + "fax_derive", ] [[package]] -name = "id-arena" -version = "2.3.0" +name = "fax_derive" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] -name = "idna" -version = "1.1.0" +name = "fdeflate" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", + "simd-adler32", ] [[package]] -name = "idna_adapter" -version = "1.2.1" +name = "ff" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "icu_normalizer", - "icu_properties", + "rand_core 0.6.4", + "subtle", ] [[package]] -name = "image" -version = "0.25.10" +name = "fiat-crypto" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "gif", - "image-webp", - "moxcms", - "num-traits", - "png", - "tiff", - "zune-core", - "zune-jpeg", -] +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] -name = "image-webp" -version = "0.2.4" +name = "figment" +version = "0.10.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ - "byteorder-lite", - "quick-error", + "atomic 0.6.1", + "serde", + "toml 0.8.23", + "uncased", + "version_check", ] [[package]] -name = "indexmap" -version = "2.13.0" +name = "filetime" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", + "cfg-if", + "libc", + "libredox", ] [[package]] -name = "ipnet" -version = "2.12.0" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] -name = "iri-string" -version = "0.7.10" +name = "flate2" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ - "memchr", - "serde", + "crc32fast", + "miniz_oxide", + "zlib-rs", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.2" +name = "fluid-let" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a" [[package]] -name = "itoa" -version = "1.0.17" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "jobserver" -version = "0.1.34" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "js-sys" -version = "0.3.91" +name = "foldhash" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "once_cell", - "wasm-bindgen", + "foreign-types-shared", ] [[package]] -name = "kamadak-exif" -version = "0.5.5" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ - "mutate_once", + "percent-encoding", ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "fs-mistrust" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "189ebb6d350de8d03181999fa9ebe8a021c5ab041004388f29e4dd2c52dc88a2" +dependencies = [ + "derive_builder_fork_arti", + "dirs", + "libc", + "pwd-grp", + "serde", + "thiserror 2.0.18", + "walkdir", +] [[package]] -name = "leb128fmt" -version = "0.1.0" +name = "fslock" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] [[package]] -name = "libc" -version = "0.2.183" +name = "fslock-arti-fork" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "8b21bd626aaab7b904b20bef6d9e06298914a0c8d9fb8b010483766b2e532791" +dependencies = [ + "libc", + "winapi", +] [[package]] -name = "libsqlite3-sys" -version = "0.36.0" +name = "fslock-guard" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" +checksum = "75f62cb7d296f7d1fabdce7291281d9f0147b06a6e79afae05e1230eab667d85" dependencies = [ - "cc", - "pkg-config", - "vcpkg", + "fslock-arti-fork", + "thiserror 2.0.18", + "winapi", ] [[package]] -name = "linux-raw-sys" -version = "0.12.1" +name = "funty" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] -name = "litemap" -version = "0.8.1" +name = "futures" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] -name = "lock_api" -version = "0.4.14" +name = "futures-channel" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ - "scopeguard", + "futures-core", + "futures-sink", ] [[package]] -name = "log" -version = "0.4.29" +name = "futures-core" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] -name = "lru-slab" -version = "0.1.2" +name = "futures-executor" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] -name = "matchers" -version = "0.2.0" +name = "futures-io" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ - "regex-automata", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "matchit" -version = "0.8.4" +name = "futures-sink" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] -name = "memchr" -version = "2.8.0" +name = "futures-task" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] -name = "mime" -version = "0.3.17" +name = "futures-util" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] [[package]] -name = "mime_guess" -version = "2.0.5" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "mime", - "unicase", + "typenum", + "version_check", + "zeroize", ] [[package]] -name = "miniz_oxide" -version = "0.8.9" +name = "getrandom" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ - "adler2", - "simd-adler32", + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", ] [[package]] -name = "mio" -version = "1.1.1" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ + "cfg-if", + "js-sys", "libc", - "wasi", - "windows-sys 0.61.2", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", ] [[package]] -name = "moxcms" -version = "0.8.1" +name = "getrandom" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ - "num-traits", - "pxfm", + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.0", + "wasip2", + "wasip3", ] [[package]] -name = "multer" -version = "3.1.0" +name = "getset" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http", - "httparse", - "memchr", - "mime", - "spin", - "version_check", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "mutate_once" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" +name = "gif" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" dependencies = [ - "windows-sys 0.61.2", + "color_quant", + "weezl", ] [[package]] -name = "num-conv" -version = "0.2.0" +name = "glob-match" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" [[package]] -name = "num-traits" -version = "0.2.19" +name = "group" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "autocfg", + "ff", + "rand_core 0.6.4", + "subtle", ] [[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "parking_lot" -version = "0.12.5" +name = "growable-bloom-filter" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" dependencies = [ - "lock_api", - "parking_lot_core", + "serde", + "serde_bytes", + "serde_derive", + "xxhash-rust", ] [[package]] -name = "parking_lot_core" -version = "0.9.12" +name = "half" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "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", + "crunchy", + "zerocopy", ] [[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] -name = "pkg-config" -version = "0.3.32" +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] [[package]] -name = "png" -version = "0.18.1" +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "bitflags", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", + "foldhash 0.2.0", ] [[package]] -name = "potential_utf" -version = "0.1.4" +name = "hashlink" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" dependencies = [ - "zerovec", + "hashbrown 0.16.1", ] [[package]] -name = "powerfmt" -version = "0.2.0" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "ppv-lite86" -version = "0.2.21" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "prettyplease" -version = "0.2.37" +name = "hkdf" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "proc-macro2", - "syn", + "hmac", ] [[package]] -name = "proc-macro2" -version = "1.0.106" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "unicode-ident", + "digest", ] [[package]] -name = "pxfm" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" - -[[package]] -name = "quick-error" -version = "2.0.1" +name = "hostname-validator" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" [[package]] -name = "quinn" -version = "0.11.9" +name = "http" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", + "itoa", ] [[package]] -name = "quinn-proto" -version = "0.11.14" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", + "http", ] [[package]] -name = "quinn-udp" -version = "0.5.14" +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", ] [[package]] -name = "quote" -version = "1.0.45" +name = "http-range-header" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" [[package]] -name = "r-efi" -version = "5.3.0" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "r-efi" -version = "6.0.0" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "r2d2" -version = "0.8.10" +name = "humantime" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] -name = "r2d2_sqlite" -version = "0.32.0" +name = "humantime-serde" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ebd03c29250cdf191da93a35118b4567c2ef0eacab54f65e058d6f4c9965f6" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" dependencies = [ - "r2d2", - "rusqlite", - "uuid", + "humantime", + "serde", ] [[package]] -name = "rand" -version = "0.9.2" +name = "hyper" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ - "rand_chacha", - "rand_core 0.9.5", -] - -[[package]] + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[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 0.62.2", +] + +[[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 = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inotify" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +dependencies = [ + "bitflags 2.11.0", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "inventory" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009ae045c87e7082cb72dab0ccd01ae075dd00141ddc108f43a0ea150a9e7227" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +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.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k12" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dc5fdb62af2f520116927304f15d25b3c2667b4817b90efdc045194c912c54" +dependencies = [ + "digest", + "sha3", +] + +[[package]] +name = "kamadak-exif" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" +dependencies = [ + "mutate_once", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "liblzma" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6033b77c21d1f56deeae8014eb9fbe7bdf1765185a6c508b5ca82eeaed7f899" +dependencies = [ + "liblzma-sys", +] + +[[package]] +name = "liblzma-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2db66f3268487b5033077f266da6777d057949b8f93c8ad82e441df25e6186" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "bitflags 2.11.0", + "libc", + "plain", + "redox_syscall 0.7.3", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "mutate_once" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nonany" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b8866ec53810a9a4b3d434a29801e78c707430a9ae11c2db4b8b62bb9675a0" + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.11.0", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-types" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oneshot-fused-workaround" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17b52d0e4a06a4c7eb8d2943c0015fa628cf4ccc409429cebc0f5bed6d33a82" +dependencies = [ + "futures", +] + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "openssl" +version = "0.10.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +dependencies = [ + "memchr", +] + +[[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", +] + +[[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", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", + "sha2", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link 0.2.1", +] + +[[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 = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "postage" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" +dependencies = [ + "atomic 0.5.3", + "crossbeam-queue", + "futures", + "parking_lot", + "pin-project", + "static_assertions", + "thiserror 1.0.69", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +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 = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "priority-queue" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" +dependencies = [ + "equivalent", + "indexmap 2.13.0", + "serde", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.4+spec-1.1.0", +] + +[[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.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pwd-grp" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e2023f41b5fcb7c30eb5300a5733edfaa9e0e0d502d51b586f65633fd39e40c" +dependencies = [ + "derive-deftly", + "libc", + "paste", + "thiserror 2.0.18", +] + +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "r2d2_sqlite" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ebd03c29250cdf191da93a35118b4567c2ef0eacab54f65e058d6f4c9965f6" +dependencies = [ + "r2d2", + "rusqlite", + "uuid", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "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" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.0", +] + +[[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]] +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[package]] +name = "rand_jitter" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16df48f071248e67b8fc5e866d9448d45c08ad8b672baaaf796e2f15e606ff0" +dependencies = [ + "libc", + "rand_core 0.9.5", + "winapi", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rdrand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92195228612ac8eed47adbc2ed0f04e513a4ccb98175b6f2bd04d963b533655" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "retry-error" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c322ea521636c5a3f13685a6266055b2dda7e54e2be35214d7c2a5d0672a5db" +dependencies = [ + "humantime", +] + +[[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", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rsqlite-vfs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d" +dependencies = [ + "hashbrown 0.16.1", + "thiserror 2.0.18", +] + +[[package]] +name = "rusqlite" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3" +dependencies = [ + "bitflags 2.11.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", + "sqlite-wasm-rs", + "time", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustchan" +version = "1.1.0" +dependencies = [ + "anyhow", + "argon2", + "arti-client", + "axum", + "axum-extra", + "chrono", + "clap", + "dashmap", + "data-encoding", + "futures", + "hex", + "image", + "kamadak-exif", + "libc", + "once_cell", + "parking_lot", + "r2d2", + "r2d2_sqlite", + "rand_core 0.6.4", + "regex", + "reqwest", + "rusqlite", + "serde", + "serde_json", + "sha2", + "sha3", + "subtle", + "tempfile", + "thiserror 2.0.18", + "time", + "tokio", + "tokio-util", + "toml 1.0.6+spec-1.1.0", + "tor-cell", + "tor-hsservice", + "tower", + "tower-http", + "tracing", + "tracing-appender", + "tracing-subscriber", + "uuid", + "zip", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "safelog" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8949ab2810bf603caef654634e5b4cedcbc05c120342a177cf8aaa122ef4bb76" +dependencies = [ + "derive_more", + "educe", + "either", + "fluid-let", + "thiserror 2.0.18", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sanitize-filename" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d" +dependencies = [ + "regex", +] + +[[package]] +name = "saturating-time" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63583a1dd0647d1484228529ab4ecaa874048d2956f117362aa5f5826456230" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_ignored" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[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", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shellexpand" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" +dependencies = [ + "bstr", + "dirs", + "os_str_bytes", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[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 = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "slotmap-careful" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed92816c1fbb29891a525b92d5fa95757c9dee47044f76c8e06ceb1e052a8d64" dependencies = [ - "chacha20", - "getrandom 0.4.2", - "rand_core 0.10.0", + "paste", + "serde", + "slotmap", + "thiserror 2.0.18", + "void", ] [[package]] -name = "rand_chacha" -version = "0.9.0" +name = "smallvec" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "rand_core" -version = "0.6.4" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ - "getrandom 0.2.17", + "base64ct", + "der", ] [[package]] -name = "rand_core" -version = "0.9.5" +name = "sqlite-wasm-rs" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +checksum = "2f4206ed3a67690b9c29b77d728f6acc3ce78f16bf846d83c94f76400320181b" dependencies = [ - "getrandom 0.3.4", + "cc", + "js-sys", + "rsqlite-vfs", + "wasm-bindgen", ] [[package]] -name = "rand_core" +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher", + "ssh-encoding", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468", + "sha2", +] + +[[package]] +name = "ssh-key" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" +dependencies = [ + "num-bigint-dig", + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "sha2", + "signature", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "redox_syscall" -version = "0.5.18" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "bitflags", + "strum_macros", ] [[package]] -name = "regex" -version = "1.12.3" +name = "strum_macros" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "regex-automata" -version = "0.4.14" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "regex-syntax" -version = "0.8.10" +name = "syn" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] -name = "reqwest" -version = "0.12.28" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "base64", - "bytes", "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", ] [[package]] -name = "ring" -version = "0.17.14" +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sysinfo" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "time" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", + "deranged", + "itoa", + "js-sys", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", ] [[package]] -name = "rsqlite-vfs" -version = "0.1.0" +name = "time-core" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d" -dependencies = [ - "hashbrown 0.16.1", - "thiserror", -] +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] -name = "rusqlite" -version = "0.38.0" +name = "time-macros" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ - "bitflags", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", - "sqlite-wasm-rs", + "num-conv", + "time-core", ] [[package]] -name = "rustc-hash" -version = "2.1.1" +name = "tinystr" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec", +] [[package]] -name = "rustchan" -version = "1.1.0" +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "anyhow", - "argon2", - "axum", - "axum-extra", - "chrono", - "clap", - "dashmap", - "hex", - "image", - "kamadak-exif", - "libc", - "once_cell", - "parking_lot", - "r2d2", - "r2d2_sqlite", - "rand_core 0.6.4", - "regex", - "reqwest", - "rusqlite", "serde", "serde_json", - "sha2", - "subtle", - "tempfile", - "thiserror", - "time", - "tokio", - "tokio-util", - "toml", - "tower", - "tower-http", - "tracing", - "tracing-appender", - "tracing-subscriber", - "uuid", - "zip", ] [[package]] -name = "rustix" -version = "1.1.4" +name = "tinyvec" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ - "bitflags", - "errno", + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", "libc", - "linux-raw-sys", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", "windows-sys 0.61.2", ] [[package]] -name = "rustls" -version = "0.23.37" +name = "tokio-macros" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "rustls-pki-types" -version = "1.14.0" +name = "tokio-rustls" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "web-time", - "zeroize", + "rustls", + "tokio", ] [[package]] -name = "rustls-webpki" -version = "0.103.9" +name = "tokio-util" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] -name = "rustversion" -version = "1.0.22" +name = "toml" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] [[package]] -name = "ryu" -version = "1.0.23" +name = "toml" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.13.0", + "serde_core", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", +] [[package]] -name = "scheduled-thread-pool" -version = "0.2.7" +name = "toml" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +checksum = "399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc" dependencies = [ - "parking_lot", + "indexmap 2.13.0", + "serde_core", + "serde_spanned 1.0.4", + "toml_datetime 1.0.0+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "toml_datetime" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] [[package]] -name = "semver" -version = "1.0.27" +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] [[package]] -name = "serde" -version = "1.0.228" +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" dependencies = [ "serde_core", - "serde_derive", ] [[package]] -name = "serde_core" -version = "1.0.228" +name = "toml_edit" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "serde_derive", + "indexmap 2.13.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow", ] [[package]] -name = "serde_derive" -version = "1.0.228" +name = "toml_edit" +version = "0.25.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" dependencies = [ - "proc-macro2", - "quote", - "syn", + "indexmap 2.13.0", + "toml_datetime 1.0.0+spec-1.1.0", + "toml_parser", + "winnow", ] [[package]] -name = "serde_json" -version = "1.0.149" +name = "toml_parser" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", + "winnow", ] [[package]] -name = "serde_path_to_error" -version = "0.1.20" +name = "toml_write" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "tor-async-utils" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "895c61a46909134501c6815eceeb66c9c80fc494ee89429821b0f05ccf34b4f5" dependencies = [ - "itoa", + "derive-deftly", + "educe", + "futures", + "oneshot-fused-workaround", + "pin-project", + "postage", + "thiserror 2.0.18", + "void", +] + +[[package]] +name = "tor-basic-utils" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac6e4d7e131b7d69bc85558383cd4ac61e46b4dd0d4ed51632f28fac98cac0c" +dependencies = [ + "derive_more", + "hex", + "itertools 0.14.0", + "libc", + "paste", + "rand 0.9.2", + "rand_chacha 0.9.0", "serde", - "serde_core", + "slab", + "smallvec", + "thiserror 2.0.18", + "weak-table", ] [[package]] -name = "serde_spanned" -version = "1.0.4" +name = "tor-bytes" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "64454947258e49f238a5f06a06250a0c54598a1c7409898b5c79505e6a99e7af" dependencies = [ - "serde_core", + "bytes", + "derive-deftly", + "digest", + "educe", + "getrandom 0.4.2", + "safelog", + "thiserror 2.0.18", + "tor-error", + "tor-llcrypto", + "zeroize", ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "tor-cell" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "4ab0c79bc92a957e85959cf397a2d8f9c8294c35fa4f65247a9393b20ac95551" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", + "amplify", + "bitflags 2.11.0", + "bytes", + "caret", + "derive-deftly", + "derive_more", + "educe", + "itertools 0.14.0", + "paste", + "rand 0.9.2", + "smallvec", + "thiserror 2.0.18", + "tor-basic-utils", + "tor-bytes", + "tor-cert", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-protover", + "tor-units", + "void", ] [[package]] -name = "sha2" -version = "0.10.9" +name = "tor-cert" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "debc911738298ee801fce4577c36a50c55295b0bb9c5519461b83cc486a1f86e" dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", + "caret", + "derive_builder_fork_arti", + "derive_more", "digest", + "thiserror 2.0.18", + "tor-bytes", + "tor-checkable", + "tor-error", + "tor-llcrypto", ] [[package]] -name = "sharded-slab" -version = "0.1.7" +name = "tor-chanmgr" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "7af5b7c2f1e16d1304b5185fcbc91ca5c8df991c21be00702f925f055573eea1" dependencies = [ - "lazy_static", + "async-trait", + "caret", + "cfg-if", + "derive-deftly", + "derive_more", + "educe", + "futures", + "oneshot-fused-workaround", + "percent-encoding", + "postage", + "rand 0.9.2", + "safelog", + "serde", + "serde_with", + "thiserror 2.0.18", + "tor-async-utils", + "tor-basic-utils", + "tor-cell", + "tor-config", + "tor-error", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-netdir", + "tor-proto", + "tor-rtcompat", + "tor-socksproto", + "tor-units", + "tracing", + "url", + "void", ] [[package]] -name = "shlex" -version = "1.3.0" +name = "tor-checkable" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "15b13a5b50bb55037f2e81b25dde42f420d57c75154216b8ef989006cea3ebee" +dependencies = [ + "humantime", + "signature", + "thiserror 2.0.18", + "tor-llcrypto", +] [[package]] -name = "signal-hook-registry" -version = "1.4.8" +name = "tor-circmgr" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "b878f3f7c6be0c7f130d90b347ada2e7c46519dfbdde8273eae2e5d1792caa87" dependencies = [ - "errno", - "libc", + "amplify", + "async-trait", + "cfg-if", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "educe", + "futures", + "humantime-serde", + "itertools 0.14.0", + "once_cell", + "oneshot-fused-workaround", + "pin-project", + "rand 0.9.2", + "retry-error", + "safelog", + "serde", + "thiserror 2.0.18", + "tor-async-utils", + "tor-basic-utils", + "tor-cell", + "tor-chanmgr", + "tor-config", + "tor-dircommon", + "tor-error", + "tor-guardmgr", + "tor-linkspec", + "tor-memquota", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tor-units", + "tracing", + "void", + "weak-table", ] [[package]] -name = "simd-adler32" -version = "0.3.8" +name = "tor-config" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "3cbc74a00ab15bb986e3747c6969e40a58a63065d6f99077e7ee2f4657bb8b03" +dependencies = [ + "amplify", + "cfg-if", + "derive-deftly", + "derive_builder_fork_arti", + "educe", + "either", + "figment", + "fs-mistrust", + "futures", + "humantime-serde", + "itertools 0.14.0", + "notify", + "paste", + "postage", + "regex", + "serde", + "serde-value", + "serde_ignored", + "strum", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", + "tor-basic-utils", + "tor-error", + "tor-rtcompat", + "tracing", + "void", +] [[package]] -name = "slab" -version = "0.4.12" +name = "tor-config-path" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +checksum = "3005ab7b9a26a7271e5adf3dfb4ae18c09a943e32aeccc4f6d1c53a60de74b8d" +dependencies = [ + "directories", + "serde", + "shellexpand", + "thiserror 2.0.18", + "tor-error", + "tor-general-addr", +] [[package]] -name = "smallvec" -version = "1.15.1" +name = "tor-consdiff" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "6bfa2b7b71c72830f61c48da4bb3e13191e0c0e1404b9c5168c795e4f5feb4a8" +dependencies = [ + "digest", + "hex", + "thiserror 2.0.18", + "tor-llcrypto", +] [[package]] -name = "socket2" -version = "0.6.3" +name = "tor-dirclient" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "8ccd6fac844ac77c33ccdfcb56bf23ff40ebbb821ea708be79a481ec30e8c39c" dependencies = [ - "libc", - "windows-sys 0.61.2", + "async-compression", + "base64ct", + "derive_more", + "futures", + "hex", + "http", + "httparse", + "httpdate", + "itertools 0.14.0", + "memchr", + "thiserror 2.0.18", + "tor-circmgr", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-proto", + "tor-rtcompat", + "tracing", ] [[package]] -name = "spin" -version = "0.9.8" +name = "tor-dircommon" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "dd0cf39a3c30321d145a4d60753ae7ef5bb58a66a00ac9e2bfc30bd823faf2a4" +dependencies = [ + "base64ct", + "derive-deftly", + "getset", + "humantime", + "humantime-serde", + "serde", + "tor-basic-utils", + "tor-checkable", + "tor-config", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tracing", +] [[package]] -name = "sqlite-wasm-rs" -version = "0.5.2" +name = "tor-dirmgr" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4206ed3a67690b9c29b77d728f6acc3ce78f16bf846d83c94f76400320181b" +checksum = "b52919aa9dbb82a354c5b904bef82e91beb702b9f8ad14e6eac4237d6128bf67" dependencies = [ - "cc", - "js-sys", - "rsqlite-vfs", - "wasm-bindgen", + "async-trait", + "base64ct", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "event-listener", + "fs-mistrust", + "fslock", + "futures", + "hex", + "humantime", + "humantime-serde", + "itertools 0.14.0", + "memmap2", + "oneshot-fused-workaround", + "paste", + "postage", + "rand 0.9.2", + "rusqlite", + "safelog", + "scopeguard", + "serde", + "serde_json", + "signature", + "static_assertions", + "strum", + "thiserror 2.0.18", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-consdiff", + "tor-dirclient", + "tor-dircommon", + "tor-error", + "tor-guardmgr", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-rtcompat", + "tracing", ] [[package]] -name = "stable_deref_trait" -version = "1.2.1" +name = "tor-error" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "595b005e6f571ac3890a34a00f361200aab781fd0218f2c528c86fc7af088df5" +dependencies = [ + "derive_more", + "futures", + "paste", + "retry-error", + "static_assertions", + "strum", + "thiserror 2.0.18", + "tracing", + "void", +] [[package]] -name = "strsim" -version = "0.11.1" +name = "tor-general-addr" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "727b8c8bc01c1587486055edab5c2cd0d5c960f5bb3fac796fc9911872b8b397" +dependencies = [ + "derive_more", + "thiserror 2.0.18", + "void", +] [[package]] -name = "subtle" -version = "2.6.1" +name = "tor-guardmgr" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "d337f465a477c0fb3b2faafa4654d70ff9df3590e57d22707591dddb4e4450c1" +dependencies = [ + "amplify", + "base64ct", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "dyn-clone", + "educe", + "futures", + "humantime", + "humantime-serde", + "itertools 0.14.0", + "num_enum", + "oneshot-fused-workaround", + "pin-project", + "postage", + "rand 0.9.2", + "safelog", + "serde", + "strum", + "thiserror 2.0.18", + "tor-async-utils", + "tor-basic-utils", + "tor-config", + "tor-dircommon", + "tor-error", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-relay-selection", + "tor-rtcompat", + "tor-units", + "tracing", +] [[package]] -name = "syn" -version = "2.0.117" +name = "tor-hsclient" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "4c701ef4eccab73f23d619ef5650c886239d1215e6d4aa0c3dec6342072a8178" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "async-trait", + "derive-deftly", + "derive_more", + "educe", + "either", + "futures", + "itertools 0.14.0", + "oneshot-fused-workaround", + "postage", + "rand 0.9.2", + "retry-error", + "safelog", + "slotmap-careful", + "strum", + "thiserror 2.0.18", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-dirclient", + "tor-error", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-rtcompat", + "tracing", ] [[package]] -name = "sync_wrapper" -version = "1.0.2" +name = "tor-hscrypto" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +checksum = "a3693cd43f05cd01ac0aaa060dae5c5e53c4364f89e0d769e33cd629a2fd3118" dependencies = [ - "futures-core", + "cipher", + "data-encoding", + "derive-deftly", + "derive_more", + "digest", + "hex", + "humantime", + "itertools 0.14.0", + "paste", + "rand 0.9.2", + "safelog", + "serde", + "signature", + "subtle", + "thiserror 2.0.18", + "tor-basic-utils", + "tor-bytes", + "tor-error", + "tor-key-forge", + "tor-llcrypto", + "tor-memquota", + "tor-units", + "void", + "zeroize", ] [[package]] -name = "synstructure" -version = "0.13.2" +name = "tor-hsservice" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "6c2d6a533ffd01b6764bfa59526f12883d6025fd64fdc73e219033ec29936aa3" dependencies = [ - "proc-macro2", - "quote", - "syn", + "amplify", + "async-trait", + "base64ct", + "cfg-if", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "fs-mistrust", + "futures", + "growable-bloom-filter", + "hex", + "humantime", + "itertools 0.14.0", + "k12", + "once_cell", + "oneshot-fused-workaround", + "postage", + "rand 0.9.2", + "rand_core 0.9.5", + "retry-error", + "safelog", + "serde", + "serde_with", + "strum", + "thiserror 2.0.18", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-circmgr", + "tor-config", + "tor-config-path", + "tor-dirclient", + "tor-error", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-log-ratelim", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tracing", + "void", ] [[package]] -name = "tempfile" -version = "3.27.0" +name = "tor-key-forge" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +checksum = "3ade9065ae49cfe2ab020ca9ca9f2b3c5c9b5fc0d8980fa681d8b3a0668e042f" dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys 0.61.2", + "derive-deftly", + "derive_more", + "downcast-rs", + "paste", + "rand 0.9.2", + "rsa", + "signature", + "ssh-key", + "thiserror 2.0.18", + "tor-bytes", + "tor-cert", + "tor-checkable", + "tor-error", + "tor-llcrypto", ] [[package]] -name = "thiserror" -version = "2.0.18" +name = "tor-keymgr" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "243c3163d376c4723cd67271fcd6e5d6b498a6865c6b98299640e1be01c38826" dependencies = [ - "thiserror-impl", + "amplify", + "arrayvec", + "cfg-if", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "fs-mistrust", + "glob-match", + "humantime", + "inventory", + "itertools 0.14.0", + "rand 0.9.2", + "safelog", + "serde", + "signature", + "ssh-key", + "thiserror 2.0.18", + "tor-basic-utils", + "tor-bytes", + "tor-config", + "tor-config-path", + "tor-error", + "tor-hscrypto", + "tor-key-forge", + "tor-llcrypto", + "tor-persist", + "tracing", + "visibility", + "walkdir", + "zeroize", ] [[package]] -name = "thiserror-impl" -version = "2.0.18" +name = "tor-linkspec" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "05f1ea8786900d6fbe4c9f775d341b1ba01bbd1f750d89bd63be78b6b01e1836" dependencies = [ - "proc-macro2", - "quote", - "syn", + "base64ct", + "by_address", + "caret", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "hex", + "itertools 0.14.0", + "safelog", + "serde", + "serde_with", + "strum", + "thiserror 2.0.18", + "tor-basic-utils", + "tor-bytes", + "tor-config", + "tor-llcrypto", + "tor-memquota", + "tor-protover", ] [[package]] -name = "thread_local" -version = "1.1.9" +name = "tor-llcrypto" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1c6989a1c6d06ffd6835e2917edaae4aeef544f8e5fdd68b54cc365f2af523de" dependencies = [ - "cfg-if", + "aes", + "base64ct", + "ctr", + "curve25519-dalek", + "der-parser", + "derive-deftly", + "derive_more", + "digest", + "ed25519-dalek", + "educe", + "getrandom 0.4.2", + "hex", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_core 0.6.4", + "rand_core 0.9.5", + "rand_jitter", + "rdrand", + "rsa", + "safelog", + "serde", + "sha1", + "sha2", + "sha3", + "signature", + "subtle", + "thiserror 2.0.18", + "tor-error", + "tor-memquota-cost", + "visibility", + "x25519-dalek", + "zeroize", ] [[package]] -name = "tiff" -version = "0.11.3" +name = "tor-log-ratelim" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +checksum = "3f1cd642180923d12e3fab5996b4aa2189718da7f465df6eb196ce2b9c70e293" dependencies = [ - "fax", - "flate2", - "half", - "quick-error", - "weezl", - "zune-jpeg", + "futures", + "humantime", + "thiserror 2.0.18", + "tor-error", + "tor-rtcompat", + "tracing", + "weak-table", ] [[package]] -name = "time" -version = "0.3.47" +name = "tor-memquota" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "599daea60fd3272eb72a795d1c593b45bbe15343cbc702340a81db124c06eed5" dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", + "cfg-if", + "derive-deftly", + "derive_more", + "dyn-clone", + "educe", + "futures", + "itertools 0.14.0", + "paste", + "pin-project", + "serde", + "slotmap-careful", + "static_assertions", + "sysinfo", + "thiserror 2.0.18", + "tor-async-utils", + "tor-basic-utils", + "tor-config", + "tor-error", + "tor-log-ratelim", + "tor-memquota-cost", + "tor-rtcompat", + "tracing", + "void", ] [[package]] -name = "time-core" -version = "0.1.8" +name = "tor-memquota-cost" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "dd92b07c0fc24e6d8166a5ff45e5b8654e68d89743c46d01889a16ab74c0b578" +dependencies = [ + "derive-deftly", + "itertools 0.14.0", + "paste", + "void", +] [[package]] -name = "time-macros" -version = "0.2.27" +name = "tor-netdir" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "41be8f47f521fc95206d2ba5facac8fb1a5b5b82169bd41ebeecdf46d1e77246" dependencies = [ - "num-conv", - "time-core", + "async-trait", + "bitflags 2.11.0", + "derive_more", + "digest", + "futures", + "hex", + "humantime", + "itertools 0.14.0", + "num_enum", + "rand 0.9.2", + "serde", + "strum", + "thiserror 2.0.18", + "time", + "tor-basic-utils", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-protover", + "tor-units", + "tracing", + "typed-index-collections", ] [[package]] -name = "tinystr" -version = "0.8.2" +name = "tor-netdoc" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "ea8bce73d2c78bd78a2a927336ca639cf6bd5d8ad092ebcd0b3fdeaa47dcc77e" dependencies = [ - "displaydoc", - "zerovec", + "amplify", + "base64ct", + "cipher", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "enumset", + "hex", + "humantime", + "itertools 0.14.0", + "memchr", + "paste", + "phf", + "rand 0.9.2", + "saturating-time", + "serde", + "serde_with", + "signature", + "smallvec", + "strum", + "subtle", + "thiserror 2.0.18", + "time", + "tinystr", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-checkable", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-protover", + "tor-units", + "void", + "zeroize", ] [[package]] -name = "tinyvec" -version = "1.10.0" +name = "tor-persist" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "507ab4b6a3d59ed0df5804eeed66dcacde75e3be13d3694216cdfdb666bce625" dependencies = [ - "tinyvec_macros", + "amplify", + "derive-deftly", + "derive_more", + "filetime", + "fs-mistrust", + "fslock", + "fslock-guard", + "futures", + "itertools 0.14.0", + "oneshot-fused-workaround", + "paste", + "sanitize-filename", + "serde", + "serde_json", + "thiserror 2.0.18", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-error", + "tracing", + "void", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.50.0" +name = "tor-proto" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "bbfc552d535d36539d5782bb02028590bc472d219e49da51a96810725e80ff56" dependencies = [ + "amplify", + "async-trait", + "asynchronous-codec", + "bitvec", "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", + "caret", + "cfg-if", + "cipher", + "coarsetime", + "criterion-cycles-per-byte", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "enum_dispatch", + "futures", + "futures-util", + "hkdf", + "hmac", + "itertools 0.14.0", + "nonany", + "oneshot-fused-workaround", + "pin-project", + "postage", + "rand 0.9.2", + "rand_core 0.9.5", + "safelog", + "slotmap-careful", + "smallvec", + "static_assertions", + "subtle", + "sync_wrapper", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-checkable", + "tor-config", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-log-ratelim", + "tor-memquota", + "tor-protover", + "tor-relay-crypto", + "tor-rtcompat", + "tor-rtmock", + "tor-units", + "tracing", + "typenum", + "visibility", + "void", + "zeroize", ] [[package]] -name = "tokio-macros" -version = "2.6.1" +name = "tor-protover" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "aed88527d070c4b7ea4e55a36d2d56d0500e30ca66298b5264f047f7f2f89cfa" dependencies = [ - "proc-macro2", - "quote", - "syn", + "caret", + "paste", + "serde_with", + "thiserror 2.0.18", + "tor-basic-utils", + "tor-bytes", ] [[package]] -name = "tokio-rustls" -version = "0.26.4" +name = "tor-relay-crypto" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +checksum = "f7e57e9f71b22ae1df63dbccc8e428cb07feec0abd654735109fa563c10bbb90" dependencies = [ - "rustls", - "tokio", + "derive-deftly", + "derive_more", + "humantime", + "tor-cert", + "tor-checkable", + "tor-error", + "tor-key-forge", + "tor-keymgr", + "tor-llcrypto", + "tor-persist", ] [[package]] -name = "tokio-util" -version = "0.7.18" +name = "tor-relay-selection" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "a372072ac9dea7d17e49693cc3f3ae77b3abf8125630516c9f2d622239b1920a" dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", + "rand 0.9.2", + "serde", + "tor-basic-utils", + "tor-linkspec", + "tor-netdir", + "tor-netdoc", ] [[package]] -name = "toml" -version = "1.0.6+spec-1.1.0" +name = "tor-rtcompat" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc" +checksum = "14428b930e59003e801c0c32697c0aeb9b0495ad33ecbe8c6753bdb596233270" dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime", - "toml_parser", - "toml_writer", - "winnow", + "async-native-tls", + "async-trait", + "async_executors", + "asynchronous-codec", + "cfg-if", + "coarsetime", + "derive_more", + "dyn-clone", + "educe", + "futures", + "hex", + "libc", + "native-tls", + "paste", + "pin-project", + "socket2", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tor-error", + "tor-general-addr", + "tracing", + "void", + "zeroize", ] [[package]] -name = "toml_datetime" -version = "1.0.0+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" -dependencies = [ - "serde_core", +name = "tor-rtmock" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2da91a432cdaee8a93e0bb21b02f3e9c7667832ccbb4b54e00d9c1214638e70" +dependencies = [ + "amplify", + "assert_matches", + "async-trait", + "derive-deftly", + "derive_more", + "educe", + "futures", + "humantime", + "itertools 0.14.0", + "oneshot-fused-workaround", + "pin-project", + "priority-queue", + "slotmap-careful", + "strum", + "thiserror 2.0.18", + "tor-error", + "tor-general-addr", + "tor-rtcompat", + "tracing", + "tracing-test", + "void", ] [[package]] -name = "toml_parser" -version = "1.0.9+spec-1.1.0" +name = "tor-socksproto" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "adbc9115a2f506d9bb86ae4446f0ca70eb523dc2f5e900a33582e7c39decc23a" dependencies = [ - "winnow", + "amplify", + "caret", + "derive-deftly", + "educe", + "safelog", + "subtle", + "thiserror 2.0.18", + "tor-bytes", + "tor-error", ] [[package]] -name = "toml_writer" -version = "1.0.6+spec-1.1.0" +name = "tor-units" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "da90e93b4b4aa4ec356ecbe9e19aced36fdd655e94ca459d1915120d873363f0" +dependencies = [ + "derive-deftly", + "derive_more", + "serde", + "thiserror 2.0.18", + "tor-memquota", +] [[package]] name = "tower" @@ -2284,7 +5877,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", - "bitflags", + "bitflags 2.11.0", "bytes", "futures-core", "futures-util", @@ -2337,7 +5930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 2.0.18", "time", "tracing-subscriber", ] @@ -2350,7 +5943,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2405,12 +5998,43 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "tracing-test" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a4c448db514d4f24c5ddb9f73f2ee71bfb24c526cf0c570ba142d1119e0051" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-index-collections" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898160f1dfd383b4e92e17f0512a7d62f3c51c44937b23b6ffc3a1614a8eaccd" +dependencies = [ + "bincode", + "serde", +] + [[package]] name = "typed-path" version = "0.12.3" @@ -2423,6 +6047,15 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicase" version = "2.9.0" @@ -2435,6 +6068,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2447,6 +6086,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" version = "2.5.8" @@ -2502,6 +6147,33 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2535,6 +6207,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasix" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1757e0d1f8456693c7e5c6c629bdb54884e032aa0bb53c155f6a39f94440d332" +dependencies = [ + "wasi", +] + [[package]] name = "wasm-bindgen" version = "0.2.114" @@ -2581,7 +6262,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -2611,7 +6292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.13.0", "wasm-encoder", "wasmparser", ] @@ -2622,12 +6303,18 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags", + "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "semver", ] +[[package]] +name = "weak-table" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" + [[package]] name = "web-sys" version = "0.3.91" @@ -2663,6 +6350,72 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -2671,9 +6424,20 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] @@ -2684,7 +6448,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2695,22 +6459,56 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[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.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -2719,7 +6517,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2746,7 +6544,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2771,7 +6569,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -2782,6 +6580,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -2883,6 +6690,9 @@ name = "winnow" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" @@ -2912,9 +6722,9 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.13.0", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2930,7 +6740,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -2942,8 +6752,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags", - "indexmap", + "bitflags 2.11.0", + "indexmap 2.13.0", "log", "serde", "serde_derive", @@ -2962,7 +6772,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.13.0", "log", "semver", "serde", @@ -2978,6 +6788,33 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yoke" version = "0.8.1" @@ -2997,7 +6834,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -3018,7 +6855,7 @@ checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3038,7 +6875,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -3047,6 +6884,20 @@ 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.117", +] [[package]] name = "zerotrie" @@ -3065,6 +6916,7 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ + "serde", "yoke", "zerofrom", "zerovec-derive", @@ -3078,7 +6930,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3089,7 +6941,7 @@ checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004" dependencies = [ "crc32fast", "flate2", - "indexmap", + "indexmap 2.13.0", "memchr", "typed-path", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 23fa9b4..585f8c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "rustchan" version = "1.1.0" edition = "2021" # axum 0.8 requires Rust 1.75; that is the effective floor for this project. -rust-version = "1.88.0" +rust-version = "1.90" license = "MIT" # FIX[LOW-1]: Removed hardware-specific description. This binary is portable # and targets any architecture/OS that Rust supports. @@ -94,6 +94,13 @@ thiserror = "2" tempfile = "3" libc = "0.2" +arti-client = { version = "0.40", features = ["tokio", "native-tls", "onion-service-service"] } +tor-hsservice = { version = "0.40" } +tor-cell = { version = "0.40" } +futures = "0.3" +sha3 = "0.10" +data-encoding = "2" + [lints.clippy] too_many_arguments = "allow" manual_pattern_char_comparison = "allow" diff --git a/README.md b/README.md index 65676cf..cf3d68e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ RustChan is a fully-featured imageboard engine compiled into a **single Rust binary**. Deploy it on a VPS, a Raspberry Pi, or a local machine β€” no containers, no runtime, no package manager required. All persistent data lives in a single directory alongside the binary, making migrations as simple as `cp -r`. -Two external tools are supported as **optional enhancements**: [**ffmpeg**](#ffmpeg--video--audio-processing) for video transcoding and audio waveforms, and [**Tor**](#tor--onion-service) for anonymous `.onion` access. Neither is required β€” RustChan degrades gracefully without them. +**[ffmpeg](#ffmpeg--video--audio-processing)** is supported as an optional enhancement for video transcoding and audio waveforms. **[Tor](#tor--onion-service)** onion service hosting is built in via [Arti](https://gitlab.torproject.org/tpo/core/arti) β€” no system `tor` installation required. Both degrade gracefully when disabled.
@@ -274,14 +274,18 @@ See **[SETUP.md β€” Installing ffmpeg](SETUP.md#installing-ffmpeg)** for platfor ### Tor β€” Onion Service -When `enable_tor_support = true` and a Tor daemon is running: +RustChan includes **built-in Tor onion service support via [Arti](https://gitlab.torproject.org/tpo/core/arti)** β€” no system `tor` installation required. Set `enable_tor_support = true` in `settings.toml` and restart. On first launch RustChan will: -- The `.onion` address is read from the hidden-service `hostname` file and displayed on the home page and admin panel -- Setup hints are printed to the console if Tor is detected but not yet configured +1. Download ~2 MB of Tor directory data and bootstrap to the network (~30 seconds) +2. Generate a persistent Ed25519 keypair in `rustchan-data/arti_state/keys/` +3. Derive your permanent `.onion` address from that keypair and start the hidden service +4. Begin accepting and proxying inbound onion connections to the local HTTP port -Tor handles all onion routing independently β€” RustChan binds to its normal port while your `torrc` forwards `.onion` traffic to it. +The `.onion` address appears on the home page and in the admin panel as soon as the service is ready. Subsequent starts are ready in ~5 seconds using the cached consensus in `rustchan-data/arti_cache/`. -See **[SETUP.md β€” Installing Tor](SETUP.md#installing-tor)** for configuration details. +**Back up `rustchan-data/arti_state/keys/`** β€” this directory contains your service keypair. Losing it means a new `.onion` address on the next start. Delete it intentionally to rotate to a new address. + +See **[SETUP.md β€” Tor](SETUP.md#tor--onion-service)** for details on key management and migrating from a previous system `tor` installation.
@@ -360,7 +364,8 @@ max_audio_size_mb = 150 # existing IP hashes and bans will become invalid. cookie_secret = "" -# Display .onion address if a Tor daemon is running. +# Built-in Tor onion service (via Arti β€” no system tor required). +# First run bootstraps in ~30 s; keypair in rustchan-data/arti_state/keys/. enable_tor_support = false # Hard-exit if ffmpeg is not found (default: warn only). @@ -434,7 +439,6 @@ All settings can be overridden via environment variables, which take precedence | `CHAN_WAVEFORM_CACHE_MB` | `200` | Max waveform thumbnail cache per board (MiB) | | `CHAN_BLOCKING_THREADS` | `cpus Γ— 4` | Tokio blocking thread pool size | | `CHAN_ARCHIVE_BEFORE_PRUNE` | `true` | Archive globally before any hard-delete | -| `CHAN_TOR_HOSTNAME_FILE` | *(auto-detected)* | Override path to the Tor `hostname` file | | `RUST_LOG` | `rustchan-cli=info` | Log verbosity |
@@ -542,6 +546,7 @@ RustChan is intentionally minimal — no template engine, no ORM, no JavaScript | HTML rendering | Plain Rust `format!` strings | | Configuration | `settings.toml` (atomic writes) + env var overrides via `once_cell::Lazy` | | Federation | ChanNet API on port 7070 (ZIP-based, text-only) | +| Tor onion service | [Arti](https://gitlab.torproject.org/tpo/core/arti) in-process (`onion-service-service` feature); keypair in `arti_state/keys/` | ### Source Layout @@ -663,7 +668,7 @@ Six built-in themes, selectable via the floating picker on every page. Persisted See **[CHANGELOG.md](CHANGELOG.md)** for the full version history. **Latest — v1.1.0-alpha.2:** -Critical fix: ChanNet gateway posts have no IP — `ip_hash` changed to `Option` throughout (no more 500s on pages with gateway posts) · Log files now written to `rustchan-data/` (not the binary directory) · Log file names fixed (`rustchan.2024-01-15.log` format) · Logs changed from dense JSON to human-readable text · Per-field multipart size caps (~100 KB body, ~4 KB name/subject) eliminate OOM risk from oversized form submissions · Poll duration overflow hardened · Backup system rewrites: `rusqlite::backup` API replaces fragile SQL string, RAII temp-file cleanup, pool exhaustion → 503 · Tor detection mutex poisoning fixed; panic hook hardened with `try_lock()` · DB pool size configurable; `r2d2::Error` correctly maps to 503 · All write transactions upgraded from DEFERRED to `BEGIN IMMEDIATE` · Rotating log files prevent disk exhaustion · 304 response builders fixed · Atomic `settings.toml` writes · Request timeout middleware (slowloris protection) · Worker `JoinHandle`s persisted; graceful shutdown via `CancellationToken` + bounded await · Job recovery on startup for interrupted jobs · ChanNet server graceful shutdown unified with main HTTP server · Background tasks use `tokio::select!` for clean cancellation +**Tor migrated to Arti (built-in, no system `tor` required)** — Arti bootstraps in-process at startup, derives a `.onion` address from a persistent keypair in `arti_state/keys/`, and proxies onion connections to the local HTTP port; no subprocess, no `torrc`, no hostname file polling · Critical fix: ChanNet gateway posts have no IP — `ip_hash` changed to `Option` throughout (no more 500s on pages with gateway posts) · Log files now written to `rustchan-data/` (not the binary directory) · Log file names fixed (`rustchan.2024-01-15.log` format) · Logs changed from dense JSON to human-readable text · Per-field multipart size caps (~100 KB body, ~4 KB name/subject) eliminate OOM risk from oversized form submissions · Poll duration overflow hardened · Backup system rewrites: `rusqlite::backup` API replaces fragile SQL string, RAII temp-file cleanup, pool exhaustion → 503 · DB pool size configurable; `r2d2::Error` correctly maps to 503 · All write transactions upgraded from DEFERRED to `BEGIN IMMEDIATE` · Rotating log files prevent disk exhaustion · 304 response builders fixed · Atomic `settings.toml` writes · Request timeout middleware (slowloris protection) · Worker `JoinHandle`s persisted; graceful shutdown via `CancellationToken` + bounded await · Job recovery on startup for interrupted jobs · ChanNet server graceful shutdown unified with main HTTP server · Background tasks use `tokio::select!` for clean cancellation **v1.1.0-alpha.1:** ChanNet API on port 7070 (federation + RustWave gateway) · Major codebase refactor: `main.rs` shrunk from 1,757 → ~50 lines; `handlers/admin.rs` split into 6 focused files; new `server/` module (server, console, CLI); new `src/media/` module (ffmpeg, convert, thumbnail, exif) · BMP, TIFF, SVG upload support · GIF→WebM inline conversion · All thumbnails output as WebP · SVG placeholders for video/audio/SVG sources · PNG→WebP with size-check fallback · Atomic temp-then-rename for all conversions @@ -691,7 +696,7 @@ Scheduled VACUUM · expired poll vote cleanup · DB size warning banner · job q
-Built with πŸ¦€ Rust  Β·  Powered by SQLite  Β·  Optional integrations: ffmpeg Β· Tor +Built with πŸ¦€ Rust  Β·  Powered by SQLite  Β·  Optional: ffmpeg  Β·  Tor built-in via Arti *Drop it anywhere. It just runs.* diff --git a/audit.toml b/audit.toml new file mode 100644 index 0000000..373e1e0 --- /dev/null +++ b/audit.toml @@ -0,0 +1,3 @@ +# audit.toml +[advisories] +ignore = ["RUSTSEC-2023-0071"] \ No newline at end of file diff --git a/deny.toml b/deny.toml index a325889..d9863f6 100644 --- a/deny.toml +++ b/deny.toml @@ -8,6 +8,20 @@ all-features = false unmaintained = "all" yanked = "deny" +# RUSTSEC-2023-0071: Marvin Attack timing side-channel in rsa 0.9.x. +# No fixed version available. rsa is a transitive dep pulled in by arti-client; +# we do not use RSA decryption directly, and Arti uses it only for TLS relay +# connections where an adaptive chosen-ciphertext oracle is not exposed. +[[advisories.ignore]] +id = "RUSTSEC-2023-0071" + +# RUSTSEC-2024-0436: paste crate marked unmaintained by its author. +# paste is a transitive dep of arti-client / tor-hsservice. No direct usage +# in RustChan code; no functional impact. Will be resolved when Arti updates +# its dependency tree. +[[advisories.ignore]] +id = "RUSTSEC-2024-0436" + [licenses] confidence-threshold = 0.8 @@ -24,6 +38,14 @@ allow = [ "ISC", # Used by webpki-roots (Mozilla CA certificate bundle) "CDLA-Permissive-2.0", + # Used by notify (arti-client transitive dep) + "CC0-1.0", + # Used by option-ext and priority-queue (arti-client transitive deps) + "MPL-2.0", + # Used by priority-queue (arti-client transitive dep) + "LGPL-3.0-or-later", + # Used by xxhash-rust (arti-client transitive dep) + "BSL-1.0", ] [[licenses.exceptions]] @@ -34,6 +56,8 @@ allow = ["MIT", "Apache-2.0", "BSD-2-Clause"] [bans] multiple-versions = "warn" +# ── Pre-existing skips ───────────────────────────────────────────────────────── + # argon2 β†’ password-hash β†’ rand_core 0.6 [[bans.skip]] name = "rand_core" @@ -71,15 +95,164 @@ version = "0.16" name = "windows-sys" version = "0.61" +# ── Arti transitive dep duplicates ──────────────────────────────────────────── +# These arise from arti-client / tor-hsservice pulling in newer versions of +# crates that other parts of the tree already depend on at an older version. +# All are benign version splits with no API conflicts in RustChan code. + [[bans.skip]] -name = "zune-core" +name = "atomic" version = "0.5" [[bans.skip]] -name = "zune-jpeg" -version = "0.5" +name = "bitflags" +version = "1" + +[[bans.skip]] +name = "cpufeatures" +version = "0.2" + +[[bans.skip]] +name = "darling" +version = "0.14" + +[[bans.skip]] +name = "darling" +version = "0.21" + +[[bans.skip]] +name = "darling_core" +version = "0.14" + +[[bans.skip]] +name = "darling_core" +version = "0.21" + +[[bans.skip]] +name = "darling_macro" +version = "0.14" + +[[bans.skip]] +name = "darling_macro" +version = "0.21" + +[[bans.skip]] +name = "itertools" +version = "0.13" + +[[bans.skip]] +name = "rand" +version = "0.8" + +[[bans.skip]] +name = "rand" +version = "0.9" + +[[bans.skip]] +name = "rand_chacha" +version = "0.3" + +[[bans.skip]] +name = "rand_core" +version = "0.9" + +[[bans.skip]] +name = "serde_spanned" +version = "0.6" + +[[bans.skip]] +name = "strsim" +version = "0.10" + +[[bans.skip]] +name = "syn" +version = "1" + +[[bans.skip]] +name = "thiserror" +version = "1" + +[[bans.skip]] +name = "thiserror-impl" +version = "1" + +[[bans.skip]] +name = "toml" +version = "0.8" + +[[bans.skip]] +name = "toml" +version = "0.9" + +[[bans.skip]] +name = "toml_datetime" +version = "0.6" + +[[bans.skip]] +name = "toml_datetime" +version = "0.7" + +[[bans.skip]] +name = "toml_edit" +version = "0.22" + +[[bans.skip]] +name = "windows-core" +version = "0.61" + +[[bans.skip]] +name = "windows-link" +version = "0.1" + +[[bans.skip]] +name = "windows-result" +version = "0.3" + +[[bans.skip]] +name = "windows-strings" +version = "0.4" + +[[bans.skip]] +name = "windows-sys" +version = "0.52" + +[[bans.skip]] +name = "windows-targets" +version = "0.52" + +[[bans.skip]] +name = "windows_aarch64_gnullvm" +version = "0.52" + +[[bans.skip]] +name = "windows_aarch64_msvc" +version = "0.52" + +[[bans.skip]] +name = "windows_i686_gnu" +version = "0.52" + +[[bans.skip]] +name = "windows_i686_gnullvm" +version = "0.52" + +[[bans.skip]] +name = "windows_i686_msvc" +version = "0.52" + +[[bans.skip]] +name = "windows_x86_64_gnu" +version = "0.52" + +[[bans.skip]] +name = "windows_x86_64_gnullvm" +version = "0.52" + +[[bans.skip]] +name = "windows_x86_64_msvc" +version = "0.52" [sources] unknown-registry = "deny" unknown-git = "deny" -allow-registry = ["https://github.com/rust-lang/crates.io-index"] \ No newline at end of file +allow-registry = ["https://github.com/rust-lang/crates.io-index"] diff --git a/ffmpeg-migration.md b/ffmpeg-migration.md new file mode 100644 index 0000000..555e5f7 --- /dev/null +++ b/ffmpeg-migration.md @@ -0,0 +1,1233 @@ +# FFmpeg Static Migration β€” Full Implementation + +Replace every `Command::new("ffmpeg")` / `TokioCommand::new("ffmpeg")` call with +in-process `ffmpeg-next` library calls. After this migration the binary requires +no system ffmpeg or ffprobe installation. + +--- + +## 1. Cargo.toml + +```toml +[dependencies] +# Add this block. Remove nothing else. +ffmpeg-next = { version = "7", default-features = false, features = [ + "static", # compiles libav* from source into the binary + "codec", # libavcodec β€” encode / decode + "format", # libavformat β€” container mux / demux + "filter", # libavfilter β€” scale, showwavespic + "software-resampling", # libswresample β€” audio resampling for Opus + "software-scaling", # libswscale β€” pixel-format conversion +] } +``` + +Pin the ffmpeg source version and supply build flags via `.cargo/config.toml` +(create this file if it does not exist): + +```toml +# .cargo/config.toml +[env] +FFMPEG_BUILD_VERSION = "7.1" +# If nasm is absent on the build machine, add: +# FFMPEG_EXTRA_FLAGS = "--disable-x86asm" +``` + +Build-tool requirements (install once per machine): + +| Tool | Ubuntu/Debian | macOS | +|---|---|---| +| nasm | `apt install nasm` | `brew install nasm` | +| cmake | `apt install cmake` | `brew install cmake` | +| pkg-config | `apt install pkg-config` | `brew install pkg-config` | + +CI (GitHub Actions Ubuntu runner) β€” add before `cargo build`: + +```yaml +- name: Install ffmpeg build deps + run: sudo apt-get install -y nasm cmake pkg-config libssl-dev +``` + +--- + +## 2. `src/media/ffmpeg.rs` β€” complete replacement + +Drop the entire existing file and replace with the following. +All public function signatures are **identical** to the originals. + +```rust +// media/ffmpeg.rs +// +// FFmpeg wrappers using statically linked libav* (ffmpeg-next). +// +// All public signatures are unchanged from the subprocess-based version so +// that no caller in convert.rs, thumbnail.rs, workers/, or detect.rs needs +// to be modified for signature reasons. +// +// Call site changes required elsewhere: +// β€’ detect.rs β€” detection functions become no-ops (see Β§4 below) +// β€’ workers/ β€” TokioCommand::new("ffmpeg") blocks replaced (see Β§3 below) +// +// Thread safety: ffmpeg-next is safe to call from multiple threads once +// init_ffmpeg() has been called. Call it once from main() or detect.rs. + +use anyhow::{Context, Result}; +use ffmpeg_next as ffmpeg; +use ffmpeg_next::{ + codec, filter, format, frame, media, + software::scaling::{context::Context as SwsContext, flag::Flags as SwsFlags}, + Dictionary, Rational, +}; +use std::path::Path; + +// ─── Library initialisation ─────────────────────────────────────────────────── + +/// Initialise the ffmpeg library. Idempotent and thread-safe. +/// +/// Call once at startup (from detect.rs or main.rs) before any codec +/// operation. Safe to call multiple times β€” subsequent calls are no-ops. +pub fn init_ffmpeg() { + ffmpeg::init().expect("ffmpeg library init failed β€” this is a build error"); + // Suppress ffmpeg's internal log output. rustchan's tracing handles all + // user-facing diagnostics. + unsafe { + ffmpeg_next::ffi::av_log_set_level(ffmpeg_next::ffi::AV_LOG_QUIET); + } +} + +// ─── Detection stubs (always true β€” codecs are compiled in) ────────────────── + +/// Always returns true: ffmpeg is compiled into the binary. +#[must_use] +pub fn detect_ffmpeg() -> bool { + true +} + +/// Always returns true: libwebp is compiled in. +#[must_use] +pub fn check_webp_encoder() -> bool { + true +} + +/// Always returns true: libvpx-vp9 is compiled in. +#[must_use] +pub fn check_vp9_encoder() -> bool { + true +} + +/// Always returns true: libopus is compiled in. +#[must_use] +pub fn check_opus_encoder() -> bool { + true +} + +// ─── run_ffmpeg (no longer used β€” kept for any external callers) ────────────── + +/// Deprecated: previously spawned a subprocess. Now always returns Ok(()). +/// Remove callers and delete this function in a follow-up cleanup. +#[allow(dead_code)] +pub fn run_ffmpeg(_args: &[&str]) -> Result<()> { + Ok(()) +} + +// ─── Image β†’ WebP ───────────────────────────────────────────────────────────── + +/// Convert any ffmpeg-readable image (JPEG, PNG, BMP, TIFF, GIF) to WebP. +/// +/// Animated GIF inputs produce animated WebP (loop 0 = loop forever). +/// Metadata is stripped. Quality is fixed at 85 per project spec. +/// +/// # Errors +/// Returns an error if the input cannot be decoded or the output cannot be +/// written. +pub fn ffmpeg_image_to_webp(input: &Path, output: &Path) -> Result<()> { + init_ffmpeg(); + + // ── Open input ──────────────────────────────────────────────────────── + let mut ictx = format::input(input) + .with_context(|| format!("ffmpeg_image_to_webp: cannot open {}", input.display()))?; + + let in_stream = ictx + .streams() + .best(media::Type::Video) + .context("ffmpeg_image_to_webp: no video/image stream in input")?; + let in_idx = in_stream.index(); + let in_tb = in_stream.time_base(); + + let mut decoder = codec::context::Context::from_parameters(in_stream.parameters()) + .context("ffmpeg_image_to_webp: decoder context")? + .decoder() + .video() + .context("ffmpeg_image_to_webp: open video decoder")?; + + // ── Open output ─────────────────────────────────────────────────────── + let mut octx = format::output(output) + .with_context(|| format!("ffmpeg_image_to_webp: cannot open output {}", output.display()))?; + + let webp_codec = codec::encoder::find_by_name("libwebp") + .context("libwebp encoder not found β€” static build is missing the codec")?; + + let mut enc_ctx = codec::context::Context::new_with_codec(webp_codec) + .encoder() + .video() + .context("ffmpeg_image_to_webp: encoder context")?; + + // Dimensions and format are set from the first decoded frame because some + // inputs (e.g. TIFF) report wrong dimensions in stream parameters. + // We defer the actual encoder open until after decoding the first frame. + + let mut out_stream = octx.add_stream(webp_codec) + .context("ffmpeg_image_to_webp: add output stream")?; + + let global_header = octx + .format() + .flags() + .contains(format::flag::Flags::GLOBAL_HEADER); + if global_header { + enc_ctx.set_flags(codec::flag::Flags::GLOBAL_HEADER); + } + + // ── Decode β†’ scale β†’ encode loop ───────────────────────────────────── + let mut encoder_opened = false; + let mut sws: Option = None; + let mut frame_count = 0u64; + + let mut encode_and_write = |enc: &mut codec::encoder::video::Encoder, + octx: &mut format::context::Output, + frame: Option<&frame::Video>| + -> Result<()> { + enc.send_frame(frame).context("ffmpeg_image_to_webp: send_frame")?; + let mut pkt = ffmpeg_next::Packet::empty(); + while enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(Rational(1, 25), out_stream.time_base()); + pkt.write_interleaved(octx) + .context("ffmpeg_image_to_webp: write_interleaved")?; + } + Ok(()) + }; + + for (stream, packet) in ictx.packets() { + if stream.index() != in_idx { + continue; + } + decoder + .send_packet(&packet) + .context("ffmpeg_image_to_webp: send_packet")?; + + let mut decoded = frame::Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + if !encoder_opened { + // First frame: configure encoder dimensions and open it. + enc_ctx.set_width(decoded.width()); + enc_ctx.set_height(decoded.height()); + enc_ctx.set_format(ffmpeg_next::format::Pixel::YUVA420P); + enc_ctx.set_time_base(Rational(1, 25)); + + let mut opts = Dictionary::new(); + opts.set("quality", "85"); + opts.set("loop", "0"); // animated WebP loops forever (GIF parity) + opts.set("lossless", "0"); + + enc_ctx + .open_with(opts) + .context("ffmpeg_image_to_webp: open encoder")?; + out_stream.set_parameters(&enc_ctx); + + octx.write_header() + .context("ffmpeg_image_to_webp: write_header")?; + encoder_opened = true; + + // Pixel format converter: input format β†’ YUVA420P for libwebp. + sws = Some( + SwsContext::get( + decoded.format(), + decoded.width(), + decoded.height(), + ffmpeg_next::format::Pixel::YUVA420P, + decoded.width(), + decoded.height(), + SwsFlags::BILINEAR, + ) + .context("ffmpeg_image_to_webp: sws_getContext")?, + ); + } + + // Convert pixel format. + let mut rgb_frame = frame::Video::new( + ffmpeg_next::format::Pixel::YUVA420P, + decoded.width(), + decoded.height(), + ); + if let Some(ref mut s) = sws { + s.run(&decoded, &mut rgb_frame) + .context("ffmpeg_image_to_webp: sws_scale")?; + } + rgb_frame.set_pts(Some(frame_count as i64)); + frame_count += 1; + + // We need mutable enc_ctx below β€” restructure to avoid borrow conflict. + enc_ctx + .send_frame(Some(&rgb_frame)) + .context("ffmpeg_image_to_webp: send_frame")?; + let mut pkt = ffmpeg_next::Packet::empty(); + while enc_ctx.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(Rational(1, 25), out_stream.time_base()); + pkt.write_interleaved(&mut octx) + .context("ffmpeg_image_to_webp: write_interleaved")?; + } + } + } + + // Flush decoder. + decoder + .send_eof() + .context("ffmpeg_image_to_webp: send_eof to decoder")?; + let mut decoded = frame::Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + // (same encode block as above β€” flush remaining frames) + let mut rgb_frame = frame::Video::new( + ffmpeg_next::format::Pixel::YUVA420P, + decoded.width(), + decoded.height(), + ); + if let Some(ref mut s) = sws { + s.run(&decoded, &mut rgb_frame) + .context("ffmpeg_image_to_webp: sws_scale flush")?; + } + rgb_frame.set_pts(Some(frame_count as i64)); + frame_count += 1; + enc_ctx + .send_frame(Some(&rgb_frame)) + .context("ffmpeg_image_to_webp: flush send_frame")?; + let mut pkt = ffmpeg_next::Packet::empty(); + while enc_ctx.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(Rational(1, 25), out_stream.time_base()); + pkt.write_interleaved(&mut octx) + .context("ffmpeg_image_to_webp: flush write")?; + } + } + + // Flush encoder. + enc_ctx + .send_eof() + .context("ffmpeg_image_to_webp: send_eof to encoder")?; + let mut pkt = ffmpeg_next::Packet::empty(); + while enc_ctx.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(Rational(1, 25), out_stream.time_base()); + pkt.write_interleaved(&mut octx) + .context("ffmpeg_image_to_webp: final write")?; + } + + octx.write_trailer() + .context("ffmpeg_image_to_webp: write_trailer")?; + + Ok(()) +} + +// ─── Thumbnail ──────────────────────────────────────────────────────────────── + +/// Extract the first frame from an image or video, scale to fit within +/// `max_dim Γ— max_dim` (aspect preserved), and save as WebP quality 80. +/// +/// Equivalent to: +/// ffmpeg -i -vframes 1 +/// -vf "scale='if(gt(iw,ih),MAX,-2)':'if(gt(iw,ih),-2,MAX)'" +/// -c:v libwebp -quality 80 +/// +/// # Errors +/// Returns an error if the input cannot be demuxed/decoded or the output +/// cannot be written. +pub fn ffmpeg_thumbnail(input: &Path, output: &Path, max_dim: u32) -> Result<()> { + init_ffmpeg(); + + let mut ictx = format::input(input) + .with_context(|| format!("ffmpeg_thumbnail: cannot open {}", input.display()))?; + + let in_stream = ictx + .streams() + .best(media::Type::Video) + .context("ffmpeg_thumbnail: no video stream")?; + let in_idx = in_stream.index(); + + let mut decoder = codec::context::Context::from_parameters(in_stream.parameters()) + .context("ffmpeg_thumbnail: decoder context")? + .decoder() + .video() + .context("ffmpeg_thumbnail: open decoder")?; + + // Seek to the first key frame (important for video sources). + let _ = ictx.seek(0, ..0); + + // Decode until we get one complete frame. + let mut first_frame: Option = None; + 'outer: for (stream, packet) in ictx.packets() { + if stream.index() != in_idx { + continue; + } + decoder + .send_packet(&packet) + .context("ffmpeg_thumbnail: send_packet")?; + let mut f = frame::Video::empty(); + while decoder.receive_frame(&mut f).is_ok() { + first_frame = Some(f); + break 'outer; + } + } + // Flush decoder in case the frame was buffered. + if first_frame.is_none() { + let _ = decoder.send_eof(); + let mut f = frame::Video::empty(); + if decoder.receive_frame(&mut f).is_ok() { + first_frame = Some(f); + } + } + + let src_frame = first_frame.context("ffmpeg_thumbnail: no frame decoded from input")?; + + // ── Scale to fit max_dim Γ— max_dim, preserving aspect ratio ────────── + let (src_w, src_h) = (src_frame.width(), src_frame.height()); + let (dst_w, dst_h) = scale_dims(src_w, src_h, max_dim); + + let mut scaled = frame::Video::new( + ffmpeg_next::format::Pixel::YUVA420P, + dst_w, + dst_h, + ); + let mut sws = SwsContext::get( + src_frame.format(), + src_w, + src_h, + ffmpeg_next::format::Pixel::YUVA420P, + dst_w, + dst_h, + SwsFlags::LANCZOS, + ) + .context("ffmpeg_thumbnail: sws_getContext")?; + sws.run(&src_frame, &mut scaled) + .context("ffmpeg_thumbnail: sws_scale")?; + scaled.set_pts(Some(0)); + + // ── Encode as WebP quality 80 ───────────────────────────────────────── + let webp_codec = codec::encoder::find_by_name("libwebp") + .context("ffmpeg_thumbnail: libwebp encoder missing")?; + + let mut octx = format::output(output) + .with_context(|| format!("ffmpeg_thumbnail: cannot open output {}", output.display()))?; + + let mut enc_ctx = codec::context::Context::new_with_codec(webp_codec) + .encoder() + .video() + .context("ffmpeg_thumbnail: encoder context")?; + + enc_ctx.set_width(dst_w); + enc_ctx.set_height(dst_h); + enc_ctx.set_format(ffmpeg_next::format::Pixel::YUVA420P); + enc_ctx.set_time_base(Rational(1, 1)); + + let global_header = octx + .format() + .flags() + .contains(format::flag::Flags::GLOBAL_HEADER); + if global_header { + enc_ctx.set_flags(codec::flag::Flags::GLOBAL_HEADER); + } + + let mut opts = Dictionary::new(); + opts.set("quality", "80"); + opts.set("lossless", "0"); + + let mut enc = enc_ctx + .open_with(opts) + .context("ffmpeg_thumbnail: open encoder")?; + + let mut out_stream = octx.add_stream(webp_codec) + .context("ffmpeg_thumbnail: add output stream")?; + out_stream.set_parameters(&enc); + + octx.write_header() + .context("ffmpeg_thumbnail: write_header")?; + + enc.send_frame(Some(&scaled)) + .context("ffmpeg_thumbnail: send_frame")?; + enc.send_eof() + .context("ffmpeg_thumbnail: send_eof")?; + + let mut pkt = ffmpeg_next::Packet::empty(); + while enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(Rational(1, 1), out_stream.time_base()); + pkt.write_interleaved(&mut octx) + .context("ffmpeg_thumbnail: write_interleaved")?; + } + + octx.write_trailer() + .context("ffmpeg_thumbnail: write_trailer")?; + + Ok(()) +} + +// ─── Codec probe ────────────────────────────────────────────────────────────── + +/// Return the lowercase codec name for the primary video stream in `path`. +/// +/// Replaces the `ffprobe` subprocess. Opens the container, reads the stream +/// parameters, and returns the codec descriptor name β€” no decoding is done. +/// +/// Returns e.g. `"vp9"`, `"av1"`, `"h264"`. +/// +/// # Errors +/// Returns an error if the file cannot be opened or contains no video stream. +pub fn probe_video_codec(path: &str) -> Result { + init_ffmpeg(); + + let ictx = format::input(&path) + .with_context(|| format!("probe_video_codec: cannot open {path}"))?; + + let stream = ictx + .streams() + .best(media::Type::Video) + .with_context(|| format!("probe_video_codec: no video stream in {path}"))?; + + let codec_id = stream.parameters().id(); + + // ffmpeg_next exposes the codec name through the descriptor. + let name = unsafe { + let desc = ffmpeg_next::ffi::avcodec_descriptor_get(codec_id.into()); + if desc.is_null() { + return Err(anyhow::anyhow!( + "probe_video_codec: no codec descriptor for id {:?}", + codec_id + )); + } + std::ffi::CStr::from_ptr((*desc).name) + .to_string_lossy() + .to_ascii_lowercase() + }; + + if name.is_empty() { + return Err(anyhow::anyhow!( + "probe_video_codec: empty codec name for {path}" + )); + } + + Ok(name) +} + +// ─── Video transcode (MP4 / WebM-AV1 β†’ WebM VP9+Opus) ─────────────────────── + +/// Transcode `input` to WebM with VP9 video and Opus audio, writing to `output`. +/// +/// Equivalent to: +/// ffmpeg -i -c:v libvpx-vp9 -crf 30 -b:v 0 +/// -c:a libopus -b:a 128k -map_metadata -1 +/// +/// Called from `workers/mod.rs` inside `spawn_blocking`. +/// +/// # Errors +/// Returns an error if any stage of the transcode fails. +pub fn ffmpeg_transcode_to_webm(input: &Path, output: &Path) -> Result<()> { + init_ffmpeg(); + + // ── Input context ───────────────────────────────────────────────────── + let mut ictx = format::input(input) + .with_context(|| format!("ffmpeg_transcode_to_webm: cannot open {}", input.display()))?; + + // Find video and audio streams. + let video_in_idx = ictx + .streams() + .best(media::Type::Video) + .map(|s| s.index()); + let audio_in_idx = ictx + .streams() + .best(media::Type::Audio) + .map(|s| s.index()); + + if video_in_idx.is_none() { + return Err(anyhow::anyhow!( + "ffmpeg_transcode_to_webm: no video stream in {}", + input.display() + )); + } + + // ── Output context ──────────────────────────────────────────────────── + let mut octx = format::output(output) + .with_context(|| format!("ffmpeg_transcode_to_webm: cannot open output {}", output.display()))?; + + // ── Video encoder (VP9) ─────────────────────────────────────────────── + let vp9_codec = codec::encoder::find_by_name("libvpx-vp9") + .context("libvpx-vp9 encoder missing from static build")?; + + let in_video = ictx + .stream(video_in_idx.unwrap()) + .context("ffmpeg_transcode_to_webm: get video stream")?; + + let mut v_dec = codec::context::Context::from_parameters(in_video.parameters()) + .context("ffmpeg_transcode_to_webm: video decoder context")? + .decoder() + .video() + .context("ffmpeg_transcode_to_webm: open video decoder")?; + + let mut venc_ctx = codec::context::Context::new_with_codec(vp9_codec) + .encoder() + .video() + .context("ffmpeg_transcode_to_webm: video encoder context")?; + + venc_ctx.set_width(v_dec.width()); + venc_ctx.set_height(v_dec.height()); + venc_ctx.set_format(ffmpeg_next::format::Pixel::YUV420P); + venc_ctx.set_time_base(in_video.avg_frame_rate().invert()); + + let mut v_opts = Dictionary::new(); + v_opts.set("crf", "30"); + v_opts.set("b:v", "0"); // constant quality mode + v_opts.set("deadline", "good"); + v_opts.set("cpu-used", "2"); + + if octx.format().flags().contains(format::flag::Flags::GLOBAL_HEADER) { + venc_ctx.set_flags(codec::flag::Flags::GLOBAL_HEADER); + } + + let mut v_enc = venc_ctx + .open_with(v_opts) + .context("ffmpeg_transcode_to_webm: open VP9 encoder")?; + let mut out_v = octx.add_stream(vp9_codec) + .context("ffmpeg_transcode_to_webm: add video stream")?; + out_v.set_parameters(&v_enc); + + // ── Audio encoder (Opus) ────────────────────────────────────────────── + let mut a_dec_opt: Option = None; + let mut a_enc_opt: Option = None; + let mut out_a_idx: Option = None; + let mut swr_opt: Option = None; + + if let Some(a_idx) = audio_in_idx { + let opus_codec = codec::encoder::find_by_name("libopus") + .context("libopus encoder missing from static build")?; + + let in_audio = ictx + .stream(a_idx) + .context("ffmpeg_transcode_to_webm: get audio stream")?; + + let a_dec = codec::context::Context::from_parameters(in_audio.parameters()) + .context("ffmpeg_transcode_to_webm: audio decoder context")? + .decoder() + .audio() + .context("ffmpeg_transcode_to_webm: open audio decoder")?; + + let mut aenc_ctx = codec::context::Context::new_with_codec(opus_codec) + .encoder() + .audio() + .context("ffmpeg_transcode_to_webm: audio encoder context")?; + + aenc_ctx.set_rate(48000); // Opus native rate + aenc_ctx.set_channel_layout(ffmpeg_next::channel_layout::ChannelLayout::STEREO); + aenc_ctx.set_format(ffmpeg_next::format::Sample::F32( + ffmpeg_next::format::sample::Type::Packed, + )); + aenc_ctx.set_time_base(Rational(1, 48000)); + if octx.format().flags().contains(format::flag::Flags::GLOBAL_HEADER) { + aenc_ctx.set_flags(codec::flag::Flags::GLOBAL_HEADER); + } + + let mut a_opts = Dictionary::new(); + a_opts.set("b:a", "128k"); + + let a_enc = aenc_ctx + .open_with(a_opts) + .context("ffmpeg_transcode_to_webm: open Opus encoder")?; + + // Resampler: source format β†’ Opus (48 kHz stereo f32). + let swr = ffmpeg_next::software::resampling::context::Context::get( + a_dec.format(), + a_dec.channel_layout(), + a_dec.rate(), + ffmpeg_next::format::Sample::F32(ffmpeg_next::format::sample::Type::Packed), + ffmpeg_next::channel_layout::ChannelLayout::STEREO, + 48000, + ) + .context("ffmpeg_transcode_to_webm: swr_alloc_set_opts")?; + + let mut out_a = octx.add_stream(opus_codec) + .context("ffmpeg_transcode_to_webm: add audio stream")?; + out_a.set_parameters(&a_enc); + out_a_idx = Some(out_a.index()); + + a_dec_opt = Some(a_dec); + a_enc_opt = Some(a_enc); + swr_opt = Some(swr); + } + + // ── Transcode loop ──────────────────────────────────────────────────── + octx.write_header() + .context("ffmpeg_transcode_to_webm: write_header")?; + + let mut v_pts: i64 = 0; + let mut a_pts: i64 = 0; + + let video_in_idx = video_in_idx.unwrap(); + + for (stream, packet) in ictx.packets() { + let idx = stream.index(); + + if idx == video_in_idx { + v_dec.send_packet(&packet) + .context("ffmpeg_transcode_to_webm: send video packet")?; + let mut vf = frame::Video::empty(); + while v_dec.receive_frame(&mut vf).is_ok() { + // Reformat to YUV420P if needed. + let enc_frame = if vf.format() == ffmpeg_next::format::Pixel::YUV420P { + vf.clone() + } else { + let mut converted = frame::Video::new( + ffmpeg_next::format::Pixel::YUV420P, + vf.width(), + vf.height(), + ); + let mut sws = SwsContext::get( + vf.format(), vf.width(), vf.height(), + ffmpeg_next::format::Pixel::YUV420P, vf.width(), vf.height(), + SwsFlags::BILINEAR, + ).context("ffmpeg_transcode_to_webm: sws for video reformat")?; + sws.run(&vf, &mut converted) + .context("ffmpeg_transcode_to_webm: sws_scale video")?; + converted + }; + let mut enc_frame = enc_frame; + enc_frame.set_pts(Some(v_pts)); + v_pts += 1; + + v_enc.send_frame(Some(&enc_frame)) + .context("ffmpeg_transcode_to_webm: send video frame")?; + let mut pkt = ffmpeg_next::Packet::empty(); + while v_enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(v_enc.time_base(), out_v.time_base()); + pkt.write_interleaved(&mut octx) + .context("ffmpeg_transcode_to_webm: write video pkt")?; + } + } + } else if Some(idx) == audio_in_idx { + if let (Some(ref mut a_dec), Some(ref mut a_enc), Some(ref mut swr), Some(out_a)) = + (&mut a_dec_opt, &mut a_enc_opt, &mut swr_opt, out_a_idx) + { + a_dec.send_packet(&packet) + .context("ffmpeg_transcode_to_webm: send audio packet")?; + let mut af = frame::Audio::empty(); + while a_dec.receive_frame(&mut af).is_ok() { + let mut resampled = frame::Audio::empty(); + swr.run(&af, &mut resampled) + .context("ffmpeg_transcode_to_webm: swr_convert")?; + resampled.set_pts(Some(a_pts)); + a_pts += resampled.samples() as i64; + + a_enc.send_frame(Some(&resampled)) + .context("ffmpeg_transcode_to_webm: send audio frame")?; + let mut pkt = ffmpeg_next::Packet::empty(); + while a_enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(out_a); + pkt.rescale_ts(a_enc.time_base(), octx.stream(out_a).unwrap().time_base()); + pkt.write_interleaved(&mut octx) + .context("ffmpeg_transcode_to_webm: write audio pkt")?; + } + } + } + } + } + + // ── Flush video encoder ─────────────────────────────────────────────── + v_dec.send_eof().ok(); + let mut vf = frame::Video::empty(); + while v_dec.receive_frame(&mut vf).is_ok() { + vf.set_pts(Some(v_pts)); + v_pts += 1; + v_enc.send_frame(Some(&vf)).ok(); + let mut pkt = ffmpeg_next::Packet::empty(); + while v_enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(v_enc.time_base(), out_v.time_base()); + pkt.write_interleaved(&mut octx).ok(); + } + } + v_enc.send_eof().ok(); + let mut pkt = ffmpeg_next::Packet::empty(); + while v_enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.rescale_ts(v_enc.time_base(), out_v.time_base()); + pkt.write_interleaved(&mut octx).ok(); + } + + // ── Flush audio encoder ─────────────────────────────────────────────── + if let (Some(ref mut a_dec), Some(ref mut a_enc), Some(out_a)) = + (a_dec_opt, a_enc_opt, out_a_idx) + { + a_dec.send_eof().ok(); + let mut af = frame::Audio::empty(); + while a_dec.receive_frame(&mut af).is_ok() { + a_enc.send_frame(Some(&af)).ok(); + } + a_enc.send_eof().ok(); + let mut pkt = ffmpeg_next::Packet::empty(); + while a_enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(out_a); + pkt.write_interleaved(&mut octx).ok(); + } + } + + octx.write_trailer() + .context("ffmpeg_transcode_to_webm: write_trailer")?; + + Ok(()) +} + +// ─── Audio waveform PNG ─────────────────────────────────────────────────────── + +/// Render a waveform image for an audio file via libavfilter's `showwavespic`. +/// +/// Equivalent to: +/// ffmpeg -i +/// -filter_complex "showwavespic=s=WxH:colors=0x888888" +/// -frames:v 1 +/// +/// Called from `workers/mod.rs` inside `spawn_blocking`. +/// +/// # Errors +/// Returns an error if the filtergraph cannot be built or the PNG cannot be +/// written. +pub fn ffmpeg_audio_waveform( + input: &Path, + output: &Path, + width: u32, + height: u32, +) -> Result<()> { + init_ffmpeg(); + + // ── Build filter graph ──────────────────────────────────────────────── + // showwavespic reads the *entire* audio stream and produces a single + // frame. We route it through a buffer source β†’ showwavespic β†’ buffersink. + let mut ictx = format::input(input) + .with_context(|| format!("ffmpeg_audio_waveform: cannot open {}", input.display()))?; + + let in_stream = ictx + .streams() + .best(media::Type::Audio) + .context("ffmpeg_audio_waveform: no audio stream")?; + let in_idx = in_stream.index(); + + let mut a_dec = codec::context::Context::from_parameters(in_stream.parameters()) + .context("ffmpeg_audio_waveform: decoder context")? + .decoder() + .audio() + .context("ffmpeg_audio_waveform: open decoder")?; + + // Build lavfi graph: + // abuffer β†’ showwavespic=s=WxH:colors=0x888888 β†’ buffersink + let filter_str = format!("showwavespic=s={width}x{height}:colors=0x888888"); + + let mut graph = filter::Graph::new(); + + // abuffer: feed raw audio frames into the graph. + let abuf_args = format!( + "sample_rate={}:sample_fmt={}:channel_layout=0x{:x}:time_base={}/{}", + a_dec.rate(), + ffmpeg_next::format::Sample::name(a_dec.format()), + a_dec.channel_layout().bits(), + in_stream.time_base().0, + in_stream.time_base().1, + ); + graph + .add(&filter::find("abuffer").context("abuffer filter not found")?, "in", &abuf_args) + .context("ffmpeg_audio_waveform: add abuffer")?; + + // showwavespic filter + graph + .add( + &filter::find("showwavespic").context("showwavespic filter not found")?, + "showwavespic", + &filter_str, + ) + .context("ffmpeg_audio_waveform: add showwavespic")?; + + // buffersink: pull the rendered frame out. + graph + .add( + &filter::find("buffersink").context("buffersink filter not found")?, + "out", + "", + ) + .context("ffmpeg_audio_waveform: add buffersink")?; + + // Link: in β†’ showwavespic β†’ out + { + let mut in_node = graph.get("in").context("ffmpeg_audio_waveform: get abuffer")?; + let mut wave_node = graph.get("showwavespic").context("ffmpeg_audio_waveform: get showwavespic")?; + let mut out_node = graph.get("out").context("ffmpeg_audio_waveform: get buffersink")?; + in_node + .output("default", 0) + .context("ffmpeg_audio_waveform: abuffer output")? + .input("default", 0) + .context("ffmpeg_audio_waveform: showwavespic input")? + .add() + .context("ffmpeg_audio_waveform: link inβ†’wave")?; + wave_node + .output("default", 0) + .context("ffmpeg_audio_waveform: showwavespic output")? + .input("default", 0) + .context("ffmpeg_audio_waveform: buffersink input")? + .add() + .context("ffmpeg_audio_waveform: link waveβ†’out")?; + } + graph + .validate() + .context("ffmpeg_audio_waveform: filter graph validate")?; + + // ── Feed all audio frames into the graph ────────────────────────────── + for (stream, packet) in ictx.packets() { + if stream.index() != in_idx { + continue; + } + a_dec + .send_packet(&packet) + .context("ffmpeg_audio_waveform: send_packet")?; + let mut frame = frame::Audio::empty(); + while a_dec.receive_frame(&mut frame).is_ok() { + graph + .get("in") + .context("ffmpeg_audio_waveform: get abuffer for push")? + .source() + .add(&frame.into()) + .context("ffmpeg_audio_waveform: push frame")?; + } + } + // Signal EOF so showwavespic renders the final frame. + a_dec.send_eof().ok(); + let mut frame = frame::Audio::empty(); + while a_dec.receive_frame(&mut frame).is_ok() { + graph + .get("in") + .unwrap() + .source() + .add(&frame.into()) + .ok(); + } + graph + .get("in") + .unwrap() + .source() + .flush() + .context("ffmpeg_audio_waveform: flush abuffer")?; + + // ── Pull the rendered video frame from the sink ─────────────────────── + let mut rendered = frame::Video::empty(); + graph + .get("out") + .context("ffmpeg_audio_waveform: get buffersink for pull")? + .sink() + .frame(&mut rendered) + .context("ffmpeg_audio_waveform: pull rendered frame")?; + + // ── Encode rendered frame as PNG ────────────────────────────────────── + let png_codec = codec::encoder::find_by_name("png") + .context("png encoder not found in static build")?; + + let mut octx = format::output(output) + .with_context(|| format!("ffmpeg_audio_waveform: cannot open output {}", output.display()))?; + + let mut enc_ctx = codec::context::Context::new_with_codec(png_codec) + .encoder() + .video() + .context("ffmpeg_audio_waveform: PNG encoder context")?; + + enc_ctx.set_width(width); + enc_ctx.set_height(height); + enc_ctx.set_format(ffmpeg_next::format::Pixel::RGB24); + enc_ctx.set_time_base(Rational(1, 1)); + + let mut enc = enc_ctx + .open() + .context("ffmpeg_audio_waveform: open PNG encoder")?; + + // showwavespic outputs RGBA; convert to RGB24 for PNG encoder. + let mut rgb = frame::Video::new(ffmpeg_next::format::Pixel::RGB24, width, height); + let mut sws = SwsContext::get( + rendered.format(), width, height, + ffmpeg_next::format::Pixel::RGB24, width, height, + SwsFlags::BILINEAR, + ) + .context("ffmpeg_audio_waveform: sws for RGB24 convert")?; + sws.run(&rendered, &mut rgb) + .context("ffmpeg_audio_waveform: sws_scale to RGB24")?; + rgb.set_pts(Some(0)); + + let mut out_stream = octx.add_stream(png_codec) + .context("ffmpeg_audio_waveform: add PNG stream")?; + out_stream.set_parameters(&enc); + + octx.write_header() + .context("ffmpeg_audio_waveform: write_header")?; + + enc.send_frame(Some(&rgb)) + .context("ffmpeg_audio_waveform: send_frame")?; + enc.send_eof() + .context("ffmpeg_audio_waveform: send_eof")?; + + let mut pkt = ffmpeg_next::Packet::empty(); + while enc.receive_packet(&mut pkt).is_ok() { + pkt.set_stream(0); + pkt.write_interleaved(&mut octx) + .context("ffmpeg_audio_waveform: write_interleaved")?; + } + + octx.write_trailer() + .context("ffmpeg_audio_waveform: write_trailer")?; + + Ok(()) +} + +// ─── Dead-code stubs (kept for API compatibility) ───────────────────────────── + +/// Formerly converted GIF β†’ WebM/VP9. Superseded by animated WebP path. +/// Retained as dead code. Remove in a follow-up cleanup. +#[allow(dead_code)] +pub fn ffmpeg_gif_to_webm(input: &Path, output: &Path) -> Result<()> { + ffmpeg_transcode_to_webm(input, output) +} + +// ─── Internal helpers ───────────────────────────────────────────────────────── + +/// Compute output dimensions that fit within `max_dim Γ— max_dim` while +/// preserving the source aspect ratio. The smaller axis is rounded to an +/// even number (required by many YUV codecs). +fn scale_dims(src_w: u32, src_h: u32, max_dim: u32) -> (u32, u32) { + if src_w == 0 || src_h == 0 { + return (max_dim, max_dim); + } + let (w, h) = if src_w >= src_h { + let h = (src_h * max_dim / src_w).max(2) & !1; + (max_dim, h) + } else { + let w = (src_w * max_dim / src_h).max(2) & !1; + (w, max_dim) + }; + (w, h) +} +``` + +--- + +## 3. `src/workers/mod.rs` β€” surgical changes only + +Only the two sections that spawn `TokioCommand::new("ffmpeg")` change. +Everything else (prepare, finalise, timeout logic, DB updates) is untouched. + +### 3a. Remove the import + +```rust +// REMOVE this line: +use tokio::process::Command as TokioCommand; +// REMOVE this if only used by ffmpeg spawn: +use std::process::Stdio; +``` + +### 3b. `transcode_video()` β€” replace the subprocess block + +The function currently has three phases: `transcode_video_prepare` (spawn_blocking), +subprocess spawn + wait, `transcode_video_finalise` (spawn_blocking). +Replace only **phase 2** (lines roughly 490–520): + +```rust +// ── REMOVE: TokioCommand subprocess spawn ───────────────────────────────── +// let child = TokioCommand::new("ffmpeg") +// .args(&args) +// .stderr(Stdio::piped()) +// .stdout(Stdio::null()) +// .kill_on_drop(true) +// .spawn() +// .map_err(|e| anyhow::anyhow!("failed to spawn ffmpeg: {e}"))?; +// +// match timeout(ffmpeg_timeout, child.wait_with_output()).await { ... } + +// ── REPLACE WITH: in-process library call ──────────────────────────────── +// `args` is no longer needed β€” pass src/dst paths directly. +// Re-derive them from the prepare result (src_path and tmp.path()). +let src_path2 = src_path.clone(); +let tmp_path2 = tmp.path().to_path_buf(); +let timed_out = timeout( + ffmpeg_timeout, + tokio::task::spawn_blocking(move || { + crate::media::ffmpeg::ffmpeg_transcode_to_webm(&src_path2, &tmp_path2) + }), +) +.await; + +match timed_out { + Ok(Ok(Ok(()))) => {} + Ok(Ok(Err(e))) => return Err(e), + Ok(Err(join_err)) => { + return Err(anyhow::anyhow!("spawn_blocking panicked: {join_err}")) + } + Err(_elapsed) => { + // tmp is still alive here β€” NamedTempFile drops and removes the + // partial output when this function returns, so no cleanup needed. + warn!( + "VideoTranscode: post {post_id} timed out after {timeout_secs}s" + ); + return Err(anyhow::anyhow!( + "transcode timed out after {timeout_secs}s" + )); + } +} +``` + +> **Note on `args`**: `transcode_video_prepare` builds a `Vec` of +> ffmpeg CLI arguments. After migration this vector is unused. You can either +> leave the prepare function as-is (the `args` binding is silently dropped) or +> simplify `transcode_video_prepare` to return only +> `(src_path, webm_abs, webm_rel, webm_name, tmp)` by removing the argument +> construction block. The prepare function is only called from one place, so +> either approach is safe. + +### 3b. `generate_waveform()` β€” replace the subprocess block + +Same pattern. Replace only the `TokioCommand` phase between `waveform_prepare` +and `waveform_finalise`: + +```rust +// ── REMOVE: TokioCommand subprocess spawn ───────────────────────────────── +// let child = TokioCommand::new("ffmpeg") +// .args(&args) +// .stderr(Stdio::piped()) +// .stdout(Stdio::null()) +// .kill_on_drop(true) +// .spawn() +// .map_err(...)?; +// match timeout(ffmpeg_timeout, child.wait_with_output()).await { ... } + +// ── REPLACE WITH ────────────────────────────────────────────────────────── +let src_path3 = src.clone(); // src comes from waveform_prepare +let out_path3 = png_abs.clone(); +let thumb_size = CONFIG.thumb_size; + +let timed_out = timeout( + ffmpeg_timeout, + tokio::task::spawn_blocking(move || { + crate::media::ffmpeg::ffmpeg_audio_waveform( + &src_path3, + &out_path3, + thumb_size, + thumb_size / 2, + ) + }), +) +.await; + +match timed_out { + Ok(Ok(Ok(()))) => {} + Ok(Ok(Err(e))) => return Err(e), + Ok(Err(join_err)) => { + return Err(anyhow::anyhow!("spawn_blocking panicked: {join_err}")) + } + Err(_elapsed) => { + warn!("AudioWaveform: post {post_id} timed out after {timeout_secs}s"); + return Err(anyhow::anyhow!( + "waveform timed out after {timeout_secs}s" + )); + } +} +``` + +> **Note**: `waveform_prepare` currently passes `src_str` and `tmp_str` as +> strings for the CLI arg list. After migration the only paths needed are the +> original `src: PathBuf` and the temp file path from `tmp_png.path()`. +> The prepare function can be simplified to not build a `Vec` at all, +> but this is optional cleanup. + +--- + +## 4. `src/detect.rs` β€” ffmpeg section replacement + +Replace the entire ffmpeg detection section (roughly lines 38–175) with: + +```rust +// ─── ffmpeg ─────────────────────────────────────────────────────────────────── + +/// Initialise the statically linked ffmpeg library and report it as available. +/// +/// Previously probed for the `ffmpeg` binary on PATH. Now just calls +/// `init_ffmpeg()` and always returns `Available`. +/// +/// The `require_ffmpeg` parameter is retained for API compatibility; it is +/// ignored because ffmpeg is always present in a static build. +pub fn detect_ffmpeg(_require_ffmpeg: bool) -> ToolStatus { + crate::media::ffmpeg::init_ffmpeg(); + tracing::info!( + target: "detect", + available = true, + "ffmpeg compiled-in β€” media conversion and thumbnails always enabled" + ); + ToolStatus::Available +} + +/// Always returns true: libwebp is compiled into the static ffmpeg build. +pub fn detect_webp_encoder(_ffmpeg_ok: bool) -> bool { + tracing::info!(target: "detect", webp = true, "libwebp compiled-in"); + true +} + +/// Always returns true: libvpx-vp9 and libopus are compiled in. +pub fn detect_webm_encoder(_ffmpeg_ok: bool) -> bool { + tracing::info!( + target: "detect", + vp9 = true, + opus = true, + "VP9 + Opus compiled-in β€” MP4β†’WebM transcoding always enabled" + ); + true +} +``` + +Delete the following functions entirely (they are only reached when a codec is +absent, which can never happen with a static build): + +- `webp_install_hint()` +- `webm_install_hint(has_vp9, has_opus)` + +--- + +## 5. Optional: simplify `src/media/mod.rs` + +`MediaProcessor::new()` currently probes for ffmpeg at construction time. +With a static build the probes always succeed, so the constructor can be +simplified. This is safe to skip β€” the existing code still works correctly. + +```rust +// Optional simplification of MediaProcessor::new() +#[must_use] +pub fn new() -> Self { + // ffmpeg is compiled in β€” no runtime probe needed. + crate::media::ffmpeg::init_ffmpeg(); + Self { + ffmpeg_available: true, + ffmpeg_webp_available: true, + } +} +``` + +--- + +## 6. Summary of changed files + +| File | Change | +|---|---| +| `Cargo.toml` | Add `ffmpeg-next` with `static` feature | +| `.cargo/config.toml` | New file β€” pin `FFMPEG_BUILD_VERSION` | +| `src/media/ffmpeg.rs` | Complete replacement (see Β§2) | +| `src/workers/mod.rs` | Replace two `TokioCommand` blocks (see Β§3) | +| `src/detect.rs` | Replace ffmpeg detection section (see Β§4) | +| `src/media/mod.rs` | Optional simplification of `new()` (see Β§5) | +| `src/media/convert.rs` | **No changes** | +| `src/media/thumbnail.rs` | **No changes** | +| `src/server/server.rs` | **No changes** | +| `src/middleware/mod.rs` | **No changes** | diff --git a/src/config.rs b/src/config.rs index 3cc6d75..ed2cbc7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -161,9 +161,11 @@ max_video_size_mb = 50 # Maximum size for audio uploads in megabytes (mp3, ogg, flac, wav, m4a, aac). max_audio_size_mb = 150 -# Tor Onion Service support. -# When true, the server probes for `tor` at startup and prints torrc hints. -# The server always starts regardless β€” this is purely informational. +# Tor Onion Service support (powered by Arti β€” no system tor required). +# When true, Arti bootstraps at startup and hosts a .onion hidden service. +# First run downloads ~2 MB of directory data and takes ~30 s. +# The service keypair lives in rustchan-data/arti_state/keys/ β€” back it up. +# Delete that directory to rotate to a new .onion address. enable_tor_support = true # Set to true to hard-exit at startup when ffmpeg is not found. diff --git a/src/detect.rs b/src/detect.rs index 79866fe..9b809ee 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -11,7 +11,7 @@ use std::path::Path; use std::process::{Command, Stdio}; -use std::sync::{Arc, Mutex, OnceLock}; +use std::sync::Arc; /// Result of probing for a tool at startup. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -20,20 +20,6 @@ pub enum ToolStatus { Missing, } -// ─── Global Tor child handle ────────────────────────────────────────────────── - -static TOR_CHILD: OnceLock>> = OnceLock::new(); - -#[allow(dead_code)] -pub fn kill_tor() { - if let Some(child) = TOR_CHILD.get() { - if let Ok(mut c) = child.lock() { - let _ = c.kill(); - let _ = c.wait(); - } - } -} - // ─── ffmpeg ─────────────────────────────────────────────────────────────────── pub fn detect_ffmpeg(require_ffmpeg: bool) -> ToolStatus { @@ -207,397 +193,200 @@ fn webm_install_hint(has_vp9: bool, has_opus: bool) -> String { s } -// ─── Tor ───────────────────────────────────────────────────────────────────── - -#[allow(clippy::too_many_lines)] -#[allow(clippy::expect_used)] -#[allow(clippy::arithmetic_side_effects)] -pub fn detect_tor(enable_tor_support: bool, bind_port: u16, data_dir: &Path) -> ToolStatus { +// ─── Tor (Arti in-process) ──────────────────────────────────────────────────── +// +// Previously: spawned the system `tor` binary as a subprocess, wrote a torrc, +// created two directories with chmod 0700, and polled for the hostname file +// for up to 120 seconds. +// +// Now: spawns one Tokio task that bootstraps Arti in-process, launches the +// onion service, and proxies incoming connections to the local HTTP server. +// No subprocess, no torrc, no hostname file, no polling loop. + +use arti_client::{config::TorClientConfigBuilder, TorClient}; +use futures::StreamExt; +use tokio::net::TcpStream; +use tokio::sync::RwLock; +use tor_cell::relaycell::msg::Connected; +use tor_hsservice::{config::OnionServiceConfigBuilder, handle_rend_requests, HsId, StreamRequest}; + +/// Spawn the Arti in-process Tor task. +/// +/// Returns `Available` immediately. The onion address becomes available in +/// `onion_address` roughly 30 seconds later on first run or ~5 seconds on +/// subsequent runs (consensus served from `arti_cache/`). +/// +/// If `enable_tor_support` is false this is a no-op and returns `Missing`. +pub fn detect_tor( + enable_tor_support: bool, + bind_port: u16, + data_dir: &Path, + onion_address: Arc>>, +) -> ToolStatus { if !enable_tor_support { return ToolStatus::Missing; } - let candidates = [ - "/opt/homebrew/bin/tor", - "/usr/local/bin/tor", - "/usr/bin/tor", - "tor", - ]; - - let tor_bin: Option<&str> = candidates.iter().copied().find(|bin| { - Command::new(bin) - .arg("--version") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .is_ok() - }); + let data_dir = data_dir.to_path_buf(); - let Some(tor_bin) = tor_bin else { - tracing::warn!(target: "detect", available = false, "Tor not found β€” onion service disabled"); - if crate::logging::is_tty() { - crate::logging::console_print_raw(&tor_install_hint(bind_port)); + tokio::spawn(async move { + if let Err(e) = run_arti(data_dir, bind_port, onion_address).await { + tracing::error!(target: "detect", error = %e, "Tor: fatal error in Arti task"); } - return ToolStatus::Missing; - }; + }); - tracing::info!(target: "detect", binary = tor_bin, "Tor binary found"); + tracing::info!( + target: "detect", + "Tor: Arti task spawned β€” bootstrapping in background (first run ~30 s)" + ); + ToolStatus::Available +} - let hs_dir = data_dir.join("tor_hidden_service"); - let data_subdir = data_dir.join("tor_data"); +/// No-op. Previously killed the tor subprocess. +/// The [`TorClient`] is owned by the `tokio::spawn` task; dropping the runtime +/// closes all circuits cleanly. +#[allow(dead_code)] +pub const fn kill_tor() {} - for dir in [&hs_dir, &data_subdir] { - if let Err(e) = std::fs::create_dir_all(dir) { - tracing::warn!( - target: "detect", - path = %dir.display(), - error = %e, - "Tor: cannot create directory" - ); - if crate::logging::is_tty() { - crate::logging::console_print_raw(&tor_torrc_hint(&hs_dir, bind_port)); - } - return ToolStatus::Missing; - } - } +// ─── Core Arti task ─────────────────────────────────────────────────────────── - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - for dir in [&hs_dir, &data_subdir] { - if let Err(e) = std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o700)) { - tracing::warn!( - target: "detect", - path = %dir.display(), - error = %e, - "Tor: cannot set 0700 permissions (Tor may reject directory)" - ); - } - } - } +async fn run_arti( + data_dir: std::path::PathBuf, + bind_port: u16, + onion_address: Arc>>, +) -> Result<(), Box> { + // arti_cache/ β€” consensus cache (safe to delete; re-fetched on next start). + // arti_state/ β€” service keypair. Back this up. + // Delete it only if you want a new .onion address. + // + // NOTE: TorClientConfigBuilder::from_directories takes AsRef directly. + // Do NOT use the builder().storage().cache_dir(PathBuf) path β€” CfgPath does + // not implement From and it will not compile. + let config = TorClientConfigBuilder::from_directories( + data_dir.join("arti_state"), + data_dir.join("arti_cache"), + ) + .build()?; - let canon = |p: &Path| p.canonicalize().unwrap_or_else(|_| p.to_path_buf()); - let hs_abs = canon(&hs_dir); - let data_abs = canon(&data_subdir); - let torrc_path = canon(data_dir).join("torrc"); - - let torrc = format!( - "# RustChan β€” auto-generated torrc (do not edit while Tor is running)\n\ - \n\ - SocksPort 0\n\ - DataDirectory \"{data}\"\n\ - \n\ - HiddenServiceDir \"{hs}\"\n\ - HiddenServicePort 80 127.0.0.1:{port}\n", - data = data_abs.display(), - hs = hs_abs.display(), - port = bind_port, + tracing::info!( + target: "detect", + cache_dir = %data_dir.join("arti_cache").display(), + state_dir = %data_dir.join("arti_state").display(), + "Tor: bootstrapping β€” first run downloads ~2 MB of directory data" ); - if let Err(e) = std::fs::write(&torrc_path, &torrc) { - tracing::warn!( - target: "detect", - path = %torrc_path.display(), - error = %e, - "Tor: cannot write torrc" - ); - if crate::logging::is_tty() { - crate::logging::console_print_raw(&tor_torrc_hint(&hs_dir, bind_port)); - } - return ToolStatus::Missing; - } + let tor_client = TorClient::create_bootstrapped(config) + .await + .map_err(|e| format!("Tor bootstrap failed: {e}"))?; + + tracing::info!(target: "detect", "Tor: connected to the Tor network"); + + let svc_config = OnionServiceConfigBuilder::default() + .nickname("rustchan".parse()?) + .build()?; + + let (onion_service, rend_requests) = tor_client + .launch_onion_service(svc_config)? + .ok_or("launch_onion_service returned None β€” unexpected with code-only config")?; + + let hsid = onion_service + .onion_address() + .ok_or("onion_address() returned None immediately after launch")?; + let onion_name = hsid_to_onion_address(hsid); tracing::info!( target: "detect", - torrc = %torrc_path.display(), - hidden_svc = %hs_abs.display(), - data_dir = %data_abs.display(), - "Tor configured" + onion_address = %onion_name, + keys_dir = %data_dir.join("arti_state").join("keys").display(), + "Tor: hidden service active" ); - let child = Command::new(tor_bin) - .arg("-f") - .arg(&torrc_path) - .stdout(Stdio::null()) - .stderr(Stdio::piped()) - .spawn(); - - let mut child = match child { - Err(e) => { - tracing::warn!( - target: "detect", - binary = tor_bin, - error = %e, - "Tor: failed to start process" - ); - if crate::logging::is_tty() { - crate::logging::console_print_raw(&tor_torrc_hint(&hs_dir, bind_port)); - } - return ToolStatus::Missing; - } - Ok(c) => c, - }; - - let stderr_lines: Arc>> = Arc::new(Mutex::new(Vec::new())); - if let Some(pipe) = child.stderr.take() { - let buf = Arc::clone(&stderr_lines); - std::thread::spawn(move || { - use std::io::{BufRead, BufReader}; - for line in BufReader::new(pipe).lines().map_while(Result::ok).take(500) { - if let Ok(mut g) = buf.lock() { - g.push(line); - } - } - }); + *onion_address.write().await = Some(onion_name.clone()); + + if crate::logging::is_tty() { + crate::logging::console_print_raw(&format!( + "\n\ + ╔══════════════════════════════════════════════════════╗\n\ + β•‘ TOR ONION SERVICE ACTIVE βœ“ β•‘\n\ + ╠══════════════════════════════════════════════════════╣\n\ + β•‘ http://{onion:<44}β•‘\n\ + β•‘ β•‘\n\ + β•‘ Keys stored at: β•‘\n\ + β•‘ {keys:<50}β•‘\n\ + β•‘ Back up this directory to preserve your address. β•‘\n\ + β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n\n", + onion = onion_name, + keys = data_dir.join("arti_state/keys").display(), + )); } - let child = Arc::new(Mutex::new(child)); - let _ = TOR_CHILD.set(Arc::clone(&child)); - - let prev_hook = std::panic::take_hook(); - std::panic::set_hook(Box::new(move |info| { - // A panic hook MUST NOT panic β€” doing so causes an unconditional abort - // with no useful diagnostic output. We therefore use try_lock() so - // that a poisoned or already-locked mutex is silently skipped; the OS - // will reap the Tor child on process exit regardless. - if let Some(child) = TOR_CHILD.get() { - if let Ok(mut c) = child.try_lock() { - let _ = c.kill(); - let _ = c.wait(); - } - // If try_lock fails (mutex poisoned or already held during unwind), - // skip the kill β€” do NOT call .lock()/.expect() here. - } - prev_hook(info); - })); - - // Recover from a poisoned mutex instead of crashing the process. - let pid = child - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .id(); - tracing::info!(target: "detect", pid = pid, "Tor process started β€” waiting for .onion address"); - - let hostname_path = hs_abs.join("hostname"); - let torrc_display = torrc_path.display().to_string(); - let tor_bin_owned = tor_bin.to_string(); - - let child_bg = Arc::clone(&child); - let stderr_bg = Arc::clone(&stderr_lines); - - std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_secs(4)); - - let try_wait_result = child_bg - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .try_wait(); - - match try_wait_result { - Ok(Some(status)) => { - let lines = stderr_bg - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - tracing::error!( - target: "detect", - exit_status = %status, - "Tor process exited early" - ); - if crate::logging::is_tty() && !lines.is_empty() { - let mut block = - String::from("\n ── Tor stderr ──────────────────────────────────\n"); - for line in lines.iter().take(20) { - block.push_str(" "); - block.push_str(line); - block.push('\n'); - } - block.push_str(" ────────────────────────────────────────────────\n\n"); - drop(lines); - crate::logging::console_print_raw(&block); - crate::logging::console_print_raw(&tor_diagnosis_hint( - &torrc_display, - &tor_bin_owned, - bind_port, - )); - } - return; - } - Ok(None) => {} - Err(e) => { - tracing::warn!(target: "detect", error = %e, "Tor: could not query process status"); - } - } - - poll_for_hostname( - &hostname_path, - &child_bg, - &stderr_bg, - &torrc_display, - &tor_bin_owned, - bind_port, - ); - }); - - ToolStatus::Available -} + let mut stream_requests = handle_rend_requests(rend_requests); -// ─── Hostname polling ───────────────────────────────────────────────────────── - -#[allow(clippy::expect_used)] -#[allow(clippy::arithmetic_side_effects)] -fn poll_for_hostname( - hostname_path: &Path, - child: &Arc>, - stderr_lines: &Arc>>, - torrc_display: &str, - tor_bin: &str, - bind_port: u16, -) { - const TIMEOUT_SECS: u64 = 120; - const POLL_MS: u64 = 500; - let deadline = std::time::Instant::now() + std::time::Duration::from_secs(TIMEOUT_SECS); - - loop { - if let Ok(mut c) = child.try_lock() { - if let Ok(Some(status)) = c.try_wait() { - let lines = stderr_lines - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - tracing::error!( + while let Some(stream_req) = stream_requests.next().await { + let local_addr = format!("127.0.0.1:{bind_port}"); + tokio::spawn(async move { + if let Err(e) = proxy_tor_stream(stream_req, &local_addr).await { + tracing::debug!( target: "detect", - exit_status = %status, - "Tor process crashed during startup" + error = %e, + "Tor: stream closed" ); - if crate::logging::is_tty() && !lines.is_empty() { - let mut block = - String::from("\n ── Tor stderr ──────────────────────────────────\n"); - for line in lines.iter().take(20) { - block.push_str(" "); - block.push_str(line); - block.push('\n'); - } - block.push_str(" ────────────────────────────────────────────────\n\n"); - drop(lines); - crate::logging::console_print_raw(&block); - crate::logging::console_print_raw(&tor_diagnosis_hint( - torrc_display, - tor_bin, - bind_port, - )); - } - return; - } - } - - if hostname_path.exists() { - match std::fs::read_to_string(hostname_path) { - Ok(raw) => { - let onion = raw.trim(); - if !onion.is_empty() { - tracing::info!( - target: "detect", - onion_address = onion, - "Tor hidden service active" - ); - if crate::logging::is_tty() { - crate::logging::console_print_raw(&tor_onion_banner( - onion, - hostname_path, - )); - } - return; - } - } - Err(e) => { - tracing::warn!(target: "detect", error = %e, "Tor: hostname file unreadable"); - } } - } - - if std::time::Instant::now() >= deadline { - tracing::warn!( - target: "detect", - timeout_secs = TIMEOUT_SECS, - hostname_path = %hostname_path.display(), - "Tor timed out waiting for hostname file" - ); - if crate::logging::is_tty() { - crate::logging::console_print_raw(&tor_diagnosis_hint( - torrc_display, - tor_bin, - bind_port, - )); - } - return; - } - - std::thread::sleep(std::time::Duration::from_millis(POLL_MS)); + }); } -} -// ─── Tor print helpers (console_print_raw blocks) ──────────────────────────── - -fn tor_onion_banner(onion: &str, hostname_path: &Path) -> String { - let addr_line = format!("http://{onion}"); - let key_dir = hostname_path.parent().unwrap_or(hostname_path); - format!( - "\n\ - ╔════════════════════════════════════════════════════════════════════════╗\n\ - β•‘ TOR ONION SERVICE ACTIVE βœ“ β•‘\n\ - ╠════════════════════════════════════════════════════════════════════════╣\n\ - β•‘ {addr:<70}β•‘\n\ - β•‘ β•‘\n\ - β•‘ Share this with Tor Browser users. β•‘\n\ - β•‘ Your private key is stored at: β•‘\n\ - β•‘ {key:<68}β•‘\n\ - β•‘ Back it up β€” losing it means a new .onion address. β•‘\n\ - β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n\n", - addr = addr_line, - key = key_dir.display(), - ) + tracing::warn!( + target: "detect", + "Tor: rendezvous stream ended β€” onion service offline" + ); + Ok(()) } -fn tor_install_hint(bind_port: u16) -> String { - format!( - "\n\ - \x1b[2m ── Install Tor ────────────────────────────────────────────────────────\n\ - macOS: brew install tor\n\ - Linux: sudo apt-get install tor\n\ - Other: https://www.torproject.org/download/tor/\n\ - \n\ - After installing, restart RustChan β€” it configures Tor automatically.\n\ - Or add to your own torrc:\n\ - HiddenServicePort 80 127.0.0.1:{bind_port}\x1b[0m\n\n" - ) -} +// ─── Connection proxy ───────────────────────────────────────────────────────── -fn tor_torrc_hint(hs_dir: &Path, bind_port: u16) -> String { - format!( - "\n Add to your torrc and restart Tor:\n\ - \x1b[2m SocksPort 0\n\ - DataDirectory /var/lib/tor/rustchan-data/\n\ - HiddenServiceDir {hs}\n\ - HiddenServicePort 80 127.0.0.1:{bind_port}\x1b[0m\n\n", - hs = hs_dir.display(), - ) +async fn proxy_tor_stream( + stream_req: StreamRequest, + local_addr: &str, +) -> Result<(), Box> { + let mut tor_stream = stream_req.accept(Connected::new_empty()).await?; + let mut local = TcpStream::connect(local_addr).await?; + tokio::io::copy_bidirectional(&mut tor_stream, &mut local).await?; + Ok(()) } -fn tor_diagnosis_hint(torrc_path: &str, tor_bin: &str, bind_port: u16) -> String { - format!( - "\n \x1b[2m── Tor troubleshooting ──────────────────────────────────────────────────\n\ - \n\ - 1. Run manually to see live output:\n\ - {tor_bin} -f {torrc_path}\n\ - \n\ - 2. Common causes:\n\ - a) DataDirectory permissions β€” chmod 700 /tor_data/\n\ - b) HiddenServiceDir perms β€” chmod 700 /tor_hidden_service/\n\ - c) Firewall: Tor needs outbound TCP on ports 9001 and 443.\n\ - d) macOS brew conflict: brew services stop tor, then restart RustChan.\n\ - e) Linux SELinux/AppArmor: sudo journalctl -u tor --since '5 min ago'\n\ - \n\ - 3. To manage Tor yourself:\n\ - Set enable_tor_support = false in settings.toml\n\ - and add to your torrc: HiddenServicePort 80 127.0.0.1:{bind_port}\x1b[0m\n\n" - ) +// ─── Onion address encoding ─────────────────────────────────────────────────── + +/// Encode an [`HsId`] (Ed25519 public key) as a v3 `.onion` address string. +/// +/// [`HsId`] does not implement `std::fmt::Display` in arti-client 0.40. +/// Encoded manually using `HsId: AsRef<[u8; 32]>`. +/// +/// Format: `base32( pubkey || sha3_256(".onion checksum" || pubkey || version)[..2] || version )` +fn hsid_to_onion_address(hsid: HsId) -> String { + use sha3::{Digest, Sha3_256}; + + let pubkey: &[u8; 32] = hsid.as_ref(); + let version: u8 = 3; + + let mut hasher = Sha3_256::new(); + hasher.update(b".onion checksum"); + hasher.update(pubkey); + hasher.update([version]); + let hash = hasher.finalize(); + + // Destructure via iterator β€” avoids the indexing_slicing lint. + // Safe: Sha3_256 always produces exactly 32 bytes. + let mut iter = hash.iter().copied(); + let checksum = [iter.next().unwrap_or(0), iter.next().unwrap_or(0)]; + + let mut address_bytes = [0u8; 35]; + address_bytes[..32].copy_from_slice(pubkey); + address_bytes[32..34].copy_from_slice(&checksum); + address_bytes[34] = version; + + let encoded = data_encoding::BASE32_NOPAD + .encode(&address_bytes) + .to_ascii_lowercase(); + + format!("{encoded}.onion") } diff --git a/src/handlers/admin/mod.rs b/src/handlers/admin/mod.rs index 72a0c12..43ee27f 100644 --- a/src/handlers/admin/mod.rs +++ b/src/handlers/admin/mod.rs @@ -123,6 +123,14 @@ pub async fn admin_panel( None }; + // Read onion address before entering spawn_blocking β€” await is not allowed + // inside the synchronous closure. + let onion_address_val: Option = if CONFIG.enable_tor_support { + state.onion_address.read().await.clone() + } else { + None + }; + let html = tokio::task::spawn_blocking({ let pool = state.db.clone(); move || -> Result { @@ -161,22 +169,8 @@ pub async fn admin_panel( false }; - // Read the tor onion address from the hostname file if tor is enabled. - let tor_address: Option = if CONFIG.enable_tor_support { - let data_dir = std::path::PathBuf::from(&CONFIG.database_path) - .parent() - .map_or_else( - || std::path::PathBuf::from("."), - std::path::Path::to_path_buf, - ); - let hostname_path = data_dir.join("tor_hidden_service").join("hostname"); - std::fs::read_to_string(&hostname_path) - .ok() - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - } else { - None - }; + // Onion address resolved before spawn_blocking (see above). + let tor_address: Option = onion_address_val; let flash_ref = flash.as_ref().map(|(is_err, msg)| (*is_err, msg.as_str())); diff --git a/src/handlers/admin/settings.rs b/src/handlers/admin/settings.rs index 0bbc625..ac7b053 100644 --- a/src/handlers/admin/settings.rs +++ b/src/handlers/admin/settings.rs @@ -332,6 +332,14 @@ pub async fn admin_panel( None }; + // Read onion address before entering spawn_blocking β€” await is not allowed + // inside the synchronous closure. + let onion_address_val: Option = if CONFIG.enable_tor_support { + state.onion_address.read().await.clone() + } else { + None + }; + let html = tokio::task::spawn_blocking({ let pool = state.db.clone(); move || -> Result { @@ -370,22 +378,8 @@ pub async fn admin_panel( false }; - // Read the tor onion address from the hostname file if tor is enabled. - let tor_address: Option = if CONFIG.enable_tor_support { - let data_dir = std::path::PathBuf::from(&CONFIG.database_path) - .parent() - .map_or_else( - || std::path::PathBuf::from("."), - std::path::Path::to_path_buf, - ); - let hostname_path = data_dir.join("tor_hidden_service").join("hostname"); - std::fs::read_to_string(&hostname_path) - .ok() - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - } else { - None - }; + // Onion address resolved before spawn_blocking (see above). + let tor_address: Option = onion_address_val; let flash_ref = flash.as_ref().map(|(is_err, msg)| (*is_err, msg.as_str())); diff --git a/src/handlers/board.rs b/src/handlers/board.rs index 3be86e7..a7e4848 100644 --- a/src/handlers/board.rs +++ b/src/handlers/board.rs @@ -56,19 +56,9 @@ pub async fn index( .await .map_err(|e| AppError::Internal(anyhow::anyhow!(e)))??; - // Read the tor onion address from the hostname file if tor is enabled. + // Read the onion address from AppState (populated by the Arti task on startup). let onion_address: Option = if crate::config::CONFIG.enable_tor_support { - let data_dir = std::path::PathBuf::from(&crate::config::CONFIG.database_path) - .parent() - .map_or_else( - || std::path::PathBuf::from("."), - std::path::Path::to_path_buf, - ); - let hostname_path = data_dir.join("tor_hidden_service").join("hostname"); - std::fs::read_to_string(&hostname_path) - .ok() - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) + state.onion_address.read().await.clone() } else { None }; diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 7eea105..a7c336b 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -137,6 +137,9 @@ pub struct AppState { /// even if the ledger is cleared by a server restart. pub chan_ledger: Option>>>, + /// Onion address populated by the Arti task once the hidden service is ready. + /// `None` until Arti has bootstrapped (~30 s first run, ~5 s subsequent runs). + pub onion_address: std::sync::Arc>>, } /// Get current Unix timestamp in seconds. diff --git a/src/server/server.rs b/src/server/server.rs index 883c14e..0fa5e46 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -216,17 +216,12 @@ pub async fn run_server(port_override: Option, chan_net: bool) -> anyhow::R // only these codecs still enables image conversion and thumbnail generation. let ffmpeg_vp9_available = crate::detect::detect_webm_encoder(ffmpeg_available); - // Tor: create hidden-service directory + torrc, launch tor as a background - // process, and poll for the hostname file (all non-blocking). - // Fix #1: derive bind_port from `bind_addr` (which already incorporates - // port_override) rather than CONFIG.bind_addr. Previously, starting with - // `--port 9000` would still pass 8080 to Tor's HiddenServicePort. + // Derive bind_port from `bind_addr` (which already incorporates port_override). // rsplit_once(':') handles both IPv4 ("0.0.0.0:9000") and IPv6 ("[::1]:9000"). let bind_port = bind_addr .rsplit_once(':') .and_then(|(_, p)| p.parse::().ok()) .unwrap_or(8080); - crate::detect::detect_tor(CONFIG.enable_tor_support, bind_port, &data_dir); // CRIT-1 FIX: Capture JoinHandles from start_worker_pool so the shutdown // sequence can await each worker instead of blindly sleeping for 10 s. @@ -249,7 +244,18 @@ pub async fn run_server(port_override: Option, chan_net: bool) -> anyhow::R } else { None }, + onion_address: std::sync::Arc::new(tokio::sync::RwLock::new(None)), }; + + // Tor: spawn Arti in-process. The onion address is written to + // state.onion_address once Arti has bootstrapped and the hidden service + // is active (~30 s first run, ~5 s on subsequent runs). + crate::detect::detect_tor( + CONFIG.enable_tor_support, + bind_port, + &data_dir, + state.onion_address.clone(), + ); // Keep a reference to the job queue cancel token for graceful shutdown (#7). let worker_cancel = state.job_queue.cancel.clone(); let start_time = Instant::now();