From 40a3cd28b7a399f78cceeaa5f9f68fe14ca80532 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 13:46:17 +0100 Subject: [PATCH 01/51] otel: add tracing --- Cargo.lock | 3209 ++++++++--------- Cargo.toml | 8 +- common/bin-common/Cargo.toml | 20 +- common/bin-common/src/logging/mod.rs | 84 +- .../client-libs/mixnet-client/src/client.rs | 107 +- nym-node/Cargo.toml | 3 + nym-node/src/cli/mod.rs | 90 +- nym-node/src/logging.rs | 79 +- nym-node/src/node/mixnet/handler.rs | 251 +- .../src/node/mixnet/packet_forwarding/mod.rs | 45 +- nym-node/src/node/mixnet/shared/final_hop.rs | 44 +- nym-node/src/node/mixnet/shared/mod.rs | 14 +- 12 files changed, 2191 insertions(+), 1763 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8bca7fa945..368e9a2430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -24,6 +24,15 @@ dependencies = [ "psl-types", ] +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.1" @@ -36,7 +45,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "generic-array 0.14.7", ] @@ -47,18 +56,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "aes" -version = "0.9.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9e1c818b25efb32214df89b0ec22f01aa397aaeb718d1022bf0635a3bfd1a8" -dependencies = [ - "cfg-if", - "cipher 0.5.0-rc.3", + "cipher", "cpufeatures", ] @@ -69,8 +67,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", - "aes 0.8.4", - "cipher 0.4.4", + "aes", + "cipher", "ctr", "ghash", "subtle 2.6.1", @@ -83,24 +81,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" dependencies = [ "aead", - "aes 0.8.4", - "cipher 0.4.4", + "aes", + "cipher", "ctr", "polyval", "subtle 2.6.1", "zeroize", ] -[[package]] -name = "aes-keywrap" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b6f24a1f796bc46415a1d0d18dc0a8203ccba088acf5def3291c4f61225522" -dependencies = [ - "aes 0.9.0-rc.2", - "byteorder", -] - [[package]] name = "ahash" version = "0.8.12" @@ -115,9 +103,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -156,6 +144,12 @@ dependencies = [ "url", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -182,9 +176,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -197,9 +191,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" @@ -212,47 +206,44 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.11" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arbitrary" -version = "1.4.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] [[package]] name = "arc-swap" -version = "1.8.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" -dependencies = [ - "rustversion", -] +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "argon2" @@ -405,7 +396,20 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" dependencies = [ - "askama_derive", + "askama_derive 0.13.1", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" +dependencies = [ + "askama_derive 0.14.0", "itoa", "percent-encoding", "serde", @@ -418,7 +422,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" dependencies = [ - "askama_parser", + "askama_parser 0.13.0", "basic-toml", "memchr", "proc-macro2", @@ -426,7 +430,24 @@ dependencies = [ "rustc-hash", "serde", "serde_derive", - "syn 2.0.114", + "syn 2.0.106", +] + +[[package]] +name = "askama_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" +dependencies = [ + "askama_parser 0.14.0", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "serde", + "serde_derive", + "syn 2.0.106", ] [[package]] @@ -441,6 +462,18 @@ dependencies = [ "winnow", ] +[[package]] +name = "askama_parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow", +] + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -451,37 +484,29 @@ dependencies = [ "serde_json", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-compression" -version = "0.4.36" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" +checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ - "compression-codecs", - "compression-core", + "brotli", + "flate2", "futures-core", + "memchr", "pin-project-lite", "tokio", + "zstd", + "zstd-safe", ] [[package]] name = "async-lock" -version = "3.4.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] @@ -505,18 +530,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -583,9 +608,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -604,10 +629,10 @@ dependencies = [ "axum-macros", "bytes", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-util", "itoa", "matchit", @@ -648,7 +673,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", @@ -669,10 +694,10 @@ dependencies = [ "axum", "axum-core", "bytes", - "fastrand 2.3.0", + "fastrand", "futures-util", "headers", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", @@ -692,7 +717,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -708,9 +733,9 @@ dependencies = [ "bytes", "bytesize", "cookie", - "http 1.4.0", + "http 1.3.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-util", "mime", "pretty_assertions", @@ -725,6 +750,21 @@ dependencies = [ "url", ] +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -751,9 +791,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "base85rs" @@ -772,9 +812,9 @@ dependencies = [ [[package]] name = "bech32" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "bincode" @@ -810,9 +850,9 @@ dependencies = [ [[package]] name = "bip39" -version = "2.2.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +checksum = "43d193de1f7487df1914d3a568b772458861d33f9c54249612cc2893d6915054" dependencies = [ "bitcoin_hashes", "rand 0.8.5", @@ -823,11 +863,18 @@ dependencies = [ ] [[package]] -name = "bitcoin_hashes" -version = "0.14.1" +name = "bitcoin-internals" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ + "bitcoin-internals", "hex-conservative", ] @@ -839,11 +886,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ - "serde_core", + "serde", ] [[package]] @@ -949,9 +996,9 @@ checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "brotli" -version = "8.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -980,9 +1027,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-tools" @@ -1023,11 +1070,11 @@ checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" [[package]] name = "camino" -version = "1.2.2" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" dependencies = [ - "serde_core", + "serde", ] [[package]] @@ -1047,7 +1094,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.27", + "semver 1.0.26", "serde", "serde_json", "thiserror 1.0.69", @@ -1061,10 +1108,10 @@ checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver 1.0.27", + "semver 1.0.26", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -1073,17 +1120,11 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" -[[package]] -name = "castaway" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" - [[package]] name = "cc" -version = "1.2.51" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -1097,7 +1138,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc3ba3c5408fae183329e0d1ac8f8eed3cb7b647590fd93e6d6288f6b09db0be" dependencies = [ - "phf 0.11.3", + "phf", "serde", ] @@ -1109,9 +1150,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -1136,7 +1177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher 0.4.4", + "cipher", "cpufeatures", ] @@ -1148,23 +1189,24 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", "chacha20", - "cipher 0.4.4", + "cipher", "poly1305", "zeroize", ] [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link 0.1.3", ] [[package]] @@ -1200,26 +1242,16 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common 0.1.7", - "inout 0.1.4", + "crypto-common", + "inout", "zeroize", ] -[[package]] -name = "cipher" -version = "0.5.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d708bac5451350d56398433b19a7889022fa9187df1a769c0edbc3b2c03167" -dependencies = [ - "crypto-common 0.2.0-rc.9", - "inout 0.2.2", -] - [[package]] name = "clap" -version = "4.5.54" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" dependencies = [ "clap_builder", "clap_derive", @@ -1227,9 +1259,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" dependencies = [ "anstream", "anstyle", @@ -1239,9 +1271,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.65" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d" +checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a" dependencies = [ "clap", ] @@ -1258,26 +1290,26 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "classic-mceliece-rust" version = "3.2.0" -source = "git+https://github.com/georgio/classic-mceliece-rust#7bdf0c3c8c727cbc4784e05b0972641ad0791ab9" +source = "git+https://github.com/georgio/classic-mceliece-rust#f2f27048b621df103bbe64369a18174ffec04ae1" dependencies = [ "rand 0.9.2", "sha3", @@ -1332,35 +1364,15 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.2.1" +version = "7.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b7db8e0b4b2fdad6c551e634134e99ec000e5c8c3b6856c65e8bbaded7a3b" +checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" dependencies = [ - "crossterm 0.29.0", + "crossterm 0.28.1", "unicode-segmentation", - "unicode-width 0.2.2", + "unicode-width 0.2.1", ] -[[package]] -name = "compression-codecs" -version = "0.4.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" -dependencies = [ - "brotli", - "compression-core", - "flate2", - "memchr", - "zstd", - "zstd-safe", -] - -[[package]] -name = "compression-core" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1372,15 +1384,15 @@ dependencies = [ [[package]] name = "console" -version = "0.16.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.2", - "windows-sys 0.61.2", + "unicode-width 0.2.1", + "windows-sys 0.60.2", ] [[package]] @@ -1390,7 +1402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8030735ecb0d128428b64cd379809817e620a40e5001c54465b99ec5feec2857" dependencies = [ "futures-core", - "prost", + "prost 0.13.5", "prost-types", "tonic 0.12.3", "tracing-core", @@ -1409,7 +1421,7 @@ dependencies = [ "hdrhistogram", "humantime", "hyper-util", - "prost", + "prost 0.13.5", "prost-types", "serde", "serde_json", @@ -1446,9 +1458,9 @@ checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9" [[package]] name = "const_format" -version = "0.2.35" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] @@ -1509,10 +1521,10 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-models" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "hax-lib", - "pastey 0.2.1", + "pastey", "rand 0.9.2", ] @@ -1523,7 +1535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95ac39be7373404accccaede7cc1ec942ccef14f0ca18d209967a756bf1dbb1f" dependencies = [ "informalsystems-pbjson", - "prost", + "prost 0.13.5", "serde", "tendermint-proto", "tonic 0.13.1", @@ -1552,15 +1564,15 @@ dependencies = [ [[package]] name = "cosmwasm-core" -version = "2.3.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63efd882086668c3bad621b5b897a0e058d0b5fae027c984e1a6230bf284ab85" +checksum = "35b6dc17e7fd89d0a0a58f12ef33f0bbdf09a6a14c3dfb383eae665e5889250e" [[package]] name = "cosmwasm-crypto" -version = "2.3.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0969e6f569f867725bf89e672058ae336e130c1fcf914def4f7c6c0380b2bc83" +checksum = "aa2f53285517db3e33d825b3e46301efe845135778527e1295154413b2f0469e" dependencies = [ "ark-bls12-381", "ark-ec", @@ -1582,13 +1594,13 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "2.3.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0da0db99715fc86e6a5ad7be9c19d0d9e5a92179862b17cae7406b729a402c2" +checksum = "a782b93fae93e57ca8ad3e9e994e784583f5933aeaaa5c80a545c4b437be2047" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -1612,7 +1624,7 @@ checksum = "e01c9214319017f6ebd8e299036e1f717fa9bb6724e758f7d6fb2477599d1a29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -1650,9 +1662,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -1777,15 +1789,14 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.29.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "crossterm_winapi", - "document-features", "parking_lot", - "rustix", + "rustix 0.38.44", "winapi", ] @@ -1818,24 +1829,15 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] -[[package]] -name = "crypto-common" -version = "0.2.0-rc.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b8986f836d4aeb30ccf4c9d3bd562fd716074cfd7fc4a2948359fbd21ed809" -dependencies = [ - "hybrid-array", -] - [[package]] name = "crypto-mac" version = "0.7.0" @@ -1855,7 +1857,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.3", + "phf", "smallvec", ] @@ -1866,26 +1868,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "csv" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", "ryu", - "serde_core", + "serde", ] [[package]] name = "csv-core" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -1902,7 +1904,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -1915,36 +1917,6 @@ dependencies = [ "rustc_version 0.2.3", ] -[[package]] -name = "curl" -version = "0.4.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe 0.1.6", - "openssl-sys", - "schannel", - "socket2 0.6.1", - "windows-sys 0.59.0", -] - -[[package]] -name = "curl-sys" -version = "0.4.84+curl-8.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc4294dc41b882eaff37973c2ec3ae203d0091341ee68fbadd1d06e0c18a73b" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "windows-sys 0.59.0", -] - [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -1970,7 +1942,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2014,11 +1986,11 @@ dependencies = [ "cw-storage-plus", "cw-utils", "itertools 0.14.0", - "prost", + "prost 0.13.5", "schemars 0.8.22", "serde", "sha2 0.10.9", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -2055,7 +2027,7 @@ dependencies = [ "cosmwasm-std", "cw-storage-plus", "schemars 0.8.22", - "semver 1.0.27", + "semver 1.0.26", "serde", "thiserror 1.0.69", ] @@ -2107,18 +2079,8 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", + "darling_core", + "darling_macro", ] [[package]] @@ -2132,21 +2094,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", -] - -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2155,20 +2103,9 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.11", + "darling_core", "quote", - "syn 2.0.114", -] - -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", - "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2193,9 +2130,9 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "defguard_boringtun" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e226ae51c414f475137460063382d0bb6b5a1b1b1a2b135d4b3b830ab43f06ec" +checksum = "4b7c7f465dde186f958a0a0e4ae823af623451ba26817b8b366b9968286df7a1" dependencies = [ "aead", "base64 0.22.1", @@ -2206,13 +2143,13 @@ dependencies = [ "ip_network", "ip_network_table", "libc", - "nix 0.30.1", + "nix 0.31.1", "parking_lot", "ring", - "socket2 0.6.1", - "thiserror 2.0.17", + "socket2 0.6.0", + "thiserror 2.0.12", "tracing", - "uniffi 0.30.0", + "uniffi 0.31.0", "untrusted", "x25519-dalek", ] @@ -2228,16 +2165,16 @@ dependencies = [ "ipnet", "libc", "log", - "netlink-packet-core", - "netlink-packet-generic", + "netlink-packet-core 0.8.1", + "netlink-packet-generic 0.4.0", "netlink-packet-route", - "netlink-packet-utils", + "netlink-packet-utils 0.6.0", "netlink-packet-wireguard", "netlink-sys", "nix 0.30.1", "regex", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "windows 0.62.2", "wireguard-nt", "x25519-dalek", @@ -2254,7 +2191,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2270,12 +2207,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", - "serde_core", + "serde", ] [[package]] @@ -2291,13 +2228,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.4.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2315,10 +2252,10 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling 0.20.11", + "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2328,7 +2265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2342,11 +2279,11 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ - "derive_more-impl 2.1.1", + "derive_more-impl 2.0.1", ] [[package]] @@ -2357,20 +2294,19 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "unicode-xid", ] [[package]] name = "derive_more-impl" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.106", "unicode-xid", ] @@ -2406,7 +2342,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "const-oid", - "crypto-common 0.1.7", + "crypto-common", "subtle 2.6.1", ] @@ -2428,7 +2364,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2439,7 +2375,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2455,18 +2391,9 @@ dependencies = [ [[package]] name = "doc-comment" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dotenvy" @@ -2476,9 +2403,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dtoa" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dtoa-short" @@ -2497,9 +2424,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.20" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "easy-addr" @@ -2507,7 +2434,7 @@ version = "1.20.4" dependencies = [ "cosmwasm-std", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2563,12 +2490,12 @@ dependencies = [ [[package]] name = "ed25519-compact" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ce99a9e19c84beb4cc35ece85374335ccc398240712114c85038319ed709bd" +checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" dependencies = [ "ct-codecs", - "getrandom 0.3.4", + "getrandom 0.2.16", ] [[package]] @@ -2669,14 +2596,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "env_filter" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -2703,12 +2630,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.14" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2733,15 +2660,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.3" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -2754,7 +2675,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -2768,7 +2689,7 @@ dependencies = [ "nym-wasm-storage", "nym-wasm-utils", "serde-wasm-bindgen 0.6.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "wasm-bindgen", "wasm-bindgen-futures", "zeroize", @@ -2799,16 +2720,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.114", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", + "syn 2.0.106", ] [[package]] @@ -2835,21 +2747,21 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" -version = "0.2.26" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] name = "find-msvc-tools" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -2859,9 +2771,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -2902,9 +2814,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -3018,21 +2930,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.31" @@ -3041,7 +2938,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3080,6 +2977,20 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +[[package]] +name = "generator" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.61.3", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -3110,24 +3021,37 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasip2", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "ghash" version = "0.5.1" @@ -3139,10 +3063,16 @@ dependencies = [ ] [[package]] -name = "glob" -version = "0.3.3" +name = "gimli" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gloo-net" @@ -3154,7 +3084,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils 0.2.0", - "http 1.4.0", + "http 1.3.1", "js-sys", "pin-project", "serde", @@ -3237,7 +3167,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.13.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -3246,17 +3176,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.0", - "indexmap 2.13.0", + "http 1.3.1", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -3265,13 +3195,12 @@ dependencies = [ [[package]] name = "half" -version = "2.7.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", - "zerocopy", ] [[package]] @@ -3315,28 +3244,22 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.15.4", ] [[package]] @@ -3360,7 +3283,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3398,7 +3321,7 @@ dependencies = [ "base64 0.22.1", "bytes", "headers-core", - "http 1.4.0", + "http 1.3.1", "httpdate", "mime", "sha1", @@ -3410,7 +3333,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 1.4.0", + "http 1.3.1", ] [[package]] @@ -3439,12 +3362,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.2.2" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" -dependencies = [ - "arrayvec", -] +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" [[package]] name = "hex-literal" @@ -3466,18 +3386,18 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "h2 0.4.13", - "http 1.4.0", + "h2 0.4.11", + "http 1.3.1", "idna", "ipnet", "once_cell", "rand 0.9.2", "ring", - "rustls 0.23.36", - "thiserror 2.0.17", + "rustls 0.23.29", + "thiserror 2.0.12", "tinyvec", "tokio", - "tokio-rustls 0.26.4", + "tokio-rustls 0.26.2", "tracing", "url", "webpki-roots 0.26.11", @@ -3498,11 +3418,11 @@ dependencies = [ "parking_lot", "rand 0.9.2", "resolv-conf", - "rustls 0.23.36", + "rustls 0.23.29", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", - "tokio-rustls 0.26.4", + "tokio-rustls 0.26.2", "tracing", "webpki-roots 0.26.11", ] @@ -3539,9 +3459,9 @@ dependencies = [ [[package]] name = "hmac-sha1-compact" -version = "1.1.6" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a4e188bd5d537801721276d1771f1cea235cd94961ddb2828eb76e16688356" +checksum = "18492c9f6f9a560e0d346369b665ad2bdbc89fa9bceca75796584e79042694c3" [[package]] name = "hmac-sha256" @@ -3563,11 +3483,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.12" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3594,11 +3514,12 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", + "fnv", "itoa", ] @@ -3620,7 +3541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http 1.3.1", ] [[package]] @@ -3631,7 +3552,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] @@ -3672,9 +3593,9 @@ checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1" [[package]] name = "humantime" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "humantime-serde" @@ -3686,15 +3607,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hybrid-array" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" -dependencies = [ - "typenum", -] - [[package]] name = "hyper" version = "0.14.32" @@ -3721,22 +3633,20 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ - "atomic-waker", "bytes", "futures-channel", - "futures-core", - "h2 0.4.13", - "http 1.4.0", + "futures-util", + "h2 0.4.11", + "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -3762,13 +3672,13 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.4.0", - "hyper 1.8.1", + "http 1.3.1", + "hyper 1.6.0", "hyper-util", - "rustls 0.23.36", + "rustls 0.23.29", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.4", + "tokio-rustls 0.26.2", "tower-service", ] @@ -3778,7 +3688,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.8.1", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -3787,23 +3697,23 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", - "hyper 1.8.1", + "hyper 1.6.0", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -3811,9 +3721,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3821,7 +3731,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.61.2", ] [[package]] @@ -3845,7 +3755,7 @@ dependencies = [ "flex-error", "ics23", "informalsystems-pbjson", - "prost", + "prost 0.13.5", "serde", "subtle-encoding", "tendermint-proto", @@ -3862,15 +3772,15 @@ dependencies = [ "bytes", "hex", "informalsystems-pbjson", - "prost", + "prost 0.13.5", "serde", ] [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", @@ -3881,9 +3791,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -3894,10 +3804,11 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ + "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -3908,38 +3819,42 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", + "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", "icu_locale_core", + "stable_deref_trait", + "tinystr", "writeable", "yoke", "zerofrom", @@ -3947,6 +3862,12 @@ dependencies = [ "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" @@ -3955,9 +3876,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.1.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", @@ -3982,7 +3903,7 @@ checksum = "0ab604ee7085efba6efc65e4ebca0e9533e3aff6cb501d7d77b211e3a781c6d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4016,9 +3937,9 @@ dependencies = [ [[package]] name = "indenter" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexed_db_futures" @@ -4029,13 +3950,13 @@ dependencies = [ "accessory", "cfg-if", "delegate-display", - "derive_more 2.1.1", + "derive_more 2.0.1", "fancy_constructor", "indexed_db_futures_macros_internal", "js-sys", "sealed", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "wasm-bindgen", "wasm-bindgen-futures", @@ -4051,7 +3972,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4067,25 +3988,24 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.15.4", "serde", - "serde_core", ] [[package]] name = "indicatif" -version = "0.18.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" +checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" dependencies = [ "console", "portable-atomic", - "unicode-width 0.2.2", + "unicode-width 0.2.1", "unit-prefix", "vt100", "web-time", @@ -4131,15 +4051,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "inout" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" -dependencies = [ - "hybrid-array", -] - [[package]] name = "inquire" version = "0.6.2" @@ -4156,21 +4067,6 @@ dependencies = [ "unicode-width 0.1.14", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - [[package]] name = "integration-tests" version = "0.1.0" @@ -4201,6 +4097,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ip_network" version = "0.4.1" @@ -4230,7 +4137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ "socket2 0.5.10", - "widestring 1.2.1", + "widestring 1.2.0", "windows-sys 0.48.0", "winreg", ] @@ -4252,9 +4159,9 @@ dependencies = [ [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" dependencies = [ "memchr", "serde", @@ -4262,45 +4169,20 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.17" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.2" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "isahc" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" -dependencies = [ - "async-channel", - "castaway", - "crossbeam-utils", - "curl", - "curl-sys", - "event-listener 2.5.3", - "futures-lite", - "http 0.2.12", - "log", - "once_cell", - "polling", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -4322,32 +4204,32 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.18" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde_core", + "serde", ] [[package]] name = "jiff-static" -version = "0.2.18" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4374,19 +4256,19 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.3.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -4394,9 +4276,9 @@ dependencies = [ [[package]] name = "jwt-simple" -version = "0.12.13" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ad8761f175784dfbb83709f322fc4daf6b27afd5bf375492f2876f9e925ef5a" +checksum = "731011e9647a71ff4f8474176ff6ce6e0d2de87a0173f15613af3a84c3e3401a" dependencies = [ "anyhow", "binstring", @@ -4414,7 +4296,7 @@ dependencies = [ "serde", "serde_json", "superboring", - "thiserror 2.0.17", + "thiserror 2.0.12", "zeroize", ] @@ -4476,6 +4358,12 @@ dependencies = [ "spin", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "ledger-apdu" version = "0.10.0" @@ -4519,21 +4407,10 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" -[[package]] -name = "libcrux-aesgcm" -version = "0.0.5" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" -dependencies = [ - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-secrets", - "libcrux-traits", -] - [[package]] name = "libcrux-chacha20poly1305" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4545,7 +4422,7 @@ dependencies = [ [[package]] name = "libcrux-curve25519" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4556,7 +4433,7 @@ dependencies = [ [[package]] name = "libcrux-ecdh" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-curve25519", "libcrux-p256", @@ -4567,7 +4444,7 @@ dependencies = [ [[package]] name = "libcrux-ed25519" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4579,7 +4456,7 @@ dependencies = [ [[package]] name = "libcrux-hacl-rs" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-macros", ] @@ -4587,7 +4464,7 @@ dependencies = [ [[package]] name = "libcrux-hkdf" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-hmac", @@ -4597,7 +4474,7 @@ dependencies = [ [[package]] name = "libcrux-hmac" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4607,7 +4484,7 @@ dependencies = [ [[package]] name = "libcrux-intrinsics" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "core-models", "hax-lib", @@ -4616,7 +4493,7 @@ dependencies = [ [[package]] name = "libcrux-kem" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-curve25519", "libcrux-ecdh", @@ -4631,30 +4508,16 @@ dependencies = [ [[package]] name = "libcrux-macros" version = "0.0.3" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "quote", - "syn 2.0.114", -] - -[[package]] -name = "libcrux-ml-dsa" -version = "0.0.5" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" -dependencies = [ - "core-models", - "hax-lib", - "libcrux-intrinsics", - "libcrux-macros", - "libcrux-platform", - "libcrux-sha3", - "tls_codec", + "syn 2.0.106", ] [[package]] name = "libcrux-ml-kem" -version = "0.0.5" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +version = "0.0.4" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "hax-lib", "libcrux-intrinsics", @@ -4669,7 +4532,7 @@ dependencies = [ [[package]] name = "libcrux-p256" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4680,8 +4543,8 @@ dependencies = [ [[package]] name = "libcrux-platform" -version = "0.0.3" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libc", ] @@ -4689,7 +4552,7 @@ dependencies = [ [[package]] name = "libcrux-poly1305" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4698,16 +4561,14 @@ dependencies = [ [[package]] name = "libcrux-psq" version = "0.0.5" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ - "libcrux-aesgcm", "libcrux-chacha20poly1305", "libcrux-ecdh", "libcrux-ed25519", "libcrux-hkdf", "libcrux-hmac", "libcrux-kem", - "libcrux-ml-dsa", "libcrux-ml-kem", "libcrux-sha2", "libcrux-traits", @@ -4718,7 +4579,7 @@ dependencies = [ [[package]] name = "libcrux-secrets" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "hax-lib", ] @@ -4726,7 +4587,7 @@ dependencies = [ [[package]] name = "libcrux-sha2" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4735,8 +4596,8 @@ dependencies = [ [[package]] name = "libcrux-sha3" -version = "0.0.5" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +version = "0.0.4" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "hax-lib", "libcrux-intrinsics", @@ -4747,7 +4608,7 @@ dependencies = [ [[package]] name = "libcrux-traits" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#21cf9cae922f55ca2394641d5ee7bbabfd36ace0" +source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" dependencies = [ "libcrux-secrets", "rand 0.9.2", @@ -4771,13 +4632,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "libc", - "redox_syscall 0.7.0", + "redox_syscall", ] [[package]] @@ -4792,22 +4653,16 @@ dependencies = [ ] [[package]] -name = "libz-sys" -version = "1.1.23" +name = "linux-raw-sys" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "lioness" @@ -4823,30 +4678,38 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.14" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.29" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] [[package]] name = "lru-slab" @@ -4880,7 +4743,7 @@ dependencies = [ "proc-macro2", "quote", "sealed", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4892,7 +4755,7 @@ dependencies = [ "proc-macro2", "quote", "sealed", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4905,7 +4768,7 @@ dependencies = [ "macroific_core", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4933,7 +4796,7 @@ checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4963,9 +4826,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -4994,9 +4857,9 @@ dependencies = [ [[package]] name = "minicov" -version = "0.3.8" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" dependencies = [ "cc", "walkdir", @@ -5015,7 +4878,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", - "simd-adler32", ] [[package]] @@ -5026,19 +4888,19 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.48.0", ] [[package]] name = "mio" -version = "1.1.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi", - "windows-sys 0.61.2", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -5059,7 +4921,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde-wasm-bindgen 0.6.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tsify", "url", @@ -5090,21 +4952,23 @@ checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6" [[package]] name = "moka" -version = "0.12.12" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3dec6bd31b08944e08b58fd99373893a6c17054d6f3ea5006cc894f4f4eee2a" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ "async-lock", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "equivalent", - "event-listener 5.4.1", + "event-listener", "futures-util", + "loom", "parking_lot", "portable-atomic", + "rustc_version 0.4.1", "smallvec", "tagptr", + "thiserror 1.0.69", "uuid", ] @@ -5117,7 +4981,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.4.0", + "http 1.3.1", "httparse", "memchr", "mime", @@ -5125,6 +4989,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils 0.5.2", +] + [[package]] name = "netlink-packet-core" version = "0.8.1" @@ -5134,13 +5009,25 @@ dependencies = [ "paste", ] +[[package]] +name = "netlink-packet-generic" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7eb8ad331c84c6b8cb7f685b448133e5ad82e1ffd5acafac374af4a5a308b" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-core 0.7.0", + "netlink-packet-utils 0.5.2", +] + [[package]] name = "netlink-packet-generic" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f891b2e0054cac5a684a06628f59568f841c93da4e551239da6e518f539e775" dependencies = [ - "netlink-packet-core", + "netlink-packet-core 0.8.1", ] [[package]] @@ -5149,10 +5036,22 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ec2f5b6839be2a19d7fa5aab5bc444380f6311c2b693551cb80f45caaa7b5ef" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "libc", "log", - "netlink-packet-core", + "netlink-packet-core 0.8.1", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror 1.0.69", ] [[package]] @@ -5162,20 +5061,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3176f18d11a1ae46053e59ec89d46ba318ae1343615bd3f8c908bfc84edae35c" dependencies = [ "byteorder", - "pastey 0.1.1", - "thiserror 2.0.17", + "pastey", + "thiserror 2.0.12", ] [[package]] name = "netlink-packet-wireguard" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598962d9067d3153a00106da10e7b8276cea68f396f4a22f5b4a079270d92e29" +checksum = "60b25b050ff1f6a1e23c6777b72db22790fe5b6b5ccfd3858672587a79876c8f" dependencies = [ + "anyhow", + "byteorder", "libc", "log", - "netlink-packet-core", - "netlink-packet-generic", + "netlink-packet-generic 0.3.3", + "netlink-packet-utils 0.5.2", ] [[package]] @@ -5210,7 +5111,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", @@ -5222,13 +5123,25 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", "memoffset", ] +[[package]] +name = "nix" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -5277,30 +5190,20 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "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", + "windows-sys 0.60.2", ] [[package]] @@ -5315,10 +5218,11 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.6" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ + "byteorder", "lazy_static", "libm", "num-integer", @@ -5331,9 +5235,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" @@ -5365,16 +5269,6 @@ dependencies = [ "libm", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_enum" version = "0.7.5" @@ -5394,7 +5288,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5466,7 +5360,7 @@ dependencies = [ "rand_chacha 0.3.1", "reqwest 0.13.1", "schemars 0.8.22", - "semver 1.0.27", + "semver 1.0.26", "serde", "serde_json", "sha2 0.10.9", @@ -5474,7 +5368,7 @@ dependencies = [ "tempfile", "tendermint", "test-with", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -5525,7 +5419,7 @@ dependencies = [ "strum_macros", "tendermint", "tendermint-rpc", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "ts-rs", @@ -5557,8 +5451,8 @@ dependencies = [ "nym-service-provider-requests-common", "nym-validator-client", "nym-wireguard-types", - "semver 1.0.27", - "thiserror 2.0.17", + "semver 1.0.26", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -5579,11 +5473,11 @@ dependencies = [ "nym-test-utils", "nym-wireguard-types", "rand 0.8.5", - "semver 1.0.27", + "semver 1.0.26", "serde", "sha2 0.10.9", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.12", "tracing", "x25519-dalek", ] @@ -5602,7 +5496,7 @@ dependencies = [ "nym-task", "nym-validator-client", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -5615,14 +5509,14 @@ dependencies = [ "const-str", "log", "opentelemetry", - "opentelemetry-jaeger", + "opentelemetry-otlp", + "opentelemetry_sdk", "schemars 0.8.22", "serde", "serde_json", "tracing", "tracing-opentelemetry", "tracing-subscriber", - "tracing-tree", "utoipa", "vergen 8.3.1", ] @@ -5726,7 +5620,7 @@ dependencies = [ "serde_json", "tap", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "toml 0.8.23", @@ -5763,7 +5657,7 @@ dependencies = [ "serde", "serde_json", "tap", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-tungstenite", @@ -5785,7 +5679,7 @@ dependencies = [ "gloo-timers", "http-body-util", "humantime", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-util", "nym-bandwidth-controller", "nym-client-core-config-types", @@ -5817,7 +5711,7 @@ dependencies = [ "sha2 0.10.9", "si-scale", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -5843,7 +5737,7 @@ dependencies = [ "nym-sphinx-params", "nym-statistics-common", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "url", ] @@ -5858,7 +5752,7 @@ dependencies = [ "nym-gateway-requests", "serde", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -5878,7 +5772,7 @@ dependencies = [ "nym-sqlx-pool-guard", "nym-task", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -5903,7 +5797,7 @@ dependencies = [ "serde", "serde-wasm-bindgen 0.6.5", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio_with_wasm", "tsify", "wasm-bindgen", @@ -5962,7 +5856,7 @@ dependencies = [ "serde", "sha2 0.10.9", "subtle 2.6.1", - "thiserror 2.0.17", + "thiserror 2.0.12", "zeroize", ] @@ -5975,7 +5869,7 @@ dependencies = [ "log", "nym-network-defaults", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "toml 0.8.23", "url", ] @@ -5992,7 +5886,7 @@ dependencies = [ "nym-ip-packet-requests", "nym-sdk", "pnet_packet", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -6010,7 +5904,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "utoipa", "vergen 8.3.1", ] @@ -6079,7 +5973,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-util", @@ -6120,7 +6014,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-util", @@ -6171,7 +6065,7 @@ dependencies = [ "nym-test-utils", "serde", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "zeroize", @@ -6190,7 +6084,7 @@ dependencies = [ "nym-credentials-interface", "nym-ecash-time", "nym-validator-client", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", ] @@ -6217,7 +6111,7 @@ dependencies = [ "nym-upgrade-mode-check", "nym-validator-client", "si-scale", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -6242,7 +6136,7 @@ dependencies = [ "nym-validator-client", "rand 0.8.5", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "zeroize", ] @@ -6260,7 +6154,7 @@ dependencies = [ "serde", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "utoipa", ] @@ -6270,13 +6164,13 @@ name = "nym-crypto" version = "1.20.4" dependencies = [ "aead", - "aes 0.8.4", + "aes", "aes-gcm-siv", "anyhow", "base64 0.22.1", "blake3", "bs58", - "cipher 0.4.4", + "cipher", "ctr", "curve25519-dalek", "digest 0.10.7", @@ -6295,7 +6189,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "subtle-encoding", - "thiserror 2.0.17", + "thiserror 2.0.12", "x25519-dalek", "zeroize", ] @@ -6324,7 +6218,7 @@ dependencies = [ "serde", "serde_json", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-util", @@ -6355,7 +6249,7 @@ dependencies = [ "serde", "serde_derive", "sha2 0.10.9", - "thiserror 2.0.17", + "thiserror 2.0.12", "zeroize", ] @@ -6370,7 +6264,7 @@ dependencies = [ "cw-utils", "cw2", "nym-multisig-contract-common", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -6382,9 +6276,9 @@ dependencies = [ "nym-http-api-client", "nym-network-defaults", "nym-validator-client", - "semver 1.0.27", + "semver 1.0.26", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tracing", "url", @@ -6396,9 +6290,9 @@ version = "1.20.4" dependencies = [ "nym-coconut-dkg-common", "nym-crypto", - "semver 1.0.27", + "semver 1.0.26", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "url", @@ -6420,7 +6314,7 @@ dependencies = [ "reqwest 0.13.1", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "tracing", "utoipa", ] @@ -6437,8 +6331,8 @@ dependencies = [ "nym-sdk", "nym-sphinx-anonymous-replies", "tokio", - "uniffi 0.29.5", - "uniffi_build 0.29.5", + "uniffi 0.29.3", + "uniffi_build 0.29.3", ] [[package]] @@ -6453,7 +6347,7 @@ dependencies = [ "bytes", "dashmap", "defguard_wireguard_rs", - "fastrand 2.3.0", + "fastrand", "futures", "humantime-serde", "ipnetwork", @@ -6493,7 +6387,7 @@ dependencies = [ "nym-wireguard-types", "rand 0.8.5", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -6527,7 +6421,7 @@ dependencies = [ "rand 0.8.5", "serde", "si-scale", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -6615,7 +6509,7 @@ dependencies = [ "serde_json", "strum", "subtle 2.6.1", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -6634,7 +6528,7 @@ dependencies = [ "nym-statistics-common", "sqlx", "strum", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -6653,7 +6547,7 @@ dependencies = [ "nym-gateway-requests", "nym-sphinx", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -6670,10 +6564,10 @@ dependencies = [ "nym-ffi-shared", "nym-sdk", "nym-sphinx-anonymous-replies", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", - "uniffi 0.29.5", - "uniffi_build 0.29.5", + "uniffi 0.29.3", + "uniffi_build 0.29.3", ] [[package]] @@ -6697,7 +6591,7 @@ dependencies = [ "cfg-if", "encoding_rs", "hickory-resolver", - "http 1.4.0", + "http 1.3.1", "inventory", "itertools 0.14.0", "mime", @@ -6711,7 +6605,7 @@ dependencies = [ "serde_json", "serde_plain", "serde_yaml", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tracing", "tracing-subscriber", @@ -6728,7 +6622,7 @@ dependencies = [ "proc-macro2", "quote", "reqwest 0.13.1", - "syn 2.0.114", + "syn 2.0.106", "uuid", ] @@ -6760,7 +6654,7 @@ version = "1.20.4" dependencies = [ "nym-credential-storage", "nym-credentials", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "zeroize", @@ -6786,7 +6680,7 @@ version = "1.20.4" dependencies = [ "log", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -6798,7 +6692,7 @@ dependencies = [ "futures", "nym-ip-packet-requests", "nym-sdk", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -6816,7 +6710,7 @@ dependencies = [ "nym-sphinx", "rand 0.8.5", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-util", @@ -6858,7 +6752,7 @@ dependencies = [ "reqwest 0.13.1", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-tun", @@ -6874,7 +6768,7 @@ dependencies = [ "bytes", "env_logger", "log", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio-util", ] @@ -6895,7 +6789,7 @@ dependencies = [ "rand 0.9.2", "rand_chacha 0.9.0", "strum", - "thiserror 2.0.17", + "thiserror 2.0.12", "zeroize", ] @@ -6908,7 +6802,7 @@ dependencies = [ "num_enum", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -6919,7 +6813,7 @@ dependencies = [ "k256", "ledger-transport", "ledger-transport-hid", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -6948,7 +6842,7 @@ dependencies = [ "serde", "sha2 0.10.9", "snow", - "thiserror 2.0.17", + "thiserror 2.0.12", "tls_codec", "tokio", "tracing", @@ -7044,10 +6938,10 @@ dependencies = [ "nym-contracts-common", "rand_chacha 0.3.1", "schemars 0.8.22", - "semver 1.0.27", + "semver 1.0.26", "serde", "serde_repr", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "ts-rs", "utoipa", @@ -7073,7 +6967,7 @@ dependencies = [ "nym-task", "rand 0.8.5", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-util", @@ -7092,7 +6986,7 @@ dependencies = [ "cw4", "schemars 0.8.22", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7188,7 +7082,7 @@ dependencies = [ "sqlx", "tap", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-tungstenite", @@ -7262,13 +7156,15 @@ dependencies = [ "nym-verloc", "nym-wireguard", "nym-wireguard-types", + "opentelemetry", + "opentelemetry_sdk", "rand 0.8.5", "rand_chacha 0.3.1", "serde", "serde_json", "sha2 0.10.9", "sysinfo", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -7319,7 +7215,7 @@ dependencies = [ "serde_json", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "url", @@ -7384,21 +7280,21 @@ dependencies = [ "rand_chacha 0.3.1", "regex", "reqwest 0.13.1", - "semver 1.0.27", + "semver 1.0.26", "serde", "serde_json", "serde_json_path", "sqlx", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", "tokio-util", "tower-http", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", "utoipa", "utoipa-swagger-ui", @@ -7438,7 +7334,7 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", ] @@ -7454,7 +7350,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde-wasm-bindgen 0.6.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tsify", "wasm-bindgen", @@ -7479,7 +7375,7 @@ dependencies = [ "snow", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -7526,7 +7422,7 @@ name = "nym-ordered-buffer" version = "1.20.4" dependencies = [ "log", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7537,13 +7433,13 @@ dependencies = [ "chacha20", "chacha20poly1305", "criterion", - "fastrand 2.3.0", + "fastrand", "getrandom 0.2.16", "log", "rand 0.8.5", "rayon", "sphinx-packet", - "thiserror 2.0.17", + "thiserror 2.0.12", "x25519-dalek", "zeroize", ] @@ -7567,7 +7463,7 @@ dependencies = [ "nym-contracts-common", "schemars 0.8.22", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7579,7 +7475,7 @@ dependencies = [ "cw-controllers", "schemars 0.8.22", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", ] @@ -7602,7 +7498,7 @@ dependencies = [ "nym-validator-client", "nym-wireguard-types", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -7643,7 +7539,7 @@ dependencies = [ "dotenvy", "futures", "hex", - "http 1.4.0", + "http 1.3.1", "httpcodec", "log", "nym-bandwidth-controller", @@ -7673,7 +7569,7 @@ dependencies = [ "serde", "tap", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -7703,7 +7599,7 @@ version = "1.20.4" dependencies = [ "bincode", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7716,7 +7612,7 @@ dependencies = [ "nym-sphinx-anonymous-replies", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7765,7 +7661,7 @@ dependencies = [ "serde", "serde_json", "tap", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "url", @@ -7799,7 +7695,7 @@ dependencies = [ "schemars 0.8.22", "serde", "tap", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "url", ] @@ -7831,7 +7727,7 @@ dependencies = [ "serde", "serde_json", "tap", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7855,7 +7751,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_distr", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -7874,7 +7770,7 @@ dependencies = [ "nym-topology", "rand 0.8.5", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "zeroize", ] @@ -7888,7 +7784,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7904,7 +7800,7 @@ dependencies = [ "nym-topology", "rand 0.8.5", "rand_chacha 0.3.1", - "thiserror 2.0.17", + "thiserror 2.0.12", "tracing", "wasm-bindgen", ] @@ -7922,7 +7818,7 @@ dependencies = [ "nym-sphinx-types", "rand 0.8.5", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "utoipa", "wasmtimer", ] @@ -7941,7 +7837,7 @@ dependencies = [ "nym-sphinx-types", "nym-topology", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7952,7 +7848,7 @@ dependencies = [ "nym-sphinx-anonymous-replies", "nym-sphinx-params", "nym-sphinx-types", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7966,7 +7862,7 @@ dependencies = [ "nym-sphinx-forwarding", "nym-sphinx-params", "nym-sphinx-types", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -7979,7 +7875,7 @@ dependencies = [ "nym-crypto", "nym-sphinx-types", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7988,7 +7884,7 @@ version = "1.20.4" dependencies = [ "nym-sphinx-addressing", "nym-sphinx-types", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -7997,7 +7893,7 @@ version = "1.20.4" dependencies = [ "nym-outfox", "sphinx-packet", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -8061,7 +7957,7 @@ dependencies = [ "strum", "strum_macros", "sysinfo", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "utoipa", @@ -8079,7 +7975,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "zeroize", ] @@ -8092,7 +7988,7 @@ dependencies = [ "futures", "log", "nym-test-utils", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -8145,7 +8041,7 @@ dependencies = [ "reqwest 0.13.1", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "tsify", @@ -8159,7 +8055,7 @@ dependencies = [ "etherparse", "log", "nym-wireguard-types", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-tun", ] @@ -8188,7 +8084,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "ts-rs", "url", "utoipa", @@ -8207,7 +8103,7 @@ dependencies = [ "reqwest 0.13.1", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "utoipa", @@ -8248,13 +8144,13 @@ dependencies = [ "nym-performance-contract-common", "nym-serde-helpers", "nym-vesting-contract-common", - "prost", + "prost 0.13.5", "reqwest 0.13.1", "serde", "serde_json", "sha2 0.10.9", "tendermint-rpc", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -8299,7 +8195,7 @@ dependencies = [ "serde_with", "sha2 0.10.9", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -8320,7 +8216,7 @@ dependencies = [ "nym-task", "nym-validator-client", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-util", @@ -8338,7 +8234,7 @@ dependencies = [ "nym-contracts-common", "nym-mixnet-contract-common", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "ts-rs", ] @@ -8360,7 +8256,7 @@ dependencies = [ "serde", "serde-wasm-bindgen 0.6.5", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tsify", "wasm-bindgen", @@ -8412,7 +8308,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde-wasm-bindgen 0.6.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tsify", "url", @@ -8433,7 +8329,7 @@ dependencies = [ "nym-wasm-utils", "serde", "serde-wasm-bindgen 0.6.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "wasm-bindgen", ] @@ -8477,7 +8373,7 @@ dependencies = [ "nym-test-utils", "nym-wireguard-types", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tokio-stream", "tracing", @@ -8522,7 +8418,7 @@ dependencies = [ "nym-credentials-interface", "schemars 0.8.22", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "utoipa", ] @@ -8558,7 +8454,7 @@ dependencies = [ "nym-crypto", "rand 0.8.5", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "x25519-dalek", ] @@ -8585,7 +8481,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "tar", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -8611,7 +8507,7 @@ dependencies = [ "schemars 0.8.22", "serde", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-util", @@ -8635,7 +8531,7 @@ dependencies = [ "serde", "serde_json", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -8653,13 +8549,13 @@ dependencies = [ "futures", "humantime", "ibc-proto", - "prost", + "prost 0.13.5", "serde", "serde_json", "sha2 0.10.9", "tendermint", "tendermint-rpc", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -8676,30 +8572,39 @@ dependencies = [ "async-trait", "nyxd-scraper-shared", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tracing", ] [[package]] name = "objc2-core-foundation" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", ] [[package]] name = "objc2-io-kit" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" dependencies = [ "libc", "objc2-core-foundation", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -8712,9 +8617,9 @@ dependencies = [ [[package]] name = "once_cell_polyfill" -version = "1.70.2" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "oorandom" @@ -8746,106 +8651,78 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "opentelemetry" -version = "0.19.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4b8347cc26099d3aeee044065ecc3ae11469796b4d65d065a23a584ed92a6f" +checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" dependencies = [ - "opentelemetry_api", - "opentelemetry_sdk", + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.12", + "tracing", ] [[package]] name = "opentelemetry-http" -version = "0.8.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a819b71d6530c4297b49b3cae2939ab3a8cc1b9f382826a1bc29dd0ca3864906" +checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" dependencies = [ "async-trait", "bytes", - "http 0.2.12", - "isahc", - "opentelemetry_api", + "http 1.3.1", + "opentelemetry", + "reqwest 0.12.22", ] [[package]] -name = "opentelemetry-jaeger" -version = "0.18.0" +name = "opentelemetry-otlp" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e028dc9f4f304e9320ce38c80e7cf74067415b1ad5a8750a38bae54a4d450d" +checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" dependencies = [ - "async-trait", - "futures", - "futures-executor", - "http 0.2.12", - "isahc", - "once_cell", + "http 1.3.1", "opentelemetry", "opentelemetry-http", - "opentelemetry-semantic-conventions", - "thiserror 1.0.69", - "thrift", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost 0.14.3", + "reqwest 0.12.22", + "thiserror 2.0.12", "tokio", + "tonic 0.14.4", + "tracing", ] [[package]] -name = "opentelemetry-semantic-conventions" -version = "0.11.0" +name = "opentelemetry-proto" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e33428e6bf08c6f7fcea4ddb8e358fab0fe48ab877a87c70c6ebe20f673ce5" +checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ "opentelemetry", -] - -[[package]] -name = "opentelemetry_api" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2" -dependencies = [ - "fnv", - "futures-channel", - "futures-util", - "indexmap 1.9.3", - "once_cell", - "pin-project-lite", - "thiserror 1.0.69", - "urlencoding", + "opentelemetry_sdk", + "prost 0.14.3", + "tonic 0.14.4", + "tonic-prost", ] [[package]] name = "opentelemetry_sdk" -version = "0.19.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1" +checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" dependencies = [ - "async-trait", - "crossbeam-channel", - "dashmap", - "fnv", "futures-channel", "futures-executor", "futures-util", - "once_cell", - "opentelemetry_api", + "opentelemetry", "percent-encoding", - "rand 0.8.5", - "thiserror 1.0.69", - "tokio", - "tokio-stream", + "rand 0.9.2", + "thiserror 2.0.12", ] [[package]] @@ -8854,21 +8731,6 @@ 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 = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "p256" version = "0.13.2" @@ -8910,9 +8772,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.5" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -8920,15 +8782,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.12" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", - "windows-link 0.2.1", + "windows-targets 0.52.6", ] [[package]] @@ -8954,12 +8816,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" -[[package]] -name = "pastey" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" - [[package]] name = "peg" version = "0.8.5" @@ -9009,25 +8865,26 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", + "thiserror 2.0.12", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -9035,22 +8892,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ "pest", "sha2 0.10.9", @@ -9063,7 +8920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.13.0", + "indexmap 2.10.0", ] [[package]] @@ -9073,17 +8930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_shared 0.13.1", - "serde", + "phf_shared", ] [[package]] @@ -9093,7 +8940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ "phf_generator", - "phf_shared 0.11.3", + "phf_shared", ] [[package]] @@ -9102,7 +8949,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.3", + "phf_shared", "rand 0.8.5", ] @@ -9113,10 +8960,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", - "phf_shared 0.11.3", + "phf_shared", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -9128,15 +8975,6 @@ dependencies = [ "siphasher 1.0.1", ] -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher 1.0.1", -] - [[package]] name = "pin-project" version = "1.1.10" @@ -9154,7 +8992,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -9248,7 +9086,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -9272,22 +9110,6 @@ dependencies = [ "pnet_macros_support", ] -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "poly1305" version = "0.8.0" @@ -9313,9 +9135,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -9328,9 +9150,9 @@ dependencies = [ [[package]] name = "postgres-protocol" -version = "0.6.9" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbef655056b916eb868048276cfd5d6a7dea4f81560dfd047f97c8c6fe3fcfd4" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" dependencies = [ "base64 0.22.1", "byteorder", @@ -9346,9 +9168,9 @@ dependencies = [ [[package]] name = "postgres-types" -version = "0.2.11" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4605b7c057056dd35baeb6ac0c0338e4975b1f2bef0f65da953285eb007095" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" dependencies = [ "bytes", "fallible-iterator", @@ -9357,9 +9179,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ "zerovec", ] @@ -9395,6 +9217,16 @@ dependencies = [ "yansi", ] +[[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.106", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -9406,11 +9238,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit", ] [[package]] @@ -9432,14 +9264,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -9465,7 +9297,7 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -9475,7 +9307,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.13.5", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive 0.14.3", ] [[package]] @@ -9488,7 +9330,20 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -9497,7 +9352,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" dependencies = [ - "prost", + "prost 0.13.5", ] [[package]] @@ -9522,9 +9377,9 @@ dependencies = [ [[package]] name = "psl" -version = "2.1.176" +version = "2.1.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f06bb516febf92008f7e4ebdc1129a75e07905eadc4601af27093cb83928c78" +checksum = "45f621acfbd2ca5670eee9a95270747dfa9a29e63e1937d7e6a1ac6994331966" dependencies = [ "psl-types", ] @@ -9553,9 +9408,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", "cfg_aliases", @@ -9563,9 +9418,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.36", - "socket2 0.6.1", - "thiserror 2.0.17", + "rustls 0.23.29", + "socket2 0.5.10", + "thiserror 2.0.12", "tokio", "tracing", "web-time", @@ -9573,21 +9428,21 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.3.3", "lru-slab", "rand 0.9.2", "ring", "rustc-hash", - "rustls 0.23.36", + "rustls 0.23.29", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -9595,23 +9450,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.43" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -9684,7 +9539,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.3.3", ] [[package]] @@ -9699,9 +9554,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.11.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -9709,9 +9564,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.13.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -9719,20 +9574,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.18" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" dependencies = [ - "bitflags 2.10.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" -dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", ] [[package]] @@ -9743,34 +9589,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -9780,9 +9626,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -9791,9 +9637,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -9836,6 +9682,40 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tower 0.5.2", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "reqwest" version = "0.13.1" @@ -9846,10 +9726,10 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-rustls 0.27.7", "hyper-util", "js-sys", @@ -9857,7 +9737,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.36", + "rustls 0.23.29", "rustls-pki-types", "rustls-platform-verifier", "serde", @@ -9865,7 +9745,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.26.4", + "tokio-rustls 0.26.2", "tokio-util", "tower 0.5.2", "tower-http", @@ -9883,14 +9763,14 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21918d6644020c6f6ef1993242989bf6d4952d2e025617744f184c02df51c356" dependencies = [ - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] name = "resolv-conf" -version = "0.7.6" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" +checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" [[package]] name = "rfc6979" @@ -9927,19 +9807,22 @@ dependencies = [ [[package]] name = "rmp" -version = "0.8.15" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ + "byteorder", "num-traits", + "paste", ] [[package]] name = "rmp-serde" -version = "1.3.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" dependencies = [ + "byteorder", "rmp", "serde", ] @@ -9955,9 +9838,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.10" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest 0.10.7", @@ -9976,9 +9859,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.9.0" +version = "8.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" +checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -9987,22 +9870,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.9.0" +version = "8.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" +checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.114", + "syn 2.0.106", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.9.0" +version = "8.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" +checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" dependencies = [ "sha2 0.10.9", "walkdir", @@ -10024,6 +9907,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -10045,20 +9934,33 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.27", + "semver 1.0.26", ] [[package]] name = "rustix" -version = "1.1.3" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.61.2", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.60.2", ] [[package]] @@ -10089,16 +9991,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ "aws-lc-rs", "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.4", "subtle 2.6.1", "zeroize", ] @@ -10137,7 +10039,7 @@ dependencies = [ "openssl-probe 0.2.1", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework 3.6.0", ] [[package]] @@ -10160,9 +10062,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", "zeroize", @@ -10179,14 +10081,14 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.36", + "rustls 0.23.29", "rustls-native-certs 0.8.3", "rustls-platform-verifier-android", - "rustls-webpki 0.103.8", - "security-framework 3.5.1", + "rustls-webpki 0.103.4", + "security-framework 3.6.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -10218,9 +10120,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "aws-lc-rs", "ring", @@ -10230,15 +10132,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -10251,11 +10153,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -10286,9 +10188,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "ref-cast", @@ -10305,9 +10207,15 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals 0.29.1", - "syn 2.0.114", + "syn 2.0.106", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -10331,7 +10239,7 @@ checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10352,7 +10260,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10394,7 +10302,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -10403,11 +10311,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -10416,9 +10324,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" dependencies = [ "core-foundation-sys", "libc", @@ -10435,12 +10343,11 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", - "serde_core", ] [[package]] @@ -10492,12 +10399,11 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.19" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", - "serde_core", ] [[package]] @@ -10517,7 +10423,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10528,7 +10434,7 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10539,20 +10445,19 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" dependencies = [ "itoa", "memchr", + "ryu", "serde", - "serde_core", - "zmij", ] [[package]] @@ -10568,7 +10473,7 @@ dependencies = [ "serde_json", "serde_json_path_core", "serde_json_path_macros", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -10580,7 +10485,7 @@ dependencies = [ "inventory", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", ] [[package]] @@ -10602,18 +10507,17 @@ checksum = "aafbefbe175fa9bf03ca83ef89beecff7d2a95aaacd5732325b90ac8c3bd7b90" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "serde_path_to_error" -version = "0.1.20" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", - "serde_core", ] [[package]] @@ -10633,7 +10537,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10659,18 +10563,19 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.10.0", "schemars 0.9.0", - "schemars 1.2.0", - "serde_core", + "schemars 1.0.4", + "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -10678,14 +10583,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ - "darling 0.21.3", + "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10694,7 +10599,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -10799,9 +10704,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio 0.8.11", @@ -10810,11 +10715,10 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.8" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ - "errno", "libc", ] @@ -10830,9 +10734,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "siphasher" @@ -10852,17 +10756,6 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -10928,12 +10821,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -10942,7 +10835,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c26f0c20d909fdda1c5d0ece3973127ca421984d55b000215df365e93722fc6e" dependencies = [ - "aes 0.8.4", + "aes", "arrayref", "blake2 0.8.1", "bs58", @@ -11006,24 +10899,24 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.4.1", + "event-listener", "futures-core", "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.5", + "hashbrown 0.15.4", "hashlink", - "indexmap 2.13.0", + "indexmap 2.10.0", "log", "memchr", "once_cell", "percent-encoding", - "rustls 0.23.36", + "rustls 0.23.29", "serde", "serde_json", "sha2 0.10.9", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "tokio-stream", @@ -11042,7 +10935,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -11065,7 +10958,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.114", + "syn 2.0.106", "tokio", "url", ] @@ -11078,7 +10971,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.9.1", "byteorder", "bytes", "chrono", @@ -11108,7 +11001,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "whoami", @@ -11122,7 +11015,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.9.1", "byteorder", "chrono", "crc", @@ -11147,7 +11040,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "whoami", @@ -11173,7 +11066,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tracing", "url", @@ -11191,9 +11084,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_assertions" @@ -11209,7 +11102,7 @@ checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared 0.11.3", + "phf_shared", "precomputed-hash", "serde", ] @@ -11221,7 +11114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ "phf_generator", - "phf_shared 0.11.3", + "phf_shared", "proc-macro2", "quote", ] @@ -11261,7 +11154,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -11293,12 +11186,10 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "superboring" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d8c985e81c88f5694d5dfc232691b2aa34f3e1f66e860db9cd1ddf2bb6dc64" +checksum = "515cce34a781d7250b8a65706e0f2a5b99236ea605cb235d4baed6685820478f" dependencies = [ - "aes-gcm", - "aes-keywrap", "getrandom 0.2.16", "hmac-sha256", "hmac-sha512", @@ -11319,9 +11210,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -11351,14 +11242,14 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "sysinfo" -version = "0.37.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" +checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" dependencies = [ "libc", "memchr", @@ -11414,15 +11305,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.24.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "fastrand 2.3.0", - "getrandom 0.3.4", + "fastrand", + "getrandom 0.3.3", "once_cell", - "rustix", - "windows-sys 0.61.2", + "rustix 1.0.8", + "windows-sys 0.59.0", ] [[package]] @@ -11440,7 +11331,7 @@ dependencies = [ "k256", "num-traits", "once_cell", - "prost", + "prost 0.13.5", "ripemd", "serde", "serde_bytes", @@ -11477,7 +11368,7 @@ checksum = "d2c40e13d39ca19082d8a7ed22de7595979350319833698f8b1080f29620a094" dependencies = [ "bytes", "flex-error", - "prost", + "prost 0.13.5", "serde", "serde_bytes", "subtle-encoding", @@ -11500,7 +11391,7 @@ dependencies = [ "pin-project", "rand 0.8.5", "reqwest 0.11.27", - "semver 1.0.27", + "semver 1.0.26", "serde", "serde_bytes", "serde_json", @@ -11540,15 +11431,15 @@ dependencies = [ [[package]] name = "test-with" -version = "0.15.6" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ec38bac967f54b11756d3b8b42069f5b30c3ff449972c516683b98340a4e8e" +checksum = "e0f370b9efbfbbc5f057cbce9888373eaeb146a3095bb8cc869b199c94d15559" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", "regex", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -11584,7 +11475,7 @@ dependencies = [ "serde_json", "sqlx", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.12", "time", "tokio", "toml 0.8.23", @@ -11613,11 +11504,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.12", ] [[package]] @@ -11628,18 +11519,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -11651,33 +11542,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "thrift" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" -dependencies = [ - "byteorder", - "integer-encoding", - "log", - "ordered-float", - "threadpool", -] - [[package]] name = "time" -version = "0.3.47" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -11686,22 +11555,22 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde_core", + "serde", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -11709,9 +11578,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -11729,9 +11598,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -11760,43 +11629,46 @@ checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "tokio" -version = "1.49.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ + "backtrace", "bytes", + "io-uring", "libc", - "mio 1.1.1", + "mio 1.0.4", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "slab", + "socket2 0.6.0", "tokio-macros", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "tokio-postgres" -version = "0.7.15" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b40d66d9b2cfe04b628173409368e58247e8eddbbd3b0e6c6ba1d09f20f6c9e" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" dependencies = [ "async-trait", "byteorder", @@ -11807,12 +11679,12 @@ dependencies = [ "log", "parking_lot", "percent-encoding", - "phf 0.13.1", + "phf", "pin-project-lite", "postgres-protocol", "postgres-types", "rand 0.9.2", - "socket2 0.6.1", + "socket2 0.5.10", "tokio", "tokio-util", "whoami", @@ -11841,19 +11713,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.4" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.36", + "rustls 0.23.29", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -11863,10 +11735,12 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6d24790a10a7af737693a3e8f1d03faef7e6ca0cc99aae5066f533766de545" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ + "async-stream", + "bytes", "futures-core", "tokio", "tokio-stream", @@ -11901,14 +11775,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-sink", "futures-util", + "hashbrown 0.15.4", "pin-project-lite", "slab", "tokio", @@ -11935,7 +11810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e04c1865c281139e5ccf633cb9f76ffdaabeebfe53b703984cf82878e2aabb" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -11955,8 +11830,8 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", + "toml_datetime", + "toml_edit", ] [[package]] @@ -11968,50 +11843,20 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.10.0", "serde", "serde_spanned", - "toml_datetime 0.6.11", + "toml_datetime", "toml_write", "winnow", ] -[[package]] -name = "toml_edit" -version = "0.23.10+spec-1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" -dependencies = [ - "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.6+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" -dependencies = [ - "winnow", -] - [[package]] name = "toml_write" version = "0.1.2" @@ -12029,16 +11874,16 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.13", - "http 1.4.0", + "h2 0.4.11", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", - "prost", + "prost 0.13.5", "socket2 0.5.10", "tokio", "tokio-stream", @@ -12057,16 +11902,16 @@ dependencies = [ "async-trait", "base64 0.22.1", "bytes", - "h2 0.4.13", - "http 1.4.0", + "h2 0.4.11", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", - "prost", + "prost 0.13.5", "socket2 0.5.10", "tokio", "tokio-stream", @@ -12076,6 +11921,43 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f32a6f80051a4111560201420c7885d0082ba9efe2ab61875c587bb6b18b9a0" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "sync_wrapper 1.0.2", + "tokio", + "tokio-stream", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f86539c0089bfd09b1f8c0ab0239d80392af74c21bc9e0f15e1b4aca4c1647f" +dependencies = [ + "bytes", + "prost 0.14.3", + "tonic 0.14.4", +] + [[package]] name = "tower" version = "0.4.13" @@ -12104,7 +11986,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "indexmap 2.13.0", + "indexmap 2.10.0", "pin-project-lite", "slab", "sync_wrapper 1.0.2", @@ -12122,11 +12004,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", - "bitflags 2.10.0", + "bitflags 2.9.1", "bytes", "futures-core", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "http-range-header", @@ -12176,7 +12058,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -12189,21 +12071,11 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-indicatif" -version = "0.3.14" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ef6990e0438749f0080573248e96631171a0b5ddfddde119aa5ba8c3a9c47e" +checksum = "8c714cc8fc46db04fcfddbd274c6ef59bebb1b435155984e7c6e89c3ce66f200" dependencies = [ "indicatif", "tracing", @@ -12211,17 +12083,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -12235,16 +12096,18 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.19.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a39dcf9bfc1742fa4d6215253b33a6e474be78275884c216fc2a06267b3600" +checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" dependencies = [ - "once_cell", + "js-sys", "opentelemetry", + "smallvec", "tracing", "tracing-core", - "tracing-log 0.1.4", + "tracing-log", "tracing-subscriber", + "web-time", ] [[package]] @@ -12254,7 +12117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", - "nu-ansi-term 0.50.3", + "nu-ansi-term", "once_cell", "regex-automata", "sharded-slab", @@ -12262,7 +12125,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] @@ -12283,19 +12146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.114", -] - -[[package]] -name = "tracing-tree" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d" -dependencies = [ - "nu-ansi-term 0.46.0", - "tracing-core", - "tracing-log 0.1.4", - "tracing-subscriber", + "syn 2.0.106", ] [[package]] @@ -12340,7 +12191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e640d9b0964e9d39df633548591090ab92f7a4567bc31d3891af23471a3365c6" dependencies = [ "lazy_static", - "thiserror 2.0.17", + "thiserror 2.0.12", "ts-rs-macros", ] @@ -12367,7 +12218,7 @@ checksum = "0e9d8656589772eeec2cf7a8264d9cda40fb28b9bc53118ceb9e8c07f8f38730" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "termcolor", ] @@ -12394,7 +12245,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals 0.28.0", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -12427,7 +12278,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.4.0", + "http 1.3.1", "httparse", "log", "rand 0.8.5", @@ -12441,29 +12292,29 @@ dependencies = [ [[package]] name = "typed-builder" -version = "0.23.2" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda" +checksum = "0d0dd654273fc253fde1df4172c31fb6615cf8b041d3a4008a028ef8b1119e66" dependencies = [ "typed-builder-macro", ] [[package]] name = "typed-builder-macro" -version = "0.23.2" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26" +checksum = "016c26257f448222014296978b2c8456e2cad4de308c35bdb1e383acd569ef5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "typenum" -version = "1.19.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd-trie" @@ -12473,9 +12324,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicase" -version = "2.9.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" @@ -12485,24 +12336,24 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-normalization" -version = "0.1.25" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-segmentation" @@ -12518,9 +12369,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" @@ -12530,117 +12381,117 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uniffi" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3291800a6b06569f7d3e15bdb6dc235e0f0c8bd3eb07177f430057feb076415f" +checksum = "b334fd69b3cf198b63616c096aabf9820ab21ed9b2aa1367ddd4b411068bf520" dependencies = [ "anyhow", "camino", "cargo_metadata 0.19.2", "clap", - "uniffi_bindgen 0.29.5", - "uniffi_build 0.29.5", - "uniffi_core 0.29.5", - "uniffi_macros 0.29.5", - "uniffi_pipeline 0.29.5", + "uniffi_bindgen 0.29.3", + "uniffi_build 0.29.3", + "uniffi_core 0.29.3", + "uniffi_macros 0.29.3", + "uniffi_pipeline 0.29.3", ] [[package]] name = "uniffi" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c866f627c3f04c3df068b68bb2d725492caaa539dd313e2a9d26bb85b1a32f4e" +checksum = "b8c6dec3fc6645f71a16a3fa9ff57991028153bd194ca97f4b55e610c73ce66a" dependencies = [ "anyhow", "camino", "cargo_metadata 0.19.2", "clap", - "uniffi_bindgen 0.30.0", - "uniffi_build 0.30.0", - "uniffi_core 0.30.0", - "uniffi_macros 0.30.0", - "uniffi_pipeline 0.30.0", + "uniffi_bindgen 0.31.0", + "uniffi_build 0.31.0", + "uniffi_core 0.31.0", + "uniffi_macros 0.31.0", + "uniffi_pipeline 0.31.0", ] [[package]] name = "uniffi_bindgen" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04b99fa7796eaaa7b87976a0dbdd1178dc1ee702ea00aca2642003aef9b669e" +checksum = "2ff0132b533483cf19abb30bba5c72c24d9f3e4d9a2ff71cb3e22e73899fd46e" dependencies = [ "anyhow", - "askama", + "askama 0.13.1", "camino", "cargo_metadata 0.19.2", "fs-err", "glob", "goblin", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.10.0", "once_cell", "serde", "tempfile", "textwrap", "toml 0.5.11", - "uniffi_internal_macros 0.29.5", - "uniffi_meta 0.29.5", - "uniffi_pipeline 0.29.5", - "uniffi_udl 0.29.5", + "uniffi_internal_macros 0.29.3", + "uniffi_meta 0.29.3", + "uniffi_pipeline 0.29.3", + "uniffi_udl 0.29.3", ] [[package]] name = "uniffi_bindgen" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c8ca600167641ebe7c8ba9254af40492dda3397c528cc3b2f511bd23e8541a5" +checksum = "4ed0150801958d4825da56a41c71f000a457ac3a4613fa9647df78ac4b6b6881" dependencies = [ "anyhow", - "askama", + "askama 0.14.0", "camino", "cargo_metadata 0.19.2", "fs-err", "glob", "goblin", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.10.0", "once_cell", "serde", "tempfile", "textwrap", "toml 0.8.23", - "uniffi_internal_macros 0.30.0", - "uniffi_meta 0.30.0", - "uniffi_pipeline 0.30.0", - "uniffi_udl 0.30.0", + "uniffi_internal_macros 0.31.0", + "uniffi_meta 0.31.0", + "uniffi_pipeline 0.31.0", + "uniffi_udl 0.31.0", ] [[package]] name = "uniffi_build" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025a05cba02ee22b6624ac3d257e59c7395319ea8fe1aae33a7cdb4e2a3016cc" +checksum = "0d84d607076008df3c32dd2100ee4e727269f11d3faa35691af70d144598f666" dependencies = [ "anyhow", "camino", - "uniffi_bindgen 0.29.5", + "uniffi_bindgen 0.29.3", ] [[package]] name = "uniffi_build" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e55c05228f4858bb258f651d21d743fcc1fe5a2ec20d3c0f9daefddb105ee4d" +checksum = "b78fd9271a4c2e85bd2c266c5a9ede1fac676eb39fd77f636c27eaf67426fd5f" dependencies = [ "anyhow", "camino", - "uniffi_bindgen 0.30.0", + "uniffi_bindgen 0.31.0", ] [[package]] name = "uniffi_core" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38a9a27529ccff732f8efddb831b65b1e07f7dea3fd4cacd4a35a8c4b253b98" +checksum = "53e3b997192dc15ef1778c842001811ec7f241a093a693ac864e1fc938e64fa9" dependencies = [ "anyhow", "bytes", @@ -12650,9 +12501,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7a5a038ebffe8f4cf91416b154ef3c2468b18e828b7009e01b1b99938089f9" +checksum = "b0ef62e69762fbb9386dcb6c87cd3dd05d525fa8a3a579a290892e60ddbda47e" dependencies = [ "anyhow", "bytes", @@ -12662,35 +12513,35 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09acd2ce09c777dd65ee97c251d33c8a972afc04873f1e3b21eb3492ade16933" +checksum = "f64bec2f3a33f2f08df8150e67fa45ba59a2ca740bf20c1beb010d4d791f9a1b" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.10.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "uniffi_internal_macros" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c2a6f93e7b73726e2015696ece25ca0ac5a5f1cf8d6a7ab5214dd0a01d2edf" +checksum = "98f51ebca0d9a4b2aa6c644d5ede45c56f73906b96403c08a1985e75ccb64a01" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.10.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "uniffi_macros" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5596f178c4f7aafa1a501c4e0b96236a96bc2ef92bdb453d83e609dad0040152" +checksum = "5d8708716d2582e4f3d7e9f320290b5966eb951ca421d7630571183615453efc" dependencies = [ "camino", "fs-err", @@ -12698,16 +12549,16 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.114", + "syn 2.0.106", "toml 0.5.11", - "uniffi_meta 0.29.5", + "uniffi_meta 0.29.3", ] [[package]] name = "uniffi_macros" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c6309fc36c7992afc03bc0c5b059c656bccbef3f2a4bc362980017f8936141" +checksum = "db9d12529f1223d014fd501e5f29ca0884d15d6ed5ddddd9f506e55350327dc3" dependencies = [ "camino", "fs-err", @@ -12715,90 +12566,90 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.114", + "syn 2.0.106", "toml 0.8.23", - "uniffi_meta 0.30.0", + "uniffi_meta 0.31.0", ] [[package]] name = "uniffi_meta" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beadc1f460eb2e209263c49c4f5b19e9a02e00a3b2b393f78ad10d766346ecff" +checksum = "3d226fc167754ce548c5ece9828c8a06f03bf1eea525d2659ba6bd648bd8e2f3" dependencies = [ "anyhow", "siphasher 0.3.11", - "uniffi_internal_macros 0.29.5", - "uniffi_pipeline 0.29.5", + "uniffi_internal_macros 0.29.3", + "uniffi_pipeline 0.29.3", ] [[package]] name = "uniffi_meta" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a138823392dba19b0aa494872689f97d0ee157de5852e2bec157ce6de9cdc22" +checksum = "9df6d413db2827c68588f8149d30d49b71d540d46539e435b23a7f7dbd4d4f86" dependencies = [ "anyhow", - "siphasher 0.3.11", - "uniffi_internal_macros 0.30.0", - "uniffi_pipeline 0.30.0", + "siphasher 1.0.1", + "uniffi_internal_macros 0.31.0", + "uniffi_pipeline 0.31.0", ] [[package]] name = "uniffi_pipeline" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd76b3ac8a2d964ca9fce7df21c755afb4c77b054a85ad7a029ad179cc5abb8a" +checksum = "b925b6421df15cf4bedee27714022cd9626fb4d7eee0923522a608b274ba4371" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.10.0", "tempfile", - "uniffi_internal_macros 0.29.5", + "uniffi_internal_macros 0.29.3", ] [[package]] name = "uniffi_pipeline" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c27c4b515d25f8e53cc918e238c39a79c3144a40eaf2e51c4a7958973422c29" +checksum = "a806dddc8208f22efd7e95a5cdf88ed43d0f3271e8f63b47e757a8bbdb43b63a" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.10.0", "tempfile", - "uniffi_internal_macros 0.30.0", + "uniffi_internal_macros 0.31.0", ] [[package]] name = "uniffi_udl" -version = "0.29.5" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319cf905911d70d5b97ce0f46f101619a22e9a189c8c46d797a9955e9233716" +checksum = "9c42649b721df759d9d4692a376b82b62ce3028ec9fc466f4780fb8cdf728996" dependencies = [ "anyhow", "textwrap", - "uniffi_meta 0.29.5", + "uniffi_meta 0.29.3", "weedle2", ] [[package]] name = "uniffi_udl" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0adacdd848aeed7af4f5af7d2f621d5e82531325d405e29463482becfdeafca" +checksum = "0d1a7339539bf6f6fa3e9b534dece13f778bda2d54b1a6d4e40b4d6090ac26e7" dependencies = [ "anyhow", "textwrap", - "uniffi_meta 0.30.0", + "uniffi_meta 0.31.0", "weedle2", ] [[package]] name = "unit-prefix" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" [[package]] name = "universal-hash" @@ -12806,7 +12657,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "subtle 2.6.1", ] @@ -12824,23 +12675,16 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.8" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", - "serde_derive", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf-8" version = "0.7.6" @@ -12865,7 +12709,7 @@ version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.10.0", "serde", "serde_json", "utoipa-gen", @@ -12880,7 +12724,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.114", + "syn 2.0.106", "uuid", ] @@ -12919,7 +12763,7 @@ checksum = "268d76aaebb80eba79240b805972e52d7d410d4bcc52321b951318b0f440cd60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -12930,17 +12774,17 @@ checksum = "382673bda1d05c85b4550d32fd4192ccd4cffe9a908543a0795d1e7682b36246" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "utoipauto-core", ] [[package]] name = "uuid" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "serde_core", "wasm-bindgen", @@ -13041,30 +12885,36 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vt100" -version = "0.16.2" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ff75fb8fa83e609e685106df4faeffdf3a735d3c74ebce97ec557d5d36fd9" +checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de" dependencies = [ "itoa", - "unicode-width 0.2.2", + "log", + "unicode-width 0.1.14", "vte", ] [[package]] name = "vte" -version = "0.15.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" dependencies = [ "arrayvec", - "memchr", + "utf8parse", + "vte_generate_state_changes", ] [[package]] -name = "waker-fn" -version = "1.2.0" +name = "vte_generate_state_changes" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" +dependencies = [ + "proc-macro2", + "quote", +] [[package]] name = "walkdir" @@ -13092,10 +12942,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" +name = "wasi" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] @@ -13112,27 +12980,40 @@ version = "0.12.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" dependencies = [ - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -13143,9 +13024,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -13153,42 +13034,34 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ - "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", + "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-test" -version = "0.3.56" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e90e66d265d3a1efc0e72a54809ab90b9c0c515915c67cdf658689d2c22c6c" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "async-trait", - "cast", "js-sys", - "libm", "minicov", - "nu-ansi-term 0.50.3", - "num-traits", - "oorandom", - "serde", - "serde_json", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -13196,13 +13069,35 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.56" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7150335716dce6028bead2b848e72f47b45e7b9422f64cccdc23bedca89affc1" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.10.0", + "wasm-encoder", + "wasmparser", ] [[package]] @@ -13219,10 +13114,22 @@ dependencies = [ ] [[package]] -name = "wasmtimer" -version = "0.4.3" +name = "wasmparser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.9.1", + "hashbrown 0.15.4", + "indexmap 2.10.0", + "semver 1.0.26", +] + +[[package]] +name = "wasmtimer" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" dependencies = [ "futures", "js-sys", @@ -13234,9 +13141,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -13258,7 +13165,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" dependencies = [ - "phf 0.11.3", + "phf", "phf_codegen", "string_cache", "string_cache_codegen", @@ -13266,9 +13173,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] @@ -13294,14 +13201,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.5", + "webpki-roots 1.0.2", ] [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -13317,11 +13224,11 @@ dependencies = [ [[package]] name = "whoami" -version = "1.6.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "libredox", + "redox_syscall", "wasite", "web-sys", ] @@ -13334,9 +13241,9 @@ checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" [[package]] name = "widestring" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi" @@ -13356,11 +13263,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.11" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -13468,7 +13375,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13479,7 +13386,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13592,16 +13499,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", + "windows-targets 0.53.2", ] [[package]] @@ -13652,19 +13550,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.5" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ - "windows-link 0.2.1", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -13705,9 +13602,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" @@ -13729,9 +13626,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" @@ -13753,9 +13650,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[package]] name = "windows_i686_gnullvm" @@ -13765,9 +13662,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" @@ -13789,9 +13686,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" @@ -13813,9 +13710,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" @@ -13837,9 +13734,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" @@ -13861,15 +13758,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -13890,7 +13787,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22b4dbcc6c93786cf22e420ef96e8976bfb92a455070282302b74de5848191f4" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "getrandom 0.2.16", "ipnet", "libloading", @@ -13902,15 +13799,106 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.10.0", + "prettyplease", + "syn 2.0.106", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.106", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.9.1", + "indexmap 2.10.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.10.0", + "log", + "semver 1.0.26", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wyz" @@ -13935,12 +13923,12 @@ dependencies = [ [[package]] name = "xattr" -version = "1.6.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" dependencies = [ "libc", - "rustix", + "rustix 1.0.8", ] [[package]] @@ -13951,10 +13939,11 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ + "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -13962,34 +13951,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -14009,35 +13998,35 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", "yoke", @@ -14046,9 +14035,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -14057,13 +14046,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -14077,9 +14066,9 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.13.0", + "indexmap 2.10.0", "memchr", - "thiserror 2.0.17", + "thiserror 2.0.12", "zopfli", ] @@ -14101,7 +14090,7 @@ dependencies = [ "rand 0.8.5", "reqwest 0.13.1", "serde", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tsify", "uuid", @@ -14110,17 +14099,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "zmij" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" - [[package]] name = "zopfli" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ "bumpalo", "crc32fast", @@ -14148,9 +14131,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", @@ -14166,7 +14149,7 @@ dependencies = [ "reqwest 0.13.1", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.12", "tokio", "tracing", "url", diff --git a/Cargo.toml b/Cargo.toml index 5d4ba68b35..89f1add4c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -309,8 +309,9 @@ nix = "0.30.1" notify = "5.1.0" num_enum = "0.7.5" once_cell = "1.21.3" -opentelemetry = "0.19.0" -opentelemetry-jaeger = "0.18.0" +opentelemetry = "0.31.0" +opentelemetry_sdk = "0.31.0" +opentelemetry-otlp = "0.31.0" parking_lot = "0.12.3" pem = "0.8" petgraph = "0.6.5" @@ -368,9 +369,8 @@ tower = "0.5.2" tower-http = "0.6.6" tracing = "0.1.41" tracing-log = "0.2" -tracing-opentelemetry = "0.19.0" +tracing-opentelemetry = "0.32.1" tracing-subscriber = "0.3.20" -tracing-tree = "0.2.2" tracing-indicatif = "0.3.9" tracing-test = "0.2.5" ts-rs = "10.1.0" diff --git a/common/bin-common/Cargo.toml b/common/bin-common/Cargo.toml index 8220494794..3a69311369 100644 --- a/common/bin-common/Cargo.toml +++ b/common/bin-common/Cargo.toml @@ -19,12 +19,14 @@ serde_json = { workspace = true, optional = true } ## tracing tracing-subscriber = { workspace = true, features = ["env-filter"], optional = true } -tracing-tree = { workspace = true, optional = true } tracing = { workspace = true, optional = true } -opentelemetry-jaeger = { workspace = true, features = ["rt-tokio", "collector_client", "isahc_collector_client"], optional = true } tracing-opentelemetry = { workspace = true, optional = true } utoipa = { workspace = true, optional = true } -opentelemetry = { workspace = true, features = ["rt-tokio"], optional = true } +opentelemetry = { workspace = true, features = ["trace"], optional = true } + +## otel-otlp (modern OTLP export to SigNoz/any OTLP collector) +opentelemetry_sdk = { workspace = true, features = ["trace"], optional = true } +opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "trace"], optional = true } [build-dependencies] @@ -35,13 +37,13 @@ default = [] openapi = ["utoipa"] output_format = ["serde_json", "dep:clap"] bin_info_schema = ["schemars"] -basic_tracing = ["dep:tracing", "tracing-subscriber"] -tracing = [ +basic_tracing = ["dep:tracing", "dep:tracing-subscriber"] +otel-otlp = [ "basic_tracing", - "tracing-tree", - "opentelemetry-jaeger", - "tracing-opentelemetry", - "opentelemetry", + "dep:opentelemetry", + "dep:opentelemetry_sdk", + "dep:opentelemetry-otlp", + "dep:tracing-opentelemetry", ] clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"] models = [] diff --git a/common/bin-common/src/logging/mod.rs b/common/bin-common/src/logging/mod.rs index 20d2e9e274..9b13d32628 100644 --- a/common/bin-common/src/logging/mod.rs +++ b/common/bin-common/src/logging/mod.rs @@ -4,16 +4,9 @@ use serde::{Deserialize, Serialize}; use std::io::IsTerminal; -#[cfg(feature = "tracing")] -pub use opentelemetry; -#[cfg(feature = "tracing")] -pub use opentelemetry_jaeger; -#[cfg(feature = "tracing")] -pub use tracing_opentelemetry; +// Re-export tracing_subscriber for consumers that need to compose layers #[cfg(feature = "basic_tracing")] pub use tracing_subscriber; -#[cfg(feature = "tracing")] -pub use tracing_tree; #[derive(Debug, Default, Copy, Clone, Deserialize, PartialEq, Eq, Serialize)] #[serde(deny_unknown_fields)] @@ -69,40 +62,55 @@ pub fn setup_tracing_logger() { build_tracing_logger().init() } -// TODO: This has to be a macro, running it as a function does not work for the file_appender for some reason -#[cfg(feature = "tracing")] -#[macro_export] -macro_rules! setup_tracing { - ($service_name: expr) => { - use nym_bin_common::logging::tracing_subscriber::layer::SubscriberExt; - use nym_bin_common::logging::tracing_subscriber::util::SubscriberInitExt; +/// Initialize an OpenTelemetry tracing layer that exports spans via OTLP/gRPC. +/// +/// This produces a layer compatible with `tracing_subscriber::registry()` that +/// sends traces to any OTLP-compatible collector (SigNoz, Grafana Tempo, etc). +/// +/// Returns both the tracing layer and the [`SdkTracerProvider`] so the caller +/// can invoke [`SdkTracerProvider::shutdown`] for graceful flush on exit. +/// +/// # Arguments +/// * `service_name` - The service name reported to the collector (e.g. "nym-node") +/// * `endpoint` - The OTLP/gRPC collector endpoint (e.g. "http://localhost:4317") +#[cfg(feature = "otel-otlp")] +pub fn init_otel_layer( + service_name: &str, + endpoint: &str, +) -> Result< + ( + tracing_opentelemetry::OpenTelemetryLayer, + opentelemetry_sdk::trace::SdkTracerProvider, + ), + Box, +> +where + S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>, +{ + use opentelemetry::trace::TracerProvider as _; + use opentelemetry_otlp::WithExportConfig; - let registry = nym_bin_common::logging::tracing_subscriber::Registry::default() - .with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env()) - .with( - nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4) - .with_targets(true) - .with_bracketed_fields(true), - ); + let exporter = opentelemetry_otlp::SpanExporter::builder() + .with_tonic() + .with_endpoint(endpoint) + .build()?; - let tracer = nym_bin_common::logging::opentelemetry_jaeger::new_collector_pipeline() - .with_endpoint("http://44.199.230.10:14268/api/traces") - .with_service_name($service_name) - .with_isahc() - .with_trace_config( - nym_bin_common::logging::opentelemetry::sdk::trace::config().with_sampler( - nym_bin_common::logging::opentelemetry::sdk::trace::Sampler::TraceIdRatioBased( - 0.1, - ), - ), - ) - .install_batch(nym_bin_common::logging::opentelemetry::runtime::Tokio) - .expect("Could not init tracer"); + let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() + .with_batch_exporter(exporter) + .with_resource( + opentelemetry_sdk::Resource::builder() + .with_service_name(service_name.to_owned()) + .build(), + ) + .build(); - let telemetry = nym_bin_common::logging::tracing_opentelemetry::layer().with_tracer(tracer); + opentelemetry::global::set_tracer_provider(tracer_provider.clone()); + let tracer = tracer_provider.tracer(service_name.to_owned()); - registry.with(telemetry).init(); - }; + Ok(( + tracing_opentelemetry::layer().with_tracer(tracer), + tracer_provider, + )) } pub fn banner(crate_name: &str, crate_version: &str) -> String { diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index 5f001810bd..c7136c5ed9 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -128,54 +128,95 @@ impl ManagedConnection { async fn run(self) { let address = self.address; + let reconnection_attempt = self.current_reconnection.load(Ordering::Acquire); + let connect_start = tokio::time::Instant::now(); let connection_fut = TcpStream::connect(address); let conn = match tokio::time::timeout(self.connection_timeout, connection_fut).await { Ok(stream_res) => match stream_res { Ok(stream) => { - debug!("Managed to establish connection to {}", self.address); + let connect_ms = connect_start.elapsed().as_millis() as u64; + debug!( + peer = %address, + connect_ms, + "Managed to establish connection to {}", self.address + ); + let noise_start = tokio::time::Instant::now(); let noise_stream = match upgrade_noise_initiator(stream, &self.noise_config).await { Ok(noise_stream) => noise_stream, Err(err) => { - error!("Failed to perform Noise handshake with {address} - {err}"); - // we failed to finish the noise handshake - increase reconnection attempt + let noise_handshake_ms = noise_start.elapsed().as_millis() as u64; + warn!( + event = "connection.failed.noise", + peer = %address, + error = %err, + connect_ms, + noise_handshake_ms, + reconnection_attempt, + exit_reason = "noise_error", + "Failed to perform Noise initiator handshake with {address}" + ); self.current_reconnection.fetch_add(1, Ordering::SeqCst); return; } }; - // if we managed to connect AND do the noise handshake, reset the reconnection count (whatever it might have been) + let noise_handshake_ms = noise_start.elapsed().as_millis() as u64; self.current_reconnection.store(0, Ordering::Release); - debug!("Noise initiator handshake completed for {:?}", address); + debug!( + peer = %address, + connect_ms, + noise_handshake_ms, + "Noise initiator handshake completed for {:?}", address + ); Framed::new(noise_stream, NymCodec) } Err(err) => { - debug!("failed to establish connection to {address} (err: {err})",); + let connect_ms = connect_start.elapsed().as_millis() as u64; + warn!( + event = "connection.failed.connect", + peer = %address, + error = %err, + connect_ms, + reconnection_attempt, + exit_reason = "connect_error", + "failed to establish connection to {address}" + ); return; } }, Err(_) => { - debug!( + let connect_ms = connect_start.elapsed().as_millis() as u64; + warn!( + event = "connection.failed.timeout", + peer = %address, + timeout_ms = self.connection_timeout.as_millis() as u64, + connect_ms, + reconnection_attempt, + exit_reason = "timeout", "failed to connect to {address} within {:?}", self.connection_timeout ); - - // we failed to connect - increase reconnection attempt self.current_reconnection.fetch_add(1, Ordering::SeqCst); return; } }; - // Take whatever the receiver channel produces and put it on the connection. - // We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care - // about neither receiver nor the connection, it doesn't matter which one gets consumed if let Err(err) = self.message_receiver.map(Ok).forward(conn).await { - warn!("Failed to forward packets to {address}: {err}"); + warn!( + event = "connection.forward_error", + peer = %address, + error = %err, + exit_reason = "forward_error", + "Failed to forward packets to {address}: {err}" + ); } debug!( - "connection manager to {address} is finished. Either the connection failed or mixnet client got dropped", + peer = %address, + exit_reason = "sender_dropped", + "connection manager to {address} finished" ); } } @@ -271,17 +312,16 @@ impl SendWithoutResponse for Client { let address = packet.next_hop_address(); trace!("Sending packet to {address}"); - // TODO: optimisation for the future: rather than constantly using legacy encoding, - // once we're addressing by node_id (and thus have full node info here), - // we could simply infer supported encoding based on their version let framed_packet = FramedNymPacket::from_mix_packet(packet, self.config.use_legacy_packet_encoding); let Some(sender) = self.active_connections.get_mut(&address) else { - // there was never a connection to begin with - debug!("establishing initial connection to {address}"); - // it's not a 'big' error, but we did not manage to send the packet, but queue the packet - // for sending for as soon as the connection is created + debug!( + event = "mixclient.try_send", + peer = %address, + result = "not_connected", + "establishing initial connection to {address}" + ); self.make_connection(address, framed_packet); return Err(io::Error::new( io::ErrorKind::NotConnected, @@ -289,15 +329,24 @@ impl SendWithoutResponse for Client { )); }; + let channel_capacity = sender.channel.max_capacity(); + let channel_available = sender.channel.capacity(); + let channel_used = channel_capacity - channel_available; + let sending_res = sender.channel.try_send(framed_packet); drop(sender); sending_res.map_err(|err| { match err { TrySendError::Full(_) => { - debug!("Connection to {address} seems to not be able to handle all the traffic - dropping the current packet"); - // it's not a 'big' error, but we did not manage to send the packet - // if the queue is full, we can't really do anything but to drop the packet + warn!( + event = "mixclient.try_send", + peer = %address, + result = "full_dropped", + channel_capacity, + channel_used, + "dropping packet: connection buffer to {address} is full ({channel_used}/{channel_capacity})" + ); io::Error::new( io::ErrorKind::WouldBlock, "connection queue is full", @@ -305,11 +354,13 @@ impl SendWithoutResponse for Client { } TrySendError::Closed(dropped) => { debug!( - "Connection to {address} seems to be dead. attempting to re-establish it...", + event = "mixclient.try_send", + peer = %address, + result = "closed_reconnecting", + channel_capacity, + channel_used, + "connection to {address} dead, attempting re-establishment" ); - - // it's not a 'big' error, but we did not manage to send the packet, but queue - // it up to send it as soon as the connection is re-established self.make_connection(address, dropped); io::Error::new( io::ErrorKind::ConnectionAborted, diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index d2dd9f0951..b2b5389569 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -40,6 +40,8 @@ thiserror.workspace = true tracing.workspace = true tracing-indicatif = { workspace = true } tracing-subscriber.workspace = true +opentelemetry = { workspace = true, features = ["trace"], optional = true } +opentelemetry_sdk = { workspace = true, features = ["trace"], optional = true } tokio = { workspace = true, features = ["macros", "sync", "rt-multi-thread"] } tokio-util = { workspace = true, features = ["codec"] } tokio-stream = { workspace = true } @@ -135,6 +137,7 @@ rand_chacha = { workspace = true } [features] tokio-console = ["console-subscriber", "nym-task/tokio-tracing"] +otel = ["nym-bin-common/otel-otlp", "dep:opentelemetry", "dep:opentelemetry_sdk"] [lints] workspace = true diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index ad72438f41..f3a144d46e 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -40,6 +40,20 @@ pub(crate) struct Cli { )] pub(crate) no_banner: bool, + /// Enable OpenTelemetry tracing export via OTLP/gRPC. + /// Requires the binary to be built with the `otel` feature. + #[clap(long, env = "NYMNODE_OTEL_ENABLE")] + pub(crate) otel: bool, + + /// OpenTelemetry OTLP collector endpoint (gRPC). + /// Only used when --otel is enabled. + #[clap( + long, + env = "NYMNODE_OTEL_ENDPOINT", + default_value = "http://localhost:4317" + )] + pub(crate) otel_endpoint: String, + #[clap(subcommand)] command: Commands, } @@ -54,30 +68,68 @@ impl Cli { pub(crate) fn execute(self) -> anyhow::Result<()> { // NOTE: `test_throughput` sets up its own logger as it has to include additional layers + #[cfg(feature = "otel")] + let _otel_guard = if !matches!(self.command, Commands::TestThroughput(..)) { + let otel_config = if self.otel { + Some(crate::logging::OtelConfig { + endpoint: self.otel_endpoint.clone(), + service_name: "nym-node".to_string(), + }) + } else { + None + }; + crate::logging::setup_tracing_logger(otel_config)? + } else { + None + }; + + #[cfg(not(feature = "otel"))] if !matches!(self.command, Commands::TestThroughput(..)) { - crate::logging::setup_tracing_logger()?; + let otel_config = if self.otel { + Some(crate::logging::OtelConfig { + endpoint: self.otel_endpoint.clone(), + service_name: "nym-node".to_string(), + }) + } else { + None + }; + crate::logging::setup_tracing_logger(otel_config)?; } - match self.command { - Commands::BuildInfo(args) => build_info::execute(args)?, - Commands::BondingInformation(args) => { - { Self::execute_async(bonding_information::execute(args))? }? - } - Commands::NodeDetails(args) => { Self::execute_async(node_details::execute(args))? }?, - Commands::Run(args) => { Self::execute_async(run::execute(*args))? }?, - Commands::Migrate(args) => migrate::execute(*args)?, - Commands::Sign(args) => { Self::execute_async(sign::execute(args))? }?, - Commands::TestThroughput(args) => test_throughput::execute(args)?, - Commands::UnsafeResetSphinxKeys(args) => { - { Self::execute_async(reset_sphinx_keys::execute(args))? }? - } - Commands::Debug(debug) => match debug.command { - DebugCommands::ResetProvidersGatewayDbs(args) => { - { Self::execute_async(debug::reset_providers_dbs::execute(args))? }? + let result = (|| -> anyhow::Result<()> { + match self.command { + Commands::BuildInfo(args) => build_info::execute(args)?, + Commands::BondingInformation(args) => { + { Self::execute_async(bonding_information::execute(args))? }? } - }, + Commands::NodeDetails(args) => { + { Self::execute_async(node_details::execute(args))? }? + } + Commands::Run(args) => { Self::execute_async(run::execute(*args))? }?, + Commands::Migrate(args) => migrate::execute(*args)?, + Commands::Sign(args) => { Self::execute_async(sign::execute(args))? }?, + Commands::TestThroughput(args) => test_throughput::execute(args)?, + Commands::UnsafeResetSphinxKeys(args) => { + { Self::execute_async(reset_sphinx_keys::execute(args))? }? + } + Commands::Debug(debug) => match debug.command { + DebugCommands::ResetProvidersGatewayDbs(args) => { + { Self::execute_async(debug::reset_providers_dbs::execute(args))? }? + } + }, + } + Ok(()) + })(); + + // Flush any pending OTel spans before exit + #[cfg(feature = "otel")] + if let Some(guard) = _otel_guard { + if let Err(e) = guard.provider.shutdown() { + eprintln!("OpenTelemetry shutdown error: {e}"); + } } - Ok(()) + + result } } diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index e1812059be..f35ac8e2a9 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -7,6 +7,20 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::{EnvFilter, Layer}; +/// Configuration for OpenTelemetry OTLP export. +/// Fields are only read when the `otel` feature is enabled. +#[allow(dead_code)] +pub(crate) struct OtelConfig { + pub endpoint: String, + pub service_name: String, +} + +/// Handle returned when OTel is active so the caller can trigger a graceful shutdown. +#[cfg(feature = "otel")] +pub(crate) struct OtelGuard { + pub provider: opentelemetry_sdk::trace::SdkTracerProvider, +} + pub(crate) fn granual_filtered_env() -> anyhow::Result { fn directive_checked(directive: impl Into) -> anyhow::Result { directive.into().parse().map_err(From::from) @@ -22,12 +36,73 @@ pub(crate) fn granual_filtered_env() -> anyhow::Result { Ok(filter) } -pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> { +/// Initialise the tracing subscriber stack. +/// +/// When the `otel` feature is enabled **and** an `OtelConfig` is supplied, an +/// OTLP exporter layer is added and the returned `OtelGuard` must be used to +/// flush pending spans on shutdown. +#[cfg(feature = "otel")] +pub(crate) fn setup_tracing_logger( + otel: Option, +) -> anyhow::Result> { + let stderr_layer = + default_tracing_fmt_layer(std::io::stderr).with_filter(granual_filtered_env()?); + + cfg_if::cfg_if! {if #[cfg(feature = "tokio-console")] { + let console_layer = console_subscriber::spawn(); + + if let Some(otel_config) = otel { + let (otel_layer, provider) = nym_bin_common::logging::init_otel_layer( + &otel_config.service_name, + &otel_config.endpoint, + ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; + + tracing_subscriber::registry() + .with(console_layer) + .with(stderr_layer) + .with(otel_layer) + .init(); + + Ok(Some(OtelGuard { provider })) + } else { + tracing_subscriber::registry() + .with(console_layer) + .with(stderr_layer) + .init(); + + Ok(None) + } + } else { + if let Some(otel_config) = otel { + let (otel_layer, provider) = nym_bin_common::logging::init_otel_layer( + &otel_config.service_name, + &otel_config.endpoint, + ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; + + tracing_subscriber::registry() + .with(stderr_layer) + .with(otel_layer) + .init(); + + Ok(Some(OtelGuard { provider })) + } else { + tracing_subscriber::registry() + .with(stderr_layer) + .init(); + + Ok(None) + } + }} +} + +/// Non-OTel variant -- identical subscriber stack without the OTLP layer. +#[cfg(not(feature = "otel"))] +pub(crate) fn setup_tracing_logger(otel: Option) -> anyhow::Result<()> { + let _ = otel; let stderr_layer = default_tracing_fmt_layer(std::io::stderr).with_filter(granual_filtered_env()?); cfg_if::cfg_if! {if #[cfg(feature = "tokio-console")] { - // instrument tokio console subscriber needs RUSTFLAGS="--cfg tokio_unstable" at build time let console_layer = console_subscriber::spawn(); tracing_subscriber::registry() diff --git a/nym-node/src/node/mixnet/handler.rs b/nym-node/src/node/mixnet/handler.rs index a2a50aa4f2..ae0793cebd 100644 --- a/nym-node/src/node/mixnet/handler.rs +++ b/nym-node/src/node/mixnet/handler.rs @@ -20,7 +20,7 @@ use std::net::SocketAddr; use tokio::net::TcpStream; use tokio::time::Instant; use tokio_util::codec::Framed; -use tracing::{debug, error, instrument, trace, warn}; +use tracing::{debug, error, instrument, trace, warn, Span}; struct PendingReplayCheckPackets { // map of rotation id used for packet creation to the packets @@ -51,6 +51,10 @@ impl PendingReplayCheckPackets { .push(packet.packet) } + fn total_count(&self) -> usize { + self.packets.values().map(|v| v.len()).sum() + } + fn replay_tags(&self) -> HashMap> { let mut replay_tags = HashMap::with_capacity(self.packets.len()); 'outer: for (rotation_id, packets) in &self.packets { @@ -130,20 +134,54 @@ impl ConnectionHandler { Some(now + delay) } + #[instrument( + name = "mixnode.forward_packet", + skip(self, mix_packet, delay), + level = "debug", + fields( + remote_addr = %self.remote_address, + delay_ms, + ) + )] fn handle_forward_packet(&self, now: Instant, mix_packet: MixPacket, delay: Option) { if !self.shared.processing_config.forward_hop_processing_enabled { - trace!("this nym-node does not support forward hop packets"); + warn!( + event = "packet.dropped.forward_disabled", + remote_addr = %self.remote_address, + "dropping packet: forward hop processing disabled" + ); self.shared.dropped_forward_packet(self.remote_address.ip()); return; } let forward_instant = self.create_delay_target(now, delay); + Span::current().record( + "delay_ms", + forward_instant + .map(|i| i.saturating_duration_since(now).as_millis() as u64) + .unwrap_or(0), + ); self.shared.forward_mix_packet(mix_packet, forward_instant); } + #[instrument( + name = "mixnode.final_hop", + skip(self, final_hop_data), + level = "debug", + fields( + remote_addr = %self.remote_address, + client_online, + disk_fallback = false, + ack_forwarded = false, + ) + )] async fn handle_final_hop(&self, final_hop_data: ProcessedFinalHop) { if !self.shared.processing_config.final_hop_processing_enabled { - trace!("this nym-node does not support final hop packets"); + warn!( + event = "packet.dropped.final_hop_disabled", + remote_addr = %self.remote_address, + "dropping packet: final hop processing disabled" + ); self.shared .dropped_final_hop_packet(self.remote_address.ip()); return; @@ -151,11 +189,11 @@ impl ConnectionHandler { let client = final_hop_data.destination; let message = final_hop_data.message; + let has_ack = final_hop_data.forward_ack.is_some(); - // if possible attempt to push message directly to the client match self.shared.try_push_message_to_client(client, message) { Err(unsent_plaintext) => { - // if that failed, store it on disk (to be 🔥 soon...) + Span::current().record("client_online", false); match self .shared .store_processed_packet_payload(client, unsent_plaintext) @@ -163,6 +201,7 @@ impl ConnectionHandler { { Err(err) => error!("Failed to store client data - {err}"), Ok(_) => { + Span::current().record("disk_fallback", true); self.shared .metrics .mixnet @@ -172,13 +211,16 @@ impl ConnectionHandler { } } } - Ok(_) => trace!("Pushed received packet to {client}"), + Ok(_) => { + Span::current().record("client_online", true); + trace!("Pushed received packet to {client}"); + } } - // if we managed to either push message directly to the [online] client or store it at - // its inbox, it means that it must exist at this gateway, hence we can send the - // received ack back into the network self.shared.forward_ack_packet(final_hop_data.forward_ack); + if has_ack { + Span::current().record("ack_forwarded", true); + } } fn within_deferral_threshold(&self, now: Instant) -> bool { @@ -206,32 +248,56 @@ impl ConnectionHandler { if !time_threshold { warn!( - "{}: time failure - {}", + event = "replay_detection.deferral_exceeded", + threshold_type = "time", + deferred_count = self.pending_packets.total_count(), + deferral_ms = now.saturating_duration_since(self.pending_packets.last_acquired_mutex).as_millis() as u64, + remote_addr = %self.remote_address, + "{}: time deferral threshold exceeded with {} pending packets", self.remote_address, - self.pending_packets.packets.len() + self.pending_packets.total_count() ) } if !count_threshold { - warn!("{}, count failure", self.remote_address) + warn!( + event = "replay_detection.deferral_exceeded", + threshold_type = "count", + deferred_count = self.pending_packets.total_count(), + remote_addr = %self.remote_address, + "{}: count deferral threshold exceeded", + self.remote_address + ) } time_threshold && count_threshold } + #[instrument( + name = "mixnode.sphinx_partial_unwrap", + skip(self, packet), + level = "debug", + fields( + key_rotation, + unwrap_result, + ) + )] fn try_partially_unwrap_packet( &self, packet: FramedNymPacket, ) -> Result { - // based on the received sphinx key rotation information, - // attempt to choose appropriate key for processing the packet - match packet.header().key_rotation { + let rotation_label = match packet.header().key_rotation { + SphinxKeyRotation::Unknown => "unknown", + SphinxKeyRotation::OddRotation => "odd", + SphinxKeyRotation::EvenRotation => "even", + }; + Span::current().record("key_rotation", rotation_label); + + let result = match packet.header().key_rotation { SphinxKeyRotation::Unknown => { let primary = self.shared.sphinx_keys.primary(); let primary_rotation = primary.rotation_id(); - // we have to try both keys, start with the primary as it has higher likelihood of being correct - // if let Ok(partially_unwrapped) = PartiallyUnwrappedPacket::new() match PartiallyUnwrappedPacket::new(packet, primary.inner().as_ref()) { Ok(unwrapped_packet) => { Ok(unwrapped_packet.with_key_rotation(primary_rotation)) @@ -250,6 +316,12 @@ impl ConnectionHandler { } SphinxKeyRotation::OddRotation => { let Some(odd_key) = self.shared.sphinx_keys.odd() else { + warn!( + event = "packet.dropped.expired_key", + key_rotation = "odd", + remote_addr = %self.remote_address, + "dropping packet: odd key rotation expired" + ); return Err(PacketProcessingError::ExpiredKey); }; let odd_rotation = odd_key.rotation_id(); @@ -259,6 +331,12 @@ impl ConnectionHandler { } SphinxKeyRotation::EvenRotation => { let Some(even_key) = self.shared.sphinx_keys.even() else { + warn!( + event = "packet.dropped.expired_key", + key_rotation = "even", + remote_addr = %self.remote_address, + "dropping packet: even key rotation expired" + ); return Err(PacketProcessingError::ExpiredKey); }; let even_rotation = even_key.rotation_id(); @@ -266,7 +344,10 @@ impl ConnectionHandler { .map_err(|(_, err)| err) .map(|p| p.with_key_rotation(even_rotation)) } - } + }; + + Span::current().record("unwrap_result", if result.is_ok() { "ok" } else { "err" }); + result } async fn handle_received_packet_with_replay_detection( @@ -280,6 +361,12 @@ impl ConnectionHandler { Ok(unwrapped) => unwrapped, Err(err) => { trace!("failed to process received mix packet: {err}"); + warn!( + event = "packet.dropped.malformed", + error = %err, + remote_addr = %self.remote_address, + "dropping malformed packet" + ); self.shared .metrics .mixnet @@ -316,7 +403,9 @@ impl ConnectionHandler { // 3. forward the packet to the relevant sink (if enabled) match unwrapped_packet { - Err(err) => trace!("failed to process received mix packet: {err}"), + Err(err) => { + trace!("failed to process received mix packet: {err}"); + } Ok(processed_packet) => match processed_packet.processing_data { MixProcessingResultData::ForwardHop { packet, delay } => { self.handle_forward_packet(now, packet, delay); @@ -334,6 +423,7 @@ impl ConnectionHandler { packets: HashMap>, replay_check_results: HashMap>, ) { + let mut replays_detected: u64 = 0; for (rotation_id, packets) in packets { let Some(replay_checks) = replay_check_results.get(&rotation_id) else { // this should never happen, but if we messed up, and it does, don't panic, just drop the packets @@ -342,6 +432,13 @@ impl ConnectionHandler { }; for (packet, &replayed) in packets.into_iter().zip(replay_checks) { let unwrapped_packet = if replayed { + replays_detected += 1; + warn!( + event = "packet.dropped.replay", + remote_addr = %self.remote_address, + rotation_id, + "dropping replayed packet" + ); Err(PacketProcessingError::PacketReplay) } else { packet.finalise_unwrapping() @@ -350,6 +447,13 @@ impl ConnectionHandler { self.handle_unwrapped_packet(now, unwrapped_packet).await; } } + if replays_detected > 0 { + debug!( + replays_detected, + remote_addr = %self.remote_address, + "replay detection batch completed with replays" + ); + } } async fn handle_pending_packets_batch_no_locking(&mut self, now: Instant) -> bool { @@ -379,13 +483,26 @@ impl ConnectionHandler { true } + #[instrument( + name = "mixnode.replay_check_batch", + skip(self), + level = "debug", + fields( + batch_size, + mutex_wait_ms, + ) + )] async fn handle_pending_packets_batch(&mut self, now: Instant) { + let batch_size = self.pending_packets.total_count(); + Span::current().record("batch_size", batch_size as u64); + let batch = self.pending_packets.reset(now); let replay_tags = self.pending_packets.replay_tags(); if replay_tags.is_empty() { return; } + let mutex_start = Instant::now(); let Ok(replay_check_results) = self .shared .replay_protection_filter @@ -396,32 +513,56 @@ impl ConnectionHandler { self.shared.shutdown_token.cancel(); return; }; + Span::current().record( + "mutex_wait_ms", + mutex_start.elapsed().as_millis() as u64, + ); self.handle_post_replay_detection_packets(now, batch, replay_check_results) .await; } + #[instrument( + name = "mixnode.sphinx_full_unwrap", + skip(self, packet), + level = "debug", + fields(key_rotation) + )] fn try_full_unwrap_packet( &self, packet: FramedNymPacket, ) -> Result { - // based on the received sphinx key rotation information, - // attempt to choose appropriate key for processing the packet - // NOTE: due to the function signatures, outfox packets will **only** attempt primary key - // if no rotation information is available (but that's fine given outfox is not really in use, - // and by the time we need it, the rotation info should be present) + let rotation_label = match packet.header().key_rotation { + SphinxKeyRotation::Unknown => "unknown", + SphinxKeyRotation::OddRotation => "odd", + SphinxKeyRotation::EvenRotation => "even", + }; + Span::current().record("key_rotation", rotation_label); + match packet.header().key_rotation { SphinxKeyRotation::Unknown => { process_framed_packet(packet, self.shared.sphinx_keys.primary().inner().as_ref()) } SphinxKeyRotation::OddRotation => { let Some(odd_key) = self.shared.sphinx_keys.odd() else { + warn!( + event = "packet.dropped.expired_key", + key_rotation = "odd", + remote_addr = %self.remote_address, + "dropping packet: odd key rotation expired" + ); return Err(PacketProcessingError::ExpiredKey); }; process_framed_packet(packet, odd_key.inner().as_ref()) } SphinxKeyRotation::EvenRotation => { let Some(even_key) = self.shared.sphinx_keys.even() else { + warn!( + event = "packet.dropped.expired_key", + key_rotation = "even", + remote_addr = %self.remote_address, + "dropping packet: even key rotation expired" + ); return Err(PacketProcessingError::ExpiredKey); }; process_framed_packet(packet, even_key.inner().as_ref()) @@ -456,23 +597,36 @@ impl ConnectionHandler { } #[instrument( - skip(self), + name = "mixnode.connection", + skip(self, socket), level = "debug", fields( - remote = %self.remote_address + remote = %self.remote_address, + noise_handshake_ms, ) )] pub(crate) async fn handle_connection(&mut self, socket: TcpStream) { + let handshake_start = Instant::now(); let noise_stream = match upgrade_noise_responder(socket, &self.shared.noise_config).await { Ok(noise_stream) => noise_stream, Err(err) => { - error!( - "Failed to perform Noise handshake with {:?} - {err}", - self.remote_address + Span::current().record( + "noise_handshake_ms", + handshake_start.elapsed().as_millis() as u64, + ); + warn!( + event = "connection.failed.noise", + remote_addr = %self.remote_address, + error = %err, + "Noise responder handshake failed" ); return; } }; + Span::current().record( + "noise_handshake_ms", + handshake_start.elapsed().as_millis() as u64, + ); debug!( "Noise responder handshake completed for {:?}", self.remote_address @@ -481,26 +635,58 @@ impl ConnectionHandler { .await } + #[instrument( + name = "mixnode.stream", + skip(self, mixnet_connection), + level = "debug", + fields( + remote = %self.remote_address, + packets_processed = 0u64, + exit_reason, + ) + )] pub(crate) async fn handle_stream( &mut self, mut mixnet_connection: Framed, NymCodec>, ) { + let mut packets_processed: u64 = 0; loop { tokio::select! { biased; _ = self.shared.shutdown_token.cancelled() => { trace!("connection handler: received shutdown"); + Span::current().record("exit_reason", "shutdown"); break } maybe_framed_nym_packet = mixnet_connection.next() => { match maybe_framed_nym_packet { - Some(Ok(packet)) => self.handle_received_nym_packet(packet).await, + Some(Ok(packet)) => { + self.handle_received_nym_packet(packet).await; + packets_processed += 1; + if packets_processed % 10000 == 0 { + Span::current().record("packets_processed", packets_processed); + } + } Some(Err(err)) => { - debug!("connection got corrupted with: {err}"); + warn!( + event = "connection.corrupted", + remote_addr = %self.remote_address, + error = %err, + packets_processed, + "connection stream corrupted" + ); + Span::current().record("exit_reason", "corrupted"); + Span::current().record("packets_processed", packets_processed); return } None => { - debug!("connection got closed by the remote"); + debug!( + remote_addr = %self.remote_address, + packets_processed, + "connection closed by remote" + ); + Span::current().record("exit_reason", "closed_by_remote"); + Span::current().record("packets_processed", packets_processed); return } } @@ -508,6 +694,7 @@ impl ConnectionHandler { } } + Span::current().record("packets_processed", packets_processed); debug!("exiting and closing connection"); } } diff --git a/nym-node/src/node/mixnet/packet_forwarding/mod.rs b/nym-node/src/node/mixnet/packet_forwarding/mod.rs index 09055daacd..3a887284f2 100644 --- a/nym-node/src/node/mixnet/packet_forwarding/mod.rs +++ b/nym-node/src/node/mixnet/packet_forwarding/mod.rs @@ -55,12 +55,17 @@ impl PacketForwarder { if let Err(err) = self.mixnet_client.send_without_response(packet) { if err.kind() == io::ErrorKind::WouldBlock { - // we only know for sure if we dropped a packet if our sending queue was full - // in any other case the connection might still be re-established (or created for the first time) - // and the packet might get sent, but we won't know about it + warn!( + event = "packet.dropped.buffer_full", + next_hop = %next_hop, + "dropping packet: egress connection buffer full (WouldBlock)" + ); self.metrics.mixnet.egress_dropped_forward_packet(next_hop) } else if err.kind() == io::ErrorKind::NotConnected { - // let's give the benefit of the doubt and assume we manage to establish connection + debug!( + next_hop = %next_hop, + "packet queued for not-yet-connected peer" + ); self.metrics.mixnet.egress_sent_forward_packet(next_hop) } } else { @@ -86,7 +91,11 @@ impl PacketForwarder { let next_hop = new_packet.packet.next_hop(); if !self.routing_filter.should_route(next_hop.as_ref().ip()) { - debug!("dropping packet as the egress address does not belong to any known node"); + warn!( + event = "packet.dropped.routing_filter", + next_hop = %next_hop, + "dropping packet: egress address does not belong to any known node" + ); self.metrics .mixnet .egress_dropped_forward_packet(next_hop.into()); @@ -125,7 +134,7 @@ impl PacketForwarder { C: SendWithoutResponse, F: RoutingFilter, { - let mut processed = 0; + let mut processed: u64 = 0; trace!("starting PacketForwarder"); loop { tokio::select! { @@ -145,11 +154,29 @@ impl PacketForwarder { #[allow(clippy::unwrap_used)] self.handle_new_packet(new_packet.unwrap()); let channel_len = self.packet_sender.len(); + let delay_queue_len = self.delay_queue.len(); if processed % 1000 == 0 { match channel_len { - n if n > 1000 => error!("there are currently {n} mix packets waiting to get forwarded - the node seems to be significantly overloaded!"), - n if n > 500 => warn!("there are currently {n} mix packets waiting to get forwarded - is the node overloaded?"), - n => trace!("there are currently {n} mix packets waiting to get forwarded"), + n if n > 1000 => error!( + event = "forwarder.queue_overload", + channel_depth = n, + delay_queue_depth = delay_queue_len, + packets_processed = processed, + "there are currently {n} mix packets waiting to get forwarded - the node seems to be significantly overloaded!" + ), + n if n > 500 => warn!( + event = "forwarder.queue_high", + channel_depth = n, + delay_queue_depth = delay_queue_len, + packets_processed = processed, + "there are currently {n} mix packets waiting to get forwarded - is the node overloaded?" + ), + n => trace!( + channel_depth = n, + delay_queue_depth = delay_queue_len, + packets_processed = processed, + "forwarder queue status" + ), } } self.update_channel_size_metric(channel_len); diff --git a/nym-node/src/node/mixnet/shared/final_hop.rs b/nym-node/src/node/mixnet/shared/final_hop.rs index cb6bc0ea64..0f852707f5 100644 --- a/nym-node/src/node/mixnet/shared/final_hop.rs +++ b/nym-node/src/node/mixnet/shared/final_hop.rs @@ -5,7 +5,8 @@ use nym_gateway::node::{ ActiveClientsStore, GatewayStorage, GatewayStorageError, InboxGatewayStorage, }; use nym_sphinx_types::DestinationAddressBytes; -use tracing::debug; +use tokio::time::Instant; +use tracing::{debug, warn}; #[derive(Clone)] pub(crate) struct SharedFinalHopData { @@ -27,14 +28,34 @@ impl SharedFinalHopData { message: Vec, ) -> Result<(), Vec> { match self.active_clients.get_sender(client_address) { - None => Err(message), + None => { + debug!( + event = "gateway.push_to_client", + client_found = false, + send_result = "client_not_found", + "client {client_address} not found in active clients" + ); + Err(message) + } Some(sender_channel) => { if let Err(unsent) = sender_channel.unbounded_send(vec![message]) { + warn!( + event = "gateway.push_to_client", + client_found = true, + send_result = "channel_closed", + "client {client_address} channel closed, message not delivered" + ); // the unwrap here is fine as the original message got returned; // plus we're only ever sending 1 message at the time (for now) #[allow(clippy::unwrap_used)] Err(unsent.into_inner().pop().unwrap()) } else { + debug!( + event = "gateway.push_to_client", + client_found = true, + send_result = "ok", + "pushed message to client {client_address}" + ); Ok(()) } } @@ -46,8 +67,23 @@ impl SharedFinalHopData { client_address: DestinationAddressBytes, message: Vec, ) -> Result<(), GatewayStorageError> { + let start = Instant::now(); debug!("Storing received message for {client_address} on the disk...",); - - self.storage.store_message(client_address, message).await + let result = self.storage.store_message(client_address, message).await; + let store_ms = start.elapsed().as_millis() as u64; + if result.is_ok() { + debug!( + event = "gateway.disk_store", + store_ms, + "stored message for {client_address} on disk in {store_ms}ms" + ); + } else { + warn!( + event = "gateway.disk_store_failed", + store_ms, + "failed to store message for {client_address} on disk after {store_ms}ms" + ); + } + result } } diff --git a/nym-node/src/node/mixnet/shared/mod.rs b/nym-node/src/node/mixnet/shared/mod.rs index ac8dd9ecc7..13a664772c 100644 --- a/nym-node/src/node/mixnet/shared/mod.rs +++ b/nym-node/src/node/mixnet/shared/mod.rs @@ -185,16 +185,20 @@ impl SharedData { } pub(super) fn forward_mix_packet(&self, packet: MixPacket, delay_until: Option) { + let has_delay = delay_until.is_some(); if self .mixnet_forwarder .forward_packet(PacketToForward::new(packet, delay_until)) .is_err() - && !self.shutdown_token.is_cancelled() { - error!( - "failed to forward sphinx packet on the channel while the process is not going through the shutdown!" - ); - self.shutdown_token.cancel(); + if !self.shutdown_token.is_cancelled() { + error!( + event = "forwarder.channel_send_failed", + has_delay, + "failed to forward sphinx packet on the channel while the process is not going through the shutdown!" + ); + self.shutdown_token.cancel(); + } } } From 597aae1a20e303feac305a683f8406ebc565b50e Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 13:46:23 +0100 Subject: [PATCH 02/51] localnet: wire otel --- docker/localnet/Dockerfile.localnet | 7 +- docker/localnet/localnet-logs.sh | 21 +++-- docker/localnet/localnet.sh | 140 ++++++++++++++++------------ 3 files changed, 99 insertions(+), 69 deletions(-) diff --git a/docker/localnet/Dockerfile.localnet b/docker/localnet/Dockerfile.localnet index a6eb260f1a..e3d3460c6e 100644 --- a/docker/localnet/Dockerfile.localnet +++ b/docker/localnet/Dockerfile.localnet @@ -10,10 +10,9 @@ COPY ./ ./ ENV CARGO_BUILD_JOBS=8 # Build all required binaries in release mode -RUN cargo build --release --locked \ - -p nym-node \ - -p nym-network-requester \ - -p nym-socks5-client +# nym-node is built with the otel feature for OpenTelemetry tracing support +RUN cargo build --release --locked -p nym-node --features otel && \ + cargo build --release --locked -p nym-network-requester -p nym-socks5-client # Install runtime dependencies including Go for wireguard-go RUN apt update && apt install -y \ diff --git a/docker/localnet/localnet-logs.sh b/docker/localnet/localnet-logs.sh index 3347943e09..9dfcdb5291 100755 --- a/docker/localnet/localnet-logs.sh +++ b/docker/localnet/localnet-logs.sh @@ -5,6 +5,13 @@ SESSION_NAME="nym-localnet-logs" +# Detect runtime +if command -v container &> /dev/null; then + RUNTIME="container" +else + RUNTIME="docker" +fi + # Container names CONTAINERS=( "nym-mixnode1" @@ -17,9 +24,9 @@ CONTAINERS=( # Check if containers are running running_containers=() -for container in "${CONTAINERS[@]}"; do - if container inspect "$container" &>/dev/null; then - running_containers+=("$container") +for ctr in "${CONTAINERS[@]}"; do + if $RUNTIME inspect "$ctr" &>/dev/null; then + running_containers+=("$ctr") fi done @@ -32,11 +39,11 @@ fi # Check if we're already in tmux if [ -n "$TMUX" ]; then # Inside tmux - create new window - tmux new-window -n "logs" "container logs -f ${running_containers[0]}" + tmux new-window -n "logs" "$RUNTIME logs -f ${running_containers[0]}" # Split for remaining containers for ((i=1; i<${#running_containers[@]}; i++)); do - tmux split-window -t logs "container logs -f ${running_containers[$i]}" + tmux split-window -t logs "$RUNTIME logs -f ${running_containers[$i]}" tmux select-layout -t logs tiled done @@ -48,11 +55,11 @@ else exec tmux attach-session -t "$SESSION_NAME" else # Create new session - tmux new-session -d -s "$SESSION_NAME" -n "logs" "container logs -f ${running_containers[0]}" + tmux new-session -d -s "$SESSION_NAME" -n "logs" "$RUNTIME logs -f ${running_containers[0]}" # Split for remaining containers for ((i=1; i<${#running_containers[@]}; i++)); do - tmux split-window -t "$SESSION_NAME:logs" "container logs -f ${running_containers[$i]}" + tmux split-window -t "$SESSION_NAME:logs" "$RUNTIME logs -f ${running_containers[$i]}" tmux select-layout -t "$SESSION_NAME:logs" tiled done diff --git a/docker/localnet/localnet.sh b/docker/localnet/localnet.sh index e021310bc2..e8b73b8cd1 100755 --- a/docker/localnet/localnet.sh +++ b/docker/localnet/localnet.sh @@ -2,8 +2,8 @@ set -ex -# Nym Localnet Orchestration Script for Apple Container Runtime -# Emulates docker-compose functionality +# Nym Localnet Orchestration Script +# Supports both Docker and Apple Container Runtime SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" @@ -14,6 +14,28 @@ NYM_VOLUME_PATH="/tmp/nym-localnet-home-$$" SUFFIX=${NYM_NODE_SUFFIX:-localnet} +# Detect container runtime: prefer Apple 'container' if available, fall back to docker +if command -v container &> /dev/null; then + RUNTIME="container" + HOST_INTERNAL="host.containers.internal" +else + RUNTIME="docker" + HOST_INTERNAL="host.docker.internal" +fi + +# OpenTelemetry configuration +# Set OTEL_ENABLE=1 to enable OTel tracing on all nym-node instances. +# OTEL_ENDPOINT should point to the OTLP gRPC collector reachable from containers. +OTEL_ENABLE=${OTEL_ENABLE:-1} +OTEL_ENDPOINT=${OTEL_ENDPOINT:-"http://${HOST_INTERNAL}:4317"} + +# Build OTel flags for nym-node run commands +otel_flags() { + if [ "$OTEL_ENABLE" = "1" ]; then + echo "--otel --otel-endpoint $OTEL_ENDPOINT" + fi +} + # Container names INIT_CONTAINER="nym-localnet-init" MIXNODE1_CONTAINER="nym-mixnode1" @@ -64,13 +86,13 @@ cleanup_host_state() { done } -# Check if container command exists +# Check prerequisites check_prerequisites() { - if ! command -v container &> /dev/null; then - log_error "Apple 'container' command not found" - log_error "Install from: https://github.com/apple/container" + if ! command -v docker &> /dev/null; then + log_error "Docker not found" exit 1 fi + log_info "Using runtime: $RUNTIME" } # Build the Docker image @@ -80,7 +102,6 @@ build_image() { cd "$PROJECT_ROOT" - # Build with Docker log_info "Building with Docker..." if ! docker build \ -f "$SCRIPT_DIR/Dockerfile.localnet" \ @@ -90,30 +111,24 @@ build_image() { exit 1 fi - # Transfer image to container runtime - log_info "Transferring image to container runtime..." - - # Save to temporary file (container image load doesn't support stdin) - TEMP_IMAGE="/tmp/nym-localnet-image-$$.tar" - if ! docker save -o "$TEMP_IMAGE" "$IMAGE_NAME"; then - log_error "Failed to save Docker image" - exit 1 - fi - - # Load into container runtime from file - if ! container image load --input "$TEMP_IMAGE"; then + # If using Apple container runtime, transfer image from Docker + if [ "$RUNTIME" = "container" ]; then + log_info "Transferring image to Apple container runtime..." + TEMP_IMAGE="/tmp/nym-localnet-image-$$.tar" + if ! docker save -o "$TEMP_IMAGE" "$IMAGE_NAME"; then + log_error "Failed to save Docker image" + exit 1 + fi + if ! container image load --input "$TEMP_IMAGE"; then + rm -f "$TEMP_IMAGE" + log_error "Failed to load image into container runtime" + exit 1 + fi rm -f "$TEMP_IMAGE" - log_error "Failed to load image into container runtime" - exit 1 - fi - - # Clean up temporary file - rm -f "$TEMP_IMAGE" - - # Verify image is available - if ! container image inspect "$IMAGE_NAME" &>/dev/null; then - log_error "Image not found in container runtime after load" - exit 1 + if ! container image inspect "$IMAGE_NAME" &>/dev/null; then + log_error "Image not found in container runtime after load" + exit 1 + fi fi log_success "Image built and loaded: $IMAGE_NAME" @@ -155,7 +170,7 @@ NETWORK_NAME="nym-localnet-network" # Create container network create_network() { log_info "Creating container network: $NETWORK_NAME" - if container network create "$NETWORK_NAME" 2>/dev/null; then + if $RUNTIME network create "$NETWORK_NAME" 2>/dev/null; then log_success "Network created: $NETWORK_NAME" else log_info "Network $NETWORK_NAME already exists or creation failed" @@ -164,9 +179,9 @@ create_network() { # Remove container network remove_network() { - if container network list | grep -q "$NETWORK_NAME"; then + if $RUNTIME network list | grep -q "$NETWORK_NAME"; then log_info "Removing network: $NETWORK_NAME" - container network rm "$NETWORK_NAME" 2>/dev/null || true + $RUNTIME network rm "$NETWORK_NAME" 2>/dev/null || true log_success "Network removed" fi } @@ -183,7 +198,10 @@ start_mixnode() { local verloc_port="2000${node_id}" local http_port="3000${node_id}" - container run \ + local otel_args + otel_args=$(otel_flags) + + $RUNTIME run \ --name "$container_name" \ -m 2G \ --network "$NETWORK_NAME" \ @@ -215,7 +233,7 @@ start_mixnode() { sleep 2; done; echo "Starting mix'"${node_id}"'..."; - exec nym-node run --id mix'"${node_id}"'-localnet --unsafe-disable-replay-protection --local + exec nym-node '"${otel_args}"' run --id mix'"${node_id}"'-localnet --unsafe-disable-replay-protection --local ' log_success "$container_name started" @@ -224,7 +242,10 @@ start_mixnode() { start_gateway() { log_info "Starting $GATEWAY_CONTAINER..." - container run \ + local otel_args + otel_args=$(otel_flags) + + $RUNTIME run \ --name "$GATEWAY_CONTAINER" \ -m 2G \ --network "$NETWORK_NAME" \ @@ -267,7 +288,7 @@ start_gateway() { sleep 2; done; echo "Starting gateway with LP listener (mock ecash)..."; - exec nym-node run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true + exec nym-node '"${otel_args}"' run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true ' log_success "$GATEWAY_CONTAINER started" @@ -291,7 +312,10 @@ start_gateway() { start_gateway2() { log_info "Starting $GATEWAY2_CONTAINER..." - container run \ + local otel_args + otel_args=$(otel_flags) + + $RUNTIME run \ --name "$GATEWAY2_CONTAINER" \ -m 2G \ --network "$NETWORK_NAME" \ @@ -334,7 +358,7 @@ start_gateway2() { sleep 2; done; echo "Starting gateway2 with LP listener (mock ecash)..."; - exec nym-node run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true + exec nym-node '"${otel_args}"' run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true ' log_success "$GATEWAY2_CONTAINER started" @@ -360,10 +384,10 @@ start_network_requester() { # Get gateway IP address log_info "Getting gateway IP address..." - GATEWAY_IP=$(container exec "$GATEWAY_CONTAINER" hostname -i) + GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i) log_info "Gateway IP: $GATEWAY_IP" - container run \ + $RUNTIME run \ --name "$REQUESTER_CONTAINER" \ --network "$NETWORK_NAME" \ -v "$VOLUME_PATH:/localnet" \ @@ -398,7 +422,7 @@ start_network_requester() { start_socks5_client() { log_info "Starting $SOCKS5_CONTAINER..." - container run \ + $RUNTIME run \ --name "$SOCKS5_CONTAINER" \ --network "$NETWORK_NAME" \ -p 1080:1080 \ @@ -451,15 +475,15 @@ stop_containers() { log_info "Stopping all containers..." for container_name in "${ALL_CONTAINERS[@]}"; do - if container inspect "$container_name" &>/dev/null; then + if $RUNTIME inspect "$container_name" &>/dev/null; then log_info "Stopping $container_name" - container stop "$container_name" 2>/dev/null || true - container rm "$container_name" 2>/dev/null || true + $RUNTIME stop "$container_name" 2>/dev/null || true + $RUNTIME rm "$container_name" 2>/dev/null || true fi done # Also clean up init container if it exists - container rm "$INIT_CONTAINER" 2>/dev/null || true + $RUNTIME rm "$INIT_CONTAINER" 2>/dev/null || true log_success "All containers stopped" @@ -467,7 +491,7 @@ stop_containers() { remove_network } -# Show container logs +# Show $RUNTIME logs show_logs() { local container_name=${1:-} @@ -478,8 +502,8 @@ show_logs() { fi # Show logs for specific container - if container inspect "$container_name" &>/dev/null; then - container logs -f "$container_name" + if $RUNTIME inspect "$container_name" &>/dev/null; then + $RUNTIME logs -f "$container_name" else log_error "Container not found: $container_name" log_info "Available containers:" @@ -496,8 +520,8 @@ show_status() { echo "" for container_name in "${ALL_CONTAINERS[@]}"; do - if container inspect "$container_name" &>/dev/null; then - local status=$(container inspect "$container_name" 2>/dev/null | grep -o '"Status":"[^"]*"' | cut -d'"' -f4 || echo "unknown") + if $RUNTIME inspect "$container_name" &>/dev/null; then + local status=$($RUNTIME inspect "$container_name" 2>/dev/null | grep -o '"Status":"[^"]*"' | cut -d'"' -f4 || echo "unknown") echo -e " ${GREEN}●${NC} $container_name - $status" else echo -e " ${RED}○${NC} $container_name - not running" @@ -554,11 +578,11 @@ build_topology() { # Get container IPs log_info "Getting container IP addresses..." - MIX1_IP=$(container exec "$MIXNODE1_CONTAINER" hostname -i) - MIX2_IP=$(container exec "$MIXNODE2_CONTAINER" hostname -i) - MIX3_IP=$(container exec "$MIXNODE3_CONTAINER" hostname -i) - GATEWAY_IP=$(container exec "$GATEWAY_CONTAINER" hostname -i) - GATEWAY2_IP=$(container exec "$GATEWAY2_CONTAINER" hostname -i) + MIX1_IP=$($RUNTIME exec "$MIXNODE1_CONTAINER" hostname -i) + MIX2_IP=$($RUNTIME exec "$MIXNODE2_CONTAINER" hostname -i) + MIX3_IP=$($RUNTIME exec "$MIXNODE3_CONTAINER" hostname -i) + GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i) + GATEWAY2_IP=$($RUNTIME exec "$GATEWAY2_CONTAINER" hostname -i) log_info "Container IPs:" echo " mix1: $MIX1_IP" @@ -568,7 +592,7 @@ build_topology() { echo " gateway2: $GATEWAY2_IP" # Run build_topology.py in a container with access to the volumes - container run \ + $RUNTIME run \ --name "nym-localnet-topology-builder" \ --network "$NETWORK_NAME" \ -v "$VOLUME_PATH:/localnet" \ @@ -613,7 +637,7 @@ start_all() { # Note: Runs after build_topology to ensure gateways have finished WireGuard setup log_info "Configuring gateway networking (IP forwarding, NAT)..." for gw in "$GATEWAY_CONTAINER" "$GATEWAY2_CONTAINER"; do - container exec "$gw" sh -c " + $RUNTIME exec "$gw" sh -c " # Enable IP forwarding echo 1 > /proc/sys/net/ipv4/ip_forward # Add NAT masquerade for outbound traffic From f3d10004726ae0e2bc85b0e311c08036a0f1faf4 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 13:57:04 +0100 Subject: [PATCH 03/51] Add gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 988c9e1691..9c639d33b2 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ CLAUDE.md .claude/settings.json /notes +/target-otel From 00467e44408da5efec4a9626eadcfa2e83c9a5d5 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 14:11:40 +0100 Subject: [PATCH 04/51] fix upstream build: update lockfile and stabilise nym-lp --- Cargo.lock | 55 ++++++------------------------------ common/nym-lp/src/message.rs | 4 +-- 2 files changed, 11 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 368e9a2430..af7d625080 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2165,10 +2165,10 @@ dependencies = [ "ipnet", "libc", "log", - "netlink-packet-core 0.8.1", - "netlink-packet-generic 0.4.0", + "netlink-packet-core", + "netlink-packet-generic", "netlink-packet-route", - "netlink-packet-utils 0.6.0", + "netlink-packet-utils", "netlink-packet-wireguard", "netlink-sys", "nix 0.30.1", @@ -4989,17 +4989,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "netlink-packet-core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" -dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils 0.5.2", -] - [[package]] name = "netlink-packet-core" version = "0.8.1" @@ -5009,25 +4998,13 @@ dependencies = [ "paste", ] -[[package]] -name = "netlink-packet-generic" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7eb8ad331c84c6b8cb7f685b448133e5ad82e1ffd5acafac374af4a5a308b" -dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-core 0.7.0", - "netlink-packet-utils 0.5.2", -] - [[package]] name = "netlink-packet-generic" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f891b2e0054cac5a684a06628f59568f841c93da4e551239da6e518f539e775" dependencies = [ - "netlink-packet-core 0.8.1", + "netlink-packet-core", ] [[package]] @@ -5039,19 +5016,7 @@ dependencies = [ "bitflags 2.9.1", "libc", "log", - "netlink-packet-core 0.8.1", -] - -[[package]] -name = "netlink-packet-utils" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" -dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror 1.0.69", + "netlink-packet-core", ] [[package]] @@ -5067,16 +5032,14 @@ dependencies = [ [[package]] name = "netlink-packet-wireguard" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b25b050ff1f6a1e23c6777b72db22790fe5b6b5ccfd3858672587a79876c8f" +checksum = "598962d9067d3153a00106da10e7b8276cea68f396f4a22f5b4a079270d92e29" dependencies = [ - "anyhow", - "byteorder", "libc", "log", - "netlink-packet-generic 0.3.3", - "netlink-packet-utils 0.5.2", + "netlink-packet-core", + "netlink-packet-generic", ] [[package]] diff --git a/common/nym-lp/src/message.rs b/common/nym-lp/src/message.rs index fbd692175b..77f332eec2 100644 --- a/common/nym-lp/src/message.rs +++ b/common/nym-lp/src/message.rs @@ -391,14 +391,14 @@ impl ForwardPacketData { } // SAFETY: we ensured we have sufficient data, and the length is correct for casting #[allow(clippy::unwrap_used)] - let ipv6 = IpAddr::V6(Ipv6Addr::from_octets(bytes[33..49].try_into().unwrap())); + let ipv6 = IpAddr::V6(Ipv6Addr::from(<[u8; 16]>::try_from(&bytes[33..49]).unwrap())); let port = u16::from_le_bytes([bytes[49], bytes[50]]); (SocketAddr::new(ipv6, port), 51) } else { // IPv4. Length check done at the start // SAFETY: we ensured we have sufficient data, and the length is correct for casting #[allow(clippy::unwrap_used)] - let ipv4 = IpAddr::V4(Ipv4Addr::from_octets(bytes[33..37].try_into().unwrap())); + let ipv4 = IpAddr::V4(Ipv4Addr::from(<[u8; 4]>::try_from(&bytes[33..37]).unwrap())); let port = u16::from_le_bytes([bytes[37], bytes[38]]); (SocketAddr::new(ipv4, port), 39) }; From c7cd9626271478f9949a3d3a13fe58dd1b3b3d3d Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 14:33:02 +0100 Subject: [PATCH 05/51] localnet: multi-stage dockerfile --- .dockerignore | 1 + docker/localnet/Dockerfile.localnet | 43 ++++++++++++++++------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/.dockerignore b/.dockerignore index a11022eb2b..09428cfe68 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,4 +3,5 @@ .gitignore **/node_modules **/target +target-otel dist diff --git a/docker/localnet/Dockerfile.localnet b/docker/localnet/Dockerfile.localnet index e3d3460c6e..528ea3bf7e 100644 --- a/docker/localnet/Dockerfile.localnet +++ b/docker/localnet/Dockerfile.localnet @@ -1,21 +1,24 @@ -# Single-stage Dockerfile for Nym localnet -# Builds: nym-node, nym-network-requester, nym-socks5-client -# Target: Apple Container Runtime with host networking +# Multi-stage Dockerfile for Nym localnet +# Stage 1: Build binaries +# Stage 2: Slim runtime with only the final binaries -FROM rust:latest +# --- Build stage --- +FROM rust:latest AS builder WORKDIR /usr/src/nym COPY ./ ./ ENV CARGO_BUILD_JOBS=8 -# Build all required binaries in release mode -# nym-node is built with the otel feature for OpenTelemetry tracing support RUN cargo build --release --locked -p nym-node --features otel && \ cargo build --release --locked -p nym-network-requester -p nym-socks5-client -# Install runtime dependencies including Go for wireguard-go -RUN apt update && apt install -y \ +# --- Runtime stage --- +FROM debian:trixie-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + build-essential \ python3 \ python3-pip \ netcat-openbsd \ @@ -23,31 +26,33 @@ RUN apt update && apt install -y \ iproute2 \ net-tools \ wireguard-tools \ - golang-go \ git \ iptables \ + curl \ && rm -rf /var/lib/apt/lists/* -# Install wireguard-go (userspace WireGuard implementation) -RUN git clone https://git.zx2c4.com/wireguard-go && \ +# Install Go and build wireguard-go, then clean up +ARG TARGETARCH +RUN curl -fsSL "https://go.dev/dl/go1.23.6.linux-${TARGETARCH}.tar.gz" \ + | tar -C /usr/local -xz && \ + export PATH="/usr/local/go/bin:$PATH" && \ + git clone https://git.zx2c4.com/wireguard-go && \ cd wireguard-go && \ make && \ cp wireguard-go /usr/local/bin/ && \ cd .. && \ - rm -rf wireguard-go + rm -rf wireguard-go /usr/local/go && \ + apt-get purge -y --auto-remove build-essential curl -# Install Python dependencies for build_topology.py RUN pip3 install --break-system-packages base58 -# Move binaries to /usr/local/bin for easy access -RUN cp target/release/nym-node /usr/local/bin/ && \ - cp target/release/nym-network-requester /usr/local/bin/ && \ - cp target/release/nym-socks5-client /usr/local/bin/ +# Copy only the compiled binaries from the builder stage +COPY --from=builder /usr/src/nym/target/release/nym-node /usr/local/bin/ +COPY --from=builder /usr/src/nym/target/release/nym-network-requester /usr/local/bin/ +COPY --from=builder /usr/src/nym/target/release/nym-socks5-client /usr/local/bin/ -# Copy supporting scripts COPY ./docker/localnet/build_topology.py /usr/local/bin/ WORKDIR /nym -# Default command CMD ["nym-node", "--help"] From e753f24ed1c58654bb9e8f6c2d6f39e60c4fd05c Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 15:21:45 +0100 Subject: [PATCH 06/51] localnet: fix runtime and gateway flags --- docker/localnet/localnet.sh | 73 ++++++++++++++++++---------- nym-node/src/cli/mod.rs | 94 ++++++++++++++++++++----------------- 2 files changed, 98 insertions(+), 69 deletions(-) diff --git a/docker/localnet/localnet.sh b/docker/localnet/localnet.sh index e8b73b8cd1..5b92158f76 100755 --- a/docker/localnet/localnet.sh +++ b/docker/localnet/localnet.sh @@ -26,8 +26,18 @@ fi # OpenTelemetry configuration # Set OTEL_ENABLE=1 to enable OTel tracing on all nym-node instances. # OTEL_ENDPOINT should point to the OTLP gRPC collector reachable from containers. +# When SigNoz runs in Docker (signoz-net), we route to its collector directly. OTEL_ENABLE=${OTEL_ENABLE:-1} -OTEL_ENDPOINT=${OTEL_ENDPOINT:-"http://${HOST_INTERNAL}:4317"} +if [ -z "${OTEL_ENDPOINT:-}" ]; then + SIGNOZ_NET=$(docker network ls --filter name=signoz-net --format '{{.Name}}' 2>/dev/null || true) + if [ "$RUNTIME" = "docker" ] && [ -n "$SIGNOZ_NET" ]; then + OTEL_ENDPOINT="http://signoz-otel-collector:4317" + OTEL_SIGNOZ_NET="$SIGNOZ_NET" + else + OTEL_ENDPOINT="http://${HOST_INTERNAL}:4317" + OTEL_SIGNOZ_NET="" + fi +fi # Build OTel flags for nym-node run commands otel_flags() { @@ -248,6 +258,8 @@ start_gateway() { $RUNTIME run \ --name "$GATEWAY_CONTAINER" \ -m 2G \ + --cap-add=NET_ADMIN \ + --device /dev/net/tun \ --network "$NETWORK_NAME" \ -p 9000:9000 \ -p 10004:10004 \ @@ -276,11 +288,9 @@ start_gateway() { --http-bind-address=0.0.0.0:30004 \ --http-access-token=lala \ --public-ips $CONTAINER_IP \ - --enable-lp true \ --lp-use-mock-ecash true \ --output=json \ - --wireguard-enabled true \ - --wireguard-userspace true \ + --wireguard-enabled false \ --bonding-information-output="/localnet/gateway.json"; echo "Waiting for network.json..."; @@ -288,7 +298,7 @@ start_gateway() { sleep 2; done; echo "Starting gateway with LP listener (mock ecash)..."; - exec nym-node '"${otel_args}"' run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true + exec nym-node '"${otel_args}"' run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled false --lp-use-mock-ecash true ' log_success "$GATEWAY_CONTAINER started" @@ -318,6 +328,8 @@ start_gateway2() { $RUNTIME run \ --name "$GATEWAY2_CONTAINER" \ -m 2G \ + --cap-add=NET_ADMIN \ + --device /dev/net/tun \ --network "$NETWORK_NAME" \ -p 9001:9001 \ -p 10005:10005 \ @@ -346,11 +358,9 @@ start_gateway2() { --http-bind-address=0.0.0.0:30005 \ --http-access-token=lala \ --public-ips $CONTAINER_IP \ - --enable-lp true \ --lp-use-mock-ecash true \ --output=json \ - --wireguard-enabled true \ - --wireguard-userspace true \ + --wireguard-enabled false \ --bonding-information-output="/localnet/gateway2.json"; echo "Waiting for network.json..."; @@ -358,7 +368,7 @@ start_gateway2() { sleep 2; done; echo "Starting gateway2 with LP listener (mock ecash)..."; - exec nym-node '"${otel_args}"' run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true + exec nym-node '"${otel_args}"' run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled false --lp-use-mock-ecash true ' log_success "$GATEWAY2_CONTAINER started" @@ -382,9 +392,9 @@ start_gateway2() { start_network_requester() { log_info "Starting $REQUESTER_CONTAINER..." - # Get gateway IP address + # Get gateway IP address (first IP only, in case container has multiple networks) log_info "Getting gateway IP address..." - GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i) + GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i | awk '{print $1}') log_info "Gateway IP: $GATEWAY_IP" $RUNTIME run \ @@ -576,13 +586,13 @@ build_topology() { log_success " $file created" done - # Get container IPs + # Get container IPs (first IP only, containers may be on multiple networks) log_info "Getting container IP addresses..." - MIX1_IP=$($RUNTIME exec "$MIXNODE1_CONTAINER" hostname -i) - MIX2_IP=$($RUNTIME exec "$MIXNODE2_CONTAINER" hostname -i) - MIX3_IP=$($RUNTIME exec "$MIXNODE3_CONTAINER" hostname -i) - GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i) - GATEWAY2_IP=$($RUNTIME exec "$GATEWAY2_CONTAINER" hostname -i) + MIX1_IP=$($RUNTIME exec "$MIXNODE1_CONTAINER" hostname -i | awk '{print $1}') + MIX2_IP=$($RUNTIME exec "$MIXNODE2_CONTAINER" hostname -i | awk '{print $1}') + MIX3_IP=$($RUNTIME exec "$MIXNODE3_CONTAINER" hostname -i | awk '{print $1}') + GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i | awk '{print $1}') + GATEWAY2_IP=$($RUNTIME exec "$GATEWAY2_CONTAINER" hostname -i | awk '{print $1}') log_info "Container IPs:" echo " mix1: $MIX1_IP" @@ -631,19 +641,32 @@ start_all() { start_mixnode 3 "$MIXNODE3_CONTAINER" start_gateway start_gateway2 + + # Connect nym containers to SigNoz network for direct OTLP routing + if [ -n "${OTEL_SIGNOZ_NET:-}" ]; then + log_info "Connecting containers to SigNoz network ($OTEL_SIGNOZ_NET)..." + for c in "$MIXNODE1_CONTAINER" "$MIXNODE2_CONTAINER" "$MIXNODE3_CONTAINER" \ + "$GATEWAY_CONTAINER" "$GATEWAY2_CONTAINER"; do + docker network connect "$OTEL_SIGNOZ_NET" "$c" 2>/dev/null && \ + log_success " $c connected to $OTEL_SIGNOZ_NET" || true + done + fi + build_topology # Configure networking for two-hop WireGuard routing on both gateways - # Note: Runs after build_topology to ensure gateways have finished WireGuard setup + # Note: Requires --privileged or --cap-add=NET_ADMIN on the containers. + # Non-fatal: only needed for WireGuard VPN routing, not mixnet packet testing. log_info "Configuring gateway networking (IP forwarding, NAT)..." for gw in "$GATEWAY_CONTAINER" "$GATEWAY2_CONTAINER"; do - $RUNTIME exec "$gw" sh -c " - # Enable IP forwarding - echo 1 > /proc/sys/net/ipv4/ip_forward - # Add NAT masquerade for outbound traffic - iptables-legacy -t nat -A POSTROUTING -o eth0 -j MASQUERADE - " - log_success "Configured $gw" + if $RUNTIME exec "$gw" sh -c " + echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null + iptables-legacy -t nat -A POSTROUTING -o eth0 -j MASQUERADE 2>/dev/null + " 2>/dev/null; then + log_success "Configured $gw" + else + log_warn "Could not configure NAT on $gw (needs --privileged). WireGuard VPN routing will not work." + fi done start_network_requester diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index f3a144d46e..837003bcc0 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -8,7 +8,6 @@ use crate::cli::commands::{ use crate::env::vars::{NYMNODE_CONFIG_ENV_FILE_ARG, NYMNODE_NO_BANNER_ARG}; use clap::{Args, Parser, Subcommand}; use nym_bin_common::bin_info; -use std::future::Future; use std::sync::OnceLock; pub(crate) mod commands; @@ -59,67 +58,41 @@ pub(crate) struct Cli { } impl Cli { - fn execute_async(fut: F) -> anyhow::Result { - Ok(tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build()? - .block_on(fut)) - } - pub(crate) fn execute(self) -> anyhow::Result<()> { - // NOTE: `test_throughput` sets up its own logger as it has to include additional layers - #[cfg(feature = "otel")] - let _otel_guard = if !matches!(self.command, Commands::TestThroughput(..)) { - let otel_config = if self.otel { - Some(crate::logging::OtelConfig { - endpoint: self.otel_endpoint.clone(), - service_name: "nym-node".to_string(), - }) - } else { - None - }; - crate::logging::setup_tracing_logger(otel_config)? - } else { - None - }; + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build()?; - #[cfg(not(feature = "otel"))] - if !matches!(self.command, Commands::TestThroughput(..)) { - let otel_config = if self.otel { - Some(crate::logging::OtelConfig { - endpoint: self.otel_endpoint.clone(), - service_name: "nym-node".to_string(), - }) - } else { - None - }; - crate::logging::setup_tracing_logger(otel_config)?; - } + // Set up tracing inside the runtime so the OTel batch exporter + // can spawn its background tasks on the tokio reactor. + let _otel_guard = runtime.block_on(async { + self.setup_logging() + })?; - let result = (|| -> anyhow::Result<()> { + let result = runtime.block_on(async { match self.command { Commands::BuildInfo(args) => build_info::execute(args)?, Commands::BondingInformation(args) => { - { Self::execute_async(bonding_information::execute(args))? }? + bonding_information::execute(args).await? } Commands::NodeDetails(args) => { - { Self::execute_async(node_details::execute(args))? }? + node_details::execute(args).await? } - Commands::Run(args) => { Self::execute_async(run::execute(*args))? }?, + Commands::Run(args) => run::execute(*args).await?, Commands::Migrate(args) => migrate::execute(*args)?, - Commands::Sign(args) => { Self::execute_async(sign::execute(args))? }?, + Commands::Sign(args) => sign::execute(args).await?, Commands::TestThroughput(args) => test_throughput::execute(args)?, Commands::UnsafeResetSphinxKeys(args) => { - { Self::execute_async(reset_sphinx_keys::execute(args))? }? + reset_sphinx_keys::execute(args).await? } Commands::Debug(debug) => match debug.command { DebugCommands::ResetProvidersGatewayDbs(args) => { - { Self::execute_async(debug::reset_providers_dbs::execute(args))? }? + debug::reset_providers_dbs::execute(args).await? } }, } - Ok(()) - })(); + Ok::<(), anyhow::Error>(()) + }); // Flush any pending OTel spans before exit #[cfg(feature = "otel")] @@ -131,6 +104,39 @@ impl Cli { result } + + #[cfg(feature = "otel")] + fn setup_logging(&self) -> anyhow::Result> { + if matches!(self.command, Commands::TestThroughput(..)) { + return Ok(None); + } + let otel_config = if self.otel { + Some(crate::logging::OtelConfig { + endpoint: self.otel_endpoint.clone(), + service_name: "nym-node".to_string(), + }) + } else { + None + }; + crate::logging::setup_tracing_logger(otel_config) + } + + #[cfg(not(feature = "otel"))] + fn setup_logging(&self) -> anyhow::Result> { + if matches!(self.command, Commands::TestThroughput(..)) { + return Ok(None); + } + let otel_config = if self.otel { + Some(crate::logging::OtelConfig { + endpoint: self.otel_endpoint.clone(), + service_name: "nym-node".to_string(), + }) + } else { + None + }; + crate::logging::setup_tracing_logger(otel_config)?; + Ok(None) + } } #[derive(Subcommand, Debug)] From 8bb29f4d074cf735002e3d2e3f694e488ce787aa Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 15:26:53 +0100 Subject: [PATCH 07/51] localnet: add loadtest script and signoz docs --- docker/localnet/README.md | 140 ++++++++++++---- docker/localnet/loadtest.sh | 297 +++++++++++++++++++++++++++++++++ docker/localnet/otel-report.sh | 222 ++++++++++++++++++++++++ 3 files changed, 631 insertions(+), 28 deletions(-) create mode 100755 docker/localnet/loadtest.sh create mode 100755 docker/localnet/otel-report.sh diff --git a/docker/localnet/README.md b/docker/localnet/README.md index 415b38e01b..84c52cb1b9 100644 --- a/docker/localnet/README.md +++ b/docker/localnet/README.md @@ -1,37 +1,73 @@ -# Nym Localnet for Kata Container Runtimes +# Nym Localnet -A complete Nym mixnet test environment running on Apple's container runtime for macOS (for now). +A complete Nym mixnet test environment with OpenTelemetry instrumentation. +Supports both Docker Desktop and Apple Container Runtime on macOS. ## Overview This localnet setup provides a fully functional Nym mixnet for local development and testing: - **3 mixnodes** (layer 1, 2, 3) -- **1 gateway** (entry + exit mode) +- **2 gateways** (entry + exit mode) - **1 network-requester** (service provider) - **1 SOCKS5 client** +- **OpenTelemetry tracing** via OTLP/gRPC to SigNoz (or any OTLP collector) All components run in isolated containers with proper networking and dynamic IP resolution. +When the `otel` feature is enabled (default), every nym-node exports traces covering +the full packet lifecycle: ingress, Sphinx processing, forwarding, and final-hop delivery. ## Prerequisites ### Required - **macOS** (tested on macOS Sequoia 15.0+) -- **Apple Container Runtime** - Built into macOS -- **Docker Desktop** (for building images only) +- **Docker Desktop** (recommended) or **Apple Container Runtime** - **Python 3** with `base58` library +### SigNoz (for trace viewing) + +SigNoz is an open-source APM that receives and visualises OpenTelemetry data. +Install it locally with Docker Compose -- this takes about 2 minutes: + +```bash +# Clone the SigNoz repository +git clone -b main https://github.com/SigNoz/signoz.git ~/signoz +cd ~/signoz/deploy + +# Start SigNoz (runs ClickHouse, otel-collector, query-service, frontend) +docker compose up -d + +# Verify it is running +docker ps --filter "name=signoz" --format "table {{.Names}}\t{{.Status}}" +``` + +Once running: +- **SigNoz UI**: http://localhost:8080 +- **OTLP gRPC collector**: localhost:4317 (used by nym-nodes) +- **OTLP HTTP collector**: localhost:4318 + +The localnet script auto-detects the SigNoz Docker network (`signoz-net`) and +routes OTel traffic directly to the collector container -- no manual endpoint +configuration needed. + +To stop SigNoz later: +```bash +cd ~/signoz/deploy && docker compose down +``` + ### Installation ```bash # Install Python dependencies pip3 install --break-system-packages base58 -# Verify container runtime is available -container --version - -# Verify Docker is installed (for building) +# Verify Docker is installed docker --version ``` +If using Apple Container Runtime instead of Docker: +```bash +container --version +``` + ## Quick Start ```bash @@ -198,54 +234,99 @@ container logs nym-gateway --follow ### Status ```bash # List all containers -container list +docker ps --filter "name=nym-" --format "table {{.Names}}\t{{.Status}}" # Check specific container -container logs nym-gateway +docker logs nym-gateway # Inspect network -container network inspect nym-localnet-network +docker network inspect nym-localnet-network ``` ## Testing ### Basic SOCKS5 Test ```bash -# Simple HTTP request with redirect following -curl -L --socks5 localhost:1080 http://example.com +# Simple HTTP request through the mixnet +curl -x socks5h://127.0.0.1:1080 https://httpbin.org/get # HTTPS request -curl -L --socks5 localhost:1080 https://nymtech.net +curl -x socks5h://127.0.0.1:1080 https://nymtech.net # Download a file -curl -L --socks5 localhost:1080 \ +curl -x socks5h://127.0.0.1:1080 \ https://test-download-files-nym.s3.amazonaws.com/download-files/1MB.zip \ --output /tmp/test.zip ``` +### Load Testing + +A load test script is included to generate sustained traffic and populate SigNoz +with meaningful trace data: + +```bash +# Default: 10 concurrent workers, 60 seconds +./loadtest.sh + +# Heavier load: 20 workers for 2 minutes +./loadtest.sh -c 20 -d 120 + +# Light single-threaded test +./loadtest.sh -c 1 -d 10 + +# Target a specific URL +./loadtest.sh -c 5 -d 30 -u https://httpbin.org/bytes/4096 +``` + +The script reports live progress, then prints a summary with request counts, +throughput, and latency percentiles (p50/p95/p99). + ### Verify Network Topology ```bash # View the generated topology -container exec nym-gateway cat /localnet/network.json | jq . +docker exec nym-gateway cat /localnet/network.json | jq . -# Check container IPs -container list | grep nym- +# Check container status +docker ps --filter "name=nym-" --format "table {{.Names}}\t{{.Status}}" # Verify all bonding files exist -container exec nym-gateway ls -la /localnet/ +docker exec nym-gateway ls -la /localnet/ ``` ### Test Mixnet Routing ```bash -# All traffic flows through: client → mix1 → mix2 → mix3 → gateway → internet +# All traffic flows through: client -> gateway -> mix1 -> mix2 -> mix3 -> gateway -> internet # Watch logs to verify routing: -container logs nym-mixnode1 --follow & -container logs nym-mixnode2 --follow & -container logs nym-mixnode3 --follow & -container logs nym-gateway --follow & +docker logs nym-mixnode1 --follow & +docker logs nym-mixnode2 --follow & +docker logs nym-mixnode3 --follow & +docker logs nym-gateway --follow & # Make a request -curl -L --socks5 localhost:1080 https://nymtech.com +curl -x socks5h://127.0.0.1:1080 https://nymtech.net +``` + +## OpenTelemetry + +OTel is enabled by default. Each nym-node exports traces via OTLP/gRPC covering +packet ingress, Sphinx processing, forwarding, and final-hop delivery. + +### Viewing Traces + +- **SigNoz UI**: http://localhost:8080 -- filter by `serviceName = nym-node` +- **Terminal report** (queries ClickHouse directly, no login needed): + +```bash +./otel-report.sh # last 15 minutes +./otel-report.sh 60 # last 60 minutes +./otel-report.sh live # auto-refresh every 10s +``` + +### Disabling OTel + +```bash +OTEL_ENABLE=0 ./localnet.sh start # disable +OTEL_ENDPOINT=http://my-collector:4317 ./localnet.sh start # custom collector ``` ### LP (Lewes Protocol) Testing @@ -289,8 +370,11 @@ This makes localnet perfect for rapid LP protocol development and testing. docker/localnet/ ├── README.md # This file ├── localnet.sh # Main orchestration script -├── Dockerfile.localnet # Docker image definition -└── build_topology.py # Topology generator +├── loadtest.sh # Load test / traffic generator +├── otel-report.sh # Terminal-based OTel metrics report +├── Dockerfile.localnet # Multi-stage Docker image (builder + slim runtime) +├── build_topology.py # Topology generator +└── localnet-logs.sh # Tmux-based multi-container log viewer ``` ## How It Works diff --git a/docker/localnet/loadtest.sh b/docker/localnet/loadtest.sh new file mode 100755 index 0000000000..e76b956ad6 --- /dev/null +++ b/docker/localnet/loadtest.sh @@ -0,0 +1,297 @@ +#!/bin/bash + +# Nym Localnet Load Test +# Generates sustained traffic through the mixnet SOCKS5 proxy to produce +# OTel traces and exercise the packet pipeline end-to-end. +# +# Usage: +# ./loadtest.sh # defaults: 10 concurrent, 60s, mixed sizes +# ./loadtest.sh -c 20 -d 120 # 20 concurrent, 120s +# ./loadtest.sh -s 64k # fixed 64KB responses (many Sphinx fragments) +# ./loadtest.sh -s 1k -c 5 -d 30 # small payloads, 5 workers +# +# Payload sizes (-s flag) map to Sphinx packet fragmentation: +# 1k = ~1 Sphinx packet (sub-MTU, minimal fragmentation) +# 4k = ~2-3 packets (small payload) +# 16k = ~8-10 packets (medium payload) +# 64k = ~32-35 packets (large payload, stresses forwarding) +# 256k = ~128-130 packets (heavy payload, stresses queues) +# 1m = ~512 packets (very heavy, potential backpressure) +# +# Prerequisites: +# - Localnet running (./localnet.sh start) +# - SOCKS5 proxy available on localhost:1080 + +set -e + +CONCURRENCY=10 +DURATION=60 +PROXY="socks5h://127.0.0.1:1080" +PAYLOAD_SIZE="" +CUSTOM_URL="" +STATS_INTERVAL=5 + +# Default targets: mixed sizes for general testing +TARGETS=( + "https://httpbin.org/get" + "https://httpbin.org/bytes/1024" + "https://httpbin.org/delay/1" + "https://example.com" + "https://nym.com" +) + +# Convert human-readable size to bytes for httpbin +parse_size() { + local s + s=$(echo "$1" | tr '[:upper:]' '[:lower:]') + local num + num=$(echo "$s" | sed 's/[a-z]*$//') + case "$s" in + *m|*mb) echo $(( num * 1024 * 1024 )) ;; + *k|*kb) echo $(( num * 1024 )) ;; + *) echo "$num" ;; + esac +} + +usage() { + echo "Usage: $0 [-c concurrency] [-d duration_secs] [-s payload_size] [-u url] [-p proxy]" + echo "" + echo "Options:" + echo " -c Number of concurrent workers (default: $CONCURRENCY)" + echo " -d Test duration in seconds (default: $DURATION)" + echo " -s Response payload size: 1k, 4k, 16k, 64k, 256k, 1m (default: mixed)" + echo " -u Custom target URL (overrides -s and default targets)" + echo " -p SOCKS5 proxy address (default: $PROXY)" + echo "" + echo "Examples:" + echo " $0 # 10 workers, 60s, mixed targets/sizes" + echo " $0 -s 1k # small payloads (~1 Sphinx packet each)" + echo " $0 -s 64k -c 5 # large payloads, 5 workers" + echo " $0 -s 256k -c 2 -d 30 # very large payloads, observe queue pressure" + echo " $0 -c 20 -d 120 # heavier concurrency, 2 minutes" + exit 0 +} + +while getopts "c:d:s:u:p:h" opt; do + case $opt in + c) CONCURRENCY=$OPTARG ;; + d) DURATION=$OPTARG ;; + s) PAYLOAD_SIZE=$OPTARG ;; + u) CUSTOM_URL=$OPTARG ;; + p) PROXY=$OPTARG ;; + h) usage ;; + *) usage ;; + esac +done + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { echo -e "${BLUE}[INFO]${NC} $*"; } +log_ok() { echo -e "${GREEN}[OK]${NC} $*"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +log_err() { echo -e "${RED}[ERROR]${NC} $*"; } + +# Build sized URL if -s was specified +SIZED_URL="" +SIZE_LABEL="mixed" +if [ -n "$PAYLOAD_SIZE" ]; then + PAYLOAD_BYTES=$(parse_size "$PAYLOAD_SIZE") + SIZED_URL="https://httpbin.org/bytes/${PAYLOAD_BYTES}" + SIZE_LABEL="${PAYLOAD_SIZE} (~${PAYLOAD_BYTES} bytes)" +fi + +# Preflight checks +if ! nc -z 127.0.0.1 1080 2>/dev/null; then + log_err "SOCKS5 proxy not reachable on localhost:1080. Is the localnet running?" + exit 1 +fi + +# Counters (written to temp files for cross-process aggregation) +STATS_DIR=$(mktemp -d) +cleanup() { + kill $(jobs -p) 2>/dev/null || true + rm -rf "$STATS_DIR" +} +trap cleanup INT TERM EXIT + +pick_url() { + if [ -n "$CUSTOM_URL" ]; then + echo "$CUSTOM_URL" + elif [ -n "$PAYLOAD_SIZE" ]; then + echo "$SIZED_URL" + else + local idx=$((RANDOM % ${#TARGETS[@]})) + echo "${TARGETS[$idx]}" + fi +} + +# Millisecond timestamp (works on both GNU and BSD/macOS date) +now_ms() { + python3 -c 'import time; print(int(time.time()*1000))' +} + +# Worker function: runs requests in a loop until duration expires +worker() { + local id=$1 + local end_time=$2 + local ok=0 + local fail=0 + + while [ "$(date +%s)" -lt "$end_time" ]; do + local url + url=$(pick_url) + local start_ms + start_ms=$(now_ms) + + if curl -x "$PROXY" -m 15 -sf -o /dev/null -w "" "$url" 2>/dev/null; then + ok=$((ok + 1)) + else + fail=$((fail + 1)) + fi + + local end_ms + end_ms=$(now_ms) + local latency=$((end_ms - start_ms)) + + echo "$latency" >> "$STATS_DIR/latencies_${id}.txt" + done + + echo "$ok" > "$STATS_DIR/ok_${id}.txt" + echo "$fail" > "$STATS_DIR/fail_${id}.txt" +} + +echo "" +log_info "=== Nym Localnet Load Test ===" +log_info "Concurrency: $CONCURRENCY workers" +log_info "Duration: ${DURATION}s" +log_info "Payload: $SIZE_LABEL" +if [ -n "$CUSTOM_URL" ]; then + log_info "Target: $CUSTOM_URL" +elif [ -n "$PAYLOAD_SIZE" ]; then + log_info "Target: $SIZED_URL" +else + log_info "Targets: ${#TARGETS[@]} rotating URLs" +fi +log_info "Proxy: $PROXY" +echo "" + +# Quick connectivity check +log_info "Preflight: testing SOCKS5 proxy..." +if curl -x "$PROXY" -m 15 -sf -o /dev/null "https://httpbin.org/get"; then + log_ok "SOCKS5 proxy is working" +else + log_err "SOCKS5 proxy test failed. Check localnet status." + exit 1 +fi + +END_TIME=$(( $(date +%s) + DURATION )) +START_TIME=$(date +%s) + +log_info "Starting $CONCURRENCY workers for ${DURATION}s..." +echo "" + +for i in $(seq 1 "$CONCURRENCY"); do + worker "$i" "$END_TIME" & +done + +# Progress reporter (counts completed latency entries as a proxy for request count) +while [ "$(date +%s)" -lt "$END_TIME" ]; do + sleep "$STATS_INTERVAL" + elapsed=$(( $(date +%s) - START_TIME )) + remaining=$(( END_TIME - $(date +%s) )) + if [ "$remaining" -lt 0 ]; then remaining=0; fi + + total=0 + for f in "$STATS_DIR"/latencies_*.txt; do + if [ -f "$f" ]; then + count=$(wc -l < "$f" 2>/dev/null || echo 0) + total=$((total + count)) + fi + done + + if [ "$elapsed" -gt 0 ]; then + rps=$(echo "scale=1; $total / $elapsed" | bc 2>/dev/null || echo "?") + else + rps="?" + fi + + printf "\r [%3ds / %3ds] requests: %d | ~%s req/s | remaining: %ds " \ + "$elapsed" "$DURATION" "$total" "$rps" "$remaining" +done + +echo "" +log_info "Waiting for workers to finish..." +wait 2>/dev/null || true + +# Final stats +echo "" +log_info "=== Results ===" +total_ok=0 +total_fail=0 +all_latencies="" + +for f in "$STATS_DIR"/ok_*.txt; do + [ -f "$f" ] && total_ok=$((total_ok + $(cat "$f" 2>/dev/null || echo 0))) +done +for f in "$STATS_DIR"/fail_*.txt; do + [ -f "$f" ] && total_fail=$((total_fail + $(cat "$f" 2>/dev/null || echo 0))) +done +for f in "$STATS_DIR"/latencies_*.txt; do + [ -f "$f" ] && all_latencies="$all_latencies $(cat "$f" 2>/dev/null | tr '\n' ' ')" +done + +total=$((total_ok + total_fail)) +actual_duration=$(( $(date +%s) - START_TIME )) + +echo "" +echo " Total requests: $total" +echo " Successful: $total_ok" +echo " Failed: $total_fail" +if [ "$actual_duration" -gt 0 ]; then + rps=$(echo "scale=2; $total / $actual_duration" | bc 2>/dev/null || echo "?") + echo " Duration: ${actual_duration}s" + echo " Throughput: ~${rps} req/s" +fi + +if [ -n "$all_latencies" ]; then + sorted=$(echo "$all_latencies" | tr ' ' '\n' | sort -n | grep -v '^$') + count=$(echo "$sorted" | wc -l | tr -d ' ') + if [ "$count" -gt 0 ]; then + p50_idx=$(( count * 50 / 100 )) + p95_idx=$(( count * 95 / 100 )) + p99_idx=$(( count * 99 / 100 )) + [ "$p50_idx" -lt 1 ] && p50_idx=1 + [ "$p95_idx" -lt 1 ] && p95_idx=1 + [ "$p99_idx" -lt 1 ] && p99_idx=1 + + min_lat=$(echo "$sorted" | head -1) + max_lat=$(echo "$sorted" | tail -1) + p50=$(echo "$sorted" | sed -n "${p50_idx}p") + p95=$(echo "$sorted" | sed -n "${p95_idx}p") + p99=$(echo "$sorted" | sed -n "${p99_idx}p") + + echo "" + echo " Latency (ms):" + echo " min: ${min_lat}ms" + echo " p50: ${p50}ms" + echo " p95: ${p95}ms" + echo " p99: ${p99}ms" + echo " max: ${max_lat}ms" + fi +fi + +echo "" +if [ "$total_fail" -gt 0 ] && [ "$total" -gt 0 ]; then + fail_pct=$(echo "scale=1; $total_fail * 100 / $total" | bc 2>/dev/null || echo "?") + log_warn "Failure rate: ${fail_pct}% -- ${total_fail} of ${total} failed" +else + log_ok "All requests succeeded" +fi +echo "" +log_info "View traces in SigNoz: http://localhost:8080/traces" +log_info "Filter by service: nym-node" +echo "" diff --git a/docker/localnet/otel-report.sh b/docker/localnet/otel-report.sh new file mode 100755 index 0000000000..7df437979b --- /dev/null +++ b/docker/localnet/otel-report.sh @@ -0,0 +1,222 @@ +#!/bin/bash + +# Nym Localnet OTel Report +# Queries ClickHouse directly to produce a terminal-based summary of +# the core metrics captured by the OTel-instrumented nym-nodes. +# +# Usage: +# ./otel-report.sh # last 15 minutes +# ./otel-report.sh 60 # last 60 minutes +# ./otel-report.sh live # live mode: refresh every 10s +# +# Prerequisites: localnet + SigNoz running + +set -e + +CH_CONTAINER="signoz-clickhouse" +TRACES_TABLE="signoz_traces.distributed_signoz_index_v3" +LOOKBACK_MIN=${1:-15} +LIVE=false + +if [ "$1" = "live" ]; then + LIVE=true + LOOKBACK_MIN=5 +fi + +BLUE='\033[0;34m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BOLD='\033[1m' +DIM='\033[2m' +NC='\033[0m' + +ch() { + docker exec "$CH_CONTAINER" clickhouse-client --query "$1" 2>/dev/null +} + +divider() { + echo -e "${DIM}$(printf '%.0s-' {1..78})${NC}" +} + +print_report() { + local window="$1" + + echo "" + echo -e "${BOLD} Nym Localnet -- OTel Packet Pipeline Report${NC}" + echo -e " ${DIM}Window: last ${window} minutes | $(date '+%Y-%m-%d %H:%M:%S')${NC}" + divider + + # 1. Throughput per operation + echo -e "\n${BOLD} [1] Packet Throughput (packets/sec by operation)${NC}\n" + ch " + SELECT + name AS operation, + count(*) AS total, + round(count(*) / (${window} * 60), 1) AS per_sec + FROM ${TRACES_TABLE} + WHERE timestamp >= now() - INTERVAL ${window} MINUTE + AND serviceName = 'nym-node' + AND name IN ( + 'handle_received_nym_packet', + 'mixnode.sphinx_full_unwrap', + 'mixnode.forward_packet', + 'mixnode.final_hop' + ) + GROUP BY name + ORDER BY total DESC + FORMAT PrettyCompactNoEscapes + " + + divider + + # 2. Latency per operation + echo -e "\n${BOLD} [2] Processing Latency (milliseconds)${NC}\n" + ch " + SELECT + name AS operation, + round(quantile(0.50)(duration_nano / 1e6), 3) AS p50_ms, + round(quantile(0.95)(duration_nano / 1e6), 3) AS p95_ms, + round(quantile(0.99)(duration_nano / 1e6), 3) AS p99_ms, + round(quantile(0.999)(duration_nano / 1e6), 3) AS p999_ms, + round(avg(duration_nano / 1e6), 3) AS avg_ms + FROM ${TRACES_TABLE} + WHERE timestamp >= now() - INTERVAL ${window} MINUTE + AND serviceName = 'nym-node' + AND name IN ( + 'handle_received_nym_packet', + 'mixnode.sphinx_full_unwrap', + 'mixnode.forward_packet', + 'mixnode.final_hop' + ) + AND duration_nano < 60000000000 + GROUP BY name + ORDER BY p50_ms DESC + FORMAT PrettyCompactNoEscapes + " + + divider + + # 3. Error rate + echo -e "\n${BOLD} [3] Error Rate${NC}\n" + local errors + errors=$(ch " + SELECT + name, + countIf(has_error = true) AS errors, + count(*) AS total, + round(100.0 * countIf(has_error = true) / count(*), 3) AS error_pct + FROM ${TRACES_TABLE} + WHERE timestamp >= now() - INTERVAL ${window} MINUTE + AND serviceName = 'nym-node' + AND name IN ( + 'handle_received_nym_packet', + 'mixnode.sphinx_full_unwrap', + 'mixnode.forward_packet', + 'mixnode.final_hop' + ) + GROUP BY name + HAVING errors > 0 + ORDER BY errors DESC + FORMAT PrettyCompactNoEscapes + ") + + if [ -z "$errors" ]; then + echo -e " ${GREEN}No errors detected across all operations${NC}" + else + echo "$errors" + fi + + divider + + # 4. Forwarding ratio (are packets being dropped between stages?) + echo -e "\n${BOLD} [4] Pipeline Funnel (packet drop detection)${NC}\n" + ch " + SELECT + name AS stage, + count(*) AS packets, + round(100.0 * count(*) / max(total_ingress), 1) AS pct_of_ingress + FROM ${TRACES_TABLE} + CROSS JOIN ( + SELECT count(*) AS total_ingress + FROM ${TRACES_TABLE} + WHERE timestamp >= now() - INTERVAL ${window} MINUTE + AND serviceName = 'nym-node' + AND name = 'handle_received_nym_packet' + ) AS t + WHERE timestamp >= now() - INTERVAL ${window} MINUTE + AND serviceName = 'nym-node' + AND name IN ( + 'handle_received_nym_packet', + 'mixnode.sphinx_full_unwrap', + 'mixnode.forward_packet', + 'mixnode.final_hop' + ) + GROUP BY name + ORDER BY packets DESC + FORMAT PrettyCompactNoEscapes + " + + echo "" + echo -e " ${DIM}Expected ratios: sphinx_unwrap ~ 100%, forward ~ 75% (3 of 4 hops forward),${NC}" + echo -e " ${DIM}final_hop ~ 25% (1 of 4 hops is the last one). Significantly lower = drops.${NC}" + + divider + + # 5. Throughput over time (1-minute buckets) + echo -e "\n${BOLD} [5] Throughput Timeline (1-min buckets, ingress packets)${NC}\n" + ch " + SELECT + toStartOfMinute(timestamp) AS minute, + count(*) AS packets, + round(count(*) / 60, 1) AS per_sec + FROM ${TRACES_TABLE} + WHERE timestamp >= now() - INTERVAL ${window} MINUTE + AND serviceName = 'nym-node' + AND name = 'handle_received_nym_packet' + GROUP BY minute + ORDER BY minute + FORMAT PrettyCompactNoEscapes + " + + divider + + # 6. Latency spikes (potential TCP congestion / backpressure indicators) + echo -e "\n${BOLD} [6] Latency Spikes (sphinx_unwrap p99 per minute)${NC}\n" + ch " + SELECT + toStartOfMinute(timestamp) AS minute, + round(quantile(0.99)(duration_nano / 1e6), 3) AS p99_ms, + round(quantile(0.50)(duration_nano / 1e6), 3) AS p50_ms, + round(p99_ms / greatest(p50_ms, 0.001), 1) AS spike_ratio, + count(*) AS samples + FROM ${TRACES_TABLE} + WHERE timestamp >= now() - INTERVAL ${window} MINUTE + AND serviceName = 'nym-node' + AND name = 'mixnode.sphinx_full_unwrap' + GROUP BY minute + ORDER BY minute + FORMAT PrettyCompactNoEscapes + " + + echo "" + echo -e " ${DIM}spike_ratio > 10x suggests backpressure or queue buildup.${NC}" + echo -e " ${DIM}Sustained high p99 across minutes may indicate TCP meltdown.${NC}" + + divider + echo "" + echo -e " ${BLUE}SigNoz UI:${NC} http://localhost:8080" + echo -e " ${DIM}Traces tab -> Filter: serviceName = nym-node${NC}" + echo "" +} + +if [ "$LIVE" = "true" ]; then + while true; do + clear + print_report "$LOOKBACK_MIN" + echo -e " ${DIM}Refreshing in 10s... (Ctrl+C to stop)${NC}" + sleep 10 + done +else + print_report "$LOOKBACK_MIN" +fi From b2d7b54f34a77fb0153bacf02bb96e8c62903d9c Mon Sep 17 00:00:00 2001 From: Tommy Verrall <60836166+tommyv1987@users.noreply.github.com> Date: Mon, 16 Feb 2026 16:10:55 +0100 Subject: [PATCH 08/51] Enhance CI workflow with feature inputs Allow features in the CI workflow. Updated handling of cargo features and RUSTFLAGS based on inputs. --- .../workflows/ci-build-upload-binaries.yml | 101 ++++++++++++------ 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci-build-upload-binaries.yml b/.github/workflows/ci-build-upload-binaries.yml index 24b680c908..d66189281b 100644 --- a/.github/workflows/ci-build-upload-binaries.yml +++ b/.github/workflows/ci-build-upload-binaries.yml @@ -3,13 +3,28 @@ name: ci-build-upload-binaries on: workflow_dispatch: inputs: + feature_profile: + description: "Select a predefined cargo feature profile" + required: false + default: "none" + type: choice + options: + - none + - tokio-console + - otel + - otel,tokio-console + extra_features: + description: "Additional comma-separated cargo features (e.g. feat1,feat2)" + required: false + default: "" + type: string add_tokio_unstable: - description: 'True to add RUSTFLAGS="--cfg tokio_unstable"' - required: true + description: 'Force RUSTFLAGS="--cfg tokio_unstable" (auto-set when tokio-console is selected)' + required: false default: false type: boolean enable_deb: - description: "True to enable cargo-deb installation and .deb package building" + description: "Enable cargo-deb installation and .deb package building" required: false default: false type: boolean @@ -21,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ arc-linux-latest ] + platform: [arc-linux-latest] runs-on: ${{ matrix.platform }} env: @@ -36,38 +51,62 @@ jobs: OUTPUT_DIR: ci-builds/${{ github.ref_name }} run: | rm -rf ci-builds || true - mkdir -p $OUTPUT_DIR - echo $OUTPUT_DIR + mkdir -p "$OUTPUT_DIR" + echo "$OUTPUT_DIR" + - name: Install Dependencies (Linux) run: sudo apt-get update && sudo apt-get -y install libudev-dev - - name: Sets env vars for tokio if set in manual dispatch inputs - if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true + - name: Resolve cargo features and RUSTFLAGS + if: github.event_name == 'workflow_dispatch' + shell: bash run: | - echo "RUSTFLAGS=--cfg tokio_unstable" >> $GITHUB_ENV - echo "CARGO_FEATURES=--features tokio-console" >> $GITHUB_ENV + FEATURES="" + PROFILE="${{ inputs.feature_profile }}" + EXTRA="${{ inputs.extra_features }}" + + if [[ "$PROFILE" != "none" && -n "$PROFILE" ]]; then + FEATURES="$PROFILE" + fi + + if [[ -n "$EXTRA" ]]; then + if [[ -n "$FEATURES" ]]; then + FEATURES="${FEATURES},${EXTRA}" + else + FEATURES="$EXTRA" + fi + fi + + if [[ -n "$FEATURES" ]]; then + echo "CARGO_FEATURES=--features ${FEATURES}" >> "$GITHUB_ENV" + echo "::notice::Selected cargo features: $FEATURES" + else + echo "::notice::No additional cargo features selected" + fi + + if [[ "$FEATURES" == *"tokio-console"* ]] || [[ "${{ inputs.add_tokio_unstable }}" == "true" ]]; then + echo "RUSTFLAGS=--cfg tokio_unstable" >> "$GITHUB_ENV" + echo "::notice::Enabled RUSTFLAGS --cfg tokio_unstable" + fi + - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: toolchain: ${{ vars.REQUIRED_RUSTC_VERSION }} - name: Build all binaries - uses: actions-rs/cargo@v1 - with: - command: build - args: --workspace --release ${{ env.CARGO_FEATURES }} + shell: bash + run: cargo build --workspace --release ${{ env.CARGO_FEATURES }} - name: Install cargo-deb - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-deb if: github.event_name == 'workflow_dispatch' && inputs.enable_deb == true + shell: bash + run: cargo install cargo-deb - name: Build deb packages + if: github.event_name == 'workflow_dispatch' && inputs.enable_deb == true shell: bash run: make deb - if: github.event_name == 'workflow_dispatch' && inputs.enable_deb == true - name: Upload Artifact if: github.event_name == 'workflow_dispatch' @@ -84,24 +123,22 @@ jobs: target/release/nym-node retention-days: 30 - # If this was a pull_request or nightly, upload to build server - - name: Prepare build output - # if: github.event_name == 'schedule' || github.event_name == 'pull_request' shell: bash env: OUTPUT_DIR: ci-builds/${{ github.ref_name }} run: | - cp target/release/nym-client $OUTPUT_DIR - cp target/release/nym-socks5-client $OUTPUT_DIR - cp target/release/nym-api $OUTPUT_DIR - cp target/release/nym-network-requester $OUTPUT_DIR - cp target/release/nymvisor $OUTPUT_DIR - cp target/release/nym-node $OUTPUT_DIR - cp target/release/nym-cli $OUTPUT_DIR - if [ ${{ github.event_name == 'workflow_dispatch' && inputs.enable_deb == true }} = true ]; then - cp target/debian/*.deb $OUTPUT_DIR + cp target/release/nym-client "$OUTPUT_DIR" + cp target/release/nym-socks5-client "$OUTPUT_DIR" + cp target/release/nym-api "$OUTPUT_DIR" + cp target/release/nym-network-requester "$OUTPUT_DIR" + cp target/release/nymvisor "$OUTPUT_DIR" + cp target/release/nym-node "$OUTPUT_DIR" + cp target/release/nym-cli "$OUTPUT_DIR" + if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.enable_deb }}" == "true" ]]; then + cp target/debian/*.deb "$OUTPUT_DIR" fi + - name: Deploy branch to CI www continue-on-error: true uses: easingthemes/ssh-deploy@main From cb277fe487e52ee2e22901af2b4f98882189ec49 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 16:11:31 +0100 Subject: [PATCH 09/51] otel: support signoz cloud ingestion key and TLS --- Cargo.lock | 97 ++++++++++++++++++++++------ Cargo.toml | 1 + common/bin-common/Cargo.toml | 4 +- common/bin-common/src/logging/mod.rs | 23 +++++-- nym-node/src/cli/mod.rs | 8 +++ nym-node/src/logging.rs | 3 + 6 files changed, 110 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af7d625080..61b7daa89f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -625,7 +625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", "axum-macros", "bytes", "futures-util", @@ -635,7 +635,7 @@ dependencies = [ "hyper 1.6.0", "hyper-util", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -653,13 +653,38 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core 0.5.6", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper 1.0.2", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-client-ip" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac" dependencies = [ - "axum", + "axum 0.7.9", "forwarded-header-value", "serde", ] @@ -685,14 +710,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-extra" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" dependencies = [ - "axum", - "axum-core", + "axum 0.7.9", + "axum-core 0.4.5", "bytes", "fastrand", "futures-util", @@ -729,7 +772,7 @@ dependencies = [ "anyhow", "assert-json-diff", "auto-future", - "axum", + "axum 0.7.9", "bytes", "bytesize", "cookie", @@ -4814,6 +4857,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -5269,7 +5318,7 @@ version = "1.1.73" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "axum-test", "bincode", "bip39", @@ -5477,6 +5526,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", + "tonic 0.14.4", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -5905,7 +5955,7 @@ name = "nym-credential-proxy" version = "0.3.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "bip39", "bs58", "cfg-if", @@ -5954,7 +6004,7 @@ name = "nym-credential-proxy-lib" version = "1.20.4" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "bip39", "bs58", "futures", @@ -6163,7 +6213,7 @@ version = "1.0.1" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "blake3", "chrono", "clap", @@ -6593,7 +6643,7 @@ dependencies = [ name = "nym-http-api-common" version = "1.20.4" dependencies = [ - "axum", + "axum 0.7.9", "axum-client-ip", "bincode", "bytes", @@ -6973,7 +7023,7 @@ name = "nym-network-monitor" version = "1.0.2" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "clap", "dashmap", "futures", @@ -7061,7 +7111,7 @@ dependencies = [ "arc-swap", "arrayref", "async-trait", - "axum", + "axum 0.7.9", "bip39", "blake2 0.8.1", "bloomfilter", @@ -7210,7 +7260,7 @@ version = "4.1.0" dependencies = [ "ammonia", "anyhow", - "axum", + "axum 0.7.9", "axum-test", "bip39", "bs58", @@ -7877,7 +7927,7 @@ name = "nym-statistics-api" version = "0.3.1" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "axum-client-ip", "axum-extra", "celes", @@ -8358,7 +8408,7 @@ version = "1.20.4" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "futures", "nym-credential-verification", "nym-credentials-interface", @@ -8376,7 +8426,7 @@ dependencies = [ name = "nym-wireguard-private-metadata-shared" version = "1.20.4" dependencies = [ - "axum", + "axum 0.7.9", "bincode", "nym-credentials-interface", "schemars 0.8.22", @@ -8390,7 +8440,7 @@ name = "nym-wireguard-private-metadata-tests" version = "1.20.4" dependencies = [ "async-trait", - "axum", + "axum 0.7.9", "futures", "nym-credential-verification", "nym-credentials-interface", @@ -8457,7 +8507,7 @@ version = "0.1.15" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "chrono", "clap", "nym-bin-common", @@ -11834,7 +11884,7 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.7.9", "base64 0.22.1", "bytes", "h2 0.4.11", @@ -11891,8 +11941,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f32a6f80051a4111560201420c7885d0082ba9efe2ab61875c587bb6b18b9a0" dependencies = [ "async-trait", + "axum 0.8.8", "base64 0.22.1", "bytes", + "h2 0.4.11", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -11901,8 +11953,11 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", + "rustls-native-certs 0.8.3", + "socket2 0.6.0", "sync_wrapper 1.0.2", "tokio", + "tokio-rustls 0.26.2", "tokio-stream", "tower 0.5.2", "tower-layer", @@ -12697,7 +12752,7 @@ version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4b5ac679cc6dfc5ea3f2823b0291c777750ffd5e13b21137e0f7ac0e8f9617" dependencies = [ - "axum", + "axum 0.7.9", "base64 0.22.1", "mime_guess", "regex", diff --git a/Cargo.toml b/Cargo.toml index 89f1add4c1..99451a3484 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -312,6 +312,7 @@ once_cell = "1.21.3" opentelemetry = "0.31.0" opentelemetry_sdk = "0.31.0" opentelemetry-otlp = "0.31.0" +tonic = "0.14.4" parking_lot = "0.12.3" pem = "0.8" petgraph = "0.6.5" diff --git a/common/bin-common/Cargo.toml b/common/bin-common/Cargo.toml index 3a69311369..b10a29b84c 100644 --- a/common/bin-common/Cargo.toml +++ b/common/bin-common/Cargo.toml @@ -26,7 +26,8 @@ opentelemetry = { workspace = true, features = ["trace"], optional = true } ## otel-otlp (modern OTLP export to SigNoz/any OTLP collector) opentelemetry_sdk = { workspace = true, features = ["trace"], optional = true } -opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "trace"], optional = true } +opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "trace", "tls-roots"], optional = true } +tonic = { workspace = true, optional = true } [build-dependencies] @@ -44,6 +45,7 @@ otel-otlp = [ "dep:opentelemetry_sdk", "dep:opentelemetry-otlp", "dep:tracing-opentelemetry", + "dep:tonic", ] clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"] models = [] diff --git a/common/bin-common/src/logging/mod.rs b/common/bin-common/src/logging/mod.rs index 9b13d32628..56815b93a5 100644 --- a/common/bin-common/src/logging/mod.rs +++ b/common/bin-common/src/logging/mod.rs @@ -72,11 +72,15 @@ pub fn setup_tracing_logger() { /// /// # Arguments /// * `service_name` - The service name reported to the collector (e.g. "nym-node") -/// * `endpoint` - The OTLP/gRPC collector endpoint (e.g. "http://localhost:4317") +/// * `endpoint` - The OTLP/gRPC collector endpoint (e.g. "http://localhost:4317" +/// or "https://ingest.eu.signoz.cloud:443" for SigNoz Cloud) +/// * `ingestion_key` - Optional SigNoz Cloud ingestion key. When provided, it is +/// sent as the `signoz-ingestion-key` gRPC metadata header on every export. #[cfg(feature = "otel-otlp")] pub fn init_otel_layer( service_name: &str, endpoint: &str, + ingestion_key: Option<&str>, ) -> Result< ( tracing_opentelemetry::OpenTelemetryLayer, @@ -90,10 +94,21 @@ where use opentelemetry::trace::TracerProvider as _; use opentelemetry_otlp::WithExportConfig; - let exporter = opentelemetry_otlp::SpanExporter::builder() + let mut builder = opentelemetry_otlp::SpanExporter::builder() .with_tonic() - .with_endpoint(endpoint) - .build()?; + .with_endpoint(endpoint); + + if let Some(key) = ingestion_key { + use opentelemetry_otlp::WithTonicConfig; + let mut metadata = tonic::metadata::MetadataMap::new(); + metadata.insert( + "signoz-ingestion-key", + key.parse().map_err(|_| "invalid ingestion key value")?, + ); + builder = builder.with_metadata(metadata); + } + + let exporter = builder.build()?; let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() .with_batch_exporter(exporter) diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index 837003bcc0..175b3022f5 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -46,6 +46,7 @@ pub(crate) struct Cli { /// OpenTelemetry OTLP collector endpoint (gRPC). /// Only used when --otel is enabled. + /// For SigNoz Cloud use https://ingest..signoz.cloud:443 #[clap( long, env = "NYMNODE_OTEL_ENDPOINT", @@ -53,6 +54,11 @@ pub(crate) struct Cli { )] pub(crate) otel_endpoint: String, + /// SigNoz Cloud ingestion key for authenticated OTLP export. + /// Only needed for SigNoz Cloud (not self-hosted). + #[clap(long, env = "NYMNODE_OTEL_KEY")] + pub(crate) otel_key: Option, + #[clap(subcommand)] command: Commands, } @@ -114,6 +120,7 @@ impl Cli { Some(crate::logging::OtelConfig { endpoint: self.otel_endpoint.clone(), service_name: "nym-node".to_string(), + ingestion_key: self.otel_key.clone(), }) } else { None @@ -130,6 +137,7 @@ impl Cli { Some(crate::logging::OtelConfig { endpoint: self.otel_endpoint.clone(), service_name: "nym-node".to_string(), + ingestion_key: self.otel_key.clone(), }) } else { None diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index f35ac8e2a9..c36e1d776c 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -13,6 +13,7 @@ use tracing_subscriber::{EnvFilter, Layer}; pub(crate) struct OtelConfig { pub endpoint: String, pub service_name: String, + pub ingestion_key: Option, } /// Handle returned when OTel is active so the caller can trigger a graceful shutdown. @@ -55,6 +56,7 @@ pub(crate) fn setup_tracing_logger( let (otel_layer, provider) = nym_bin_common::logging::init_otel_layer( &otel_config.service_name, &otel_config.endpoint, + otel_config.ingestion_key.as_deref(), ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; tracing_subscriber::registry() @@ -77,6 +79,7 @@ pub(crate) fn setup_tracing_logger( let (otel_layer, provider) = nym_bin_common::logging::init_otel_layer( &otel_config.service_name, &otel_config.endpoint, + otel_config.ingestion_key.as_deref(), ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; tracing_subscriber::registry() From a779b7a266617425155f5a2f44ec2b1cac15db45 Mon Sep 17 00:00:00 2001 From: Tommy Verrall <60836166+tommyv1987@users.noreply.github.com> Date: Mon, 16 Feb 2026 16:21:42 +0100 Subject: [PATCH 10/51] Update Rust toolchain version to stable --- .github/workflows/ci-build-upload-binaries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build-upload-binaries.yml b/.github/workflows/ci-build-upload-binaries.yml index d66189281b..a86210efec 100644 --- a/.github/workflows/ci-build-upload-binaries.yml +++ b/.github/workflows/ci-build-upload-binaries.yml @@ -90,7 +90,7 @@ jobs: fi - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@master + uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ vars.REQUIRED_RUSTC_VERSION }} From b8d8ee61099017d1c8930f2798dfab2fa279cba4 Mon Sep 17 00:00:00 2001 From: Tommy Verrall <60836166+tommyv1987@users.noreply.github.com> Date: Mon, 16 Feb 2026 16:25:43 +0100 Subject: [PATCH 11/51] Update ci-build-upload-binaries.yml Fix bash errors From 2d73ea5c820fc3ad5812136fecfb2fb54b8fbed2 Mon Sep 17 00:00:00 2001 From: Tommy Verrall <60836166+tommyv1987@users.noreply.github.com> Date: Mon, 16 Feb 2026 16:27:21 +0100 Subject: [PATCH 12/51] Update Rust toolchain to use master branch This is correct unless we want to pin the stable version --- .github/workflows/ci-build-upload-binaries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build-upload-binaries.yml b/.github/workflows/ci-build-upload-binaries.yml index a86210efec..d66189281b 100644 --- a/.github/workflows/ci-build-upload-binaries.yml +++ b/.github/workflows/ci-build-upload-binaries.yml @@ -90,7 +90,7 @@ jobs: fi - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master with: toolchain: ${{ vars.REQUIRED_RUSTC_VERSION }} From bc47e9a1b256d1a774f68dd31f94c02fa36f3750 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 18:11:28 +0100 Subject: [PATCH 13/51] otel: explicit TLS config for https endpoints --- common/bin-common/src/logging/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/bin-common/src/logging/mod.rs b/common/bin-common/src/logging/mod.rs index 56815b93a5..45f7c5fa85 100644 --- a/common/bin-common/src/logging/mod.rs +++ b/common/bin-common/src/logging/mod.rs @@ -94,12 +94,19 @@ where use opentelemetry::trace::TracerProvider as _; use opentelemetry_otlp::WithExportConfig; + use opentelemetry_otlp::WithTonicConfig; + let mut builder = opentelemetry_otlp::SpanExporter::builder() .with_tonic() .with_endpoint(endpoint); + // Explicitly configure TLS when the endpoint uses HTTPS + if endpoint.starts_with("https://") { + builder = builder + .with_tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()); + } + if let Some(key) = ingestion_key { - use opentelemetry_otlp::WithTonicConfig; let mut metadata = tonic::metadata::MetadataMap::new(); metadata.insert( "signoz-ingestion-key", From dce4d6b34bc9708710e728338e03f96a5e32f97f Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 19:13:11 +0100 Subject: [PATCH 14/51] otel: refactor key selection, add environment label, fix clippy --- common/bin-common/src/logging/mod.rs | 11 +- common/nym-lp/src/message.rs | 4 +- docker/localnet/Dockerfile.localnet | 45 ++--- docker/localnet/loadtest.sh | 6 +- nym-node/src/cli/mod.rs | 38 ++-- nym-node/src/logging.rs | 31 +++- nym-node/src/node/mixnet/handler.rs | 165 ++++++++---------- .../src/node/mixnet/packet_forwarding/mod.rs | 2 +- nym-node/src/node/mixnet/shared/final_hop.rs | 9 +- nym-node/src/node/mixnet/shared/mod.rs | 15 +- 10 files changed, 162 insertions(+), 164 deletions(-) diff --git a/common/bin-common/src/logging/mod.rs b/common/bin-common/src/logging/mod.rs index 45f7c5fa85..fe0848cc3e 100644 --- a/common/bin-common/src/logging/mod.rs +++ b/common/bin-common/src/logging/mod.rs @@ -76,11 +76,14 @@ pub fn setup_tracing_logger() { /// or "https://ingest.eu.signoz.cloud:443" for SigNoz Cloud) /// * `ingestion_key` - Optional SigNoz Cloud ingestion key. When provided, it is /// sent as the `signoz-ingestion-key` gRPC metadata header on every export. +/// * `environment` - Deployment environment label (e.g. "sandbox", "mainnet", "canary"). +/// Attached as the `deployment.environment` OTel resource attribute. #[cfg(feature = "otel-otlp")] pub fn init_otel_layer( service_name: &str, endpoint: &str, ingestion_key: Option<&str>, + environment: &str, ) -> Result< ( tracing_opentelemetry::OpenTelemetryLayer, @@ -102,8 +105,8 @@ where // Explicitly configure TLS when the endpoint uses HTTPS if endpoint.starts_with("https://") { - builder = builder - .with_tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()); + builder = + builder.with_tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()); } if let Some(key) = ingestion_key { @@ -122,6 +125,10 @@ where .with_resource( opentelemetry_sdk::Resource::builder() .with_service_name(service_name.to_owned()) + .with_attribute(opentelemetry::KeyValue::new( + "deployment.environment", + environment.to_owned(), + )) .build(), ) .build(); diff --git a/common/nym-lp/src/message.rs b/common/nym-lp/src/message.rs index 77f332eec2..aa58476997 100644 --- a/common/nym-lp/src/message.rs +++ b/common/nym-lp/src/message.rs @@ -391,7 +391,9 @@ impl ForwardPacketData { } // SAFETY: we ensured we have sufficient data, and the length is correct for casting #[allow(clippy::unwrap_used)] - let ipv6 = IpAddr::V6(Ipv6Addr::from(<[u8; 16]>::try_from(&bytes[33..49]).unwrap())); + let ipv6 = IpAddr::V6(Ipv6Addr::from( + <[u8; 16]>::try_from(&bytes[33..49]).unwrap(), + )); let port = u16::from_le_bytes([bytes[49], bytes[50]]); (SocketAddr::new(ipv6, port), 51) } else { diff --git a/docker/localnet/Dockerfile.localnet b/docker/localnet/Dockerfile.localnet index 528ea3bf7e..f10f044c92 100644 --- a/docker/localnet/Dockerfile.localnet +++ b/docker/localnet/Dockerfile.localnet @@ -1,8 +1,9 @@ # Multi-stage Dockerfile for Nym localnet -# Stage 1: Build binaries -# Stage 2: Slim runtime with only the final binaries +# Stage 1: Build Rust binaries +# Stage 2: Build wireguard-go (cached separately from Rust builds) +# Stage 3: Slim runtime with only the final binaries -# --- Build stage --- +# --- Rust build stage --- FROM rust:latest AS builder WORKDIR /usr/src/nym @@ -13,12 +14,28 @@ ENV CARGO_BUILD_JOBS=8 RUN cargo build --release --locked -p nym-node --features otel && \ cargo build --release --locked -p nym-network-requester -p nym-socks5-client +# --- wireguard-go build stage (cached independently) --- +FROM debian:trixie-slim AS wireguard-builder + +ARG TARGETARCH + +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl git build-essential ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL "https://go.dev/dl/go1.23.6.linux-${TARGETARCH}.tar.gz" \ + | tar -C /usr/local -xz + +ENV PATH="/usr/local/go/bin:$PATH" + +RUN git clone https://git.zx2c4.com/wireguard-go && \ + cd wireguard-go && make + # --- Runtime stage --- FROM debian:trixie-slim RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ - build-essential \ python3 \ python3-pip \ netcat-openbsd \ @@ -26,27 +43,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ iproute2 \ net-tools \ wireguard-tools \ - git \ iptables \ - curl \ && rm -rf /var/lib/apt/lists/* -# Install Go and build wireguard-go, then clean up -ARG TARGETARCH -RUN curl -fsSL "https://go.dev/dl/go1.23.6.linux-${TARGETARCH}.tar.gz" \ - | tar -C /usr/local -xz && \ - export PATH="/usr/local/go/bin:$PATH" && \ - git clone https://git.zx2c4.com/wireguard-go && \ - cd wireguard-go && \ - make && \ - cp wireguard-go /usr/local/bin/ && \ - cd .. && \ - rm -rf wireguard-go /usr/local/go && \ - apt-get purge -y --auto-remove build-essential curl - RUN pip3 install --break-system-packages base58 -# Copy only the compiled binaries from the builder stage +# Copy wireguard-go from its dedicated build stage +COPY --from=wireguard-builder /wireguard-go/wireguard-go /usr/local/bin/ + +# Copy only the compiled binaries from the Rust builder stage COPY --from=builder /usr/src/nym/target/release/nym-node /usr/local/bin/ COPY --from=builder /usr/src/nym/target/release/nym-network-requester /usr/local/bin/ COPY --from=builder /usr/src/nym/target/release/nym-socks5-client /usr/local/bin/ diff --git a/docker/localnet/loadtest.sh b/docker/localnet/loadtest.sh index e76b956ad6..fa0eb609e1 100755 --- a/docker/localnet/loadtest.sh +++ b/docker/localnet/loadtest.sh @@ -131,7 +131,11 @@ pick_url() { # Millisecond timestamp (works on both GNU and BSD/macOS date) now_ms() { - python3 -c 'import time; print(int(time.time()*1000))' + if command -v python3 &>/dev/null; then + python3 -c 'import time; print(int(time.time()*1000))' + else + echo $(($(date +%s) * 1000)) + fi } # Worker function: runs requests in a loop until duration expires diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index 175b3022f5..be68f15e14 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -59,6 +59,11 @@ pub(crate) struct Cli { #[clap(long, env = "NYMNODE_OTEL_KEY")] pub(crate) otel_key: Option, + /// Deployment environment label attached to all exported traces. + /// Used to distinguish sandbox / mainnet / canary in the OTel backend. + #[clap(long, env = "NYMNODE_OTEL_ENV", default_value = "mainnet")] + pub(crate) otel_env: String, + #[clap(subcommand)] command: Commands, } @@ -71,26 +76,19 @@ impl Cli { // Set up tracing inside the runtime so the OTel batch exporter // can spawn its background tasks on the tokio reactor. - let _otel_guard = runtime.block_on(async { - self.setup_logging() - })?; + let _otel_guard = runtime.block_on(async { self.setup_logging() })?; - let result = runtime.block_on(async { + // `_otel_guard` is dropped at function exit, flushing pending spans via its Drop impl + runtime.block_on(async { match self.command { Commands::BuildInfo(args) => build_info::execute(args)?, - Commands::BondingInformation(args) => { - bonding_information::execute(args).await? - } - Commands::NodeDetails(args) => { - node_details::execute(args).await? - } + Commands::BondingInformation(args) => bonding_information::execute(args).await?, + Commands::NodeDetails(args) => node_details::execute(args).await?, Commands::Run(args) => run::execute(*args).await?, Commands::Migrate(args) => migrate::execute(*args)?, Commands::Sign(args) => sign::execute(args).await?, Commands::TestThroughput(args) => test_throughput::execute(args)?, - Commands::UnsafeResetSphinxKeys(args) => { - reset_sphinx_keys::execute(args).await? - } + Commands::UnsafeResetSphinxKeys(args) => reset_sphinx_keys::execute(args).await?, Commands::Debug(debug) => match debug.command { DebugCommands::ResetProvidersGatewayDbs(args) => { debug::reset_providers_dbs::execute(args).await? @@ -98,17 +96,7 @@ impl Cli { }, } Ok::<(), anyhow::Error>(()) - }); - - // Flush any pending OTel spans before exit - #[cfg(feature = "otel")] - if let Some(guard) = _otel_guard { - if let Err(e) = guard.provider.shutdown() { - eprintln!("OpenTelemetry shutdown error: {e}"); - } - } - - result + }) } #[cfg(feature = "otel")] @@ -121,6 +109,7 @@ impl Cli { endpoint: self.otel_endpoint.clone(), service_name: "nym-node".to_string(), ingestion_key: self.otel_key.clone(), + environment: self.otel_env.clone(), }) } else { None @@ -138,6 +127,7 @@ impl Cli { endpoint: self.otel_endpoint.clone(), service_name: "nym-node".to_string(), ingestion_key: self.otel_key.clone(), + environment: self.otel_env.clone(), }) } else { None diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index c36e1d776c..dd14b1b780 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -14,14 +14,25 @@ pub(crate) struct OtelConfig { pub endpoint: String, pub service_name: String, pub ingestion_key: Option, + pub environment: String, } -/// Handle returned when OTel is active so the caller can trigger a graceful shutdown. +/// Handle returned when OTel is active. Flushes pending spans on drop +/// to prevent telemetry loss during panics or early exits. #[cfg(feature = "otel")] pub(crate) struct OtelGuard { pub provider: opentelemetry_sdk::trace::SdkTracerProvider, } +#[cfg(feature = "otel")] +impl Drop for OtelGuard { + fn drop(&mut self) { + if let Err(e) = self.provider.shutdown() { + eprintln!("OpenTelemetry shutdown error in Drop: {e}"); + } + } +} + pub(crate) fn granual_filtered_env() -> anyhow::Result { fn directive_checked(directive: impl Into) -> anyhow::Result { directive.into().parse().map_err(From::from) @@ -43,9 +54,7 @@ pub(crate) fn granual_filtered_env() -> anyhow::Result { /// OTLP exporter layer is added and the returned `OtelGuard` must be used to /// flush pending spans on shutdown. #[cfg(feature = "otel")] -pub(crate) fn setup_tracing_logger( - otel: Option, -) -> anyhow::Result> { +pub(crate) fn setup_tracing_logger(otel: Option) -> anyhow::Result> { let stderr_layer = default_tracing_fmt_layer(std::io::stderr).with_filter(granual_filtered_env()?); @@ -57,7 +66,12 @@ pub(crate) fn setup_tracing_logger( &otel_config.service_name, &otel_config.endpoint, otel_config.ingestion_key.as_deref(), - ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; + &otel_config.environment, + ).map_err(|e| anyhow::anyhow!( + "failed to initialise OpenTelemetry exporter (endpoint: {}, service: {}): {e}", + otel_config.endpoint, + otel_config.service_name, + ))?; tracing_subscriber::registry() .with(console_layer) @@ -80,7 +94,12 @@ pub(crate) fn setup_tracing_logger( &otel_config.service_name, &otel_config.endpoint, otel_config.ingestion_key.as_deref(), - ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; + &otel_config.environment, + ).map_err(|e| anyhow::anyhow!( + "failed to initialise OpenTelemetry exporter (endpoint: {}, service: {}): {e}", + otel_config.endpoint, + otel_config.service_name, + ))?; tracing_subscriber::registry() .with(stderr_layer) diff --git a/nym-node/src/node/mixnet/handler.rs b/nym-node/src/node/mixnet/handler.rs index ae0793cebd..e644a2c5d7 100644 --- a/nym-node/src/node/mixnet/handler.rs +++ b/nym-node/src/node/mixnet/handler.rs @@ -1,6 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only +use crate::node::key_rotation::active_keys::SphinxKeyGuard; use crate::node::mixnet::shared::SharedData; use futures::StreamExt; use nym_noise::connection::Connection; @@ -20,7 +21,10 @@ use std::net::SocketAddr; use tokio::net::TcpStream; use tokio::time::Instant; use tokio_util::codec::Framed; -use tracing::{debug, error, instrument, trace, warn, Span}; +use tracing::{Span, debug, error, instrument, trace, warn}; + +/// How often (in packets) the stream-level span updates its packet count. +const SPAN_UPDATE_INTERVAL: u64 = 10_000; struct PendingReplayCheckPackets { // map of rotation id used for packet creation to the packets @@ -140,7 +144,7 @@ impl ConnectionHandler { level = "debug", fields( remote_addr = %self.remote_address, - delay_ms, + delay_ms = tracing::field::Empty, ) )] fn handle_forward_packet(&self, now: Instant, mix_packet: MixPacket, delay: Option) { @@ -155,12 +159,12 @@ impl ConnectionHandler { } let forward_instant = self.create_delay_target(now, delay); - Span::current().record( - "delay_ms", - forward_instant - .map(|i| i.saturating_duration_since(now).as_millis() as u64) - .unwrap_or(0), - ); + if let Some(target) = forward_instant { + Span::current().record( + "delay_ms", + target.saturating_duration_since(now).as_millis() as u64, + ); + } self.shared.forward_mix_packet(mix_packet, forward_instant); } @@ -273,29 +277,59 @@ impl ConnectionHandler { time_threshold && count_threshold } - #[instrument( - name = "mixnode.sphinx_partial_unwrap", - skip(self, packet), - level = "debug", - fields( - key_rotation, - unwrap_result, - ) - )] - fn try_partially_unwrap_packet( + /// Resolve the sphinx key for the given rotation, recording the rotation + /// label on the current tracing span. Returns `ExpiredKey` if the requested + /// odd/even key has already been rotated out. + fn resolve_rotation_key( &self, - packet: FramedNymPacket, - ) -> Result { - let rotation_label = match packet.header().key_rotation { + rotation: SphinxKeyRotation, + ) -> Result { + let rotation_label = match rotation { SphinxKeyRotation::Unknown => "unknown", SphinxKeyRotation::OddRotation => "odd", SphinxKeyRotation::EvenRotation => "even", }; Span::current().record("key_rotation", rotation_label); - let result = match packet.header().key_rotation { + match rotation { + SphinxKeyRotation::Unknown => Ok(self.shared.sphinx_keys.primary()), + SphinxKeyRotation::OddRotation => self.shared.sphinx_keys.odd().ok_or_else(|| { + warn!( + event = "packet.dropped.expired_key", + key_rotation = "odd", + remote_addr = %self.remote_address, + "dropping packet: odd key rotation expired" + ); + PacketProcessingError::ExpiredKey + }), + SphinxKeyRotation::EvenRotation => self.shared.sphinx_keys.even().ok_or_else(|| { + warn!( + event = "packet.dropped.expired_key", + key_rotation = "even", + remote_addr = %self.remote_address, + "dropping packet: even key rotation expired" + ); + PacketProcessingError::ExpiredKey + }), + } + } + + #[instrument( + name = "mixnode.sphinx_partial_unwrap", + skip(self, packet), + level = "debug", + fields(key_rotation, unwrap_result,) + )] + fn try_partially_unwrap_packet( + &self, + packet: FramedNymPacket, + ) -> Result { + let rotation = packet.header().key_rotation; + + let result = match rotation { SphinxKeyRotation::Unknown => { - let primary = self.shared.sphinx_keys.primary(); + // Unknown rotation: try primary, fallback to secondary + let primary = self.resolve_rotation_key(rotation)?; let primary_rotation = primary.rotation_id(); match PartiallyUnwrappedPacket::new(packet, primary.inner().as_ref()) { @@ -314,35 +348,12 @@ impl ConnectionHandler { } } } - SphinxKeyRotation::OddRotation => { - let Some(odd_key) = self.shared.sphinx_keys.odd() else { - warn!( - event = "packet.dropped.expired_key", - key_rotation = "odd", - remote_addr = %self.remote_address, - "dropping packet: odd key rotation expired" - ); - return Err(PacketProcessingError::ExpiredKey); - }; - let odd_rotation = odd_key.rotation_id(); - PartiallyUnwrappedPacket::new(packet, odd_key.inner().as_ref()) + _ => { + let key = self.resolve_rotation_key(rotation)?; + let rotation_id = key.rotation_id(); + PartiallyUnwrappedPacket::new(packet, key.inner().as_ref()) .map_err(|(_, err)| err) - .map(|p| p.with_key_rotation(odd_rotation)) - } - SphinxKeyRotation::EvenRotation => { - let Some(even_key) = self.shared.sphinx_keys.even() else { - warn!( - event = "packet.dropped.expired_key", - key_rotation = "even", - remote_addr = %self.remote_address, - "dropping packet: even key rotation expired" - ); - return Err(PacketProcessingError::ExpiredKey); - }; - let even_rotation = even_key.rotation_id(); - PartiallyUnwrappedPacket::new(packet, even_key.inner().as_ref()) - .map_err(|(_, err)| err) - .map(|p| p.with_key_rotation(even_rotation)) + .map(|p| p.with_key_rotation(rotation_id)) } }; @@ -487,10 +498,7 @@ impl ConnectionHandler { name = "mixnode.replay_check_batch", skip(self), level = "debug", - fields( - batch_size, - mutex_wait_ms, - ) + fields(batch_size, mutex_wait_ms,) )] async fn handle_pending_packets_batch(&mut self, now: Instant) { let batch_size = self.pending_packets.total_count(); @@ -513,10 +521,7 @@ impl ConnectionHandler { self.shared.shutdown_token.cancel(); return; }; - Span::current().record( - "mutex_wait_ms", - mutex_start.elapsed().as_millis() as u64, - ); + Span::current().record("mutex_wait_ms", mutex_start.elapsed().as_millis() as u64); self.handle_post_replay_detection_packets(now, batch, replay_check_results) .await; @@ -532,42 +537,8 @@ impl ConnectionHandler { &self, packet: FramedNymPacket, ) -> Result { - let rotation_label = match packet.header().key_rotation { - SphinxKeyRotation::Unknown => "unknown", - SphinxKeyRotation::OddRotation => "odd", - SphinxKeyRotation::EvenRotation => "even", - }; - Span::current().record("key_rotation", rotation_label); - - match packet.header().key_rotation { - SphinxKeyRotation::Unknown => { - process_framed_packet(packet, self.shared.sphinx_keys.primary().inner().as_ref()) - } - SphinxKeyRotation::OddRotation => { - let Some(odd_key) = self.shared.sphinx_keys.odd() else { - warn!( - event = "packet.dropped.expired_key", - key_rotation = "odd", - remote_addr = %self.remote_address, - "dropping packet: odd key rotation expired" - ); - return Err(PacketProcessingError::ExpiredKey); - }; - process_framed_packet(packet, odd_key.inner().as_ref()) - } - SphinxKeyRotation::EvenRotation => { - let Some(even_key) = self.shared.sphinx_keys.even() else { - warn!( - event = "packet.dropped.expired_key", - key_rotation = "even", - remote_addr = %self.remote_address, - "dropping packet: even key rotation expired" - ); - return Err(PacketProcessingError::ExpiredKey); - }; - process_framed_packet(packet, even_key.inner().as_ref()) - } - } + let key = self.resolve_rotation_key(packet.header().key_rotation)?; + process_framed_packet(packet, key.inner().as_ref()) } async fn handle_received_packet_with_no_replay_detection( @@ -602,7 +573,7 @@ impl ConnectionHandler { level = "debug", fields( remote = %self.remote_address, - noise_handshake_ms, + noise_handshake_ms = tracing::field::Empty, ) )] pub(crate) async fn handle_connection(&mut self, socket: TcpStream) { @@ -663,7 +634,7 @@ impl ConnectionHandler { Some(Ok(packet)) => { self.handle_received_nym_packet(packet).await; packets_processed += 1; - if packets_processed % 10000 == 0 { + if packets_processed.is_multiple_of(SPAN_UPDATE_INTERVAL) { Span::current().record("packets_processed", packets_processed); } } diff --git a/nym-node/src/node/mixnet/packet_forwarding/mod.rs b/nym-node/src/node/mixnet/packet_forwarding/mod.rs index 3a887284f2..ef695a05eb 100644 --- a/nym-node/src/node/mixnet/packet_forwarding/mod.rs +++ b/nym-node/src/node/mixnet/packet_forwarding/mod.rs @@ -155,7 +155,7 @@ impl PacketForwarder { self.handle_new_packet(new_packet.unwrap()); let channel_len = self.packet_sender.len(); let delay_queue_len = self.delay_queue.len(); - if processed % 1000 == 0 { + if processed.is_multiple_of(1000) { match channel_len { n if n > 1000 => error!( event = "forwarder.queue_overload", diff --git a/nym-node/src/node/mixnet/shared/final_hop.rs b/nym-node/src/node/mixnet/shared/final_hop.rs index 0f852707f5..0235617342 100644 --- a/nym-node/src/node/mixnet/shared/final_hop.rs +++ b/nym-node/src/node/mixnet/shared/final_hop.rs @@ -38,11 +38,13 @@ impl SharedFinalHopData { Err(message) } Some(sender_channel) => { + let send_start = Instant::now(); if let Err(unsent) = sender_channel.unbounded_send(vec![message]) { warn!( event = "gateway.push_to_client", client_found = true, send_result = "channel_closed", + send_us = send_start.elapsed().as_micros() as u64, "client {client_address} channel closed, message not delivered" ); // the unwrap here is fine as the original message got returned; @@ -54,6 +56,7 @@ impl SharedFinalHopData { event = "gateway.push_to_client", client_found = true, send_result = "ok", + send_us = send_start.elapsed().as_micros() as u64, "pushed message to client {client_address}" ); Ok(()) @@ -74,14 +77,12 @@ impl SharedFinalHopData { if result.is_ok() { debug!( event = "gateway.disk_store", - store_ms, - "stored message for {client_address} on disk in {store_ms}ms" + store_ms, "stored message for {client_address} on disk in {store_ms}ms" ); } else { warn!( event = "gateway.disk_store_failed", - store_ms, - "failed to store message for {client_address} on disk after {store_ms}ms" + store_ms, "failed to store message for {client_address} on disk after {store_ms}ms" ); } result diff --git a/nym-node/src/node/mixnet/shared/mod.rs b/nym-node/src/node/mixnet/shared/mod.rs index 13a664772c..19b15d9116 100644 --- a/nym-node/src/node/mixnet/shared/mod.rs +++ b/nym-node/src/node/mixnet/shared/mod.rs @@ -190,15 +190,14 @@ impl SharedData { .mixnet_forwarder .forward_packet(PacketToForward::new(packet, delay_until)) .is_err() + && !self.shutdown_token.is_cancelled() { - if !self.shutdown_token.is_cancelled() { - error!( - event = "forwarder.channel_send_failed", - has_delay, - "failed to forward sphinx packet on the channel while the process is not going through the shutdown!" - ); - self.shutdown_token.cancel(); - } + error!( + event = "forwarder.channel_send_failed", + has_delay, + "failed to forward sphinx packet on the channel while the process is not going through the shutdown!" + ); + self.shutdown_token.cancel(); } } From d28d0ac39ef625c7e229933756c9b55313b4efc5 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 19:42:24 +0100 Subject: [PATCH 15/51] fix replay batch drop, harden error handling and scripts --- common/bin-common/src/logging/mod.rs | 16 +++++++++++--- common/nym-lp/src/message.rs | 25 +++++++++++++--------- docker/localnet/Dockerfile.localnet | 6 ++++-- docker/localnet/loadtest.sh | 12 +++++++++++ docker/localnet/otel-report.sh | 17 +++++++++++++++ nym-node/src/cli/mod.rs | 31 +++++++++++----------------- nym-node/src/logging.rs | 7 +++++++ nym-node/src/node/mixnet/handler.rs | 8 +++---- 8 files changed, 84 insertions(+), 38 deletions(-) diff --git a/common/bin-common/src/logging/mod.rs b/common/bin-common/src/logging/mod.rs index fe0848cc3e..247ce5e1bc 100644 --- a/common/bin-common/src/logging/mod.rs +++ b/common/bin-common/src/logging/mod.rs @@ -96,9 +96,16 @@ where { use opentelemetry::trace::TracerProvider as _; use opentelemetry_otlp::WithExportConfig; - use opentelemetry_otlp::WithTonicConfig; + // Validate endpoint URI early to fail with a clear message + if !endpoint.starts_with("http://") && !endpoint.starts_with("https://") { + return Err(format!( + "invalid OTLP endpoint URI: {endpoint} (must start with http:// or https://)" + ) + .into()); + } + let mut builder = opentelemetry_otlp::SpanExporter::builder() .with_tonic() .with_endpoint(endpoint); @@ -113,12 +120,15 @@ where let mut metadata = tonic::metadata::MetadataMap::new(); metadata.insert( "signoz-ingestion-key", - key.parse().map_err(|_| "invalid ingestion key value")?, + key.parse() + .map_err(|_| "invalid ingestion key format (value redacted)")?, ); builder = builder.with_metadata(metadata); } - let exporter = builder.build()?; + let exporter = builder + .build() + .map_err(|e| format!("failed to build OTLP exporter for endpoint {endpoint}: {e}"))?; let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() .with_batch_exporter(exporter) diff --git a/common/nym-lp/src/message.rs b/common/nym-lp/src/message.rs index aa58476997..38431783cc 100644 --- a/common/nym-lp/src/message.rs +++ b/common/nym-lp/src/message.rs @@ -374,9 +374,11 @@ impl ForwardPacketData { bytes.len() ))); } - // SAFETY: we ensured we have sufficient data - #[allow(clippy::unwrap_used)] - let target_gateway_identity = bytes[0..32].try_into().unwrap(); + // length >= 43 guaranteed above, so bytes[0..32] is always 32 bytes + #[allow(clippy::expect_used)] + let target_gateway_identity: [u8; 32] = bytes[0..32] + .try_into() + .expect("length check above guarantees at least 43 bytes"); let target_lp_address_is_ipv6 = bytes[32] != 0; let (target_lp_address, next_index) = if target_lp_address_is_ipv6 { @@ -389,18 +391,21 @@ impl ForwardPacketData { bytes.len() ))); } - // SAFETY: we ensured we have sufficient data, and the length is correct for casting - #[allow(clippy::unwrap_used)] + // length >= 55 guaranteed above, so bytes[33..49] is always 16 bytes + #[allow(clippy::expect_used)] let ipv6 = IpAddr::V6(Ipv6Addr::from( - <[u8; 16]>::try_from(&bytes[33..49]).unwrap(), + <[u8; 16]>::try_from(&bytes[33..49]) + .expect("length check above guarantees at least 55 bytes"), )); let port = u16::from_le_bytes([bytes[49], bytes[50]]); (SocketAddr::new(ipv6, port), 51) } else { - // IPv4. Length check done at the start - // SAFETY: we ensured we have sufficient data, and the length is correct for casting - #[allow(clippy::unwrap_used)] - let ipv4 = IpAddr::V4(Ipv4Addr::from(<[u8; 4]>::try_from(&bytes[33..37]).unwrap())); + // length >= 43 guaranteed at function entry, so bytes[33..37] is always 4 bytes + #[allow(clippy::expect_used)] + let ipv4 = IpAddr::V4(Ipv4Addr::from( + <[u8; 4]>::try_from(&bytes[33..37]) + .expect("length check at entry guarantees at least 43 bytes"), + )); let port = u16::from_le_bytes([bytes[37], bytes[38]]); (SocketAddr::new(ipv4, port), 39) }; diff --git a/docker/localnet/Dockerfile.localnet b/docker/localnet/Dockerfile.localnet index f10f044c92..63829c77a3 100644 --- a/docker/localnet/Dockerfile.localnet +++ b/docker/localnet/Dockerfile.localnet @@ -11,8 +11,10 @@ COPY ./ ./ ENV CARGO_BUILD_JOBS=8 -RUN cargo build --release --locked -p nym-node --features otel && \ - cargo build --release --locked -p nym-network-requester -p nym-socks5-client +RUN cargo build --release --locked \ + -p nym-node --features otel \ + -p nym-network-requester \ + -p nym-socks5-client # --- wireguard-go build stage (cached independently) --- FROM debian:trixie-slim AS wireguard-builder diff --git a/docker/localnet/loadtest.sh b/docker/localnet/loadtest.sh index fa0eb609e1..adfc5902a6 100755 --- a/docker/localnet/loadtest.sh +++ b/docker/localnet/loadtest.sh @@ -24,6 +24,18 @@ set -e +check_dependencies() { + local missing=() + for cmd in curl jq bc; do + command -v "$cmd" &>/dev/null || missing+=("$cmd") + done + if [ ${#missing[@]} -gt 0 ]; then + echo "ERROR: missing required commands: ${missing[*]}" >&2 + exit 1 + fi +} +check_dependencies + CONCURRENCY=10 DURATION=60 PROXY="socks5h://127.0.0.1:1080" diff --git a/docker/localnet/otel-report.sh b/docker/localnet/otel-report.sh index 7df437979b..7930603382 100755 --- a/docker/localnet/otel-report.sh +++ b/docker/localnet/otel-report.sh @@ -13,6 +13,18 @@ set -e +check_dependencies() { + local missing=() + for cmd in docker jq; do + command -v "$cmd" &>/dev/null || missing+=("$cmd") + done + if [ ${#missing[@]} -gt 0 ]; then + echo "ERROR: missing required commands: ${missing[*]}" >&2 + exit 1 + fi +} +check_dependencies + CH_CONTAINER="signoz-clickhouse" TRACES_TABLE="signoz_traces.distributed_signoz_index_v3" LOOKBACK_MIN=${1:-15} @@ -31,6 +43,11 @@ BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' +if ! docker ps --filter "name=$CH_CONTAINER" --format '{{.Names}}' | grep -q "$CH_CONTAINER"; then + echo "ERROR: ClickHouse container '$CH_CONTAINER' not running. Is SigNoz started?" >&2 + exit 1 +fi + ch() { docker exec "$CH_CONTAINER" clickhouse-client --query "$1" 2>/dev/null } diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index be68f15e14..ceb4ca7a61 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -99,12 +99,8 @@ impl Cli { }) } - #[cfg(feature = "otel")] - fn setup_logging(&self) -> anyhow::Result> { - if matches!(self.command, Commands::TestThroughput(..)) { - return Ok(None); - } - let otel_config = if self.otel { + fn build_otel_config(&self) -> Option { + if self.otel { Some(crate::logging::OtelConfig { endpoint: self.otel_endpoint.clone(), service_name: "nym-node".to_string(), @@ -113,8 +109,15 @@ impl Cli { }) } else { None - }; - crate::logging::setup_tracing_logger(otel_config) + } + } + + #[cfg(feature = "otel")] + fn setup_logging(&self) -> anyhow::Result> { + if matches!(self.command, Commands::TestThroughput(..)) { + return Ok(None); + } + crate::logging::setup_tracing_logger(self.build_otel_config()) } #[cfg(not(feature = "otel"))] @@ -122,17 +125,7 @@ impl Cli { if matches!(self.command, Commands::TestThroughput(..)) { return Ok(None); } - let otel_config = if self.otel { - Some(crate::logging::OtelConfig { - endpoint: self.otel_endpoint.clone(), - service_name: "nym-node".to_string(), - ingestion_key: self.otel_key.clone(), - environment: self.otel_env.clone(), - }) - } else { - None - }; - crate::logging::setup_tracing_logger(otel_config)?; + crate::logging::setup_tracing_logger(self.build_otel_config())?; Ok(None) } } diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index dd14b1b780..31be976f31 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -11,9 +11,16 @@ use tracing_subscriber::{EnvFilter, Layer}; /// Fields are only read when the `otel` feature is enabled. #[allow(dead_code)] pub(crate) struct OtelConfig { + /// OTLP/gRPC collector endpoint, e.g. `http://localhost:4317` + /// or `https://ingest.eu.signoz.cloud:443` for SigNoz Cloud. pub endpoint: String, + /// Service name reported to the collector (appears in SigNoz "Services" view). pub service_name: String, + /// Optional SigNoz Cloud ingestion key for authenticated export. + /// Sent as the `signoz-ingestion-key` gRPC metadata header. pub ingestion_key: Option, + /// Deployment environment label, e.g. `mainnet`, `sandbox`, `canary`. + /// Attached as the `deployment.environment` OTel resource attribute. pub environment: String, } diff --git a/nym-node/src/node/mixnet/handler.rs b/nym-node/src/node/mixnet/handler.rs index e644a2c5d7..12ed41b638 100644 --- a/nym-node/src/node/mixnet/handler.rs +++ b/nym-node/src/node/mixnet/handler.rs @@ -501,15 +501,14 @@ impl ConnectionHandler { fields(batch_size, mutex_wait_ms,) )] async fn handle_pending_packets_batch(&mut self, now: Instant) { - let batch_size = self.pending_packets.total_count(); - Span::current().record("batch_size", batch_size as u64); - - let batch = self.pending_packets.reset(now); let replay_tags = self.pending_packets.replay_tags(); if replay_tags.is_empty() { return; } + let batch_size = self.pending_packets.total_count(); + Span::current().record("batch_size", batch_size as u64); + let mutex_start = Instant::now(); let Ok(replay_check_results) = self .shared @@ -523,6 +522,7 @@ impl ConnectionHandler { }; Span::current().record("mutex_wait_ms", mutex_start.elapsed().as_millis() as u64); + let batch = self.pending_packets.reset(now); self.handle_post_replay_detection_packets(now, batch, replay_check_results) .await; } From 260f8e971476f113ec124f28107a1a6445b30e81 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 17 Feb 2026 08:37:49 +0100 Subject: [PATCH 16/51] revert docker/localnet to develop; localnet work to follow in separate PR --- docker/localnet/Dockerfile.localnet | 61 +++--- docker/localnet/README.md | 140 +++---------- docker/localnet/loadtest.sh | 313 ---------------------------- docker/localnet/localnet-logs.sh | 21 +- docker/localnet/localnet.sh | 193 +++++++---------- docker/localnet/otel-report.sh | 239 --------------------- 6 files changed, 133 insertions(+), 834 deletions(-) delete mode 100755 docker/localnet/loadtest.sh delete mode 100755 docker/localnet/otel-report.sh diff --git a/docker/localnet/Dockerfile.localnet b/docker/localnet/Dockerfile.localnet index 63829c77a3..a6eb260f1a 100644 --- a/docker/localnet/Dockerfile.localnet +++ b/docker/localnet/Dockerfile.localnet @@ -1,43 +1,22 @@ -# Multi-stage Dockerfile for Nym localnet -# Stage 1: Build Rust binaries -# Stage 2: Build wireguard-go (cached separately from Rust builds) -# Stage 3: Slim runtime with only the final binaries +# Single-stage Dockerfile for Nym localnet +# Builds: nym-node, nym-network-requester, nym-socks5-client +# Target: Apple Container Runtime with host networking -# --- Rust build stage --- -FROM rust:latest AS builder +FROM rust:latest WORKDIR /usr/src/nym COPY ./ ./ ENV CARGO_BUILD_JOBS=8 +# Build all required binaries in release mode RUN cargo build --release --locked \ - -p nym-node --features otel \ + -p nym-node \ -p nym-network-requester \ -p nym-socks5-client -# --- wireguard-go build stage (cached independently) --- -FROM debian:trixie-slim AS wireguard-builder - -ARG TARGETARCH - -RUN apt-get update && apt-get install -y --no-install-recommends \ - curl git build-essential ca-certificates \ - && rm -rf /var/lib/apt/lists/* - -RUN curl -fsSL "https://go.dev/dl/go1.23.6.linux-${TARGETARCH}.tar.gz" \ - | tar -C /usr/local -xz - -ENV PATH="/usr/local/go/bin:$PATH" - -RUN git clone https://git.zx2c4.com/wireguard-go && \ - cd wireguard-go && make - -# --- Runtime stage --- -FROM debian:trixie-slim - -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates \ +# Install runtime dependencies including Go for wireguard-go +RUN apt update && apt install -y \ python3 \ python3-pip \ netcat-openbsd \ @@ -45,21 +24,31 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ iproute2 \ net-tools \ wireguard-tools \ + golang-go \ + git \ iptables \ && rm -rf /var/lib/apt/lists/* +# Install wireguard-go (userspace WireGuard implementation) +RUN git clone https://git.zx2c4.com/wireguard-go && \ + cd wireguard-go && \ + make && \ + cp wireguard-go /usr/local/bin/ && \ + cd .. && \ + rm -rf wireguard-go + +# Install Python dependencies for build_topology.py RUN pip3 install --break-system-packages base58 -# Copy wireguard-go from its dedicated build stage -COPY --from=wireguard-builder /wireguard-go/wireguard-go /usr/local/bin/ - -# Copy only the compiled binaries from the Rust builder stage -COPY --from=builder /usr/src/nym/target/release/nym-node /usr/local/bin/ -COPY --from=builder /usr/src/nym/target/release/nym-network-requester /usr/local/bin/ -COPY --from=builder /usr/src/nym/target/release/nym-socks5-client /usr/local/bin/ +# Move binaries to /usr/local/bin for easy access +RUN cp target/release/nym-node /usr/local/bin/ && \ + cp target/release/nym-network-requester /usr/local/bin/ && \ + cp target/release/nym-socks5-client /usr/local/bin/ +# Copy supporting scripts COPY ./docker/localnet/build_topology.py /usr/local/bin/ WORKDIR /nym +# Default command CMD ["nym-node", "--help"] diff --git a/docker/localnet/README.md b/docker/localnet/README.md index 84c52cb1b9..415b38e01b 100644 --- a/docker/localnet/README.md +++ b/docker/localnet/README.md @@ -1,71 +1,35 @@ -# Nym Localnet +# Nym Localnet for Kata Container Runtimes -A complete Nym mixnet test environment with OpenTelemetry instrumentation. -Supports both Docker Desktop and Apple Container Runtime on macOS. +A complete Nym mixnet test environment running on Apple's container runtime for macOS (for now). ## Overview This localnet setup provides a fully functional Nym mixnet for local development and testing: - **3 mixnodes** (layer 1, 2, 3) -- **2 gateways** (entry + exit mode) +- **1 gateway** (entry + exit mode) - **1 network-requester** (service provider) - **1 SOCKS5 client** -- **OpenTelemetry tracing** via OTLP/gRPC to SigNoz (or any OTLP collector) All components run in isolated containers with proper networking and dynamic IP resolution. -When the `otel` feature is enabled (default), every nym-node exports traces covering -the full packet lifecycle: ingress, Sphinx processing, forwarding, and final-hop delivery. ## Prerequisites ### Required - **macOS** (tested on macOS Sequoia 15.0+) -- **Docker Desktop** (recommended) or **Apple Container Runtime** +- **Apple Container Runtime** - Built into macOS +- **Docker Desktop** (for building images only) - **Python 3** with `base58` library -### SigNoz (for trace viewing) - -SigNoz is an open-source APM that receives and visualises OpenTelemetry data. -Install it locally with Docker Compose -- this takes about 2 minutes: - -```bash -# Clone the SigNoz repository -git clone -b main https://github.com/SigNoz/signoz.git ~/signoz -cd ~/signoz/deploy - -# Start SigNoz (runs ClickHouse, otel-collector, query-service, frontend) -docker compose up -d - -# Verify it is running -docker ps --filter "name=signoz" --format "table {{.Names}}\t{{.Status}}" -``` - -Once running: -- **SigNoz UI**: http://localhost:8080 -- **OTLP gRPC collector**: localhost:4317 (used by nym-nodes) -- **OTLP HTTP collector**: localhost:4318 - -The localnet script auto-detects the SigNoz Docker network (`signoz-net`) and -routes OTel traffic directly to the collector container -- no manual endpoint -configuration needed. - -To stop SigNoz later: -```bash -cd ~/signoz/deploy && docker compose down -``` - ### Installation ```bash # Install Python dependencies pip3 install --break-system-packages base58 -# Verify Docker is installed -docker --version -``` - -If using Apple Container Runtime instead of Docker: -```bash +# Verify container runtime is available container --version + +# Verify Docker is installed (for building) +docker --version ``` ## Quick Start @@ -234,99 +198,54 @@ container logs nym-gateway --follow ### Status ```bash # List all containers -docker ps --filter "name=nym-" --format "table {{.Names}}\t{{.Status}}" +container list # Check specific container -docker logs nym-gateway +container logs nym-gateway # Inspect network -docker network inspect nym-localnet-network +container network inspect nym-localnet-network ``` ## Testing ### Basic SOCKS5 Test ```bash -# Simple HTTP request through the mixnet -curl -x socks5h://127.0.0.1:1080 https://httpbin.org/get +# Simple HTTP request with redirect following +curl -L --socks5 localhost:1080 http://example.com # HTTPS request -curl -x socks5h://127.0.0.1:1080 https://nymtech.net +curl -L --socks5 localhost:1080 https://nymtech.net # Download a file -curl -x socks5h://127.0.0.1:1080 \ +curl -L --socks5 localhost:1080 \ https://test-download-files-nym.s3.amazonaws.com/download-files/1MB.zip \ --output /tmp/test.zip ``` -### Load Testing - -A load test script is included to generate sustained traffic and populate SigNoz -with meaningful trace data: - -```bash -# Default: 10 concurrent workers, 60 seconds -./loadtest.sh - -# Heavier load: 20 workers for 2 minutes -./loadtest.sh -c 20 -d 120 - -# Light single-threaded test -./loadtest.sh -c 1 -d 10 - -# Target a specific URL -./loadtest.sh -c 5 -d 30 -u https://httpbin.org/bytes/4096 -``` - -The script reports live progress, then prints a summary with request counts, -throughput, and latency percentiles (p50/p95/p99). - ### Verify Network Topology ```bash # View the generated topology -docker exec nym-gateway cat /localnet/network.json | jq . +container exec nym-gateway cat /localnet/network.json | jq . -# Check container status -docker ps --filter "name=nym-" --format "table {{.Names}}\t{{.Status}}" +# Check container IPs +container list | grep nym- # Verify all bonding files exist -docker exec nym-gateway ls -la /localnet/ +container exec nym-gateway ls -la /localnet/ ``` ### Test Mixnet Routing ```bash -# All traffic flows through: client -> gateway -> mix1 -> mix2 -> mix3 -> gateway -> internet +# All traffic flows through: client → mix1 → mix2 → mix3 → gateway → internet # Watch logs to verify routing: -docker logs nym-mixnode1 --follow & -docker logs nym-mixnode2 --follow & -docker logs nym-mixnode3 --follow & -docker logs nym-gateway --follow & +container logs nym-mixnode1 --follow & +container logs nym-mixnode2 --follow & +container logs nym-mixnode3 --follow & +container logs nym-gateway --follow & # Make a request -curl -x socks5h://127.0.0.1:1080 https://nymtech.net -``` - -## OpenTelemetry - -OTel is enabled by default. Each nym-node exports traces via OTLP/gRPC covering -packet ingress, Sphinx processing, forwarding, and final-hop delivery. - -### Viewing Traces - -- **SigNoz UI**: http://localhost:8080 -- filter by `serviceName = nym-node` -- **Terminal report** (queries ClickHouse directly, no login needed): - -```bash -./otel-report.sh # last 15 minutes -./otel-report.sh 60 # last 60 minutes -./otel-report.sh live # auto-refresh every 10s -``` - -### Disabling OTel - -```bash -OTEL_ENABLE=0 ./localnet.sh start # disable -OTEL_ENDPOINT=http://my-collector:4317 ./localnet.sh start # custom collector +curl -L --socks5 localhost:1080 https://nymtech.com ``` ### LP (Lewes Protocol) Testing @@ -370,11 +289,8 @@ This makes localnet perfect for rapid LP protocol development and testing. docker/localnet/ ├── README.md # This file ├── localnet.sh # Main orchestration script -├── loadtest.sh # Load test / traffic generator -├── otel-report.sh # Terminal-based OTel metrics report -├── Dockerfile.localnet # Multi-stage Docker image (builder + slim runtime) -├── build_topology.py # Topology generator -└── localnet-logs.sh # Tmux-based multi-container log viewer +├── Dockerfile.localnet # Docker image definition +└── build_topology.py # Topology generator ``` ## How It Works diff --git a/docker/localnet/loadtest.sh b/docker/localnet/loadtest.sh deleted file mode 100755 index adfc5902a6..0000000000 --- a/docker/localnet/loadtest.sh +++ /dev/null @@ -1,313 +0,0 @@ -#!/bin/bash - -# Nym Localnet Load Test -# Generates sustained traffic through the mixnet SOCKS5 proxy to produce -# OTel traces and exercise the packet pipeline end-to-end. -# -# Usage: -# ./loadtest.sh # defaults: 10 concurrent, 60s, mixed sizes -# ./loadtest.sh -c 20 -d 120 # 20 concurrent, 120s -# ./loadtest.sh -s 64k # fixed 64KB responses (many Sphinx fragments) -# ./loadtest.sh -s 1k -c 5 -d 30 # small payloads, 5 workers -# -# Payload sizes (-s flag) map to Sphinx packet fragmentation: -# 1k = ~1 Sphinx packet (sub-MTU, minimal fragmentation) -# 4k = ~2-3 packets (small payload) -# 16k = ~8-10 packets (medium payload) -# 64k = ~32-35 packets (large payload, stresses forwarding) -# 256k = ~128-130 packets (heavy payload, stresses queues) -# 1m = ~512 packets (very heavy, potential backpressure) -# -# Prerequisites: -# - Localnet running (./localnet.sh start) -# - SOCKS5 proxy available on localhost:1080 - -set -e - -check_dependencies() { - local missing=() - for cmd in curl jq bc; do - command -v "$cmd" &>/dev/null || missing+=("$cmd") - done - if [ ${#missing[@]} -gt 0 ]; then - echo "ERROR: missing required commands: ${missing[*]}" >&2 - exit 1 - fi -} -check_dependencies - -CONCURRENCY=10 -DURATION=60 -PROXY="socks5h://127.0.0.1:1080" -PAYLOAD_SIZE="" -CUSTOM_URL="" -STATS_INTERVAL=5 - -# Default targets: mixed sizes for general testing -TARGETS=( - "https://httpbin.org/get" - "https://httpbin.org/bytes/1024" - "https://httpbin.org/delay/1" - "https://example.com" - "https://nym.com" -) - -# Convert human-readable size to bytes for httpbin -parse_size() { - local s - s=$(echo "$1" | tr '[:upper:]' '[:lower:]') - local num - num=$(echo "$s" | sed 's/[a-z]*$//') - case "$s" in - *m|*mb) echo $(( num * 1024 * 1024 )) ;; - *k|*kb) echo $(( num * 1024 )) ;; - *) echo "$num" ;; - esac -} - -usage() { - echo "Usage: $0 [-c concurrency] [-d duration_secs] [-s payload_size] [-u url] [-p proxy]" - echo "" - echo "Options:" - echo " -c Number of concurrent workers (default: $CONCURRENCY)" - echo " -d Test duration in seconds (default: $DURATION)" - echo " -s Response payload size: 1k, 4k, 16k, 64k, 256k, 1m (default: mixed)" - echo " -u Custom target URL (overrides -s and default targets)" - echo " -p SOCKS5 proxy address (default: $PROXY)" - echo "" - echo "Examples:" - echo " $0 # 10 workers, 60s, mixed targets/sizes" - echo " $0 -s 1k # small payloads (~1 Sphinx packet each)" - echo " $0 -s 64k -c 5 # large payloads, 5 workers" - echo " $0 -s 256k -c 2 -d 30 # very large payloads, observe queue pressure" - echo " $0 -c 20 -d 120 # heavier concurrency, 2 minutes" - exit 0 -} - -while getopts "c:d:s:u:p:h" opt; do - case $opt in - c) CONCURRENCY=$OPTARG ;; - d) DURATION=$OPTARG ;; - s) PAYLOAD_SIZE=$OPTARG ;; - u) CUSTOM_URL=$OPTARG ;; - p) PROXY=$OPTARG ;; - h) usage ;; - *) usage ;; - esac -done - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -log_info() { echo -e "${BLUE}[INFO]${NC} $*"; } -log_ok() { echo -e "${GREEN}[OK]${NC} $*"; } -log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } -log_err() { echo -e "${RED}[ERROR]${NC} $*"; } - -# Build sized URL if -s was specified -SIZED_URL="" -SIZE_LABEL="mixed" -if [ -n "$PAYLOAD_SIZE" ]; then - PAYLOAD_BYTES=$(parse_size "$PAYLOAD_SIZE") - SIZED_URL="https://httpbin.org/bytes/${PAYLOAD_BYTES}" - SIZE_LABEL="${PAYLOAD_SIZE} (~${PAYLOAD_BYTES} bytes)" -fi - -# Preflight checks -if ! nc -z 127.0.0.1 1080 2>/dev/null; then - log_err "SOCKS5 proxy not reachable on localhost:1080. Is the localnet running?" - exit 1 -fi - -# Counters (written to temp files for cross-process aggregation) -STATS_DIR=$(mktemp -d) -cleanup() { - kill $(jobs -p) 2>/dev/null || true - rm -rf "$STATS_DIR" -} -trap cleanup INT TERM EXIT - -pick_url() { - if [ -n "$CUSTOM_URL" ]; then - echo "$CUSTOM_URL" - elif [ -n "$PAYLOAD_SIZE" ]; then - echo "$SIZED_URL" - else - local idx=$((RANDOM % ${#TARGETS[@]})) - echo "${TARGETS[$idx]}" - fi -} - -# Millisecond timestamp (works on both GNU and BSD/macOS date) -now_ms() { - if command -v python3 &>/dev/null; then - python3 -c 'import time; print(int(time.time()*1000))' - else - echo $(($(date +%s) * 1000)) - fi -} - -# Worker function: runs requests in a loop until duration expires -worker() { - local id=$1 - local end_time=$2 - local ok=0 - local fail=0 - - while [ "$(date +%s)" -lt "$end_time" ]; do - local url - url=$(pick_url) - local start_ms - start_ms=$(now_ms) - - if curl -x "$PROXY" -m 15 -sf -o /dev/null -w "" "$url" 2>/dev/null; then - ok=$((ok + 1)) - else - fail=$((fail + 1)) - fi - - local end_ms - end_ms=$(now_ms) - local latency=$((end_ms - start_ms)) - - echo "$latency" >> "$STATS_DIR/latencies_${id}.txt" - done - - echo "$ok" > "$STATS_DIR/ok_${id}.txt" - echo "$fail" > "$STATS_DIR/fail_${id}.txt" -} - -echo "" -log_info "=== Nym Localnet Load Test ===" -log_info "Concurrency: $CONCURRENCY workers" -log_info "Duration: ${DURATION}s" -log_info "Payload: $SIZE_LABEL" -if [ -n "$CUSTOM_URL" ]; then - log_info "Target: $CUSTOM_URL" -elif [ -n "$PAYLOAD_SIZE" ]; then - log_info "Target: $SIZED_URL" -else - log_info "Targets: ${#TARGETS[@]} rotating URLs" -fi -log_info "Proxy: $PROXY" -echo "" - -# Quick connectivity check -log_info "Preflight: testing SOCKS5 proxy..." -if curl -x "$PROXY" -m 15 -sf -o /dev/null "https://httpbin.org/get"; then - log_ok "SOCKS5 proxy is working" -else - log_err "SOCKS5 proxy test failed. Check localnet status." - exit 1 -fi - -END_TIME=$(( $(date +%s) + DURATION )) -START_TIME=$(date +%s) - -log_info "Starting $CONCURRENCY workers for ${DURATION}s..." -echo "" - -for i in $(seq 1 "$CONCURRENCY"); do - worker "$i" "$END_TIME" & -done - -# Progress reporter (counts completed latency entries as a proxy for request count) -while [ "$(date +%s)" -lt "$END_TIME" ]; do - sleep "$STATS_INTERVAL" - elapsed=$(( $(date +%s) - START_TIME )) - remaining=$(( END_TIME - $(date +%s) )) - if [ "$remaining" -lt 0 ]; then remaining=0; fi - - total=0 - for f in "$STATS_DIR"/latencies_*.txt; do - if [ -f "$f" ]; then - count=$(wc -l < "$f" 2>/dev/null || echo 0) - total=$((total + count)) - fi - done - - if [ "$elapsed" -gt 0 ]; then - rps=$(echo "scale=1; $total / $elapsed" | bc 2>/dev/null || echo "?") - else - rps="?" - fi - - printf "\r [%3ds / %3ds] requests: %d | ~%s req/s | remaining: %ds " \ - "$elapsed" "$DURATION" "$total" "$rps" "$remaining" -done - -echo "" -log_info "Waiting for workers to finish..." -wait 2>/dev/null || true - -# Final stats -echo "" -log_info "=== Results ===" -total_ok=0 -total_fail=0 -all_latencies="" - -for f in "$STATS_DIR"/ok_*.txt; do - [ -f "$f" ] && total_ok=$((total_ok + $(cat "$f" 2>/dev/null || echo 0))) -done -for f in "$STATS_DIR"/fail_*.txt; do - [ -f "$f" ] && total_fail=$((total_fail + $(cat "$f" 2>/dev/null || echo 0))) -done -for f in "$STATS_DIR"/latencies_*.txt; do - [ -f "$f" ] && all_latencies="$all_latencies $(cat "$f" 2>/dev/null | tr '\n' ' ')" -done - -total=$((total_ok + total_fail)) -actual_duration=$(( $(date +%s) - START_TIME )) - -echo "" -echo " Total requests: $total" -echo " Successful: $total_ok" -echo " Failed: $total_fail" -if [ "$actual_duration" -gt 0 ]; then - rps=$(echo "scale=2; $total / $actual_duration" | bc 2>/dev/null || echo "?") - echo " Duration: ${actual_duration}s" - echo " Throughput: ~${rps} req/s" -fi - -if [ -n "$all_latencies" ]; then - sorted=$(echo "$all_latencies" | tr ' ' '\n' | sort -n | grep -v '^$') - count=$(echo "$sorted" | wc -l | tr -d ' ') - if [ "$count" -gt 0 ]; then - p50_idx=$(( count * 50 / 100 )) - p95_idx=$(( count * 95 / 100 )) - p99_idx=$(( count * 99 / 100 )) - [ "$p50_idx" -lt 1 ] && p50_idx=1 - [ "$p95_idx" -lt 1 ] && p95_idx=1 - [ "$p99_idx" -lt 1 ] && p99_idx=1 - - min_lat=$(echo "$sorted" | head -1) - max_lat=$(echo "$sorted" | tail -1) - p50=$(echo "$sorted" | sed -n "${p50_idx}p") - p95=$(echo "$sorted" | sed -n "${p95_idx}p") - p99=$(echo "$sorted" | sed -n "${p99_idx}p") - - echo "" - echo " Latency (ms):" - echo " min: ${min_lat}ms" - echo " p50: ${p50}ms" - echo " p95: ${p95}ms" - echo " p99: ${p99}ms" - echo " max: ${max_lat}ms" - fi -fi - -echo "" -if [ "$total_fail" -gt 0 ] && [ "$total" -gt 0 ]; then - fail_pct=$(echo "scale=1; $total_fail * 100 / $total" | bc 2>/dev/null || echo "?") - log_warn "Failure rate: ${fail_pct}% -- ${total_fail} of ${total} failed" -else - log_ok "All requests succeeded" -fi -echo "" -log_info "View traces in SigNoz: http://localhost:8080/traces" -log_info "Filter by service: nym-node" -echo "" diff --git a/docker/localnet/localnet-logs.sh b/docker/localnet/localnet-logs.sh index 9dfcdb5291..3347943e09 100755 --- a/docker/localnet/localnet-logs.sh +++ b/docker/localnet/localnet-logs.sh @@ -5,13 +5,6 @@ SESSION_NAME="nym-localnet-logs" -# Detect runtime -if command -v container &> /dev/null; then - RUNTIME="container" -else - RUNTIME="docker" -fi - # Container names CONTAINERS=( "nym-mixnode1" @@ -24,9 +17,9 @@ CONTAINERS=( # Check if containers are running running_containers=() -for ctr in "${CONTAINERS[@]}"; do - if $RUNTIME inspect "$ctr" &>/dev/null; then - running_containers+=("$ctr") +for container in "${CONTAINERS[@]}"; do + if container inspect "$container" &>/dev/null; then + running_containers+=("$container") fi done @@ -39,11 +32,11 @@ fi # Check if we're already in tmux if [ -n "$TMUX" ]; then # Inside tmux - create new window - tmux new-window -n "logs" "$RUNTIME logs -f ${running_containers[0]}" + tmux new-window -n "logs" "container logs -f ${running_containers[0]}" # Split for remaining containers for ((i=1; i<${#running_containers[@]}; i++)); do - tmux split-window -t logs "$RUNTIME logs -f ${running_containers[$i]}" + tmux split-window -t logs "container logs -f ${running_containers[$i]}" tmux select-layout -t logs tiled done @@ -55,11 +48,11 @@ else exec tmux attach-session -t "$SESSION_NAME" else # Create new session - tmux new-session -d -s "$SESSION_NAME" -n "logs" "$RUNTIME logs -f ${running_containers[0]}" + tmux new-session -d -s "$SESSION_NAME" -n "logs" "container logs -f ${running_containers[0]}" # Split for remaining containers for ((i=1; i<${#running_containers[@]}; i++)); do - tmux split-window -t "$SESSION_NAME:logs" "$RUNTIME logs -f ${running_containers[$i]}" + tmux split-window -t "$SESSION_NAME:logs" "container logs -f ${running_containers[$i]}" tmux select-layout -t "$SESSION_NAME:logs" tiled done diff --git a/docker/localnet/localnet.sh b/docker/localnet/localnet.sh index 5b92158f76..e021310bc2 100755 --- a/docker/localnet/localnet.sh +++ b/docker/localnet/localnet.sh @@ -2,8 +2,8 @@ set -ex -# Nym Localnet Orchestration Script -# Supports both Docker and Apple Container Runtime +# Nym Localnet Orchestration Script for Apple Container Runtime +# Emulates docker-compose functionality SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" @@ -14,38 +14,6 @@ NYM_VOLUME_PATH="/tmp/nym-localnet-home-$$" SUFFIX=${NYM_NODE_SUFFIX:-localnet} -# Detect container runtime: prefer Apple 'container' if available, fall back to docker -if command -v container &> /dev/null; then - RUNTIME="container" - HOST_INTERNAL="host.containers.internal" -else - RUNTIME="docker" - HOST_INTERNAL="host.docker.internal" -fi - -# OpenTelemetry configuration -# Set OTEL_ENABLE=1 to enable OTel tracing on all nym-node instances. -# OTEL_ENDPOINT should point to the OTLP gRPC collector reachable from containers. -# When SigNoz runs in Docker (signoz-net), we route to its collector directly. -OTEL_ENABLE=${OTEL_ENABLE:-1} -if [ -z "${OTEL_ENDPOINT:-}" ]; then - SIGNOZ_NET=$(docker network ls --filter name=signoz-net --format '{{.Name}}' 2>/dev/null || true) - if [ "$RUNTIME" = "docker" ] && [ -n "$SIGNOZ_NET" ]; then - OTEL_ENDPOINT="http://signoz-otel-collector:4317" - OTEL_SIGNOZ_NET="$SIGNOZ_NET" - else - OTEL_ENDPOINT="http://${HOST_INTERNAL}:4317" - OTEL_SIGNOZ_NET="" - fi -fi - -# Build OTel flags for nym-node run commands -otel_flags() { - if [ "$OTEL_ENABLE" = "1" ]; then - echo "--otel --otel-endpoint $OTEL_ENDPOINT" - fi -} - # Container names INIT_CONTAINER="nym-localnet-init" MIXNODE1_CONTAINER="nym-mixnode1" @@ -96,13 +64,13 @@ cleanup_host_state() { done } -# Check prerequisites +# Check if container command exists check_prerequisites() { - if ! command -v docker &> /dev/null; then - log_error "Docker not found" + if ! command -v container &> /dev/null; then + log_error "Apple 'container' command not found" + log_error "Install from: https://github.com/apple/container" exit 1 fi - log_info "Using runtime: $RUNTIME" } # Build the Docker image @@ -112,6 +80,7 @@ build_image() { cd "$PROJECT_ROOT" + # Build with Docker log_info "Building with Docker..." if ! docker build \ -f "$SCRIPT_DIR/Dockerfile.localnet" \ @@ -121,24 +90,30 @@ build_image() { exit 1 fi - # If using Apple container runtime, transfer image from Docker - if [ "$RUNTIME" = "container" ]; then - log_info "Transferring image to Apple container runtime..." - TEMP_IMAGE="/tmp/nym-localnet-image-$$.tar" - if ! docker save -o "$TEMP_IMAGE" "$IMAGE_NAME"; then - log_error "Failed to save Docker image" - exit 1 - fi - if ! container image load --input "$TEMP_IMAGE"; then - rm -f "$TEMP_IMAGE" - log_error "Failed to load image into container runtime" - exit 1 - fi + # Transfer image to container runtime + log_info "Transferring image to container runtime..." + + # Save to temporary file (container image load doesn't support stdin) + TEMP_IMAGE="/tmp/nym-localnet-image-$$.tar" + if ! docker save -o "$TEMP_IMAGE" "$IMAGE_NAME"; then + log_error "Failed to save Docker image" + exit 1 + fi + + # Load into container runtime from file + if ! container image load --input "$TEMP_IMAGE"; then rm -f "$TEMP_IMAGE" - if ! container image inspect "$IMAGE_NAME" &>/dev/null; then - log_error "Image not found in container runtime after load" - exit 1 - fi + log_error "Failed to load image into container runtime" + exit 1 + fi + + # Clean up temporary file + rm -f "$TEMP_IMAGE" + + # Verify image is available + if ! container image inspect "$IMAGE_NAME" &>/dev/null; then + log_error "Image not found in container runtime after load" + exit 1 fi log_success "Image built and loaded: $IMAGE_NAME" @@ -180,7 +155,7 @@ NETWORK_NAME="nym-localnet-network" # Create container network create_network() { log_info "Creating container network: $NETWORK_NAME" - if $RUNTIME network create "$NETWORK_NAME" 2>/dev/null; then + if container network create "$NETWORK_NAME" 2>/dev/null; then log_success "Network created: $NETWORK_NAME" else log_info "Network $NETWORK_NAME already exists or creation failed" @@ -189,9 +164,9 @@ create_network() { # Remove container network remove_network() { - if $RUNTIME network list | grep -q "$NETWORK_NAME"; then + if container network list | grep -q "$NETWORK_NAME"; then log_info "Removing network: $NETWORK_NAME" - $RUNTIME network rm "$NETWORK_NAME" 2>/dev/null || true + container network rm "$NETWORK_NAME" 2>/dev/null || true log_success "Network removed" fi } @@ -208,10 +183,7 @@ start_mixnode() { local verloc_port="2000${node_id}" local http_port="3000${node_id}" - local otel_args - otel_args=$(otel_flags) - - $RUNTIME run \ + container run \ --name "$container_name" \ -m 2G \ --network "$NETWORK_NAME" \ @@ -243,7 +215,7 @@ start_mixnode() { sleep 2; done; echo "Starting mix'"${node_id}"'..."; - exec nym-node '"${otel_args}"' run --id mix'"${node_id}"'-localnet --unsafe-disable-replay-protection --local + exec nym-node run --id mix'"${node_id}"'-localnet --unsafe-disable-replay-protection --local ' log_success "$container_name started" @@ -252,14 +224,9 @@ start_mixnode() { start_gateway() { log_info "Starting $GATEWAY_CONTAINER..." - local otel_args - otel_args=$(otel_flags) - - $RUNTIME run \ + container run \ --name "$GATEWAY_CONTAINER" \ -m 2G \ - --cap-add=NET_ADMIN \ - --device /dev/net/tun \ --network "$NETWORK_NAME" \ -p 9000:9000 \ -p 10004:10004 \ @@ -288,9 +255,11 @@ start_gateway() { --http-bind-address=0.0.0.0:30004 \ --http-access-token=lala \ --public-ips $CONTAINER_IP \ + --enable-lp true \ --lp-use-mock-ecash true \ --output=json \ - --wireguard-enabled false \ + --wireguard-enabled true \ + --wireguard-userspace true \ --bonding-information-output="/localnet/gateway.json"; echo "Waiting for network.json..."; @@ -298,7 +267,7 @@ start_gateway() { sleep 2; done; echo "Starting gateway with LP listener (mock ecash)..."; - exec nym-node '"${otel_args}"' run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled false --lp-use-mock-ecash true + exec nym-node run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true ' log_success "$GATEWAY_CONTAINER started" @@ -322,14 +291,9 @@ start_gateway() { start_gateway2() { log_info "Starting $GATEWAY2_CONTAINER..." - local otel_args - otel_args=$(otel_flags) - - $RUNTIME run \ + container run \ --name "$GATEWAY2_CONTAINER" \ -m 2G \ - --cap-add=NET_ADMIN \ - --device /dev/net/tun \ --network "$NETWORK_NAME" \ -p 9001:9001 \ -p 10005:10005 \ @@ -358,9 +322,11 @@ start_gateway2() { --http-bind-address=0.0.0.0:30005 \ --http-access-token=lala \ --public-ips $CONTAINER_IP \ + --enable-lp true \ --lp-use-mock-ecash true \ --output=json \ - --wireguard-enabled false \ + --wireguard-enabled true \ + --wireguard-userspace true \ --bonding-information-output="/localnet/gateway2.json"; echo "Waiting for network.json..."; @@ -368,7 +334,7 @@ start_gateway2() { sleep 2; done; echo "Starting gateway2 with LP listener (mock ecash)..."; - exec nym-node '"${otel_args}"' run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled false --lp-use-mock-ecash true + exec nym-node run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true ' log_success "$GATEWAY2_CONTAINER started" @@ -392,12 +358,12 @@ start_gateway2() { start_network_requester() { log_info "Starting $REQUESTER_CONTAINER..." - # Get gateway IP address (first IP only, in case container has multiple networks) + # Get gateway IP address log_info "Getting gateway IP address..." - GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i | awk '{print $1}') + GATEWAY_IP=$(container exec "$GATEWAY_CONTAINER" hostname -i) log_info "Gateway IP: $GATEWAY_IP" - $RUNTIME run \ + container run \ --name "$REQUESTER_CONTAINER" \ --network "$NETWORK_NAME" \ -v "$VOLUME_PATH:/localnet" \ @@ -432,7 +398,7 @@ start_network_requester() { start_socks5_client() { log_info "Starting $SOCKS5_CONTAINER..." - $RUNTIME run \ + container run \ --name "$SOCKS5_CONTAINER" \ --network "$NETWORK_NAME" \ -p 1080:1080 \ @@ -485,15 +451,15 @@ stop_containers() { log_info "Stopping all containers..." for container_name in "${ALL_CONTAINERS[@]}"; do - if $RUNTIME inspect "$container_name" &>/dev/null; then + if container inspect "$container_name" &>/dev/null; then log_info "Stopping $container_name" - $RUNTIME stop "$container_name" 2>/dev/null || true - $RUNTIME rm "$container_name" 2>/dev/null || true + container stop "$container_name" 2>/dev/null || true + container rm "$container_name" 2>/dev/null || true fi done # Also clean up init container if it exists - $RUNTIME rm "$INIT_CONTAINER" 2>/dev/null || true + container rm "$INIT_CONTAINER" 2>/dev/null || true log_success "All containers stopped" @@ -501,7 +467,7 @@ stop_containers() { remove_network } -# Show $RUNTIME logs +# Show container logs show_logs() { local container_name=${1:-} @@ -512,8 +478,8 @@ show_logs() { fi # Show logs for specific container - if $RUNTIME inspect "$container_name" &>/dev/null; then - $RUNTIME logs -f "$container_name" + if container inspect "$container_name" &>/dev/null; then + container logs -f "$container_name" else log_error "Container not found: $container_name" log_info "Available containers:" @@ -530,8 +496,8 @@ show_status() { echo "" for container_name in "${ALL_CONTAINERS[@]}"; do - if $RUNTIME inspect "$container_name" &>/dev/null; then - local status=$($RUNTIME inspect "$container_name" 2>/dev/null | grep -o '"Status":"[^"]*"' | cut -d'"' -f4 || echo "unknown") + if container inspect "$container_name" &>/dev/null; then + local status=$(container inspect "$container_name" 2>/dev/null | grep -o '"Status":"[^"]*"' | cut -d'"' -f4 || echo "unknown") echo -e " ${GREEN}●${NC} $container_name - $status" else echo -e " ${RED}○${NC} $container_name - not running" @@ -586,13 +552,13 @@ build_topology() { log_success " $file created" done - # Get container IPs (first IP only, containers may be on multiple networks) + # Get container IPs log_info "Getting container IP addresses..." - MIX1_IP=$($RUNTIME exec "$MIXNODE1_CONTAINER" hostname -i | awk '{print $1}') - MIX2_IP=$($RUNTIME exec "$MIXNODE2_CONTAINER" hostname -i | awk '{print $1}') - MIX3_IP=$($RUNTIME exec "$MIXNODE3_CONTAINER" hostname -i | awk '{print $1}') - GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i | awk '{print $1}') - GATEWAY2_IP=$($RUNTIME exec "$GATEWAY2_CONTAINER" hostname -i | awk '{print $1}') + MIX1_IP=$(container exec "$MIXNODE1_CONTAINER" hostname -i) + MIX2_IP=$(container exec "$MIXNODE2_CONTAINER" hostname -i) + MIX3_IP=$(container exec "$MIXNODE3_CONTAINER" hostname -i) + GATEWAY_IP=$(container exec "$GATEWAY_CONTAINER" hostname -i) + GATEWAY2_IP=$(container exec "$GATEWAY2_CONTAINER" hostname -i) log_info "Container IPs:" echo " mix1: $MIX1_IP" @@ -602,7 +568,7 @@ build_topology() { echo " gateway2: $GATEWAY2_IP" # Run build_topology.py in a container with access to the volumes - $RUNTIME run \ + container run \ --name "nym-localnet-topology-builder" \ --network "$NETWORK_NAME" \ -v "$VOLUME_PATH:/localnet" \ @@ -641,32 +607,19 @@ start_all() { start_mixnode 3 "$MIXNODE3_CONTAINER" start_gateway start_gateway2 - - # Connect nym containers to SigNoz network for direct OTLP routing - if [ -n "${OTEL_SIGNOZ_NET:-}" ]; then - log_info "Connecting containers to SigNoz network ($OTEL_SIGNOZ_NET)..." - for c in "$MIXNODE1_CONTAINER" "$MIXNODE2_CONTAINER" "$MIXNODE3_CONTAINER" \ - "$GATEWAY_CONTAINER" "$GATEWAY2_CONTAINER"; do - docker network connect "$OTEL_SIGNOZ_NET" "$c" 2>/dev/null && \ - log_success " $c connected to $OTEL_SIGNOZ_NET" || true - done - fi - build_topology # Configure networking for two-hop WireGuard routing on both gateways - # Note: Requires --privileged or --cap-add=NET_ADMIN on the containers. - # Non-fatal: only needed for WireGuard VPN routing, not mixnet packet testing. + # Note: Runs after build_topology to ensure gateways have finished WireGuard setup log_info "Configuring gateway networking (IP forwarding, NAT)..." for gw in "$GATEWAY_CONTAINER" "$GATEWAY2_CONTAINER"; do - if $RUNTIME exec "$gw" sh -c " - echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null - iptables-legacy -t nat -A POSTROUTING -o eth0 -j MASQUERADE 2>/dev/null - " 2>/dev/null; then - log_success "Configured $gw" - else - log_warn "Could not configure NAT on $gw (needs --privileged). WireGuard VPN routing will not work." - fi + container exec "$gw" sh -c " + # Enable IP forwarding + echo 1 > /proc/sys/net/ipv4/ip_forward + # Add NAT masquerade for outbound traffic + iptables-legacy -t nat -A POSTROUTING -o eth0 -j MASQUERADE + " + log_success "Configured $gw" done start_network_requester diff --git a/docker/localnet/otel-report.sh b/docker/localnet/otel-report.sh deleted file mode 100755 index 7930603382..0000000000 --- a/docker/localnet/otel-report.sh +++ /dev/null @@ -1,239 +0,0 @@ -#!/bin/bash - -# Nym Localnet OTel Report -# Queries ClickHouse directly to produce a terminal-based summary of -# the core metrics captured by the OTel-instrumented nym-nodes. -# -# Usage: -# ./otel-report.sh # last 15 minutes -# ./otel-report.sh 60 # last 60 minutes -# ./otel-report.sh live # live mode: refresh every 10s -# -# Prerequisites: localnet + SigNoz running - -set -e - -check_dependencies() { - local missing=() - for cmd in docker jq; do - command -v "$cmd" &>/dev/null || missing+=("$cmd") - done - if [ ${#missing[@]} -gt 0 ]; then - echo "ERROR: missing required commands: ${missing[*]}" >&2 - exit 1 - fi -} -check_dependencies - -CH_CONTAINER="signoz-clickhouse" -TRACES_TABLE="signoz_traces.distributed_signoz_index_v3" -LOOKBACK_MIN=${1:-15} -LIVE=false - -if [ "$1" = "live" ]; then - LIVE=true - LOOKBACK_MIN=5 -fi - -BLUE='\033[0;34m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -BOLD='\033[1m' -DIM='\033[2m' -NC='\033[0m' - -if ! docker ps --filter "name=$CH_CONTAINER" --format '{{.Names}}' | grep -q "$CH_CONTAINER"; then - echo "ERROR: ClickHouse container '$CH_CONTAINER' not running. Is SigNoz started?" >&2 - exit 1 -fi - -ch() { - docker exec "$CH_CONTAINER" clickhouse-client --query "$1" 2>/dev/null -} - -divider() { - echo -e "${DIM}$(printf '%.0s-' {1..78})${NC}" -} - -print_report() { - local window="$1" - - echo "" - echo -e "${BOLD} Nym Localnet -- OTel Packet Pipeline Report${NC}" - echo -e " ${DIM}Window: last ${window} minutes | $(date '+%Y-%m-%d %H:%M:%S')${NC}" - divider - - # 1. Throughput per operation - echo -e "\n${BOLD} [1] Packet Throughput (packets/sec by operation)${NC}\n" - ch " - SELECT - name AS operation, - count(*) AS total, - round(count(*) / (${window} * 60), 1) AS per_sec - FROM ${TRACES_TABLE} - WHERE timestamp >= now() - INTERVAL ${window} MINUTE - AND serviceName = 'nym-node' - AND name IN ( - 'handle_received_nym_packet', - 'mixnode.sphinx_full_unwrap', - 'mixnode.forward_packet', - 'mixnode.final_hop' - ) - GROUP BY name - ORDER BY total DESC - FORMAT PrettyCompactNoEscapes - " - - divider - - # 2. Latency per operation - echo -e "\n${BOLD} [2] Processing Latency (milliseconds)${NC}\n" - ch " - SELECT - name AS operation, - round(quantile(0.50)(duration_nano / 1e6), 3) AS p50_ms, - round(quantile(0.95)(duration_nano / 1e6), 3) AS p95_ms, - round(quantile(0.99)(duration_nano / 1e6), 3) AS p99_ms, - round(quantile(0.999)(duration_nano / 1e6), 3) AS p999_ms, - round(avg(duration_nano / 1e6), 3) AS avg_ms - FROM ${TRACES_TABLE} - WHERE timestamp >= now() - INTERVAL ${window} MINUTE - AND serviceName = 'nym-node' - AND name IN ( - 'handle_received_nym_packet', - 'mixnode.sphinx_full_unwrap', - 'mixnode.forward_packet', - 'mixnode.final_hop' - ) - AND duration_nano < 60000000000 - GROUP BY name - ORDER BY p50_ms DESC - FORMAT PrettyCompactNoEscapes - " - - divider - - # 3. Error rate - echo -e "\n${BOLD} [3] Error Rate${NC}\n" - local errors - errors=$(ch " - SELECT - name, - countIf(has_error = true) AS errors, - count(*) AS total, - round(100.0 * countIf(has_error = true) / count(*), 3) AS error_pct - FROM ${TRACES_TABLE} - WHERE timestamp >= now() - INTERVAL ${window} MINUTE - AND serviceName = 'nym-node' - AND name IN ( - 'handle_received_nym_packet', - 'mixnode.sphinx_full_unwrap', - 'mixnode.forward_packet', - 'mixnode.final_hop' - ) - GROUP BY name - HAVING errors > 0 - ORDER BY errors DESC - FORMAT PrettyCompactNoEscapes - ") - - if [ -z "$errors" ]; then - echo -e " ${GREEN}No errors detected across all operations${NC}" - else - echo "$errors" - fi - - divider - - # 4. Forwarding ratio (are packets being dropped between stages?) - echo -e "\n${BOLD} [4] Pipeline Funnel (packet drop detection)${NC}\n" - ch " - SELECT - name AS stage, - count(*) AS packets, - round(100.0 * count(*) / max(total_ingress), 1) AS pct_of_ingress - FROM ${TRACES_TABLE} - CROSS JOIN ( - SELECT count(*) AS total_ingress - FROM ${TRACES_TABLE} - WHERE timestamp >= now() - INTERVAL ${window} MINUTE - AND serviceName = 'nym-node' - AND name = 'handle_received_nym_packet' - ) AS t - WHERE timestamp >= now() - INTERVAL ${window} MINUTE - AND serviceName = 'nym-node' - AND name IN ( - 'handle_received_nym_packet', - 'mixnode.sphinx_full_unwrap', - 'mixnode.forward_packet', - 'mixnode.final_hop' - ) - GROUP BY name - ORDER BY packets DESC - FORMAT PrettyCompactNoEscapes - " - - echo "" - echo -e " ${DIM}Expected ratios: sphinx_unwrap ~ 100%, forward ~ 75% (3 of 4 hops forward),${NC}" - echo -e " ${DIM}final_hop ~ 25% (1 of 4 hops is the last one). Significantly lower = drops.${NC}" - - divider - - # 5. Throughput over time (1-minute buckets) - echo -e "\n${BOLD} [5] Throughput Timeline (1-min buckets, ingress packets)${NC}\n" - ch " - SELECT - toStartOfMinute(timestamp) AS minute, - count(*) AS packets, - round(count(*) / 60, 1) AS per_sec - FROM ${TRACES_TABLE} - WHERE timestamp >= now() - INTERVAL ${window} MINUTE - AND serviceName = 'nym-node' - AND name = 'handle_received_nym_packet' - GROUP BY minute - ORDER BY minute - FORMAT PrettyCompactNoEscapes - " - - divider - - # 6. Latency spikes (potential TCP congestion / backpressure indicators) - echo -e "\n${BOLD} [6] Latency Spikes (sphinx_unwrap p99 per minute)${NC}\n" - ch " - SELECT - toStartOfMinute(timestamp) AS minute, - round(quantile(0.99)(duration_nano / 1e6), 3) AS p99_ms, - round(quantile(0.50)(duration_nano / 1e6), 3) AS p50_ms, - round(p99_ms / greatest(p50_ms, 0.001), 1) AS spike_ratio, - count(*) AS samples - FROM ${TRACES_TABLE} - WHERE timestamp >= now() - INTERVAL ${window} MINUTE - AND serviceName = 'nym-node' - AND name = 'mixnode.sphinx_full_unwrap' - GROUP BY minute - ORDER BY minute - FORMAT PrettyCompactNoEscapes - " - - echo "" - echo -e " ${DIM}spike_ratio > 10x suggests backpressure or queue buildup.${NC}" - echo -e " ${DIM}Sustained high p99 across minutes may indicate TCP meltdown.${NC}" - - divider - echo "" - echo -e " ${BLUE}SigNoz UI:${NC} http://localhost:8080" - echo -e " ${DIM}Traces tab -> Filter: serviceName = nym-node${NC}" - echo "" -} - -if [ "$LIVE" = "true" ]; then - while true; do - clear - print_report "$LOOKBACK_MIN" - echo -e " ${DIM}Refreshing in 10s... (Ctrl+C to stop)${NC}" - sleep 10 - done -else - print_report "$LOOKBACK_MIN" -fi From 988df7cff7cf4c1278b4a45504872dfb078807b8 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 17 Feb 2026 09:10:52 +0100 Subject: [PATCH 17/51] sampling to avoid costs - add otel timeouts --- common/bin-common/src/logging/mod.rs | 14 +++++++++++++- nym-node/src/cli/mod.rs | 10 ++++++++++ nym-node/src/logging.rs | 8 ++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/common/bin-common/src/logging/mod.rs b/common/bin-common/src/logging/mod.rs index 247ce5e1bc..70b2f76a54 100644 --- a/common/bin-common/src/logging/mod.rs +++ b/common/bin-common/src/logging/mod.rs @@ -78,12 +78,18 @@ pub fn setup_tracing_logger() { /// sent as the `signoz-ingestion-key` gRPC metadata header on every export. /// * `environment` - Deployment environment label (e.g. "sandbox", "mainnet", "canary"). /// Attached as the `deployment.environment` OTel resource attribute. +/// * `sample_ratio` - Trace sampling ratio in 0.0..=1.0 (e.g. 0.1 = 10% of traces). +/// Used to limit cost when exporting from many nodes; clamped to [0.0, 1.0]. +/// * `export_timeout_secs` - Timeout in seconds for each OTLP export batch. Prevents +/// unbounded blocking if the collector is slow or unreachable. #[cfg(feature = "otel-otlp")] pub fn init_otel_layer( service_name: &str, endpoint: &str, ingestion_key: Option<&str>, environment: &str, + sample_ratio: f64, + export_timeout_secs: u64, ) -> Result< ( tracing_opentelemetry::OpenTelemetryLayer, @@ -97,6 +103,8 @@ where use opentelemetry::trace::TracerProvider as _; use opentelemetry_otlp::WithExportConfig; use opentelemetry_otlp::WithTonicConfig; + use opentelemetry_sdk::trace::Sampler; + use std::time::Duration; // Validate endpoint URI early to fail with a clear message if !endpoint.starts_with("http://") && !endpoint.starts_with("https://") { @@ -106,9 +114,12 @@ where .into()); } + let sample_ratio_clamped = sample_ratio.clamp(0.0, 1.0); + let mut builder = opentelemetry_otlp::SpanExporter::builder() .with_tonic() - .with_endpoint(endpoint); + .with_endpoint(endpoint) + .with_timeout(Duration::from_secs(export_timeout_secs)); // Explicitly configure TLS when the endpoint uses HTTPS if endpoint.starts_with("https://") { @@ -131,6 +142,7 @@ where .map_err(|e| format!("failed to build OTLP exporter for endpoint {endpoint}: {e}"))?; let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() + .with_sampler(Sampler::TraceIdRatioBased(sample_ratio_clamped)) .with_batch_exporter(exporter) .with_resource( opentelemetry_sdk::Resource::builder() diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index ceb4ca7a61..426a77eb39 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -64,6 +64,14 @@ pub(crate) struct Cli { #[clap(long, env = "NYMNODE_OTEL_ENV", default_value = "mainnet")] pub(crate) otel_env: String, + /// Trace sampling ratio (0.0 to 1.0). e.g. 0.1 = 10%% of traces exported. Reduces cost. + #[clap(long, env = "NYMNODE_OTEL_SAMPLE_RATIO", default_value = "0.1")] + pub(crate) otel_sample_ratio: f64, + + /// Timeout in seconds for each OTLP export batch. Prevents unbounded blocking. + #[clap(long, env = "NYMNODE_OTEL_EXPORT_TIMEOUT", default_value = "10")] + pub(crate) otel_export_timeout: u64, + #[clap(subcommand)] command: Commands, } @@ -106,6 +114,8 @@ impl Cli { service_name: "nym-node".to_string(), ingestion_key: self.otel_key.clone(), environment: self.otel_env.clone(), + sample_ratio: self.otel_sample_ratio, + export_timeout_secs: self.otel_export_timeout, }) } else { None diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index 31be976f31..e8592c8b67 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -22,6 +22,10 @@ pub(crate) struct OtelConfig { /// Deployment environment label, e.g. `mainnet`, `sandbox`, `canary`. /// Attached as the `deployment.environment` OTel resource attribute. pub environment: String, + /// Trace sampling ratio in 0.0..=1.0 (e.g. 0.1 = 10% of traces). Used to limit cost. + pub sample_ratio: f64, + /// Timeout in seconds for each OTLP export batch. Prevents unbounded blocking. + pub export_timeout_secs: u64, } /// Handle returned when OTel is active. Flushes pending spans on drop @@ -74,6 +78,8 @@ pub(crate) fn setup_tracing_logger(otel: Option) -> anyhow::Result) -> anyhow::Result Date: Tue, 17 Feb 2026 10:17:22 +0100 Subject: [PATCH 18/51] remove non OTEL work here --- common/nym-lp/src/message.rs | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/common/nym-lp/src/message.rs b/common/nym-lp/src/message.rs index 38431783cc..fbd692175b 100644 --- a/common/nym-lp/src/message.rs +++ b/common/nym-lp/src/message.rs @@ -374,11 +374,9 @@ impl ForwardPacketData { bytes.len() ))); } - // length >= 43 guaranteed above, so bytes[0..32] is always 32 bytes - #[allow(clippy::expect_used)] - let target_gateway_identity: [u8; 32] = bytes[0..32] - .try_into() - .expect("length check above guarantees at least 43 bytes"); + // SAFETY: we ensured we have sufficient data + #[allow(clippy::unwrap_used)] + let target_gateway_identity = bytes[0..32].try_into().unwrap(); let target_lp_address_is_ipv6 = bytes[32] != 0; let (target_lp_address, next_index) = if target_lp_address_is_ipv6 { @@ -391,21 +389,16 @@ impl ForwardPacketData { bytes.len() ))); } - // length >= 55 guaranteed above, so bytes[33..49] is always 16 bytes - #[allow(clippy::expect_used)] - let ipv6 = IpAddr::V6(Ipv6Addr::from( - <[u8; 16]>::try_from(&bytes[33..49]) - .expect("length check above guarantees at least 55 bytes"), - )); + // SAFETY: we ensured we have sufficient data, and the length is correct for casting + #[allow(clippy::unwrap_used)] + let ipv6 = IpAddr::V6(Ipv6Addr::from_octets(bytes[33..49].try_into().unwrap())); let port = u16::from_le_bytes([bytes[49], bytes[50]]); (SocketAddr::new(ipv6, port), 51) } else { - // length >= 43 guaranteed at function entry, so bytes[33..37] is always 4 bytes - #[allow(clippy::expect_used)] - let ipv4 = IpAddr::V4(Ipv4Addr::from( - <[u8; 4]>::try_from(&bytes[33..37]) - .expect("length check at entry guarantees at least 43 bytes"), - )); + // IPv4. Length check done at the start + // SAFETY: we ensured we have sufficient data, and the length is correct for casting + #[allow(clippy::unwrap_used)] + let ipv4 = IpAddr::V4(Ipv4Addr::from_octets(bytes[33..37].try_into().unwrap())); let port = u16::from_le_bytes([bytes[37], bytes[38]]); (SocketAddr::new(ipv4, port), 39) }; From a519f4ccb8d347d891e6211680096f9455a32e53 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Wed, 18 Feb 2026 10:48:54 +0100 Subject: [PATCH 19/51] pr feedback - Moved OTel CLI options into a separate OtelArgs - Otel is built behind the feature flag otel - Store timing is in microseconds - Restore comments to existing files --- .../client-libs/mixnet-client/src/client.rs | 3 + nym-node/src/cli/mod.rs | 90 +++++++++++-------- nym-node/src/logging.rs | 6 +- nym-node/src/node/mixnet/handler.rs | 4 + .../src/node/mixnet/packet_forwarding/mod.rs | 1 + nym-node/src/node/mixnet/shared/final_hop.rs | 6 +- 6 files changed, 65 insertions(+), 45 deletions(-) diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index c7136c5ed9..ffd4d025e1 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -312,10 +312,13 @@ impl SendWithoutResponse for Client { let address = packet.next_hop_address(); trace!("Sending packet to {address}"); + // TODO: optimisation for the future: rather than constantly using legacy encoding, + // use the mix packet type / flags to pick encoding per packet let framed_packet = FramedNymPacket::from_mix_packet(packet, self.config.use_legacy_packet_encoding); let Some(sender) = self.active_connections.get_mut(&address) else { + // there was never a connection to begin with debug!( event = "mixclient.try_send", peer = %address, diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index 426a77eb39..2cbeacbd8a 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -21,26 +21,11 @@ fn pretty_build_info_static() -> &'static str { PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print()) } -#[derive(Parser, Debug)] -#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)] -pub(crate) struct Cli { - /// Path pointing to an env file that configures the nym-node and overrides any preconfigured values. - #[clap( - short, - long, - env = NYMNODE_CONFIG_ENV_FILE_ARG - )] - pub(crate) config_env_file: Option, - - /// Flag used for disabling the printed banner in tty. - #[clap( - long, - env = NYMNODE_NO_BANNER_ARG - )] - pub(crate) no_banner: bool, - +/// OpenTelemetry-related CLI arguments. Only present when built with the `otel` feature. +#[cfg(feature = "otel")] +#[derive(Args, Debug, Clone)] +pub(crate) struct OtelArgs { /// Enable OpenTelemetry tracing export via OTLP/gRPC. - /// Requires the binary to be built with the `otel` feature. #[clap(long, env = "NYMNODE_OTEL_ENABLE")] pub(crate) otel: bool, @@ -71,6 +56,29 @@ pub(crate) struct Cli { /// Timeout in seconds for each OTLP export batch. Prevents unbounded blocking. #[clap(long, env = "NYMNODE_OTEL_EXPORT_TIMEOUT", default_value = "10")] pub(crate) otel_export_timeout: u64, +} + +#[derive(Parser, Debug)] +#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)] +pub(crate) struct Cli { + /// Path pointing to an env file that configures the nym-node and overrides any preconfigured values. + #[clap( + short, + long, + env = NYMNODE_CONFIG_ENV_FILE_ARG + )] + pub(crate) config_env_file: Option, + + /// Flag used for disabling the printed banner in tty. + #[clap( + long, + env = NYMNODE_NO_BANNER_ARG + )] + pub(crate) no_banner: bool, + + #[cfg(feature = "otel")] + #[clap(flatten)] + pub(crate) otel: OtelArgs, #[clap(subcommand)] command: Commands, @@ -78,13 +86,19 @@ pub(crate) struct Cli { impl Cli { pub(crate) fn execute(self) -> anyhow::Result<()> { + // test_throughput sets up its own logger and builds a runtime internally. + if let Commands::TestThroughput(args) = self.command { + return test_throughput::execute(args); + } + let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?; - // Set up tracing inside the runtime so the OTel batch exporter + // Set up tracing inside the runtime so the OTel batch exporter (when enabled) // can spawn its background tasks on the tokio reactor. - let _otel_guard = runtime.block_on(async { self.setup_logging() })?; + let use_otel = matches!(self.command, Commands::Run(..)); + let _otel_guard = runtime.block_on(async { self.setup_logging(use_otel) })?; // `_otel_guard` is dropped at function exit, flushing pending spans via its Drop impl runtime.block_on(async { @@ -95,7 +109,7 @@ impl Cli { Commands::Run(args) => run::execute(*args).await?, Commands::Migrate(args) => migrate::execute(*args)?, Commands::Sign(args) => sign::execute(args).await?, - Commands::TestThroughput(args) => test_throughput::execute(args)?, + Commands::TestThroughput(..) => unreachable!(), Commands::UnsafeResetSphinxKeys(args) => reset_sphinx_keys::execute(args).await?, Commands::Debug(debug) => match debug.command { DebugCommands::ResetProvidersGatewayDbs(args) => { @@ -107,15 +121,16 @@ impl Cli { }) } + #[cfg(feature = "otel")] fn build_otel_config(&self) -> Option { - if self.otel { + if self.otel.otel { Some(crate::logging::OtelConfig { - endpoint: self.otel_endpoint.clone(), + endpoint: self.otel.otel_endpoint.clone(), service_name: "nym-node".to_string(), - ingestion_key: self.otel_key.clone(), - environment: self.otel_env.clone(), - sample_ratio: self.otel_sample_ratio, - export_timeout_secs: self.otel_export_timeout, + ingestion_key: self.otel.otel_key.clone(), + environment: self.otel.otel_env.clone(), + sample_ratio: self.otel.otel_sample_ratio, + export_timeout_secs: self.otel.otel_export_timeout, }) } else { None @@ -123,19 +138,18 @@ impl Cli { } #[cfg(feature = "otel")] - fn setup_logging(&self) -> anyhow::Result> { - if matches!(self.command, Commands::TestThroughput(..)) { - return Ok(None); - } - crate::logging::setup_tracing_logger(self.build_otel_config()) + fn setup_logging(&self, use_otel: bool) -> anyhow::Result> { + let otel_config = if use_otel { + self.build_otel_config() + } else { + None + }; + crate::logging::setup_tracing_logger(otel_config) } #[cfg(not(feature = "otel"))] - fn setup_logging(&self) -> anyhow::Result> { - if matches!(self.command, Commands::TestThroughput(..)) { - return Ok(None); - } - crate::logging::setup_tracing_logger(self.build_otel_config())?; + fn setup_logging(&self, _use_otel: bool) -> anyhow::Result> { + crate::logging::setup_tracing_logger()?; Ok(None) } } diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index e8592c8b67..4ed952388d 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -8,8 +8,7 @@ use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::{EnvFilter, Layer}; /// Configuration for OpenTelemetry OTLP export. -/// Fields are only read when the `otel` feature is enabled. -#[allow(dead_code)] +#[cfg(feature = "otel")] pub(crate) struct OtelConfig { /// OTLP/gRPC collector endpoint, e.g. `http://localhost:4317` /// or `https://ingest.eu.signoz.cloud:443` for SigNoz Cloud. @@ -134,8 +133,7 @@ pub(crate) fn setup_tracing_logger(otel: Option) -> anyhow::Result) -> anyhow::Result<()> { - let _ = otel; +pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> { let stderr_layer = default_tracing_fmt_layer(std::io::stderr).with_filter(granual_filtered_env()?); diff --git a/nym-node/src/node/mixnet/handler.rs b/nym-node/src/node/mixnet/handler.rs index 12ed41b638..4b193bfe87 100644 --- a/nym-node/src/node/mixnet/handler.rs +++ b/nym-node/src/node/mixnet/handler.rs @@ -195,8 +195,10 @@ impl ConnectionHandler { let message = final_hop_data.message; let has_ack = final_hop_data.forward_ack.is_some(); + // if possible attempt to push message directly to the client match self.shared.try_push_message_to_client(client, message) { Err(unsent_plaintext) => { + // if that failed, store it on disk Span::current().record("client_online", false); match self .shared @@ -221,6 +223,8 @@ impl ConnectionHandler { } } + // if we managed to either push message directly to the [online] client or store it at + // disk, forward the ack self.shared.forward_ack_packet(final_hop_data.forward_ack); if has_ack { Span::current().record("ack_forwarded", true); diff --git a/nym-node/src/node/mixnet/packet_forwarding/mod.rs b/nym-node/src/node/mixnet/packet_forwarding/mod.rs index ef695a05eb..a6a5b32d73 100644 --- a/nym-node/src/node/mixnet/packet_forwarding/mod.rs +++ b/nym-node/src/node/mixnet/packet_forwarding/mod.rs @@ -55,6 +55,7 @@ impl PacketForwarder { if let Err(err) = self.mixnet_client.send_without_response(packet) { if err.kind() == io::ErrorKind::WouldBlock { + // we only know for sure if we dropped a packet if our sending queue was full warn!( event = "packet.dropped.buffer_full", next_hop = %next_hop, diff --git a/nym-node/src/node/mixnet/shared/final_hop.rs b/nym-node/src/node/mixnet/shared/final_hop.rs index 0235617342..2a9d102e51 100644 --- a/nym-node/src/node/mixnet/shared/final_hop.rs +++ b/nym-node/src/node/mixnet/shared/final_hop.rs @@ -73,16 +73,16 @@ impl SharedFinalHopData { let start = Instant::now(); debug!("Storing received message for {client_address} on the disk...",); let result = self.storage.store_message(client_address, message).await; - let store_ms = start.elapsed().as_millis() as u64; + let store_us = start.elapsed().as_micros() as u64; if result.is_ok() { debug!( event = "gateway.disk_store", - store_ms, "stored message for {client_address} on disk in {store_ms}ms" + store_us, "stored message for {client_address} on disk in {store_us}us" ); } else { warn!( event = "gateway.disk_store_failed", - store_ms, "failed to store message for {client_address} on disk after {store_ms}ms" + store_us, "failed to store message for {client_address} on disk after {store_us}us" ); } result From 3eff6e5e3bc56a41fa59388036ae63b2d899b2dd Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Wed, 18 Feb 2026 11:06:42 +0100 Subject: [PATCH 20/51] fix testthroughput --- nym-node/src/cli/mod.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index 2cbeacbd8a..7042a739ea 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -86,11 +86,6 @@ pub(crate) struct Cli { impl Cli { pub(crate) fn execute(self) -> anyhow::Result<()> { - // test_throughput sets up its own logger and builds a runtime internally. - if let Commands::TestThroughput(args) = self.command { - return test_throughput::execute(args); - } - let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?; @@ -109,7 +104,7 @@ impl Cli { Commands::Run(args) => run::execute(*args).await?, Commands::Migrate(args) => migrate::execute(*args)?, Commands::Sign(args) => sign::execute(args).await?, - Commands::TestThroughput(..) => unreachable!(), + Commands::TestThroughput(args) => test_throughput::execute(args)?, Commands::UnsafeResetSphinxKeys(args) => reset_sphinx_keys::execute(args).await?, Commands::Debug(debug) => match debug.command { DebugCommands::ResetProvidersGatewayDbs(args) => { @@ -139,6 +134,9 @@ impl Cli { #[cfg(feature = "otel")] fn setup_logging(&self, use_otel: bool) -> anyhow::Result> { + if matches!(self.command, Commands::TestThroughput(..)) { + return Ok(None); + } let otel_config = if use_otel { self.build_otel_config() } else { @@ -149,6 +147,9 @@ impl Cli { #[cfg(not(feature = "otel"))] fn setup_logging(&self, _use_otel: bool) -> anyhow::Result> { + if matches!(self.command, Commands::TestThroughput(..)) { + return Ok(None); + } crate::logging::setup_tracing_logger()?; Ok(None) } From a6bc54461ae27ee2856a62531fdd955f348d743e Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Wed, 18 Feb 2026 16:57:55 +0000 Subject: [PATCH 21/51] [DOCs]: Diagnostic tool (#6467) * create diagnostic-tool page * add to menu * add to list of tools * syntax fix * syntax fix * syntax fix * syntax fix * rm old --- .../docs/pages/developers/tools/_meta.json | 1 + .../developers/tools/diagnostic-tool.mdx | 134 ++++++++++++++++++ .../operators/performance-and-testing.mdx | 9 +- documentation/docs/pages/operators/tools.mdx | 10 ++ 4 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 documentation/docs/pages/developers/tools/diagnostic-tool.mdx diff --git a/documentation/docs/pages/developers/tools/_meta.json b/documentation/docs/pages/developers/tools/_meta.json index 8aa37f3c3b..d2efea2a75 100644 --- a/documentation/docs/pages/developers/tools/_meta.json +++ b/documentation/docs/pages/developers/tools/_meta.json @@ -1,5 +1,6 @@ { "nym-cli": "Nym-cli", + "diagnostic-tool": "Diagnostic Tool", "echo-server": "Echo Server", "standalone-tcpproxy": "TcpProxy Binaries (Standalone)" } diff --git a/documentation/docs/pages/developers/tools/diagnostic-tool.mdx b/documentation/docs/pages/developers/tools/diagnostic-tool.mdx new file mode 100644 index 0000000000..7c6e61b2d2 --- /dev/null +++ b/documentation/docs/pages/developers/tools/diagnostic-tool.mdx @@ -0,0 +1,134 @@ +import { Steps } from 'nextra/components'; + +# Diagnostic Tool + +The Diagnostic Tool is a standalone binary designed to perform various network tests, including DNS, HTTP, and gateway connectivity tests. This tool helps diagnose connectivity issues and provides insights into network performance. + +It’s also possible tu run it within the daemon with the same CLI interface. + +## Download Binary + +To get `nym-diagnostic` follow these steps: + +###### 1. Download `nym-vpn-core` +- Navigate to [github.com/nymtech/nym-vpn-client/releases](https://github.com/nymtech/nym-vpn-client/releases) +- Find latest `nym-vpn-core-` +- Download version for your system + +###### 2. Install or extract and make executable + +- If you downloaded `.deb` installer, install it with this command: +```sh +sudo dpkg -i +``` + +- If you downloaded `.tar.gz`, in temrinal you can extract the file with +```sh +tar -xvf +``` + +- Navigate inside the directory and make executable: +```sh +cd nym-vpn-core- +chmod +x ./* +``` + + +## CLI Usage + +The Diagnostic Tool can be executed from the command line interface (CLI). Below are the usage instructions and options available. Read in the chapter [*Tests Performed*](#tests-performed) about the purpose and oucome of these commands. + +### Command Syntax + +```sh +./nym-diagnostic [command] [options] +./nym-vpnc diagnostic [command] [options] +``` + +#### `run` command arguments + +The most useful command is `run`, here are the options for that command: + +```sh +-h, --help Display help information and exit. +--skip-dns Skip the DNS tests +--skip-http Skip the HTTP tests +--gateway Run the gateway connectivity test on the given gateway. Skip those tests if not provided +-v, --verbose Enable verbose output for detailed logging. +``` + +#### `register` command arguments + +Command `register` requires valid credential. Here are the options for that command: + +```sh +--gateway Register to the given gateway +--storage-path Path to the directory containing the credentials database. If it is not valid registration will be skipped. +--skip-wireguard Skip Wireguard tests +``` + +### Command Examples + + +- Run all tests on a gateway: +```sh +./nym-diagnostic run --gateway +``` + +- Run the DNS tests only: +```sh +./nym-diagnostic run --skip-http +``` + +- Register to a gateway: +```sh +sudo ./nym-diagnostic register --gateway --storage-path /var/lib/nym-vpnd/mainnet +# sudo is required to read the database +``` + +- You can also run DNS and HTTP tests from `nym-vpnc` (installation [here](/developers/nymvpncli)): +```sh +./nym-vpnc diagnostic run​ +``` + + +## Tests Performed + +The Diagnostic Tool runs the following tests: + + +### 1. DNS Test + +- **Purpose**: To check the resolution DNS availability. +- **Process**: We try to resolve all the domain names present in a given nym network environment with different DNS configurations +- **Output**: Displays the resolved IP address and the time taken for the resolution. + + +### 2. HTTP Test + +- **Purpose**: To verify the accessibility of the NymVPN API. +- **Process**: The tool query the `health` endpoint as well as the `nodes/described` endpoint. +- **Output**: Displays the response of the `health` endpoint, the time skew and the number of nodes in the network (sanity check) + +### 3. Gateway Test + +- **Purpose**: To check the connectivity to a given gateway. +- **Process**: The tool fetches information about the gateway, then establishes a TCP connection, upgrades it to WS and sends a request +- **Output**: Display the gateway reported information, the status of the connections and the WS response. + +### 4. Registration Test + +- **Purpose:** To check the correctness of the registration process. +- **Process:** The tool tries to build a mixnet client to the provided gateway and then tries to register to the entry authenticator +- **Output:** Display the status of the different steps +- **Caveat:** This test requires a credential to be spent, which is why is it available as a separate command only + +### 5. Wireguard Test + +- **Purpose:** To check the soundness of a wireguard connection +- **Process:** The tool uses the registration data from the previous step to establish a wireguard connection and ping an IP. +- **Output:** Display the ping RTTs and any error that might have happened + +## Reports + +Reports are logged in a JSON format and also returned by the commands for a future use \ No newline at end of file diff --git a/documentation/docs/pages/operators/performance-and-testing.mdx b/documentation/docs/pages/operators/performance-and-testing.mdx index 317e4f0978..9e3d96c565 100644 --- a/documentation/docs/pages/operators/performance-and-testing.mdx +++ b/documentation/docs/pages/operators/performance-and-testing.mdx @@ -62,12 +62,13 @@ There are multiple ways to monitor performance of nodes and the machines on whic ### Guides to Setup Own Metrics -A list of different scripts, templates and guides for easier navigation: +A list of different tools, templates and guides for easier navigation: + +* [`nym-gateway-probe`](performance-and-testing/gateway-probe.mdx): a useful tool used under the hood of [Node Status Observatory](https://harbourmaster.nymtech.net) + +* [Diagnostic Tool](/developers/tools/diagnostic-tool): diagnose connectivity issues and provides insights into network performance -* [`nym-gateway-probe`](performance-and-testing/gateway-probe.mdx) - a useful tool used under the hood of [Node Status Observatory](https://harbourmaster.nymtech.net) * [Prometheus and Grafana](performance-and-testing/prometheus-grafana.mdx) self-hosted setup -* [Nym-node CPU cron service](https://gist.github.com/tommyv1987/97e939a7adf491333d686a8eaa68d4bd) - an easy bash script by Nym core developer [@tommy1987](https://gist.github.com/tommyv1987), designed to monitor a CPU usage of your node, running locally -* Nym's script [`prom_targets.py`](https://github.com/nymtech/nym/blob/develop/scripts/prom_targets.py) - a useful python program to request data from API and can be run on its own or plugged to more sophisticated flows ### Collecting Testing Metrics diff --git a/documentation/docs/pages/operators/tools.mdx b/documentation/docs/pages/operators/tools.mdx index c29ecd6038..77ffef91dc 100644 --- a/documentation/docs/pages/operators/tools.mdx +++ b/documentation/docs/pages/operators/tools.mdx @@ -202,3 +202,13 @@ PING_RETRIES=10 PING_TIMEOUT=5 CONCURRENCY=16 ./test-nodes-pings.sh You can look up the IPs from `ping_not_working.csv`, using some online database, like [ipinfo.io](https://ipinfo.io). Feel invited to share the outcome with Nym team, mentors and the rest of the operators in our [Matrix Node Operators channel](https://matrix.to/#/#operators:nymtech.chat). + +## Guides to Setup Own Metrics + +A list of different tools, templates and guides for easier navigation: + +* [`nym-gateway-probe`](performance-and-testing/gateway-probe.mdx): a useful tool used under the hood of [Node Status Observatory](https://harbourmaster.nymtech.net) + +* [Diagnostic Tool](/developers/tools/diagnostic-tool): diagnose connectivity issues and provides insights into network performance + +* [Prometheus and Grafana](performance-and-testing/prometheus-grafana.mdx) self-hosted setup \ No newline at end of file From 94a3599b4d5aa18a10a4d395b0d7b6f3a24c19c6 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:08:04 +0000 Subject: [PATCH 22/51] [DOCs]: Fix missing diagnostic tool in developers menu (#6470) * bump up stats and run prebuild * fix typos --- .../api-scraping-outputs/nyx-outputs/nyx-percent-stake.md | 2 +- .../api-scraping-outputs/nyx-outputs/nyx-total-stake.md | 2 +- .../components/outputs/api-scraping-outputs/time-now.md | 2 +- .../outputs/command-outputs/node-api-check-query-help.md | 2 +- .../outputs/command-outputs/nym-node-cli-install-help.md | 3 ++- .../outputs/command-outputs/nym-node-run-help.md | 4 ---- .../docs/pages/developers/tools/diagnostic-tool.mdx | 8 ++++---- 7 files changed, 10 insertions(+), 13 deletions(-) diff --git a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-percent-stake.md b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-percent-stake.md index 00cd13c087..7b90ea43ab 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-percent-stake.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-percent-stake.md @@ -1 +1 @@ -0.87% +0.90% diff --git a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-total-stake.md b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-total-stake.md index 6b54162743..0df94bdc04 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-total-stake.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/nyx-outputs/nyx-total-stake.md @@ -1 +1 @@ -33.087 +31.932 diff --git a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md index 1aaa64f171..f274715f58 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md @@ -1 +1 @@ -Wednesday, February 11th 2026, 11:35:05 UTC +Thursday, February 19th 2026, 13:59:34 UTC diff --git a/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md b/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md index 999f3d4a6f..bfa068ddb0 100644 --- a/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md +++ b/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md @@ -11,7 +11,7 @@ options: --no_routing_history Display node stats without routing history --no_verloc_metrics Display node stats without verloc metrics -m, --markdown Display results in markdown format - -o, --output [OUTPUT] + -o [OUTPUT], --output [OUTPUT] Save results to file (in current dir or supply with path without filename) ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md index ddc7c867b3..70baf6f736 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md @@ -12,7 +12,8 @@ usage: nym-node-cli install [-h] [-V] [-d BRANCH] [-v] options: -h, --help show this help message and exit -V, --version show program's version number and exit - -d, --dev BRANCH Define github branch (default: develop) + -d BRANCH, --dev BRANCH + Define github branch (default: develop) -v, --verbose Show full error tracebacks --mode {mixnode,entry-gateway,exit-gateway} Node mode: 'mixnode', 'entry-gateway', or 'exit- diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md index 1d0b089b38..ab11bb896e 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md @@ -62,8 +62,6 @@ Options: Tunnel port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] --wireguard-private-network-prefix The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=] - --wireguard-userspace - Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. Useful in containerized environments without kernel WireGuard support [env: NYMNODE_WG_USERSPACE=] [possible values: true, false] --verloc-bind-address Socket address this node will use for binding its verloc API. default: `[::]:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=] --verloc-announce-port @@ -82,8 +80,6 @@ Options: Endpoint to query to retrieve current upgrade mode attestation. This argument should never be set outside testnets and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTATION_URL=] --upgrade-mode-attester-public-key Expected public key of the entity signing the published attestation. This argument should never be set outside testnets and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY=] - --lp-use-mock-ecash - Use mock ecash manager for LP testing. WARNING: Only use this for local testing! Never enable in production. When enabled, the LP listener will accept any credential without blockchain verification [env: NYMNODE_LP_USE_MOCK_ECASH=] [possible values: true, false] --upstream-exit-policy-url Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=] --open-proxy diff --git a/documentation/docs/pages/developers/tools/diagnostic-tool.mdx b/documentation/docs/pages/developers/tools/diagnostic-tool.mdx index 7c6e61b2d2..dec7b67cf2 100644 --- a/documentation/docs/pages/developers/tools/diagnostic-tool.mdx +++ b/documentation/docs/pages/developers/tools/diagnostic-tool.mdx @@ -4,7 +4,7 @@ import { Steps } from 'nextra/components'; The Diagnostic Tool is a standalone binary designed to perform various network tests, including DNS, HTTP, and gateway connectivity tests. This tool helps diagnose connectivity issues and provides insights into network performance. -It’s also possible tu run it within the daemon with the same CLI interface. +It’s also possible to run it within the daemon with the same CLI interface. ## Download Binary @@ -22,7 +22,7 @@ To get `nym-diagnostic` follow these steps: sudo dpkg -i ``` -- If you downloaded `.tar.gz`, in temrinal you can extract the file with +- If you downloaded `.tar.gz`, in terminal you can extract the file with ```sh tar -xvf ``` @@ -36,7 +36,7 @@ chmod +x ./* ## CLI Usage -The Diagnostic Tool can be executed from the command line interface (CLI). Below are the usage instructions and options available. Read in the chapter [*Tests Performed*](#tests-performed) about the purpose and oucome of these commands. +The Diagnostic Tool can be executed from the command line interface (CLI). Below are the usage instructions and options available. Read in the chapter [*Tests Performed*](#tests-performed) about the purpose and outcome of these commands. ### Command Syntax @@ -121,7 +121,7 @@ The Diagnostic Tool runs the following tests: - **Purpose:** To check the correctness of the registration process. - **Process:** The tool tries to build a mixnet client to the provided gateway and then tries to register to the entry authenticator - **Output:** Display the status of the different steps -- **Caveat:** This test requires a credential to be spent, which is why is it available as a separate command only +- **Caveat:** This test requires a credential to be spent, which is why it is available as a separate command only ### 5. Wireguard Test From 453e1cbe707594403c766aed1d887ed099bb00be Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:19:25 +0000 Subject: [PATCH 23/51] [DOCs/operators]: Documentation for SOCKS5 probe score (#6473) * bump up stats and run prebuild * fix typos * add socks5 probe calculation * fix conflicts * fix wording --- .../outputs/api-scraping-outputs/time-now.md | 2 +- .../operators/performance-and-testing.mdx | 43 +++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md index f274715f58..06bc0da1c0 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md @@ -1 +1 @@ -Thursday, February 19th 2026, 13:59:34 UTC +Friday, February 20th 2026, 13:42:15 UTC \ No newline at end of file diff --git a/documentation/docs/pages/operators/performance-and-testing.mdx b/documentation/docs/pages/operators/performance-and-testing.mdx index 9e3d96c565..e4e01f1301 100644 --- a/documentation/docs/pages/operators/performance-and-testing.mdx +++ b/documentation/docs/pages/operators/performance-and-testing.mdx @@ -1,4 +1,5 @@ import { Callout } from 'nextra/components'; +import { Steps } from 'nextra/components'; import { Tabs } from 'nextra/components'; import { MyTab } from 'components/generic-tabs.tsx'; import { AccordionTemplate } from 'components/accordion-template.tsx'; @@ -7,11 +8,11 @@ import NodePerfMixnet from 'components/operators/snippets/node-perf-mixnet.mdx'; # Performance Monitoring & Testing -As Nym developers constantly improve the software, the role of Node Operators is to keep their nodes up to date, monitor their performance and share feedback with the rest of the community and Nym team. Node performance measurements and [server monitoring](#monitoring) are an essential pillar of our common work. +As Nym developers constantly improve the software, the role of Node Operators is to keep their nodes up to date, monitor their performance and share feedback with the rest of the community and Nym team. Node performance measurements and [server monitoring](#monitoring) are an essential pillar of our common work. -Nym Network is routed either through the Mixnet (5-hop) or through Wireguard (2-hop). In all cases Nym node operators always employ only one binary called [`nym-node`](/operators/nodes/nym-node). Through provided arguments (or changes in the config file), `nym-node` can be utilised for different [functionalities](/operators/nodes/nym-node/setup#functionality-mode). However, once it's [registered to Nym Network](/operators/nodes/nym-node/bonding) it's by default available for Nym Mixnet not for Wireguard routing. Only nodes with Wireguard enabled, are also available for Wireguard routing. This creates a situation where every Wireguard enabled `nym-node` is required to have a solid performance score in Mixnet to begin with, but not every Mixnet routing `nym-node` must have Wireguard enabled. +Nym Network is routed either through the Mixnet (5-hop) or through Wireguard (2-hop). In all cases Nym node operators always employ only one binary called [`nym-node`](/operators/nodes/nym-node). Through provided arguments (or changes in the config file), `nym-node` can be utilised for different [functionalities](/operators/nodes/nym-node/setup#functionality-mode). However, once it's [registered to Nym Network](/operators/nodes/nym-node/bonding) it's by default available for Nym Mixnet not for Wireguard routing. Only nodes with Wireguard enabled, are also available for Wireguard routing. This creates a situation where every Wireguard enabled `nym-node` is required to have a solid performance score in Mixnet to begin with, but not every Mixnet routing `nym-node` must have Wireguard enabled. -Given this complexity, we divided the part below about perfromance calculation logic and node selection into two parallel tabs: Mixnet and Wireguard. +Given this complexity, we divided the part below about perfromance calculation logic and node selection into two parallel tabs: Mixnet and Wireguard.
/api/v1/roles ``` +## Socks5 Score Calculation + +Geateway probe also runs tests through a Network requester - a module build as a part of `nym-node`, active only in mode Exit Gateway, used for [Socks5](/developers/clients/socks5) proxy TCP connection. + +Socks5 score is displayed in [Nym Node Status Observatory](https://harbourmaster.nymtech.net) (if you open a page with a particular gateway) and in detail it can be previewed at [mainnet-node-status-api.nymtech.cc/dvpn/v1/directory/gateways](https://mainnet-node-status-api.nymtech.cc/dvpn/v1/directory/gateways) or when running own instance of [Gateway probe](/operators/performance-and-testing/gateway-probe). + +### Socks5 Score Calculation Process + +Socks5 score is defined in the json output of Gateway probe as `"socks5"` key. Here is an example of the dictionary: + +```json + "socks5": { + "can_proxy_https": true, + "score": "medium", + "errors": null + } +``` + +> Note: When we write *gateway* we refer to a `nym-node --mode exit-gateway` in this sub-chapter. + + +1. Gateway gets probed as part of a Gateway probe test where other components get tested as well + +2. Probe tries to connect to the Gateway through Socks5 10 times per testing instance + +3. Latency is calculated as an average of the successful attempts + +4. Gateway is scored as `"low"`, `"medium"`, `"high"` or `"offline"`, in numbers it means: + - `"offline"`: Gateway failed the test 3 or more times (out of 10 attempts) + - `"high"`: Top 50% of nodes with lowest average latency + - `"medium"`: Following 25% of nodes with lowest average latency below top 50% nodes + - `"low"`: Remaining 25% of nodes with the highest average latency time + + ## Monitoring There are multiple ways to monitor performance of nodes and the machines on which they run. For the purpose of maximal privacy and decentralisation of the data - preventing Nym Mixnet from any global adversary takeover - we created these pages as a source of mutual empowerment, a place where operators can share and learn new skills to **setup metrics monitors on their own infrastructure**. @@ -94,4 +129,4 @@ We do testing in order to **understand and increase the overall quality of the N 7. Adjust rewarding based on the machine specs and server pricing Visit [Nym Harbour Master](https://harbourmaster.nymtech.net/) monitoring page to monitor network components (nodes) performance. -*/} \ No newline at end of file +*/} From 8529a3c351ad64f20c45a591731331cfc4e1d46c Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:43:05 +0000 Subject: [PATCH 24/51] [DOCs/operators]: Cleanup (#6474) --- .../docs/pages/operators/performance-and-testing.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/pages/operators/performance-and-testing.mdx b/documentation/docs/pages/operators/performance-and-testing.mdx index e4e01f1301..208530a4c2 100644 --- a/documentation/docs/pages/operators/performance-and-testing.mdx +++ b/documentation/docs/pages/operators/performance-and-testing.mdx @@ -12,7 +12,7 @@ As Nym developers constantly improve the software, the role of Node Operators is Nym Network is routed either through the Mixnet (5-hop) or through Wireguard (2-hop). In all cases Nym node operators always employ only one binary called [`nym-node`](/operators/nodes/nym-node). Through provided arguments (or changes in the config file), `nym-node` can be utilised for different [functionalities](/operators/nodes/nym-node/setup#functionality-mode). However, once it's [registered to Nym Network](/operators/nodes/nym-node/bonding) it's by default available for Nym Mixnet not for Wireguard routing. Only nodes with Wireguard enabled, are also available for Wireguard routing. This creates a situation where every Wireguard enabled `nym-node` is required to have a solid performance score in Mixnet to begin with, but not every Mixnet routing `nym-node` must have Wireguard enabled. -Given this complexity, we divided the part below about perfromance calculation logic and node selection into two parallel tabs: Mixnet and Wireguard. +Given this complexity, we divided the part below about performance calculation logic and node selection into two parallel tabs: Mixnet and Wireguard.
/api/v1/roles ## Socks5 Score Calculation -Geateway probe also runs tests through a Network requester - a module build as a part of `nym-node`, active only in mode Exit Gateway, used for [Socks5](/developers/clients/socks5) proxy TCP connection. +Gateway probe also runs tests through a Network requester - a module build as a part of `nym-node`, active only in mode Exit Gateway, used for [Socks5](/developers/clients/socks5) proxy TCP connection. Socks5 score is displayed in [Nym Node Status Observatory](https://harbourmaster.nymtech.net) (if you open a page with a particular gateway) and in detail it can be previewed at [mainnet-node-status-api.nymtech.cc/dvpn/v1/directory/gateways](https://mainnet-node-status-api.nymtech.cc/dvpn/v1/directory/gateways) or when running own instance of [Gateway probe](/operators/performance-and-testing/gateway-probe). From 6edbece3ad00e082472f1be17fe58b82353bcab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Sat, 21 Feb 2026 19:10:15 +0000 Subject: [PATCH 25/51] bugfix: restore 'latest_measurement' field for nym-node /verloc endpoint (#6452) --- nym-node/nym-node-requests/src/api/v1/metrics/models.rs | 2 ++ nym-node/src/node/http/router/api/v1/metrics/verloc.rs | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/nym-node/nym-node-requests/src/api/v1/metrics/models.rs b/nym-node/nym-node-requests/src/api/v1/metrics/models.rs index 125c8b4352..2d0eddfda4 100644 --- a/nym-node/nym-node-requests/src/api/v1/metrics/models.rs +++ b/nym-node/nym-node-requests/src/api/v1/metrics/models.rs @@ -106,6 +106,8 @@ pub mod verloc { #[serde(with = "bs58_ed25519_pubkey")] #[cfg_attr(feature = "openapi", schema(value_type = String))] pub node_identity: ed25519::PublicKey, + + pub latest_measurement: Option, } #[derive(Serialize, Deserialize, Default, Debug, Clone)] diff --git a/nym-node/src/node/http/router/api/v1/metrics/verloc.rs b/nym-node/src/node/http/router/api/v1/metrics/verloc.rs index 8a2a046637..df8fbb5083 100644 --- a/nym-node/src/node/http/router/api/v1/metrics/verloc.rs +++ b/nym-node/src/node/http/router/api/v1/metrics/verloc.rs @@ -4,7 +4,7 @@ use axum::extract::{Query, State}; use nym_http_api_common::{FormattedResponse, OutputParams}; use nym_node_requests::api::v1::metrics::models::{ - VerlocNodeResult, VerlocResult, VerlocResultData, VerlocStats, + VerlocMeasurement, VerlocNodeResult, VerlocResult, VerlocResultData, VerlocStats, }; use nym_verloc::measurements::SharedVerlocStats; @@ -43,6 +43,12 @@ async fn build_response(verloc_stats: &SharedVerlocStats) -> VerlocStats { .iter() .map(|r| VerlocNodeResult { node_identity: r.node_identity, + latest_measurement: r.latest_measurement.map(|m| VerlocMeasurement { + minimum: m.minimum, + mean: m.mean, + maximum: m.maximum, + standard_deviation: m.standard_deviation, + }), }) .collect(), } From 630c4922ac7dc835afe8709cdf416bfddfee0dee Mon Sep 17 00:00:00 2001 From: mfahampshire Date: Mon, 23 Feb 2026 15:30:49 +0000 Subject: [PATCH 26/51] Max/mixfetch concurrent test (#6417) * * Experiment with changing address mapping from canonical -> full URL as string. * Up MaxConns config. * Bump webpack-cli version * Modify internal-dev tester for concurrent testing * Add logging + POST request to internal-dev/ * push lockfiles * Remove RequestURL from RequestOptions struct for interface * Bump versions + update lockfiles --- Cargo.lock | 2 +- .../packages/mix-fetch-node/package.json | 2 +- .../packages/mix-fetch/package.json | 2 +- wasm/mix-fetch/Cargo.toml | 2 +- wasm/mix-fetch/Makefile | 1 + .../go-mix-conn/internal/mixfetch/mixfetch.go | 55 +- .../go-mix-conn/internal/state/state.go | 45 +- wasm/mix-fetch/internal-dev/index.html | 111 +- wasm/mix-fetch/internal-dev/index.js | 152 +- wasm/mix-fetch/internal-dev/package-lock.json | 1553 ++++++++++++----- wasm/mix-fetch/internal-dev/package.json | 4 +- wasm/mix-fetch/internal-dev/webpack.config.js | 67 +- wasm/mix-fetch/internal-dev/worker.js | 219 +-- wasm/mix-fetch/internal-dev/yarn.lock | 709 ++++---- wasm/mix-fetch/src/client.rs | 2 + 15 files changed, 1910 insertions(+), 1016 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61b7daa89f..f0a471c655 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4954,7 +4954,7 @@ dependencies = [ [[package]] name = "mix-fetch-wasm" -version = "1.4.1" +version = "1.4.2" dependencies = [ "async-trait", "futures", diff --git a/sdk/typescript/packages/mix-fetch-node/package.json b/sdk/typescript/packages/mix-fetch-node/package.json index 6956bd22c0..93cb47e5e6 100644 --- a/sdk/typescript/packages/mix-fetch-node/package.json +++ b/sdk/typescript/packages/mix-fetch-node/package.json @@ -1,6 +1,6 @@ { "name": "@nymproject/mix-fetch-node", - "version": "1.4.1", + "version": "1.4.2", "description": "This package is a drop-in replacement for `fetch` in NodeJS to send HTTP requests over the Nym Mixnet.", "license": "Apache-2.0", "author": "Nym Technologies SA", diff --git a/sdk/typescript/packages/mix-fetch/package.json b/sdk/typescript/packages/mix-fetch/package.json index 41969de717..660ce89fba 100644 --- a/sdk/typescript/packages/mix-fetch/package.json +++ b/sdk/typescript/packages/mix-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@nymproject/mix-fetch", - "version": "1.4.1", + "version": "1.4.2", "description": "This package is a drop-in replacement for `fetch` to send HTTP requests over the Nym Mixnet.", "license": "Apache-2.0", "author": "Nym Technologies SA", diff --git a/wasm/mix-fetch/Cargo.toml b/wasm/mix-fetch/Cargo.toml index f1d81694ad..4ff903254a 100644 --- a/wasm/mix-fetch/Cargo.toml +++ b/wasm/mix-fetch/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mix-fetch-wasm" authors = ["Jedrzej Stuczynski "] -version = "1.4.1" +version = "1.4.2" edition = "2021" keywords = ["nym", "fetch", "wasm", "webassembly", "privacy"] license = "Apache-2.0" diff --git a/wasm/mix-fetch/Makefile b/wasm/mix-fetch/Makefile index 1d6f1d01ab..66a3106377 100644 --- a/wasm/mix-fetch/Makefile +++ b/wasm/mix-fetch/Makefile @@ -10,6 +10,7 @@ build-go-opt: build-rust: taskset -c 0-11 wasm-pack build --scope nymproject --target web --out-dir ../../dist/wasm/mix-fetch + # taskset -c 0-11 wasm-pack build --scope nymproject --target no-modules --out-dir ../../dist/wasm/mix-fetch taskset -c 0-11 wasm-opt -Oz -o ../../dist/wasm/mix-fetch/mix_fetch_wasm_bg.wasm ../../dist/wasm/mix-fetch/mix_fetch_wasm_bg.wasm build-rust-debug: diff --git a/wasm/mix-fetch/go-mix-conn/internal/mixfetch/mixfetch.go b/wasm/mix-fetch/go-mix-conn/internal/mixfetch/mixfetch.go index 7725be27b6..617cd78842 100644 --- a/wasm/mix-fetch/go-mix-conn/internal/mixfetch/mixfetch.go +++ b/wasm/mix-fetch/go-mix-conn/internal/mixfetch/mixfetch.go @@ -142,7 +142,7 @@ func schemeFetch(req *conv.ParsedRequest) error { } } -func dialContext(_ctx context.Context, opts *types.RequestOptions, _network, addr string) (net.Conn, error) { +func dialContext(_ctx context.Context, requestURL string, _network, addr string) (net.Conn, error) { log.Debug("dialing plain connection to %s", addr) requestId, err := rust_bridge.RsStartNewMixnetRequest(addr) @@ -154,12 +154,14 @@ func dialContext(_ctx context.Context, opts *types.RequestOptions, _network, add } conn, inj := state.NewFakeConnection(requestId, addr) - state.ActiveRequests.Insert(requestId, addr, inj) + // Use requestURL (full URL) as the mapping key, meaning we can now + // have concurrent requests to different paths on the same domain. + state.ActiveRequests.Insert(requestId, requestURL, inj) return conn, nil } -func dialTLSContext(_ctx context.Context, opts *types.RequestOptions, _network, addr string) (net.Conn, error) { +func dialTLSContext(_ctx context.Context, requestURL string, _network, addr string) (net.Conn, error) { log.Debug("dialing TLS connection to %s", addr) requestId, err := rust_bridge.RsStartNewMixnetRequest(addr) @@ -171,7 +173,9 @@ func dialTLSContext(_ctx context.Context, opts *types.RequestOptions, _network, } conn, inj := state.NewFakeTlsConn(requestId, addr) - state.ActiveRequests.Insert(requestId, addr, inj) + // Use requestURL (full URL) as the mapping key, meaning we can now + // have concurrent requests to different paths on the same domain. + state.ActiveRequests.Insert(requestId, requestURL, inj) if err := conn.Handshake(); err != nil { return nil, err @@ -180,7 +184,7 @@ func dialTLSContext(_ctx context.Context, opts *types.RequestOptions, _network, return conn, nil } -func buildHttpClient(reqCtx *types.RequestContext, opts *types.RequestOptions) *http.Client { +func buildHttpClient(reqCtx *types.RequestContext, opts *types.RequestOptions, requestURL string) *http.Client { return &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { return checkRedirect(reqCtx, opts, req, via) @@ -188,17 +192,19 @@ func buildHttpClient(reqCtx *types.RequestContext, opts *types.RequestOptions) * Transport: &http.Transport{ DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return dialContext(ctx, opts, network, addr) + return dialContext(ctx, requestURL, network, addr) }, DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return dialTLSContext(ctx, opts, network, addr) + return dialTLSContext(ctx, requestURL, network, addr) }, //TLSClientConfig: &tlsConfig, - DisableKeepAlives: true, - MaxIdleConns: 1, - MaxIdleConnsPerHost: 1, - MaxConnsPerHost: 1, + DisableKeepAlives: true, + // Allow multiple concurrent connections to the same host. + // Previously set to 1. + MaxIdleConns: 10, + MaxIdleConnsPerHost: 10, + MaxConnsPerHost: 10, }, Timeout: state.RequestTimeout, } @@ -270,7 +276,7 @@ func doCorsCheck(reqOpts *types.RequestOptions, resp *http.Response) error { return errors.New("failed cors check") } -func performRequest(req *conv.ParsedRequest) (*conv.ResponseWrapper, error) { +func performRequest(req *conv.ParsedRequest, requestURL string) (*conv.ResponseWrapper, error) { err := mainFetchChecks(req) if err != nil { return nil, err @@ -278,7 +284,7 @@ func performRequest(req *conv.ParsedRequest) (*conv.ResponseWrapper, error) { reqCtx := &types.RequestContext{} - reqClient := buildHttpClient(reqCtx, req.Options) + reqClient := buildHttpClient(reqCtx, req.Options, requestURL) if req.Options.ReferrerPolicy == "" { // 4.1.8 @@ -322,12 +328,15 @@ func performRequest(req *conv.ParsedRequest) (*conv.ResponseWrapper, error) { func onErrCleanup(url *url.URL) { // TODO: cancel stuff here.... somehow... - canonicalAddr := canonicalAddr(url) - id := state.ActiveRequests.GetId(canonicalAddr) + // Use full URL string to match the key used in MixFetch for request deduplication. + // Makes sure we clean up the correct request when multiple requests to + // different paths on the same domain are in process. + requestURL := url.String() + id := state.ActiveRequests.GetId(requestURL) // TODO: can we guarantee that rust is not holding any references to that id (that we don't know on this side)? if id == 0 { // if id doesn't exist it [probably] means the error was thrown before the request was properly created - log.Debug("there doesn't seem to exist a request associated with addr %s", canonicalAddr) + log.Debug("there doesn't seem to exist a request associated with URL %s", requestURL) return } state.ActiveRequests.Remove(id) @@ -341,16 +350,20 @@ func onErrCleanup(url *url.URL) { func MixFetch(request *conv.ParsedRequest) (any, error) { log.Info("_mixFetch: start") - canonical := canonicalAddr(request.Request.URL) - if state.ActiveRequests.ExistsCanonical(canonical) { - // TODO: how to deal with it to allow for concurrent requests to say `https://foo.com/index.html` and `https://foo.com/index.js`? - return nil, errors.New(fmt.Sprintf("there is already an active request for %s", canonical)) + // Use the full URL (inc path and query params) as the deduplication key. + // Allows concurrent requests to different paths on the same domain + // (e.g., foo.com/index.html and foo.com/index.js) while still preventing + // duplicate requests to the exact same URL. + requestURL := request.Request.URL.String() + + if state.ActiveRequests.ExistsCanonical(requestURL) { + return nil, errors.New(fmt.Sprintf("there is already an active request for %s", requestURL)) } resCh := make(chan *conv.ResponseWrapper) errCh := make(chan error) go func(resCh chan *conv.ResponseWrapper, errCh chan error) { - resp, err := performRequest(request) + resp, err := performRequest(request, requestURL) if err != nil { errCh <- err } else { diff --git a/wasm/mix-fetch/go-mix-conn/internal/state/state.go b/wasm/mix-fetch/go-mix-conn/internal/state/state.go index a9b99b9630..b2e8b1e32f 100644 --- a/wasm/mix-fetch/go-mix-conn/internal/state/state.go +++ b/wasm/mix-fetch/go-mix-conn/internal/state/state.go @@ -25,17 +25,23 @@ func InitialiseGlobalState() { } } +// CurrentActiveRequests tracks ongoign requests for thread-safe access. +// The AddressMapping uses full URLs (inc path and query params) as keys, +// allowing concurrent requests to different paths on the same domain while +// preventing duplicate requests to the *exact* same URL. type CurrentActiveRequests struct { sync.Mutex Requests map[types.RequestId]*ActiveRequest - AddressMapping map[string]types.RequestId + AddressMapping map[string]types.RequestId // key is full URL string } -func (ar *CurrentActiveRequests) GetId(canonicalAddr string) types.RequestId { - log.Trace("getting id associated with request for %s", canonicalAddr) +// GetId returns the request ID associated with the given URL string. +// The URL should be the full URL including path and query params. +func (ar *CurrentActiveRequests) GetId(requestURL string) types.RequestId { + log.Trace("getting id associated with request for %s", requestURL) ar.Lock() defer ar.Unlock() - return ar.AddressMapping[canonicalAddr] + return ar.AddressMapping[requestURL] } func (ar *CurrentActiveRequests) Exists(id types.RequestId) bool { @@ -46,25 +52,30 @@ func (ar *CurrentActiveRequests) Exists(id types.RequestId) bool { return exists } -func (ar *CurrentActiveRequests) ExistsCanonical(canonicalAddr string) bool { - return ar.GetId(canonicalAddr) != 0 +// ExistsCanonical checks if there's already an active request for the given URL. +// The URL should be the full URL including path and query params. +// Allows concurrent requests to different paths on the same domain. +func (ar *CurrentActiveRequests) ExistsCanonical(requestURL string) bool { + return ar.GetId(requestURL) != 0 } -func (ar *CurrentActiveRequests) Insert(id types.RequestId, canonicalAddr string, inj ConnectionInjector) { - log.Trace("inserting request %d for %s", id, canonicalAddr) +// Insert adds a new active request to the tracking maps. +// The requestURL should be the full URL including path and query params. +func (ar *CurrentActiveRequests) Insert(id types.RequestId, requestURL string, inj ConnectionInjector) { + log.Trace("inserting request %d for %s", id, requestURL) ar.Lock() defer ar.Unlock() _, exists := ar.Requests[id] if exists { panic("attempted to overwrite active connection id") } - _, exists = ar.AddressMapping[canonicalAddr] + _, exists = ar.AddressMapping[requestURL] if exists { - panic("attempted to overwrite active connection canonicalAddr") + panic("attempted to overwrite active connection for URL") } - ar.Requests[id] = &ActiveRequest{injector: inj, canonicalAddr: canonicalAddr} - ar.AddressMapping[canonicalAddr] = id + ar.Requests[id] = &ActiveRequest{injector: inj, requestURL: requestURL} + ar.AddressMapping[requestURL] = id } func (ar *CurrentActiveRequests) Remove(id types.RequestId) { @@ -75,13 +86,13 @@ func (ar *CurrentActiveRequests) Remove(id types.RequestId) { if !exists { panic("attempted to remove active connection id that doesn't exist") } - _, exists = ar.AddressMapping[req.canonicalAddr] + _, exists = ar.AddressMapping[req.requestURL] if !exists { - panic("attempted to remove active connection canonicalAddr that doesn't exist") + panic("attempted to remove active connection URL that doesn't exist") } delete(ar.Requests, id) - delete(ar.AddressMapping, req.canonicalAddr) + delete(ar.AddressMapping, req.requestURL) } func (ar *CurrentActiveRequests) InjectData(id types.RequestId, data []byte) { @@ -119,6 +130,6 @@ func (ar *CurrentActiveRequests) SendError(id types.RequestId, err error) { } type ActiveRequest struct { - injector ConnectionInjector - canonicalAddr string + injector ConnectionInjector + requestURL string // Full URL including path and query params } diff --git a/wasm/mix-fetch/internal-dev/index.html b/wasm/mix-fetch/internal-dev/index.html index ffe5047581..558751a743 100644 --- a/wasm/mix-fetch/internal-dev/index.html +++ b/wasm/mix-fetch/internal-dev/index.html @@ -1,27 +1,102 @@ - - - - + + + Nym MixFetch Demo - + - -

Mix Fetch Demo

-
- - - -
+ +

Mix Fetch Demo

+
+ MixFetch Configuration +

+ You can either use the default Gateway/NR combo run by us, or have + MixFetch choose a random one on startup. +

+
+ +
+
+ +
+ + Not started +
-
-

- -

+
- +
+ Fetch Controls +
+ + + +
+
+ + + +
+

+ Note: if you're hammering these endpoints and you start to get timeouts + (or you've been reloading the page a lot) then change the target hosts + to e.g. http://ipv4.icanhazip.com and https://ipv6.icanhazip.com/ +

+
+ +

+ This does what is says on the tin - sends 10 fetch requests to + https://jsonplaceholder.typicode.com/posts/ 1 through 10 +

+
+
+
+ + +
+
+ + +
+
+ +
+

Do a POST and get it echoed back

+
- \ No newline at end of file +
+

+ +

+ + diff --git a/wasm/mix-fetch/internal-dev/index.js b/wasm/mix-fetch/internal-dev/index.js index 20c4a6894d..e2b614ab23 100644 --- a/wasm/mix-fetch/internal-dev/index.js +++ b/wasm/mix-fetch/internal-dev/index.js @@ -25,11 +25,36 @@ class WebWorkerClient { const { rawString } = ev.data.args; displayReceivedRawString(rawString) break; + case 'Log': + const { message, level } = ev.data.args; + displayLog(message, level); + break; + case 'MixFetchReady': + onMixFetchReady(); + break; + case 'MixFetchError': + const { error } = ev.data.args; + onMixFetchError(error); + break; } } }; } + startMixFetch = (preferredGateway) => { + if (!this.worker) { + console.error('Could not send message because worker does not exist'); + return; + } + + this.worker.postMessage({ + kind: 'StartMixFetch', + args: { + preferredGateway, + }, + }); + } + doFetch = (target) => { if (!this.worker) { console.error('Could not send message because worker does not exist'); @@ -43,25 +68,140 @@ class WebWorkerClient { }, }); } + + doPost = (url, body) => { + if (!this.worker) { + console.error('Could not send message because worker does not exist'); + return; + } + + this.worker.postMessage({ + kind: 'PostPayload', + args: { + url, + body, + }, + }); + } } let client = null; +const DEFAULT_GATEWAY = "q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1"; + async function main() { client = new WebWorkerClient(); - const fetchButton = document.querySelector('#fetch-button'); - fetchButton.onclick = function () { - doFetch(); + const startButton = document.querySelector('#start-mixfetch'); + startButton.onclick = function () { + const gatewayMode = document.querySelector('input[name="gateway-mode"]:checked').value; + const preferredGateway = gatewayMode === 'default' ? DEFAULT_GATEWAY : undefined; + + startButton.disabled = true; + document.querySelectorAll('input[name="gateway-mode"]').forEach(r => r.disabled = true); + updateStatus('Starting...', 'orange'); + + displayLog(`Starting MixFetch with ${gatewayMode} gateway${preferredGateway ? ` (${preferredGateway})` : ''}...`, 'info'); + client.startMixFetch(preferredGateway); + } + + const fetchButton1 = document.querySelector('#fetch-button-1'); + fetchButton1.onclick = function () { + doFetch(1); + } + + const fetchButton2 = document.querySelector('#fetch-button-2'); + fetchButton2.onclick = function () { + doFetch(2); + } + + const fetch10Button = document.querySelector('#fetch-10-concurrent'); + fetch10Button.onclick = function () { + doFetch10Concurrent(); + } + + const postButton = document.querySelector('#post-button'); + postButton.onclick = function () { + doPost(); } } +function updateStatus(text, color) { + const status = document.getElementById('mixfetch-status'); + status.textContent = text; + status.style.color = color; +} -async function doFetch() { - const payload = document.getElementById('fetch_payload').value; +function onMixFetchReady() { + updateStatus('Ready', 'green'); + document.getElementById('fetch-controls').disabled = false; + displayLog('MixFetch is ready!', 'info'); +} + +function onMixFetchError(error) { + updateStatus('Error: ' + error, 'red'); + document.querySelector('#start-mixfetch').disabled = false; + document.querySelectorAll('input[name="gateway-mode"]').forEach(r => r.disabled = false); + displayLog('MixFetch error: ' + error, 'error'); +} + + +async function doFetch(id) { + const payload = document.getElementById(`fetch_payload_${id}`).value; await client.doFetch(payload) - displaySend(`clicked the button and the payload is: ${payload}...`); + displaySend(`[${id}] clicked the button and the payload is: ${payload}...`); +} + +async function doFetch10Concurrent() { + const baseUrl = 'https://jsonplaceholder.typicode.com/posts/'; + displaySend('Starting 10 concurrent requests to posts/1-10...'); + + const requests = []; + for (let i = 1; i <= 10; i++) { + const url = `${baseUrl}${i}`; + displaySend(`[${i}] Sending request to ${url}`); + requests.push(client.doFetch(url)); + } + + await Promise.all(requests); + displaySend('All 10 concurrent requests dispatched!'); +} + +async function doPost() { + const url = document.getElementById('post_url').value; + const body = document.getElementById('post_body').value; + + displaySend(`[POST] Sending POST request to ${url}`); + displaySend(`[POST] Body: ${body}`); + + await client.doPost(url, body); +} + +/** + * Display log messages from MixFetch. Colors based on level. + * + * @param {string} message + * @param {string} level - 'info', 'error', 'warn', or 'debug' + */ +function displayLog(message, level) { + let timestamp = new Date().toISOString().substr(11, 12); + + const colors = { + info: 'gray', + error: 'red', + warn: 'orange', + debug: 'purple', + }; + + let logDiv = document.createElement('div'); + let paragraph = document.createElement('p'); + paragraph.setAttribute('style', `color: ${colors[level] || 'gray'}`); + let paragraphContent = document.createTextNode(timestamp + ' [' + level.toUpperCase() + '] ' + message); + paragraph.appendChild(paragraphContent); + + logDiv.appendChild(paragraph); + document.getElementById('output').appendChild(logDiv); } /** diff --git a/wasm/mix-fetch/internal-dev/package-lock.json b/wasm/mix-fetch/internal-dev/package-lock.json index afe79b3098..d46f8e3e42 100644 --- a/wasm/mix-fetch/internal-dev/package-lock.json +++ b/wasm/mix-fetch/internal-dev/package-lock.json @@ -18,18 +18,16 @@ "devDependencies": { "copy-webpack-plugin": "^11.0.0", "hello-wasm-pack": "^0.1.0", - "webpack": "^5.105.0", - "webpack-cli": "^4.9.2", + "webpack": "^5.98.0", + "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.2.1" } }, - "../go-mix-conn/build": { - "name": "go-mix-conn", - "version": "0.0.1" - }, + "../go-mix-conn/build": {}, "../pkg": { "name": "@nymproject/mix-fetch-wasm", - "version": "1.2.2" + "version": "1.4.2", + "license": "Apache-2.0" }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", @@ -108,17 +106,369 @@ "tslib": "2" } }, - "node_modules/@jsonjoy.com/json-pack": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz", - "integrity": "sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==", + "node_modules/@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-core": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.56.10.tgz", + "integrity": "sha512-PyAEA/3cnHhsGcdY+AmIU+ZPqTuZkDhCXQ2wkXypdLitSpd6d5Ivxhnq4wa2ETRWFVJGabYynBWxIijOswSmOw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/base64": "^1.1.1", - "@jsonjoy.com/util": "^1.1.2", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-fsa": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.56.10.tgz", + "integrity": "sha512-/FVK63ysNzTPOnCCcPoPHt77TOmachdMS422txM4KhxddLdbW1fIbFMYH0AM0ow/YchCyS5gqEjKLNyv71j/5Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.56.10.tgz", + "integrity": "sha512-7R4Gv3tkUdW3dXfXiOkqxkElxKNVdd8BDOWC0/dbERd0pXpPY+s2s1Mino+aTvkGrFPiY+mmVxA7zhskm4Ue4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "@jsonjoy.com/fs-print": "4.56.10", + "@jsonjoy.com/fs-snapshot": "4.56.10", + "glob-to-regex.js": "^1.0.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-builtins": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.56.10.tgz", + "integrity": "sha512-uUnKz8R0YJyKq5jXpZtkGV9U0pJDt8hmYcLRrPjROheIfjMXsz82kXMgAA/qNg0wrZ1Kv+hrg7azqEZx6XZCVw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-to-fsa": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.56.10.tgz", + "integrity": "sha512-oH+O6Y4lhn9NyG6aEoFwIBNKZeYy66toP5LJcDOMBgL99BKQMUf/zWJspdRhMdn/3hbzQsZ8EHHsuekbFLGUWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-fsa": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-utils": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.56.10.tgz", + "integrity": "sha512-8EuPBgVI2aDPwFdaNQeNpHsyqPi3rr+85tMNG/lHvQLiVjzoZsvxA//Xd8aB567LUhy4QS03ptT+unkD/DIsNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.56.10" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-print": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.56.10.tgz", + "integrity": "sha512-JW4fp5mAYepzFsSGrQ48ep8FXxpg4niFWHdF78wDrFGof7F3tKDJln72QFDEn/27M1yHd4v7sKHHVPh78aWcEw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-utils": "4.56.10", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.56.10.tgz", + "integrity": "sha512-DkR6l5fj7+qj0+fVKm/OOXMGfDFCGXLfyHkORH3DF8hxkpDgIHbhf/DwncBMs2igu/ST7OEkexn1gIqoU6Y+9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", "hyperdyperid": "^1.2.0", - "thingies": "^1.20.0" + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/util": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" }, "engines": { "node": ">=10.0" @@ -132,9 +482,30 @@ } }, "node_modules/@jsonjoy.com/util": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz", - "integrity": "sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -155,6 +526,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -197,6 +581,165 @@ "resolved": "../pkg", "link": true }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", + "integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/asn1-x509-attr": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz", + "integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz", + "integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz", + "integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.1", + "@peculiar/asn1-pkcs8": "^2.6.1", + "@peculiar/asn1-rsa": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz", + "integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz", + "integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.1", + "@peculiar/asn1-pfx": "^2.6.1", + "@peculiar/asn1-pkcs8": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/asn1-x509-attr": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz", + "integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", + "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz", + "integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz", + "integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/x509": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", + "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-csr": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.0", + "@peculiar/asn1-pkcs9": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -269,22 +812,22 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", - "@types/serve-static": "*" + "@types/serve-static": "^1" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", "dev": true, "license": "MIT", "dependencies": { @@ -302,9 +845,9 @@ "license": "MIT" }, "node_modules/@types/http-proxy": { - "version": "1.17.16", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", - "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", + "version": "1.17.17", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", + "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", "dev": true, "license": "MIT", "dependencies": { @@ -326,20 +869,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz", + "integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "undici-types": "~7.18.0" } }, "node_modules/@types/qs": { @@ -364,13 +900,12 @@ "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -385,15 +920,26 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", "@types/node": "*", - "@types/send": "*" + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, "node_modules/@types/sockjs": { @@ -578,37 +1124,45 @@ } }, "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, "license": "MIT", - "dependencies": { - "envinfo": "^7.7.3" + "engines": { + "node": ">=14.15.0" }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -644,10 +1198,20 @@ "node": ">= 0.6" } }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -671,16 +1235,16 @@ } }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -745,14 +1309,39 @@ "node": ">= 8" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asn1js": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz", + "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/batch": { @@ -776,40 +1365,30 @@ } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/bonjour-service": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", @@ -892,15 +1471,25 @@ } }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -933,9 +1522,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001769", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", - "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "version": "1.0.30001774", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", + "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", "dev": true, "funding": [ { @@ -992,9 +1581,9 @@ } }, "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", "engines": { @@ -1017,9 +1606,9 @@ } }, "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, "license": "MIT" }, @@ -1044,31 +1633,24 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", @@ -1103,9 +1685,9 @@ } }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, "license": "MIT", "engines": { @@ -1113,9 +1695,9 @@ } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "dev": true, "license": "MIT" }, @@ -1152,9 +1734,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -1177,9 +1759,9 @@ } }, "node_modules/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "dev": true, "license": "MIT", "dependencies": { @@ -1194,9 +1776,9 @@ } }, "node_modules/default-browser-id": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "dev": true, "license": "MIT", "engines": { @@ -1296,9 +1878,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", "dev": true, "license": "ISC" }, @@ -1327,9 +1909,9 @@ } }, "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "dev": true, "license": "MIT", "bin": { @@ -1517,29 +2099,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/express/node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1548,9 +2107,9 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -1558,7 +2117,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -1577,6 +2136,23 @@ "node": ">= 6" } }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -1588,9 +2164,9 @@ } }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { @@ -1624,18 +2200,18 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "dev": true, "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "statuses": "2.0.1", + "statuses": "~2.0.2", "unpipe": "~1.0.0" }, "engines": { @@ -1656,10 +2232,20 @@ "node": ">=8" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -1774,6 +2360,23 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", @@ -1832,19 +2435,6 @@ "dev": true, "license": "MIT" }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1941,26 +2531,30 @@ "license": "MIT" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", "dev": true, "license": "MIT" }, @@ -2028,9 +2622,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -2038,9 +2632,9 @@ } }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", "dependencies": { @@ -2065,19 +2659,19 @@ "license": "ISC" }, "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" } }, "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", "dev": true, "license": "MIT", "engines": { @@ -2098,13 +2692,16 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2169,9 +2766,9 @@ } }, "node_modules/is-network-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", - "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", "dev": true, "license": "MIT", "engines": { @@ -2218,9 +2815,9 @@ } }, "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", "dev": true, "license": "MIT", "dependencies": { @@ -2297,14 +2894,14 @@ } }, "node_modules/launch-editor": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", - "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.0.tgz", + "integrity": "sha512-u+9asUHMJ99lA15VRMXw5XKfySFR9dGXwgsgS14YTbUq3GITP58mIM32At90P5fZ+MUId5Yw+IwI/yKub7jnCQ==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" } }, "node_modules/loader-runner": { @@ -2355,23 +2952,33 @@ } }, "node_modules/memfs": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz", - "integrity": "sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==", + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.56.10.tgz", + "integrity": "sha512-eLvzyrwqLHnLYalJP7YZ3wBe79MXktMdfQbvMrVD80K+NhrIukCVBvgP30zTJYEEDh9hZ/ep9z0KOdD7FSHo7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.3.0", - "tree-dump": "^1.0.1", + "@jsonjoy.com/fs-core": "4.56.10", + "@jsonjoy.com/fs-fsa": "4.56.10", + "@jsonjoy.com/fs-node": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-to-fsa": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "@jsonjoy.com/fs-print": "4.56.10", + "@jsonjoy.com/fs-snapshot": "4.56.10", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", "tslib": "^2.0.0" }, - "engines": { - "node": ">= 4.0.0" - }, "funding": { "type": "github", "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, "node_modules/merge-descriptors": { @@ -2412,13 +3019,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -2490,9 +3097,9 @@ } }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "dev": true, "license": "MIT", "engines": { @@ -2506,16 +3113,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", - "dev": true, - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -2567,9 +3164,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, "license": "MIT", "engines": { @@ -2577,16 +3174,16 @@ } }, "node_modules/open": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", - "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, "license": "MIT", "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" + "wsl-utils": "^0.1.0" }, "engines": { "node": ">=18" @@ -2739,6 +3336,24 @@ "node": ">=8" } }, + "node_modules/pkijs": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz", + "integrity": "sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2770,24 +3385,34 @@ "node": ">= 0.10" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=16.0.0" } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -2838,31 +3463,21 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -2892,18 +3507,25 @@ } }, "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "license": "MIT", "dependencies": { - "resolve": "^1.9.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2922,19 +3544,22 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2973,9 +3598,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -2984,9 +3609,9 @@ } }, "node_modules/run-applescript": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "dev": true, "license": "MIT", "engines": { @@ -3076,54 +3701,44 @@ "license": "MIT" }, "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", + "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", "dev": true, "license": "MIT", "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" + "@peculiar/x509": "^1.14.2", + "pkijs": "^3.3.3" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "dev": true, "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3142,22 +3757,26 @@ } }, "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", "dev": true, "license": "MIT", "dependencies": { - "accepts": "~1.3.4", + "accepts": "~1.3.8", "batch": "0.6.1", "debug": "2.6.9", "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" }, "engines": { "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serve-index/node_modules/depd": { @@ -3171,35 +3790,22 @@ } }, "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, "license": "MIT", "dependencies": { "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.6" } }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true, - "license": "ISC" - }, "node_modules/serve-index/node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -3211,16 +3817,16 @@ } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "dev": true, "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" @@ -3437,13 +4043,13 @@ } }, "node_modules/spdy-transport/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3455,20 +4061,20 @@ } }, "node_modules/spdy-transport/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/spdy/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3480,16 +4086,16 @@ } }, "node_modules/spdy/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { @@ -3604,14 +4210,18 @@ } }, "node_modules/thingies": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", - "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz", + "integrity": "sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==", "dev": true, - "license": "Unlicense", + "license": "MIT", "engines": { "node": ">=10.18" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, "peerDependencies": { "tslib": "^2" } @@ -3647,9 +4257,9 @@ } }, "node_modules/tree-dump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz", - "integrity": "sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3670,6 +4280,26 @@ "dev": true, "license": "0BSD" }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3684,6 +4314,13 @@ "node": ">= 0.6" } }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3725,16 +4362,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3797,9 +4424,9 @@ } }, "node_modules/webpack": { - "version": "5.105.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", - "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "version": "5.105.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz", + "integrity": "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==", "dev": true, "license": "MIT", "dependencies": { @@ -3846,45 +4473,43 @@ } }, "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x" + "webpack": "5.x.x" }, "peerDependenciesMeta": { "@webpack-cli/generators": { "optional": true }, - "@webpack-cli/migrate": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -3894,25 +4519,25 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=14" } }, "node_modules/webpack-dev-middleware": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", - "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz", + "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==", "dev": true, "license": "MIT", "dependencies": { "colorette": "^2.0.10", - "memfs": "^4.6.0", - "mime-types": "^2.1.31", + "memfs": "^4.43.1", + "mime-types": "^3.0.1", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" @@ -3933,16 +4558,43 @@ } } }, + "node_modules/webpack-dev-middleware/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/webpack-dev-server": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz", - "integrity": "sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", + "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", "dev": true, "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", - "@types/express": "^4.17.21", + "@types/express": "^4.17.25", "@types/express-serve-static-core": "^4.17.21", "@types/serve-index": "^1.9.4", "@types/serve-static": "^1.15.5", @@ -3952,17 +4604,17 @@ "bonjour-service": "^1.2.1", "chokidar": "^3.6.0", "colorette": "^2.0.10", - "compression": "^1.7.4", + "compression": "^1.8.1", "connect-history-api-fallback": "^2.0.0", - "express": "^4.21.2", + "express": "^4.22.1", "graceful-fs": "^4.2.6", - "http-proxy-middleware": "^2.0.7", + "http-proxy-middleware": "^2.0.9", "ipaddr.js": "^2.1.0", "launch-editor": "^2.6.1", "open": "^10.0.3", "p-retry": "^6.2.0", "schema-utils": "^4.2.0", - "selfsigned": "^2.4.1", + "selfsigned": "^5.5.0", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", @@ -3992,13 +4644,14 @@ } }, "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", + "flat": "^5.0.2", "wildcard": "^2.0.0" }, "engines": { @@ -4006,9 +4659,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", "dev": true, "license": "MIT", "engines": { @@ -4057,16 +4710,16 @@ } }, "node_modules/wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true, "license": "MIT" }, "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "dev": true, "license": "MIT", "engines": { @@ -4084,6 +4737,22 @@ "optional": true } } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/wasm/mix-fetch/internal-dev/package.json b/wasm/mix-fetch/internal-dev/package.json index 6eb75e1998..7f837369f4 100644 --- a/wasm/mix-fetch/internal-dev/package.json +++ b/wasm/mix-fetch/internal-dev/package.json @@ -30,8 +30,8 @@ "devDependencies": { "copy-webpack-plugin": "^11.0.0", "hello-wasm-pack": "^0.1.0", - "webpack": "^5.105.0", - "webpack-cli": "^4.9.2", + "webpack": "^5.98.0", + "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.2.1" }, "dependencies": { diff --git a/wasm/mix-fetch/internal-dev/webpack.config.js b/wasm/mix-fetch/internal-dev/webpack.config.js index 1cf7d49e34..dbf1df203a 100644 --- a/wasm/mix-fetch/internal-dev/webpack.config.js +++ b/wasm/mix-fetch/internal-dev/webpack.config.js @@ -1,37 +1,36 @@ -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const path = require('path'); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const path = require("path"); module.exports = { - performance: { - hints: false, - maxEntrypointSize: 512000, - maxAssetSize: 512000 - }, - entry: { - bootstrap: './bootstrap.js', - worker: './worker.js', - }, - output: { - path: path.resolve(__dirname, 'dist'), - filename: '[name].js', - }, - mode: 'development', - // mode: 'production', - plugins: [ - new CopyWebpackPlugin({ - patterns: [ - 'index.html', - { - from: '../pkg/*.(js|wasm)', - to: '[name][ext]', - }, - { - from: '../go-mix-conn/build/*.(js|wasm)', - to: '[name][ext]', - }, - ], - }), - - ], - experiments: { syncWebAssembly: true }, + performance: { + hints: false, + maxEntrypointSize: 512000, + maxAssetSize: 512000, + }, + entry: { + bootstrap: "./bootstrap.js", + worker: "./worker.js", + }, + output: { + path: path.resolve(__dirname, "dist"), + filename: "[name].js", + }, + mode: "development", + // mode: 'production', + plugins: [ + new CopyWebpackPlugin({ + patterns: [ + "index.html", + { + from: "../pkg/*.(js|wasm)", + to: "[name][ext]", + }, + { + from: "../go-mix-conn/build/*.(js|wasm)", + to: "[name][ext]", + }, + ], + }), + ], + experiments: { syncWebAssembly: true }, }; diff --git a/wasm/mix-fetch/internal-dev/worker.js b/wasm/mix-fetch/internal-dev/worker.js index b420bd8034..3ccc2cb5a6 100644 --- a/wasm/mix-fetch/internal-dev/worker.js +++ b/wasm/mix-fetch/internal-dev/worker.js @@ -43,6 +43,25 @@ let client = null; let tester = null; const go = new Go(); // Defined in wasm_exec.js var goWasm; +let mixFetchReady = false; + +function sendLog(message, level = 'info') { + self.postMessage({ + kind: 'Log', + args: { message, level }, + }); +} + +function sendReady() { + self.postMessage({ kind: 'MixFetchReady' }); +} + +function sendError(error) { + self.postMessage({ + kind: 'MixFetchError', + args: { error: String(error) }, + }); +} async function logFetchResult(res) { console.log(res) @@ -121,114 +140,117 @@ async function wasm_bindgenSetup() { // await setupMixFetchWithConfig(config, { storagePassphrase: "foomp", preferredGateway }) } -async function nativeSetup() { - const preferredGateway = "8ookuLkA9oWfRTjb7Jq4tLGcWrqoXKGQxw84MjMrv2S4"; - const validator = 'https://sandbox-nym-api1.nymtech.net/api'; +async function nativeSetup(preferredGateway) { + sendLog('Setting up MixFetch...'); + if (preferredGateway) { + sendLog(`Using preferred gateway: ${preferredGateway}`); + } else { + sendLog('Using random gateway selection'); + } - // local - // const preferredNetworkRequester= "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi.6uQGnCqSczpXwh86NdbsCoDDXuqZQM9Uwko8GE7uC9g8@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM"; - // const preferredNetworkRequester= "GqiGWmKRCbGQFSqH88BzLKijvZgipnqhmbNFsmkZw84t.4L8sXFuAUyUYyHZYgMdM3AtiusKnYUft6Pd8e41rrCHA@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM"; - - // those are just some examples, there are obviously more permutations; - // note, the extra optional argument is of the following type: - /* - export interface MixFetchOpts extends MixFetchOptsSimple { - clientId?: string; - nymApiUrl?: string; - nyxdUrl?: string; - clientOverride?: DebugWasmOverride; - mixFetchOverride?: MixFetchDebugOverride; - } - - where - export interface MixFetchDebugOverride { - requestTimeoutMs?: number; - } - - and `DebugWasmOverride` is a rather nested struct that you can look up yourself : ) - */ - - // #1 - // await setupMixFetch(preferredNetworkRequester) - // #2 - // await setupMixFetch(preferredNetworkRequester, { nymApiUrl: validator }) - // // #3 const noCoverTrafficOverride = { traffic: {disableMainPoissonPacketDistribution: true}, coverTraffic: {disableLoopCoverTrafficStream: true}, } const mixFetchOverride = { - requestTimeoutMs: 20000 + requestTimeoutMs: 60000 } - await setupMixFetch({ - // preferredNetworkRequester, - // preferredGateway: preferredGateway, - // storagePassphrase: "foomp", + const opts = { forceTls: true, - // nymApiUrl: validator, clientId: "my-client", clientOverride: noCoverTrafficOverride, mixFetchOverride, - }) + }; + + if (preferredGateway) { + opts.preferredGateway = preferredGateway; + } + + sendLog('Calling setupMixFetch...'); + await setupMixFetch(opts); + sendLog('setupMixFetch completed'); } -async function testMixFetch() { - console.log('Instantiating Mix Fetch...'); - // await wasm_bindgenSetup() +async function startMixFetch(preferredGateway) { + sendLog('Instantiating MixFetch...'); - await nativeSetup() + try { + await nativeSetup(preferredGateway); + mixFetchReady = true; + sendLog('MixFetch client running!'); + sendReady(); + } catch (e) { + sendLog('Failed to start MixFetch: ' + e, 'error'); + sendError(e); + } +} +async function handleFetchPayload(target) { + if (!mixFetchReady) { + sendLog('MixFetch not ready yet', 'error'); + return; + } - console.log('Mix Fetch client running!'); + const url = target; + const args = {mode: "unsafe-ignore-cors"}; - // Set callback to handle messages passed to the worker. + try { + sendLog(`Fetching: ${url}`); + const mixFetchRes = await mixFetch(url, args); + sendLog('Fetch completed'); + await logFetchResult(mixFetchRes); + } catch (e) { + sendLog('Fetch request failure: ' + e, 'error'); + console.error("mix fetch request failure: ", e); + } +} + +async function handlePostPayload(url, body) { + if (!mixFetchReady) { + sendLog('MixFetch not ready yet', 'error'); + return; + } + + const args = { + method: 'POST', + mode: "unsafe-ignore-cors", + headers: { + 'Content-Type': 'application/json', + }, + body: body, + }; + + try { + sendLog(`POST request to: ${url}`); + sendLog(`POST body: ${body}`); + const mixFetchRes = await mixFetch(url, args); + sendLog('POST completed'); + await logFetchResult(mixFetchRes); + } catch (e) { + sendLog('POST request failure: ' + e, 'error'); + console.error("mix fetch POST request failure: ", e); + } +} + +function setupMessageHandler() { self.onmessage = async event => { if (event.data && event.data.kind) { switch (event.data.kind) { + case 'StartMixFetch': { + const { preferredGateway } = event.data.args; + await startMixFetch(preferredGateway); + break; + } case 'FetchPayload': { - const {target} = event.data.args; - const url = target; - - // const args = { mode: "ors", redirect: "manual", signal } - const args = {mode: "unsafe-ignore-cors"} - - try { - console.log('using mixFetch...'); - const mixFetchRes = await mixFetch(url, args) - console.log(">>> MIX FETCH") - await logFetchResult(mixFetchRes) - - console.log('done') - - } catch (e) { - console.error("mix fetch request failure: ", e) - } - - // console.log("will disconnect"); - // await disconnectMixFetch() - // - // try { - // console.log('using mixFetch...'); - // const mixFetchRes = await mixFetch(url, args) - // console.log(">>> MIX FETCH") - // await logFetchResult(mixFetchRes) - // - // console.log('done') - // - // } catch(e) { - // console.error("mix fetch request failure: ", e) - // } - - - // try { - // console.log('using normal Fetch...'); - // const fetchRes = await fetch(url, args) - // console.log(">>> NORMAL FETCH") - // await logFetchResult(fetchRes) - // } catch(e) { - // console.error("fetch request failure: ", e) - // } + const { target } = event.data.args; + await handleFetchPayload(target); + break; + } + case 'PostPayload': { + const { url, body } = event.data.args; + await handlePostPayload(url, body); + break; } } } @@ -263,15 +285,17 @@ function setupRsGoBridge() { } async function main() { - console.log(">>>>>>>>>>>>>>>>>>>>> JS WORKER MAIN START"); + sendLog('Worker starting...'); // load rust WASM package + sendLog('Loading Rust WASM...'); await wasm_bindgen(RUST_WASM_URL); - console.log('Loaded RUST WASM'); + sendLog('Loaded Rust WASM'); // load go WASM package + sendLog('Loading Go WASM...'); await loadGoWasm(); - console.log("Loaded GO WASM"); + sendLog('Loaded Go WASM'); // sets up better stack traces in case of in-rust panics set_panic_hook(); @@ -280,19 +304,10 @@ async function main() { goWasmSetLogging("trace") - // test mixFetch - await testMixFetch(); - // - // // run test on simplified and dedicated tester: - // // await testWithTester() - // - // // hook-up the whole client for testing - // // await testWithNymClient() - // - // // 'Normal' client setup (to send 'normal' messages) - // // await normalNymClientUsage() - // - console.log(">>>>>>>>>>>>>>>>>>>>> JS WORKER MAIN END") + // Set up message handler (MixFetch will be started on demand) + setupMessageHandler(); + + sendLog('Worker ready - click Start MixFetch to begin'); } // Let's get started! diff --git a/wasm/mix-fetch/internal-dev/yarn.lock b/wasm/mix-fetch/internal-dev/yarn.lock index 484b776f5b..3fc632d7ac 100644 --- a/wasm/mix-fetch/internal-dev/yarn.lock +++ b/wasm/mix-fetch/internal-dev/yarn.lock @@ -8,58 +8,47 @@ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== "@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + version "0.3.13" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - "@jridgewell/source-map@^0.3.3": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" - integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + version "0.3.11" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz" + integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA== dependencies: "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.14": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + version "0.3.31" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" "@jsonjoy.com/base64@^1.1.1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" + resolved "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz" integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== "@jsonjoy.com/json-pack@^1.0.3": version "1.2.0" - resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz#e658900e81d194903171c42546e1aa27f446846a" + resolved "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz" integrity sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA== dependencies: "@jsonjoy.com/base64" "^1.1.1" @@ -69,13 +58,13 @@ "@jsonjoy.com/util@^1.1.2", "@jsonjoy.com/util@^1.3.0": version "1.6.0" - resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.6.0.tgz#23991b2fe12cb3a006573d9dc97c768d3ed2c9f1" + resolved "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz" integrity sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A== "@leichtgewicht/ip-codec@^2.0.1": - version "2.0.4" - resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" - integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + version "2.0.5" + resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz" + integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -85,7 +74,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -99,62 +88,63 @@ fastq "^1.6.0" "@nymproject/mix-fetch-wasm@file:../pkg": - version "1.2.2" + version "1.4.1" + resolved "file:../pkg" "@types/body-parser@*": - version "1.19.2" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + version "1.19.6" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz" + integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== dependencies: "@types/connect" "*" "@types/node" "*" "@types/bonjour@^3.5.13": version "3.5.13" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz" integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" "@types/connect-history-api-fallback@^1.5.4": version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz" integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" "@types/connect@*": - version "3.4.35" - resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + version "3.4.38" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== dependencies: "@types/node" "*" "@types/eslint-scope@^3.7.7": version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz" integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.37.0" - resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz" - integrity sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ== + version "9.6.1" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*", "@types/estree@^1.0.8": version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.33": version "4.19.6" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz" integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== dependencies: "@types/node" "*" @@ -162,9 +152,9 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@*", "@types/express@^4.17.21": +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.21": version "4.17.23" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.23.tgz#35af3193c640bfd4d7fe77191cd0ed411a433bef" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz" integrity sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ== dependencies: "@types/body-parser" "*" @@ -174,29 +164,29 @@ "@types/http-errors@*": version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz" integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== "@types/http-proxy@^1.17.8": - version "1.17.10" - resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz" - integrity sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g== + version "1.17.16" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz" + integrity sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w== dependencies: "@types/node" "*" "@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/mime@^1": version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== "@types/node-forge@^1.3.0": version "1.3.11" - resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + resolved "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz" integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== dependencies: "@types/node" "*" @@ -207,23 +197,23 @@ integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== "@types/qs@*": - version "6.9.7" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + version "6.14.0" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz" + integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== "@types/range-parser@*": - version "1.2.4" - resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + version "1.2.7" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== "@types/retry@0.12.2": version "0.12.2" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz" integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== "@types/send@*": version "0.17.5" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.5.tgz#d991d4f2b16f2b1ef497131f00a9114290791e74" + resolved "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz" integrity sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w== dependencies: "@types/mime" "^1" @@ -231,14 +221,14 @@ "@types/serve-index@^1.9.4": version "1.9.4" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz" integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" "@types/serve-static@*", "@types/serve-static@^1.15.5": version "1.15.8" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.8.tgz#8180c3fbe4a70e8f00b9f70b9ba7f08f35987877" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz" integrity sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg== dependencies: "@types/http-errors" "*" @@ -247,21 +237,21 @@ "@types/sockjs@^0.3.36": version "0.3.36" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz" integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" "@types/ws@^8.5.10": version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz" integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== dependencies: "@types/node" "*" -"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": +"@webassemblyjs/ast@^1.14.1", "@webassemblyjs/ast@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz" integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: "@webassemblyjs/helper-numbers" "1.13.2" @@ -269,22 +259,22 @@ "@webassemblyjs/floating-point-hex-parser@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz" integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== "@webassemblyjs/helper-api-error@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz" integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== "@webassemblyjs/helper-buffer@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz" integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== "@webassemblyjs/helper-numbers@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz" integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: "@webassemblyjs/floating-point-hex-parser" "1.13.2" @@ -293,12 +283,12 @@ "@webassemblyjs/helper-wasm-bytecode@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz" integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== "@webassemblyjs/helper-wasm-section@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz" integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -308,26 +298,26 @@ "@webassemblyjs/ieee754@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz" integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== dependencies: "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz" integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== dependencies: "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz" integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== "@webassemblyjs/wasm-edit@^1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz" integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -341,7 +331,7 @@ "@webassemblyjs/wasm-gen@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz" integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -352,7 +342,7 @@ "@webassemblyjs/wasm-opt@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz" integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -360,9 +350,9 @@ "@webassemblyjs/wasm-gen" "1.14.1" "@webassemblyjs/wasm-parser" "1.14.1" -"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": +"@webassemblyjs/wasm-parser@^1.14.1", "@webassemblyjs/wasm-parser@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz" integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -374,28 +364,26 @@ "@webassemblyjs/wast-printer@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz" integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== dependencies: "@webassemblyjs/ast" "1.14.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^1.2.0": - version "1.2.0" - resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz" - integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== -"@webpack-cli/info@^1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz" - integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== - dependencies: - envinfo "^7.7.3" +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== -"@webpack-cli/serve@^1.7.0": - version "1.7.0" - resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz" - integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -417,12 +405,12 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: acorn-import-phases@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" + resolved "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz" integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== -acorn@^8.15.0, acorn@^8.8.2: +acorn@^8.14.0, acorn@^8.15.0: version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== ajv-formats@^2.1.1: @@ -439,7 +427,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^8.0.0: +ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -449,16 +437,6 @@ ajv@^8.0.0: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^8.9.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" @@ -479,7 +457,7 @@ array-flatten@1.1.1: baseline-browser-mapping@^2.9.0: version "2.9.19" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" + resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz" integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== batch@0.6.1: @@ -488,31 +466,31 @@ batch@0.6.1: integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + version "2.3.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== body-parser@~1.20.3: - version "1.20.4" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f" - integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== + version "1.20.3" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== dependencies: - bytes "~3.1.2" + bytes "3.1.2" content-type "~1.0.5" debug "2.6.9" depd "2.0.0" - destroy "~1.2.0" - http-errors "~2.0.1" - iconv-lite "~0.4.24" - on-finished "~2.4.1" - qs "~6.14.0" - raw-body "~2.5.3" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" type-is "~1.6.18" - unpipe "~1.0.0" + unpipe "1.0.0" bonjour-service@^1.2.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.3.0.tgz#80d867430b5a0da64e82a8047fc1e355bdb71722" + resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz" integrity sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA== dependencies: fast-deep-equal "^3.1.3" @@ -525,9 +503,9 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.28.1: +browserslist@^4.28.1, "browserslist@>= 4.21.0": version "4.28.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz" integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== dependencies: baseline-browser-mapping "^2.9.0" @@ -543,7 +521,7 @@ buffer-from@^1.0.0: bundle-name@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + resolved "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz" integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== dependencies: run-applescript "^7.0.0" @@ -553,14 +531,14 @@ bytes@3.0.0: resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== -bytes@~3.1.2: +bytes@3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: es-errors "^1.3.0" @@ -568,7 +546,7 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: call-bound@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== dependencies: call-bind-apply-helpers "^1.0.2" @@ -576,12 +554,12 @@ call-bound@^1.0.2: caniuse-lite@^1.0.30001759: version "1.0.30001769" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz#1ad91594fad7dc233777c2781879ab5409f7d9c2" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz" integrity sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg== chokidar@^3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== dependencies: anymatch "~3.1.2" @@ -613,16 +591,16 @@ colorette@^2.0.10, colorette@^2.0.14: resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.20.0: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^7.0.0: - version "7.2.0" - resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - compressible@~2.0.16: version "2.0.18" resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" @@ -650,7 +628,7 @@ connect-history-api-fallback@^2.0.0: content-disposition@~0.5.4: version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" @@ -661,14 +639,14 @@ content-type@~1.0.4, content-type@~1.0.5: integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== cookie-signature@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.7.tgz#ab5dd7ab757c54e60f37ef6550f481c426d10454" - integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA== + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== cookie@~0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" - integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + version "0.7.1" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== copy-webpack-plugin@^11.0.0: version "11.0.0" @@ -696,13 +674,6 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@^4.1.0: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" @@ -710,14 +681,21 @@ debug@^4.1.0: dependencies: ms "2.1.2" +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + default-browser-id@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz" integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== default-browser@^5.2.1: version "5.2.1" - resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz" integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== dependencies: bundle-name "^4.1.0" @@ -725,20 +703,20 @@ default-browser@^5.2.1: define-lazy-prop@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz" integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== -depd@2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -destroy@1.2.0, destroy@~1.2.0: +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -756,15 +734,15 @@ dir-glob@^3.0.1: path-type "^4.0.0" dns-packet@^5.2.2: - version "5.5.0" - resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.5.0.tgz" - integrity sha512-USawdAUzRkV6xrqTjiAEp6M9YagZEzWcSUaZTcIFAiyQWW1SoI6KyId8y2+/71wbgHKQAKd+iupLv4YvEwYWvA== + version "5.6.1" + resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== dependencies: "@leichtgewicht/ip-codec" "^2.0.1" dunder-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: call-bind-apply-helpers "^1.0.1" @@ -778,17 +756,22 @@ ee-first@1.1.1: electron-to-chromium@^1.5.263: version "1.5.286" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz#142be1ab5e1cd5044954db0e5898f60a4960384e" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz" integrity sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A== +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + encodeurl@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== enhanced-resolve@^5.19.0: version "5.19.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz#6687446a15e969eaa63c2fa2694510e17ae6d97c" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz" integrity sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg== dependencies: graceful-fs "^4.2.4" @@ -801,29 +784,29 @@ envinfo@^7.7.3: es-define-property@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-module-lexer@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.0.0.tgz#f657cd7a9448dcdda9c070a3cb75e5dc1e85f5b1" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz" integrity sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw== es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: es-errors "^1.3.0" escalade@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-html@~1.0.3: @@ -873,7 +856,7 @@ events@^3.2.0: express@^4.21.2: version "4.22.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069" + resolved "https://registry.npmjs.org/express/-/express-4.22.1.tgz" integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g== dependencies: accepts "~1.3.8" @@ -924,11 +907,6 @@ fast-glob@^3.2.11, fast-glob@^3.3.0: merge2 "^1.3.0" micromatch "^4.0.4" -fast-uri@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" - integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== - fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" @@ -956,16 +934,16 @@ fill-range@^7.1.1: to-regex-range "^5.0.1" finalhandler@~1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88" - integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg== + version "1.3.1" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" encodeurl "~2.0.0" escape-html "~1.0.3" - on-finished "~2.4.1" + on-finished "2.4.1" parseurl "~1.3.3" - statuses "~2.0.2" + statuses "2.0.1" unpipe "~1.0.0" find-up@^4.0.0: @@ -977,38 +955,28 @@ find-up@^4.0.0: path-exists "^4.0.0" follow-redirects@^1.0.0: - version "1.15.6" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + version "1.15.9" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fresh@~0.5.2: +fresh@~0.5.2, fresh@0.5.2: version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: call-bind-apply-helpers "^1.0.2" @@ -1024,13 +992,13 @@ get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: get-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" -glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -1044,6 +1012,13 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" @@ -1061,11 +1036,11 @@ globby@^13.1.1: slash "^4.0.0" "go-mix-conn@file:../go-mix-conn/build": - version "0.0.1" + resolved "file:../go-mix-conn/build" gopd@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: @@ -1085,19 +1060,12 @@ has-flag@^4.0.0: has-symbols@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -has@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - hasown@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" @@ -1132,16 +1100,16 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@~2.0.0, http-errors@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" - integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== +http-errors@~2.0.0, http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - depd "~2.0.0" - inherits "~2.0.4" - setprototypeof "~1.2.0" - statuses "~2.0.2" - toidentifier "~1.0.1" + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" http-parser-js@>=0.5.1: version "0.5.8" @@ -1150,7 +1118,7 @@ http-parser-js@>=0.5.1: http-proxy-middleware@^2.0.7: version "2.0.9" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz" integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q== dependencies: "@types/http-proxy" "^1.17.8" @@ -1170,12 +1138,12 @@ http-proxy@^1.18.1: hyperdyperid@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + resolved "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz" integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== -iconv-lite@~0.4.24: +iconv-lite@0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" @@ -1193,31 +1161,31 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + inherits@2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== -interpret@^2.2.0: +ipaddr.js@^2.1.0: version "2.2.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" @@ -1225,16 +1193,16 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: - has "^1.0.3" + hasown "^2.0.2" is-docker@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz" integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extglob@^2.1.1: @@ -1251,14 +1219,14 @@ is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: is-inside-container@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz" integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== dependencies: is-docker "^3.0.0" is-network-error@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" + resolved "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz" integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== is-number@^7.0.0: @@ -1280,7 +1248,7 @@ is-plain-object@^2.0.4: is-wsl@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz" integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== dependencies: is-inside-container "^1.0.0" @@ -1326,7 +1294,7 @@ kind-of@^6.0.2: launch-editor@^2.6.1: version "2.10.0" - resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.10.0.tgz#5ca3edfcb9667df1e8721310f3a40f1127d4bc42" + resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz" integrity sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA== dependencies: picocolors "^1.0.0" @@ -1334,7 +1302,7 @@ launch-editor@^2.6.1: loader-runner@^4.3.1: version "4.3.1" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.1.tgz#6c76ed29b0ccce9af379208299f07f876de737e3" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz" integrity sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q== locate-path@^5.0.0: @@ -1346,7 +1314,7 @@ locate-path@^5.0.0: math-intrinsics@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== media-typer@0.3.0: @@ -1356,7 +1324,7 @@ media-typer@0.3.0: memfs@^4.6.0: version "4.17.2" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.17.2.tgz#1f71a6d85c8c53b4f1b388234ed981a690c7e227" + resolved "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz" integrity sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg== dependencies: "@jsonjoy.com/json-pack" "^1.0.3" @@ -1366,7 +1334,7 @@ memfs@^4.6.0: merge-descriptors@1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: @@ -1392,7 +1360,7 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2", mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== @@ -1449,12 +1417,12 @@ neo-async@^2.6.2: node-forge@^1: version "1.3.3" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.3.tgz#0ad80f6333b3a0045e827ac20b7f735f93716751" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz" integrity sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg== node-releases@^2.0.27: version "2.0.27" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz" integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== normalize-path@^3.0.0, normalize-path@~3.0.0: @@ -1464,7 +1432,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: object-inspect@^1.13.3: version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== obuf@^1.0.0, obuf@^1.1.2: @@ -1472,7 +1440,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@^2.4.1, on-finished@~2.4.1: +on-finished@^2.4.1, on-finished@~2.4.1, on-finished@2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -1486,7 +1454,7 @@ on-headers@~1.0.2: open@^10.0.3: version "10.1.2" - resolved "https://registry.yarnpkg.com/open/-/open-10.1.2.tgz#d5df40984755c9a9c3c93df8156a12467e882925" + resolved "https://registry.npmjs.org/open/-/open-10.1.2.tgz" integrity sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw== dependencies: default-browser "^5.2.1" @@ -1510,7 +1478,7 @@ p-locate@^4.1.0: p-retry@^6.2.0: version "6.2.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.1.tgz#81828f8dc61c6ef5a800585491572cc9892703af" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz" integrity sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ== dependencies: "@types/retry" "0.12.2" @@ -1544,7 +1512,7 @@ path-parse@^1.0.7: path-to-regexp@~0.1.12: version "0.1.12" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== path-type@^4.0.0: @@ -1552,14 +1520,9 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picocolors@^1.1.1: +picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: @@ -1593,12 +1556,19 @@ punycode@^2.1.0: integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== qs@~6.14.0: - version "6.14.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159" - integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ== + version "6.14.2" + resolved "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz" + integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q== dependencies: side-channel "^1.1.0" +qs@6.13.0: + version "6.13.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -1616,15 +1586,15 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@~2.5.3: - version "2.5.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" - integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: - bytes "~3.1.2" - http-errors "~2.0.1" - iconv-lite "~0.4.24" - unpipe "~1.0.0" + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" readable-stream@^2.0.1: version "2.3.8" @@ -1655,12 +1625,12 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -rechoir@^0.7.0: - version "0.7.1" - resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz" - integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: - resolve "^1.9.0" + resolve "^1.20.0" require-from-string@^2.0.2: version "2.0.2" @@ -1684,12 +1654,12 @@ resolve-from@^5.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.9.0: - version "1.22.1" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.20.0: + version "1.22.11" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.16.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -1705,7 +1675,7 @@ reusify@^1.0.4: run-applescript@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz" integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== run-parallel@^1.1.9: @@ -1715,15 +1685,20 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" @@ -1732,7 +1707,7 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0, schema-utils@^4.3.3: version "4.3.3" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz" integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== dependencies: "@types/json-schema" "^7.0.9" @@ -1747,41 +1722,34 @@ select-hose@^2.0.0: selfsigned@^2.4.1: version "2.4.1" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== dependencies: "@types/node-forge" "^1.3.0" node-forge "^1" -send@~0.19.0, send@~0.19.1: - version "0.19.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.2.tgz#59bc0da1b4ea7ad42736fd642b1c4294e114ff29" - integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg== +send@~0.19.0, send@0.19.0: + version "0.19.0" + resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" depd "2.0.0" destroy "1.2.0" - encodeurl "~2.0.0" + encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - fresh "~0.5.2" - http-errors "~2.0.1" + fresh "0.5.2" + http-errors "2.0.0" mime "1.6.0" ms "2.1.3" - on-finished "~2.4.1" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~2.0.2" + statuses "2.0.1" -serialize-javascript@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== - dependencies: - randombytes "^2.1.0" - -serialize-javascript@^6.0.2: +serialize-javascript@^6.0.0, serialize-javascript@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -1800,21 +1768,21 @@ serve-index@^1.9.1: parseurl "~1.3.2" serve-static@~1.16.2: - version "1.16.3" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.3.tgz#a97b74d955778583f3862a4f0b841eb4d5d78cf9" - integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA== + version "1.16.2" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "~0.19.1" + send "0.19.0" setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.2.0, setprototypeof@~1.2.0: +setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -1840,12 +1808,12 @@ shebang-regex@^3.0.0: shell-quote@^1.8.1: version "1.8.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz" integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== side-channel-list@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz" integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: es-errors "^1.3.0" @@ -1853,7 +1821,7 @@ side-channel-list@^1.0.0: side-channel-map@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz" integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== dependencies: call-bound "^1.0.2" @@ -1863,7 +1831,7 @@ side-channel-map@^1.0.1: side-channel-weakmap@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz" integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== dependencies: call-bound "^1.0.2" @@ -1872,9 +1840,9 @@ side-channel-weakmap@^1.0.2: object-inspect "^1.13.3" side-channel-map "^1.0.1" -side-channel@^1.1.0: +side-channel@^1.0.6, side-channel@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== dependencies: es-errors "^1.3.0" @@ -1938,10 +1906,10 @@ spdy@^4.0.2: resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -statuses@~2.0.1, statuses@~2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" - integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== +statuses@~2.0.1, statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== string_decoder@^1.1.1: version "1.3.0" @@ -1971,12 +1939,12 @@ supports-preserve-symlinks-flag@^1.0.0: tapable@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz" integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== terser-webpack-plugin@^5.3.16: version "5.3.16" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz#741e448cc3f93d8026ebe4f7ef9e4afacfd56330" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz" integrity sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q== dependencies: "@jridgewell/trace-mapping" "^0.3.25" @@ -1986,18 +1954,18 @@ terser-webpack-plugin@^5.3.16: terser "^5.31.1" terser@^5.31.1: - version "5.39.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.39.0.tgz#0e82033ed57b3ddf1f96708d123cca717d86ca3a" - integrity sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw== + version "5.46.0" + resolved "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz" + integrity sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg== dependencies: "@jridgewell/source-map" "^0.3.3" - acorn "^8.8.2" + acorn "^8.15.0" commander "^2.20.0" source-map-support "~0.5.20" thingies@^1.20.0: version "1.21.0" - resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" + resolved "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz" integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== thunky@^1.0.2: @@ -2012,19 +1980,19 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@~1.0.1: +toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tree-dump@^1.0.1: version "1.0.3" - resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.0.3.tgz#2f0e42e77354714418ed7ab44291e435ccdb0f80" + resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz" integrity sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg== -tslib@^2.0.0: +tslib@^2, tslib@^2.0.0, tslib@2: version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== type-is@~1.6.18: @@ -2035,14 +2003,14 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -unpipe@~1.0.0: +unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== update-browserslist-db@^1.2.0: version "1.2.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz" integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== dependencies: escalade "^3.2.0" @@ -2077,7 +2045,7 @@ vary@~1.1.2: watchpack@^2.5.1: version "2.5.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz" integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg== dependencies: glob-to-regexp "^0.4.1" @@ -2090,27 +2058,28 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-cli@^4.9.2: - version "4.10.0" - resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz" - integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== +webpack-cli@^5.1.4, webpack-cli@5.x.x: + version "5.1.4" + resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.2.0" - "@webpack-cli/info" "^1.5.0" - "@webpack-cli/serve" "^1.7.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" colorette "^2.0.14" - commander "^7.0.0" + commander "^10.0.1" cross-spawn "^7.0.3" + envinfo "^7.7.3" fastest-levenshtein "^1.0.12" import-local "^3.0.2" - interpret "^2.2.0" - rechoir "^0.7.0" + interpret "^3.1.1" + rechoir "^0.8.0" webpack-merge "^5.7.3" webpack-dev-middleware@^7.4.2: version "7.4.2" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz#40e265a3d3d26795585cff8207630d3a8ff05877" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz" integrity sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA== dependencies: colorette "^2.0.10" @@ -2122,7 +2091,7 @@ webpack-dev-middleware@^7.4.2: webpack-dev-server@^5.2.1: version "5.2.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz#049072d6e19cbda8cf600b9e364e6662d61218ba" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz" integrity sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ== dependencies: "@types/bonjour" "^3.5.13" @@ -2164,12 +2133,12 @@ webpack-merge@^5.7.3: webpack-sources@^3.3.3: version "3.3.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz" integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== -webpack@^5.105.0: +webpack@^5.0.0, webpack@^5.1.0, webpack@^5.98.0, webpack@5.x.x: version "5.105.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.105.0.tgz#38b5e6c5db8cbe81debbd16e089335ada05ea23a" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz" integrity sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw== dependencies: "@types/eslint-scope" "^3.7.7" @@ -2198,7 +2167,7 @@ webpack@^5.105.0: watchpack "^2.5.1" webpack-sources "^3.3.3" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: +websocket-driver@^0.7.4, websocket-driver@>=0.5.1: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== @@ -2225,6 +2194,6 @@ wildcard@^2.0.0: integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== ws@^8.18.0: - version "8.18.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" - integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== + version "8.18.1" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== diff --git a/wasm/mix-fetch/src/client.rs b/wasm/mix-fetch/src/client.rs index 6fd5d642ac..7c685f5b75 100644 --- a/wasm/mix-fetch/src/client.rs +++ b/wasm/mix-fetch/src/client.rs @@ -249,6 +249,8 @@ impl MixFetchClient { request_id: RequestId, target: RemoteAddress, ) -> Result<(), MixFetchError> { + console_log!(">>>>>>>>>>> SENDING CONNECT"); + let raw_conn_req = socks5_connect_request(request_id, target, self.self_address); let lane = TransmissionLane::ConnectionId(request_id); let input = InputMessage::new_regular( From 77a34fe3bf85821a9f4d92930da6f88b9813d883 Mon Sep 17 00:00:00 2001 From: mfahampshire Date: Tue, 24 Feb 2026 09:29:15 +0000 Subject: [PATCH 27/51] Update MixFetch docs playground + components (#6479) --- .../sdk/typescript/mixfetch-example-code.mdx | 273 ++++- documentation/docs/components/mix-fetch.tsx | 272 ++++- .../outputs/api-scraping-outputs/time-now.md | 2 +- .../node-api-check-query-help.md | 2 +- .../nym-node-cli-install-help.md | 3 +- .../command-outputs/nym-node-run-help.md | 4 + documentation/docs/package.json | 2 +- .../typescript/examples/mix-fetch.mdx | 60 +- documentation/docs/pnpm-lock.yaml | 88 +- wasm/mix-fetch/internal-dev/yarn.lock | 1015 +++++++++++------ 10 files changed, 1255 insertions(+), 466 deletions(-) diff --git a/documentation/docs/code-examples/sdk/typescript/mixfetch-example-code.mdx b/documentation/docs/code-examples/sdk/typescript/mixfetch-example-code.mdx index 85eb29277f..e6b003c6fb 100644 --- a/documentation/docs/code-examples/sdk/typescript/mixfetch-example-code.mdx +++ b/documentation/docs/code-examples/sdk/typescript/mixfetch-example-code.mdx @@ -1,86 +1,273 @@ -```tsx copy filename="mixFetchExample.tsx" -import React, { useState } from "react"; +```tsx +import React, { useState, useRef, useEffect } from "react"; import CircularProgress from "@mui/material/CircularProgress"; import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import Typography from "@mui/material/Typography"; import Box from "@mui/material/Box"; -import { mixFetch } from "@nymproject/mix-fetch-full-fat"; +import { mixFetch, createMixFetch } from "@nymproject/mix-fetch-full-fat"; import Stack from "@mui/material/Stack"; import Paper from "@mui/material/Paper"; import type { SetupMixFetchOps } from "@nymproject/mix-fetch-full-fat"; -const defaultUrl = "https://nymtech.net/.wellknown/network-requester/exit-policy.txt"; +const defaultUrl = + "https://nymtech.net/.wellknown/network-requester/exit-policy.txt"; const args = { mode: "unsafe-ignore-cors" }; - const mixFetchOptions: SetupMixFetchOps = { - preferredGateway: "2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW", // with WSS - // preferredNetworkRequester: - // "CTDxrcXgrZHWyCWnuCgjpJPghQUcEVz1HkhUr5mGdFnT.3UAww1YWNyVNYNWFQL1LaHYouQtDiXBGK5GiDZgpXkTK@2RFtU5BwxvJJXagAWAEuaPgb5ZVPRoy2542TT93Edw6v", + clientId: "docs-mixfetch-demo", // explicit ID + preferredGateway: "q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1", mixFetchOverride: { requestTimeoutMs: 60_000, }, forceTls: true, // force WSS }; +// Log entry type for the visible log panel +type LogLevel = "info" | "error" | "send" | "receive"; +type LogEntry = { timestamp: string; message: string; level: LogLevel }; + +const logColors: Record = { + info: "gray", + error: "red", + send: "blue", + receive: "green", +}; + +const logLabels: Record = { + info: "INFO", + error: "ERROR", + send: "SEND", + receive: "RECV", +}; + export const MixFetch = () => { + // MixFetch initialization state + const [status, setStatus] = useState<"idle" | "starting" | "ready" | "error">("idle"); + const [errorMsg, setErrorMsg] = useState(null); + + // Log panel state + const [logs, setLogs] = useState([]); + const logEndRef = useRef(null); + + // Single fetch state const [url, setUrl] = useState(defaultUrl); const [html, setHtml] = useState(); const [busy, setBusy] = useState(false); + // Concurrent fetch state + const [concurrentResults, setConcurrentResults] = useState([]); + const [concurrentBusy, setConcurrentBusy] = useState(false); + + // Auto-scroll log panel to bottom + useEffect(() => { + logEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [logs]); + + // Helper to add a timestamped log entry + const addLog = (message: string, level: LogLevel) => { + const timestamp = new Date().toISOString().substring(11, 23); + setLogs((prev) => [...prev, { timestamp, message, level }]); + }; + + // Initialize MixFetch explicitly via createMixFetch + const handleStart = async () => { + try { + setStatus("starting"); + setErrorMsg(null); + addLog("Starting MixFetch...", "info"); + await createMixFetch(mixFetchOptions); + setStatus("ready"); + addLog("MixFetch is ready!", "info"); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + setStatus("error"); + setErrorMsg(msg); + addLog(`Error: ${msg}`, "error"); + } + }; + + // Single URL fetch — reuses the existing MixFetch singleton const handleFetch = async () => { try { setBusy(true); setHtml(undefined); + addLog(`Sending request to ${url}...`, "send"); const response = await mixFetch(url, args, mixFetchOptions); - console.log(response); const resHtml = await response.text(); setHtml(resHtml); + addLog(`Response received (${resHtml.length} bytes)`, "receive"); } catch (err) { - console.log(err); + const msg = err instanceof Error ? err.message : String(err); + addLog(`Fetch error: ${msg}`, "error"); } finally { setBusy(false); } }; + // Send 5 concurrent requests to different URLs on the same domain + const handleConcurrentFetch = async () => { + const baseUrl = "https://jsonplaceholder.typicode.com/posts/"; + const count = 5; + try { + setConcurrentBusy(true); + setConcurrentResults([]); + addLog( + `Starting ${count} concurrent requests to ${baseUrl}1-${count}...`, + "send", + ); + // Fire off all requests concurrently using Promise.all + const requests = Array.from({ length: count }, (_, i) => { + const targetUrl = `${baseUrl}${i + 1}`; + return mixFetch(targetUrl, args, mixFetchOptions) + .then((res) => res.json()) + .then((json: { id: number; title: string }) => { + const entry = `[${json.id}] ${json.title}`; + addLog(entry, "receive"); + return entry; + }); + }); + const results = await Promise.all(requests); + setConcurrentResults(results); + addLog(`All ${count} concurrent requests completed!`, "info"); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + addLog(`Concurrent fetch error: ${msg}`, "error"); + } finally { + setConcurrentBusy(false); + } + }; + + const isReady = status === "ready"; + + const statusText = { + idle: "Not started", + starting: "Starting...", + ready: "Ready", + error: `Error: ${errorMsg}`, + }; + const statusColor = { + idle: "gray", + starting: "orange", + ready: "green", + error: "red", + }; + return (
- - setUrl(e.target.value)} - /> - - + {/* Start MixFetch */} + + + + {status === "starting" && } + + {statusText[status]} + + + - {busy && ( - - - - )} - {html && ( - <> - - Response + {/* Fetch controls — disabled until MixFetch is ready */} + + {/* Single fetch */} + + setUrl(e.target.value)} + /> + + + {busy && ( + + - - - {html} - + )} + {html && ( + <> + + Response + + + + {html} + + + + )} + + {/* Concurrent fetch */} + + Concurrent Requests + + + + + {concurrentBusy && ( + + + + )} + {concurrentResults.length > 0 && ( + + {concurrentResults.map((result, i) => ( + + {result} + + ))} - + )} + + + {/* Log Panel */} + {logs.length > 0 && ( + + Log + {logs.map((entry, i) => ( + + {entry.timestamp} [{logLabels[entry.level]}] {entry.message} + + ))} +
+ )}
); diff --git a/documentation/docs/components/mix-fetch.tsx b/documentation/docs/components/mix-fetch.tsx index a7b51c3b9d..a754ba7f62 100644 --- a/documentation/docs/components/mix-fetch.tsx +++ b/documentation/docs/components/mix-fetch.tsx @@ -1,10 +1,10 @@ -import React, { useState } from "react"; +import React, { useState, useRef, useEffect } from "react"; import CircularProgress from "@mui/material/CircularProgress"; import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import Typography from "@mui/material/Typography"; import Box from "@mui/material/Box"; -import { mixFetch } from "@nymproject/mix-fetch-full-fat"; +import { mixFetch, createMixFetch } from "@nymproject/mix-fetch-full-fat"; import Stack from "@mui/material/Stack"; import Paper from "@mui/material/Paper"; import type { SetupMixFetchOps } from "@nymproject/mix-fetch-full-fat"; @@ -12,8 +12,8 @@ import type { SetupMixFetchOps } from "@nymproject/mix-fetch-full-fat"; const defaultUrl = "https://nymtech.net/.wellknown/network-requester/exit-policy.txt"; const args = { mode: "unsafe-ignore-cors" }; - const mixFetchOptions: SetupMixFetchOps = { + clientId: "docs-mixfetch-demo", // explicit ID preferredGateway: "q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1", mixFetchOverride: { requestTimeoutMs: 60_000, @@ -21,64 +21,260 @@ const mixFetchOptions: SetupMixFetchOps = { forceTls: true, // force WSS }; +// Log entry type for the visible log panel +type LogLevel = "info" | "error" | "send" | "receive"; +type LogEntry = { timestamp: string; message: string; level: LogLevel }; + +// Color map for log levels +const logColors: Record = { + info: "gray", + error: "red", + send: "blue", + receive: "green", +}; + +// Label map for log levels +const logLabels: Record = { + info: "INFO", + error: "ERROR", + send: "SEND", + receive: "RECV", +}; + export const MixFetch = () => { + // MixFetch initialization state + const [status, setStatus] = useState<"idle" | "starting" | "ready" | "error">( + "idle" + ); + const [errorMsg, setErrorMsg] = useState(null); + + // Log panel state + const [logs, setLogs] = useState([]); + + // Single fetch state const [url, setUrl] = useState(defaultUrl); const [html, setHtml] = useState(); const [busy, setBusy] = useState(false); + // Concurrent fetch state + const [concurrentResults, setConcurrentResults] = useState([]); + const [concurrentBusy, setConcurrentBusy] = useState(false); + + // Auto-scroll within the log panel when new entries are added (without scrolling the page) + const logContainerRef = useRef(null); + useEffect(() => { + if (logContainerRef.current) { + logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight; + } + }, [logs]); + + // Helper to add a timestamped log entry + const addLog = (message: string, level: LogLevel) => { + const timestamp = new Date().toISOString().substring(11, 23); // HH:MM:SS.mmm + setLogs((prev) => [...prev, { timestamp, message, level }]); + }; + + // Initialize MixFetch explicitly via createMixFetch + const handleStart = async () => { + try { + setStatus("starting"); + setErrorMsg(null); + addLog("Starting MixFetch...", "info"); + await createMixFetch(mixFetchOptions); + setStatus("ready"); + addLog("MixFetch is ready!", "info"); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + setStatus("error"); + setErrorMsg(msg); + addLog(`Error: ${msg}`, "error"); + } + }; + + // Single URL fetch — mixFetch reuses the existing singleton const handleFetch = async () => { try { setBusy(true); setHtml(undefined); + addLog(`Sending request to ${url}...`, "send"); const response = await mixFetch(url, args, mixFetchOptions); - console.log(response); const resHtml = await response.text(); setHtml(resHtml); + addLog(`Response received (${resHtml.length} bytes)`, "receive"); } catch (err) { - console.log(err); + const msg = err instanceof Error ? err.message : String(err); + addLog(`Fetch error: ${msg}`, "error"); } finally { setBusy(false); } }; + // Send 5 concurrent requests to different URLs on the same domain + const handleConcurrentFetch = async () => { + const baseUrl = "https://jsonplaceholder.typicode.com/posts/"; + const count = 5; + try { + setConcurrentBusy(true); + setConcurrentResults([]); + addLog( + `Starting ${count} concurrent requests to ${baseUrl}1-${count}...`, + "send" + ); + // Fire off all requests concurrently using Promise.all + const requests = Array.from({ length: count }, (_, i) => { + const targetUrl = `${baseUrl}${i + 1}`; + return mixFetch(targetUrl, args, mixFetchOptions) + .then((res) => res.json()) + .then((json: { id: number; title: string }) => { + const entry = `[${json.id}] ${json.title}`; + addLog(entry, "receive"); + return entry; + }); + }); + const results = await Promise.all(requests); + setConcurrentResults(results); + addLog(`All ${count} concurrent requests completed!`, "info"); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + addLog(`Concurrent fetch error: ${msg}`, "error"); + } finally { + setConcurrentBusy(false); + } + }; + + // Are fetch controls enabled? + const isReady = status === "ready"; + + // Status text + color for the startup indicator + const statusText: Record = { + idle: "Not started", + starting: "Starting...", + ready: "Ready", + error: `Error: ${errorMsg}`, + }; + const statusColor: Record = { + idle: "gray", + starting: "orange", + ready: "green", + error: "red", + }; + return (
- - setUrl(e.target.value)} - /> - - + {/* --- Start MixFetch Section --- */} + + + + {status === "starting" && } + + {statusText[status]} + + + - {busy && ( - - - - )} - {html && ( - <> - - Response + {/* --- Fetch Controls (disabled until ready) --- */} + + {/* Single fetch */} + + setUrl(e.target.value)} + /> + + + {busy && ( + + - - - {html} - + )} + {html && ( + <> + + Response + + + + {html} + + + + )} + + {/* Concurrent fetch demo */} + + Concurrent Requests + + + + + {concurrentBusy && ( + + + + )} + {concurrentResults.length > 0 && ( + + {concurrentResults.map((result, i) => ( + + {result} + + ))} - + )} + + + {/* --- Log Panel --- */} + {logs.length > 0 && ( + + Log + {logs.map((entry, i) => ( + + {entry.timestamp} [{logLabels[entry.level]}] {entry.message} + + ))} + )}
); diff --git a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md index 06bc0da1c0..518b0b968d 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md @@ -1 +1 @@ -Friday, February 20th 2026, 13:42:15 UTC \ No newline at end of file +Monday, February 23rd 2026, 22:45:55 UTC diff --git a/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md b/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md index bfa068ddb0..999f3d4a6f 100644 --- a/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md +++ b/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md @@ -11,7 +11,7 @@ options: --no_routing_history Display node stats without routing history --no_verloc_metrics Display node stats without verloc metrics -m, --markdown Display results in markdown format - -o [OUTPUT], --output [OUTPUT] + -o, --output [OUTPUT] Save results to file (in current dir or supply with path without filename) ``` diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md index 70baf6f736..ddc7c867b3 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-cli-install-help.md @@ -12,8 +12,7 @@ usage: nym-node-cli install [-h] [-V] [-d BRANCH] [-v] options: -h, --help show this help message and exit -V, --version show program's version number and exit - -d BRANCH, --dev BRANCH - Define github branch (default: develop) + -d, --dev BRANCH Define github branch (default: develop) -v, --verbose Show full error tracebacks --mode {mixnode,entry-gateway,exit-gateway} Node mode: 'mixnode', 'entry-gateway', or 'exit- diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md index ab11bb896e..1d0b089b38 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md @@ -62,6 +62,8 @@ Options: Tunnel port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] --wireguard-private-network-prefix The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=] + --wireguard-userspace + Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. Useful in containerized environments without kernel WireGuard support [env: NYMNODE_WG_USERSPACE=] [possible values: true, false] --verloc-bind-address Socket address this node will use for binding its verloc API. default: `[::]:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=] --verloc-announce-port @@ -80,6 +82,8 @@ Options: Endpoint to query to retrieve current upgrade mode attestation. This argument should never be set outside testnets and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTATION_URL=] --upgrade-mode-attester-public-key Expected public key of the entity signing the published attestation. This argument should never be set outside testnets and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY=] + --lp-use-mock-ecash + Use mock ecash manager for LP testing. WARNING: Only use this for local testing! Never enable in production. When enabled, the LP listener will accept any credential without blockchain verification [env: NYMNODE_LP_USE_MOCK_ECASH=] [possible values: true, false] --upstream-exit-policy-url Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=] --open-proxy diff --git a/documentation/docs/package.json b/documentation/docs/package.json index 63e3132a88..cc52e7403b 100644 --- a/documentation/docs/package.json +++ b/documentation/docs/package.json @@ -38,7 +38,7 @@ "@nextui-org/accordion": "^2.0.40", "@nextui-org/react": "^2.4.8", "@nymproject/contract-clients": ">=1.2.4-rc.2 || ^1", - "@nymproject/mix-fetch-full-fat": ">=1.5.1-rc.0 || ^1.4.1", + "@nymproject/mix-fetch-full-fat": "^1.4.2", "@nymproject/sdk-full-fat": ">=1.5.1-rc.0 || ^1.4.1", "@redocly/cli": "^1.25.15", "@types/mdx": "^2.0.13", diff --git a/documentation/docs/pages/developers/typescript/examples/mix-fetch.mdx b/documentation/docs/pages/developers/typescript/examples/mix-fetch.mdx index 4bc35df9a1..5f12f9ae12 100644 --- a/documentation/docs/pages/developers/typescript/examples/mix-fetch.mdx +++ b/documentation/docs/pages/developers/typescript/examples/mix-fetch.mdx @@ -13,7 +13,7 @@ Sounds great, are there any catches? Well, there are a few (for now): - For now, `mixfetch` doesn't work with SURBS, although this will change in the future. -- For now, `mixFetch` cannot deal with concurrent requests with the same base URL. +- `mixFetch` supports concurrent requests (up to 10) to either different URLs on the same domain or different domains. Duplicate requests to the exact same URL will be deduplicated. Right now Gateways are not required to run a Secure Websocket (WSS) listener, so only a subset of nodes running in Gateway mode have configured their nodes to do so. @@ -33,6 +33,7 @@ curl -X 'GET' \ import type { SetupMixFetchOps } from '@nymproject/mix-fetch'; const mixFetchOptions: SetupMixFetchOps = { + clientId: "my-mixfetch-client", // explicit ID to avoid stale default IndexedDB storage preferredGateway: "q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1", // with WSS mixFetchOverride: { requestTimeoutMs: 60_000, @@ -78,7 +79,7 @@ import { mixFetch } from '@nymproject/mix-fetch-full-fat'; ##### Example: using the `mixFetch` client: -`Get` and `Post` outputs will be observable from your console. +`Get`, `Post`, and `Concurrent` outputs will be observable from your console. MixFetch auto-initializes on the first request. Individual concurrent results are logged as they arrive. ```ts import './App.css'; @@ -86,6 +87,7 @@ import { mixFetch, SetupMixFetchOps } from '@nymproject/mix-fetch-full-fat'; import React from 'react'; const mixFetchOptions: SetupMixFetchOps = { + clientId: "my-mixfetch-client", // explicit ID to avoid stale default IndexedDB storage preferredGateway: '23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb', // with WSS preferredNetworkRequester: 'HuNL1pFprNSKW6jdqppibXP5KNKCNJxDh7ivpYcoULN9.C62NahRTUf6kqpNtDVHXoVriQr6yyaU5LtxdgpbsGrtA@23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb', @@ -103,7 +105,7 @@ export function HttpGET() { const response = await mixFetch('https://nym.com/favicon.svg', { mode: 'unsafe-ignore-cors' }, mixFetchOptions); const text = await response.text(); console.log('response was', text); - setHtml(html); + setHtml(text); } return ( @@ -146,11 +148,63 @@ export function HttpPOST() { ); } +// Send 5 concurrent requests to different URLs on the same domain using Promise.all +export function HttpConcurrent() { + const [results, setResults] = React.useState([]); + const [busy, setBusy] = React.useState(false); + + async function fetchConcurrent() { + const baseUrl = 'https://jsonplaceholder.typicode.com/posts/'; + const count = 5; + setBusy(true); + setResults([]); + console.log(`Starting ${count} concurrent requests to ${baseUrl}1-${count}...`); + + try { + // Fire off all requests at once with Promise.all + const requests = Array.from({ length: count }, (_, i) => { + const url = `${baseUrl}${i + 1}`; + return mixFetch(url, { mode: 'unsafe-ignore-cors' }, mixFetchOptions) + .then((res) => res.json()) + .then((json: { id: number; title: string }) => { + const entry = `[${json.id}] ${json.title}`; + console.log(entry); + return entry; + }); + }); + + const allResults = await Promise.all(requests); + setResults(allResults); + console.log('All concurrent requests completed!', allResults); + } catch (err) { + console.error('Concurrent fetch error:', err); + } finally { + setBusy(false); + } + } + + return ( + <> + + {results.length > 0 && ( +
    + {results.map((r, i) => ( +
  • {r}
  • + ))} +
+ )} + + ); +} + export default function App() { return ( <> + ); } diff --git a/documentation/docs/pnpm-lock.yaml b/documentation/docs/pnpm-lock.yaml index b04428b429..df8846f7a9 100644 --- a/documentation/docs/pnpm-lock.yaml +++ b/documentation/docs/pnpm-lock.yaml @@ -78,14 +78,14 @@ importers: specifier: '>=1.2.4-rc.2 || ^1' version: 1.4.1 '@nymproject/mix-fetch-full-fat': - specifier: '>=1.5.1-rc.0 || ^1.4.1' - version: 1.4.1 + specifier: ^1.4.2 + version: 1.4.2 '@nymproject/sdk-full-fat': specifier: '>=1.5.1-rc.0 || ^1.4.1' version: 1.4.1 '@redocly/cli': specifier: ^1.25.15 - version: 1.34.5(ajv@8.17.1) + version: 1.34.5(ajv@6.12.6) '@types/mdx': specifier: ^2.0.13 version: 2.0.13 @@ -1062,7 +1062,6 @@ packages: '@next/swc-linux-x64-gnu@15.5.0': resolution: {integrity: sha512-zPisT+obYypM/l6EZ0yRkK3LEuoZqHaSoYKj+5jiD9ESHwdr6QhnabnNxYkdy34uCigNlWIaCbjFmQ8FY5AlxA==} engines: {node: '>= 10'} - cpu: [x64] os: [linux] '@next/swc-linux-x64-gnu@15.5.7': @@ -1705,8 +1704,8 @@ packages: '@nymproject/contract-clients@1.4.1': resolution: {integrity: sha512-HuJZ4Hv+Rl6ZZEtCHKgurNLJapM+QQRJlGkevFH2a4UdqUqF9omUkUi3AVes4679dPoSFgvA7plyVSDBdbgV6w==} - '@nymproject/mix-fetch-full-fat@1.4.1': - resolution: {integrity: sha512-AMa21sEd9FELqAJe1lCyHPqxxbc13ApiJ1P/exAslQjiFPb/de/3Ow0FHqKGNPrwyVRS/T2pSzjQ3l8TddiEBA==} + '@nymproject/mix-fetch-full-fat@1.4.2': + resolution: {integrity: sha512-QHPwa7A+c/2VUm4Imq2I21toFiZhbZxcjHud1sFsE9hN5BWxZ+QJKV2bg9oBUzulzoQabsk48RA13/hqU7c4KA==} '@nymproject/sdk-full-fat@1.4.1': resolution: {integrity: sha512-dh5bvMUj3m8nEssvO8Nl66WpcJAjwRZrGNwqfczJWLG4nX3Vt95tPLv4v0/Z1W3DQWQFW6WmEPPYHNjl18V/fA==} @@ -3215,6 +3214,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -3390,8 +3394,9 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.19: - resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} hasBin: true bech32@1.1.4: @@ -3510,6 +3515,9 @@ packages: caniuse-lite@1.0.30001769: resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} + caniuse-lite@1.0.30001774: + resolution: {integrity: sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==} + cardinal@2.1.1: resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} hasBin: true @@ -4022,8 +4030,8 @@ packages: duplexify@4.1.3: resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} - electron-to-chromium@1.5.286: - resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + electron-to-chromium@1.5.302: + resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} elkjs@0.9.3: resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} @@ -5355,8 +5363,8 @@ packages: modern-ahocorasick@1.1.0: resolution: {integrity: sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ==} - motion-dom@12.34.0: - resolution: {integrity: sha512-Lql3NuEcScRDxTAO6GgUsRHBZOWI/3fnMlkMcH5NftzcN37zJta+bpbMAV9px4Nj057TuvRooMK7QrzMCgtz6Q==} + motion-dom@12.34.3: + resolution: {integrity: sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ==} motion-utils@12.29.2: resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==} @@ -6709,8 +6717,8 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webpack-sources@3.3.3: - resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} engines: {node: '>=10.13.0'} webpack@5.101.3: @@ -9307,7 +9315,7 @@ snapshots: '@nymproject/contract-clients@1.4.1': {} - '@nymproject/mix-fetch-full-fat@1.4.1': {} + '@nymproject/mix-fetch-full-fat@1.4.2': {} '@nymproject/sdk-full-fat@1.4.1': {} @@ -11046,7 +11054,7 @@ snapshots: require-from-string: 2.0.2 uri-js-replace: 1.0.1 - '@redocly/cli@1.34.5(ajv@8.17.1)': + '@redocly/cli@1.34.5(ajv@6.12.6)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-http': 0.53.0(@opentelemetry/api@1.9.0) @@ -11055,7 +11063,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.27.0 '@redocly/config': 0.22.2 '@redocly/openapi-core': 1.34.5 - '@redocly/respect-core': 1.34.5(ajv@8.17.1) + '@redocly/respect-core': 1.34.5(ajv@6.12.6) abort-controller: 3.0.0 chokidar: 3.6.0 colorette: 1.4.0 @@ -11098,12 +11106,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@redocly/respect-core@1.34.5(ajv@8.17.1)': + '@redocly/respect-core@1.34.5(ajv@6.12.6)': dependencies: '@faker-js/faker': 7.6.0 '@redocly/ajv': 8.11.2 '@redocly/openapi-core': 1.34.5 - better-ajv-errors: 1.2.0(ajv@8.17.1) + better-ajv-errors: 1.2.0(ajv@6.12.6) colorette: 2.0.20 concat-stream: 2.0.0 cookie: 0.7.2 @@ -11810,9 +11818,9 @@ snapshots: dependencies: event-target-shim: 5.0.1 - acorn-import-phases@1.0.4(acorn@8.15.0): + acorn-import-phases@1.0.4(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -11820,6 +11828,8 @@ snapshots: acorn@8.15.0: {} + acorn@8.16.0: {} + agent-base@7.1.4: {} ajv-draft-04@1.0.0(ajv@8.17.1): @@ -12009,15 +12019,15 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.9.19: {} + baseline-browser-mapping@2.10.0: {} bech32@1.1.4: {} - better-ajv-errors@1.2.0(ajv@8.17.1): + better-ajv-errors@1.2.0(ajv@6.12.6): dependencies: '@babel/code-frame': 7.27.1 '@humanwhocodes/momoa': 2.0.4 - ajv: 8.17.1 + ajv: 6.12.6 chalk: 4.1.2 jsonpointer: 5.0.1 leven: 3.1.0 @@ -12079,9 +12089,9 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.19 - caniuse-lite: 1.0.30001769 - electron-to-chromium: 1.5.286 + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001774 + electron-to-chromium: 1.5.302 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -12135,6 +12145,8 @@ snapshots: caniuse-lite@1.0.30001769: {} + caniuse-lite@1.0.30001774: {} + cardinal@2.1.1: dependencies: ansicolors: 0.3.2 @@ -12672,7 +12684,7 @@ snapshots: readable-stream: 3.6.2 stream-shift: 1.0.3 - electron-to-chromium@1.5.286: {} + electron-to-chromium@1.5.302: {} elkjs@0.9.3: {} @@ -12830,7 +12842,7 @@ snapshots: eslint: 8.46.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@8.46.0))(eslint@8.46.0) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.46.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.46.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.46.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.46.0))(eslint@8.46.0))(eslint@8.46.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.46.0) eslint-plugin-react: 7.37.5(eslint@8.46.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.46.0) @@ -12860,7 +12872,7 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.46.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.46.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.46.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.46.0))(eslint@8.46.0))(eslint@8.46.0) transitivePeerDependencies: - supports-color @@ -12875,7 +12887,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.46.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.46.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.46.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.46.0))(eslint@8.46.0))(eslint@8.46.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -13171,7 +13183,7 @@ snapshots: framer-motion@12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - motion-dom: 12.34.0 + motion-dom: 12.34.3 motion-utils: 12.29.2 tslib: 2.8.1 optionalDependencies: @@ -14491,7 +14503,7 @@ snapshots: modern-ahocorasick@1.1.0: {} - motion-dom@12.34.0: + motion-dom@12.34.3: dependencies: motion-utils: 12.29.2 @@ -15781,7 +15793,7 @@ snapshots: terser@5.46.0: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 + acorn: 8.16.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -16171,7 +16183,7 @@ snapshots: webidl-conversions@3.0.1: {} - webpack-sources@3.3.3: {} + webpack-sources@3.3.4: {} webpack@5.101.3: dependencies: @@ -16181,8 +16193,8 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.19.0 @@ -16199,7 +16211,7 @@ snapshots: tapable: 2.3.0 terser-webpack-plugin: 5.3.16(webpack@5.101.3) watchpack: 2.5.1 - webpack-sources: 3.3.3 + webpack-sources: 3.3.4 transitivePeerDependencies: - '@swc/core' - esbuild diff --git a/wasm/mix-fetch/internal-dev/yarn.lock b/wasm/mix-fetch/internal-dev/yarn.lock index 3fc632d7ac..53c976c49e 100644 --- a/wasm/mix-fetch/internal-dev/yarn.lock +++ b/wasm/mix-fetch/internal-dev/yarn.lock @@ -41,31 +41,181 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jsonjoy.com/base64@^1.1.1": +"@jsonjoy.com/base64@^1.1.2": version "1.1.2" resolved "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz" integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== -"@jsonjoy.com/json-pack@^1.0.3": - version "1.2.0" - resolved "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz" - integrity sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA== - dependencies: - "@jsonjoy.com/base64" "^1.1.1" - "@jsonjoy.com/util" "^1.1.2" - hyperdyperid "^1.2.0" - thingies "^1.20.0" +"@jsonjoy.com/base64@17.67.0": + version "17.67.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz" + integrity sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw== -"@jsonjoy.com/util@^1.1.2", "@jsonjoy.com/util@^1.3.0": - version "1.6.0" - resolved "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz" - integrity sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A== +"@jsonjoy.com/buffers@^1.0.0": + version "1.2.1" + resolved "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz" + integrity sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA== + +"@jsonjoy.com/buffers@^1.2.0": + version "1.2.1" + resolved "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz" + integrity sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA== + +"@jsonjoy.com/buffers@^17.65.0", "@jsonjoy.com/buffers@17.67.0": + version "17.67.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz" + integrity sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw== + +"@jsonjoy.com/codegen@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz" + integrity sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g== + +"@jsonjoy.com/codegen@17.67.0": + version "17.67.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz" + integrity sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q== + +"@jsonjoy.com/fs-core@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.56.10.tgz" + integrity sha512-PyAEA/3cnHhsGcdY+AmIU+ZPqTuZkDhCXQ2wkXypdLitSpd6d5Ivxhnq4wa2ETRWFVJGabYynBWxIijOswSmOw== + dependencies: + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + thingies "^2.5.0" + +"@jsonjoy.com/fs-fsa@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.56.10.tgz" + integrity sha512-/FVK63ysNzTPOnCCcPoPHt77TOmachdMS422txM4KhxddLdbW1fIbFMYH0AM0ow/YchCyS5gqEjKLNyv71j/5Q== + dependencies: + "@jsonjoy.com/fs-core" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + thingies "^2.5.0" + +"@jsonjoy.com/fs-node-builtins@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.56.10.tgz" + integrity sha512-uUnKz8R0YJyKq5jXpZtkGV9U0pJDt8hmYcLRrPjROheIfjMXsz82kXMgAA/qNg0wrZ1Kv+hrg7azqEZx6XZCVw== + +"@jsonjoy.com/fs-node-to-fsa@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.56.10.tgz" + integrity sha512-oH+O6Y4lhn9NyG6aEoFwIBNKZeYy66toP5LJcDOMBgL99BKQMUf/zWJspdRhMdn/3hbzQsZ8EHHsuekbFLGUWw== + dependencies: + "@jsonjoy.com/fs-fsa" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + +"@jsonjoy.com/fs-node-utils@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.56.10.tgz" + integrity sha512-8EuPBgVI2aDPwFdaNQeNpHsyqPi3rr+85tMNG/lHvQLiVjzoZsvxA//Xd8aB567LUhy4QS03ptT+unkD/DIsNg== + dependencies: + "@jsonjoy.com/fs-node-builtins" "4.56.10" + +"@jsonjoy.com/fs-node@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.56.10.tgz" + integrity sha512-7R4Gv3tkUdW3dXfXiOkqxkElxKNVdd8BDOWC0/dbERd0pXpPY+s2s1Mino+aTvkGrFPiY+mmVxA7zhskm4Ue4Q== + dependencies: + "@jsonjoy.com/fs-core" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + "@jsonjoy.com/fs-print" "4.56.10" + "@jsonjoy.com/fs-snapshot" "4.56.10" + glob-to-regex.js "^1.0.0" + thingies "^2.5.0" + +"@jsonjoy.com/fs-print@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.56.10.tgz" + integrity sha512-JW4fp5mAYepzFsSGrQ48ep8FXxpg4niFWHdF78wDrFGof7F3tKDJln72QFDEn/27M1yHd4v7sKHHVPh78aWcEw== + dependencies: + "@jsonjoy.com/fs-node-utils" "4.56.10" + tree-dump "^1.1.0" + +"@jsonjoy.com/fs-snapshot@4.56.10": + version "4.56.10" + resolved "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.56.10.tgz" + integrity sha512-DkR6l5fj7+qj0+fVKm/OOXMGfDFCGXLfyHkORH3DF8hxkpDgIHbhf/DwncBMs2igu/ST7OEkexn1gIqoU6Y+9g== + dependencies: + "@jsonjoy.com/buffers" "^17.65.0" + "@jsonjoy.com/fs-node-utils" "4.56.10" + "@jsonjoy.com/json-pack" "^17.65.0" + "@jsonjoy.com/util" "^17.65.0" + +"@jsonjoy.com/json-pack@^1.11.0": + version "1.21.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz" + integrity sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg== + dependencies: + "@jsonjoy.com/base64" "^1.1.2" + "@jsonjoy.com/buffers" "^1.2.0" + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/json-pointer" "^1.0.2" + "@jsonjoy.com/util" "^1.9.0" + hyperdyperid "^1.2.0" + thingies "^2.5.0" + tree-dump "^1.1.0" + +"@jsonjoy.com/json-pack@^17.65.0": + version "17.67.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz" + integrity sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w== + dependencies: + "@jsonjoy.com/base64" "17.67.0" + "@jsonjoy.com/buffers" "17.67.0" + "@jsonjoy.com/codegen" "17.67.0" + "@jsonjoy.com/json-pointer" "17.67.0" + "@jsonjoy.com/util" "17.67.0" + hyperdyperid "^1.2.0" + thingies "^2.5.0" + tree-dump "^1.1.0" + +"@jsonjoy.com/json-pointer@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz" + integrity sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg== + dependencies: + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/util" "^1.9.0" + +"@jsonjoy.com/json-pointer@17.67.0": + version "17.67.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz" + integrity sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA== + dependencies: + "@jsonjoy.com/util" "17.67.0" + +"@jsonjoy.com/util@^1.9.0": + version "1.9.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz" + integrity sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ== + dependencies: + "@jsonjoy.com/buffers" "^1.0.0" + "@jsonjoy.com/codegen" "^1.0.0" + +"@jsonjoy.com/util@^17.65.0", "@jsonjoy.com/util@17.67.0": + version "17.67.0" + resolved "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz" + integrity sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew== + dependencies: + "@jsonjoy.com/buffers" "17.67.0" + "@jsonjoy.com/codegen" "17.67.0" "@leichtgewicht/ip-codec@^2.0.1": version "2.0.5" resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz" integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== +"@noble/hashes@1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -88,9 +238,132 @@ fastq "^1.6.0" "@nymproject/mix-fetch-wasm@file:../pkg": - version "1.4.1" + version "1.4.2" resolved "file:../pkg" +"@peculiar/asn1-cms@^2.6.0", "@peculiar/asn1-cms@^2.6.1": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz" + integrity sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + "@peculiar/asn1-x509-attr" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-csr@^2.6.0": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz" + integrity sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-ecc@^2.6.0": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz" + integrity sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pfx@^2.6.1": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz" + integrity sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw== + dependencies: + "@peculiar/asn1-cms" "^2.6.1" + "@peculiar/asn1-pkcs8" "^2.6.1" + "@peculiar/asn1-rsa" "^2.6.1" + "@peculiar/asn1-schema" "^2.6.0" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pkcs8@^2.6.1": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz" + integrity sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pkcs9@^2.6.0": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz" + integrity sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw== + dependencies: + "@peculiar/asn1-cms" "^2.6.1" + "@peculiar/asn1-pfx" "^2.6.1" + "@peculiar/asn1-pkcs8" "^2.6.1" + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + "@peculiar/asn1-x509-attr" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-rsa@^2.6.0", "@peculiar/asn1-rsa@^2.6.1": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz" + integrity sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-schema@^2.6.0": + version "2.6.0" + resolved "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz" + integrity sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg== + dependencies: + asn1js "^3.0.6" + pvtsutils "^1.3.6" + tslib "^2.8.1" + +"@peculiar/asn1-x509-attr@^2.6.1": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz" + integrity sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-x509@^2.6.0", "@peculiar/asn1-x509@^2.6.1": + version "2.6.1" + resolved "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz" + integrity sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + asn1js "^3.0.6" + pvtsutils "^1.3.6" + tslib "^2.8.1" + +"@peculiar/x509@^1.14.2": + version "1.14.3" + resolved "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz" + integrity sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA== + dependencies: + "@peculiar/asn1-cms" "^2.6.0" + "@peculiar/asn1-csr" "^2.6.0" + "@peculiar/asn1-ecc" "^2.6.0" + "@peculiar/asn1-pkcs9" "^2.6.0" + "@peculiar/asn1-rsa" "^2.6.0" + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.0" + pvtsutils "^1.3.6" + reflect-metadata "^0.2.2" + tslib "^2.8.1" + tsyringe "^4.10.0" + "@types/body-parser@*": version "1.19.6" resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz" @@ -143,24 +416,24 @@ integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.33": - version "4.19.6" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz" - integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== + version "4.19.8" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz" + integrity sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/send" "*" -"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.21": - version "4.17.23" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz" - integrity sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ== +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.25": + version "4.17.25" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz" + integrity sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.33" "@types/qs" "*" - "@types/serve-static" "*" + "@types/serve-static" "^1" "@types/http-errors@*": version "2.0.5" @@ -168,9 +441,9 @@ integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== "@types/http-proxy@^1.17.8": - version "1.17.16" - resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz" - integrity sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w== + version "1.17.17" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz" + integrity sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw== dependencies: "@types/node" "*" @@ -184,17 +457,12 @@ resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== -"@types/node-forge@^1.3.0": - version "1.3.11" - resolved "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz" - integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== - dependencies: - "@types/node" "*" - "@types/node@*": - version "18.15.11" - resolved "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz" - integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== + version "25.3.0" + resolved "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz" + integrity sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A== + dependencies: + undici-types "~7.18.0" "@types/qs@*": version "6.14.0" @@ -212,9 +480,16 @@ integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== "@types/send@*": - version "0.17.5" - resolved "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz" - integrity sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w== + version "1.2.1" + resolved "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz" + integrity sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ== + dependencies: + "@types/node" "*" + +"@types/send@<1": + version "0.17.6" + resolved "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz" + integrity sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og== dependencies: "@types/mime" "^1" "@types/node" "*" @@ -226,14 +501,14 @@ dependencies: "@types/express" "*" -"@types/serve-static@*", "@types/serve-static@^1.15.5": - version "1.15.8" - resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz" - integrity sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg== +"@types/serve-static@^1", "@types/serve-static@^1.15.5": + version "1.15.10" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz" + integrity sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw== dependencies: "@types/http-errors" "*" "@types/node" "*" - "@types/send" "*" + "@types/send" "<1" "@types/sockjs@^0.3.36": version "0.3.36" @@ -395,7 +670,7 @@ resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +accepts@~1.3.8: version "1.3.8" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -409,9 +684,9 @@ acorn-import-phases@^1.0.3: integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== acorn@^8.14.0, acorn@^8.15.0: - version "8.15.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + version "8.16.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== ajv-formats@^2.1.1: version "2.1.1" @@ -428,14 +703,14 @@ ajv-keywords@^5.1.0: fast-deep-equal "^3.1.3" ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: - version "8.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + version "8.18.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz" + integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== dependencies: - fast-deep-equal "^3.1.1" + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" - uri-js "^4.2.2" ansi-html-community@^0.0.8: version "0.0.8" @@ -455,10 +730,19 @@ array-flatten@1.1.1: resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== +asn1js@^3.0.6: + version "3.0.7" + resolved "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz" + integrity sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ== + dependencies: + pvtsutils "^1.3.6" + pvutils "^1.1.3" + tslib "^2.8.1" + baseline-browser-mapping@^2.9.0: - version "2.9.19" - resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz" - integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== + version "2.10.0" + resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz" + integrity sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA== batch@0.6.1: version "0.6.1" @@ -471,22 +755,22 @@ binary-extensions@^2.0.0: integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== body-parser@~1.20.3: - version "1.20.3" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + version "1.20.4" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz" + integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== dependencies: - bytes "3.1.2" + bytes "~3.1.2" content-type "~1.0.5" debug "2.6.9" depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" + destroy "~1.2.0" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + on-finished "~2.4.1" + qs "~6.14.0" + raw-body "~2.5.3" type-is "~1.6.18" - unpipe "1.0.0" + unpipe "~1.0.0" bonjour-service@^1.2.1: version "1.3.0" @@ -496,7 +780,7 @@ bonjour-service@^1.2.1: fast-deep-equal "^3.1.3" multicast-dns "^7.2.5" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -526,16 +810,16 @@ bundle-name@^4.1.0: dependencies: run-applescript "^7.0.0" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: +bytes@~3.1.2, bytes@3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +bytestreamjs@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz" + integrity sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ== + call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" @@ -553,9 +837,9 @@ call-bound@^1.0.2: get-intrinsic "^1.3.0" caniuse-lite@^1.0.30001759: - version "1.0.30001769" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz" - integrity sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg== + version "1.0.30001774" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz" + integrity sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA== chokidar@^3.6.0: version "3.6.0" @@ -573,9 +857,9 @@ chokidar@^3.6.0: fsevents "~2.3.2" chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + version "1.0.4" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== clone-deep@^4.0.1: version "4.0.1" @@ -587,9 +871,9 @@ clone-deep@^4.0.1: shallow-clone "^3.0.0" colorette@^2.0.10, colorette@^2.0.14: - version "2.0.19" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== commander@^10.0.1: version "10.0.1" @@ -601,24 +885,24 @@ commander@^2.20.0: resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -compressible@~2.0.16: +compressible@~2.0.18: version "2.0.18" resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== +compression@^1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz" + integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" + bytes "3.1.2" + compressible "~2.0.18" debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" + negotiator "~0.6.4" + on-headers "~1.1.0" + safe-buffer "5.2.1" vary "~1.1.2" connect-history-api-fallback@^2.0.0: @@ -639,14 +923,14 @@ content-type@~1.0.4, content-type@~1.0.5: integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== cookie-signature@~1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + version "1.0.7" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz" + integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA== cookie@~0.7.1: - version "0.7.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== + version "0.7.2" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== copy-webpack-plugin@^11.0.0: version "11.0.0" @@ -666,20 +950,20 @@ core-util-is@~1.0.0: integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" debug@^4.1.0: - version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + version "4.4.3" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: - ms "2.1.2" + ms "^2.1.3" debug@2.6.9: version "2.6.9" @@ -689,14 +973,14 @@ debug@2.6.9: ms "2.0.0" default-browser-id@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz" - integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + version "5.0.1" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz" + integrity sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q== default-browser@^5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz" - integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + version "5.5.0" + resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz" + integrity sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw== dependencies: bundle-name "^4.1.0" default-browser-id "^5.0.0" @@ -711,12 +995,12 @@ depd@~1.1.2: resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -depd@2.0.0: +depd@~2.0.0, depd@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -destroy@1.2.0: +destroy@~1.2.0, destroy@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -755,14 +1039,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.5.263: - version "1.5.286" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz" - integrity sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + version "1.5.302" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz" + integrity sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg== encodeurl@~2.0.0: version "2.0.0" @@ -778,9 +1057,9 @@ enhanced-resolve@^5.19.0: tapable "^2.3.0" envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.21.0" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz" + integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow== es-define-property@^1.0.1: version "1.0.1" @@ -854,7 +1133,7 @@ events@^3.2.0: resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -express@^4.21.2: +express@^4.22.1: version "4.22.1" resolved "https://registry.npmjs.org/express/-/express-4.22.1.tgz" integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g== @@ -891,21 +1170,26 @@ express@^4.21.2: utils-merge "1.0.1" vary "~1.1.2" -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: +fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.2.11, fast-glob@^3.3.0: - version "3.3.1" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz" - integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + version "3.3.3" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.4" + micromatch "^4.0.8" + +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== fastest-levenshtein@^1.0.12: version "1.0.16" @@ -913,9 +1197,9 @@ fastest-levenshtein@^1.0.12: integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + version "1.20.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz" + integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== dependencies: reusify "^1.0.4" @@ -934,16 +1218,16 @@ fill-range@^7.1.1: to-regex-range "^5.0.1" finalhandler@~1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + version "1.3.2" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz" + integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg== dependencies: debug "2.6.9" encodeurl "~2.0.0" escape-html "~1.0.3" - on-finished "2.4.1" + on-finished "~2.4.1" parseurl "~1.3.3" - statuses "2.0.1" + statuses "~2.0.2" unpipe "~1.0.0" find-up@^4.0.0: @@ -954,17 +1238,22 @@ find-up@^4.0.0: locate-path "^5.0.0" path-exists "^4.0.0" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + follow-redirects@^1.0.0: - version "1.15.9" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" - integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + version "1.15.11" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fresh@~0.5.2, fresh@0.5.2: +fresh@~0.5.2: version "0.5.2" resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== @@ -1019,6 +1308,11 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-to-regex.js@^1.0.0, glob-to-regex.js@^1.0.1: + version "1.2.0" + resolved "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz" + integrity sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ== + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" @@ -1090,33 +1384,34 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== +http-errors@~1.8.0: + version "1.8.1" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-errors@~2.0.0, http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" inherits "2.0.4" setprototypeof "1.2.0" - statuses "2.0.1" + statuses ">= 1.5.0 < 2" toidentifier "1.0.1" -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== +http-errors@~2.0.0, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" -http-proxy-middleware@^2.0.7: +http-parser-js@>=0.5.1: + version "0.5.10" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz" + integrity sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA== + +http-proxy-middleware@^2.0.9: version "2.0.9" resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz" integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q== @@ -1141,7 +1436,7 @@ hyperdyperid@^1.2.0: resolved "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz" integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== -iconv-lite@0.4.24: +iconv-lite@~0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -1149,37 +1444,32 @@ iconv-lite@0.4.24: safer-buffer ">= 2.1.2 < 3" ignore@^5.2.4: - version "5.2.4" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + version "5.3.2" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + version "3.2.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2.0.4: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4, inherits@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - interpret@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + version "2.3.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz" + integrity sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg== ipaddr.js@1.9.1: version "1.9.1" @@ -1225,9 +1515,9 @@ is-inside-container@^1.0.0: is-docker "^3.0.0" is-network-error@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz" - integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== + version "1.3.0" + resolved "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz" + integrity sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw== is-number@^7.0.0: version "7.0.0" @@ -1247,9 +1537,9 @@ is-plain-object@^2.0.4: isobject "^3.0.1" is-wsl@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz" - integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + version "3.1.1" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz" + integrity sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw== dependencies: is-inside-container "^1.0.0" @@ -1293,12 +1583,12 @@ kind-of@^6.0.2: integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== launch-editor@^2.6.1: - version "2.10.0" - resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz" - integrity sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA== + version "2.13.0" + resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.0.tgz" + integrity sha512-u+9asUHMJ99lA15VRMXw5XKfySFR9dGXwgsgS14YTbUq3GITP58mIM32At90P5fZ+MUId5Yw+IwI/yKub7jnCQ== dependencies: - picocolors "^1.0.0" - shell-quote "^1.8.1" + picocolors "^1.1.1" + shell-quote "^1.8.3" loader-runner@^4.3.1: version "4.3.1" @@ -1322,14 +1612,24 @@ media-typer@0.3.0: resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memfs@^4.6.0: - version "4.17.2" - resolved "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz" - integrity sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg== +memfs@^4.43.1: + version "4.56.10" + resolved "https://registry.npmjs.org/memfs/-/memfs-4.56.10.tgz" + integrity sha512-eLvzyrwqLHnLYalJP7YZ3wBe79MXktMdfQbvMrVD80K+NhrIukCVBvgP30zTJYEEDh9hZ/ep9z0KOdD7FSHo7w== dependencies: - "@jsonjoy.com/json-pack" "^1.0.3" - "@jsonjoy.com/util" "^1.3.0" - tree-dump "^1.0.1" + "@jsonjoy.com/fs-core" "4.56.10" + "@jsonjoy.com/fs-fsa" "4.56.10" + "@jsonjoy.com/fs-node" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-to-fsa" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + "@jsonjoy.com/fs-print" "4.56.10" + "@jsonjoy.com/fs-snapshot" "4.56.10" + "@jsonjoy.com/json-pack" "^1.11.0" + "@jsonjoy.com/util" "^1.9.0" + glob-to-regex.js "^1.0.1" + thingies "^2.5.0" + tree-dump "^1.0.3" tslib "^2.0.0" merge-descriptors@1.0.3: @@ -1352,26 +1652,38 @@ methods@~1.1.2: resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== +micromatch@^4.0.2, micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + "mime-db@>= 1.43.0 < 2", mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34, mime-types@~2.1.35: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime-types@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" @@ -1382,16 +1694,16 @@ minimalistic-assert@^1.0.0: resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -1405,6 +1717,11 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + negotiator@0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" @@ -1415,11 +1732,6 @@ neo-async@^2.6.2: resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -node-forge@^1: - version "1.3.3" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz" - integrity sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg== - node-releases@^2.0.27: version "2.0.27" resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz" @@ -1440,27 +1752,27 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@^2.4.1, on-finished@~2.4.1, on-finished@2.4.1: +on-finished@^2.4.1, on-finished@~2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== +on-headers@~1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz" + integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== open@^10.0.3: - version "10.1.2" - resolved "https://registry.npmjs.org/open/-/open-10.1.2.tgz" - integrity sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw== + version "10.2.0" + resolved "https://registry.npmjs.org/open/-/open-10.2.0.tgz" + integrity sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA== dependencies: default-browser "^5.2.1" define-lazy-prop "^3.0.0" is-inside-container "^1.0.0" - is-wsl "^3.1.0" + wsl-utils "^0.1.0" p-limit@^2.2.0: version "2.3.0" @@ -1490,7 +1802,7 @@ p-try@^2.0.0: resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -1520,7 +1832,7 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0, picocolors@^1.1.1: +picocolors@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -1537,6 +1849,18 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkijs@^3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz" + integrity sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw== + dependencies: + "@noble/hashes" "1.4.0" + asn1js "^3.0.6" + bytestreamjs "^2.0.1" + pvtsutils "^1.3.6" + pvutils "^1.1.3" + tslib "^2.8.1" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -1550,10 +1874,17 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pvtsutils@^1.3.6: + version "1.3.6" + resolved "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz" + integrity sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg== + dependencies: + tslib "^2.8.1" + +pvutils@^1.1.3: + version "1.1.5" + resolved "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz" + integrity sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA== qs@~6.14.0: version "6.14.2" @@ -1562,13 +1893,6 @@ qs@~6.14.0: dependencies: side-channel "^1.1.0" -qs@6.13.0: - version "6.13.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -1586,15 +1910,15 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== +raw-body@~2.5.3: + version "2.5.3" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz" + integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + unpipe "~1.0.0" readable-stream@^2.0.1: version "2.3.8" @@ -1632,6 +1956,11 @@ rechoir@^0.8.0: dependencies: resolve "^1.20.0" +reflect-metadata@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" @@ -1669,14 +1998,14 @@ retry@^0.13.1: integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + version "1.1.0" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== run-applescript@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz" - integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + version "7.1.0" + resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz" + integrity sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q== run-parallel@^1.1.9: version "1.2.0" @@ -1695,11 +2024,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -1720,32 +2044,32 @@ select-hose@^2.0.0: resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz" - integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== +selfsigned@^5.5.0: + version "5.5.0" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz" + integrity sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew== dependencies: - "@types/node-forge" "^1.3.0" - node-forge "^1" + "@peculiar/x509" "^1.14.2" + pkijs "^3.3.3" -send@~0.19.0, send@0.19.0: - version "0.19.0" - resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== +send@~0.19.0, send@~0.19.1: + version "0.19.2" + resolved "https://registry.npmjs.org/send/-/send-0.19.2.tgz" + integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg== dependencies: debug "2.6.9" depd "2.0.0" destroy "1.2.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" + fresh "~0.5.2" + http-errors "~2.0.1" mime "1.6.0" ms "2.1.3" - on-finished "2.4.1" + on-finished "~2.4.1" range-parser "~1.2.1" - statuses "2.0.1" + statuses "~2.0.2" serialize-javascript@^6.0.0, serialize-javascript@^6.0.2: version "6.0.2" @@ -1755,34 +2079,29 @@ serialize-javascript@^6.0.0, serialize-javascript@^6.0.2: randombytes "^2.1.0" serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + version "1.9.2" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz" + integrity sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ== dependencies: - accepts "~1.3.4" + accepts "~1.3.8" batch "0.6.1" debug "2.6.9" escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" + http-errors "~1.8.0" + mime-types "~2.1.35" + parseurl "~1.3.3" serve-static@~1.16.2: - version "1.16.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + version "1.16.3" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz" + integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA== dependencies: encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.19.0" + send "~0.19.1" -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: +setprototypeof@~1.2.0, setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -1806,7 +2125,7 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.8.1: +shell-quote@^1.8.3: version "1.8.3" resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz" integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== @@ -1840,7 +2159,7 @@ side-channel-weakmap@^1.0.2: object-inspect "^1.13.3" side-channel-map "^1.0.1" -side-channel@^1.0.6, side-channel@^1.1.0: +side-channel@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== @@ -1901,15 +2220,15 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" -"statuses@>= 1.4.0 < 2": +"statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -statuses@~2.0.1, statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +statuses@~2.0.1, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== string_decoder@^1.1.1: version "1.3.0" @@ -1963,10 +2282,10 @@ terser@^5.31.1: commander "^2.20.0" source-map-support "~0.5.20" -thingies@^1.20.0: - version "1.21.0" - resolved "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz" - integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== +thingies@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz" + integrity sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw== thunky@^1.0.2: version "1.1.0" @@ -1980,21 +2299,33 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.1: +toidentifier@~1.0.1, toidentifier@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tree-dump@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz" - integrity sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg== +tree-dump@^1.0.3, tree-dump@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz" + integrity sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA== -tslib@^2, tslib@^2.0.0, tslib@2: +tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2, tslib@^2.0.0, tslib@^2.8.1, tslib@2: version "2.8.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +tsyringe@^4.10.0: + version "4.10.0" + resolved "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz" + integrity sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw== + dependencies: + tslib "^1.9.3" + type-is@~1.6.18: version "1.6.18" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" @@ -2003,7 +2334,12 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -unpipe@~1.0.0, unpipe@1.0.0: +undici-types@~7.18.0: + version "7.18.2" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz" + integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== + +unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -2016,13 +2352,6 @@ update-browserslist-db@^1.2.0: escalade "^3.2.0" picocolors "^1.1.1" -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -2078,25 +2407,25 @@ webpack-cli@^5.1.4, webpack-cli@5.x.x: webpack-merge "^5.7.3" webpack-dev-middleware@^7.4.2: - version "7.4.2" - resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz" - integrity sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA== + version "7.4.5" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz" + integrity sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA== dependencies: colorette "^2.0.10" - memfs "^4.6.0" - mime-types "^2.1.31" + memfs "^4.43.1" + mime-types "^3.0.1" on-finished "^2.4.1" range-parser "^1.2.1" schema-utils "^4.0.0" webpack-dev-server@^5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz" - integrity sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ== + version "5.2.3" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz" + integrity sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ== dependencies: "@types/bonjour" "^3.5.13" "@types/connect-history-api-fallback" "^1.5.4" - "@types/express" "^4.17.21" + "@types/express" "^4.17.25" "@types/express-serve-static-core" "^4.17.21" "@types/serve-index" "^1.9.4" "@types/serve-static" "^1.15.5" @@ -2106,17 +2435,17 @@ webpack-dev-server@^5.2.1: bonjour-service "^1.2.1" chokidar "^3.6.0" colorette "^2.0.10" - compression "^1.7.4" + compression "^1.8.1" connect-history-api-fallback "^2.0.0" - express "^4.21.2" + express "^4.22.1" graceful-fs "^4.2.6" - http-proxy-middleware "^2.0.7" + http-proxy-middleware "^2.0.9" ipaddr.js "^2.1.0" launch-editor "^2.6.1" open "^10.0.3" p-retry "^6.2.0" schema-utils "^4.2.0" - selfsigned "^2.4.1" + selfsigned "^5.5.0" serve-index "^1.9.1" sockjs "^0.3.24" spdy "^4.0.2" @@ -2124,22 +2453,23 @@ webpack-dev-server@^5.2.1: ws "^8.18.0" webpack-merge@^5.7.3: - version "5.8.0" - resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + version "5.10.0" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" + flat "^5.0.2" wildcard "^2.0.0" webpack-sources@^3.3.3: - version "3.3.3" - resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz" - integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== + version "3.3.4" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz" + integrity sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q== webpack@^5.0.0, webpack@^5.1.0, webpack@^5.98.0, webpack@5.x.x: - version "5.105.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz" - integrity sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw== + version "5.105.2" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz" + integrity sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw== dependencies: "@types/eslint-scope" "^3.7.7" "@types/estree" "^1.0.8" @@ -2189,11 +2519,18 @@ which@^2.0.1: isexe "^2.0.0" wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + version "2.0.1" + resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== ws@^8.18.0: - version "8.18.1" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz" - integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== + version "8.19.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz" + integrity sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg== + +wsl-utils@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz" + integrity sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw== + dependencies: + is-wsl "^3.1.0" From 5e733a5ebfdc7511d89879a8ce44129e0151e1e6 Mon Sep 17 00:00:00 2001 From: bnemeroff Date: Wed, 25 Feb 2026 01:48:15 -0800 Subject: [PATCH 28/51] SEO: Add frontmatter, structured data, and sitemap config (#6453) * SEO: Add frontmatter, structured data, and sitemap config * Fix: restore deleted prebuild output file --------- Co-authored-by: Benjamin Nemeroff Co-authored-by: mfahampshire --- documentation/README.md | 37 ++++ .../command-outputs/node-api-check-help.md | 2 +- .../node-api-check-query-help.md | 2 +- documentation/docs/next-sitemap.config.js | 28 +++ .../docs/pages/apis/introduction.mdx | 8 + documentation/docs/pages/developers/chain.md | 8 + .../docs/pages/developers/chain/rpc-node.md | 8 + documentation/docs/pages/developers/index.mdx | 8 + .../docs/pages/developers/nymvpncli.mdx | 8 + documentation/docs/pages/developers/rust.mdx | 8 + .../docs/pages/developers/rust/mixnet.mdx | 8 + .../docs/pages/developers/rust/tcpproxy.mdx | 8 + documentation/docs/pages/developers/tools.mdx | 8 + .../docs/pages/developers/tools/nym-cli.md | 8 + .../docs/pages/developers/typescript.mdx | 7 + documentation/docs/pages/index.mdx | 8 + documentation/docs/pages/network/index.md | 8 + .../pages/operators/binaries/building-nym.mdx | 9 + .../docs/pages/operators/changelog.mdx | 8 + .../operators/community-counsel/legal.mdx | 8 + .../operators/community-counsel/templates.mdx | 8 + .../docs/pages/operators/faq/general-faq.mdx | 8 + .../docs/pages/operators/introduction.mdx | 8 + documentation/docs/pages/operators/nodes.mdx | 8 + .../nodes/nym-node/configuration.mdx | 8 + .../operators/performance-and-testing.mdx | 8 + .../docs/pages/operators/sandbox.mdx | 8 + .../pages/operators/troubleshooting/nodes.mdx | 8 + documentation/docs/public/robots.txt | 15 ++ documentation/docs/public/sitemap-0.xml | 177 ++++++++++++++++++ documentation/docs/public/sitemap.xml | 5 + documentation/docs/theme.config.tsx | 136 +++++++++++--- .../scripts/next-scripts/python-prebuild.sh | 28 +-- 33 files changed, 578 insertions(+), 44 deletions(-) create mode 100644 documentation/docs/next-sitemap.config.js create mode 100644 documentation/docs/public/robots.txt create mode 100644 documentation/docs/public/sitemap-0.xml create mode 100644 documentation/docs/public/sitemap.xml diff --git a/documentation/README.md b/documentation/README.md index b27ac7733d..7f2421af55 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -66,3 +66,40 @@ As a general approach, licensing is as follows this pattern: * Nym applications and binaries are [GPL-3.0-only](https://www.gnu.org/licenses/) * Used libraries and different components are [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.html) or [MIT](https://mit-license.org/) + +## SEO & Structured Data + +### Frontmatter +Every `.mdx` page supports frontmatter fields that control meta tags, Open Graph, and JSON-LD schema: +```yaml +--- +title: "Page Title for Search Engines" +description: "Unique meta description for this page." +schemaType: "TechArticle" # TechArticle (default), HowTo, or FAQPage +section: "Operators" # Operators, Developers, Network, APIs +lastUpdated: "2026-02-11" # Feeds dateModified schema +breadcrumbLabel: "Custom Label" # Optional, overrides URL slug in breadcrumbs +--- +``` + +Pages without frontmatter fall back to the default Nym description. See the [full spec](https://docs.google.com/document/d/14Af5brvEQSS0MIX9e_cZ3BktvgQeqA5CnzSGlKb7pYw/edit) for all page blocks. + +### Sitemap +Generated automatically on build. After building: +```bash +npx next-sitemap +``` +Outputs `sitemap.xml` and `robots.txt` to `/public`. + +### Environment Variable +Set in production: +``` +NEXT_PUBLIC_SITE_URL=https://nymtech.net/docs +``` + +### Schema Types +| Type | Use When | +|------|----------| +| TechArticle | Reference docs, config guides, overviews (default) | +| HowTo | Step-by-step install/setup guides | +| FAQPage | Question-answer pages | diff --git a/documentation/docs/components/outputs/command-outputs/node-api-check-help.md b/documentation/docs/components/outputs/command-outputs/node-api-check-help.md index 875b2921ae..d41e42995c 100644 --- a/documentation/docs/components/outputs/command-outputs/node-api-check-help.md +++ b/documentation/docs/components/outputs/command-outputs/node-api-check-help.md @@ -11,7 +11,7 @@ positional arguments: version_count (v, version) Sum of nodes in given version(s) -options: +optional arguments: -h, --help show this help message and exit -V, --version show program's version number and exit ``` diff --git a/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md b/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md index 999f3d4a6f..b96adaebe5 100644 --- a/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md +++ b/documentation/docs/components/outputs/command-outputs/node-api-check-query-help.md @@ -6,7 +6,7 @@ usage: Nym-node API check query_stats [-h] [--no_routing_history] positional arguments: id supply nym-node identity key -options: +optional arguments: -h, --help show this help message and exit --no_routing_history Display node stats without routing history --no_verloc_metrics Display node stats without verloc metrics diff --git a/documentation/docs/next-sitemap.config.js b/documentation/docs/next-sitemap.config.js new file mode 100644 index 0000000000..c676d09f91 --- /dev/null +++ b/documentation/docs/next-sitemap.config.js @@ -0,0 +1,28 @@ +/** @type {import('next-sitemap').IConfig} */ +module.exports = { + siteUrl: 'https://nymtech.net/docs', + generateRobotsTxt: true, + outDir: './public', + exclude: ['/api/*', '/docs/_*', '/404'], + robotsTxtOptions: { + policies: [ + { userAgent: '*', allow: '/' }, + { userAgent: '*', disallow: ['/api/', '/_next/'] }, + ], + additionalSitemaps: [ + 'https://nymtech.net/docs/sitemap-docs.xml', + ], + }, + transform: async (config, path) => ({ + loc: path, + changefreq: path.includes('/changelog') + ? 'weekly' + : path.includes('/docs/operators') || path.includes('/docs/developers') + ? 'monthly' + : 'yearly', + priority: path === '/docs' ? 1.0 + : path.includes('/operators/nodes') || path.includes('/developers') ? 0.8 + : 0.6, + lastmod: new Date().toISOString(), + }), +} \ No newline at end of file diff --git a/documentation/docs/pages/apis/introduction.mdx b/documentation/docs/pages/apis/introduction.mdx index c2c521ba8c..176ed71489 100644 --- a/documentation/docs/pages/apis/introduction.mdx +++ b/documentation/docs/pages/apis/introduction.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym API Reference: Network Infrastructure" +description: "Interactive API documentation for Nym network infrastructure. Query node status, network topology, blockchain state & mixnet performance programmatically." +schemaType: "TechArticle" +section: "APIs" +lastUpdated: "2026-02-01" +--- + # Introduction This site contains interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet. diff --git a/documentation/docs/pages/developers/chain.md b/documentation/docs/pages/developers/chain.md index 58eed5ecaf..aa77836f61 100644 --- a/documentation/docs/pages/developers/chain.md +++ b/documentation/docs/pages/developers/chain.md @@ -1,3 +1,11 @@ +--- +title: "Nyx Blockchain & Nym Smart Contracts" +description: "Developer guide for interacting with the Nyx blockchain via Cosmos SDK. Covers CLI wallet setup, Cosmos Registry, Ledger Live, and RPC node deployment." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-11" +--- + # Interacting with Nyx Chain and Smart Contracts There are two options for interacting with the blockchain to send tokens or interact with deployed smart contracts: diff --git a/documentation/docs/pages/developers/chain/rpc-node.md b/documentation/docs/pages/developers/chain/rpc-node.md index ddd8afc538..091edcd56e 100644 --- a/documentation/docs/pages/developers/chain/rpc-node.md +++ b/documentation/docs/pages/developers/chain/rpc-node.md @@ -1,3 +1,11 @@ +--- +title: "Run a Nyx RPC Node for the Nym Network" +description: "Set up and run a dedicated RPC node for the Nyx blockchain. Query network state, serve chain data, and interact with Nym smart contracts programmatically." +schemaType: "HowTo" +section: "Developers" +lastUpdated: "2026-02-01" +--- + # RPC Nodes RPC Nodes (which might otherwise be referred to as 'Lite Nodes' or just 'Full Nodes') differ from Validators in that they hold a copy of the Nyx blockchain, but do **not** participate in consensus / block-production. diff --git a/documentation/docs/pages/developers/index.mdx b/documentation/docs/pages/developers/index.mdx index f1cdd88680..a6e1d197f1 100644 --- a/documentation/docs/pages/developers/index.mdx +++ b/documentation/docs/pages/developers/index.mdx @@ -1,2 +1,10 @@ +--- +title: "Nym Developer Portal: SDKs & Tools" +description: "Developer documentation for building privacy-enhanced applications on the Nym mixnet. Covers Rust SDK, TypeScript SDK, blockchain interaction & CLI tools." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-01" +--- + # Introduction Nym's developer documentation covering core concepts of integrating with the Mixnet, interacting with the Nyx blockchain, an overview of the avaliable tools, and our SDK docs. diff --git a/documentation/docs/pages/developers/nymvpncli.mdx b/documentation/docs/pages/developers/nymvpncli.mdx index b3e915d854..660c7fd8e6 100644 --- a/documentation/docs/pages/developers/nymvpncli.mdx +++ b/documentation/docs/pages/developers/nymvpncli.mdx @@ -1,3 +1,11 @@ +--- +title: "NymVPN CLI: Run NymVPN from the Command Line" +description: "Install and run NymVPN from the terminal on Linux, macOS, and Windows. Requires Rust and Go. Includes mnemonic generation and testnet configuration." +schemaType: "HowTo" +section: "Developers" +lastUpdated: "2026-02-11" +--- + import { Callout } from 'nextra/components' # Nym VPN CLI diff --git a/documentation/docs/pages/developers/rust.mdx b/documentation/docs/pages/developers/rust.mdx index d39707bb44..08c93ccc12 100644 --- a/documentation/docs/pages/developers/rust.mdx +++ b/documentation/docs/pages/developers/rust.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Rust SDK: Privacy Apps for the Mixnet" +description: "Rust SDK reference for building privacy applications on the Nym mixnet. Covers TcpProxy, Mixnet module, Client Pool, FFI bindings, and code examples." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-01" +--- + # Introduction The Rust SDK allows exposes a few different modules, some more plug and play than others. Each of which handles exposes a Nym Client, which handles finding and using a route for packets through the Mixnet, encryption, and cover traffic, all under the hood. diff --git a/documentation/docs/pages/developers/rust/mixnet.mdx b/documentation/docs/pages/developers/rust/mixnet.mdx index 0524619874..2fc45fee11 100644 --- a/documentation/docs/pages/developers/rust/mixnet.mdx +++ b/documentation/docs/pages/developers/rust/mixnet.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Rust SDK: Mixnet Messaging Module" +description: "Use the Nym Rust SDK Mixnet module to send messages through the mixnet. Covers builder patterns, custom topologies, SOCKS proxy, and anonymous replies." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-01" +--- + # Mixnet Module import { Callout } from 'nextra/components'; diff --git a/documentation/docs/pages/developers/rust/tcpproxy.mdx b/documentation/docs/pages/developers/rust/tcpproxy.mdx index d34d4f0a37..e8fb886374 100644 --- a/documentation/docs/pages/developers/rust/tcpproxy.mdx +++ b/documentation/docs/pages/developers/rust/tcpproxy.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym TcpProxy: Route TCP via the Mixnet" +description: "Route TCP traffic through the Nym mixnet using the TcpProxy Rust module. Covers architecture, single and multi-connection patterns, and troubleshooting." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-11" +--- + # TcpProxy Module import { Callout } from 'nextra/components'; diff --git a/documentation/docs/pages/developers/tools.mdx b/documentation/docs/pages/developers/tools.mdx index 9f0299c333..ecfec9bd48 100644 --- a/documentation/docs/pages/developers/tools.mdx +++ b/documentation/docs/pages/developers/tools.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Developer Tools: CLI, Echo & TcpProxy" +description: "Overview of Nym developer tools including nym-cli for blockchain interaction, echo server for traffic testing, and standalone TcpProxy binary downloads." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-01" +--- + # Tools There are a few tools available to developers for chain interaction: the `nym-cli` tool, which operates as an easier-to-use wrapper around `nyxd`, to allow operators to script interactions with their infrastructure (and those who prefer CLI tools). diff --git a/documentation/docs/pages/developers/tools/nym-cli.md b/documentation/docs/pages/developers/tools/nym-cli.md index e2be8ea3ef..e8f4f56903 100644 --- a/documentation/docs/pages/developers/tools/nym-cli.md +++ b/documentation/docs/pages/developers/tools/nym-cli.md @@ -1,3 +1,11 @@ +--- +title: "Nym CLI: Mixnet & Blockchain Commands" +description: "Use nym-cli to interact with the Nym mixnet and Nyx blockchain. Manage nodes, delegate stake, and query network state from the command line." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-11" +--- + # Nym-CLI This is a CLI tool for interacting with: diff --git a/documentation/docs/pages/developers/typescript.mdx b/documentation/docs/pages/developers/typescript.mdx index 3cf683f0f4..18929c5f1b 100644 --- a/documentation/docs/pages/developers/typescript.mdx +++ b/documentation/docs/pages/developers/typescript.mdx @@ -1,3 +1,10 @@ +--- +title: "Nym TypeScript SDK: Privacy for Web Apps" +description: "TypeScript SDK for integrating web apps with the Nym mixnet. Covers mixFetch, Mixnet Client, Smart Contracts, and Cosmos Kit with live playground examples." +schemaType: "TechArticle" +section: "Developers" +lastUpdated: "2026-02-11" +--- # Introduction diff --git a/documentation/docs/pages/index.mdx b/documentation/docs/pages/index.mdx index cac9844c41..f3953b9b63 100644 --- a/documentation/docs/pages/index.mdx +++ b/documentation/docs/pages/index.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Docs: Guides, SDKs & Architecture" +description: "Official Nym documentation hub. Build privacy-enhanced applications, run mixnet nodes, and explore the network architecture and protocols powering NymVPN." +schemaType: "TechArticle" +section: "Documentation" +lastUpdated: "2026-02-11" +--- + import { LandingPage } from '../components/landing-page.tsx' diff --git a/documentation/docs/pages/network/index.md b/documentation/docs/pages/network/index.md index 2fc3a1a583..0cec22555c 100644 --- a/documentation/docs/pages/network/index.md +++ b/documentation/docs/pages/network/index.md @@ -1,3 +1,11 @@ +--- +title: "Nym Network Architecture: How the Mixnet Works" +description: "Deep dive into Nym network architecture, cryptographic systems, and how the mixnet provides network-level privacy against end-to-end attackers." +schemaType: "TechArticle" +section: "Network" +lastUpdated: "2026-02-11" +--- + # Introduction Nym's network documentation covering network architecture, node types, tokenomics, and crypto systems. diff --git a/documentation/docs/pages/operators/binaries/building-nym.mdx b/documentation/docs/pages/operators/binaries/building-nym.mdx index f730cfcdd8..8a4da7b3aa 100644 --- a/documentation/docs/pages/operators/binaries/building-nym.mdx +++ b/documentation/docs/pages/operators/binaries/building-nym.mdx @@ -1,3 +1,12 @@ +--- +title: "Building Nym from Source: Linux, macOS & Windows" +description: "How to build Nym platform binaries from source code. Covers dependencies for Debian, Arch, macOS, and Windows. Requires Rust toolchain and Git." +schemaType: "HowTo" +section: "Operators" +lastUpdated: "2026-02-01" +breadcrumbLabel: "Building from Source" +--- + import { Callout } from 'nextra/components'; import { VarInfo } from 'components/variable-info.tsx'; diff --git a/documentation/docs/pages/operators/changelog.mdx b/documentation/docs/pages/operators/changelog.mdx index 677cdaafc9..a5b5669303 100644 --- a/documentation/docs/pages/operators/changelog.mdx +++ b/documentation/docs/pages/operators/changelog.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Node Changelog & Release History" +description: "Complete changelog for Nym node releases, binary updates, SDK changes, and network upgrades. Sorted newest first with links to relevant documentation." +schemaType: "TechArticle" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Callout } from 'nextra/components'; import { Tabs } from 'nextra/components'; import { MyTab } from 'components/generic-tabs.tsx'; diff --git a/documentation/docs/pages/operators/community-counsel/legal.mdx b/documentation/docs/pages/operators/community-counsel/legal.mdx index 6edc156904..341cbb0141 100644 --- a/documentation/docs/pages/operators/community-counsel/legal.mdx +++ b/documentation/docs/pages/operators/community-counsel/legal.mdx @@ -1,3 +1,11 @@ +--- +title: "Legal Guide for Nym Exit Gateway Operators" +description: "Legal considerations for running a Nym exit gateway. Covers ISP communication templates, jurisdiction guidance, and abuse report response strategies." +schemaType: "TechArticle" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Callout } from 'nextra/components'; import { Tabs } from 'nextra/components'; import { RunTabs } from 'components/operators/nodes/node-run-command-tabs'; diff --git a/documentation/docs/pages/operators/community-counsel/templates.mdx b/documentation/docs/pages/operators/community-counsel/templates.mdx index c701f1a003..b27ceabc71 100644 --- a/documentation/docs/pages/operators/community-counsel/templates.mdx +++ b/documentation/docs/pages/operators/community-counsel/templates.mdx @@ -1,3 +1,11 @@ +--- +title: "Email & Legal Templates for Nym Node Operators" +description: "Ready-to-use templates for communicating with VPS providers about running Nym nodes. Includes introduction emails and abuse report response templates." +schemaType: "TechArticle" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Callout } from 'nextra/components'; import { Tabs } from 'nextra/components'; import { RunTabs } from 'components/operators/nodes/node-run-command-tabs'; diff --git a/documentation/docs/pages/operators/faq/general-faq.mdx b/documentation/docs/pages/operators/faq/general-faq.mdx index 400c9c0a56..e2eb502243 100644 --- a/documentation/docs/pages/operators/faq/general-faq.mdx +++ b/documentation/docs/pages/operators/faq/general-faq.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Node Operator FAQ: Questions Answered" +description: "Frequently asked questions about running Nym nodes. Covers VPS selection, staking requirements, node configuration, rewards, and community resources." +schemaType: "FAQPage" +section: "Operators" +lastUpdated: "2026-02-01" +--- + # General Operators FAQ ## Nym Network diff --git a/documentation/docs/pages/operators/introduction.mdx b/documentation/docs/pages/operators/introduction.mdx index 8a564662fe..1af11b5098 100644 --- a/documentation/docs/pages/operators/introduction.mdx +++ b/documentation/docs/pages/operators/introduction.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Node Operator Guide & Prerequisites" +description: "Introduction to running Nym nodes. Learn requirements, skill expectations, time commitment, and how to get started operating a node on the Nym mixnet." +schemaType: "TechArticle" +section: "Operators" +lastUpdated: "2026-02-11" +--- + import QuickStart from 'components/operators/snippets/quick-start.mdx' # Introduction diff --git a/documentation/docs/pages/operators/nodes.mdx b/documentation/docs/pages/operators/nodes.mdx index 5deb625aea..630aed0838 100644 --- a/documentation/docs/pages/operators/nodes.mdx +++ b/documentation/docs/pages/operators/nodes.mdx @@ -1,3 +1,11 @@ +--- +title: "How to Set Up & Run a Nym Node on the Mixnet" +description: "Step-by-step guide to installing, configuring, and running a nym-node on the Nym mixnet. Covers prerequisites, staking requirements, and CLI setup." +schemaType: "HowTo" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Callout } from 'nextra/components'; import { Steps } from 'nextra/components'; import { Tabs } from 'nextra/components'; diff --git a/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx b/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx index d791e7f946..5ea51cd7fa 100644 --- a/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx +++ b/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Node Configuration & Systemd Setup" +description: "Configure your nym-node with systemd automation, reverse proxy, IPv6, and custom settings. Includes service file templates and maintenance tips." +schemaType: "TechArticle" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Callout } from 'nextra/components'; import { Tabs } from 'nextra/components'; import { VarInfo } from 'components/variable-info.tsx'; diff --git a/documentation/docs/pages/operators/performance-and-testing.mdx b/documentation/docs/pages/operators/performance-and-testing.mdx index 208530a4c2..b6846e2dc8 100644 --- a/documentation/docs/pages/operators/performance-and-testing.mdx +++ b/documentation/docs/pages/operators/performance-and-testing.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Node Performance Monitoring & Testing Guide" +description: "Monitor your Nym node performance with Prometheus, Grafana, and community tools. Covers key metrics, routing score analysis, and testing best practices." +schemaType: "TechArticle" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Callout } from 'nextra/components'; import { Steps } from 'nextra/components'; import { Tabs } from 'nextra/components'; diff --git a/documentation/docs/pages/operators/sandbox.mdx b/documentation/docs/pages/operators/sandbox.mdx index a46d76a35c..20a4d97b85 100644 --- a/documentation/docs/pages/operators/sandbox.mdx +++ b/documentation/docs/pages/operators/sandbox.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Sandbox Testnet for Node Operators" +description: "Run your Nym node in the Sandbox testnet environment. Test configurations, try new features, and experiment safely before deploying to mainnet." +schemaType: "HowTo" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Callout } from 'nextra/components'; import { VarInfo } from 'components/variable-info.tsx'; import { Steps } from 'nextra/components';import { Tabs } from 'nextra/components' diff --git a/documentation/docs/pages/operators/troubleshooting/nodes.mdx b/documentation/docs/pages/operators/troubleshooting/nodes.mdx index 45b7b9793e..91cdbd6419 100644 --- a/documentation/docs/pages/operators/troubleshooting/nodes.mdx +++ b/documentation/docs/pages/operators/troubleshooting/nodes.mdx @@ -1,3 +1,11 @@ +--- +title: "Nym Node Troubleshooting: Common Errors & Fixes" +description: "Solutions for common nym-node issues including build failures, connectivity problems, and configuration errors. Includes error messages and fix steps." +schemaType: "TechArticle" +section: "Operators" +lastUpdated: "2026-02-01" +--- + import { Tabs } from 'nextra/components'; import { Callout } from 'nextra/components'; import { VarInfo } from 'components/variable-info.tsx'; diff --git a/documentation/docs/public/robots.txt b/documentation/docs/public/robots.txt new file mode 100644 index 0000000000..b3276b24e6 --- /dev/null +++ b/documentation/docs/public/robots.txt @@ -0,0 +1,15 @@ +# * +User-agent: * +Allow: / + +# * +User-agent: * +Disallow: /api/ +Disallow: /_next/ + +# Host +Host: https://nymtech.net/docs + +# Sitemaps +Sitemap: https://nymtech.net/docs/sitemap.xml +Sitemap: https://nymtech.net/docs/sitemap-docs.xml diff --git a/documentation/docs/public/sitemap-0.xml b/documentation/docs/public/sitemap-0.xml new file mode 100644 index 0000000000..37563d9ee6 --- /dev/null +++ b/documentation/docs/public/sitemap-0.xml @@ -0,0 +1,177 @@ + + +https://nymtech.net/docs2026-02-13T15:03:22.514Zyearly0.6 +https://nymtech.net/docs/apis/cosmos-sdk-nyx2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/cosmos-sdk-nyx/mainnet2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/cosmos-sdk-nyx/sandbox2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/explorer-api2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/explorer-api/mainnet2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/explorer-api/sandbox2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/introduction2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/ns-api2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/ns-api/mainnet2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/ns-api/ns-api-run-deploy2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/ns-api/sandbox2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/nym-api2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/apis/nym-api/mainnet2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/developers2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/archive/nym-connect2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/browsers2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/chain2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/chain/cli-wallet2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/chain/cosmos-registry2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/chain/ledger-live2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/chain/rpc-node2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/socks52026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/socks5/commands2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/socks5/config2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/socks5/setup2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/socks5/usage2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/webassembly-client2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/websocket2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/websocket/commands2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/websocket/config2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/websocket/examples2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/websocket/setup2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/clients/websocket/usage2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/coc2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/concepts/message-queue2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/integrations2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/licensing2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/native2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/nymvpncli2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/client-pool2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/client-pool/architecture2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/client-pool/example2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/ffi2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/importing2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/builders2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/builders/builder2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/builders/builder-with-storage2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/custom-topology2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/custom-topology/custom-provider2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/custom-topology/manually-overwrite-topology2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/simple2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/socks2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/split-send2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/storage2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/surbs2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/examples/testnet2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/message-helpers2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/message-types2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/mixnet/troubleshooting2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/tcpproxy2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/tcpproxy/architecture2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/tcpproxy/examples/multiconn2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/tcpproxy/examples/singleconn2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/rust/tcpproxy/troubleshooting2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/tools2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/tools/echo-server2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/tools/nym-cli2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/tools/nym-cli/commands2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/tools/nym-cli/usage2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/tools/standalone-tcpproxy2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/FAQ2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/bundling/bundling2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/bundling/esbuild2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/bundling/webpack2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/examples/cosmos-kit2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/examples/mix-fetch2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/examples/mixnet2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/examples/nym-smart-contracts2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/installation2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/overview2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/playground/cosmos-kit2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/playground/mixfetch2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/playground/mixnodes2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/playground/traffic2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/playground/wallet2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/developers/typescript/start2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/network2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/architecture2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/architecture/mixnet2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/architecture/nym-not-p2p2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/architecture/nym-vs-others2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/architecture/nyx2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/coc2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/concepts2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/concepts/anonymous-replies2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/concepts/cover-traffic2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/concepts/epochs2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/concepts/loopix2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/concepts/mixing2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/cryptography2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/cryptography/sphinx2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/cryptography/zk-nym2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/cryptography/zk-nym/double-spend-prot2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/cryptography/zk-nym/rerandomise2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/cryptography/zk-nym/unlinkability2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/cryptography/zk-nym/zk-nym-overview2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/licensing2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/traffic2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/traffic/acks2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/traffic/addressing-system2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/traffic/anonymous-replies2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/traffic/flow2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/network/traffic/hops2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/archive2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/archive/faq/smoosh-faq2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/binaries/building-nym2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/binaries/pre-built-binaries2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/changelog2026-02-13T15:03:22.515Zweekly0.6 +https://nymtech.net/docs/operators/community-counsel2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/add-content2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/exit-gateway2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/isp-list2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/jurisdictions2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/jurisdictions/swiss2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/jurisdictions/united-states2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/landing-pages2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/legal2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/community-counsel/templates2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/faq/general-faq2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/faq/nym-nodes-faq2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/faq/nyx-faq2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/introduction2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/misc/coc2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/misc/licensing2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/misc/toc2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/nodes2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/maintenance2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/maintenance/manual-upgrade2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/maintenance/nymvisor-upgrade2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/maintenance/nymvisor-upgrade/nymvisor-configuration2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/nym-node2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/nym-node/bonding2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/nym-node/configuration2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/nym-node/configuration/proxy-configuration2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/nym-node/setup2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/preliminary-steps2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/preliminary-steps/vps-setup2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/preliminary-steps/vps-setup/advanced2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/preliminary-steps/wallet-preparation2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/validator-setup2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/validator-setup/nym-api2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/nodes/validator-setup/nyx-configuration2026-02-13T15:03:22.515Zyearly0.8 +https://nymtech.net/docs/operators/orchestration2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/orchestration/ansible2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/performance-and-testing2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/performance-and-testing/gateway-probe2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/performance-and-testing/gateway-probe-details2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/performance-and-testing/prometheus-grafana2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/performance-and-testing/prometheus-grafana/explorenym-scripts2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/release-cycle2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/sandbox2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/tokenomics2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/tokenomics/mixnet-rewards2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/tokenomics/validator-rewards2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/tools2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/troubleshooting/nodes2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/troubleshooting/validators2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/troubleshooting/vps-isp2026-02-13T15:03:22.515Zyearly0.6 +https://nymtech.net/docs/operators/variables2026-02-13T15:03:22.515Zyearly0.6 + \ No newline at end of file diff --git a/documentation/docs/public/sitemap.xml b/documentation/docs/public/sitemap.xml new file mode 100644 index 0000000000..becdf3489f --- /dev/null +++ b/documentation/docs/public/sitemap.xml @@ -0,0 +1,5 @@ + + +https://nymtech.net/docs/sitemap-0.xml +https://nymtech.net/docs/sitemap-docs.xml + \ No newline at end of file diff --git a/documentation/docs/theme.config.tsx b/documentation/docs/theme.config.tsx index 9d07c2ba75..603d6407ef 100644 --- a/documentation/docs/theme.config.tsx +++ b/documentation/docs/theme.config.tsx @@ -13,52 +13,132 @@ const config: DocsThemeConfig = { const image = url + "/images/Nym_meta_Image.png"; const favicon = url + "/favicon.svg"; - // Define descriptions for different "books" - const bookDescriptions: Record = { - "/developers": - "Nym's developer documentation covering core concepts of integrating with the Mixnet, interacting with the Nyx blockchain, an overview of the avaliable tools, and our SDK docs.", - "/network": - "Nym's network documentation covering network architecture, node types, tokenomics, and cryptography.", - "/operators": - "Nym's Operators guide containing information and setup guides for the various components of Nym network and Nyx blockchain validators.", - "/apis": - "Interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet.", - }; - const defaultDescription = "Nym is a privacy platform. It provides strong network-level privacy against sophisticated end-to-end attackers, and anonymous access control using blinded, re-randomizable, decentralized credentials."; - const topLevel = "/" + route.split("/")[1]; + // Frontmatter-first description (no more shared book descriptions) const description = - config.frontMatter.description || - bookDescriptions[topLevel] || - defaultDescription; + config.frontMatter.description || defaultDescription; - const title = (route === "/" ? "Nym docs" : config.title + " - Nym docs"); + // Better title format + const baseTitle = config.frontMatter.title || config.title || ""; + const title = route === "/" + ? "Nym Docs: Privacy Network Documentation" + : baseTitle.includes("| Nym Docs") + ? baseTitle + : `${baseTitle} | Nym Docs`; + + const pageUrl = `${url}${route}`; + + // Frontmatter fields + const section = config.frontMatter.section || ""; + const lastUpdated = config.frontMatter.lastUpdated || ""; + const schemaType = config.frontMatter.schemaType || "TechArticle"; + + // JSON-LD structured data + const org = { + "@id": "https://nym.com/#org", + "@type": "Organization", + name: "Nym Technologies SA", + url: "https://nym.com", + logo: { + "@id": "https://nym.com/#logo", + "@type": "ImageObject", + url: "https://nym.com/apple-touch-icon.png", + }, + sameAs: ["https://x.com/nymproject", "https://github.com/nymtech"], + }; + + const website = { + "@id": "https://nym.com/docs#website", + "@type": "WebSite", + name: "Nym Docs", + url: "https://nym.com/docs", + publisher: { "@id": "https://nym.com/#org" }, + }; + + const webpage = { + "@id": `${pageUrl}#webpage`, + "@type": "WebPage", + url: pageUrl, + name: title, + description: description, + inLanguage: "en", + isPartOf: { "@id": "https://nym.com/docs#website" }, + breadcrumb: { "@id": `${pageUrl}#breadcrumb` }, + potentialAction: { "@type": "ReadAction", target: pageUrl }, + }; + + const articleSchema: Record = { + "@id": `${pageUrl}#article`, + "@type": schemaType, + ...(schemaType === "HowTo" + ? { name: baseTitle } + : { headline: baseTitle }), + description: description, + url: pageUrl, + author: { "@id": "https://nym.com/#org" }, + publisher: { "@id": "https://nym.com/#org" }, + mainEntityOfPage: { "@id": `${pageUrl}#webpage` }, + ...(lastUpdated && { + datePublished: lastUpdated, + dateModified: lastUpdated, + }), + }; + + const pathParts = route.split("/").filter(Boolean); + const breadcrumb = { + "@id": `${pageUrl}#breadcrumb`, + "@type": "BreadcrumbList", + itemListElement: pathParts.map((part: string, i: number) => ({ + "@type": "ListItem", + position: i + 1, + name: + config.frontMatter.breadcrumbLabel && i === pathParts.length - 1 + ? config.frontMatter.breadcrumbLabel + : part.charAt(0).toUpperCase() + part.slice(1).replace(/-/g, " "), + item: `${url}/${pathParts.slice(0, i + 1).join("/")}`, + })), + }; + + const schema = { + "@context": "https://schema.org", + "@graph": [org, website, webpage, articleSchema, breadcrumb], + }; return ( <> {title} - + - - + + - - - - - + + + + + {section && ( + + )} + {lastUpdated && ( + + )} + + - + - - + + - - - - -

everything happens in the console

- - - - \ No newline at end of file diff --git a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/index.js b/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/index.js deleted file mode 100644 index 3921411115..0000000000 --- a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/index.js +++ /dev/null @@ -1,49 +0,0 @@ -import init, { NymIssuanceTicketbook } from "@nymproject/nym-vpn-api-lib-wasm"; - -async function main() { - await init(); - - let cryptoData = new NymIssuanceTicketbook({}); - - console.log("getting partial vks"); - const partialVksRes = await fetch("http://localhost:8080/api/v1/ticketbook/partial-verification-keys", { - headers: new Headers({ "Authorization": "Bearer foomp" }) - }); - const partialVks = await partialVksRes.json(); - console.debug(partialVks); - - console.log("getting master vk"); - const masterVkRes = await fetch("http://localhost:8080/api/v1/ticketbook/master-verification-key", { - headers: new Headers({ "Authorization": "Bearer foomp" }) - }); - const masterVk = await masterVkRes.json(); - console.debug(masterVk); - - let request = cryptoData.buildRequestPayload(false); - console.log(request); - - - console.log("getting blinded wallet shares"); - const sharesRes = await fetch("http://localhost:8080/api/v1/ticketbook/obtain?include-coin-index-signatures=true&include-expiration-date-signatures=true", { - method: "POST", - headers: new Headers( - { - "Authorization": "Bearer foomp", - "Content-Type": "application/json" - } - ), - body: request - }); - - const credentialShares = await sharesRes.json(); - console.log(credentialShares); - - console.log("unblinding shares"); - const unblinded = cryptoData.unblindWalletShares(credentialShares, partialVks, masterVk); - - const serialised = unblinded.serialise(); - console.log("serialised:\n", serialised); -} - - -main(); diff --git a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/package.json b/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/package.json deleted file mode 100644 index 2eb37c3aa2..0000000000 --- a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "create-wasm-app", - "version": "0.1.0", - "description": "create an app to consume rust-generated wasm packages", - "main": "index.js", - "bin": { - "create-wasm-app": ".bin/create-wasm-app.js" - }, - "scripts": { - "build": "webpack --config webpack.config.js", - "build:wasm": "cd ../ && make wasm-build", - "start": "webpack-dev-server --port 8001" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/rustwasm/create-wasm-app.git" - }, - "keywords": [ - "webassembly", - "wasm", - "rust", - "webpack" - ], - "author": "Dave Hrycyszyn ", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/nymtech/nym/issues" - }, - "homepage": "https://nymtech.net/docs", - "devDependencies": { - "copy-webpack-plugin": "^11.0.0", - "hello-wasm-pack": "^0.1.0", - "webpack": "^5.70.0", - "webpack-cli": "^4.9.2", - "webpack-dev-server": "^5.2.1" - }, - "dependencies": { - "@nymproject/nym-vpn-api-lib-wasm": "file:../../../dist/wasm/nym-vpn-api-lib-wasm" - } -} diff --git a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/webpack.config.js b/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/webpack.config.js deleted file mode 100644 index d5f839485f..0000000000 --- a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/webpack.config.js +++ /dev/null @@ -1,33 +0,0 @@ -const CopyWebpackPlugin = require("copy-webpack-plugin"); -const path = require("path"); - -module.exports = { - performance: { - hints: false, - maxEntrypointSize: 512000, - maxAssetSize: 512000 - }, - entry: { - bootstrap: "./bootstrap.js" - // worker: "./worker.js" - }, - output: { - path: path.resolve(__dirname, "dist"), - filename: "[name].js" - }, - mode: "development", - // mode: 'production', - plugins: [ - new CopyWebpackPlugin({ - patterns: [ - "index.html", - { - from: "../../../dist/wasm/nym-vpn-api-lib-wasm/*.(js|wasm)", - to: "[name][ext]" - } - ] - }) - - ], - experiments: { syncWebAssembly: true } -}; diff --git a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/yarn.lock b/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/yarn.lock deleted file mode 100644 index 01cda5871e..0000000000 --- a/nym-credential-proxy/vpn-api-lib-wasm/internal-dev/yarn.lock +++ /dev/null @@ -1,2204 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@discoveryjs/json-ext@^0.5.0": - version "0.5.7" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" - integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" - integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/source-map@^0.3.3": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" - integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@jsonjoy.com/base64@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" - integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== - -"@jsonjoy.com/json-pack@^1.0.3": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz#e658900e81d194903171c42546e1aa27f446846a" - integrity sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA== - dependencies: - "@jsonjoy.com/base64" "^1.1.1" - "@jsonjoy.com/util" "^1.1.2" - hyperdyperid "^1.2.0" - thingies "^1.20.0" - -"@jsonjoy.com/util@^1.1.2", "@jsonjoy.com/util@^1.3.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.6.0.tgz#23991b2fe12cb3a006573d9dc97c768d3ed2c9f1" - integrity sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A== - -"@leichtgewicht/ip-codec@^2.0.1": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" - integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@nymproject/nym-vpn-api-lib-wasm@file:../../../dist/wasm/nym-vpn-api-lib-wasm": - version "0.1.0" - -"@types/body-parser@*": - version "1.19.5" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" - integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/bonjour@^3.5.13": - version "3.5.13" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" - integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== - dependencies: - "@types/node" "*" - -"@types/connect-history-api-fallback@^1.5.4": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" - integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== - dependencies: - "@types/express-serve-static-core" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.38" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" - integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== - dependencies: - "@types/node" "*" - -"@types/eslint-scope@^3.7.7": - version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" - integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "9.6.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" - integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*", "@types/estree@^1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" - integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== - -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.33": - version "4.19.6" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" - integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express@*", "@types/express@^4.17.21": - version "4.17.22" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.22.tgz#14cfcf120f7eb56ebb8ca77b7fa9a14d21de7c96" - integrity sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/http-errors@*": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" - integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== - -"@types/http-proxy@^1.17.8": - version "1.17.15" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36" - integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ== - dependencies: - "@types/node" "*" - -"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/mime@^1": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" - integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== - -"@types/node-forge@^1.3.0": - version "1.3.11" - resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" - integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== - dependencies: - "@types/node" "*" - -"@types/node@*": - version "22.5.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.0.tgz#10f01fe9465166b4cab72e75f60d8b99d019f958" - integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg== - dependencies: - undici-types "~6.19.2" - -"@types/qs@*": - version "6.9.15" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" - integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== - -"@types/range-parser@*": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" - integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== - -"@types/retry@0.12.2": - version "0.12.2" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" - integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== - -"@types/send@*": - version "0.17.4" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" - integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/serve-index@^1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" - integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== - dependencies: - "@types/express" "*" - -"@types/serve-static@*", "@types/serve-static@^1.15.5": - version "1.15.7" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" - integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== - dependencies: - "@types/http-errors" "*" - "@types/node" "*" - "@types/send" "*" - -"@types/sockjs@^0.3.36": - version "0.3.36" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" - integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== - dependencies: - "@types/node" "*" - -"@types/ws@^8.5.10": - version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" - integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== - dependencies: - "@types/node" "*" - -"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" - integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== - dependencies: - "@webassemblyjs/helper-numbers" "1.13.2" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - -"@webassemblyjs/floating-point-hex-parser@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" - integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== - -"@webassemblyjs/helper-api-error@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" - integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== - -"@webassemblyjs/helper-buffer@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" - integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== - -"@webassemblyjs/helper-numbers@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" - integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.13.2" - "@webassemblyjs/helper-api-error" "1.13.2" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/helper-wasm-bytecode@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" - integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== - -"@webassemblyjs/helper-wasm-section@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" - integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-buffer" "1.14.1" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/wasm-gen" "1.14.1" - -"@webassemblyjs/ieee754@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" - integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" - integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" - integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== - -"@webassemblyjs/wasm-edit@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" - integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-buffer" "1.14.1" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/helper-wasm-section" "1.14.1" - "@webassemblyjs/wasm-gen" "1.14.1" - "@webassemblyjs/wasm-opt" "1.14.1" - "@webassemblyjs/wasm-parser" "1.14.1" - "@webassemblyjs/wast-printer" "1.14.1" - -"@webassemblyjs/wasm-gen@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" - integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/ieee754" "1.13.2" - "@webassemblyjs/leb128" "1.13.2" - "@webassemblyjs/utf8" "1.13.2" - -"@webassemblyjs/wasm-opt@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" - integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-buffer" "1.14.1" - "@webassemblyjs/wasm-gen" "1.14.1" - "@webassemblyjs/wasm-parser" "1.14.1" - -"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" - integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-api-error" "1.13.2" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/ieee754" "1.13.2" - "@webassemblyjs/leb128" "1.13.2" - "@webassemblyjs/utf8" "1.13.2" - -"@webassemblyjs/wast-printer@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" - integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@xtuc/long" "4.2.2" - -"@webpack-cli/configtest@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" - integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== - -"@webpack-cli/info@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" - integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== - dependencies: - envinfo "^7.7.3" - -"@webpack-cli/serve@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" - integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-import-phases@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" - integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== - -acorn@^8.15.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== - -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv-keywords@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== - dependencies: - fast-deep-equal "^3.1.3" - -ajv@^8.0.0, ajv@^8.9.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - -ansi-html-community@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== - -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -baseline-browser-mapping@^2.9.0: - version "2.9.19" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" - integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== - -binary-extensions@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" - integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== - -body-parser@1.20.3: - version "1.20.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - -bonjour-service@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.3.0.tgz#80d867430b5a0da64e82a8047fc1e355bdb71722" - integrity sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA== - dependencies: - fast-deep-equal "^3.1.3" - multicast-dns "^7.2.5" - -braces@^3.0.3, braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -browserslist@^4.28.1: - version "4.28.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" - integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== - dependencies: - baseline-browser-mapping "^2.9.0" - caniuse-lite "^1.0.30001759" - electron-to-chromium "^1.5.263" - node-releases "^2.0.27" - update-browserslist-db "^1.2.0" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -bundle-name@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" - integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== - dependencies: - run-applescript "^7.0.0" - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bound@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" - -caniuse-lite@^1.0.30001759: - version "1.0.30001769" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz#1ad91594fad7dc233777c2781879ab5409f7d9c2" - integrity sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg== - -chokidar@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chrome-trace-event@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" - integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -colorette@^2.0.10, colorette@^2.0.14: - version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -connect-history-api-fallback@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" - integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4, content-type@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== - -copy-webpack-plugin@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" - integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== - dependencies: - fast-glob "^3.2.11" - glob-parent "^6.0.1" - globby "^13.1.1" - normalize-path "^3.0.0" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0: - version "4.3.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" - integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== - dependencies: - ms "2.1.2" - -default-browser-id@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" - integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== - -default-browser@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" - integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== - dependencies: - bundle-name "^4.1.0" - default-browser-id "^5.0.0" - -define-lazy-prop@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" - integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== - -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -dns-packet@^5.2.2: - version "5.6.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" - integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== - dependencies: - "@leichtgewicht/ip-codec" "^2.0.1" - -dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -electron-to-chromium@^1.5.263: - version "1.5.286" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz#142be1ab5e1cd5044954db0e5898f60a4960384e" - integrity sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - -enhanced-resolve@^5.17.4: - version "5.19.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz#6687446a15e969eaa63c2fa2694510e17ae6d97c" - integrity sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.3.0" - -envinfo@^7.7.3: - version "7.13.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" - integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== - -es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-module-lexer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.0.0.tgz#f657cd7a9448dcdda9c070a3cb75e5dc1e85f5b1" - integrity sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== - dependencies: - es-errors "^1.3.0" - -escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -express@^4.21.2: - version "4.21.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.3" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.7.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~2.0.0" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.3.1" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.3" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.12" - proxy-addr "~2.0.7" - qs "6.13.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.2.11, fast-glob@^3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" - integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-uri@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134" - integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== - -fastest-levenshtein@^1.0.12: - version "1.0.16" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" - integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== - -fastq@^1.6.0: - version "1.17.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" - integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== - dependencies: - reusify "^1.0.4" - -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== - dependencies: - debug "2.6.9" - encodeurl "~2.0.0" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -follow-redirects@^1.0.0: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" - -get-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== - -globby@^13.1.1: - version "13.2.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" - integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== - dependencies: - dir-glob "^3.0.1" - fast-glob "^3.3.0" - ignore "^5.2.4" - merge2 "^1.4.1" - slash "^4.0.0" - -gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -hello-wasm-pack@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/hello-wasm-pack/-/hello-wasm-pack-0.1.0.tgz#482a2e3371828056ac35f5b5fec76c0b99dcd530" - integrity sha512-3hx0GDkDLf/a9ThCMV2qG4mwza8N/MCtm8aeFFc/cdBCL2zMJ1kW1wjNl7xPqD1lz8Yl5+uhnc/cpui4dLwz/w== - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - -http-proxy-middleware@^2.0.7: - version "2.0.9" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef" - integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q== - dependencies: - "@types/http-proxy" "^1.17.8" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -hyperdyperid@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" - integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore@^5.2.4: - version "5.3.2" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" - integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== - -import-local@^3.0.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" - integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - -inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -interpret@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-core-module@^2.13.0: - version "2.15.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" - integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== - dependencies: - hasown "^2.0.2" - -is-docker@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" - integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-inside-container@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" - integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== - dependencies: - is-docker "^3.0.0" - -is-network-error@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" - integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-wsl@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" - integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== - dependencies: - is-inside-container "^1.0.0" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -jest-worker@^27.4.5: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -json-parse-even-better-errors@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -launch-editor@^2.6.1: - version "2.10.0" - resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.10.0.tgz#5ca3edfcb9667df1e8721310f3a40f1127d4bc42" - integrity sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA== - dependencies: - picocolors "^1.0.0" - shell-quote "^1.8.1" - -loader-runner@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.1.tgz#6c76ed29b0ccce9af379208299f07f876de737e3" - integrity sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q== - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -memfs@^4.6.0: - version "4.17.2" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.17.2.tgz#1f71a6d85c8c53b4f1b388234ed981a690c7e227" - integrity sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg== - dependencies: - "@jsonjoy.com/json-pack" "^1.0.3" - "@jsonjoy.com/util" "^1.3.0" - tree-dump "^1.0.1" - tslib "^2.0.0" - -merge-descriptors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" - integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" - integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -"mime-db@>= 1.43.0 < 2": - version "1.53.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" - integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== - -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multicast-dns@^7.2.5: - version "7.2.5" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" - integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== - dependencies: - dns-packet "^5.2.2" - thunky "^1.0.2" - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -node-forge@^1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.2.tgz#d0d2659a26eef778bf84d73e7f55c08144ee7750" - integrity sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw== - -node-releases@^2.0.27: - version "2.0.27" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" - integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -object-inspect@^1.13.3: - version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== - -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@2.4.1, on-finished@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -open@^10.0.3: - version "10.1.2" - resolved "https://registry.yarnpkg.com/open/-/open-10.1.2.tgz#d5df40984755c9a9c3c93df8156a12467e882925" - integrity sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw== - dependencies: - default-browser "^5.2.1" - define-lazy-prop "^3.0.0" - is-inside-container "^1.0.0" - is-wsl "^3.1.0" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-retry@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.1.tgz#81828f8dc61c6ef5a800585491572cc9892703af" - integrity sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ== - dependencies: - "@types/retry" "0.12.2" - is-network-error "^1.0.0" - retry "^0.13.1" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" - integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -picocolors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -readable-stream@^2.0.1: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.0.6: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" - integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== - dependencies: - resolve "^1.9.0" - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve@^1.9.0: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -run-applescript@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" - integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0, schema-utils@^4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" - integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== - dependencies: - "@types/json-schema" "^7.0.9" - ajv "^8.9.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.1.0" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== - -selfsigned@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" - integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== - dependencies: - "@types/node-forge" "^1.3.0" - node-forge "^1" - -send@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -serialize-javascript@^6.0.0, serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.16.2: - version "1.16.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== - dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.19.0" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shell-quote@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - -side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - side-channel-map "^1.0.1" - -side-channel@^1.0.6: - version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - side-channel-list "^1.0.0" - side-channel-map "^1.0.1" - side-channel-weakmap "^1.0.2" - -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== - -sockjs@^0.3.24: - version "0.3.24" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== - dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" - -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -tapable@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" - integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== - -terser-webpack-plugin@^5.3.16: - version "5.3.16" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz#741e448cc3f93d8026ebe4f7ef9e4afacfd56330" - integrity sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q== - dependencies: - "@jridgewell/trace-mapping" "^0.3.25" - jest-worker "^27.4.5" - schema-utils "^4.3.0" - serialize-javascript "^6.0.2" - terser "^5.31.1" - -terser@^5.31.1: - version "5.46.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.0.tgz#1b81e560d584bbdd74a8ede87b4d9477b0ff9695" - integrity sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg== - dependencies: - "@jridgewell/source-map" "^0.3.3" - acorn "^8.15.0" - commander "^2.20.0" - source-map-support "~0.5.20" - -thingies@^1.20.0: - version "1.21.0" - resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" - integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== - -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -tree-dump@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.0.3.tgz#2f0e42e77354714418ed7ab44291e435ccdb0f80" - integrity sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg== - -tslib@^2.0.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -undici-types@~6.19.2: - version "6.19.8" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" - integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" - integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -watchpack@^2.4.4: - version "2.5.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102" - integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg== - dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -webpack-cli@^4.9.2: - version "4.10.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" - integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== - dependencies: - "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.2.0" - "@webpack-cli/info" "^1.5.0" - "@webpack-cli/serve" "^1.7.0" - colorette "^2.0.14" - commander "^7.0.0" - cross-spawn "^7.0.3" - fastest-levenshtein "^1.0.12" - import-local "^3.0.2" - interpret "^2.2.0" - rechoir "^0.7.0" - webpack-merge "^5.7.3" - -webpack-dev-middleware@^7.4.2: - version "7.4.2" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz#40e265a3d3d26795585cff8207630d3a8ff05877" - integrity sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA== - dependencies: - colorette "^2.0.10" - memfs "^4.6.0" - mime-types "^2.1.31" - on-finished "^2.4.1" - range-parser "^1.2.1" - schema-utils "^4.0.0" - -webpack-dev-server@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz#049072d6e19cbda8cf600b9e364e6662d61218ba" - integrity sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ== - dependencies: - "@types/bonjour" "^3.5.13" - "@types/connect-history-api-fallback" "^1.5.4" - "@types/express" "^4.17.21" - "@types/express-serve-static-core" "^4.17.21" - "@types/serve-index" "^1.9.4" - "@types/serve-static" "^1.15.5" - "@types/sockjs" "^0.3.36" - "@types/ws" "^8.5.10" - ansi-html-community "^0.0.8" - bonjour-service "^1.2.1" - chokidar "^3.6.0" - colorette "^2.0.10" - compression "^1.7.4" - connect-history-api-fallback "^2.0.0" - express "^4.21.2" - graceful-fs "^4.2.6" - http-proxy-middleware "^2.0.7" - ipaddr.js "^2.1.0" - launch-editor "^2.6.1" - open "^10.0.3" - p-retry "^6.2.0" - schema-utils "^4.2.0" - selfsigned "^2.4.1" - serve-index "^1.9.1" - sockjs "^0.3.24" - spdy "^4.0.2" - webpack-dev-middleware "^7.4.2" - ws "^8.18.0" - -webpack-merge@^5.7.3: - version "5.10.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" - integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== - dependencies: - clone-deep "^4.0.1" - flat "^5.0.2" - wildcard "^2.0.0" - -webpack-sources@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723" - integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== - -webpack@^5.70.0: - version "5.104.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.104.1.tgz#94bd41eb5dbf06e93be165ba8be41b8260d4fb1a" - integrity sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA== - dependencies: - "@types/eslint-scope" "^3.7.7" - "@types/estree" "^1.0.8" - "@types/json-schema" "^7.0.15" - "@webassemblyjs/ast" "^1.14.1" - "@webassemblyjs/wasm-edit" "^1.14.1" - "@webassemblyjs/wasm-parser" "^1.14.1" - acorn "^8.15.0" - acorn-import-phases "^1.0.3" - browserslist "^4.28.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.4" - es-module-lexer "^2.0.0" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.11" - json-parse-even-better-errors "^2.3.1" - loader-runner "^4.3.1" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^4.3.3" - tapable "^2.3.0" - terser-webpack-plugin "^5.3.16" - watchpack "^2.4.4" - webpack-sources "^3.3.3" - -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wildcard@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" - integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== - -ws@^8.18.0: - version "8.18.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" - integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== diff --git a/nym-credential-proxy/vpn-api-lib-wasm/src/error.rs b/nym-credential-proxy/vpn-api-lib-wasm/src/error.rs deleted file mode 100644 index 5e1255bdbc..0000000000 --- a/nym-credential-proxy/vpn-api-lib-wasm/src/error.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2024 Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use nym_wasm_utils::wasm_error; -use serde_wasm_bindgen::Error; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum VpnApiLibError { - #[error("{0}")] - Json(String), - - #[error("[ecash] cryptographic failure: {source}")] - EcashFailure { - #[from] - source: nym_compact_ecash::CompactEcashError, - }, - - #[error("provided invalid ticket type")] - MalformedTicketType, - - #[error("the provided shares and issuers are not from the same epoch! {shares} and {issuers}")] - InconsistentEpochId { shares: u64, issuers: u64 }, - - #[error("failed to recover ed25519 private key from its base58 representation")] - MalformedEd25519Key, -} - -wasm_error!(VpnApiLibError); - -impl From for VpnApiLibError { - fn from(value: Error) -> Self { - VpnApiLibError::Json(value.to_string()) - } -} diff --git a/nym-credential-proxy/vpn-api-lib-wasm/src/lib.rs b/nym-credential-proxy/vpn-api-lib-wasm/src/lib.rs deleted file mode 100644 index 00ca53805a..0000000000 --- a/nym-credential-proxy/vpn-api-lib-wasm/src/lib.rs +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2024 Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::error::VpnApiLibError; -use nym_compact_ecash::scheme::keygen::KeyPairUser; -use nym_compact_ecash::scheme::withdrawal::RequestInfo; -use nym_compact_ecash::{ - Base58, BlindedSignature, VerificationKeyAuth, WithdrawalRequest, aggregate_wallets, - issue_verify, withdrawal_request, -}; -use nym_credential_proxy_requests::api::v1::ticketbook::models::{ - MasterVerificationKeyResponse, PartialVerificationKeysResponse, TicketbookRequest, - TicketbookWalletSharesResponse, WalletShare, -}; -use nym_credentials::{ - AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, EpochVerificationKey, - IssuedTicketBook, -}; -use nym_credentials_interface::TicketType; -use nym_crypto::asymmetric::ed25519; -use nym_ecash_time::{EcashTime, ecash_default_expiration_date}; -use nym_wasm_utils::console_error; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use time::Date; -use tsify::Tsify; -use wasm_bindgen::prelude::*; -use zeroize::Zeroizing; - -pub mod error; - -#[derive(Tsify, Debug, Default, Clone, Serialize, Deserialize)] -#[tsify(into_wasm_abi, from_wasm_abi)] -#[serde(rename_all = "camelCase")] -pub struct WalletShares(Vec); - -pub type WalletIssuers = PartialVerificationKeysResponse; - -impl From> for WalletShares { - fn from(shares: Vec) -> Self { - WalletShares(shares) - } -} - -#[derive(Tsify, Debug, Default, Clone, Serialize, Deserialize)] -#[tsify(into_wasm_abi, from_wasm_abi)] -#[serde(rename_all = "camelCase")] -pub struct NymIssuanceTicketbookOpts { - #[tsify(optional)] - pub ticketbook_type: Option, - - // bs58-encoded user secret key used for seeding the ecash crypto keypair generation - // I reiterate, this is a **SECRET** key, not a public key. - #[tsify(optional)] - pub user_secret_key: Option, -} - -#[wasm_bindgen] -#[derive(Debug)] -#[allow(dead_code)] -pub struct NymIssuanceTicketbook { - /// ecash keypair related to the credential - ecash_keypair: KeyPairUser, - - withdrawal_request: WithdrawalRequest, - - ticketbook_type: TicketType, - - expiration_date: Date, - - request_info: Zeroizing, -} - -#[wasm_bindgen] -impl NymIssuanceTicketbook { - #[wasm_bindgen(constructor)] - pub fn new(opts: NymIssuanceTicketbookOpts) -> Result { - let ecash_keypair = match opts.user_secret_key { - None => KeyPairUser::new(), - Some(maybe_sk) => { - let pk = ed25519::PrivateKey::from_base58_string(maybe_sk) - .map(Zeroizing::new) - .map_err(|_| VpnApiLibError::MalformedEd25519Key)?; - let bytes = Zeroizing::new(pk.to_bytes()); - KeyPairUser::new_seeded(&bytes) - } - }; - - let ticketbook_type = match opts.ticketbook_type { - None => TicketType::V1MixnetEntry, - Some(typ) => typ - .parse() - .map_err(|_| VpnApiLibError::MalformedTicketType)?, - }; - - let expiration_date = ecash_default_expiration_date(); - - let (withdrawal_request, request_info) = withdrawal_request( - ecash_keypair.secret_key(), - expiration_date.ecash_unix_timestamp(), - ticketbook_type.encode(), - )?; - - Ok(NymIssuanceTicketbook { - ecash_keypair, - withdrawal_request, - ticketbook_type, - expiration_date, - request_info: Zeroizing::new(request_info), - }) - } - - #[wasm_bindgen(js_name = "buildRequestPayload")] - pub fn build_request_payload(&self, is_freepass_request: bool) -> String { - serde_json::to_string(&TicketbookRequest { - withdrawal_request: self.withdrawal_request.clone().into(), - ecash_pubkey: self.ecash_keypair.public_key(), - expiration_date: self.expiration_date, - ticketbook_type: self.ticketbook_type, - is_freepass_request, - }) - .unwrap() - } - - #[wasm_bindgen(js_name = "getWithdrawalRequest")] - pub fn get_encoded_withdrawal_request(&self) -> String { - self.withdrawal_request.to_bs58() - } - - #[wasm_bindgen(js_name = "getEncodedPublicKey")] - pub fn get_encoded_public_key(&self) -> String { - self.ecash_keypair.public_key().to_bs58() - } - - // - // #[wasm_bindgen(js_name = "unblindShare")] - // pub fn unblind_share(&self, share: UnblindableShare) -> Result { - // let blinded_sig = BlindedSignature::try_from_bs58(share.blinded_share_bs58)?; - // let vk = VerificationKey::try_from_bs58(share.issuer_key_bs58)?; - // - // Ok(blinded_sig - // .unblind(&vk, &self.pedersen_commitments_openings) - // .into()) - // } - // - #[wasm_bindgen(js_name = "unblindWalletShares")] - pub fn unblind_wallet_shares( - self, - shares: JsValue, - issuers: WalletIssuers, - master_key: MasterVerificationKeyResponse, - ) -> Result { - // we couldn't derive all the required abi traits due to crypto types deep in the stack - let shares: TicketbookWalletSharesResponse = serde_wasm_bindgen::from_value(shares)?; - - if shares.epoch_id != issuers.epoch_id { - console_error!( - "the provided shares and issuers are not from the same epoch! {} and {}", - shares.epoch_id, - issuers.epoch_id - ); - return Err(VpnApiLibError::InconsistentEpochId { - shares: shares.epoch_id, - issuers: issuers.epoch_id, - }); - } - - let master_vk = VerificationKeyAuth::try_from_bs58(master_key.bs58_encoded_key)?; - - let mut decoded_keys = HashMap::new(); - for key in issuers.keys { - let vk = VerificationKeyAuth::try_from_bs58(key.bs58_encoded_key)?; - decoded_keys.insert(key.node_index, vk); - } - - let mut partial_wallets = Vec::new(); - for share in shares.shares { - let blinded_sig = BlindedSignature::try_from_bs58(share.bs58_encoded_share)?; - let Some(vk) = decoded_keys.get(&share.node_index) else { - console_error!( - "received a share from issuer {} but did not receive a corresponding verification key!", - share.node_index - ); - continue; - }; - - match issue_verify( - vk, - self.ecash_keypair.secret_key(), - &blinded_sig, - &self.request_info, - share.node_index, - ) { - Ok(partial_wallet) => partial_wallets.push(partial_wallet), - Err(err) => { - console_error!( - "failed to unblind partial wallet corresponding to index {}: {err}", - share.node_index - ) - } - } - } - - let aggregated_wallet = aggregate_wallets( - &master_vk, - self.ecash_keypair.secret_key(), - &partial_wallets, - &self.request_info, - )?; - - Ok(NymIssuedTicketbook { - inner_ticketbook: IssuedTicketBook::new( - aggregated_wallet.into_wallet_signatures(), - shares.epoch_id, - self.ecash_keypair.into(), - self.ticketbook_type, - self.expiration_date, - ), - master_vk: EpochVerificationKey { - epoch_id: shares.epoch_id, - key: master_vk, - }, - expiration_date_signatures: shares - .aggregated_expiration_date_signatures - .map(|s| s.signatures), - coin_index_signatures: shares - .aggregated_coin_index_signatures - .map(|s| s.signatures), - }) - } -} - -#[wasm_bindgen] -pub struct NymIssuedTicketbook { - inner_ticketbook: IssuedTicketBook, - - master_vk: EpochVerificationKey, - expiration_date_signatures: Option, - coin_index_signatures: Option, -} - -#[wasm_bindgen] -impl NymIssuedTicketbook { - pub fn serialise(self) -> FullSerialisedNymIssuedTicketbook { - let serialised = self - .inner_ticketbook - .begin_export() - .with_master_verification_key(&self.master_vk) - .with_maybe_expiration_date_signatures(&self.expiration_date_signatures) - .with_maybe_coin_index_signatures(&self.coin_index_signatures) - .finalize_export(); - - FullSerialisedNymIssuedTicketbook { - serialisation_revision: serialised.revision, - bs58_encoded_data: bs58::encode(serialised.data).into_string(), - } - } -} - -#[derive(Tsify, Serialize, Deserialize, Debug, PartialEq, Eq)] -#[tsify(into_wasm_abi, from_wasm_abi)] -#[serde(rename_all = "camelCase")] -pub struct FullSerialisedNymIssuedTicketbook { - pub serialisation_revision: u8, - pub bs58_encoded_data: String, -} - -#[wasm_bindgen(start)] -pub fn main() { - nym_wasm_utils::console_log!("[rust main]: rust module loaded"); - nym_wasm_utils::console_log!( - "vpn-api-lib version used:\n{}", - nym_bin_common::bin_info!().pretty_print() - ); - nym_wasm_utils::console_log!("[rust main]: setting panic hook"); - nym_wasm_utils::set_panic_hook(); -} diff --git a/nym-gateway-probe/Cargo.toml b/nym-gateway-probe/Cargo.toml index bf8c621e59..b18927be15 100644 --- a/nym-gateway-probe/Cargo.toml +++ b/nym-gateway-probe/Cargo.toml @@ -22,6 +22,7 @@ hex.workspace = true tracing.workspace = true pnet_packet.workspace = true rand.workspace = true +rand09.workspace = true reqwest = { workspace = true, features = ["socks"] } serde.workspace = true serde_json.workspace = true diff --git a/nym-gateway-probe/src/common/helpers.rs b/nym-gateway-probe/src/common/helpers.rs index 5693b8a7a9..7f90fca60a 100644 --- a/nym-gateway-probe/src/common/helpers.rs +++ b/nym-gateway-probe/src/common/helpers.rs @@ -1,12 +1,9 @@ // Copyright 2026 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::common::nodes::TestedNodeLpDetails; -use nym_crypto::asymmetric::ed25519; use nym_ip_packet_requests::v8::response::{ ControlResponse, DataResponse, InfoLevel, IpPacketResponse, IpPacketResponseData, }; -use nym_lp::peer::LpRemotePeer; use nym_sdk::{ DebugConfig, NymApiTopologyProvider, NymApiTopologyProviderConfig, NymNetworkDetails, TopologyProvider, mixnet::ReconstructedMessage, @@ -15,13 +12,6 @@ use nym_topology::NymTopology; use tracing::*; use url::Url; -pub fn to_lp_remote_peer(identity: ed25519::PublicKey, data: TestedNodeLpDetails) -> LpRemotePeer { - LpRemotePeer::new(identity, data.x25519).with_key_digests( - data.expected_kem_key_hashes, - data.expected_signing_key_hashes, - ) -} - pub fn mixnet_debug_config( min_gateway_performance: Option, ignore_egress_epoch_role: bool, diff --git a/nym-gateway-probe/src/common/nodes.rs b/nym-gateway-probe/src/common/nodes.rs index 4e79e84ce6..04b2b10cac 100644 --- a/nym-gateway-probe/src/common/nodes.rs +++ b/nym-gateway-probe/src/common/nodes.rs @@ -3,25 +3,25 @@ use anyhow::{Context, anyhow, bail}; use nym_api_requests::models::{ - AuthenticatorDetailsV1, DeclaredRolesV1, DescribedNodeTypeV1, HostInformationV1, - IpPacketRouterDetailsV1, NetworkRequesterDetailsV1, NymNodeDataV1, - OffsetDateTimeJsonSchemaWrapper, WebSocketsV1, WireguardDetailsV1, + AuthenticatorDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2, HostInformationV2, + IpPacketRouterDetailsV2, NetworkRequesterDetailsV2, NymNodeDataV2, + OffsetDateTimeJsonSchemaWrapper, WebSocketsV2, WireguardDetailsV2, }; use nym_authenticator_requests::AuthenticatorVersion; use nym_bin_common::build_information::BinaryBuildInformationOwned; -use nym_crypto::asymmetric::x25519; use nym_http_api_client::UserAgent; -use nym_kkt_ciphersuite::SignatureScheme; +use nym_kkt_ciphersuite::Ciphersuite; use nym_kkt_ciphersuite::{KEM, KEMKeyDigests}; +use nym_lp::peer::{DHPublicKey, LpRemotePeer}; use nym_network_defaults::DEFAULT_NYM_NODE_HTTP_PORT; use nym_node_requests::api::client::NymNodeApiClientExt; use nym_node_requests::api::v1::node::models::AuxiliaryDetails as NodeAuxiliaryDetails; use nym_sdk::mixnet::NodeIdentity; use nym_sdk::mixnet::Recipient; use nym_validator_client::client::NymApiClientExt; -use nym_validator_client::models::NymNodeDescriptionV1; +use nym_validator_client::models::NymNodeDescriptionV2; use rand::prelude::IteratorRandom; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::net::{IpAddr, SocketAddr}; use std::time::Duration; use time::OffsetDateTime; @@ -90,7 +90,7 @@ use url::Url; #[derive(Clone)] pub struct DirectoryNode { - described: NymNodeDescriptionV1, + described: NymNodeDescriptionV2, } impl DirectoryNode { @@ -237,11 +237,11 @@ pub async fn query_gateway_by_ip(address: String) -> anyhow::Result anyhow::Result = - nr_result.map(|nr| NetworkRequesterDetailsV1 { + let network_requester: Option = + nr_result.map(|nr| NetworkRequesterDetailsV2 { address: nr.address, uses_exit_policy: false, // Field not availabe, to change if it becomes useful here }); - let ip_packet_router: Option = - ipr_result.map(|ipr| IpPacketRouterDetailsV1 { + let ip_packet_router: Option = + ipr_result.map(|ipr| IpPacketRouterDetailsV2 { address: ipr.address, }); - let authenticator: Option = - authenticator_result.map(|auth| AuthenticatorDetailsV1 { + let authenticator: Option = + authenticator_result.map(|auth| AuthenticatorDetailsV2 { address: auth.address, }); #[allow(deprecated)] - let wireguard: Option = - wireguard_result.map(|wg| WireguardDetailsV1 { + let wireguard: Option = + wireguard_result.map(|wg| WireguardDetailsV2 { port: wg.tunnel_port, // Use tunnel_port for deprecated port field tunnel_port: wg.tunnel_port, metadata_port: wg.metadata_port, @@ -284,14 +284,14 @@ pub async fn query_gateway_by_ip(address: String) -> anyhow::Result anyhow::Result, - pub expected_signing_key_hashes: HashMap, - pub x25519: x25519::PublicKey, + pub expected_kem_key_hashes: BTreeMap, + pub x25519: DHPublicKey, pub lp_version: u8, + pub ciphersuite: Ciphersuite, +} + +impl TestedNodeLpDetails { + pub fn into_remote_peer(self) -> LpRemotePeer { + LpRemotePeer::new(self.x25519).with_key_digests(self.expected_kem_key_hashes) + } } diff --git a/nym-gateway-probe/src/common/probe_tests.rs b/nym-gateway-probe/src/common/probe_tests.rs index 0ee8140f77..158cbfc011 100644 --- a/nym-gateway-probe/src/common/probe_tests.rs +++ b/nym-gateway-probe/src/common/probe_tests.rs @@ -28,10 +28,12 @@ use nym_credentials_interface::{CredentialSpendingData, TicketType}; use nym_crypto::asymmetric::{ed25519, x25519}; use nym_ip_packet_client::IprClientConnect; use nym_ip_packet_requests::{IpPair, codec::MultiIpPacketCodec}; +use nym_lp::peer::DHKeyPair; use nym_registration_client::{LpRegistrationClient, NestedLpSession}; use nym_sdk::NymNetworkDetails; use nym_sdk::mixnet::{MixnetClient, MixnetClientBuilder, NodeIdentity, Recipient, Socks5}; use nym_topology::{HardcodedTopologyProvider, NymTopology}; +use rand09::SeedableRng; use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, sync::Arc, @@ -163,23 +165,25 @@ pub async fn lp_registration_probe( ) -> anyhow::Result { let lp_address = gateway_lp_data.address; let lp_version = gateway_lp_data.lp_version; - let peer = helpers::to_lp_remote_peer(gateway_identity, gateway_lp_data); + let lp_ciphersuite = gateway_lp_data.ciphersuite; + let peer = gateway_lp_data.into_remote_peer(); info!("Starting LP registration probe for gateway at {lp_address}"); let mut lp_outcome = LpProbeResults::default(); // Generate Ed25519 keypair for this connection (X25519 will be derived internally by LP) - let mut rng = rand::thread_rng(); - let client_ed25519_keypair = std::sync::Arc::new(ed25519::KeyPair::new(&mut rng)); + let mut rng09 = rand09::rngs::StdRng::from_os_rng(); + let client_x25519_keypair = Arc::new(DHKeyPair::new(&mut rng09)); // Step 0: Derive X25519 keys from Ed25519 for the gateways // Create LP registration client (uses Ed25519 keys directly, derives X25519 internally) let mut client = LpRegistrationClient::::new_with_default_config( - client_ed25519_keypair, + client_x25519_keypair, peer, lp_address, + lp_ciphersuite, lp_version, ); @@ -209,23 +213,13 @@ pub async fn lp_registration_probe( let wg_keypair = nym_crypto::asymmetric::x25519::KeyPair::new(&mut rng); // Convert gateway identity to ed25519 public key - let gateway_ed25519_pubkey = match nym_crypto::asymmetric::ed25519::PublicKey::from_bytes( - &gateway_identity.to_bytes(), - ) { - Ok(key) => key, - Err(e) => { - let error_msg = format!("Failed to convert gateway identity: {}", e); - error!("{}", error_msg); - lp_outcome.error = Some(error_msg); - return Ok(lp_outcome); - } - }; + let gateway_ed25519_pubkey = gateway_identity; // Register using the new packet-per-connection API (returns GatewayData directly) let ticket_type = TicketType::V1WireguardEntry; let gateway_data = match client .register_dvpn( - &mut rng, + &mut rng09, &wg_keypair, &gateway_ed25519_pubkey, bandwidth_controller, @@ -299,21 +293,26 @@ pub async fn wg_probe_lp( let entry_lp_version = entry_lp_data.lp_version; let exit_lp_version = exit_lp_data.lp_version; + let entry_lp_ciphersuite = entry_lp_data.ciphersuite; + let exit_lp_ciphersuite = exit_lp_data.ciphersuite; + info!("Starting LP-based WireGuard probe (entry→exit via forwarding)"); let mut wg_outcome = WgProbeResults::default(); - // Generate Ed25519 keypairs for LP protocol - let mut rng = rand::thread_rng(); - let entry_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng)); - let exit_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng)); + // Generate x25519 keypairs for LP protocol + let mut rng09 = rand09::rngs::StdRng::from_os_rng(); + + let entry_lp_keypair = Arc::new(DHKeyPair::new(&mut rng09)); + let exit_lp_keypair = Arc::new(DHKeyPair::new(&mut rng09)); // Generate WireGuard keypairs for VPN registration + let mut rng = rand::rngs::OsRng; let entry_wg_keypair = x25519::KeyPair::new(&mut rng); let exit_wg_keypair = x25519::KeyPair::new(&mut rng); - let entry_peer = helpers::to_lp_remote_peer(entry_gateway.identity, entry_lp_data); - let exit_peer = helpers::to_lp_remote_peer(exit_gateway.identity, exit_lp_data); + let entry_peer = entry_lp_data.into_remote_peer(); + let exit_peer = exit_lp_data.into_remote_peer(); // STEP 1: Establish outer LP session with entry gateway // LpRegistrationClient uses packet-per-connection model - connect() is gone, @@ -323,6 +322,7 @@ pub async fn wg_probe_lp( entry_lp_keypair, entry_peer, entry_address, + entry_lp_ciphersuite, entry_lp_version, ); @@ -335,16 +335,26 @@ pub async fn wg_probe_lp( // STEP 2: Use nested session to register with exit gateway via forwarding info!("Registering with exit gateway via entry forwarding..."); - let mut nested_session = - NestedLpSession::new(exit_address, exit_lp_keypair, exit_peer, exit_lp_version); + let mut nested_session = NestedLpSession::new( + exit_address, + exit_lp_keypair, + exit_peer, + exit_lp_ciphersuite, + exit_lp_version, + ); let exit_gateway_pubkey = exit_gateway.identity; // Perform handshake and registration with exit gateway via forwarding + if let Err(err) = nested_session.perform_handshake(&mut entry_client).await { + error!("Failed to perform handshake with exit gateway: {err}"); + return Ok(wg_outcome); + }; + let exit_gateway_data = match nested_session - .handshake_and_register_dvpn( + .register_dvpn( &mut entry_client, - &mut rng, + &mut rng09, &exit_wg_keypair, &exit_gateway_pubkey, bandwidth_controller, @@ -370,7 +380,7 @@ pub async fn wg_probe_lp( // Use packet-per-connection register() which returns GatewayData directly let entry_gateway_data = match entry_client .register_dvpn( - &mut rng, + &mut rng09, &entry_wg_keypair, &entry_gateway_pubkey, bandwidth_controller, diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index 00340e45cb..4845997d7b 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -13,6 +13,8 @@ license = "GPL-3.0" publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +path = "src/lib.rs" [dependencies] async-trait = { workspace = true } @@ -27,6 +29,7 @@ colored = { workspace = true } console-subscriber = { workspace = true, optional = true } csv = { workspace = true } clap = { workspace = true, features = ["cargo", "env"] } +dashmap = { workspace = true } futures = { workspace = true } hex = { workspace = true } humantime-serde = { workspace = true } @@ -34,6 +37,7 @@ human-repr = { workspace = true } ipnetwork = { workspace = true } indicatif = { workspace = true } rand = { workspace = true } +rand09 = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true thiserror.workspace = true @@ -112,6 +116,11 @@ nym-gateway = { path = "../gateway" } nym-network-requester = { path = "../service-providers/network-requester" } nym-ip-packet-router = { path = "../service-providers/ip-packet-router" } +# LP dependencies +nym-lp = { path = "../common/nym-lp" } +nym-registration-common = { path = "../common/registration" } +bincode = { workspace = true } + # throughput tester to recreate lioness # we don't care about particular versions - just pull whatever is used by sphinx @@ -133,7 +142,7 @@ cargo_metadata = { workspace = true } [dev-dependencies] criterion = { workspace = true, features = ["async_tokio"] } -rand_chacha = { workspace = true } +nym-test-utils = { workspace = true } [features] tokio-console = ["console-subscriber", "nym-task/tokio-tracing"] diff --git a/nym-node/nym-node-requests/Cargo.toml b/nym-node/nym-node-requests/Cargo.toml index 6c311152ab..b051086220 100644 --- a/nym-node/nym-node-requests/Cargo.toml +++ b/nym-node/nym-node-requests/Cargo.toml @@ -26,6 +26,7 @@ url = { workspace = true, features = ["serde"] } nym-crypto = { workspace = true, features = [ "asymmetric", "serde", + "libcrux_x25519" ] } nym-exit-policy = { workspace = true } nym-noise-keys = { workspace = true } @@ -47,7 +48,7 @@ nym-bin-common = { workspace = true, features = [ [dev-dependencies] tokio = { workspace = true, features = ["full"] } -rand_chacha = { workspace = true } +nym-test-utils = { workspace = true } nym-crypto = { workspace = true, features = ["rand"] } diff --git a/nym-node/nym-node-requests/src/api/client.rs b/nym-node/nym-node-requests/src/api/client.rs index dd014851cc..6e3b93eeca 100644 --- a/nym-node/nym-node-requests/src/api/client.rs +++ b/nym-node/nym-node-requests/src/api/client.rs @@ -1,24 +1,24 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use super::v1::gateway::models::Wireguard; +use super::v1::metrics::models::SessionStats; +use crate::api::SignedLewesProtocol; +use crate::api::v1::authenticator::models::Authenticator; use crate::api::v1::gateway::models::WebSockets; +use crate::api::v1::health::models::NodeHealth; +use crate::api::v1::ip_packet_router::models::IpPacketRouter; +use crate::api::v1::network_requester::exit_policy::models::UsedExitPolicy; +use crate::api::v1::network_requester::models::NetworkRequester; use crate::api::v1::node::models::{ AuxiliaryDetails, NodeDescription, NodeRoles, SignedHostInformation, }; +use crate::api::v1::node_load::models::NodeLoad; use crate::routes; use async_trait::async_trait; use nym_bin_common::build_information::BinaryBuildInformationOwned; use nym_http_api_client::{ApiClient, HttpClientError}; -use super::v1::gateway::models::Wireguard; -use super::v1::metrics::models::SessionStats; -use crate::api::v1::authenticator::models::Authenticator; -use crate::api::v1::health::models::NodeHealth; -use crate::api::v1::ip_packet_router::models::IpPacketRouter; -use crate::api::v1::lewes_protocol::models::LewesProtocol; -use crate::api::v1::network_requester::exit_policy::models::UsedExitPolicy; -use crate::api::v1::network_requester::models::NetworkRequester; -use crate::api::v1::node_load::models::NodeLoad; pub use nym_http_api_client::Client; pub type NymNodeApiClientError = HttpClientError; @@ -98,7 +98,7 @@ pub trait NymNodeApiClientExt: ApiClient { .await } - async fn get_lewes_protocol(&self) -> Result { + async fn get_lewes_protocol(&self) -> Result { self.get_json_from(routes::api::v1::lp_absolute()).await } } diff --git a/nym-node/nym-node-requests/src/api/mod.rs b/nym-node/nym-node-requests/src/api/mod.rs index ba32570c2e..d9c9835af0 100644 --- a/nym-node/nym-node-requests/src/api/mod.rs +++ b/nym-node/nym-node-requests/src/api/mod.rs @@ -20,6 +20,7 @@ pub use client::Client; // create the type alias manually if openapi is not enabled pub type SignedHostInformation = SignedData; +pub type SignedLewesProtocol = SignedData; #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub struct SignedDataHostInfo { @@ -28,11 +29,20 @@ pub struct SignedDataHostInfo { pub signature: String, } +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +pub struct SignedLewesProtocolInfo { + // #[serde(flatten)] + pub data: crate::api::v1::lewes_protocol::models::LewesProtocol, + pub signature: String, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SignedData { // #[serde(flatten)] pub data: T, - pub signature: String, + + #[serde(with = "ed25519::bs58_ed25519_signature")] + pub signature: ed25519::Signature, } impl SignedData { @@ -42,7 +52,7 @@ impl SignedData { { let plaintext = serde_json::to_string(&data)?; - let signature = key.sign(plaintext).to_base58_string(); + let signature = key.sign(plaintext); Ok(SignedData { data, signature }) } @@ -54,11 +64,7 @@ impl SignedData { return false; }; - let Ok(signature) = ed25519::Signature::from_base58_string(&self.signature) else { - return false; - }; - - key.verify(plaintext, &signature).is_ok() + key.verify(plaintext, &self.signature).is_ok() } } @@ -72,7 +78,7 @@ impl SignedHostInformation { let legacy_v3 = SignedData { data: LegacyHostInformationV3::from(self.data.clone()), - signature: self.signature.clone(), + signature: self.signature, }; if legacy_v3.verify(&self.keys.ed25519_identity) { @@ -82,7 +88,7 @@ impl SignedHostInformation { // attempt to verify legacy signatures let legacy_v3 = SignedData { data: LegacyHostInformationV3::from(self.data.clone()), - signature: self.signature.clone(), + signature: self.signature, }; if legacy_v3.verify(&self.keys.ed25519_identity) { @@ -91,7 +97,7 @@ impl SignedHostInformation { let legacy_v2 = SignedData { data: LegacyHostInformationV2::from(legacy_v3.data), - signature: self.signature.clone(), + signature: self.signature, }; if legacy_v2.verify(&self.keys.ed25519_identity) { @@ -100,7 +106,7 @@ impl SignedHostInformation { SignedData { data: LegacyHostInformationV1::from(legacy_v2.data), - signature: self.signature.clone(), + signature: self.signature, } .verify(&self.keys.ed25519_identity) } @@ -128,16 +134,15 @@ impl Display for ErrorResponse { #[allow(deprecated)] #[cfg(test)] mod tests { - use super::*; use crate::api::v1::node::models::{HostKeys, SphinxKey}; use nym_crypto::asymmetric::{ed25519, x25519}; use nym_noise_keys::{NoiseVersion, VersionedNoiseKeyV1}; - use rand_chacha::rand_core::SeedableRng; + use nym_test_utils::helpers::deterministic_rng; #[test] fn dummy_signed_host_verification() { - let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]); + let mut rng = deterministic_rng(); let ed22519 = ed25519::KeyPair::new(&mut rng); let x25519_sphinx = x25519::KeyPair::new(&mut rng); let x25519_sphinx2 = x25519::KeyPair::new(&mut rng); @@ -237,7 +242,7 @@ mod tests { #[test] fn dummy_legacy_v3_signed_host_verification() { - let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]); + let mut rng = deterministic_rng(); let ed22519 = ed25519::KeyPair::new(&mut rng); let x25519_sphinx = x25519::KeyPair::new(&mut rng); let x25519_noise = x25519::KeyPair::new(&mut rng); @@ -331,7 +336,7 @@ mod tests { #[test] fn dummy_legacy_v2_signed_host_verification() { - let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]); + let mut rng = deterministic_rng(); let ed22519 = ed25519::KeyPair::new(&mut rng); let x25519_sphinx = x25519::KeyPair::new(&mut rng); let x25519_noise = x25519::KeyPair::new(&mut rng); @@ -420,7 +425,7 @@ mod tests { #[test] fn dummy_legacy_v1_signed_host_verification() { - let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]); + let mut rng = deterministic_rng(); let ed22519 = ed25519::KeyPair::new(&mut rng); let x25519_sphinx = x25519::KeyPair::new(&mut rng); diff --git a/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs b/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs index 17900472b6..fe1ea68bd5 100644 --- a/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs +++ b/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use nym_crypto::asymmetric::x25519; -use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey; +use nym_crypto::asymmetric::x25519::serde_helpers::bs58_dh_public_key; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::collections::BTreeMap; use strum_macros::{Display, EnumIter, EnumString}; #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] @@ -21,21 +21,16 @@ pub struct LewesProtocol { /// LP UDP data address (default: 51264) for Sphinx packets wrapped in LP pub data_port: u16, - #[serde(with = "bs58_x25519_pubkey")] + #[serde(with = "bs58_dh_public_key")] #[schemars(with = "String")] - #[schema(value_type = String)] + #[cfg_attr(feature = "openapi", schema(value_type = String))] /// LP public key - pub x25519: x25519::PublicKey, + pub x25519: x25519::DHPublicKey, /// Digests of the KEM keys available to this node alongside hashing algorithms used /// for their computation. /// note: digests are hex encoded - pub kem_keys: HashMap>, - - /// Digests of the signing keys available to this node alongside hashing algorithms used - /// for their computation. - /// note: digests are hex encoded - pub signing_keys: HashMap>, + pub kem_keys: BTreeMap>, } impl LewesProtocol { @@ -43,9 +38,8 @@ impl LewesProtocol { enabled: bool, control_port: u16, data_port: u16, - x25519: x25519::PublicKey, - kem_keys: HashMap>, - signing_keys: HashMap>, + x25519: x25519::DHPublicKey, + kem_keys: BTreeMap>, ) -> Self { LewesProtocol { enabled, @@ -53,7 +47,6 @@ impl LewesProtocol { data_port, x25519, kem_keys, - signing_keys, } } } @@ -76,13 +69,12 @@ impl LewesProtocol { PartialOrd, Display, EnumString, + Ord, )] #[strum(serialize_all = "lowercase")] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum LPKEM { MlKem768, - XWing, - X25519, McEliece, } @@ -90,8 +82,6 @@ impl From for nym_kkt_ciphersuite::KEM { fn from(lpkem: LPKEM) -> Self { match lpkem { LPKEM::MlKem768 => nym_kkt_ciphersuite::KEM::MlKem768, - LPKEM::XWing => nym_kkt_ciphersuite::KEM::XWing, - LPKEM::X25519 => nym_kkt_ciphersuite::KEM::X25519, LPKEM::McEliece => nym_kkt_ciphersuite::KEM::McEliece, } } @@ -101,8 +91,6 @@ impl From for LPKEM { fn from(kem: nym_kkt_ciphersuite::KEM) -> Self { match kem { nym_kkt_ciphersuite::KEM::MlKem768 => LPKEM::MlKem768, - nym_kkt_ciphersuite::KEM::XWing => LPKEM::XWing, - nym_kkt_ciphersuite::KEM::X25519 => LPKEM::X25519, nym_kkt_ciphersuite::KEM::McEliece => LPKEM::McEliece, } } @@ -122,6 +110,7 @@ impl From for LPKEM { Display, EnumString, EnumIter, + Ord, )] #[strum(serialize_all = "lowercase")] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] @@ -199,8 +188,6 @@ mod tests { #[test] fn kem_display() { assert_eq!(LPKEM::MlKem768.to_string(), "mlkem768"); - assert_eq!(LPKEM::XWing.to_string(), "xwing"); - assert_eq!(LPKEM::X25519.to_string(), "x25519"); assert_eq!(LPKEM::McEliece.to_string(), "mceliece"); } diff --git a/nym-node/src/cli/commands/run/args.rs b/nym-node/src/cli/commands/run/args.rs index 8c214f5e5e..2fbc4d3623 100644 --- a/nym-node/src/cli/commands/run/args.rs +++ b/nym-node/src/cli/commands/run/args.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::cli::helpers::{ - ConfigArgs, EntryGatewayArgs, ExitGatewayArgs, HostArgs, HttpArgs, MetricsArgs, MixnetArgs, - VerlocArgs, WireguardArgs, + ConfigArgs, EntryGatewayArgs, ExitGatewayArgs, HostArgs, HttpArgs, LpArgs, MetricsArgs, + MixnetArgs, VerlocArgs, WireguardArgs, }; use crate::config::persistence::NymNodePaths; use crate::config::{Config, ConfigBuilder, NodeMode, NodeModes}; @@ -125,6 +125,9 @@ pub(crate) struct Args { #[clap(flatten)] exit_gateway: ExitGatewayArgs, + + #[clap(flatten)] + lp: LpArgs, } impl Args { @@ -174,6 +177,7 @@ impl Args { .with_metrics(self.metrics.build_config_section()) .with_gateway_tasks(self.entry_gateway.build_config_section(&data_dir)?) .with_service_providers(self.exit_gateway.build_config_section(&data_dir)) + .with_lp(self.lp.build_config_section()) .build() } @@ -193,6 +197,7 @@ impl Args { config.service_providers = self .exit_gateway .override_config_section(config.service_providers); + config.lp = self.lp.override_config_section(config.lp); config } } diff --git a/nym-node/src/cli/helpers.rs b/nym-node/src/cli/helpers.rs index 00193698bf..147a5a0129 100644 --- a/nym-node/src/cli/helpers.rs +++ b/nym-node/src/cli/helpers.rs @@ -458,15 +458,6 @@ pub(crate) struct EntryGatewayArgs { )] #[zeroize(skip)] pub(crate) upgrade_mode_attester_public_key: Option, - - /// Use mock ecash manager for LP testing. - /// WARNING: Only use this for local testing! Never enable in production. - /// When enabled, the LP listener will accept any credential without blockchain verification. - #[clap( - long, - env = NYMNODE_LP_USE_MOCK_ECASH_ARG - )] - pub(crate) lp_use_mock_ecash: Option, } impl EntryGatewayArgs { @@ -500,9 +491,6 @@ impl EntryGatewayArgs { if let Some(upgrade_mode_attester_public_key) = self.upgrade_mode_attester_public_key { section.upgrade_mode.attester_public_key = upgrade_mode_attester_public_key } - if let Some(use_mock_ecash) = self.lp_use_mock_ecash { - section.lp.debug.use_mock_ecash = use_mock_ecash - } section } @@ -549,3 +537,62 @@ impl ExitGatewayArgs { section } } + +#[derive(clap::Args, Debug)] +pub(crate) struct LpArgs { + /// Bind address for the TCP LP control traffic. + /// default: `[::]:41264` + #[clap( + long, + env = NYMNODE_LP_CONTROL_BIND_ADDRESS_ARG + )] + pub(crate) lp_control_bind_address: Option, + + /// Custom announced port for listening for the TCP LP control traffic. + /// If unspecified, the value from the `lp_control_bind_address` will be used instead + #[clap( + long, + env = NYMNODE_LP_CONTROL_ANNOUNCE_PORT_ARG + )] + pub(crate) lp_control_announce_port: Option, + + /// Bind address for the UDP LP data traffic. + /// default: `[::]:51264` + #[clap( + long, + env = NYMNODE_LP_DATA_BIND_ADDRESS_ARG + )] + pub(crate) lp_data_bind_address: Option, + + /// Custom announced port for listening for the UDP LP data traffic. + /// If unspecified, the value from the `lp_data_bind_address` will be used instead + #[clap( + long, + env = NYMNODE_LP_DATA_ANNOUNCE_PORT_ARG + )] + pub(crate) lp_data_announce_port: Option, + + /// Use mock ecash manager for LP testing. + /// WARNING: Only use this for local testing! Never enable in production. + /// When enabled, the LP listener will accept any credential without blockchain verification. + #[clap( + long, + env = NYMNODE_LP_USE_MOCK_ECASH_ARG + )] + pub(crate) lp_use_mock_ecash: Option, +} + +impl LpArgs { + // TODO: could we perhaps make a clap error here and call `safe_exit` instead? + pub(crate) fn build_config_section(self) -> config::LpConfig { + self.override_config_section(config::LpConfig::default()) + } + + pub(crate) fn override_config_section(self, mut section: config::LpConfig) -> config::LpConfig { + if let Some(use_mock_ecash) = self.lp_use_mock_ecash { + section.debug.use_mock_ecash = use_mock_ecash + } + + section + } +} diff --git a/nym-node/src/config/gateway_tasks.rs b/nym-node/src/config/gateway_tasks.rs index 99577f82ca..ed6335fed8 100644 --- a/nym-node/src/config/gateway_tasks.rs +++ b/nym-node/src/config/gateway_tasks.rs @@ -46,9 +46,6 @@ pub struct GatewayTasksConfig { pub upgrade_mode: UpgradeModeWatcher, - #[serde(default)] - pub lp: nym_gateway::node::LpConfig, - #[serde(default)] pub debug: Debug, } @@ -228,7 +225,6 @@ impl GatewayTasksConfig { announce_ws_port: None, announce_wss_port: None, upgrade_mode: UpgradeModeWatcher::new()?, - lp: Default::default(), debug: Default::default(), }) } diff --git a/nym-node/src/config/helpers.rs b/nym-node/src/config/helpers.rs index 20aac7cf0c..7c2e96215b 100644 --- a/nym-node/src/config/helpers.rs +++ b/nym-node/src/config/helpers.rs @@ -27,7 +27,6 @@ fn ephemeral_gateway_config(config: &Config) -> nym_gateway::config::Config { enabled: config.service_providers.network_requester.debug.enabled, }, config.gateway_tasks.upgrade_mode.clone(), - config.gateway_tasks.lp, nym_gateway::config::Debug { client_bandwidth_max_flushing_rate: config .gateway_tasks @@ -92,8 +91,6 @@ pub struct GatewayTasksConfig { pub auth_opts: Option, #[allow(dead_code)] pub wg_opts: LocalWireguardOpts, - #[allow(dead_code)] - pub lp: nym_gateway::node::LpConfig, } // that function is rather disgusting, but I hope it's not going to live for too long @@ -227,7 +224,6 @@ pub fn gateway_tasks_config(config: &Config) -> GatewayTasksConfig { ipr_opts: Some(ipr_opts), auth_opts: Some(auth_opts), wg_opts, - lp: config.gateway_tasks.lp, } } diff --git a/nym-node/src/config/lp.rs b/nym-node/src/config/lp.rs new file mode 100644 index 0000000000..260fea64b1 --- /dev/null +++ b/nym-node/src/config/lp.rs @@ -0,0 +1,153 @@ +// Copyright 2026 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use nym_config::serde_helpers::de_maybe_port; +use std::net::{IpAddr, Ipv6Addr, SocketAddr}; +use std::time::Duration; + +/// Configuration for LP listener +#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct LpConfig { + /// Bind address for the TCP LP control traffic. + /// default: `[::]:41264` + pub control_bind_address: SocketAddr, + + /// Bind address for the UDP LP data traffic. + /// default: `[::]:51264` + pub data_bind_address: SocketAddr, + + /// Custom announced port for listening for the TCP LP control traffic. + /// If unspecified, the value from the `control_bind_address` will be used instead + /// (default: None) + #[serde(deserialize_with = "de_maybe_port")] + pub announce_control_port: Option, + + /// Custom announced port for listening for the UDP LP data traffic. + /// If unspecified, the value from the `data_bind_address` will be used instead + /// (default: None) + #[serde(deserialize_with = "de_maybe_port")] + pub announce_data_port: Option, + + /// Auxiliary configuration + #[serde(default)] + pub debug: LpDebug, +} + +#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct LpDebug { + /// Maximum concurrent connections + pub max_connections: usize, + + /// Use mock ecash manager for testing (default: false) + /// + /// When enabled, the LP listener will use a mock ecash verifier that + /// accepts any credential without blockchain verification. This is + /// useful for testing the LP protocol implementation without requiring + /// a full blockchain/contract setup. + /// + /// WARNING: Only use this for local testing! Never enable in production. + pub use_mock_ecash: bool, + + /// Maximum age of in-progress handshakes before cleanup (default: 90s) + /// + /// Handshakes should complete quickly (3-5 packets). This TTL accounts for: + /// - Network latency and retransmits + /// - Slow clients + /// - Clock skew tolerance + /// + /// Stale handshakes are removed by the cleanup task to prevent memory leaks. + #[serde(with = "humantime_serde")] + pub handshake_ttl: Duration, + + /// Maximum age of established sessions before cleanup (default: 24h) + /// + /// Sessions can be long-lived for dVPN tunnels. This TTL should be set + /// high enough to accommodate expected usage patterns: + /// - dVPN sessions: hours to days + /// - Registration: minutes + /// + /// Sessions with no activity for this duration are removed by the cleanup task. + #[serde(with = "humantime_serde")] + pub session_ttl: Duration, + + /// How often to run the state cleanup task (default: 5 minutes) + /// + /// The cleanup task scans for and removes stale handshakes and sessions. + /// Lower values = more frequent cleanup but higher overhead. + /// Higher values = less overhead but slower memory reclamation. + #[serde(with = "humantime_serde")] + pub state_cleanup_interval: Duration, + + /// Maximum concurrent forward connections (default: 1000) + /// + /// Limits simultaneous outbound connections when forwarding LP packets to other gateways + /// during telescope setup. This prevents file descriptor exhaustion under high load. + /// + /// When at capacity, new forward requests return an error, signaling the client + /// to choose a different gateway. + pub max_concurrent_forwards: usize, +} + +impl LpConfig { + pub const DEFAULT_CONTROL_PORT: u16 = 41264; + pub const DEFAULT_DATA_PORT: u16 = 51264; + + pub fn announced_control_port(&self) -> u16 { + self.announce_control_port + .unwrap_or(self.control_bind_address.port()) + } + + pub fn announced_data_port(&self) -> u16 { + self.announce_data_port + .unwrap_or(self.data_bind_address.port()) + } +} + +impl Default for LpConfig { + fn default() -> Self { + LpConfig { + control_bind_address: SocketAddr::new( + IpAddr::V6(Ipv6Addr::UNSPECIFIED), + Self::DEFAULT_CONTROL_PORT, + ), + data_bind_address: SocketAddr::new( + IpAddr::V6(Ipv6Addr::UNSPECIFIED), + Self::DEFAULT_DATA_PORT, + ), + announce_control_port: None, + announce_data_port: None, + debug: Default::default(), + } + } +} + +impl LpDebug { + pub const DEFAULT_MAX_CONNECTIONS: usize = 10000; + + // 90 seconds - handshakes should complete quickly + pub const DEFAULT_HANDSHAKE_TTL: Duration = Duration::from_secs(90); + + // 24 hours - for long-lived dVPN sessions + pub const DEFAULT_SESSION_TTL: Duration = Duration::from_secs(86400); + + // 5 minutes - balances memory reclamation with task overhead + pub const DEFAULT_STATE_CLEANUP_INTERVAL: Duration = Duration::from_secs(300); + + // Limits concurrent outbound connections to prevent fd exhaustion + pub const DEFAULT_MAX_CONCURRENT_FORWARDS: usize = 1000; +} + +impl Default for LpDebug { + fn default() -> Self { + LpDebug { + max_connections: Self::DEFAULT_MAX_CONNECTIONS, + use_mock_ecash: false, + handshake_ttl: Self::DEFAULT_HANDSHAKE_TTL, + session_ttl: Self::DEFAULT_SESSION_TTL, + state_cleanup_interval: Self::DEFAULT_STATE_CLEANUP_INTERVAL, + max_concurrent_forwards: Self::DEFAULT_MAX_CONCURRENT_FORWARDS, + } + } +} diff --git a/nym-node/src/config/mod.rs b/nym-node/src/config/mod.rs index 064efc8b6a..14b00b7f5e 100644 --- a/nym-node/src/config/mod.rs +++ b/nym-node/src/config/mod.rs @@ -4,6 +4,7 @@ use crate::config::persistence::{NymNodePaths, ReplayProtectionPaths}; use crate::config::template::CONFIG_TEMPLATE; use crate::error::NymNodeError; +use crate::node::replay_protection::{bitmap_size, items_in_bloomfilter}; use celes::Country; use clap::ValueEnum; use human_repr::HumanCount; @@ -35,6 +36,7 @@ use url::Url; pub mod authenticator; pub mod gateway_tasks; pub mod helpers; +pub mod lp; pub mod metrics; mod old_configs; pub mod persistence; @@ -43,9 +45,9 @@ mod template; pub mod upgrade_helpers; pub use crate::config::gateway_tasks::GatewayTasksConfig; +pub use crate::config::lp::{LpConfig, LpDebug}; pub use crate::config::metrics::MetricsConfig; pub use crate::config::service_providers::ServiceProvidersConfig; -use crate::node::replay_protection::{bitmap_size, items_in_bloomfilter}; const DEFAULT_NYMNODES_DIR: &str = "nym-nodes"; @@ -186,6 +188,8 @@ pub struct ConfigBuilder { pub metrics: Option, + pub lp: Option, + pub logging: Option, } @@ -205,6 +209,7 @@ impl ConfigBuilder { gateway_tasks: None, service_providers: None, metrics: None, + lp: None, logging: None, } } @@ -234,6 +239,11 @@ impl ConfigBuilder { self } + pub fn with_lp(mut self, section: impl Into>) -> Self { + self.lp = section.into(); + self + } + pub fn with_wireguard(mut self, section: impl Into>) -> Self { self.wireguard = section.into(); self @@ -284,6 +294,7 @@ impl ConfigBuilder { .storage_paths .unwrap_or_else(|| NymNodePaths::new(&self.data_dir)), metrics: self.metrics.unwrap_or_default(), + lp: self.lp.unwrap_or_default(), gateway_tasks, service_providers: self .service_providers @@ -323,6 +334,9 @@ pub struct Config { pub wireguard: Wireguard, + #[serde(default)] + pub lp: LpConfig, + #[serde(alias = "entry_gateway")] pub gateway_tasks: GatewayTasksConfig, diff --git a/nym-node/src/config/old_configs/mod.rs b/nym-node/src/config/old_configs/mod.rs index c46a72d1a1..2116f94ec8 100644 --- a/nym-node/src/config/old_configs/mod.rs +++ b/nym-node/src/config/old_configs/mod.rs @@ -4,6 +4,7 @@ mod old_config_v1; mod old_config_v10; mod old_config_v11; +mod old_config_v12; mod old_config_v2; mod old_config_v3; mod old_config_v4; @@ -24,3 +25,4 @@ pub use old_config_v8::try_upgrade_config_v8; pub use old_config_v9::try_upgrade_config_v9; pub use old_config_v10::try_upgrade_config_v10; pub use old_config_v11::try_upgrade_config_v11; +pub use old_config_v12::try_upgrade_config_v12; diff --git a/nym-node/src/config/old_configs/old_config_v11.rs b/nym-node/src/config/old_configs/old_config_v11.rs index 6d3a380357..6e1abb705d 100644 --- a/nym-node/src/config/old_configs/old_config_v11.rs +++ b/nym-node/src/config/old_configs/old_config_v11.rs @@ -1,24 +1,11 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::config::authenticator::{Authenticator, AuthenticatorDebug}; -use crate::config::gateway_tasks::{ - ClientBandwidthDebug, StaleMessageDebug, UpgradeModeWatcher, ZkNymTicketHandlerDebug, -}; -use crate::config::persistence::{ - AuthenticatorPaths, GatewayTasksPaths, IpPacketRouterPaths, KeysPaths, NetworkRequesterPaths, - NymNodePaths, ReplayProtectionPaths, ServiceProvidersPaths, WireguardPaths, -}; -use crate::config::service_providers::{ - IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug, -}; -use crate::config::{ - Config, GatewayTasksConfig, Host, Http, KeyRotation, KeyRotationDebug, Mixnet, MixnetDebug, - NodeModes, ReplayProtection, ReplayProtectionDebug, ServiceProvidersConfig, Verloc, - VerlocDebug, Wireguard, gateway_tasks, service_providers, +use crate::config::old_configs::old_config_v12::{ + ConfigV12, DebugV12, GatewayTasksConfigDebugV12, GatewayTasksConfigV12, LpConfigV12, + UpgradeModeWatcherV12, WireguardV12, }; use crate::error::NymNodeError; -use nym_bin_common::logging::LoggingSettings; use nym_config::read_config_from_toml_file; use serde::{Deserialize, Serialize}; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; @@ -185,7 +172,7 @@ impl ConfigV11 { pub async fn try_upgrade_config_v11>( path: P, prev_config: Option, -) -> Result { +) -> Result { debug!("attempting to load v11 config..."); let old_cfg = if let Some(prev_config) = prev_config { @@ -197,144 +184,16 @@ pub async fn try_upgrade_config_v11>( // for future reference: when creating v12 migration, // look at how v10 -> v11 is implemented // you might be able to create a bunch of type aliases again to save you some headache - let cfg = Config { + let cfg = ConfigV12 { save_path: old_cfg.save_path, id: old_cfg.id, - modes: NodeModes { - mixnode: old_cfg.modes.mixnode, - entry: old_cfg.modes.entry, - exit: old_cfg.modes.exit, - }, - host: Host { - public_ips: old_cfg.host.public_ips, - hostname: old_cfg.host.hostname, - location: old_cfg.host.location, - }, - mixnet: Mixnet { - bind_address: old_cfg.mixnet.bind_address, - announce_port: old_cfg.mixnet.announce_port, - nym_api_urls: old_cfg.mixnet.nym_api_urls, - nyxd_urls: old_cfg.mixnet.nyxd_urls, - replay_protection: ReplayProtection { - storage_paths: ReplayProtectionPaths { - current_bloomfilters_directory: old_cfg - .mixnet - .replay_protection - .storage_paths - .current_bloomfilters_directory, - }, - debug: ReplayProtectionDebug { - unsafe_disabled: old_cfg.mixnet.replay_protection.debug.unsafe_disabled, - maximum_replay_detection_deferral: old_cfg - .mixnet - .replay_protection - .debug - .maximum_replay_detection_deferral, - maximum_replay_detection_pending_packets: old_cfg - .mixnet - .replay_protection - .debug - .maximum_replay_detection_pending_packets, - false_positive_rate: old_cfg.mixnet.replay_protection.debug.false_positive_rate, - initial_expected_packets_per_second: old_cfg - .mixnet - .replay_protection - .debug - .initial_expected_packets_per_second, - bloomfilter_minimum_packets_per_second_size: old_cfg - .mixnet - .replay_protection - .debug - .bloomfilter_minimum_packets_per_second_size, - bloomfilter_size_multiplier: old_cfg - .mixnet - .replay_protection - .debug - .bloomfilter_size_multiplier, - bloomfilter_disk_flushing_rate: old_cfg - .mixnet - .replay_protection - .debug - .bloomfilter_disk_flushing_rate, - }, - }, - key_rotation: KeyRotation { - debug: KeyRotationDebug { - rotation_state_poling_interval: old_cfg - .mixnet - .key_rotation - .debug - .rotation_state_poling_interval, - }, - }, - debug: MixnetDebug { - maximum_forward_packet_delay: old_cfg.mixnet.debug.maximum_forward_packet_delay, - packet_forwarding_initial_backoff: old_cfg - .mixnet - .debug - .packet_forwarding_initial_backoff, - packet_forwarding_maximum_backoff: old_cfg - .mixnet - .debug - .packet_forwarding_maximum_backoff, - initial_connection_timeout: old_cfg.mixnet.debug.initial_connection_timeout, - maximum_connection_buffer_size: old_cfg.mixnet.debug.maximum_connection_buffer_size, - unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise, - use_legacy_packet_encoding: old_cfg.mixnet.debug.use_legacy_packet_encoding, - }, - }, - storage_paths: NymNodePaths { - keys: KeysPaths { - private_ed25519_identity_key_file: old_cfg - .storage_paths - .keys - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .storage_paths - .keys - .public_ed25519_identity_key_file, - primary_x25519_sphinx_key_file: old_cfg - .storage_paths - .keys - .primary_x25519_sphinx_key_file, - private_x25519_noise_key_file: old_cfg - .storage_paths - .keys - .private_x25519_noise_key_file, - public_x25519_noise_key_file: old_cfg - .storage_paths - .keys - .public_x25519_noise_key_file, - secondary_x25519_sphinx_key_file: old_cfg - .storage_paths - .keys - .secondary_x25519_sphinx_key_file, - }, - description: old_cfg.storage_paths.description, - }, - http: Http { - bind_address: old_cfg.http.bind_address, - landing_page_assets_path: old_cfg.http.landing_page_assets_path, - access_token: old_cfg.http.access_token, - expose_system_info: old_cfg.http.expose_system_info, - expose_system_hardware: old_cfg.http.expose_system_hardware, - expose_crypto_hardware: old_cfg.http.expose_crypto_hardware, - node_load_cache_ttl: old_cfg.http.node_load_cache_ttl, - }, - verloc: Verloc { - bind_address: old_cfg.verloc.bind_address, - announce_port: old_cfg.verloc.announce_port, - debug: VerlocDebug { - packets_per_node: old_cfg.verloc.debug.packets_per_node, - connection_timeout: old_cfg.verloc.debug.connection_timeout, - packet_timeout: old_cfg.verloc.debug.packet_timeout, - delay_between_packets: old_cfg.verloc.debug.delay_between_packets, - tested_nodes_batch_size: old_cfg.verloc.debug.tested_nodes_batch_size, - testing_interval: old_cfg.verloc.debug.testing_interval, - retry_timeout: old_cfg.verloc.debug.retry_timeout, - }, - }, - wireguard: Wireguard { + modes: old_cfg.modes, + host: old_cfg.host, + mixnet: old_cfg.mixnet, + storage_paths: old_cfg.storage_paths, + http: old_cfg.http, + verloc: old_cfg.verloc, + wireguard: WireguardV12 { enabled: old_cfg.wireguard.enabled, bind_address: old_cfg.wireguard.bind_address, private_ipv4: old_cfg.wireguard.private_ipv4, @@ -343,259 +202,48 @@ pub async fn try_upgrade_config_v11>( announced_metadata_port: old_cfg.wireguard.announced_metadata_port, private_network_prefix_v4: old_cfg.wireguard.private_network_prefix_v4, private_network_prefix_v6: old_cfg.wireguard.private_network_prefix_v6, + // \/ ADDED use_userspace: false, - storage_paths: WireguardPaths { - private_diffie_hellman_key_file: old_cfg - .wireguard - .storage_paths - .private_diffie_hellman_key_file, - public_diffie_hellman_key_file: old_cfg - .wireguard - .storage_paths - .public_diffie_hellman_key_file, - }, + // /\ ADDED + storage_paths: old_cfg.wireguard.storage_paths, }, - gateway_tasks: GatewayTasksConfig { - storage_paths: GatewayTasksPaths { - clients_storage: old_cfg.gateway_tasks.storage_paths.clients_storage, - stats_storage: old_cfg.gateway_tasks.storage_paths.stats_storage, - cosmos_mnemonic: old_cfg.gateway_tasks.storage_paths.cosmos_mnemonic, - bridge_client_params: old_cfg.gateway_tasks.storage_paths.bridge_client_params, - }, + gateway_tasks: GatewayTasksConfigV12 { + storage_paths: old_cfg.gateway_tasks.storage_paths, enforce_zk_nyms: old_cfg.gateway_tasks.enforce_zk_nyms, ws_bind_address: old_cfg.gateway_tasks.ws_bind_address, announce_ws_port: old_cfg.gateway_tasks.announce_ws_port, announce_wss_port: old_cfg.gateway_tasks.announce_wss_port, // \/ ADDED - upgrade_mode: UpgradeModeWatcher::new() + upgrade_mode: UpgradeModeWatcherV12::new() .inspect_err(|_| { error!( "failed to set custom upgrade mode configuration - falling back to mainnet" ) }) - .unwrap_or(UpgradeModeWatcher::new_mainnet()), - lp: Default::default(), + .unwrap_or(UpgradeModeWatcherV12::new_mainnet()), + lp: LpConfigV12::default(), // /\ ADDED - debug: gateway_tasks::Debug { + debug: GatewayTasksConfigDebugV12 { message_retrieval_limit: old_cfg.gateway_tasks.debug.message_retrieval_limit, maximum_open_connections: old_cfg.gateway_tasks.debug.maximum_open_connections, minimum_mix_performance: old_cfg.gateway_tasks.debug.minimum_mix_performance, max_request_timestamp_skew: old_cfg.gateway_tasks.debug.max_request_timestamp_skew, - stale_messages: StaleMessageDebug { - cleaner_run_interval: old_cfg - .gateway_tasks - .debug - .stale_messages - .cleaner_run_interval, - max_age: old_cfg.gateway_tasks.debug.stale_messages.max_age, - }, - client_bandwidth: ClientBandwidthDebug { - max_flushing_rate: old_cfg - .gateway_tasks - .debug - .client_bandwidth - .max_flushing_rate, - max_delta_flushing_amount: old_cfg - .gateway_tasks - .debug - .client_bandwidth - .max_delta_flushing_amount, - }, - zk_nym_tickets: ZkNymTicketHandlerDebug { - revocation_bandwidth_penalty: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .revocation_bandwidth_penalty, - pending_poller: old_cfg.gateway_tasks.debug.zk_nym_tickets.pending_poller, - minimum_api_quorum: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .minimum_api_quorum, - minimum_redemption_tickets: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .minimum_redemption_tickets, - maximum_time_between_redemption: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .maximum_time_between_redemption, - }, + stale_messages: old_cfg.gateway_tasks.debug.stale_messages, + client_bandwidth: old_cfg.gateway_tasks.debug.client_bandwidth, + zk_nym_tickets: old_cfg.gateway_tasks.debug.zk_nym_tickets, + // \/ ADDED (be explicit about the value rather than using ..Default::default() - upgrade_mode_min_staleness_recheck: gateway_tasks::Debug::default() + upgrade_mode_min_staleness_recheck: GatewayTasksConfigDebugV12::default() .upgrade_mode_min_staleness_recheck, // /\ ADDED }, }, - service_providers: ServiceProvidersConfig { - storage_paths: ServiceProvidersPaths { - clients_storage: old_cfg.service_providers.storage_paths.clients_storage, - stats_storage: old_cfg.service_providers.storage_paths.stats_storage, - network_requester: NetworkRequesterPaths { - private_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .public_ed25519_identity_key_file, - private_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .private_x25519_diffie_hellman_key_file, - public_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .public_x25519_diffie_hellman_key_file, - ack_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .ack_key_file, - reply_surb_database: old_cfg - .service_providers - .storage_paths - .network_requester - .reply_surb_database, - gateway_registrations: old_cfg - .service_providers - .storage_paths - .network_requester - .gateway_registrations, - }, - ip_packet_router: IpPacketRouterPaths { - private_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .public_ed25519_identity_key_file, - private_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .private_x25519_diffie_hellman_key_file, - public_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .public_x25519_diffie_hellman_key_file, - ack_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .ack_key_file, - reply_surb_database: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .reply_surb_database, - gateway_registrations: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .gateway_registrations, - }, - authenticator: AuthenticatorPaths { - private_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .public_ed25519_identity_key_file, - private_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .private_x25519_diffie_hellman_key_file, - public_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .public_x25519_diffie_hellman_key_file, - ack_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .ack_key_file, - reply_surb_database: old_cfg - .service_providers - .storage_paths - .authenticator - .reply_surb_database, - gateway_registrations: old_cfg - .service_providers - .storage_paths - .authenticator - .gateway_registrations, - }, - }, - open_proxy: old_cfg.service_providers.open_proxy, - upstream_exit_policy_url: old_cfg.service_providers.upstream_exit_policy_url, - network_requester: NetworkRequester { - debug: NetworkRequesterDebug { - enabled: old_cfg.service_providers.network_requester.debug.enabled, - disable_poisson_rate: old_cfg - .service_providers - .network_requester - .debug - .disable_poisson_rate, - client_debug: old_cfg - .service_providers - .network_requester - .debug - .client_debug, - }, - }, - ip_packet_router: IpPacketRouter { - debug: IpPacketRouterDebug { - enabled: old_cfg.service_providers.ip_packet_router.debug.enabled, - disable_poisson_rate: old_cfg - .service_providers - .ip_packet_router - .debug - .disable_poisson_rate, - client_debug: old_cfg - .service_providers - .ip_packet_router - .debug - .client_debug, - }, - }, - authenticator: Authenticator { - debug: AuthenticatorDebug { - enabled: old_cfg.service_providers.authenticator.debug.enabled, - disable_poisson_rate: old_cfg - .service_providers - .authenticator - .debug - .disable_poisson_rate, - client_debug: old_cfg.service_providers.authenticator.debug.client_debug, - }, - }, - debug: service_providers::Debug { - message_retrieval_limit: old_cfg.service_providers.debug.message_retrieval_limit, - }, - }, - metrics: Default::default(), - logging: LoggingSettings {}, - debug: Default::default(), + service_providers: old_cfg.service_providers, + metrics: old_cfg.metrics, + logging: old_cfg.logging, + // \/ FIXED + debug: DebugV12::default(), + // /\ FIXED }; Ok(cfg) } diff --git a/nym-node/src/config/old_configs/old_config_v12.rs b/nym-node/src/config/old_configs/old_config_v12.rs new file mode 100644 index 0000000000..268da56802 --- /dev/null +++ b/nym-node/src/config/old_configs/old_config_v12.rs @@ -0,0 +1,1001 @@ +// Copyright 2026 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use crate::config::authenticator::{Authenticator, AuthenticatorDebug}; +use crate::config::gateway_tasks::{ + ClientBandwidthDebug, StaleMessageDebug, UpgradeModeWatcher, UpgradeModeWatcherDebug, + ZkNymTicketHandlerDebug, +}; +use crate::config::helpers::log_error_and_return; +use crate::config::persistence::{ + AuthenticatorPaths, DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME, + DEFAULT_MCELIECE_PUBLIC_KEY_FILENAME, DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME, + DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME, DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME, + DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME, GatewayTasksPaths, IpPacketRouterPaths, KeysPaths, + NetworkRequesterPaths, NymNodePaths, ReplayProtectionPaths, ServiceProvidersPaths, + WireguardPaths, +}; +use crate::config::service_providers::{ + IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug, +}; +use crate::config::{ + Config, Debug, GatewayTasksConfig, Host, Http, KeyRotation, KeyRotationDebug, MetricsConfig, + Mixnet, MixnetDebug, NodeModes, ReplayProtection, ReplayProtectionDebug, + ServiceProvidersConfig, Verloc, VerlocDebug, Wireguard, gateway_tasks, metrics, + service_providers, +}; +use crate::error::NymNodeError; +use crate::node::helpers::{ + store_mceliece_keypair, store_mlkem768_keypair, store_x25519_lp_keypair, +}; +use nym_bin_common::logging::LoggingSettings; +use nym_config::defaults::{mainnet, var_names}; +use nym_config::read_config_from_toml_file; +use nym_config::serde_helpers::de_maybe_port; +use nym_crypto::asymmetric::ed25519; +use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey; +use nym_kkt::key_utils::{ + generate_keypair_mceliece, generate_keypair_mlkem, generate_lp_keypair_x25519, +}; +use rand09::SeedableRng; +use serde::{Deserialize, Serialize}; +use std::env; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::path::{Path, PathBuf}; +use std::time::Duration; +use tracing::{debug, info, instrument}; +use url::Url; + +use crate::config::lp::{LpConfig, LpDebug}; +pub use unchanged_v12_types::*; + +// (while some of those are technically unused, they might be needed in future migrations, +// thus allow them to exist) +#[allow(dead_code)] +pub mod unchanged_v12_types { + use crate::config::old_configs::old_config_v11::{ + AuthenticatorDebugV11, AuthenticatorPathsV11, AuthenticatorV11, ClientBandwidthDebugV11, + GatewayTasksPathsV11, HostV11, HttpV11, IpPacketRouterDebugV11, IpPacketRouterPathsV11, + IpPacketRouterV11, KeyRotationDebugV11, KeyRotationV11, KeysPathsV11, LoggingSettingsV11, + MetricsConfigV11, MetricsDebugV11, MixnetDebugV11, MixnetV11, NetworkRequesterDebugV11, + NetworkRequesterPathsV11, NetworkRequesterV11, NodeModeV11, NodeModesV11, NymNodePathsV11, + ReplayProtectionDebugV11, ReplayProtectionPathsV11, ReplayProtectionV11, + ServiceProvidersConfigDebugV11, ServiceProvidersConfigV11, ServiceProvidersPathsV11, + StaleMessageDebugV11, VerlocDebugV11, VerlocV11, WireguardPathsV11, + ZkNymTicketHandlerDebugV11, + }; + + pub type WireguardPathsV12 = WireguardPathsV11; + pub type NodeModeV12 = NodeModeV11; + pub type NodeModesV12 = NodeModesV11; + pub type HostV12 = HostV11; + pub type KeyRotationDebugV12 = KeyRotationDebugV11; + pub type KeyRotationV12 = KeyRotationV11; + pub type MixnetDebugV12 = MixnetDebugV11; + pub type MixnetV12 = MixnetV11; + pub type ReplayProtectionV12 = ReplayProtectionV11; + pub type ReplayProtectionPathsV12 = ReplayProtectionPathsV11; + pub type ReplayProtectionDebugV12 = ReplayProtectionDebugV11; + pub type KeysPathsV12 = KeysPathsV11; + pub type NymNodePathsV12 = NymNodePathsV11; + pub type HttpV12 = HttpV11; + pub type VerlocDebugV12 = VerlocDebugV11; + pub type VerlocV12 = VerlocV11; + pub type ZkNymTicketHandlerDebugV12 = ZkNymTicketHandlerDebugV11; + pub type NetworkRequesterPathsV12 = NetworkRequesterPathsV11; + pub type IpPacketRouterPathsV12 = IpPacketRouterPathsV11; + pub type AuthenticatorPathsV12 = AuthenticatorPathsV11; + pub type AuthenticatorV12 = AuthenticatorV11; + pub type AuthenticatorDebugV12 = AuthenticatorDebugV11; + pub type IpPacketRouterDebugV12 = IpPacketRouterDebugV11; + pub type IpPacketRouterV12 = IpPacketRouterV11; + pub type NetworkRequesterDebugV12 = NetworkRequesterDebugV11; + pub type NetworkRequesterV12 = NetworkRequesterV11; + pub type GatewayTasksPathsV12 = GatewayTasksPathsV11; + pub type StaleMessageDebugV12 = StaleMessageDebugV11; + pub type ClientBandwidthDebugV12 = ClientBandwidthDebugV11; + pub type ServiceProvidersPathsV12 = ServiceProvidersPathsV11; + pub type ServiceProvidersConfigDebugV12 = ServiceProvidersConfigDebugV11; + pub type ServiceProvidersConfigV12 = ServiceProvidersConfigV11; + pub type MetricsConfigV12 = MetricsConfigV11; + pub type MetricsDebugV12 = MetricsDebugV11; + pub type LoggingSettingsV12 = LoggingSettingsV11; +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct WireguardV12 { + /// Specifies whether the wireguard service is enabled on this node. + pub enabled: bool, + + /// Socket address this node will use for binding its wireguard interface. + /// default: `[::]:51822` + pub bind_address: SocketAddr, + + /// Private IPv4 address of the wireguard gateway. + /// default: `10.1.0.1` + pub private_ipv4: Ipv4Addr, + + /// Private IPv6 address of the wireguard gateway. + /// default: `fc01::1` + pub private_ipv6: Ipv6Addr, + + /// Tunnel port announced to external clients wishing to connect to the wireguard interface. + /// Useful in the instances where the node is behind a proxy. + pub announced_tunnel_port: u16, + + /// Metadata port announced to external clients wishing to connect to the metadata endpoint. + /// Useful in the instances where the node is behind a proxy. + pub announced_metadata_port: u16, + + /// The prefix denoting the maximum number of the clients that can be connected via Wireguard using IPv4. + /// The maximum value for IPv4 is 32 + pub private_network_prefix_v4: u8, + + /// The prefix denoting the maximum number of the clients that can be connected via Wireguard using IPv6. + /// The maximum value for IPv6 is 128 + pub private_network_prefix_v6: u8, + + /// Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. + /// Useful in containerized environments without kernel WireGuard support. + /// default: `false` + #[serde(default)] + pub use_userspace: bool, + + /// Paths for wireguard keys, client registries, etc. + pub storage_paths: WireguardPathsV12, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct GatewayTasksConfigDebugV12 { + /// Number of messages from offline client that can be pulled at once (i.e. with a single SQL query) from the storage. + pub message_retrieval_limit: i64, + + /// The maximum number of client connections the gateway will keep open at once. + pub maximum_open_connections: usize, + + /// Specifies the minimum performance of mixnodes in the network that are to be used in internal topologies + /// of the services providers + pub minimum_mix_performance: u8, + + /// Defines the timestamp skew of a signed authentication request before it's deemed too excessive to process. + #[serde(alias = "maximum_auth_request_age")] + pub max_request_timestamp_skew: Duration, + + /// The minimum duration since the last explicit check for the upgrade mode to allow creation of new requests. + #[serde(with = "humantime_serde")] + pub upgrade_mode_min_staleness_recheck: Duration, + + pub stale_messages: StaleMessageDebugV12, + + pub client_bandwidth: ClientBandwidthDebugV12, + + pub zk_nym_tickets: ZkNymTicketHandlerDebugV12, +} + +impl GatewayTasksConfigDebugV12 { + pub const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100; + pub const DEFAULT_MINIMUM_MIX_PERFORMANCE: u8 = 50; + pub const DEFAULT_MAXIMUM_AUTH_REQUEST_TIMESTAMP_SKEW: Duration = Duration::from_secs(120); + pub const DEFAULT_MAXIMUM_OPEN_CONNECTIONS: usize = 8192; + pub const DEFAULT_UPGRADE_MODE_MIN_STALENESS_RECHECK: Duration = Duration::from_secs(30); +} + +impl Default for GatewayTasksConfigDebugV12 { + fn default() -> Self { + GatewayTasksConfigDebugV12 { + message_retrieval_limit: Self::DEFAULT_MESSAGE_RETRIEVAL_LIMIT, + maximum_open_connections: Self::DEFAULT_MAXIMUM_OPEN_CONNECTIONS, + max_request_timestamp_skew: Self::DEFAULT_MAXIMUM_AUTH_REQUEST_TIMESTAMP_SKEW, + minimum_mix_performance: Self::DEFAULT_MINIMUM_MIX_PERFORMANCE, + stale_messages: Default::default(), + client_bandwidth: Default::default(), + zk_nym_tickets: Default::default(), + upgrade_mode_min_staleness_recheck: Self::DEFAULT_UPGRADE_MODE_MIN_STALENESS_RECHECK, + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct UpgradeModeWatcherDebugV12 { + /// Default polling interval + #[serde(with = "humantime_serde")] + pub regular_polling_interval: Duration, + + /// Expedited polling interval for once upgrade mode is detected + #[serde(with = "humantime_serde")] + pub expedited_poll_interval: Duration, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpgradeModeWatcherV12 { + /// Specifies whether this gateway watches for upgrade mode changes + /// via the published attestation file. + pub enabled: bool, + + /// Endpoint to query to retrieve current upgrade mode attestation. + pub attestation_url: Url, + + /// Expected public key of the attester providing the upgrade mode attestation + /// on the specified endpoint + #[serde(with = "bs58_ed25519_pubkey")] + pub attester_public_key: ed25519::PublicKey, + + #[serde(default)] + pub debug: UpgradeModeWatcherDebugV12, +} + +impl UpgradeModeWatcherV12 { + pub fn new_mainnet() -> UpgradeModeWatcherV12 { + info!("using mainnet configuration for the upgrade mode:"); + info!("\t- url: {}", mainnet::UPGRADE_MODE_ATTESTATION_URL); + info!( + "\t- attester public key: {}", + mainnet::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY + ); + + // SAFETY: + // our hardcoded values should always be valid + #[allow(clippy::expect_used)] + let attestation_url = mainnet::UPGRADE_MODE_ATTESTATION_URL + .parse() + .expect("invalid default upgrade mode attestation URL"); + + #[allow(clippy::expect_used)] + let attester_public_key = mainnet::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY + .parse() + .expect("invalid default upgrade mode attester public key"); + + UpgradeModeWatcherV12 { + enabled: true, + attestation_url, + attester_public_key, + debug: UpgradeModeWatcherDebugV12::default(), + } + } + + pub fn new() -> Result { + // if env is configured, extract relevant values from there, otherwise fallback to mainnet + if env::var(var_names::CONFIGURED).is_err() { + return Ok(Self::new_mainnet()); + } + + // if env is configured, the relevant values should be set + let Ok(env_attestation_url) = env::var(var_names::UPGRADE_MODE_ATTESTATION_URL) else { + return log_error_and_return(format!( + "'{}' is not set whilst the env is set to be configured", + var_names::UPGRADE_MODE_ATTESTATION_URL + )); + }; + + let Ok(env_attester_pubkey) = + env::var(var_names::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY) + else { + return log_error_and_return(format!( + "'{}' is not set whilst the env is set to be configured", + var_names::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY + )); + }; + + let attestation_url = match env_attestation_url.parse() { + Ok(url) => url, + Err(err) => { + return log_error_and_return(format!( + "provided attestation url {env_attestation_url} is invalid: {err}!" + )); + } + }; + + let attester_public_key = match env_attester_pubkey.parse() { + Ok(public_key) => public_key, + Err(err) => { + return log_error_and_return(format!( + "provided attester public key {env_attester_pubkey} is invalid: {err}!" + )); + } + }; + + Ok(UpgradeModeWatcherV12 { + enabled: true, + attestation_url, + attester_public_key, + debug: UpgradeModeWatcherDebugV12::default(), + }) + } +} + +impl UpgradeModeWatcherDebugV12 { + const DEFAULT_REGULAR_POLLING_INTERVAL: Duration = Duration::from_secs(15 * 60); + const DEFAULT_EXPEDITED_POLL_INTERVAL: Duration = Duration::from_secs(2 * 60); +} + +impl Default for UpgradeModeWatcherDebugV12 { + fn default() -> Self { + UpgradeModeWatcherDebugV12 { + regular_polling_interval: Self::DEFAULT_REGULAR_POLLING_INTERVAL, + expedited_poll_interval: Self::DEFAULT_EXPEDITED_POLL_INTERVAL, + } + } +} + +/// Configuration for LP listener +#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct LpConfigV12 { + pub control_bind_address: SocketAddr, + + pub data_bind_address: SocketAddr, + + #[serde(deserialize_with = "de_maybe_port")] + pub announce_control_port: Option, + + #[serde(deserialize_with = "de_maybe_port")] + pub announce_data_port: Option, + + #[serde(default)] + pub debug: LpDebugV12, +} + +#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct LpDebugV12 { + pub max_connections: usize, + #[serde(with = "humantime_serde")] + pub timestamp_tolerance: Duration, + pub use_mock_ecash: bool, + #[serde(with = "humantime_serde")] + pub handshake_ttl: Duration, + #[serde(with = "humantime_serde")] + pub session_ttl: Duration, + #[serde(with = "humantime_serde")] + pub state_cleanup_interval: Duration, + pub max_concurrent_forwards: usize, +} + +impl LpConfigV12 { + pub const DEFAULT_CONTROL_PORT: u16 = 41264; + pub const DEFAULT_DATA_PORT: u16 = 51264; +} + +impl Default for LpConfigV12 { + fn default() -> Self { + LpConfigV12 { + control_bind_address: SocketAddr::new( + IpAddr::V6(Ipv6Addr::UNSPECIFIED), + Self::DEFAULT_CONTROL_PORT, + ), + data_bind_address: SocketAddr::new( + IpAddr::V6(Ipv6Addr::UNSPECIFIED), + Self::DEFAULT_DATA_PORT, + ), + announce_control_port: None, + announce_data_port: None, + debug: Default::default(), + } + } +} + +impl LpDebugV12 { + pub const DEFAULT_MAX_CONNECTIONS: usize = 10000; + pub const DEFAULT_TIMESTAMP_TOLERANCE: Duration = Duration::from_secs(30); + pub const DEFAULT_HANDSHAKE_TTL: Duration = Duration::from_secs(90); + pub const DEFAULT_SESSION_TTL: Duration = Duration::from_secs(86400); + pub const DEFAULT_STATE_CLEANUP_INTERVAL: Duration = Duration::from_secs(300); + pub const DEFAULT_MAX_CONCURRENT_FORWARDS: usize = 1000; +} + +impl Default for LpDebugV12 { + fn default() -> Self { + LpDebugV12 { + max_connections: Self::DEFAULT_MAX_CONNECTIONS, + timestamp_tolerance: Self::DEFAULT_TIMESTAMP_TOLERANCE, + use_mock_ecash: false, + handshake_ttl: Self::DEFAULT_HANDSHAKE_TTL, + session_ttl: Self::DEFAULT_SESSION_TTL, + state_cleanup_interval: Self::DEFAULT_STATE_CLEANUP_INTERVAL, + max_concurrent_forwards: Self::DEFAULT_MAX_CONCURRENT_FORWARDS, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct GatewayTasksConfigV12 { + pub storage_paths: GatewayTasksPathsV12, + + /// Indicates whether this gateway is accepting only zk-nym credentials for accessing the mixnet + /// or if it also accepts non-paying clients + pub enforce_zk_nyms: bool, + + /// Socket address this node will use for binding its client websocket API. + /// default: `[::]:9000` + pub ws_bind_address: SocketAddr, + + /// Custom announced port for listening for websocket client traffic. + /// If unspecified, the value from the `bind_address` will be used instead + /// default: None + #[serde(deserialize_with = "de_maybe_port")] + pub announce_ws_port: Option, + + /// If applicable, announced port for listening for secure websocket client traffic. + /// (default: None) + #[serde(deserialize_with = "de_maybe_port")] + pub announce_wss_port: Option, + + pub upgrade_mode: UpgradeModeWatcherV12, + + #[serde(default)] + pub lp: LpConfigV12, + + #[serde(default)] + pub debug: GatewayTasksConfigDebugV12, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct DebugV12 { + /// Specifies the time to live of the internal topology provider cache. + #[serde(with = "humantime_serde")] + pub topology_cache_ttl: Duration, + + /// Specifies the time between attempting to resolve any pending unknown nodes in the routing filter + #[serde(with = "humantime_serde")] + pub routing_nodes_check_interval: Duration, + + /// Specifies whether this node runs in testnet mode thus allowing it to route packets on local interfaces + pub testnet: bool, +} + +impl DebugV12 { + pub const DEFAULT_TOPOLOGY_CACHE_TTL: Duration = Duration::from_secs(10 * 60); + pub const DEFAULT_ROUTING_NODES_CHECK_INTERVAL: Duration = Duration::from_secs(5 * 60); +} + +impl Default for DebugV12 { + fn default() -> Self { + DebugV12 { + topology_cache_ttl: Self::DEFAULT_TOPOLOGY_CACHE_TTL, + routing_nodes_check_interval: Self::DEFAULT_ROUTING_NODES_CHECK_INTERVAL, + testnet: false, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigV12 { + // additional metadata holding on-disk location of this config file + #[serde(skip)] + pub(crate) save_path: Option, + + /// Human-readable ID of this particular node. + pub id: String, + + /// Current modes of this nym-node. + pub modes: NodeModesV12, + + pub host: HostV12, + + pub mixnet: MixnetV12, + + /// Storage paths to persistent nym-node data, such as its long term keys. + pub storage_paths: NymNodePathsV12, + + #[serde(default)] + pub http: HttpV12, + + #[serde(default)] + pub verloc: VerlocV12, + + pub wireguard: WireguardV12, + + #[serde(alias = "entry_gateway")] + pub gateway_tasks: GatewayTasksConfigV12, + + #[serde(alias = "exit_gateway")] + pub service_providers: ServiceProvidersConfigV12, + + #[serde(default)] + pub metrics: MetricsConfigV12, + + #[serde(default)] + pub logging: LoggingSettingsV12, + + #[serde(default)] + pub debug: DebugV12, +} + +impl ConfigV12 { + // simple wrapper that reads config file and assigns path location + fn read_from_path>(path: P) -> Result { + let path = path.as_ref(); + let mut loaded: ConfigV12 = + read_config_from_toml_file(path).map_err(|source| NymNodeError::ConfigLoadFailure { + path: path.to_path_buf(), + source, + })?; + loaded.save_path = Some(path.to_path_buf()); + debug!("loaded config file from {}", path.display()); + Ok(loaded) + } +} + +#[instrument(skip_all)] +pub async fn try_upgrade_config_v12>( + path: P, + prev_config: Option, +) -> Result { + debug!("attempting to load v12 config..."); + + let old_cfg = if let Some(prev_config) = prev_config { + prev_config + } else { + ConfigV12::read_from_path(&path)? + }; + + info!("migrating the old config (v12)..."); + let keys_dir = old_cfg + .storage_paths + .keys + .public_ed25519_identity_key_file + .parent() + .ok_or(NymNodeError::DataDirDerivationFailure)? + .to_path_buf(); + + let updated_keys = KeysPaths { + private_ed25519_identity_key_file: old_cfg + .storage_paths + .keys + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .storage_paths + .keys + .public_ed25519_identity_key_file, + primary_x25519_sphinx_key_file: old_cfg.storage_paths.keys.primary_x25519_sphinx_key_file, + secondary_x25519_sphinx_key_file: old_cfg + .storage_paths + .keys + .secondary_x25519_sphinx_key_file, + private_x25519_noise_key_file: old_cfg.storage_paths.keys.private_x25519_noise_key_file, + public_x25519_noise_key_file: old_cfg.storage_paths.keys.public_x25519_noise_key_file, + private_x25519_lp_key_file: keys_dir.join(DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME), + public_x25519_lp_key_file: keys_dir.join(DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME), + private_mlkem768_lp_key_file: keys_dir.join(DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME), + public_mlkem768_lp_key_file: keys_dir.join(DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME), + private_mceliece_lp_key_file: keys_dir.join(DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME), + public_mceliece_lp_key_file: keys_dir.join(DEFAULT_MCELIECE_PUBLIC_KEY_FILENAME), + }; + + let mut rng = rand09::rngs::StdRng::from_os_rng(); + + // generate new keys for LP + info!("generating new LP x25519 DH keypair"); + let x25519 = generate_lp_keypair_x25519(&mut rng); + let paths = updated_keys.x25519_lp_key_paths(); + store_x25519_lp_keypair(&x25519, &paths)?; + + info!("generating new mlkem768 keypair"); + let mlkem = generate_keypair_mlkem(&mut rng); + let paths = updated_keys.mlkem768_key_paths(); + store_mlkem768_keypair(&mlkem, &paths)?; + + info!("generating new mceliece keypair (this might take a while)"); + let mceliece = generate_keypair_mceliece(&mut rng); + let paths = updated_keys.mceliece_key_paths(); + store_mceliece_keypair(&mceliece, &paths)?; + + let cfg = Config { + save_path: old_cfg.save_path, + id: old_cfg.id, + modes: NodeModes { + mixnode: old_cfg.modes.mixnode, + entry: old_cfg.modes.entry, + exit: old_cfg.modes.exit, + }, + host: Host { + public_ips: old_cfg.host.public_ips, + hostname: old_cfg.host.hostname, + location: old_cfg.host.location, + }, + mixnet: Mixnet { + bind_address: old_cfg.mixnet.bind_address, + announce_port: old_cfg.mixnet.announce_port, + nym_api_urls: old_cfg.mixnet.nym_api_urls, + nyxd_urls: old_cfg.mixnet.nyxd_urls, + replay_protection: ReplayProtection { + storage_paths: ReplayProtectionPaths { + current_bloomfilters_directory: old_cfg + .mixnet + .replay_protection + .storage_paths + .current_bloomfilters_directory, + }, + debug: ReplayProtectionDebug { + unsafe_disabled: old_cfg.mixnet.replay_protection.debug.unsafe_disabled, + maximum_replay_detection_deferral: old_cfg + .mixnet + .replay_protection + .debug + .maximum_replay_detection_deferral, + maximum_replay_detection_pending_packets: old_cfg + .mixnet + .replay_protection + .debug + .maximum_replay_detection_pending_packets, + false_positive_rate: old_cfg.mixnet.replay_protection.debug.false_positive_rate, + initial_expected_packets_per_second: old_cfg + .mixnet + .replay_protection + .debug + .initial_expected_packets_per_second, + bloomfilter_minimum_packets_per_second_size: old_cfg + .mixnet + .replay_protection + .debug + .bloomfilter_minimum_packets_per_second_size, + bloomfilter_size_multiplier: old_cfg + .mixnet + .replay_protection + .debug + .bloomfilter_size_multiplier, + bloomfilter_disk_flushing_rate: old_cfg + .mixnet + .replay_protection + .debug + .bloomfilter_disk_flushing_rate, + }, + }, + key_rotation: KeyRotation { + debug: KeyRotationDebug { + rotation_state_poling_interval: old_cfg + .mixnet + .key_rotation + .debug + .rotation_state_poling_interval, + }, + }, + debug: MixnetDebug { + maximum_forward_packet_delay: old_cfg.mixnet.debug.maximum_forward_packet_delay, + packet_forwarding_initial_backoff: old_cfg + .mixnet + .debug + .packet_forwarding_initial_backoff, + packet_forwarding_maximum_backoff: old_cfg + .mixnet + .debug + .packet_forwarding_maximum_backoff, + initial_connection_timeout: old_cfg.mixnet.debug.initial_connection_timeout, + maximum_connection_buffer_size: old_cfg.mixnet.debug.maximum_connection_buffer_size, + unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise, + use_legacy_packet_encoding: old_cfg.mixnet.debug.use_legacy_packet_encoding, + }, + }, + storage_paths: NymNodePaths { + keys: updated_keys, + description: old_cfg.storage_paths.description, + }, + http: Http { + bind_address: old_cfg.http.bind_address, + landing_page_assets_path: old_cfg.http.landing_page_assets_path, + access_token: old_cfg.http.access_token, + expose_system_info: old_cfg.http.expose_system_info, + expose_system_hardware: old_cfg.http.expose_system_hardware, + expose_crypto_hardware: old_cfg.http.expose_crypto_hardware, + node_load_cache_ttl: old_cfg.http.node_load_cache_ttl, + }, + verloc: Verloc { + bind_address: old_cfg.verloc.bind_address, + announce_port: old_cfg.verloc.announce_port, + debug: VerlocDebug { + packets_per_node: old_cfg.verloc.debug.packets_per_node, + connection_timeout: old_cfg.verloc.debug.connection_timeout, + packet_timeout: old_cfg.verloc.debug.packet_timeout, + delay_between_packets: old_cfg.verloc.debug.delay_between_packets, + tested_nodes_batch_size: old_cfg.verloc.debug.tested_nodes_batch_size, + testing_interval: old_cfg.verloc.debug.testing_interval, + retry_timeout: old_cfg.verloc.debug.retry_timeout, + }, + }, + wireguard: Wireguard { + enabled: old_cfg.wireguard.enabled, + bind_address: old_cfg.wireguard.bind_address, + private_ipv4: old_cfg.wireguard.private_ipv4, + private_ipv6: old_cfg.wireguard.private_ipv6, + announced_tunnel_port: old_cfg.wireguard.announced_tunnel_port, + announced_metadata_port: old_cfg.wireguard.announced_metadata_port, + private_network_prefix_v4: old_cfg.wireguard.private_network_prefix_v4, + private_network_prefix_v6: old_cfg.wireguard.private_network_prefix_v6, + use_userspace: old_cfg.wireguard.use_userspace, + storage_paths: WireguardPaths { + private_diffie_hellman_key_file: old_cfg + .wireguard + .storage_paths + .private_diffie_hellman_key_file, + public_diffie_hellman_key_file: old_cfg + .wireguard + .storage_paths + .public_diffie_hellman_key_file, + }, + }, + lp: LpConfig { + control_bind_address: old_cfg.gateway_tasks.lp.control_bind_address, + data_bind_address: old_cfg.gateway_tasks.lp.data_bind_address, + announce_control_port: old_cfg.gateway_tasks.lp.announce_control_port, + announce_data_port: old_cfg.gateway_tasks.lp.announce_data_port, + debug: LpDebug { + max_connections: old_cfg.gateway_tasks.lp.debug.max_connections, + use_mock_ecash: old_cfg.gateway_tasks.lp.debug.use_mock_ecash, + handshake_ttl: old_cfg.gateway_tasks.lp.debug.handshake_ttl, + session_ttl: old_cfg.gateway_tasks.lp.debug.session_ttl, + state_cleanup_interval: old_cfg.gateway_tasks.lp.debug.state_cleanup_interval, + max_concurrent_forwards: old_cfg.gateway_tasks.lp.debug.max_concurrent_forwards, + }, + }, + gateway_tasks: GatewayTasksConfig { + storage_paths: GatewayTasksPaths { + clients_storage: old_cfg.gateway_tasks.storage_paths.clients_storage, + stats_storage: old_cfg.gateway_tasks.storage_paths.stats_storage, + cosmos_mnemonic: old_cfg.gateway_tasks.storage_paths.cosmos_mnemonic, + bridge_client_params: old_cfg.gateway_tasks.storage_paths.bridge_client_params, + }, + enforce_zk_nyms: old_cfg.gateway_tasks.enforce_zk_nyms, + ws_bind_address: old_cfg.gateway_tasks.ws_bind_address, + announce_ws_port: old_cfg.gateway_tasks.announce_ws_port, + announce_wss_port: old_cfg.gateway_tasks.announce_wss_port, + upgrade_mode: UpgradeModeWatcher { + enabled: old_cfg.gateway_tasks.upgrade_mode.enabled, + attestation_url: old_cfg.gateway_tasks.upgrade_mode.attestation_url, + attester_public_key: old_cfg.gateway_tasks.upgrade_mode.attester_public_key, + debug: UpgradeModeWatcherDebug { + regular_polling_interval: old_cfg + .gateway_tasks + .upgrade_mode + .debug + .regular_polling_interval, + expedited_poll_interval: old_cfg + .gateway_tasks + .upgrade_mode + .debug + .expedited_poll_interval, + }, + }, + debug: gateway_tasks::Debug { + message_retrieval_limit: old_cfg.gateway_tasks.debug.message_retrieval_limit, + maximum_open_connections: old_cfg.gateway_tasks.debug.maximum_open_connections, + minimum_mix_performance: old_cfg.gateway_tasks.debug.minimum_mix_performance, + max_request_timestamp_skew: old_cfg.gateway_tasks.debug.max_request_timestamp_skew, + stale_messages: StaleMessageDebug { + cleaner_run_interval: old_cfg + .gateway_tasks + .debug + .stale_messages + .cleaner_run_interval, + max_age: old_cfg.gateway_tasks.debug.stale_messages.max_age, + }, + client_bandwidth: ClientBandwidthDebug { + max_flushing_rate: old_cfg + .gateway_tasks + .debug + .client_bandwidth + .max_flushing_rate, + max_delta_flushing_amount: old_cfg + .gateway_tasks + .debug + .client_bandwidth + .max_delta_flushing_amount, + }, + zk_nym_tickets: ZkNymTicketHandlerDebug { + revocation_bandwidth_penalty: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .revocation_bandwidth_penalty, + pending_poller: old_cfg.gateway_tasks.debug.zk_nym_tickets.pending_poller, + minimum_api_quorum: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .minimum_api_quorum, + minimum_redemption_tickets: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .minimum_redemption_tickets, + maximum_time_between_redemption: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .maximum_time_between_redemption, + }, + upgrade_mode_min_staleness_recheck: old_cfg + .gateway_tasks + .debug + .upgrade_mode_min_staleness_recheck, + }, + }, + service_providers: ServiceProvidersConfig { + storage_paths: ServiceProvidersPaths { + clients_storage: old_cfg.service_providers.storage_paths.clients_storage, + stats_storage: old_cfg.service_providers.storage_paths.stats_storage, + network_requester: NetworkRequesterPaths { + private_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .ack_key_file, + reply_surb_database: old_cfg + .service_providers + .storage_paths + .network_requester + .reply_surb_database, + gateway_registrations: old_cfg + .service_providers + .storage_paths + .network_requester + .gateway_registrations, + }, + ip_packet_router: IpPacketRouterPaths { + private_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .ack_key_file, + reply_surb_database: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .reply_surb_database, + gateway_registrations: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .gateway_registrations, + }, + authenticator: AuthenticatorPaths { + private_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .ack_key_file, + reply_surb_database: old_cfg + .service_providers + .storage_paths + .authenticator + .reply_surb_database, + gateway_registrations: old_cfg + .service_providers + .storage_paths + .authenticator + .gateway_registrations, + }, + }, + open_proxy: old_cfg.service_providers.open_proxy, + upstream_exit_policy_url: old_cfg.service_providers.upstream_exit_policy_url, + network_requester: NetworkRequester { + debug: NetworkRequesterDebug { + enabled: old_cfg.service_providers.network_requester.debug.enabled, + disable_poisson_rate: old_cfg + .service_providers + .network_requester + .debug + .disable_poisson_rate, + client_debug: old_cfg + .service_providers + .network_requester + .debug + .client_debug, + }, + }, + ip_packet_router: IpPacketRouter { + debug: IpPacketRouterDebug { + enabled: old_cfg.service_providers.ip_packet_router.debug.enabled, + disable_poisson_rate: old_cfg + .service_providers + .ip_packet_router + .debug + .disable_poisson_rate, + client_debug: old_cfg + .service_providers + .ip_packet_router + .debug + .client_debug, + }, + }, + authenticator: Authenticator { + debug: AuthenticatorDebug { + enabled: old_cfg.service_providers.authenticator.debug.enabled, + disable_poisson_rate: old_cfg + .service_providers + .authenticator + .debug + .disable_poisson_rate, + client_debug: old_cfg.service_providers.authenticator.debug.client_debug, + }, + }, + debug: service_providers::Debug { + message_retrieval_limit: old_cfg.service_providers.debug.message_retrieval_limit, + }, + }, + metrics: MetricsConfig { + debug: metrics::Debug { + log_stats_to_console: false, + aggregator_update_rate: Default::default(), + stale_mixnet_metrics_cleaner_rate: Default::default(), + global_prometheus_counters_update_rate: Default::default(), + pending_egress_packets_update_rate: Default::default(), + clients_sessions_update_rate: Default::default(), + console_logging_update_interval: Default::default(), + legacy_mixing_metrics_update_rate: Default::default(), + }, + }, + logging: LoggingSettings {}, + debug: Debug { + topology_cache_ttl: old_cfg.debug.topology_cache_ttl, + routing_nodes_check_interval: old_cfg.debug.routing_nodes_check_interval, + testnet: old_cfg.debug.testnet, + }, + }; + Ok(cfg) +} diff --git a/nym-node/src/config/persistence.rs b/nym-node/src/config/persistence.rs index 332fbf2c1d..4d0a5c645c 100644 --- a/nym-node/src/config/persistence.rs +++ b/nym-node/src/config/persistence.rs @@ -19,6 +19,14 @@ pub const DEFAULT_X25519_PRIVATE_NOISE_KEY_FILENAME: &str = "x25519_noise"; pub const DEFAULT_X25519_PUBLIC_NOISE_KEY_FILENAME: &str = "x25519_noise.pub"; pub const DEFAULT_NYMNODE_DESCRIPTION_FILENAME: &str = "description.toml"; +// Global/LP: +pub const DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME: &str = "x25519_lp"; +pub const DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME: &str = "x25519_lp.pub"; +pub const DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME: &str = "mlkem768"; +pub const DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME: &str = "mlkem768.pub"; +pub const DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME: &str = "mceliece"; +pub const DEFAULT_MCELIECE_PUBLIC_KEY_FILENAME: &str = "mceliece.pub"; + // Mixnode: // Entry Gateway: @@ -99,6 +107,26 @@ pub struct KeysPaths { /// Path to file containing x25519 noise public key. pub public_x25519_noise_key_file: PathBuf, + + // >> LP KEYS START: + /// Path to file containing x25519 lp private key. + pub private_x25519_lp_key_file: PathBuf, + + /// Path to file containing x25519 lp public key. + pub public_x25519_lp_key_file: PathBuf, + + /// Path to file containing mlkem768 lp private key. + pub private_mlkem768_lp_key_file: PathBuf, + + /// Path to file containing mlkem768 lp public key. + pub public_mlkem768_lp_key_file: PathBuf, + + /// Path to file containing mceliece lp private key. + pub private_mceliece_lp_key_file: PathBuf, + + /// Path to file containing mceliece lp public key. + pub public_mceliece_lp_key_file: PathBuf, + // >> LP KEYS END } impl KeysPaths { @@ -116,6 +144,12 @@ impl KeysPaths { .join(DEFAULT_SECONDARY_X25519_SPHINX_KEY_FILENAME), private_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PRIVATE_NOISE_KEY_FILENAME), public_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_NOISE_KEY_FILENAME), + private_x25519_lp_key_file: data_dir.join(DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME), + public_x25519_lp_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME), + private_mlkem768_lp_key_file: data_dir.join(DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME), + public_mlkem768_lp_key_file: data_dir.join(DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME), + private_mceliece_lp_key_file: data_dir.join(DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME), + public_mceliece_lp_key_file: data_dir.join(DEFAULT_MCELIECE_PUBLIC_KEY_FILENAME), } } @@ -132,6 +166,27 @@ impl KeysPaths { &self.public_x25519_noise_key_file, ) } + + pub fn x25519_lp_key_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_x25519_lp_key_file, + &self.public_x25519_lp_key_file, + ) + } + + pub fn mlkem768_key_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_mlkem768_lp_key_file, + &self.public_mlkem768_lp_key_file, + ) + } + + pub fn mceliece_key_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_mceliece_lp_key_file, + &self.public_mceliece_lp_key_file, + ) + } } #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] diff --git a/nym-node/src/config/template.rs b/nym-node/src/config/template.rs index 8177119780..6b5c1e39b7 100644 --- a/nym-node/src/config/template.rs +++ b/nym-node/src/config/template.rs @@ -96,6 +96,23 @@ private_x25519_noise_key_file = '{{ storage_paths.keys.private_x25519_noise_key_ # Path to file containing x25519 noise public key. public_x25519_noise_key_file = '{{ storage_paths.keys.public_x25519_noise_key_file }}' +# Path to file containing x25519 lp private key. +private_x25519_lp_key_file = '{{ storage_paths.keys.private_x25519_lp_key_file }}' + +# Path to file containing x25519 lp public key. +public_x25519_lp_key_file = '{{ storage_paths.keys.public_x25519_lp_key_file }}' + +# Path to file containing mlkem768 lp private key. +private_mlkem768_lp_key_file = '{{ storage_paths.keys.private_mlkem768_lp_key_file }}' + +# Path to file containing mlkem768 lp public key. +public_mlkem768_lp_key_file = '{{ storage_paths.keys.public_mlkem768_lp_key_file }}' + +# Path to file containing mceliece lp private key. +private_mceliece_lp_key_file = '{{ storage_paths.keys.private_mceliece_lp_key_file }}' + +# Path to file containing mceliece lp public key. +public_mceliece_lp_key_file = '{{ storage_paths.keys.public_mceliece_lp_key_file }}' ##### http-API nym-node config options ##### @@ -166,6 +183,29 @@ private_diffie_hellman_key_file = '{{ wireguard.storage_paths.private_diffie_hel # Path to file containing wireguard x25519 diffie hellman public key. public_diffie_hellman_key_file = '{{ wireguard.storage_paths.public_diffie_hellman_key_file }}' +##### Lewes Protocol config options ##### + +[lp] +# Bind address for the TCP LP control traffic. +# default: `[::]:41264` +control_bind_address = '{{ lp.control_bind_address }}' + +# Bind address for the UDP LP data traffic. +# default: `[::]:51264` +data_bind_address = '{{ lp.data_bind_address }}' + +# Custom announced port for listening for the TCP LP control traffic. +# If unspecified, the value from the `control_bind_address` will be used instead +# Useful when the node is behind a proxy. +# (default: 0 - disabled) +announce_control_port ={{#if lp.announce_control_port }} {{ lp.announce_control_port }} {{else}} 0 {{/if}} + +# Custom announced port for listening for the UDP LP data traffic. +# If unspecified, the value from the `data_bind_address` will be used instead +# Useful when the node is behind a proxy. +# (default: 0 - disabled) +announce_data_port ={{#if lp.announce_data_port }} {{ lp.announce_data_port }} {{else}} 0 {{/if}} + ##### verloc config options ##### diff --git a/nym-node/src/config/upgrade_helpers.rs b/nym-node/src/config/upgrade_helpers.rs index b2b8018231..8c556b409d 100644 --- a/nym-node/src/config/upgrade_helpers.rs +++ b/nym-node/src/config/upgrade_helpers.rs @@ -18,7 +18,8 @@ async fn try_upgrade_config(path: &Path) -> Result<(), NymNodeError> { let cfg = try_upgrade_config_v8(path, cfg).await.ok(); let cfg = try_upgrade_config_v9(path, cfg).await.ok(); let cfg = try_upgrade_config_v10(path, cfg).await.ok(); - match try_upgrade_config_v11(path, cfg).await { + let cfg = try_upgrade_config_v11(path, cfg).await.ok(); + match try_upgrade_config_v12(path, cfg).await { Ok(cfg) => cfg.save(), Err(e) => { tracing::error!("Failed to finish upgrade: {e}"); diff --git a/nym-node/src/env.rs b/nym-node/src/env.rs index 318f96999f..01f8a7113d 100644 --- a/nym-node/src/env.rs +++ b/nym-node/src/env.rs @@ -66,9 +66,15 @@ pub mod vars { "NYMNODE_UPGRADE_MODE_ATTESTATION_URL"; pub const NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY_ARG: &str = "NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY"; - pub const NYMNODE_LP_USE_MOCK_ECASH_ARG: &str = "NYMNODE_LP_USE_MOCK_ECASH"; // exit gateway: pub const NYMNODE_UPSTREAM_EXIT_POLICY_ARG: &str = "NYMNODE_UPSTREAM_EXIT_POLICY"; pub const NYMNODE_OPEN_PROXY_ARG: &str = "NYMNODE_OPEN_PROXY"; + + // LP: + pub const NYMNODE_LP_CONTROL_BIND_ADDRESS_ARG: &str = "NYMNODE_LP_CONTROL_BIND_ADDRESS"; + pub const NYMNODE_LP_CONTROL_ANNOUNCE_PORT_ARG: &str = "NYMNODE_LP_CONTROL_ANNOUNCE_PORT"; + pub const NYMNODE_LP_DATA_BIND_ADDRESS_ARG: &str = "NYMNODE_LP_DATA_BIND_ADDRESS"; + pub const NYMNODE_LP_DATA_ANNOUNCE_PORT_ARG: &str = "NYMNODE_LP_DATA_ANNOUNCE_PORT"; + pub const NYMNODE_LP_USE_MOCK_ECASH_ARG: &str = "NYMNODE_LP_USE_MOCK_ECASH"; } diff --git a/nym-node/src/error.rs b/nym-node/src/error.rs index abe336dc44..f1328c61c9 100644 --- a/nym-node/src/error.rs +++ b/nym-node/src/error.rs @@ -2,13 +2,15 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::node::http::error::NymNodeHttpError; +use crate::node::lp::error::LpHandlerError; use crate::wireguard::error::WireguardError; use nym_http_api_client::HttpClientError; use nym_ip_packet_router::error::ClientCoreError; +use nym_kkt::keys::storage_wrappers::MalformedStoredKeyError; use nym_validator_client::ValidatorClientError; use nym_validator_client::nyxd::error::NyxdError; use std::io; -use std::net::IpAddr; +use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; use thiserror::Error; @@ -231,6 +233,18 @@ pub enum NymNodeError { #[error("failed upgrade")] FailedUpgrade, + + #[error(transparent)] + MalformedStoredKey(#[from] MalformedStoredKeyError), + + #[error("failed to bind LP to {address}: {source}")] + LpBindFailure { + address: SocketAddr, + source: io::Error, + }, + + #[error(transparent)] + LpFailure(#[from] LpHandlerError), } impl From for NymNodeError { diff --git a/nym-node/src/lib.rs b/nym-node/src/lib.rs new file mode 100644 index 0000000000..9882b20cb7 --- /dev/null +++ b/nym-node/src/lib.rs @@ -0,0 +1,10 @@ +// Copyright 2026 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +// allow dead code in the lib imports +#![allow(dead_code)] + +pub mod config; +pub mod error; +pub mod node; +pub mod wireguard; diff --git a/nym-node/src/main.rs b/nym-node/src/main.rs index 8c7a8aa2e2..3b83a1ee96 100644 --- a/nym-node/src/main.rs +++ b/nym-node/src/main.rs @@ -10,13 +10,13 @@ use nym_bin_common::logging::maybe_print_banner; use nym_config::defaults::setup_env; mod cli; -pub(crate) mod config; +pub mod config; mod env; -pub(crate) mod error; +pub mod error; mod logging; -pub(crate) mod node; +pub mod node; pub(crate) mod throughput_tester; -pub(crate) mod wireguard; +pub mod wireguard; fn main() -> anyhow::Result<()> { // std::env::set_var( diff --git a/nym-node/src/node/helpers.rs b/nym-node/src/node/helpers.rs index b0112a7004..dfe41d67ad 100644 --- a/nym-node/src/node/helpers.rs +++ b/nym-node/src/node/helpers.rs @@ -6,6 +6,10 @@ use crate::error::{KeyIOFailure, NymNodeError}; use crate::node::key_rotation::key::{SphinxPrivateKey, SphinxPublicKey}; use crate::node::nym_apis_client::NymApisClient; use nym_crypto::asymmetric::{ed25519, x25519}; +use nym_kkt::keys::storage_wrappers::StorableKey; +use nym_kkt::keys::{ + DHKeyPair, DHPrivateKey, MlKem768KeyPair, MlKem768PrivateKey, MlKem768PublicKey, mceliece, +}; use nym_node_requests::api::v1::node::models::NodeDescription; use nym_pemstore::KeyPairPath; use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair}; @@ -169,6 +173,34 @@ pub(crate) fn load_x25519_wireguard_keypair( Ok(load_keypair(paths, "x25519-wireguard")?) } +fn load_lp_key(path: P, name: &'static str) -> Result +where + P: AsRef, + S: StorableKey, +{ + let repr = load_key::<::StorableRepresentation<'_>, _>(path, name)?; + Ok(S::from_storable(repr)?) +} + +pub(crate) fn load_x25519_lp_keypair(paths: &KeyPairPath) -> Result { + let pk: DHPrivateKey = load_lp_key(&paths.private_key_path, "x25519-lp-private-key")?; + Ok(DHKeyPair::from(pk)) +} + +pub(crate) fn load_mlkem768_keypair(paths: &KeyPairPath) -> Result { + let sk: MlKem768PrivateKey = load_lp_key(&paths.private_key_path, "mlkem768-private-key")?; + let pk: MlKem768PublicKey = load_lp_key(&paths.public_key_path, "mlkem768-public-key")?; + Ok(MlKem768KeyPair::from(sk, pk)) +} + +pub(crate) fn load_mceliece_keypair( + paths: &KeyPairPath, +) -> Result { + let sk: mceliece::SecretKey = load_lp_key(&paths.private_key_path, "mceliece-private-key")?; + let pk: mceliece::PublicKey = load_lp_key(&paths.public_key_path, "mceliece-public-key")?; + Ok(mceliece::KeyPair { sk, pk }) +} + pub(crate) fn store_ed25519_identity_keypair( keys: &ed25519::KeyPair, paths: &KeyPairPath, @@ -183,6 +215,57 @@ pub(crate) fn store_x25519_noise_keypair( Ok(store_keypair(keys, paths, "x25519-noise")?) } +pub(crate) fn store_x25519_lp_keypair( + keys: &DHKeyPair, + paths: &KeyPairPath, +) -> Result<(), NymNodeError> { + store_key( + &keys.pk.to_storable(), + &paths.public_key_path, + "x25519-lp-public-key", + )?; + store_key( + &keys.sk().to_storable(), + &paths.private_key_path, + "x25519-lp-private-key", + )?; + Ok(()) +} + +pub(crate) fn store_mlkem768_keypair( + keys: &MlKem768KeyPair, + paths: &KeyPairPath, +) -> Result<(), NymNodeError> { + store_key( + &keys.public_key().to_storable(), + &paths.public_key_path, + "mlkem768-public-key", + )?; + store_key( + &keys.private_key().to_storable(), + &paths.private_key_path, + "mlkem768-private-key", + )?; + Ok(()) +} + +pub(crate) fn store_mceliece_keypair( + keys: &mceliece::KeyPair, + paths: &KeyPairPath, +) -> Result<(), NymNodeError> { + store_key( + &keys.pk.to_storable(), + &paths.public_key_path, + "mceliece-public-key", + )?; + store_key( + &keys.sk.to_storable(), + &paths.private_key_path, + "mceliece-private-key", + )?; + Ok(()) +} + pub(crate) async fn get_current_rotation_id( nym_apis: &[Url], fallback_nyxd: &[Url], diff --git a/nym-node/src/node/http/router/api/v1/lewes_protocol/mod.rs b/nym-node/src/node/http/router/api/v1/lewes_protocol/mod.rs index 36d8dc785d..6e0aa38e58 100644 --- a/nym-node/src/node/http/router/api/v1/lewes_protocol/mod.rs +++ b/nym-node/src/node/http/router/api/v1/lewes_protocol/mod.rs @@ -3,13 +3,13 @@ use axum::Router; use axum::routing::get; -use nym_node_requests::api::v1::lewes_protocol::models; +use nym_node_requests::api::SignedLewesProtocol; pub mod root; -#[derive(Debug, Default, Clone)] +#[derive(Debug, Clone)] pub struct Config { - pub details: Option, + pub details: SignedLewesProtocol, } pub(crate) fn routes(config: Config) -> Router { diff --git a/nym-node/src/node/http/router/api/v1/lewes_protocol/root.rs b/nym-node/src/node/http/router/api/v1/lewes_protocol/root.rs index b056db5a90..06b2961717 100644 --- a/nym-node/src/node/http/router/api/v1/lewes_protocol/root.rs +++ b/nym-node/src/node/http/router/api/v1/lewes_protocol/root.rs @@ -4,30 +4,29 @@ use axum::extract::Query; use axum::http::StatusCode; use nym_http_api_common::{FormattedResponse, OutputParams}; -use nym_node_requests::api::v1::lewes_protocol::models::LewesProtocol; +use nym_node_requests::api::{SignedLewesProtocol, SignedLewesProtocolInfo}; /// Returns root Lewes Protocol information #[utoipa::path( get, - path = "", - context_path = "/api/v1/lewes-protocol", + path = "/lewes-protocol", + context_path = "/api/v1", tag = "Lewes Protocol", responses( (status = 501, description = "the endpoint hasn't been implemented yet"), (status = 200, content( - (LewesProtocol = "application/json"), - (LewesProtocol = "application/yaml"), - (LewesProtocol = "application/bincode") + (SignedLewesProtocolInfo = "application/json"), + (SignedLewesProtocolInfo = "application/yaml"), + (SignedLewesProtocolInfo = "application/bincode") )) ), params(OutputParams) )] pub(crate) async fn root_lewes_protocol( - config: Option, + config: SignedLewesProtocol, Query(output): Query, ) -> Result { - let config = config.ok_or(StatusCode::NOT_IMPLEMENTED)?; Ok(output.to_response(config)) } -pub type LewesProtocolResponse = FormattedResponse; +pub type LewesProtocolResponse = FormattedResponse; diff --git a/nym-node/src/node/http/router/api/v1/node/host_information.rs b/nym-node/src/node/http/router/api/v1/node/host_information.rs index 53427f0cdf..8e828f8590 100644 --- a/nym-node/src/node/http/router/api/v1/node/host_information.rs +++ b/nym-node/src/node/http/router/api/v1/node/host_information.rs @@ -16,7 +16,8 @@ use nym_node_requests::api::{SignedDataHostInfo, v1::node::models::SignedHostInf responses( (status = 200, content( (SignedDataHostInfo = "application/json"), - (SignedDataHostInfo = "application/yaml") + (SignedDataHostInfo = "application/yaml"), + (SignedDataHostInfo = "application/bincode") )) ), params(OutputParams) diff --git a/nym-node/src/node/http/router/mod.rs b/nym-node/src/node/http/router/mod.rs index dbaee0e8d5..4c3618c76f 100644 --- a/nym-node/src/node/http/router/mod.rs +++ b/nym-node/src/node/http/router/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::node::http::NymNodeHttpServer; +use crate::node::http::api::v1::lewes_protocol; use crate::node::http::error::NymNodeHttpError; use crate::node::http::state::AppState; use axum::Router; @@ -9,6 +10,7 @@ use axum::response::Redirect; use axum::routing::get; use nym_bin_common::bin_info_owned; use nym_http_api_common::middleware::logging; +use nym_node_requests::api::SignedLewesProtocol; use nym_node_requests::api::v1::authenticator::models::Authenticator; use nym_node_requests::api::v1::gateway::models::{Bridges, Gateway}; use nym_node_requests::api::v1::ip_packet_router::models::IpPacketRouter; @@ -33,7 +35,7 @@ pub struct HttpServerConfig { } impl HttpServerConfig { - pub fn new() -> Self { + pub fn new(signed_lewes_protocol: SignedLewesProtocol) -> Self { HttpServerConfig { landing: Default::default(), api: api::Config { @@ -52,7 +54,9 @@ impl HttpServerConfig { network_requester: Default::default(), ip_packet_router: Default::default(), authenticator: Default::default(), - lewes_protocol: Default::default(), + lewes_protocol: lewes_protocol::Config { + details: signed_lewes_protocol, + }, }, }, } diff --git a/nym-node/src/node/key_rotation/key.rs b/nym-node/src/node/key_rotation/key.rs index 7d31455ae5..350cae8a7f 100644 --- a/nym-node/src/node/key_rotation/key.rs +++ b/nym-node/src/node/key_rotation/key.rs @@ -113,14 +113,11 @@ impl PemStorableKey for SphinxPrivateKey { #[cfg(test)] mod tests { use super::*; - use rand::SeedableRng; - use rand_chacha::ChaCha20Rng; + use nym_test_utils::helpers::deterministic_rng; #[test] fn private_key_bytes_convertion() { - // Set up a deterministic RNG. - let seed = [42u8; 32]; - let mut rng = ChaCha20Rng::from_seed(seed); + let mut rng = deterministic_rng(); let key = SphinxPrivateKey { rotation_id: 42, diff --git a/nym-node/src/node/lp/data_handler.rs b/nym-node/src/node/lp/data_handler.rs new file mode 100644 index 0000000000..9c842fd159 --- /dev/null +++ b/nym-node/src/node/lp/data_handler.rs @@ -0,0 +1,237 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +//! LP Data Handler - UDP listener for LP data plane (port 51264) +//! +//! This module handles the data plane for LP clients that have completed registration +//! via the control plane (TCP:41264). LP-wrapped Sphinx packets arrive here, get +//! decrypted, and are forwarded into the mixnet. +//! +//! # Packet Flow +//! +//! ```text +//! LP Client → UDP:51264 → LP Data Handler → Mixnet Entry +//! LP(Sphinx) decrypt LP forward Sphinx +//! ``` +//! + +use super::LpHandlerState; +use crate::error::NymNodeError; +use crate::node::lp::error::LpHandlerError; +use nym_lp::packet::OuterHeader; +use nym_metrics::inc; +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::net::UdpSocket; +use tracing::*; + +/// Maximum UDP packet size we'll accept +/// Sphinx packets are typically ~2KB, LP overhead is ~50 bytes, so 4KB is plenty +const MAX_UDP_PACKET_SIZE: usize = 4096; + +/// LP Data Handler for UDP data plane +pub struct LpDataHandler { + /// UDP socket for receiving LP-wrapped Sphinx packets + socket: Arc, + + /// Shared state with TCP control plane + #[allow(dead_code)] + state: LpHandlerState, + + /// Shutdown token + shutdown: nym_task::ShutdownToken, +} + +impl LpDataHandler { + /// Create a new LP data handler + pub async fn new( + bind_addr: SocketAddr, + state: LpHandlerState, + shutdown: nym_task::ShutdownToken, + ) -> Result { + let socket = UdpSocket::bind(bind_addr).await.map_err(|source| { + error!("Failed to bind LP data socket to {bind_addr}: {source}"); + NymNodeError::LpBindFailure { + address: bind_addr, + source, + } + })?; + + info!("LP data handler listening on UDP {bind_addr}"); + + Ok(Self { + socket: Arc::new(socket), + state, + shutdown, + }) + } + + /// Run the UDP packet receive loop + pub async fn run(self) -> Result<(), LpHandlerError> { + let mut buf = vec![0u8; MAX_UDP_PACKET_SIZE]; + + loop { + tokio::select! { + biased; + + _ = self.shutdown.cancelled() => { + info!("LP data handler: received shutdown signal"); + break; + } + + result = self.socket.recv_from(&mut buf) => { + match result { + Ok((len, src_addr)) => { + // Process packet in place (no spawn - UDP is fast) + if let Err(e) = self.handle_packet(&buf[..len], src_addr).await { + debug!("LP data packet error from {src_addr}: {e}"); + inc!("lp_data_packet_errors"); + } + } + Err(e) => { + warn!("LP data socket recv error: {e}"); + inc!("lp_data_recv_errors"); + } + } + } + } + } + + info!("LP data handler shutdown complete"); + Ok(()) + } + + /// Handle a single UDP packet + /// + /// # Packet Processing Steps + /// 1. Parse LP header to get receiver_idx (for routing) + /// 2. Look up session state machine by receiver_idx + /// 3. Process packet through state machine (handles decryption + replay protection) + /// 4. Forward decrypted Sphinx packet to mixnet + /// + /// # Security + /// The state machine's `process_input()` method handles replay protection by: + /// - Checking packet counter against receiving window + /// - Marking counter as used after successful decryption + /// + /// This prevents replay attacks where captured packets are re-sent. + async fn handle_packet( + &self, + packet: &[u8], + src_addr: SocketAddr, + ) -> Result<(), LpHandlerError> { + inc!("lp_data_packets_received"); + + let _ = OuterHeader::parse(packet)?; + trace!( + "received {} bytes from {src_addr} on the unimplemented LP Data endpoint", + packet.len() + ); + + Err(LpHandlerError::UnimplementedDataChannel) + // leave old code for future reference + + // + // // Step 1: Parse LP header (always cleartext for routing) + // let header = nym_lp::codec::parse_lp_header_only(packet).map_err(|e| { + // LpHandlerError::LpProtocolError(format!("Failed to parse LP header: {}", e)) + // })?; + // + // let receiver_idx = header.receiver_idx; + // let counter = header.counter; + // let len = packet.len(); + // + // trace!("LP data packet from {src_addr} (receiver_idx={receiver_idx}, counter={counter}, len={len})"); + // + // // Step 2: Look up session state machine by receiver_idx (mutable for state updates) + // let mut state_entry = self + // .state + // .session_states + // .get_mut(&receiver_idx) + // .ok_or_else(|| { + // inc!("lp_data_unknown_session"); + // LpHandlerError::LpProtocolError(format!( + // "Unknown session for receiver_idx {receiver_idx}" + // )) + // })?; + // + // // Update last activity timestamp + // state_entry.value().touch(); + // + // // Step 3: Get outer AEAD key for packet parsing + // let outer_key = state_entry + // .value() + // .state + // .session() + // .map_err(|e| LpHandlerError::LpProtocolError(format!("Session error: {e}")))? + // .outer_aead_key(); + // + // // Parse full packet with outer AEAD decryption + // let lp_packet = nym_lp::codec::parse_lp_packet(packet, Some(outer_key)).map_err(|e| { + // inc!("lp_data_decrypt_errors"); + // LpHandlerError::LpProtocolError(format!("Failed to decrypt LP packet: {}", e)) + // })?; + // + // // Step 4: Process packet through state machine + // // This handles: + // // - Replay protection (counter check + mark) + // // - Inner Noise decryption + // // - Subsession handling if applicable + // let state_machine = &mut state_entry.value_mut().state; + // + // let action = state_machine + // .process_input(LpInput::ReceivePacket(lp_packet)) + // .ok_or_else(|| { + // LpHandlerError::LpProtocolError("State machine returned no action".to_string()) + // })? + // .map_err(|e| { + // inc!("lp_data_state_machine_errors"); + // LpHandlerError::LpProtocolError(format!("State machine error: {}", e)) + // })?; + // + // // Release session lock before forwarding + // drop(state_entry); + // + // // Step 5: Handle the action from state machine + // match action { + // LpAction::DeliverData(data) => { + // // Decrypted application data - forward as Sphinx packet + // self.forward_sphinx_packet(&data.content).await?; + // inc!("lp_data_packets_forwarded"); + // Ok(()) + // } + // LpAction::SendPacket(_response_packet) => { + // // UDP is connectionless - we can't send responses back easily + // // For subsession rekeying, the client should use TCP control plane + // debug!( + // "Ignoring SendPacket action on UDP (receiver_idx={receiver_idx}) - use TCP for rekeying", + // ); + // inc!("lp_data_ignored_send_actions"); + // Ok(()) + // } + // other => { + // warn!( + // "Unexpected action on UDP data plane from {}: {:?}", + // src_addr, other + // ); + // inc!("lp_data_unexpected_actions"); + // Err(LpHandlerError::LpProtocolError(format!( + // "Unexpected state machine action on UDP: {:?}", + // other + // ))) + // } + // } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Sphinx packets are typically around 2KB + // LP overhead is small (~50 bytes header + AEAD tag) + // 4KB should be plenty with room to spare + const _: () = { + assert!(MAX_UDP_PACKET_SIZE >= 2048 + 100); + }; +} diff --git a/nym-node/src/node/lp/error.rs b/nym-node/src/node/lp/error.rs new file mode 100644 index 0000000000..d8c9da439a --- /dev/null +++ b/nym-node/src/node/lp/error.rs @@ -0,0 +1,72 @@ +// Copyright 2026 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::node::lp::LpReceiverIndex; +use nym_lp::state_machine::{LpAction, LpDataKind}; +use nym_lp::transport::LpTransportError; +use nym_lp::{LpError, packet::MalformedLpPacketError}; +use std::net::SocketAddr; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum LpHandlerError { + #[error("failed to establish egress connection to {egress}: {reason}")] + ConnectionFailure { egress: SocketAddr, reason: String }, + + #[error(transparent)] + LpTransportError(#[from] LpTransportError), + + #[error("missing session state for {receiver_index} - has it been removed due to inactivity?")] + MissingLpSession { receiver_index: LpReceiverIndex }, + + #[error(transparent)] + LpProtocolError(#[from] LpError), + + #[error("the initial KKT/PSQ handshake has not been completed")] + IncompleteHandshake, + + #[error("receiver_idx mismatch: connection bound to {established}, packet has {received}")] + MismatchedReceiverIndex { + established: LpReceiverIndex, + received: LpReceiverIndex, + }, + + #[error("no action has been emitted from the LP State Machine")] + UnexpectedStateMachineHalt, + + #[error("the state machine instructed an unexpected action: {action:?}")] + UnexpectedStateMachineAction { action: LpAction }, + + #[error("received registration request was malformed: {source}")] + MalformedRegistrationRequest { source: bincode::Error }, + + #[error("received a malformed packet: {0}")] + MalformedLpPacket(#[from] MalformedLpPacketError), + + #[error("received payload type of an unexpected type: {typ:?}")] + UnexpectedLpPayload { typ: LpDataKind }, + + #[error("timed out while attempting to send to/receive from the connection")] + ConnectionTimeout, + + #[error("data channel is not yet implemented")] + UnimplementedDataChannel, + + #[error("{0}")] + Other(String), +} + +impl LpHandlerError { + pub fn is_connection_closed(&self) -> bool { + match self { + LpHandlerError::LpTransportError(transport_err) => { + matches!(transport_err, LpTransportError::ConnectionClosed) + } + _ => false, + } + } + + pub fn other(msg: impl Into) -> Self { + LpHandlerError::Other(msg.into()) + } +} diff --git a/nym-node/src/node/lp/handler.rs b/nym-node/src/node/lp/handler.rs new file mode 100644 index 0000000000..7adbeb57e6 --- /dev/null +++ b/nym-node/src/node/lp/handler.rs @@ -0,0 +1,821 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use super::{LpHandlerState, LpReceiverIndex, TimestampedState}; +use crate::node::lp::error::LpHandlerError; +use dashmap::mapref::one::RefMut; +use nym_lp::packet::{EncryptedLpPacket, ForwardPacketData}; +use nym_lp::state_machine::{LpAction, LpData, LpDataKind, LpInput}; +use nym_lp::transport::LpHandshakeChannel; +use nym_lp::transport::traits::LpTransportChannel; +use nym_lp::{LpSession, LpStateMachine, packet::message::ExpectedResponseSize}; +use nym_metrics::{add_histogram_obs, inc}; +use nym_registration_common::{LpRegistrationRequest, RegistrationStatus}; +use std::net::SocketAddr; +use std::time::Duration; +use tokio::net::TcpStream; +use tokio::time::timeout; +use tracing::*; + +// Histogram buckets for LP operation duration (legacy - used by unused forwarding methods) +const LP_DURATION_BUCKETS: &[f64] = &[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]; + +// Timeout for forward I/O operations (send + receive on exit stream) +// Must be long enough to cover exit gateway processing time +const FORWARD_IO_TIMEOUT_SECS: u64 = 30; + +// Histogram buckets for LP connection lifecycle duration +// LP connections can be very short (registration only: ~1s) or very long (dVPN sessions: hours/days) +// Covers full range from seconds to 24 hours +const LP_CONNECTION_DURATION_BUCKETS: &[f64] = &[ + 1.0, // 1 second + 5.0, // 5 seconds + 10.0, // 10 seconds + 30.0, // 30 seconds + 60.0, // 1 minute + 300.0, // 5 minutes + 600.0, // 10 minutes + 1800.0, // 30 minutes + 3600.0, // 1 hour + 7200.0, // 2 hours + 14400.0, // 4 hours + 28800.0, // 8 hours + 43200.0, // 12 hours + 86400.0, // 24 hours +]; + +/// Connection lifecycle statistics tracking +struct ConnectionStats { + /// When the connection started + start_time: std::time::Instant, + /// Total bytes received (including protocol framing) + bytes_received: u64, + /// Total bytes sent (including protocol framing) + bytes_sent: u64, +} + +impl ConnectionStats { + fn new() -> Self { + Self { + start_time: std::time::Instant::now(), + bytes_received: 0, + bytes_sent: 0, + } + } + + fn record_bytes_received(&mut self, bytes: usize) { + self.bytes_received += bytes as u64; + } + + fn record_bytes_sent(&mut self, bytes: usize) { + self.bytes_sent += bytes as u64; + } +} + +pub struct LpConnectionHandler { + stream: S, + remote_addr: SocketAddr, + state: LpHandlerState, + stats: ConnectionStats, + + // /// Flag indicating whether this is a connection from an entry gateway serving as a proxy + // forwarded_connection: bool, + /// Bound receiver_idx for this connection (set after first packet). + /// All subsequent packets on this connection must use this receiver_idx. + /// Set from ClientHello's proposed receiver_index, or from header for non-bootstrap packets. + bound_receiver_idx: Option, + + /// Persistent connection to exit gateway for forwarding. + /// Opened on first forward, reused for subsequent forwards, closed when client disconnects. + /// Tuple contains (stream, target_address) to verify subsequent forwards go to same exit. + exit_stream: Option<(S, SocketAddr)>, +} + +impl LpConnectionHandler +where + S: LpTransportChannel + LpHandshakeChannel + Unpin, +{ + pub fn new( + stream: S, + // forwarded_connection: bool, + remote_addr: SocketAddr, + state: LpHandlerState, + ) -> Self { + Self { + stream, + remote_addr, + // forwarded_connection, + state, + stats: ConnectionStats::new(), + bound_receiver_idx: None, + exit_stream: None, + } + } + + /// Get the mutable reference to the state machine associated with this client. + /// It is vital it's never held across await points or this might lead to a deadlock. + fn state_entry_mut( + &self, + ) -> Result>, LpHandlerError> { + let receiver_index = self.bound_receiver_index()?; + self.state + .session_states + .get_mut(&receiver_index) + .ok_or_else(|| LpHandlerError::MissingLpSession { receiver_index }) + } + + /// AIDEV-NOTE: Stream-oriented packet loop + /// This handler processes multiple packets on a single TCP connection. + /// Connection lifecycle: handshake + registration, then client closes. + /// First packet binds the connection to a receiver_idx (session-affine). + /// Binding is set by handle_client_hello() from payload's receiver_index, + /// or by validate_or_set_binding() for non-bootstrap first packets. + pub async fn handle(mut self) -> Result<(), LpHandlerError> { + debug!("Handling LP connection from {}", self.remote_addr); + + // Track total LP connections handled + inc!("lp_connections_total"); + + // ============================================================ + // STREAM-ORIENTED PROCESSING: Loop until connection closes + // State persists in LpHandlerState maps across packets + // ============================================================ + + // 1. complete KKT/PSQ handshake before doing anything else. + // bail if it takes too long + let timeout = self.state.lp_config.debug.handshake_ttl; + let local_peer = self.state.local_lp_peer.clone(); + let stream = &mut self.stream; + + let session = match tokio::time::timeout(timeout, async move { + LpSession::psq_handshake_responder(stream, local_peer) + .complete_handshake() + .await + }) + .await + { + Err(_timeout) => { + debug!( + "timed out attempting to complete KTT/PSQ handshake with {}", + self.remote_addr + ); + self.emit_lifecycle_metrics(false); + return Ok(()); + } + Ok(Err(handshake_failure)) => { + debug!( + "failed to complete KKT/PSQ handshake with {}: {handshake_failure}", + self.remote_addr + ); + self.emit_lifecycle_metrics(false); + return Ok(()); + } + Ok(Ok(session)) => session, + }; + let receiver_idx = session.receiver_index(); + + // 2. insert the state machine into the shared state + let state_machine = LpStateMachine::new(session); + self.state + .session_states + .insert(receiver_idx, TimestampedState::new(state_machine)); + self.bound_receiver_idx = Some(receiver_idx); + + // 3. handle any new incoming packet + loop { + // Step 1: Receive raw packet bytes and parse header only (for routing) + let encrypted_packet = match self.receive_raw_packet().await { + Ok(result) => result, + Err(err) => { + if err.is_connection_closed() { + // Graceful EOF - client closed connection + trace!("Connection closed by {} (EOF)", self.remote_addr); + break; + } else { + inc!("lp_errors_receive_packet"); + self.emit_lifecycle_metrics(false); + return Err(err); + } + } + }; + + let receiver_idx = encrypted_packet.outer_header().receiver_idx; + + // Step 2: Validate the binding + if let Err(e) = self.validate_binding(receiver_idx) { + self.emit_lifecycle_metrics(false); + return Err(e); + } + + // Step 3: Process the packet + if let Err(e) = self.process_packet(encrypted_packet).await { + self.emit_lifecycle_metrics(false); + return Err(e); + } + } + + self.emit_lifecycle_metrics(true); + Ok(()) + } + + fn bound_receiver_index(&self) -> Result { + self.bound_receiver_idx + .ok_or_else(|| LpHandlerError::IncompleteHandshake) + } + + /// Validate that the receiver_idx matches the bound session. + fn validate_binding(&self, receiver_idx: LpReceiverIndex) -> Result<(), LpHandlerError> { + let bound_receiver_idx = self.bound_receiver_index()?; + + if bound_receiver_idx != receiver_idx { + warn!( + "Receiver_idx mismatch from {}: expected {bound_receiver_idx}, got {receiver_idx}", + self.remote_addr + ); + inc!("lp_errors_receiver_idx_mismatch"); + return Err(LpHandlerError::MismatchedReceiverIndex { + established: bound_receiver_idx, + received: receiver_idx, + }); + } + + Ok(()) + } + + /// Process a single packet: lookup session, parse, route to handler. + /// Individual handlers do NOT emit lifecycle metrics - the main loop handles that. + /// + /// This handles packets on established sessions, which can be either: + /// EncryptedData containing LpRegistrationRequest or ForwardPacketData + /// + /// We process all transport packets through the state machine. + /// The state machine returns appropriate actions: + /// - DeliverData: decrypted application data to process + /// - SendPacket: response to send + async fn process_packet( + &mut self, + encrypted_packet: EncryptedLpPacket, + ) -> Result<(), LpHandlerError> { + let receiver_index = encrypted_packet.outer_header().receiver_idx; + + let mut state_entry = self.state_entry_mut()?; + + // Update last activity timestamp + state_entry.value().touch(); + + let state_machine = &mut state_entry.value_mut().state; + + trace!( + "Received packet from {} (receiver_idx={receiver_index}, counter={})", + self.remote_addr, + encrypted_packet.outer_header().counter, + ); + + // Process packet through state machine + let action = state_machine + .process_input(LpInput::ReceivePacket(encrypted_packet)) + .ok_or(LpHandlerError::UnexpectedStateMachineHalt)??; + + drop(state_entry); + + match action { + LpAction::SendPacket(response_packet) => { + self.send_serialised_packet(&response_packet).await?; + Ok(()) + } + LpAction::DeliverData(data) => { + // Decrypted application data - process as registration/forwarding + self.handle_decrypted_payload(receiver_index, data).await + } + other @ LpAction::ConnectionClosed => { + warn!( + "Unexpected action in transport from {}: {other:?}", + self.remote_addr + ); + Err(LpHandlerError::UnexpectedStateMachineAction { action: other }) + } + } + } + + /// Handle decrypted transport payload (registration or forwarding request) + async fn handle_decrypted_payload( + &mut self, + receiver_idx: LpReceiverIndex, + decrypted_data: LpData, + ) -> Result<(), LpHandlerError> { + let remote = self.remote_addr; + + let bytes = decrypted_data.content; + match decrypted_data.kind { + LpDataKind::Registration => { + let request = LpRegistrationRequest::try_deserialise(&bytes) + .map_err(|source| LpHandlerError::MalformedRegistrationRequest { source })?; + + debug!( + "LP registration request from {remote} (receiver_idx={receiver_idx}): mode={:?}", + request.mode() + ); + + self.handle_registration_request(receiver_idx, request) + .await + } + LpDataKind::Forward => { + let forward_data = ForwardPacketData::decode(&bytes)?; + + self.handle_forwarding_request(receiver_idx, forward_data) + .await + } + typ @ LpDataKind::Opaque => { + // Neither registration nor forwarding - unknown payload type + warn!( + "Unknown transport payload type from {remote} (receiver_idx={receiver_idx}). dropping {} bytes", + bytes.len() + ); + inc!("lp_errors_unknown_payload_type"); + Err(LpHandlerError::UnexpectedLpPayload { typ }) + } + } + } + + /// Attempt to wrap and send specified response back to the client + async fn send_response_packet( + &mut self, + serialised_response: Vec, + response_kind: LpDataKind, + ) -> Result<(), LpHandlerError> { + let mut state_entry = self.state_entry_mut()?; + + // Access session via state machine for subsession support + let state_machine = &mut state_entry.value_mut().state; + + let wrapped_lp_data = LpData::new(response_kind, serialised_response); + + // Process packet through state machine + let action = state_machine + .process_input(LpInput::SendData(wrapped_lp_data)) + .ok_or(LpHandlerError::UnexpectedStateMachineHalt)??; + + let packet = match action { + LpAction::SendPacket(packet) => packet, + action => return Err(LpHandlerError::UnexpectedStateMachineAction { action }), + }; + + drop(state_entry); + + self.send_serialised_packet(&packet).await?; + Ok(()) + } + + /// Handle registration request on an established session + async fn handle_registration_request( + &mut self, + receiver_idx: LpReceiverIndex, + request: LpRegistrationRequest, + ) -> Result<(), LpHandlerError> { + // Process registration (might modify state) + let response = self.state.process_registration(receiver_idx, request).await; + let response_bytes = response + .serialise() + .map_err(|source| LpHandlerError::MalformedRegistrationRequest { source })?; + + self.send_response_packet(response_bytes, LpDataKind::Registration) + .await?; + + match response.status { + RegistrationStatus::Completed => { + info!("LP registration successful for {}", self.remote_addr); + } + RegistrationStatus::Failed => { + warn!( + "LP registration failed for {}: {:?}", + self.remote_addr, + response.error_message() + ); + } + RegistrationStatus::PendingMoreData => { + info!( + "we required more deta from {} to complete registration", + self.remote_addr + ); + } + } + + Ok(()) + } + + /// Handle forwarding request on an established session + /// + /// Entry gateway receives ForwardPacketData from client, forwards inner packet + /// to exit gateway, receives response, encrypts it, and sends back to client. + async fn handle_forwarding_request( + &mut self, + receiver_idx: LpReceiverIndex, + forward_data: ForwardPacketData, + ) -> Result<(), LpHandlerError> { + // Forward the packet to the target gateway and retrieve its response + let response_bytes = self.handle_forward_packet(forward_data).await?; + + self.send_response_packet(response_bytes, LpDataKind::Forward) + .await?; + + debug!( + "LP forwarding completed for {} (receiver_idx={receiver_idx})", + self.remote_addr + ); + + Ok(()) + } + + /// Returns reference to the established forwarding channel to the exit. + #[allow(dead_code)] + pub fn forwarding_channel(&self) -> &Option<(S, SocketAddr)> { + &self.exit_stream + } + + /// This method establishes connection to the target gateway in order to + /// forward received packets and retrieve any responses + // + // In the future it will also perform identity validation to make sure + // the target node is a valid gateway present in the network + // + // Do not manually call this function. It is only exposed for the purposes of integration tests + #[doc(hidden)] + pub async fn establish_exit_stream( + &mut self, + target_addr: SocketAddr, + ) -> Result<(), LpHandlerError> { + // Acquire semaphore permit to limit concurrent connection opens (FD exhaustion protection) + // Permit is scoped to this block - only protects the connect() call, not stream reuse + let _permit = match self.state.forward_semaphore.try_acquire() { + Ok(permit) => permit, + Err(_) => { + inc!("lp_forward_rejected"); + return Err(LpHandlerError::other("Gateway at forward capacity")); + } + }; + + // Connect to target gateway with timeout + let stream = match timeout(Duration::from_secs(5), S::connect(target_addr)).await { + Ok(Ok(stream)) => stream, + Ok(Err(e)) => { + inc!("lp_forward_failed"); + return Err(LpHandlerError::ConnectionFailure { + egress: target_addr, + reason: e.to_string(), + }); + } + Err(_) => { + inc!("lp_forward_failed"); + return Err(LpHandlerError::ConnectionFailure { + egress: target_addr, + reason: "target gateway connection timeout".to_string(), + }); + } + }; + + debug!("Opened persistent exit connection to {target_addr} for forwarding"); + self.exit_stream = Some((stream, target_addr)); + + Ok(()) + } + + /// Forward an LP packet to another gateway + /// + /// This method connects to the target gateway, forwards the inner packet bytes, + /// receives the response, and returns it. Used for telescoping (hiding client IP). + /// + /// # Arguments + /// * `forward_data` - ForwardPacketData containing target gateway info and inner packet + /// + /// # Returns + /// * `Ok(Vec)` - Raw response bytes from target gateway + /// * `Err(LpHandlerError)` - If forwarding fails + /// + /// AIDEV-NOTE: Persistent exit stream forwarding + /// Uses self.exit_stream to maintain a persistent connection to the exit gateway. + /// First forward opens the connection, subsequent forwards reuse it. + /// Connection errors clear exit_stream, causing reconnection on next forward. + /// + /// Semaphore rationale: The forward_semaphore limits concurrent connection OPENS + /// (FD exhaustion protection), not concurrent operations. Since: + /// 1. Each LpConnectionHandler owns its exit_stream exclusively + /// 2. The handler loop processes packets sequentially (no concurrent access) + /// 3. Only connection opens consume new FDs + /// + /// The semaphore is only acquired when opening a new connection, not for reuse. + async fn handle_forward_packet( + &mut self, + forward_data: ForwardPacketData, + ) -> Result, LpHandlerError> { + inc!("lp_forward_total"); + let start = std::time::Instant::now(); + + // Parse target gateway address + let target_addr = forward_data.target_lp_address; + + // Check if we need to open a new connection + let need_new_connection = match &self.exit_stream { + Some((_, existing_addr)) if *existing_addr == target_addr => false, + Some((_, existing_addr)) => { + // Target mismatch - this shouldn't happen in normal operation + // (client should only forward to one exit gateway) + // Return error to prevent silent behavior changes that could mask bugs + inc!("lp_forward_failed"); + return Err(LpHandlerError::other(format!( + "Forward target mismatch: session bound to {existing_addr}, got request for {target_addr}" + ))); + } + None => true, + }; + + if need_new_connection { + self.establish_exit_stream(target_addr).await?; + } + + // Get mutable reference to the exit stream + #[allow(clippy::unwrap_used)] + let (target_stream, _) = self.exit_stream.as_mut().unwrap(); + + debug!( + "Forwarding packet to {} ({} bytes)", + target_addr, + forward_data.inner_packet_bytes.len() + ); + + // Wrap all I/O in timeout to prevent hanging on unresponsive exit gateway + let io_timeout = Duration::from_secs(FORWARD_IO_TIMEOUT_SECS); + let inner_bytes = &forward_data.inner_packet_bytes; + + let io_result: Result, LpHandlerError> = timeout(io_timeout, async { + // Forward inner packet bytes. + // it's up to the client to ensure correct formatting, + // i.e. relevant headers or length-prefixes + target_stream.write_all_and_flush(inner_bytes).await?; + + // attempt to read response based on the provided information + + let response = match forward_data.expected_response_size { + ExpectedResponseSize::Handshake(size) => { + // client told us exactly how many bytes to expect + target_stream.read_n_bytes(size as usize).await? + } + ExpectedResponseSize::Transport => { + // transport packets are length-prefixed + target_stream + .receive_length_prefixed_transport_bytes() + .await? + } + }; + Ok(response) + }) + .await + .map_err(|_| LpHandlerError::ConnectionTimeout)?; + + // Handle result - clear exit_stream on any error + let response_buf = match io_result { + Ok(buf) => buf, + Err(e) => { + inc!("lp_forward_failed"); + self.exit_stream = None; + return Err(e); + } + }; + + // Record metrics + let duration = start.elapsed().as_secs_f64(); + add_histogram_obs!("lp_forward_duration_seconds", duration, LP_DURATION_BUCKETS); + + inc!("lp_forward_success"); + debug!( + "Forwarding successful to {} ({} bytes response, {:.3}s)", + target_addr, + response_buf.len(), + duration + ); + + Ok(response_buf) + } + + /// Receive raw packet bytes and parse outer header only (for routing before session lookup). + /// + /// Returns the raw packet bytes and parsed outer header (receiver_idx + counter). + /// The caller should look up the session to get outer_aead_key, then call + /// `parse_lp_packet()` with the key. + async fn receive_raw_packet(&mut self) -> Result { + let packet = self + .stream + .receive_length_prefixed_transport_packet() + .await?; + + // Track bytes sent (4 byte header + packet data) + self.stats + .record_bytes_received(4 + packet.encoded_length()); + + Ok(packet) + } + + /// Send a serialised LP packet over the stream with proper length-prefixed framing. + async fn send_serialised_packet( + &mut self, + packet: &EncryptedLpPacket, + ) -> Result<(), LpHandlerError> { + self.stream + .send_length_prefixed_transport_packet(packet) + .await?; + + // Track bytes sent (4 byte header + packet data) + self.stats.record_bytes_sent(4 + packet.encoded_length()); + + Ok(()) + } + + /// Emit connection lifecycle metrics + fn emit_lifecycle_metrics(&self, graceful: bool) { + use nym_metrics::inc_by; + + // Track connection duration + let duration = self.stats.start_time.elapsed().as_secs_f64(); + add_histogram_obs!( + "lp_connection_duration_seconds", + duration, + LP_CONNECTION_DURATION_BUCKETS + ); + + // Track bytes transferred + inc_by!( + "lp_connection_bytes_received_total", + self.stats.bytes_received as i64 + ); + inc_by!( + "lp_connection_bytes_sent_total", + self.stats.bytes_sent as i64 + ); + + // Track completion type + if graceful { + inc!("lp_connections_completed_gracefully"); + } else { + inc!("lp_connections_completed_with_error"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config::lp::LpDebug; + use crate::node::lp::LpConfig; + use nym_lp::peer::{KEMKeys, LpLocalPeer, generate_keypair_mceliece, generate_keypair_mlkem}; + use nym_lp::{Ciphersuite, SessionManager, sessions_for_tests}; + use nym_test_utils::helpers::{deterministic_rng, deterministic_rng_09}; + use std::sync::Arc; + // ==================== Test Helpers ==================== + + /// Create a minimal test state for handler tests + async fn create_minimal_test_state() -> LpHandlerState { + use nym_crypto::asymmetric::ed25519; + + let mut rng = deterministic_rng(); + let mut rng09 = deterministic_rng_09(); + + let lp_config = LpConfig { + debug: LpDebug { + ..Default::default() + }, + ..Default::default() + }; + let forward_semaphore = Arc::new(tokio::sync::Semaphore::new( + lp_config.debug.max_concurrent_forwards, + )); + + // Create mix forwarding channel (unused in tests but required by struct) + let (mix_sender, _mix_receiver) = nym_mixnet_client::forwarder::mix_forwarding_channels(); + + let id_keys = Arc::new(ed25519::KeyPair::new(&mut rng)); + let x_keys = Arc::new(id_keys.to_x25519().try_into().unwrap()); + + let kem_keys = KEMKeys::new( + generate_keypair_mceliece(&mut rng09), + generate_keypair_mlkem(&mut rng09), + ); + let lp_peer = LpLocalPeer::new(Ciphersuite::default(), x_keys).with_kem_keys(kem_keys); + + LpHandlerState { + lp_config, + local_lp_peer: lp_peer, + metrics: nym_node_metrics::NymNodeMetrics::default(), + outbound_mix_sender: mix_sender, + session_states: Arc::new(dashmap::DashMap::new()), + peer_registrator: None, + forward_semaphore, + } + } + + // ==================== Existing Tests ==================== + + // ==================== Packet I/O Tests ==================== + + #[tokio::test] + async fn test_receive_raw_packet_valid() { + use tokio::net::{TcpListener, TcpStream}; + + let (init, resp) = sessions_for_tests(); + let mut init_sm = SessionManager::new(); + let mut resp_sm = SessionManager::new(); + resp_sm.create_session_state_machine(resp).unwrap(); + let id = init_sm.create_session_state_machine(init).unwrap(); + + // Bind to localhost + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + // Spawn server task + let server_task = tokio::spawn(async move { + let (stream, remote_addr) = listener.accept().await.unwrap(); + let state = create_minimal_test_state().await; + let mut handler = LpConnectionHandler::new(stream, remote_addr, state); + // Two-phase: receive raw bytes + header, then parse full packet + let packet = handler.receive_raw_packet().await?; + let header = packet.outer_header(); + assert_eq!(packet.outer_header().receiver_idx, id); + let Some(LpAction::DeliverData(data)) = resp_sm.receive_packet(id, packet).unwrap() + else { + panic!("illegal state") + }; + Ok::<_, LpHandlerError>((header, data)) + }); + + // Connect as client + let mut client_stream = TcpStream::connect(addr).await.unwrap(); + + // Send a valid packet from client side + let LpAction::SendPacket(packet) = init_sm + .send_data(id, LpData::new_opaque(b"foomp".to_vec())) + .unwrap() + else { + panic!("illegal state") + }; + + client_stream + .send_length_prefixed_transport_packet(&packet) + .await + .unwrap(); + + // Handler should receive and parse it correctly + // Note: header is OuterHeader (receiver_idx + counter only), not LpHeader + let (header, received) = server_task.await.unwrap().unwrap(); + assert_eq!(header.receiver_idx, id); + assert_eq!(header.counter, 0); + assert_eq!(received.content.as_ref(), b"foomp"); + } + + #[tokio::test] + async fn test_send_lp_packet_valid() { + use tokio::net::{TcpListener, TcpStream}; + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + let (init, resp) = sessions_for_tests(); + let mut init_sm = SessionManager::new(); + let mut resp_sm = SessionManager::new(); + resp_sm.create_session_state_machine(resp).unwrap(); + let id = init_sm.create_session_state_machine(init).unwrap(); + + let server_task = tokio::spawn(async move { + let (mut stream, _) = listener.accept().await.unwrap(); + + let LpAction::SendPacket(packet) = resp_sm + .send_data(id, LpData::new_opaque(b"foomp".to_vec())) + .unwrap() + else { + panic!("illegal state") + }; + + stream + .send_length_prefixed_transport_packet(&packet) + .await + .unwrap(); + }); + + let mut client_stream = TcpStream::connect(addr).await.unwrap(); + + // Wait for server to send + server_task.await.unwrap(); + + // Client should receive it correctly + let received = client_stream + .receive_length_prefixed_transport_packet() + .await + .unwrap(); + let header = received.outer_header(); + let Some(LpAction::DeliverData(data)) = init_sm.receive_packet(id, received).unwrap() + else { + panic!("illegal state") + }; + + assert_eq!(header.receiver_idx, id); + assert_eq!(header.counter, 0); + assert_eq!(data.content.as_ref(), b"foomp"); + } +} diff --git a/gateway/src/node/lp_listener/mod.rs b/nym-node/src/node/lp/mod.rs similarity index 64% rename from gateway/src/node/lp_listener/mod.rs rename to nym-node/src/node/lp/mod.rs index 976eb2afa7..78e1eaee80 100644 --- a/gateway/src/node/lp_listener/mod.rs +++ b/nym-node/src/node/lp/mod.rs @@ -67,209 +67,30 @@ // To view metrics, the nym-metrics registry automatically collects all metrics. // They can be exported via Prometheus format using the metrics endpoint. -use crate::error::GatewayError; -use crate::node::wireguard::PeerRegistrator; -use crate::node::ActiveClientsStore; +use crate::config::lp::LpConfig; +use crate::error::NymNodeError; use dashmap::DashMap; -use nym_config::serde_helpers::de_maybe_port; -use nym_credential_verification::ecash::traits::EcashManager; -use nym_gateway_storage::GatewayStorage; +use nym_gateway::node::wireguard::PeerRegistrator; +use nym_lp::peer::LpLocalPeer; +use nym_lp::peer_config::LpReceiverIndex; use nym_lp::state_machine::LpStateMachine; +use nym_mixnet_client::forwarder::MixForwardingSender; use nym_node_metrics::NymNodeMetrics; use nym_task::ShutdownTracker; -use std::net::{IpAddr, Ipv6Addr, SocketAddr}; +use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use tokio::net::TcpListener; use tokio::sync::Semaphore; use tracing::*; -pub use nym_lp::peer::LpLocalPeer; -pub use nym_mixnet_client::forwarder::{ - mix_forwarding_channels, MixForwardingReceiver, MixForwardingSender, -}; -pub use nym_wireguard::{PeerControlRequest, WireguardGatewayData}; +pub use nym_mixnet_client::forwarder::{MixForwardingReceiver, mix_forwarding_channels}; mod data_handler; +pub mod error; pub mod handler; mod registration; -pub type ReceiverIndex = u32; - -/// Configuration for LP listener -#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] -#[serde(default)] -pub struct LpConfig { - /// Bind address for the TCP LP control traffic. - /// default: `[::]:41264` - pub control_bind_address: SocketAddr, - - /// Bind address for the UDP LP data traffic. - /// default: `[::]:51264` - pub data_bind_address: SocketAddr, - - /// Custom announced port for listening for the TCP LP control traffic. - /// If unspecified, the value from the `control_bind_address` will be used instead - /// (default: None) - #[serde(deserialize_with = "de_maybe_port")] - pub announce_control_port: Option, - - /// Custom announced port for listening for the UDP LP data traffic. - /// If unspecified, the value from the `data_bind_address` will be used instead - /// (default: None) - #[serde(deserialize_with = "de_maybe_port")] - pub announce_data_port: Option, - - /// Auxiliary configuration - #[serde(default)] - pub debug: LpDebug, -} - -#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] -#[serde(default)] -pub struct LpDebug { - /// Maximum concurrent connections - pub max_connections: usize, - - /// Maximum acceptable age of ClientHello timestamp in seconds (default: 30) - /// - /// ClientHello messages with timestamps older than this will be rejected - /// to prevent replay attacks. Value should be: - /// - Large enough to account for clock skew and network latency - /// - Small enough to limit replay attack window - /// - /// Recommended: 30-60 seconds - #[serde(with = "humantime_serde")] - pub timestamp_tolerance: Duration, - - /// Use mock ecash manager for testing (default: false) - /// - /// When enabled, the LP listener will use a mock ecash verifier that - /// accepts any credential without blockchain verification. This is - /// useful for testing the LP protocol implementation without requiring - /// a full blockchain/contract setup. - /// - /// WARNING: Only use this for local testing! Never enable in production. - pub use_mock_ecash: bool, - - /// Maximum age of in-progress handshakes before cleanup (default: 90s) - /// - /// Handshakes should complete quickly (3-5 packets). This TTL accounts for: - /// - Network latency and retransmits - /// - Slow clients - /// - Clock skew tolerance - /// - /// Stale handshakes are removed by the cleanup task to prevent memory leaks. - #[serde(with = "humantime_serde")] - pub handshake_ttl: Duration, - - /// Maximum age of established sessions before cleanup (default: 24h) - /// - /// Sessions can be long-lived for dVPN tunnels. This TTL should be set - /// high enough to accommodate expected usage patterns: - /// - dVPN sessions: hours to days - /// - Registration: minutes - /// - /// Sessions with no activity for this duration are removed by the cleanup task. - #[serde(with = "humantime_serde")] - pub session_ttl: Duration, - - /// Maximum age of demoted (read-only) sessions before cleanup (default: 60s) - /// - /// After subsession promotion, old sessions enter ReadOnlyTransport state. - /// They only need to stay alive briefly to drain in-flight packets. - /// This shorter TTL prevents memory buildup from frequent rekeying. - #[serde(with = "humantime_serde")] - pub demoted_session_ttl: Duration, - - /// How often to run the state cleanup task (default: 5 minutes) - /// - /// The cleanup task scans for and removes stale handshakes and sessions. - /// Lower values = more frequent cleanup but higher overhead. - /// Higher values = less overhead but slower memory reclamation. - #[serde(with = "humantime_serde")] - pub state_cleanup_interval: Duration, - - /// Maximum concurrent forward connections (default: 1000) - /// - /// Limits simultaneous outbound connections when forwarding LP packets to other gateways - /// during telescope setup. This prevents file descriptor exhaustion under high load. - /// - /// When at capacity, new forward requests return an error, signaling the client - /// to choose a different gateway. - pub max_concurrent_forwards: usize, -} - -impl LpConfig { - pub const DEFAULT_CONTROL_PORT: u16 = 41264; - pub const DEFAULT_DATA_PORT: u16 = 51264; - - pub fn announced_control_port(&self) -> u16 { - self.announce_control_port - .unwrap_or(self.control_bind_address.port()) - } - - pub fn announced_data_port(&self) -> u16 { - self.announce_data_port - .unwrap_or(self.data_bind_address.port()) - } -} - -impl Default for LpConfig { - fn default() -> Self { - LpConfig { - control_bind_address: SocketAddr::new( - IpAddr::V6(Ipv6Addr::UNSPECIFIED), - Self::DEFAULT_CONTROL_PORT, - ), - data_bind_address: SocketAddr::new( - IpAddr::V6(Ipv6Addr::UNSPECIFIED), - Self::DEFAULT_DATA_PORT, - ), - announce_control_port: None, - announce_data_port: None, - debug: Default::default(), - } - } -} - -impl LpDebug { - pub const DEFAULT_MAX_CONNECTIONS: usize = 10000; - - // 30 seconds - balances security vs clock skew tolerance - pub const DEFAULT_TIMESTAMP_TOLERANCE: Duration = Duration::from_secs(30); - - // 90 seconds - handshakes should complete quickly - pub const DEFAULT_HANDSHAKE_TTL: Duration = Duration::from_secs(90); - - // 24 hours - for long-lived dVPN sessions - pub const DEFAULT_SESSION_TTL: Duration = Duration::from_secs(86400); - - // 1 minute - enough to drain in-flight packets after subsession promotion - pub const DEFAULT_DEMOTED_SESSION_TTL: Duration = Duration::from_secs(60); - - // 5 minutes - balances memory reclamation with task overhead - pub const DEFAULT_STATE_CLEANUP_INTERVAL: Duration = Duration::from_secs(300); - - // Limits concurrent outbound connections to prevent fd exhaustion - pub const DEFAULT_MAX_CONCURRENT_FORWARDS: usize = 1000; -} - -impl Default for LpDebug { - fn default() -> Self { - LpDebug { - max_connections: Self::DEFAULT_MAX_CONNECTIONS, - timestamp_tolerance: Self::DEFAULT_TIMESTAMP_TOLERANCE, - use_mock_ecash: false, - handshake_ttl: Self::DEFAULT_HANDSHAKE_TTL, - session_ttl: Self::DEFAULT_SESSION_TTL, - demoted_session_ttl: Self::DEFAULT_DEMOTED_SESSION_TTL, - state_cleanup_interval: Self::DEFAULT_STATE_CLEANUP_INTERVAL, - max_concurrent_forwards: Self::DEFAULT_MAX_CONCURRENT_FORWARDS, - } - } -} - /// Wrapper for state entries with timestamp tracking for cleanup /// /// This wrapper adds `created_at` and `last_activity` timestamps to state entries, @@ -315,6 +136,7 @@ impl TimestampedState { } /// Get age since creation + #[allow(dead_code)] pub fn age(&self) -> Duration { self.created_at.elapsed() } @@ -335,21 +157,12 @@ impl TimestampedState { /// Shared state for LP connection handlers #[derive(Clone)] pub struct LpHandlerState { - /// Ecash verifier for bandwidth credentials - pub ecash_verifier: Arc, - - /// Storage backend for persistence - pub storage: GatewayStorage, - /// Encapsulates all required key information of a local Lewes Protocol Peer. pub local_lp_peer: LpLocalPeer, /// Metrics collection pub metrics: NymNodeMetrics, - /// Active clients tracking - pub active_clients_store: ActiveClientsStore, - /// Handle registering new wireguard peers pub peer_registrator: Option, @@ -360,6 +173,7 @@ pub struct LpHandlerState { /// /// Used by the LP data handler (UDP:51264) to forward decrypted Sphinx packets /// from LP clients into the mixnet for routing. + #[allow(dead_code)] pub outbound_mix_sender: MixForwardingSender, /// Established sessions keyed by session_id @@ -374,7 +188,7 @@ pub struct LpHandlerState { /// subsession/rekeying support. The state machine handles subsession initiation /// (SubsessionKK1/KK2/Ready) during transport phase, allowing long-lived connections /// to rekey without re-authentication. - pub session_states: Arc>>, + pub session_states: Arc>>, /// Semaphore limiting concurrent forward connections /// @@ -423,16 +237,18 @@ impl LpListener { self.handler_state.lp_config } - pub async fn run(&mut self) -> Result<(), GatewayError> { + pub async fn run(&mut self) -> Result<(), NymNodeError> { let control_bind_address = self.lp_config().control_bind_address; let data_bind_address = self.lp_config().data_bind_address; - let listener = TcpListener::bind(control_bind_address).await.map_err(|e| { - error!("Failed to bind LP listener to {control_bind_address}: {e}",); - GatewayError::ListenerBindFailure { - address: control_bind_address.to_string(), - source: Box::new(e), - } - })?; + let listener = TcpListener::bind(control_bind_address) + .await + .map_err(|source| { + error!("Failed to bind LP listener to {control_bind_address}: {source}",); + NymNodeError::LpBindFailure { + address: control_bind_address, + source, + } + })?; let shutdown_token = self.shutdown.clone_shutdown_token(); @@ -449,7 +265,6 @@ impl LpListener { loop { tokio::select! { biased; - _ = shutdown_token.cancelled() => { trace!("LP listener: received shutdown signal"); break; @@ -457,12 +272,8 @@ impl LpListener { result = listener.accept() => { match result { - Ok((stream, addr)) => { - self.handle_connection(stream, addr); - } - Err(e) => { - warn!("Failed to accept LP connection: {}", e); - } + Ok((stream, addr)) => self.handle_connection(stream, addr), + Err(e) => warn!("Failed to accept LP connection: {e}") } } } @@ -520,7 +331,7 @@ impl LpListener { /// The data handler listens on UDP port 51264 and processes LP-wrapped Sphinx packets /// from registered clients. It decrypts the LP layer and forwards the Sphinx packets /// into the mixnet. - async fn spawn_data_handler(&self) -> Result, GatewayError> { + async fn spawn_data_handler(&self) -> Result, NymNodeError> { // Create data handler let data_handler = data_handler::LpDataHandler::new( self.lp_config().data_bind_address, @@ -555,14 +366,15 @@ impl LpListener { let handshake_ttl = dbg_cfg.handshake_ttl; let session_ttl = dbg_cfg.session_ttl; - let demoted_session_ttl = dbg_cfg.demoted_session_ttl; let interval = dbg_cfg.state_cleanup_interval; let shutdown = self.shutdown.clone_shutdown_token(); let metrics = self.handler_state.metrics.clone(); info!( - "Starting LP state cleanup task (handshake_ttl={}s, session_ttl={}s, demoted_ttl={}s, interval={}s)", - handshake_ttl.as_secs(), session_ttl.as_secs(), demoted_session_ttl.as_secs(), interval.as_secs() + "Starting LP state cleanup task (handshake_ttl={}s, session_ttl={}s, interval={}s)", + handshake_ttl.as_secs(), + session_ttl.as_secs(), + interval.as_secs() ); self.shutdown.try_spawn_named( @@ -580,9 +392,9 @@ impl LpListener { } pub(crate) mod cleanup_task { - use crate::node::lp_listener::{LpDebug, TimestampedState}; + use crate::config::lp::LpDebug; + use crate::node::lp::{LpReceiverIndex, TimestampedState}; use dashmap::DashMap; - use nym_lp::state_machine::LpStateBare; use nym_lp::LpStateMachine; use nym_metrics::inc_by; use nym_node_metrics::NymNodeMetrics; @@ -590,42 +402,29 @@ pub(crate) mod cleanup_task { use tracing::{debug, info}; async fn perform_cleanup( - session_states: &Arc>>, + session_states: &Arc>>, cfg: LpDebug, ) { let session_ttl = cfg.session_ttl; - let demoted_session_ttl = cfg.demoted_session_ttl; let start = std::time::Instant::now(); let mut ss_removed = 0u64; - let mut demoted_removed = 0u64; // Remove stale sessions (based on time since last activity) // Use shorter TTL for demoted (ReadOnlyTransport) sessions session_states.retain(|_, timestamped| { - let is_demoted = timestamped.state.bare_state() == LpStateBare::ReadOnlyTransport; - let ttl = if is_demoted { - demoted_session_ttl - } else { - session_ttl - }; - - if timestamped.since_activity() > ttl { - if is_demoted { - demoted_removed += 1; - } else { - ss_removed += 1; - } + if timestamped.since_activity() > session_ttl { + ss_removed += 1; false } else { true } }); - if ss_removed > 0 || demoted_removed > 0 { + if ss_removed > 0 { let duration = start.elapsed(); info!( - "LP state cleanup: {ss_removed} sessions, {demoted_removed} demoted (took {:.3}s)", + "LP state cleanup: {ss_removed} sessions (took {:.3}s)", duration.as_secs_f64() ); @@ -633,9 +432,6 @@ pub(crate) mod cleanup_task { if ss_removed > 0 { inc_by!("lp_states_cleanup_session_removed", ss_removed as i64); } - if demoted_removed > 0 { - inc_by!("lp_states_cleanup_demoted_removed", demoted_removed as i64); - } } } @@ -647,7 +443,7 @@ pub(crate) mod cleanup_task { /// Demoted sessions (ReadOnlyTransport) use shorter TTL since they /// only need to drain in-flight packets after subsession promotion. pub(crate) async fn cleanup_loop( - session_states: Arc>>, + session_states: Arc>>, cfg: LpDebug, shutdown: nym_task::ShutdownToken, _metrics: NymNodeMetrics, diff --git a/gateway/src/node/lp_listener/registration.rs b/nym-node/src/node/lp/registration.rs similarity index 96% rename from gateway/src/node/lp_listener/registration.rs rename to nym-node/src/node/lp/registration.rs index 7f2e7b875c..52b6975c5b 100644 --- a/gateway/src/node/lp_listener/registration.rs +++ b/nym-node/src/node/lp/registration.rs @@ -1,7 +1,7 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::node::lp_listener::{LpHandlerState, ReceiverIndex}; +use crate::node::lp::{LpHandlerState, LpReceiverIndex}; use nym_metrics::{add_histogram_obs, inc}; use nym_registration_common::dvpn::{ LpDvpnRegistrationFinalisation, LpDvpnRegistrationInitialRequest, @@ -31,7 +31,7 @@ const LP_REGISTRATION_DURATION_BUCKETS: &[f64] = &[ impl LpHandlerState { async fn process_dvpn_initial_registration( &self, - sender: ReceiverIndex, + sender: LpReceiverIndex, request: LpDvpnRegistrationInitialRequest, ) -> LpRegistrationResponse { let Some(registrator) = self.peer_registrator.as_ref() else { @@ -54,7 +54,7 @@ impl LpHandlerState { async fn process_dvpn_registration_finalisation( &self, - sender: ReceiverIndex, + sender: LpReceiverIndex, request: LpDvpnRegistrationFinalisation, ) -> LpRegistrationResponse { let Some(registrator) = self.peer_registrator.as_ref() else { @@ -77,7 +77,7 @@ impl LpHandlerState { async fn process_dvpn_registration( &self, - sender: ReceiverIndex, + sender: LpReceiverIndex, request: Box, ) -> LpRegistrationResponse { // Track dVPN registration attempts @@ -108,7 +108,7 @@ impl LpHandlerState { /// Process an LP registration request pub async fn process_registration( &self, - sender: ReceiverIndex, + sender: LpReceiverIndex, request: LpRegistrationRequest, ) -> LpRegistrationResponse { let registration_start = std::time::Instant::now(); diff --git a/nym-node/src/node/mod.rs b/nym-node/src/node/mod.rs index 460889d3db..59213d0a3b 100644 --- a/nym-node/src/node/mod.rs +++ b/nym-node/src/node/mod.rs @@ -10,7 +10,9 @@ use crate::error::{EntryGatewayError, NymNodeError, ServiceProvidersError}; use crate::node::description::{load_node_description, save_node_description}; use crate::node::helpers::{ DisplayDetails, get_current_rotation_id, load_ed25519_identity_keypair, load_key, + load_mceliece_keypair, load_mlkem768_keypair, load_x25519_lp_keypair, load_x25519_noise_keypair, store_ed25519_identity_keypair, store_key, store_keypair, + store_mceliece_keypair, store_mlkem768_keypair, store_x25519_lp_keypair, store_x25519_noise_keypair, }; use crate::node::http::api::api_requests; @@ -20,6 +22,7 @@ use crate::node::http::{HttpServerConfig, NymNodeHttpServer, NymNodeRouter}; use crate::node::key_rotation::active_keys::ActiveSphinxKeys; use crate::node::key_rotation::controller::KeyRotationController; use crate::node::key_rotation::manager::SphinxKeyManager; +use crate::node::lp::{LpHandlerState, LpListener}; use crate::node::metrics::aggregator::MetricsAggregator; use crate::node::metrics::console_logger::ConsoleLogger; use crate::node::metrics::handler::client_sessions::GatewaySessionStatsHandler; @@ -41,7 +44,14 @@ use crate::node::shared_network::{ use nym_bin_common::bin_info; use nym_credential_verification::UpgradeModeState; use nym_crypto::asymmetric::{ed25519, x25519}; -use nym_gateway::node::{ActiveClientsStore, GatewayTasksBuilder, UpgradeModeCheckRequestSender}; +use nym_gateway::node::wireguard::PeerRegistrator; +use nym_gateway::node::{GatewayTasksBuilder, UpgradeModeCheckRequestSender}; +use nym_kkt::key_utils::{ + generate_keypair_mceliece, generate_keypair_mlkem, generate_lp_keypair_x25519, +}; +use nym_kkt::keys::{DHKeyPair, KEMKeys}; +use nym_lp::Ciphersuite; +use nym_lp::peer::LpLocalPeer; use nym_mixnet_client::client::ActiveConnections; use nym_mixnet_client::forwarder::MixForwardingSender; use nym_network_requester::{ @@ -50,6 +60,8 @@ use nym_network_requester::{ }; use nym_node_metrics::NymNodeMetrics; use nym_node_metrics::events::MetricEventsSender; +use nym_node_requests::api::SignedData; +use nym_node_requests::api::v1::lewes_protocol::models::{LPHashFunction, LPKEM, LewesProtocol}; use nym_node_requests::api::v1::node::models::{AnnouncePorts, NodeDescription}; use nym_noise::config::{NoiseConfig, NoiseNetworkView}; use nym_noise_keys::VersionedNoiseKeyV1; @@ -62,19 +74,25 @@ use nym_verloc::{self, measurements::VerlocMeasurer}; use nym_wireguard::{WireguardGatewayData, peer_controller::PeerControlRequest}; use rand::rngs::OsRng; use rand::{CryptoRng, RngCore}; +use rand09::SeedableRng; +use std::collections::BTreeMap; use std::net::SocketAddr; use std::ops::Deref; use std::path::Path; use std::sync::Arc; -use tokio::sync::mpsc; +use tokio::sync::{Semaphore, mpsc}; use tracing::{debug, info, trace}; use zeroize::Zeroizing; +pub use nym_gateway::node::ActiveClientsStore; +pub use nym_gateway::node::GatewayStorage; + pub mod bonding_information; pub mod description; pub mod helpers; pub(crate) mod http; pub(crate) mod key_rotation; +pub mod lp; pub(crate) mod metrics; pub(crate) mod mixnet; mod nym_apis_client; @@ -84,8 +102,7 @@ mod shared_network; pub struct GatewayTasksData { mnemonic: Arc>, - psq_kem_key: Arc, - client_storage: nym_gateway::node::GatewayStorage, + client_storage: GatewayStorage, stats_storage: nym_gateway::node::PersistentStatsStorage, } @@ -106,12 +123,8 @@ impl GatewayTasksData { Ok(()) } - async fn new( - config: &GatewayTasksConfig, - // this argument is temporary while we still derive KEM x25519 out of identity ed25519 - ed25519_identity: &ed25519::KeyPair, - ) -> Result { - let client_storage = nym_gateway::node::GatewayStorage::init( + async fn new(config: &GatewayTasksConfig) -> Result { + let client_storage = GatewayStorage::init( &config.storage_paths.clients_storage, config.debug.message_retrieval_limit, ) @@ -125,7 +138,6 @@ impl GatewayTasksData { Ok(GatewayTasksData { mnemonic: Arc::new(config.storage_paths.load_mnemonic_from_file()?), - psq_kem_key: Arc::new(ed25519_identity.to_x25519()), client_storage, stats_storage, }) @@ -373,7 +385,7 @@ impl From for nym_wireguard::WireguardData { } } -pub(crate) struct NymNode { +pub struct NymNode { config: Config, accepted_operator_terms_and_conditions: bool, shutdown_manager: ShutdownManager, @@ -396,8 +408,10 @@ pub(crate) struct NymNode { ed25519_identity_keys: Arc, sphinx_key_manager: Option, - // to be used when noise is integrated x25519_noise_keys: Arc, + + psq_kem_keys: KEMKeys, + x25519_lp_keys: Arc, } impl NymNode { @@ -405,12 +419,19 @@ impl NymNode { config: &Config, custom_mnemonic: Option>, ) -> Result<(), NymNodeError> { - debug!("initialising nym-node with id: {}", config.id); + info!("initialising nym-node with id: {}", config.id); let mut rng = OsRng; + let mut rng09 = rand09::rngs::StdRng::from_os_rng(); // global initialisation + info!("generating new node keys (this might take a while)"); let ed25519_identity_keys = ed25519::KeyPair::new(&mut rng); let x25519_noise_keys = x25519::KeyPair::new(&mut rng); + + let x25519_lp_keys = generate_lp_keypair_x25519(&mut rng09); + let mlkem = generate_keypair_mlkem(&mut rng09); + let mceliece = generate_keypair_mceliece(&mut rng09); + let current_rotation_id = get_current_rotation_id(&config.mixnet.nym_api_urls, &config.mixnet.nyxd_urls).await?; let _ = SphinxKeyManager::initialise_new( @@ -432,6 +453,18 @@ impl NymNode { &config.storage_paths.keys.x25519_noise_storage_paths(), )?; + trace!("attempting to x25519 lp keypair"); + store_x25519_lp_keypair( + &x25519_lp_keys, + &config.storage_paths.keys.x25519_lp_key_paths(), + )?; + + trace!("attempting to mlkem768 keypair"); + store_mlkem768_keypair(&mlkem, &config.storage_paths.keys.mlkem768_key_paths())?; + + trace!("attempting to mceliece keypair"); + store_mceliece_keypair(&mceliece, &config.storage_paths.keys.mceliece_key_paths())?; + trace!("creating description file"); save_node_description( &config.storage_paths.description, @@ -454,6 +487,30 @@ impl NymNode { config.save() } + pub async fn build_lp_listener( + &mut self, + peer_registrator: Option, + mix_packet_sender: MixForwardingSender, + ) -> Result { + let handler_state = LpHandlerState { + local_lp_peer: LpLocalPeer::new(Ciphersuite::default(), self.x25519_lp_keys.clone()) + .with_kem_keys(self.psq_kem_keys.clone()), + metrics: self.metrics.clone(), + peer_registrator, + lp_config: self.config.lp, + outbound_mix_sender: mix_packet_sender, + session_states: Arc::new(dashmap::DashMap::new()), + forward_semaphore: Arc::new(Semaphore::new( + self.config.lp.debug.max_concurrent_forwards, + )), + }; + + Ok(LpListener::new( + handler_state, + self.shutdown_manager.shutdown_tracker().clone(), + )) + } + pub(crate) async fn new(config: Config) -> Result { let wireguard_data = WireguardData::new(&config.wireguard)?; let current_rotation_id = @@ -462,8 +519,12 @@ impl NymNode { let ed25519_identity_keys = load_ed25519_identity_keypair( &config.storage_paths.keys.ed25519_identity_storage_paths(), )?; - let entry_gateway = - GatewayTasksData::new(&config.gateway_tasks, &ed25519_identity_keys).await?; + let entry_gateway = GatewayTasksData::new(&config.gateway_tasks).await?; + let x25519_lp_keys = + load_x25519_lp_keypair(&config.storage_paths.keys.x25519_lp_key_paths())?; + let mlkem = load_mlkem768_keypair(&config.storage_paths.keys.mlkem768_key_paths())?; + let mceliece = load_mceliece_keypair(&config.storage_paths.keys.mceliece_key_paths())?; + let psq_kem_keys = KEMKeys::new(mceliece, mlkem); Ok(NymNode { ed25519_identity_keys: Arc::new(ed25519_identity_keys), @@ -475,6 +536,7 @@ impl NymNode { x25519_noise_keys: Arc::new(load_x25519_noise_keypair( &config.storage_paths.keys.x25519_noise_storage_paths(), )?), + psq_kem_keys, description: load_node_description(&config.storage_paths.description)?, metrics: NymNodeMetrics::new(), verloc_stats: Default::default(), @@ -488,6 +550,7 @@ impl NymNode { accepted_operator_terms_and_conditions: false, shutdown_manager: ShutdownManager::build_new_default() .map_err(|source| NymNodeError::ShutdownSignalFailure { source })?, + x25519_lp_keys: Arc::new(x25519_lp_keys), }) } @@ -640,15 +703,14 @@ impl NymNode { let mut gateway_tasks_builder = GatewayTasksBuilder::new( config.gateway, self.ed25519_identity_keys.clone(), - self.x25519_noise_keys.clone(), - self.entry_gateway.psq_kem_key.clone(), self.entry_gateway.client_storage.clone(), - mix_packet_sender, + mix_packet_sender.clone(), metrics_sender, self.metrics.clone(), self.entry_gateway.mnemonic.clone(), Self::user_agent(), self.upgrade_mode_state.clone(), + self.config.lp.debug.use_mock_ecash, self.shutdown_tracker().clone(), ); @@ -706,16 +768,13 @@ impl NymNode { // Start LP listener if enabled info!( "starting the LP listener on {} (data handler on: {})", - self.config.gateway_tasks.lp.control_bind_address, - self.config.gateway_tasks.lp.data_bind_address, + self.config.lp.control_bind_address, self.config.lp.data_bind_address, ); - let lp_listener = gateway_tasks_builder - .build_lp_listener(wg_peer_registrator.clone(), active_clients_store.clone()) + let mut lp_listener = self + .build_lp_listener(wg_peer_registrator.clone(), mix_packet_sender) .await?; - // make sure lp listener can be built, but do not start it - let _ = lp_listener; - // self.shutdown_tracker() - // .try_spawn_named(async move { lp_listener.run().await }, "LpListener"); + self.shutdown_tracker() + .try_spawn_named(async move { lp_listener.run().await }, "LpListener"); } else { info!("node not running in entry mode: the websocket and LP will remain closed"); } @@ -791,40 +850,24 @@ impl NymNode { Ok(()) } - // - // fn compute_kem_key_hashes(&self) -> HashMap> { - // let kem = LPKEM::X25519; - // - // let kem_key_bytes = self.entry_gateway.psq_kem_key.public_key().as_bytes(); - // - // // convert from `nym_kkt_ciphersuite` types into `nym_nodes_requests` - // let digests = produce_key_digests(kem_key_bytes.as_ref()) - // .into_iter() - // .map(|(f, digest)| (f.into(), hex::encode(&digest))) - // .collect(); - // - // let mut hashes = HashMap::new(); - // hashes.insert(kem, digests); - // hashes - // } - // - // fn compute_signing_key_hashes( - // &self, - // ) -> HashMap> { - // let scheme = LPSignatureScheme::Ed25519; - // - // let kem_key_bytes = self.ed25519_identity_keys.public_key().as_bytes(); - // - // // convert from `nym_kkt_ciphersuite` types into `nym_nodes_requests` - // let digests = produce_key_digests(kem_key_bytes.as_ref()) - // .into_iter() - // .map(|(f, digest)| (f.into(), hex::encode(&digest))) - // .collect(); - // - // let mut hashes = HashMap::new(); - // hashes.insert(scheme, digests); - // hashes - // } + + fn compute_kem_key_hashes(&self) -> BTreeMap> { + let digests = self.psq_kem_keys.encapsulation_keys_digests(); + + // convert from `nym_kkt_ciphersuite` types into `nym_nodes_requests` + digests + .into_iter() + .map(|(kem, kem_digests)| { + ( + kem.into(), + kem_digests + .into_iter() + .map(|(f, digest)| (f.into(), hex::encode(&digest))) + .collect(), + ) + }) + .collect() + } pub(crate) async fn build_http_server(&self) -> Result { let auxiliary_details = api_requests::v1::node::models::AuxiliaryDetails { @@ -899,7 +942,21 @@ impl NymNode { policy: None, }; - let mut config = HttpServerConfig::new() + let lewes_protocol = LewesProtocol { + enabled: self.modes().entry, + control_port: self.config.lp.announced_control_port(), + data_port: self.config.lp.announced_data_port(), + x25519: self.x25519_lp_keys.pk, + kem_keys: self.compute_kem_key_hashes(), + }; + + // SAFETY: the only way for this call to fail is if serialisation of LewesProtocol fails. + // however, that conversion is stable and infallible + #[allow(clippy::unwrap_used)] + let signed_lewes_protocol = + SignedData::new(lewes_protocol, self.ed25519_identity_keys.private_key()).unwrap(); + + let mut config = HttpServerConfig::new(signed_lewes_protocol) .with_landing_page_assets(self.config.http.landing_page_assets_path.as_ref()) .with_mixnode_details(mixnode_details) .with_gateway_details(gateway_details) @@ -1345,7 +1402,7 @@ impl NymNode { Ok(self.shutdown_manager) } - pub(crate) async fn run(mut self) -> Result<(), NymNodeError> { + pub async fn run(mut self) -> Result<(), NymNodeError> { let mut shutdown_signals = self.shutdown_manager.detach_shutdown_signals(); // listen for shutdown signal in case we received it when attempting to spawn all the tasks diff --git a/nym-node/src/wireguard/mod.rs b/nym-node/src/wireguard/mod.rs index 03d1b32e4b..46db2413ef 100644 --- a/nym-node/src/wireguard/mod.rs +++ b/nym-node/src/wireguard/mod.rs @@ -5,3 +5,6 @@ // but let's start putting everything in here pub mod error; + +pub use nym_gateway::node::wireguard::{PeerManager, PeerRegistrator}; +pub use nym_wireguard::{PeerControlRequest, WireguardGatewayData}; diff --git a/nym-outfox/Cargo.toml b/nym-outfox/Cargo.toml index 8d7855bc48..d3ea59291d 100644 --- a/nym-outfox/Cargo.toml +++ b/nym-outfox/Cargo.toml @@ -10,18 +10,13 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rayon = { workspace = true } blake3 = { workspace = true } zeroize = { workspace = true } chacha20 = { workspace = true, features = ["std"] } x25519-dalek = { workspace = true } chacha20poly1305 = { workspace = true } -getrandom = { workspace = true, features = ["js"] } thiserror = { workspace = true } sphinx-packet = { workspace = true } -rand = { workspace = true } -log = { workspace = true } [dev-dependencies] -criterion = { workspace = true } fastrand = { workspace = true } diff --git a/nym-registration-client/Cargo.toml b/nym-registration-client/Cargo.toml index 3209fc7e47..a1c3aa335a 100644 --- a/nym-registration-client/Cargo.toml +++ b/nym-registration-client/Cargo.toml @@ -14,26 +14,28 @@ publish = false workspace = true [dependencies] +bincode.workspace = true bytes.workspace = true futures.workspace = true -rand.workspace = true +rand09.workspace = true thiserror.workspace = true tokio.workspace = true tokio-util.workspace = true tracing.workspace = true typed-builder.workspace = true -url.workspace = true - nym-authenticator-client = { workspace = true } nym-bandwidth-controller = { workspace = true } nym-credential-storage = { workspace = true } nym-credentials-interface = { workspace = true } -nym-crypto = { workspace = true } +nym-crypto = { workspace = true, features = ["asymmetric", "libcrux_x25519"] } nym-ip-packet-client = { workspace = true } nym-lp = { path = "../common/nym-lp" } -nym-lp-transport = { path = "../common/nym-lp-transport" } nym-registration-common = { workspace = true } nym-sdk = { workspace = true } nym-validator-client = { workspace = true } nym-wireguard-types = { path = "../common/wireguard-types" } + +[dev-dependencies] +nym-kkt.workspace = true +nym-test-utils.workspace = true \ No newline at end of file diff --git a/nym-registration-client/src/clients/lp.rs b/nym-registration-client/src/clients/lp.rs index 3f3ce0abbc..3b6131db76 100644 --- a/nym-registration-client/src/clients/lp.rs +++ b/nym-registration-client/src/clients/lp.rs @@ -13,10 +13,10 @@ use crate::types::{LpRegistrationResult, RegistrationResult}; use nym_bandwidth_controller::BandwidthTicketProvider; use nym_credentials_interface::TicketType; -use nym_crypto::aes::cipher::crypto_common::rand_core::{CryptoRng, RngCore}; use nym_crypto::asymmetric::ed25519; -use rand::rngs::OsRng; +use nym_lp::peer::DHKeyPair; +use rand09::{CryptoRng, RngCore}; use std::sync::Arc; use tokio::net::TcpStream; use tokio_util::sync::CancellationToken; @@ -54,19 +54,22 @@ impl LpBasedRegistrationClient { let entry_lp_protocol = entry_lp_data.lp_protocol_version; let exit_lp_protocol = exit_lp_data.lp_protocol_version; + let entry_ciphersuite = entry_lp_data.ciphersuite; + let exit_ciphersuite = exit_lp_data.ciphersuite; + let entry_address = entry_lp_data.address; let exit_address = exit_lp_data.address; tracing::debug!("Entry gateway LP address: {entry_address}"); tracing::debug!("Exit gateway LP address: {exit_address}"); - // Generate fresh Ed25519 keypairs for LP registration - // These are ephemeral and used only for the LP handshake protocol - let entry_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut OsRng)); - let exit_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut OsRng)); + // Generate fresh x25519 keypairs for LP registration + // TODO: persist them for the duration of the sessions + let entry_lp_keypair = Arc::new(DHKeyPair::new(&mut rand09::rng())); + let exit_lp_keypair = Arc::new(DHKeyPair::new(&mut rand09::rng())); - let entry_peer = to_lp_remote_peer(self.config.entry.node.identity, entry_lp_data); - let exit_peer = to_lp_remote_peer(self.config.exit.node.identity, exit_lp_data); + let entry_peer = to_lp_remote_peer(entry_lp_data); + let exit_peer = to_lp_remote_peer(exit_lp_data); // STEP 1: Establish outer session with entry gateway // This creates the LP session that will be used to forward packets to exit. @@ -76,6 +79,7 @@ impl LpBasedRegistrationClient { entry_lp_keypair.clone(), entry_peer, entry_address, + entry_ciphersuite, entry_lp_protocol, self.config.lp_registration_config, ); @@ -94,8 +98,13 @@ impl LpBasedRegistrationClient { // STEP 2: Use nested session to register with exit gateway via forwarding // This hides the client's IP address from the exit gateway tracing::info!("Registering with exit gateway via entry forwarding"); - let mut nested_session = - NestedLpSession::new(exit_address, exit_lp_keypair, exit_peer, exit_lp_protocol); + let mut nested_session = NestedLpSession::new( + exit_address, + exit_lp_keypair, + exit_peer, + exit_ciphersuite, + exit_lp_protocol, + ); // Perform handshake and registration with exit gateway (all via entry forwarding) let exit_gateway_data = nested_session @@ -149,7 +158,7 @@ impl LpBasedRegistrationClient { } async fn register_wg(self) -> Result { - let mut rng = rand::rngs::OsRng; + let mut rng = rand09::rng(); self.register_wg_with_rng(&mut rng).await } diff --git a/nym-registration-client/src/lp_client/client.rs b/nym-registration-client/src/lp_client/client.rs index 25f3265878..81d6c5f67d 100644 --- a/nym-registration-client/src/lp_client/client.rs +++ b/nym-registration-client/src/lp_client/client.rs @@ -5,29 +5,26 @@ use super::config::LpRegistrationConfig; use super::error::{LpClientError, Result}; -use crate::lp_client::helpers::{ - LpDataDeliverExt, LpDataSendExt, convert_forward_data, try_convert_forward_response, -}; +use crate::lp_client::helpers::{LpDataDeliverExt, LpDataSendExt}; use crate::lp_client::nested_session::connection::NestedConnection; use crate::lp_client::state_machine_helpers::{extract_forwarded_response, prepare_send_packet}; -use bytes::BytesMut; use nym_bandwidth_controller::{BandwidthTicketProvider, DEFAULT_TICKETS_TO_SPEND}; use nym_credentials_interface::TicketType; use nym_crypto::asymmetric::{ed25519, x25519}; -use nym_lp::codec::{OuterAeadKey, parse_lp_packet, serialize_lp_packet}; -use nym_lp::message::ForwardPacketData; -use nym_lp::packet::version; -use nym_lp::peer::{LpLocalPeer, LpRemotePeer}; -use nym_lp::state_machine::{LpAction, LpData, LpInput, LpStateMachine}; -use nym_lp::{LpPacket, LpSession}; -use nym_lp_transport::traits::LpTransport; +use nym_lp::LpSession; +use nym_lp::peer::{DHKeyPair, LpLocalPeer, LpRemotePeer}; +use nym_lp::peer_config::LpReceiverIndex; +use nym_lp::state_machine::LpStateMachine; +use nym_lp::transport::traits::LpTransportChannel; +use nym_lp::transport::{LpHandshakeChannel, LpTransportError}; +use nym_lp::{Ciphersuite, packet::EncryptedLpPacket, packet::version}; use nym_registration_common::dvpn::LpDvpnRegistrationResponseMessageContent; use nym_registration_common::{ LpRegistrationRequest, LpRegistrationResponse, WireguardConfiguration, WireguardRegistrationData, }; use nym_wireguard_types::PeerPublicKey; -use rand::{CryptoRng, RngCore}; +use rand09::{CryptoRng, Rng, RngCore}; use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; @@ -62,7 +59,7 @@ pub struct LpRegistrationClient { gateway_supported_lp_protocol_version: u8, /// LP state machine for managing connection lifecycle. - /// Created during handshake initiation. Persists across packet-per-connection calls. + /// Created during handshake initiation. state_machine: Option, /// Configuration for timeouts and TCP parameters. @@ -75,23 +72,25 @@ pub struct LpRegistrationClient { impl LpRegistrationClient where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, { /// Creates a new LP registration client. /// /// # Arguments - /// * `local_ed25519_keypair` - Client's Ed25519 identity keypair + /// * `local_x25519_keypair` - Client's x25519 keypair /// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol /// * `gateway_lp_address` - Gateway's LP listener socket address + /// * `ciphersuite` - the set of cryptographic protocols to use when negotiating the session with the node /// * `gateway_supported_lp_protocol_version` - Gateway's LP protocol version /// * `config` - Configuration for timeouts and TCP parameters (use `LpConfig::default()`) /// /// # Note /// This creates the client. Call `perform_handshake()` to establish the LP session. pub fn new( - local_ed25519_keypair: Arc, + local_x25519_keypair: Arc, gateway_lp_peer: LpRemotePeer, gateway_lp_address: SocketAddr, + ciphersuite: Ciphersuite, gateway_supported_lp_protocol_version: u8, config: LpRegistrationConfig, ) -> Self { @@ -105,8 +104,7 @@ where gateway_supported_lp_protocol_version }; - let local_x25519_keypair = local_ed25519_keypair.to_x25519(); - let lp_local_peer = LpLocalPeer::new(local_ed25519_keypair, Arc::new(local_x25519_keypair)); + let lp_local_peer = LpLocalPeer::new(ciphersuite, local_x25519_keypair); Self { lp_local_peer, gateway_lp_peer, @@ -119,13 +117,8 @@ where } /// Attempt to use this `LpRegistrationClient` as transport for `NestedSession` - pub fn as_nested_connection( - &mut self, - exit_identity: ed25519::PublicKey, - exit_address: SocketAddr, - ) -> NestedConnection<'_, S> { + pub fn as_nested_connection(&mut self, exit_address: SocketAddr) -> NestedConnection<'_, S> { NestedConnection { - exit_identity, exit_address, outer_client: self, } @@ -134,41 +127,46 @@ where /// Creates a new LP registration client with default configuration. /// /// # Arguments - /// * `local_ed25519_keypair` - Client's Ed25519 identity keypair + /// * `local_x25519_keypair` - Client's x25519 keypair /// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol /// * `gateway_lp_address` - Gateway's LP listener socket address + /// * `ciphersuite` - the set of cryptographic protocols to use when negotiating the session with the node /// * `gateway_supported_lp_protocol_version` - Gateway's LP protocol version /// /// Uses default config (LpConfig::default()) with sane timeout and TCP parameters. /// PSK is derived automatically during handshake inside the state machine. /// For custom config, use `new()` directly. pub fn new_with_default_config( - local_ed25519_keypair: Arc, + local_x25519_keypair: Arc, gateway_lp_peer: LpRemotePeer, gateway_lp_address: SocketAddr, + ciphersuite: Ciphersuite, gateway_supported_lp_protocol_version: u8, ) -> Self { Self::new( - local_ed25519_keypair, + local_x25519_keypair, gateway_lp_peer, gateway_lp_address, + ciphersuite, gateway_supported_lp_protocol_version, LpRegistrationConfig::default(), ) } + pub(crate) fn state_machine(&self) -> Result<&LpStateMachine> { + self.state_machine + .as_ref() + .ok_or(LpClientError::IncompleteHandshake) + } + pub(crate) fn state_machine_mut(&mut self) -> Result<&mut LpStateMachine> { - self.state_machine.as_mut().ok_or_else(|| { - LpClientError::transport( - "State machine not available - has the handshake been completed?", - ) - }) + self.state_machine + .as_mut() + .ok_or(LpClientError::IncompleteHandshake) } fn stream_mut(&mut self) -> Result<&mut S> { - self.stream - .as_mut() - .ok_or_else(|| LpClientError::transport("Cannot send: not connected")) + self.stream.as_mut().ok_or(LpClientError::NotConnected) } /// Returns whether the client has completed the handshake and is ready for registration. @@ -216,10 +214,10 @@ where .await .map_err(|_| LpClientError::TcpConnection { address: self.gateway_lp_address.to_string(), - source: std::io::Error::new( - std::io::ErrorKind::TimedOut, - format!("Connection timeout after {:?}", self.config.connect_timeout), - ), + source: LpTransportError::ConnectionFailure(format!( + "Connection timeout after {:?}", + self.config.connect_timeout + )), })? .map_err(|source| LpClientError::TcpConnection { address: self.gateway_lp_address.to_string(), @@ -253,7 +251,10 @@ where /// /// # Errors /// Returns an error if not connected or if send or receive fails. - async fn send_and_receive_packet(&mut self, packet: &LpPacket) -> Result { + async fn send_and_receive_packet( + &mut self, + packet: &EncryptedLpPacket, + ) -> Result { self.try_send_packet(packet).await?; self.try_receive_packet().await } @@ -262,22 +263,21 @@ where /// and attempt to immediately read a response /// within the provided timeout. /// - /// Both packets are going to be optionally encrypted/decrypted based on the availability of keys - /// within the internal `LpStateMachine` + /// Both packets are going to be encrypted /// /// # Arguments - /// * `packet` - The LP packet to send + /// * `packet` - The encrypted LP packet to send /// /// # Errors /// Returns an error if not connected, the timeout has been reached, or if send or receive fails. - async fn send_and_receive_packet_with_timeout( + async fn send_and_receive_data_packet_with_timeout( &mut self, - packet: &LpPacket, + packet: &EncryptedLpPacket, timeout: Duration, - ) -> Result { + ) -> Result { tokio::time::timeout(timeout, self.send_and_receive_packet(packet)) .await - .map_err(|_| LpClientError::ResponseReceiveTimeout { timeout })? + .map_err(|_| LpClientError::ConnectionTimeout)? } /// Sends an LP packet on the persistent stream. @@ -287,42 +287,25 @@ where /// /// # Errors /// Returns an error if not connected or if send fails. - pub(crate) async fn try_send_packet(&mut self, packet: &LpPacket) -> Result<()> { + pub(crate) async fn try_send_packet(&mut self, packet: &EncryptedLpPacket) -> Result<()> { // can't use getters due to borrow checker (i.e. requiring full borrows for function calls) - let stream = self - .stream - .as_mut() - .ok_or_else(|| LpClientError::transport("Cannot send: not connected"))?; - - let state_machine = self.state_machine.as_ref().ok_or_else(|| { - LpClientError::transport( - "State machine not available - has the handshake been completed?", - ) - })?; - - let outer_key = state_machine.session()?.outer_aead_key(); - Self::send_packet_with_key(stream, packet, outer_key).await + self.stream_mut()? + .send_length_prefixed_transport_packet(packet) + .await?; + Ok(()) } /// Receives an LP packet from the persistent stream. /// /// # Errors /// Returns an error if not connected or if receive fails. - pub(crate) async fn try_receive_packet(&mut self) -> Result { - let stream = self - .stream - .as_mut() - .ok_or_else(|| LpClientError::transport("Cannot send: not connected"))?; + pub(crate) async fn try_receive_packet(&mut self) -> Result { + let encrypted_packet = self + .stream_mut()? + .receive_length_prefixed_transport_packet() + .await?; - let state_machine = self.state_machine.as_ref().ok_or_else(|| { - LpClientError::transport( - "State machine not available - has the handshake been completed?", - ) - })?; - - let outer_key = state_machine.session()?.outer_aead_key(); - - Self::receive_packet_with_key(stream, outer_key).await + Ok(encrypted_packet) } /// Closes the persistent connection. @@ -372,7 +355,7 @@ where /// /// The connection remains open after handshake for registration/forwarding. pub async fn perform_handshake(&mut self) -> Result<()> { - // Apply handshake timeout (nym-102) + // Apply handshake timeout let result = tokio::time::timeout( self.config.handshake_timeout, self.perform_handshake_inner(), @@ -388,10 +371,7 @@ where } Err(_) => { self.close(); - Err(LpClientError::Transport(format!( - "Handshake timeout after {:?}", - self.config.handshake_timeout - ))) + Err(LpClientError::HandshakeTimeout) } } } @@ -413,15 +393,13 @@ where let connection = self.stream_mut()?; // TODO: - let ciphersuite = LpSession::default_ciphersuite(); - let session = LpSession::complete_as_initiator( + let session = LpSession::psq_handshake_initiator( connection, - ciphersuite, local_peer, remote_peer, protocol_version, ) - .complete_as_initiator() + .complete_handshake() .await?; // Store the state machine (with established session) for later use @@ -429,57 +407,6 @@ where Ok(()) } - /// Sends an LP packet over a TCP stream with length-prefixed framing. - /// - /// Format: 4-byte big-endian u32 length + packet bytes - /// - /// # Arguments - /// * `stream` - TCP stream to send on - /// * `packet` - The LP packet to send - /// * `outer_key` - Optional outer AEAD key for encryption - /// - /// # Errors - /// Returns an error if serialization or network transmission fails. - async fn send_packet_with_key( - stream: &mut S, - packet: &LpPacket, - outer_key: &OuterAeadKey, - ) -> Result<()> { - let mut packet_buf = BytesMut::new(); - serialize_lp_packet(packet, &mut packet_buf, Some(outer_key)) - .map_err(|e| LpClientError::Transport(format!("Failed to serialize packet: {e}")))?; - - stream - .send_serialised_packet(&packet_buf) - .await - .map_err(|err| LpClientError::Transport(err.to_string())) - } - - /// Receives an LP packet from a TCP stream with length-prefixed framing. - /// - /// Format: 4-byte big-endian u32 length + packet bytes - /// - /// # Arguments - /// * `stream` - TCP stream to receive from - /// * `outer_key` - Optional outer AEAD key for decryption - /// - /// # Errors - /// Returns an error if: - /// - Network read fails - /// - Packet size exceeds maximum (64KB) - /// - Packet parsing/decryption fails - async fn receive_packet_with_key(stream: &mut S, outer_key: &OuterAeadKey) -> Result { - let packet_buf = stream - .receive_raw_packet() - .await - .map_err(|err| LpClientError::transport(err.to_string()))?; - - let packet = parse_lp_packet(&packet_buf, Some(outer_key)) - .map_err(|e| LpClientError::Transport(format!("Failed to parse packet: {e}")))?; - - Ok(packet) - } - /// This is an internal method only meant to be called by `Self::register_dvpn` if the gateway /// responds with a credential request. This is expected in every initial interaction with a particular gateway. /// @@ -539,7 +466,10 @@ where // 5. Send initial request and receive response on persistent connection with timeout let response_packet = self - .send_and_receive_packet_with_timeout(&request_packet, self.config.registration_timeout) + .send_and_receive_data_packet_with_timeout( + &request_packet, + self.config.registration_timeout, + ) .await?; // 6. Decrypt via state machine (re-borrow) @@ -623,7 +553,10 @@ where // 4. Send initial request and receive response on persistent connection with timeout let response_packet = self - .send_and_receive_packet_with_timeout(&request_packet, self.config.registration_timeout) + .send_and_receive_data_packet_with_timeout( + &request_packet, + self.config.registration_timeout, + ) .await?; // 5. Decrypt via state machine (re-borrow) @@ -721,7 +654,7 @@ where if attempt > 0 { // Exponential backoff with jitter: 100ms, 200ms, 400ms, 800ms, 1600ms (capped) let base_delay_ms = 100u64 * (1 << attempt.min(4)); - let jitter_ms = rand::random::() % (base_delay_ms / 4 + 1); + let jitter_ms: u64 = rand09::rng().random_range(0..(base_delay_ms / 4 + 1)); let delay = std::time::Duration::from_millis(base_delay_ms + jitter_ms); tracing::info!("Retrying registration (attempt {attempt_display}) after {delay:?}"); tokio::time::sleep(delay).await; @@ -764,174 +697,9 @@ where } } - Err(last_error - .unwrap_or_else(|| LpClientError::transport("Registration failed after all retries"))) - } - - /// Sends a ForwardPacket message to the entry gateway for forwarding to the exit gateway. - /// - /// This method constructs a ForwardPacket containing the target gateway's identity, - /// address, and the inner LP packet bytes, encrypts it through the outer session - /// (client-entry), and receives the response from the exit gateway via the entry gateway. - /// - /// Uses the persistent TCP connection established during handshake. - /// Multiple forward packets can be sent on the same connection. - /// - /// # Arguments - /// * `forward_data` - encapsulated target gateway's ed25519 identity, socket address and serialised inner LP packet - /// - /// # Returns - /// * `Ok(Vec)` - Decrypted response bytes from the exit gateway - /// - /// # Errors - /// Returns an error if: - /// - Handshake has not been completed - /// - Serialization fails - /// - Encryption or network transmission fails - /// - Response decryption fails - /// - /// # Example Flow - /// ```ignore - /// // Construct inner packet for exit gateway (ClientHello, handshake, etc.) - /// let inner_packet = LpPacket::new(...); - /// let inner_bytes = serialize_lp_packet(&inner_packet, &mut BytesMut::new())?; - /// - /// // Forward through entry gateway - /// let response_bytes = client.send_forward_packet( - /// exit_identity, - /// "2.2.2.2:41264".to_string(), - /// inner_bytes.to_vec(), - /// ).await?; - /// ``` - pub async fn send_forward_packet_with_response( - &mut self, - forward_data: ForwardPacketData, - ) -> Result> { - let target_address = forward_data.target_lp_address; - - tracing::debug!( - "Sending ForwardPacket to {target_address} ({} inner bytes, persistent connection)", - forward_data.inner_packet_bytes.len() - ); - - // 1. Serialize the ForwardPacketData - let input = convert_forward_data(forward_data)?; - - // 2. Encrypt and prepare packet via state machine - let state_machine = self.state_machine_mut()?; - - let action = state_machine - .process_input(input) - .ok_or_else(|| LpClientError::transport("State machine returned no action"))? - .map_err(|e| { - LpClientError::Transport(format!("Failed to encrypt ForwardPacket: {e}")) - })?; - - let forward_packet = match action { - LpAction::SendPacket(packet) => packet, - other => { - return Err(LpClientError::Transport(format!( - "Unexpected action when sending ForwardPacket: {:?}", - other - ))); - } - }; - - // 3. Send and receive on persistent connection with timeout - let response_packet = tokio::time::timeout(self.config.forward_timeout, async { - self.try_send_packet(&forward_packet).await?; - self.try_receive_packet().await - }) - .await - .map_err(|_| { - LpClientError::Transport(format!( - "Forward packet timeout after {:?}", - self.config.forward_timeout - )) - })??; - tracing::trace!("Received response packet from entry gateway"); - - // 4. Decrypt via state machine (re-borrow) - let state_machine = self - .state_machine - .as_mut() - .ok_or_else(|| LpClientError::transport("State machine disappeared unexpectedly"))?; - let action = state_machine - .process_input(LpInput::ReceivePacket(response_packet)) - .ok_or_else(|| LpClientError::transport("State machine returned no action"))? - .map_err(|e| { - LpClientError::Transport(format!("Failed to decrypt forward response: {e}")) - })?; - - // 5. Extract decrypted response data - let response_data = try_convert_forward_response(action)?; - - tracing::debug!( - "Successfully received forward response from {target_address} ({} bytes)", - response_data.len() - ); - - Ok(response_data) - } - - /// Wrap data in an LP packet for UDP transmission to the data plane (port 51264). - /// - /// This method encrypts the provided data using the established LP session - /// and returns serialized LP packet bytes ready to send over UDP. - /// - /// # Prerequisites - /// - Handshake must be completed (`perform_handshake()`) - /// - /// # Arguments - /// * `data` - Raw application data to wrap (e.g., Sphinx packet bytes) - /// - /// # Returns - /// * `Ok(Vec)` - Serialized LP packet bytes (outer header + encrypted payload) - /// - /// # Wire Format - /// The returned bytes are in LP wire format: - /// - Outer header (12B): receiver_idx(4) + counter(8) - always cleartext - /// - Encrypted payload: proto(1) + reserved(3) + msg_type(4) + content + tag(16) - /// - /// # Usage - /// After LP handshake, wrap Sphinx packets before sending to gateway's LP data port: - /// ```ignore - /// client.perform_handshake().await?; - /// let sphinx_bytes = build_sphinx_packet(...); - /// let lp_bytes = client.wrap_data(&sphinx_bytes)?; - /// socket.send_to(&lp_bytes, gateway_lp_data_address).await?; // UDP:51264 - /// ``` - pub fn wrap_data(&mut self, data: &[u8]) -> Result> { - let state_machine = self - .state_machine - .as_mut() - .ok_or_else(|| LpClientError::transport("Cannot wrap data: handshake not completed"))?; - - // Process data through state machine to create LP packet - let action = state_machine - .process_input(LpInput::SendData(LpData::new_opaque(data.to_vec()))) - .ok_or_else(|| LpClientError::transport("State machine returned no action"))? - .map_err(|e| LpClientError::Transport(format!("Failed to encrypt data: {e}")))?; - - let packet = match action { - LpAction::SendPacket(packet) => packet, - other => { - return Err(LpClientError::Transport(format!( - "Unexpected action when wrapping data: {:?}", - other - ))); - } - }; - - // Get outer AEAD key for encryption - let outer_key = state_machine.session()?.outer_aead_key(); - - // Serialize the packet with outer AEAD encryption - let mut buf = BytesMut::new(); - serialize_lp_packet(&packet, &mut buf, Some(outer_key)) - .map_err(|e| LpClientError::Transport(format!("Failed to serialize LP packet: {e}")))?; - - Ok(buf.to_vec()) + Err(last_error.unwrap_or(LpClientError::RegistrationFailure { + message: "Registration failed after all retries".to_string(), + })) } /// Get the LP session ID (receiver_idx) for this client. @@ -940,41 +708,39 @@ where /// the gateway to look up the session for decryption. /// /// # Returns - /// * `Ok(u32)` - The session ID + /// * `Ok(LpReceiverIndex)` - The session ID /// /// # Errors /// Returns an error if handshake has not been completed. - pub fn session_id(&self) -> Result { - let state_machine = self.state_machine.as_ref().ok_or_else(|| { - LpClientError::transport("Cannot get session ID: handshake not completed") - })?; - - state_machine + pub fn session_id(&self) -> Result { + self.state_machine()? .session() - .map(|s| s.id()) - .map_err(|e| LpClientError::Transport(format!("Failed to get session: {e}"))) + .map(|s| s.receiver_index()) + .map_err(Into::into) } } #[cfg(test)] mod tests { use super::*; + use nym_kkt::key_utils::generate_lp_keypair_x25519; use nym_lp::packet::version; + use nym_test_utils::helpers::deterministic_rng_09; #[test] fn test_client_creation() { - let mut rng = rand::thread_rng(); - let keypair = Arc::new(ed25519::KeyPair::new(&mut rng)); - let gateway_ed_keys = ed25519::KeyPair::new(&mut rng); - let gateway_x_keys = gateway_ed_keys.to_x25519(); - let gateway_peer = - LpRemotePeer::new(*gateway_ed_keys.public_key(), *gateway_x_keys.public_key()); + let mut rng09 = deterministic_rng_09(); + let keypair = Arc::new(generate_lp_keypair_x25519(&mut rng09)); + + let gateway_x_keys = generate_lp_keypair_x25519(&mut rng09); + let gateway_peer = LpRemotePeer::from(gateway_x_keys.pk); let address = "127.0.0.1:41264".parse().unwrap(); let client = LpRegistrationClient::::new_with_default_config( keypair, gateway_peer, address, + Ciphersuite::default(), version::CURRENT, ); diff --git a/nym-registration-client/src/lp_client/error.rs b/nym-registration-client/src/lp_client/error.rs index 32443f4e01..681d34d01e 100644 --- a/nym-registration-client/src/lp_client/error.rs +++ b/nym-registration-client/src/lp_client/error.rs @@ -4,9 +4,9 @@ //! Error types for LP (Lewes Protocol) client operations. use nym_lp::LpError; -use nym_registration_common::BincodeError; -use std::io; -use std::time::Duration; +use nym_lp::packet::MalformedLpPacketError; +use nym_lp::state_machine::{LpAction, LpDataKind}; +use nym_lp::transport::LpTransportError; use thiserror::Error; /// Errors that can occur during LP client operations. @@ -17,12 +17,41 @@ pub enum LpClientError { TcpConnection { address: String, #[source] - source: io::Error, + source: LpTransportError, }, - /// Failed during LP handshake - #[error("LP handshake failed: {0}")] - HandshakeFailed(#[from] LpError), + #[error(transparent)] + LpTransportError(#[from] LpTransportError), + + #[error("the client has not opened a connection to the exit")] + NotConnected, + + #[error("the KKT/PSQ handshake does not appear to have been completed")] + IncompleteHandshake, + + #[error(transparent)] + LpProtocolError(#[from] LpError), + + #[error("no action has been emitted from the LP State Machine")] + UnexpectedStateMachineHalt, + + #[error("the state machine instructed an unexpected action: {action:?}")] + UnexpectedStateMachineAction { action: LpAction }, + + #[error("received registration data was malformed: {source}")] + MalformedRegistrationData { source: bincode::Error }, + + #[error("received a malformed packet: {0}")] + MalformedLpPacket(#[from] MalformedLpPacketError), + + #[error("received payload type of an unexpected type: {typ:?}")] + UnexpectedLpPayload { typ: LpDataKind }, + + #[error("timed out while attempting to finish the KKT/PSQ handshake")] + HandshakeTimeout, + + #[error("timed out while attempting to send to/receive from the connection")] + ConnectionTimeout, /// Failed to send registration request #[error("Failed to send registration request: {0}")] @@ -36,43 +65,20 @@ pub enum LpClientError { #[error("Gateway rejected registration: {reason}")] RegistrationRejected { reason: String }, - /// Failed to receive response within specified deadline - #[error("Failed to receive response within the set timeout: {timeout:?}")] - ResponseReceiveTimeout { timeout: Duration }, - - /// LP transport error - #[error("LP transport error: {0}")] - Transport(String), - - /// Invalid LP address format - #[error("Invalid LP address '{address}': {reason}")] - InvalidAddress { address: String, reason: String }, - - /// Serialization/deserialization error - #[error("Serialization error: {0}")] - Serialization(#[from] BincodeError), - - /// Connection closed unexpectedly - #[error("Connection closed unexpectedly")] - ConnectionClosed, - - /// Timeout waiting for response - #[error("Timeout waiting for {operation}")] - Timeout { operation: String }, + #[error("could not complete the registration: {message}")] + RegistrationFailure { message: String }, #[error("received an unexpected response: {message}")] UnexpectedResponse { message: String }, - /// Another uncategorized error + #[error("currently McEliece keys are not supported for nested registration")] + UnsupportedNestedMcEliece, + #[error("{0}")] Other(String), } impl LpClientError { - pub fn transport(message: impl Into) -> LpClientError { - LpClientError::Transport(message.into()) - } - pub fn unexpected_response(message: impl Into) -> LpClientError { LpClientError::UnexpectedResponse { message: message.into(), diff --git a/nym-registration-client/src/lp_client/helpers.rs b/nym-registration-client/src/lp_client/helpers.rs index 3eb53706fd..9de8cb5a19 100644 --- a/nym-registration-client/src/lp_client/helpers.rs +++ b/nym-registration-client/src/lp_client/helpers.rs @@ -4,8 +4,7 @@ #![allow(dead_code)] use crate::LpClientError; -use nym_crypto::asymmetric::ed25519; -use nym_lp::message::ForwardPacketData; +use nym_lp::packet::ForwardPacketData; use nym_lp::peer::LpRemotePeer; use nym_lp::state_machine::{LpAction, LpData, LpDataKind, LpInput}; use nym_registration_common::{ @@ -38,15 +37,11 @@ impl LpDataSendExt for LpRegistrationRequest { impl LpDataDeliverExt for LpRegistrationResponse { fn from_lp_data(data: LpData) -> Result { if data.kind != LpDataKind::Registration { - return Err(LpClientError::Transport(format!( - "did not receive a valid registration response. got {:?} instead", - data.kind - ))); + return Err(LpClientError::UnexpectedLpPayload { typ: data.kind }); } - let response = LpRegistrationResponse::try_deserialise(&data.content).map_err(|e| { - LpClientError::Transport(format!("Failed to deserialize registration response: {e}",)) - })?; + let response = LpRegistrationResponse::try_deserialise(&data.content) + .map_err(|source| LpClientError::MalformedRegistrationData { source })?; Ok(response) } @@ -72,30 +67,18 @@ pub(crate) fn convert_forward_data(request: ForwardPacketData) -> Result Result, LpClientError> { let response_data = match action { LpAction::DeliverData(data) => data, - other => { - return Err(LpClientError::Transport(format!( - "Unexpected action when receiving forward response: {:?}", - other - ))); - } + action => return Err(LpClientError::UnexpectedStateMachineAction { action }), }; if response_data.kind != LpDataKind::Forward { - return Err(LpClientError::Transport(format!( - "did not receive a valid forward response. got {:?} instead", - response_data.kind - ))); + return Err(LpClientError::UnexpectedLpPayload { + typ: response_data.kind, + }); } Ok(response_data.content.into()) } -pub(crate) fn to_lp_remote_peer( - identity: ed25519::PublicKey, - data: NymNodeLPInformation, -) -> LpRemotePeer { - LpRemotePeer::new(identity, data.x25519).with_key_digests( - data.expected_kem_key_hashes, - data.expected_signing_key_hashes, - ) +pub(crate) fn to_lp_remote_peer(data: NymNodeLPInformation) -> LpRemotePeer { + LpRemotePeer::new(data.x25519).with_key_digests(data.expected_kem_key_hashes) } diff --git a/nym-registration-client/src/lp_client/nested_session/connection.rs b/nym-registration-client/src/lp_client/nested_session/connection.rs index fd0f673677..70432e9bb0 100644 --- a/nym-registration-client/src/lp_client/nested_session/connection.rs +++ b/nym-registration-client/src/lp_client/nested_session/connection.rs @@ -3,60 +3,79 @@ use crate::lp_client::helpers::{convert_forward_data, try_convert_forward_response}; use crate::{LpClientError, LpRegistrationClient}; -use nym_crypto::asymmetric::ed25519; -use nym_lp::message::ForwardPacketData; +use bytes::{BufMut, BytesMut}; +use nym_lp::KEM; +use nym_lp::packet::{EncryptedLpPacket, ForwardPacketData, message::ExpectedResponseSize}; use nym_lp::state_machine::{LpAction, LpInput}; -use nym_lp_transport::traits::LpTransport; +use nym_lp::transport::traits::{HandshakeMessage, LpTransportChannel}; +use nym_lp::transport::{LpHandshakeChannel, LpTransportError}; use std::io; use std::net::SocketAddr; /// Attempt to treat the inner client as a LP connection pub struct NestedConnection<'a, S> { - /// Remote Ed25519 public key - pub(crate) exit_identity: ed25519::PublicKey, - /// Exit gateway's LP address (e.g., "2.2.2.2:41264") pub(crate) exit_address: SocketAddr, + // exact mechanisms of determining this value are TBD pub(crate) outer_client: &'a mut LpRegistrationClient, } impl<'a, S> NestedConnection<'a, S> { - async fn send_serialised_packet(&mut self, packet_data: &[u8]) -> Result<(), LpClientError> + fn prepare_handshake_message( + &self, + message: M, + handshake_kem: KEM, + ) -> Result { + let Some(response_size) = message.response_size(handshake_kem) else { + // this should NEVER happen for an initiator + return Err(LpClientError::Other("unexpected empty response".into())); + }; + + let expected_size = ExpectedResponseSize::Handshake(response_size as u32); + + Ok(ForwardPacketData::new( + self.exit_address, + expected_size, + message.into_bytes(), + )) + } + + fn prepare_transport_message(&self, packet: &EncryptedLpPacket) -> ForwardPacketData { + let mut buf = BytesMut::new(); + let len = packet.encoded_length() as u32; + buf.put_u32_le(len); + packet.encode(&mut buf); + ForwardPacketData::new( + self.exit_address, + ExpectedResponseSize::Transport, + buf.freeze().into(), + ) + } + + async fn send_forward_packet(&mut self, data: ForwardPacketData) -> Result<(), LpClientError> where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, { - let forward_packet_data = - ForwardPacketData::new(self.exit_identity, self.exit_address, packet_data.to_vec()); - - let target_address = self.exit_address; - tracing::debug!( - "Sending ForwardPacket to {target_address} ({} inner bytes, persistent connection)", - forward_packet_data.inner_packet_bytes.len() + "Sending ForwardPacket to {} ({} inner bytes, persistent connection)", + data.target_lp_address, + data.inner_packet_bytes.len() ); // 1. Serialize the ForwardPacketData - let input = convert_forward_data(forward_packet_data)?; + let input = convert_forward_data(data)?; // 2. Encrypt and prepare packet via state machine let state_machine = self.outer_client.state_machine_mut()?; let action = state_machine .process_input(input) - .ok_or_else(|| LpClientError::transport("State machine returned no action"))? - .map_err(|e| { - LpClientError::Transport(format!("Failed to encrypt ForwardPacket: {e}")) - })?; + .ok_or(LpClientError::UnexpectedStateMachineHalt)??; let forward_packet = match action { LpAction::SendPacket(packet) => packet, - other => { - return Err(LpClientError::Transport(format!( - "Unexpected action when sending ForwardPacket: {:?}", - other - ))); - } + action => return Err(LpClientError::UnexpectedStateMachineAction { action }), }; // 3. Send the packet with timeout @@ -65,16 +84,14 @@ impl<'a, S> NestedConnection<'a, S> { self.outer_client.try_send_packet(&forward_packet).await }) .await - .map_err(|_| { - LpClientError::Transport(format!("Forward packet timeout after {timeout:?}",)) - })??; + .map_err(|_| LpClientError::ConnectionTimeout)??; Ok(()) } - async fn receive_raw_packet(&mut self) -> Result, LpClientError> + async fn receive_forward_packet_data(&mut self) -> Result, LpClientError> where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, { // 1. Receive the packet with timeout let timeout = self.outer_client.config.forward_timeout; @@ -82,18 +99,13 @@ impl<'a, S> NestedConnection<'a, S> { self.outer_client.try_receive_packet().await }) .await - .map_err(|_| { - LpClientError::Transport(format!("Forward packet timeout after {timeout:?}",)) - })??; + .map_err(|_| LpClientError::ConnectionTimeout)??; // 2. Decrypt via state machine (re-borrow) let state_machine = self.outer_client.state_machine_mut()?; let action = state_machine .process_input(LpInput::ReceivePacket(response_packet)) - .ok_or_else(|| LpClientError::transport("State machine returned no action"))? - .map_err(|e| { - LpClientError::Transport(format!("Failed to decrypt forward response: {e}")) - })?; + .ok_or(LpClientError::UnexpectedStateMachineHalt)??; // 3. Extract decrypted response data let response_data = try_convert_forward_response(action)?; @@ -108,28 +120,78 @@ impl<'a, S> NestedConnection<'a, S> { } } -impl<'a, S> LpTransport for NestedConnection<'a, S> +impl<'a, S> LpHandshakeChannel for NestedConnection<'a, S> where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, { #[allow(clippy::unimplemented)] - async fn connect(_: SocketAddr) -> std::io::Result { + async fn write_all_and_flush(&mut self, _: &[u8]) -> Result<(), LpTransportError> { + // this is not being called instead we implement `send_handshake_message` directly + unimplemented!() + } + + #[allow(clippy::unimplemented)] + async fn read_n_bytes(&mut self, _: usize) -> Result, LpTransportError> { + // this is not being called instead we implement `receive_handshake_message` directly + unimplemented!() + } + + async fn send_handshake_message( + &mut self, + message: M, + handshake_kem: KEM, + ) -> Result<(), LpTransportError> { + let forward_data = self + .prepare_handshake_message(message, handshake_kem) + .map_err(|err| LpTransportError::TransportSendFailure(err.to_string()))?; + self.send_forward_packet(forward_data) + .await + .map_err(|err| LpTransportError::TransportSendFailure(err.to_string())) + } + + async fn receive_handshake_message( + &mut self, + _: usize, + ) -> Result { + let data = self + .receive_forward_packet_data() + .await + .map_err(|err| LpTransportError::TransportReceiveFailure(err.to_string()))?; + M::try_from_bytes(data) + } +} + +impl<'a, S> LpTransportChannel for NestedConnection<'a, S> +where + S: LpTransportChannel + LpHandshakeChannel + Unpin, +{ + #[allow(clippy::unimplemented)] + async fn connect(_: SocketAddr) -> Result { // this really breaks the pattern and should be refactored // since this function should never be called unimplemented!("cannot establish nested connection without an outer client") } - fn set_no_delay(&mut self, _: bool) -> std::io::Result<()> { + fn set_no_delay(&mut self, _: bool) -> Result<(), LpTransportError> { Ok(()) } - async fn send_serialised_packet(&mut self, packet_data: &[u8]) -> std::io::Result<()> { - self.send_serialised_packet(packet_data) + async fn send_length_prefixed_transport_packet( + &mut self, + packet: &EncryptedLpPacket, + ) -> Result<(), LpTransportError> { + let packet = self.prepare_transport_message(packet); + self.send_forward_packet(packet) .await .map_err(io::Error::other) + .map_err(LpTransportError::send_failure) } - async fn receive_raw_packet(&mut self) -> std::io::Result> { - self.receive_raw_packet().await.map_err(io::Error::other) + async fn receive_length_prefixed_transport_bytes( + &mut self, + ) -> Result, LpTransportError> { + self.receive_forward_packet_data() + .await + .map_err(|err| LpTransportError::TransportReceiveFailure(err.to_string())) } } diff --git a/nym-registration-client/src/lp_client/nested_session/mod.rs b/nym-registration-client/src/lp_client/nested_session/mod.rs index 7ee62c844c..a4e1c5681c 100644 --- a/nym-registration-client/src/lp_client/nested_session/mod.rs +++ b/nym-registration-client/src/lp_client/nested_session/mod.rs @@ -21,29 +21,27 @@ use super::client::LpRegistrationClient; use super::error::{LpClientError, Result}; use crate::lp_client::helpers::{LpDataDeliverExt, LpDataSendExt}; -use crate::lp_client::state_machine_helpers::{ - extract_forwarded_response, prepare_serialised_send_packet, -}; +use crate::lp_client::state_machine_helpers::{extract_forwarded_response, prepare_send_packet}; use nym_bandwidth_controller::{BandwidthTicketProvider, DEFAULT_TICKETS_TO_SPEND}; use nym_credentials_interface::TicketType; use nym_crypto::asymmetric::{ed25519, x25519}; -use nym_lp::codec::{OuterAeadKey, parse_lp_packet}; -use nym_lp::message::ForwardPacketData; +use nym_lp::packet::EncryptedLpPacket; use nym_lp::packet::version; -use nym_lp::peer::{LpLocalPeer, LpRemotePeer}; +use nym_lp::peer::{DHKeyPair, LpLocalPeer, LpRemotePeer}; use nym_lp::state_machine::{LpData, LpStateMachine}; -use nym_lp::{LpPacket, LpSession}; -use nym_lp_transport::traits::LpTransport; +use nym_lp::transport::LpHandshakeChannel; +use nym_lp::transport::traits::LpTransportChannel; +use nym_lp::{Ciphersuite, KEM, LpSession}; use nym_registration_common::dvpn::LpDvpnRegistrationResponseMessageContent; use nym_registration_common::{ LpRegistrationRequest, LpRegistrationResponse, WireguardConfiguration, WireguardRegistrationData, }; use nym_wireguard_types::PeerPublicKey; -use rand::{CryptoRng, RngCore}; +use rand09::{CryptoRng, Rng, RngCore}; use std::net::SocketAddr; use std::sync::Arc; -use tracing::warn; +use tracing::{debug, warn}; pub(crate) mod connection; @@ -90,17 +88,18 @@ impl NestedLpSession { /// /// # Arguments /// * `exit_address` - Exit gateway's LP address (e.g., "2.2.2.2:41264") - /// * `client_keypair` - Client's Ed25519 keypair + /// * `client_keypair` - Client's x25519 keypair /// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol + /// * `ciphersuite` - the set of cryptographic protocols to use when negotiating the session with the node /// * `gateway_supported_lp_protocol_version` - Gateway's LP protocol version pub fn new( exit_address: SocketAddr, - client_keypair: Arc, + client_keypair: Arc, gateway_lp_peer: LpRemotePeer, + ciphersuite: Ciphersuite, gateway_supported_lp_protocol_version: u8, ) -> Self { - let local_x25519_keypair = client_keypair.to_x25519(); - let lp_local_peer = LpLocalPeer::new(client_keypair, Arc::new(local_x25519_keypair)); + let lp_local_peer = LpLocalPeer::new(ciphersuite, client_keypair); let lp_protocol = if gateway_supported_lp_protocol_version > version::CURRENT { warn!( @@ -121,44 +120,22 @@ impl NestedLpSession { } } - fn state_machine(&self) -> Result<&LpStateMachine> { - self.state_machine.as_ref().ok_or_else(|| { - LpClientError::transport( - "State machine not available - has the handshake been completed?", - ) - }) - } - fn state_machine_mut(&mut self) -> Result<&mut LpStateMachine> { - self.state_machine.as_mut().ok_or_else(|| { - LpClientError::transport( - "State machine not available - has the handshake been completed?", - ) - }) + self.state_machine + .as_mut() + .ok_or(LpClientError::IncompleteHandshake) } - /// Attempt to parse received bytes into an LpPacket - fn parse_received_lp_packet(&self, response_bytes: Vec) -> Result { - let state_machine = self.state_machine()?; - let outer_key = state_machine.session()?.outer_aead_key(); - Self::parse_packet(&response_bytes, Some(outer_key)) - } - - /// Attempt to wrap the provided `LpData` into a `ForwardPacketData` + /// Attempt to wrap the provided `LpData` into a `EncryptedLpPacket` /// using the inner state machine. - fn prepare_forward_packet(&mut self, data: LpData) -> Result { + fn prepare_transport_packet(&mut self, data: LpData) -> Result { let state_machine = self.state_machine_mut()?; - let inner_packet_bytes = prepare_serialised_send_packet(data, state_machine)?; - Ok(ForwardPacketData::new( - self.gateway_lp_peer.ed25519(), - self.exit_address, - inner_packet_bytes, - )) + prepare_send_packet(data, state_machine) } - /// Attempt to recover received `LpData` from the received `LpPacket` + /// Attempt to recover received `LpData` from the received `EncryptedLpPacket` /// using the inner state machine. - fn extract_forwarded_response(&mut self, response_packet: LpPacket) -> Result { + fn extract_forwarded_response(&mut self, response_packet: EncryptedLpPacket) -> Result { let state_machine = self.state_machine_mut()?; extract_forwarded_response(response_packet, state_machine) } @@ -167,10 +144,8 @@ impl NestedLpSession { /// through the entry gateway. /// /// This method: - /// 1. Generates ClientHello for exit gateway - /// 2. Creates LP state machine for exit handshake - /// 3. Runs handshake loop, forwarding all packets through entry gateway - /// 4. Stores established session in internal state machine + /// 1. Runs handshake loop, forwarding all packets through entry gateway + /// 2. Stores established session in internal state machine /// /// # Arguments /// * `outer_client` - Connected LP client with established outer session to entry gateway @@ -181,38 +156,40 @@ impl NestedLpSession { /// - Forwarding through entry gateway fails /// - Exit gateway handshake fails /// - Cryptographic operations fail - async fn perform_handshake( + pub async fn perform_handshake( &mut self, outer_client: &mut LpRegistrationClient, ) -> Result<()> where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, { + if self.lp_local_peer.ciphersuite().kem() == KEM::McEliece { + return Err(LpClientError::UnsupportedNestedMcEliece); + } + tracing::debug!( "Starting nested LP handshake with exit gateway {}", self.exit_address ); - let mut nested_connection = - outer_client.as_nested_connection(self.gateway_lp_peer.ed25519(), self.exit_address); + let mut nested_connection = outer_client.as_nested_connection(self.exit_address); let local_peer = self.lp_local_peer.clone(); let remote_peer = self.gateway_lp_peer.clone(); let protocol_version = self.gateway_supported_lp_protocol_version; - let ciphersuite = LpSession::default_ciphersuite(); - let session = LpSession::complete_as_initiator( + let session = LpSession::psq_handshake_initiator( &mut nested_connection, - ciphersuite, local_peer, remote_peer, protocol_version, ) - .complete_as_initiator() + .complete_handshake() .await?; // Store the state machine (with established session) for later use self.state_machine = Some(LpStateMachine::new(session)); + debug!("completed nested handshake"); Ok(()) } @@ -246,9 +223,10 @@ impl NestedLpSession { ticket_type: TicketType, ) -> Result where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, { tracing::debug!("Acquiring bandwidth credential for registration"); + let mut nested_connection = outer_client.as_nested_connection(self.exit_address); // Step 1: Get bandwidth credential from controller let credential_spending = bandwidth_controller @@ -276,18 +254,20 @@ impl NestedLpSession { let send_data = request.to_lp_data()?; // Step 4: Encrypt and prepare packet via state machine - let forward_packet = self.prepare_forward_packet(send_data)?; + let forward_packet = self.prepare_transport_packet(send_data)?; // Step 5: Send the encrypted packet via forwarding - let response_bytes = outer_client - .send_forward_packet_with_response(forward_packet) + nested_connection + .send_length_prefixed_transport_packet(&forward_packet) .await?; - // Step 6: Parse response bytes to LP packet - let response_packet = self.parse_received_lp_packet(response_bytes)?; + // Step 6: wait for response + let response = nested_connection + .receive_length_prefixed_transport_packet() + .await?; - // Step 7: Decrypt via state machine - let response_data = self.extract_forwarded_response(response_packet)?; + // Step 7: Process via state machine + let response_data = self.extract_forwarded_response(response)?; // Step 8: Extract decrypted data and deserialise the response let response = LpRegistrationResponse::from_lp_data(response_data)?; @@ -314,13 +294,12 @@ impl NestedLpSession { } } - /// Performs handshake and registration with the exit gateway via forwarding. + /// Performs dVPN registration with the exit gateway via forwarding. /// /// This is the main entry point for nested LP registration. It: - /// 1. Performs handshake with exit gateway (via `perform_handshake`) - /// 2. Builds and sends registration request through the forwarded connection - /// 3. Receives and processes registration response - /// 4. Returns gateway data on successful registration + /// 1. Builds and sends registration request through the forwarded connection + /// 2. Receives and processes registration response + /// 3. Returns gateway data on successful registration /// /// # Arguments /// * `outer_client` - Connected LP client with established outer session to entry gateway @@ -341,7 +320,7 @@ impl NestedLpSession { /// - Forwarding through entry gateway fails /// - Response decryption/deserialization fails /// - Gateway rejects the registration - pub async fn handshake_and_register_dvpn( + pub async fn register_dvpn( &mut self, outer_client: &mut LpRegistrationClient, rng: &mut R, @@ -351,11 +330,10 @@ impl NestedLpSession { ticket_type: TicketType, ) -> Result where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, R: RngCore + CryptoRng, { - // Step 1: Perform handshake with exit gateway via forwarding - self.perform_handshake(outer_client).await?; + let mut nested_connection = outer_client.as_nested_connection(self.exit_address); tracing::debug!("Building registration request for exit gateway"); @@ -370,20 +348,21 @@ impl NestedLpSession { let send_data = request.to_lp_data()?; // Step 4: Encrypt and prepare packet via state machine - let forward_packet = self.prepare_forward_packet(send_data)?; + let forward_packet = self.prepare_transport_packet(send_data)?; // Step 5: Send the encrypted packet via forwarding - let response_bytes = outer_client - .send_forward_packet_with_response(forward_packet) + nested_connection + .send_length_prefixed_transport_packet(&forward_packet) .await?; + // Step 6: wait for response + let response = nested_connection + .receive_length_prefixed_transport_packet() + .await?; tracing::trace!("Received registration response from exit gateway"); - // Step 6: Parse response bytes to LP packet - let response_packet = self.parse_received_lp_packet(response_bytes)?; - - // Step 7: Decrypt via state machine - let response_data = self.extract_forwarded_response(response_packet)?; + // Step 7: Process via state machine + let response_data = self.extract_forwarded_response(response)?; // Step 8: Extract decrypted data and deserialise the response let response = LpRegistrationResponse::from_lp_data(response_data)?; @@ -426,6 +405,60 @@ impl NestedLpSession { }) } + /// Performs handshake and registration with the exit gateway via forwarding. + /// + /// This is the main entry point for nested LP registration. It: + /// 1. Performs handshake with exit gateway (via `perform_handshake`) + /// 2. Builds and sends registration request through the forwarded connection + /// 3. Receives and processes registration response + /// 4. Returns gateway data on successful registration + /// + /// # Arguments + /// * `outer_client` - Connected LP client with established outer session to entry gateway + /// * `wg_keypair` - Client's WireGuard x25519 keypair + /// * `gateway_identity` - Exit gateway's Ed25519 identity (for credential verification) + /// * `bandwidth_controller` - Provider for bandwidth credentials + /// * `ticket_type` - Type of bandwidth ticket to use + /// * `client_ip` - Client IP address for registration metadata + /// + /// # Returns + /// * `Ok(GatewayData)` - Exit gateway configuration data on successful registration + /// + /// # Errors + /// Returns an error if: + /// - Handshake fails + /// - Credential acquisition fails + /// - Request serialization/encryption fails + /// - Forwarding through entry gateway fails + /// - Response decryption/deserialization fails + /// - Gateway rejects the registration + pub(crate) async fn handshake_and_register_dvpn( + &mut self, + outer_client: &mut LpRegistrationClient, + rng: &mut R, + wg_keypair: &x25519::KeyPair, + gateway_identity: &ed25519::PublicKey, + bandwidth_controller: &dyn BandwidthTicketProvider, + ticket_type: TicketType, + ) -> Result + where + S: LpTransportChannel + LpHandshakeChannel + Unpin, + R: RngCore + CryptoRng, + { + // Step 1: Perform handshake with exit gateway via forwarding + self.perform_handshake(outer_client).await?; + + self.register_dvpn( + outer_client, + rng, + wg_keypair, + gateway_identity, + bandwidth_controller, + ticket_type, + ) + .await + } + /// Performs handshake and registration with the exit gateway via forwarding, /// with automatic retry on network failure. /// @@ -466,7 +499,7 @@ impl NestedLpSession { max_retries: u32, ) -> Result where - S: LpTransport + Unpin, + S: LpTransportChannel + LpHandshakeChannel + Unpin, R: RngCore + CryptoRng, { tracing::debug!( @@ -479,14 +512,14 @@ impl NestedLpSession { if attempt > 0 { // Verify outer session is still usable before retry if !outer_client.is_handshake_complete() { - return Err(LpClientError::Transport( + return Err(LpClientError::Other( "Outer session lost during retry - caller must re-establish entry gateway connection".to_string() )); } // Exponential backoff with jitter: 100ms, 200ms, 400ms, 800ms, 1600ms (capped) let base_delay_ms = 100u64 * (1 << attempt.min(4)); - let jitter_ms = rand::random::() % (base_delay_ms / 4 + 1); + let jitter_ms: u64 = rand09::rng().random_range(0..(base_delay_ms / 4 + 1)); let delay = std::time::Duration::from_millis(base_delay_ms + jitter_ms); tracing::info!( "Retrying exit registration (attempt {}) after {:?}", @@ -526,24 +559,8 @@ impl NestedLpSession { } } - Err(last_error.unwrap_or_else(|| { - LpClientError::Transport("Exit registration failed after all retries".to_string()) + Err(last_error.unwrap_or(LpClientError::RegistrationFailure { + message: "Exit Registration failed after all retries".to_string(), })) } - - /// Parses an LP packet from bytes. - /// - /// # Arguments - /// * `bytes` - The bytes to parse - /// - /// # Returns - /// * `Ok(LpPacket)` - Parsed LP packet - /// - /// # Errors - /// Returns an error if parsing fails - fn parse_packet(bytes: &[u8], outer_key: Option<&OuterAeadKey>) -> Result { - // Use outer AEAD key when available (after PSK derivation) - parse_lp_packet(bytes, outer_key) - .map_err(|e| LpClientError::Transport(format!("Failed to parse LP packet: {e}"))) - } } diff --git a/nym-registration-client/src/lp_client/state_machine_helpers.rs b/nym-registration-client/src/lp_client/state_machine_helpers.rs index b8be242621..bfa3f9edc5 100644 --- a/nym-registration-client/src/lp_client/state_machine_helpers.rs +++ b/nym-registration-client/src/lp_client/state_machine_helpers.rs @@ -2,85 +2,37 @@ // SPDX-License-Identifier: Apache-2.0 use crate::LpClientError; -use bytes::BytesMut; -use nym_lp::codec::{OuterAeadKey, serialize_lp_packet}; use nym_lp::state_machine::{LpAction, LpData, LpInput}; -use nym_lp::{LpPacket, LpStateMachine}; - -/// Serializes an LP packet to bytes. -/// -/// # Arguments -/// * `packet` - The LP packet to serialize -/// -/// # Returns -/// * `Ok(Vec)` - Serialized packet bytes -/// -/// # Errors -/// Returns an error if serialization fails -pub(crate) fn serialize_packet( - packet: &LpPacket, - outer_key: Option<&OuterAeadKey>, -) -> Result, LpClientError> { - let mut buf = BytesMut::new(); - // Use outer AEAD key when available (after PSK derivation) - serialize_lp_packet(packet, &mut buf, outer_key) - .map_err(|e| LpClientError::Transport(format!("Failed to serialize LP packet: {}", e)))?; - Ok(buf.to_vec()) -} +use nym_lp::{LpStateMachine, packet::EncryptedLpPacket}; /// Attempt to prepare the provided data for sending by wrapping it in appropriate `LpAction`, -/// and attempting to extract `LpPacket` from the provided srtate machine. +/// and attempting to extract `EncryptedLpPacket` from the provided state machine. pub(crate) fn prepare_send_packet( data: LpData, state_machine: &mut LpStateMachine, -) -> Result { +) -> Result { let action = state_machine .process_input(LpInput::SendData(data)) - .ok_or_else(|| LpClientError::transport("State machine returned no action"))? - .map_err(|e| { - LpClientError::SendRegistrationRequest(format!( - "Failed to encrypt registration request: {e}", - )) - })?; + .ok_or(LpClientError::UnexpectedStateMachineHalt)??; match action { LpAction::SendPacket(packet) => Ok(packet), - other => Err(LpClientError::Transport(format!( - "Unexpected action when trying to send packet data: {other:?}", - ))), + action => Err(LpClientError::UnexpectedStateMachineAction { action }), } } -/// Attempt to prepare the provided data for sending by wrapping it in appropriate `LpAction`, -/// serialising and finally encrypting (if appropriate key is available) the resultant `LpPacket` -/// It uses the provided state machine. -pub(crate) fn prepare_serialised_send_packet( - data: LpData, - state_machine: &mut LpStateMachine, -) -> Result, LpClientError> { - let packet = prepare_send_packet(data, state_machine)?; - let send_key = state_machine.session()?.outer_aead_key(); - - serialize_packet(&packet, Some(send_key)) -} - /// Attempt to recover received `LpData` from the received `LpPacket` /// using the provided state machine. pub(crate) fn extract_forwarded_response( - response_packet: LpPacket, + response_packet: EncryptedLpPacket, state_machine: &mut LpStateMachine, ) -> Result { let action = state_machine .process_input(LpInput::ReceivePacket(response_packet)) - .ok_or_else(|| LpClientError::Transport("State machine returned no action".to_string()))? - .map_err(|e| { - LpClientError::Transport(format!("Failed to decrypt received response: {e}")) - })?; + .ok_or(LpClientError::UnexpectedStateMachineHalt)??; match action { LpAction::DeliverData(data) => Ok(data), - other => Err(LpClientError::Transport(format!( - "Unexpected action when receiving response: {other:?}" - ))), + action => Err(LpClientError::UnexpectedStateMachineAction { action }), } } diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index dd7d19c7f7..7b049ffd8a 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -1255,6 +1255,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-models" +version = "0.0.5" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "hax-lib", + "pastey", + "rand 0.9.2", +] + [[package]] name = "cosmos-sdk-proto" version = "0.27.0" @@ -2601,15 +2611,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.7+wasi-0.2.4", "wasm-bindgen", ] @@ -2871,6 +2881,43 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +[[package]] +name = "hax-lib" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "543f93241d32b3f00569201bfce9d7a93c92c6421b23c77864ac929dc947b9fc" +dependencies = [ + "hax-lib-macros", + "num-bigint", + "num-traits", +] + +[[package]] +name = "hax-lib-macros" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8755751e760b11021765bb04cb4a6c4e24742688d9f3aa14c2079638f537b0f" +dependencies = [ + "hax-lib-macros-types", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "hax-lib-macros-types" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f177c9ae8ea456e2f71ff3c1ea47bf4464f772a05133fcbba56cd5ba169035a2" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "heck" version = "0.4.1" @@ -2943,7 +2990,7 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.0", + "rand 0.9.2", "ring", "rustls 0.23.36", "thiserror 2.0.12", @@ -2968,7 +3015,7 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.0", + "rand 0.9.2", "resolv-conf", "rustls 0.23.36", "smallvec", @@ -3659,7 +3706,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -3804,6 +3851,240 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "libcrux-aesgcm" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-secrets", + "libcrux-traits", +] + +[[package]] +name = "libcrux-chacha20poly1305" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-poly1305", + "libcrux-secrets", + "libcrux-traits", +] + +[[package]] +name = "libcrux-curve25519" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-secrets", + "libcrux-traits", +] + +[[package]] +name = "libcrux-ecdh" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-curve25519", + "libcrux-p256", + "rand 0.9.2", + "tls_codec", +] + +[[package]] +name = "libcrux-ed25519" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-sha2", + "rand_core 0.9.3", + "tls_codec", +] + +[[package]] +name = "libcrux-hacl-rs" +version = "0.0.4" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-macros", +] + +[[package]] +name = "libcrux-hkdf" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-hmac", + "libcrux-secrets", +] + +[[package]] +name = "libcrux-hmac" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-sha2", +] + +[[package]] +name = "libcrux-intrinsics" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "core-models", + "hax-lib", +] + +[[package]] +name = "libcrux-kem" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-curve25519", + "libcrux-ecdh", + "libcrux-ml-kem", + "libcrux-p256", + "libcrux-sha3", + "libcrux-traits", + "rand 0.9.2", + "tls_codec", +] + +[[package]] +name = "libcrux-macros" +version = "0.0.3" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "quote", + "syn 2.0.100", +] + +[[package]] +name = "libcrux-ml-dsa" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "core-models", + "hax-lib", + "libcrux-intrinsics", + "libcrux-macros", + "libcrux-platform", + "libcrux-sha3", + "tls_codec", +] + +[[package]] +name = "libcrux-ml-kem" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-secrets", + "libcrux-sha3", + "libcrux-traits", + "rand 0.9.2", + "tls_codec", +] + +[[package]] +name = "libcrux-p256" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-secrets", + "libcrux-sha2", + "libcrux-traits", +] + +[[package]] +name = "libcrux-platform" +version = "0.0.3" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libc", +] + +[[package]] +name = "libcrux-poly1305" +version = "0.0.4" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", +] + +[[package]] +name = "libcrux-psq" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-aesgcm", + "libcrux-chacha20poly1305", + "libcrux-ecdh", + "libcrux-ed25519", + "libcrux-hkdf", + "libcrux-hmac", + "libcrux-kem", + "libcrux-ml-dsa", + "libcrux-ml-kem", + "libcrux-sha2", + "libcrux-traits", + "rand 0.9.2", + "tls_codec", +] + +[[package]] +name = "libcrux-secrets" +version = "0.0.5" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "hax-lib", +] + +[[package]] +name = "libcrux-sha2" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-traits", +] + +[[package]] +name = "libcrux-sha3" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-traits", +] + +[[package]] +name = "libcrux-traits" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-secrets", + "rand 0.9.2", +] + [[package]] name = "libloading" version = "0.7.4" @@ -4356,6 +4637,8 @@ dependencies = [ "curve25519-dalek", "ed25519-dalek", "jwt-simple", + "libcrux-curve25519", + "libcrux-psq", "nym-pemstore", "rand 0.8.5", "serde", @@ -4480,6 +4763,7 @@ name = "nym-kkt-ciphersuite" version = "1.20.4" dependencies = [ "num_enum", + "semver", "strum", "strum_macros", "thiserror 2.0.12", @@ -5242,6 +5526,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" + [[package]] name = "pathdiff" version = "0.2.3" @@ -5711,6 +6001,28 @@ dependencies = [ "version_check", ] +[[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.100", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -5807,8 +6119,8 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.2", - "rand 0.9.0", + "getrandom 0.3.3", + "rand 0.9.2", "ring", "rustc-hash", "rustls 0.23.36", @@ -5876,13 +6188,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.24", ] [[package]] @@ -5939,7 +6250,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -7638,7 +7949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix 1.0.5", "windows-sys 0.59.0", @@ -7881,6 +8192,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "tokio" version = "1.47.1" @@ -8398,7 +8730,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "serde", ] @@ -8494,11 +8826,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" dependencies = [ - "wit-bindgen-rt", + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", ] [[package]] @@ -9408,13 +9749,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", -] +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "wl-clipboard-rs" diff --git a/tools/nym-lp-client/Cargo.toml b/tools/nym-lp-client/Cargo.toml index d3e6b08bd5..e91e052662 100644 --- a/tools/nym-lp-client/Cargo.toml +++ b/tools/nym-lp-client/Cargo.toml @@ -15,6 +15,7 @@ anyhow = { workspace = true } bytes = { workspace = true } clap = { workspace = true, features = ["derive"] } rand = { workspace = true } +rand09 = { workspace = true } rand_chacha = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/tools/nym-lp-client/src/client.rs b/tools/nym-lp-client/src/client.rs index 3be0bc18b2..881a781392 100644 --- a/tools/nym-lp-client/src/client.rs +++ b/tools/nym-lp-client/src/client.rs @@ -20,7 +20,8 @@ use nym_sphinx_anonymous_replies::requests::{AnonymousSenderTag, RepliableMessag use nym_sphinx_anonymous_replies::{ReplySurb, SurbEncryptionKey}; use nym_sphinx_framing::codec::NymCodec; use nym_sphinx_framing::packet::FramedNymPacket; -use rand_chacha::rand_core::SeedableRng; +use rand::SeedableRng; +use rand09::SeedableRng as SeedableRng09; use rand_chacha::ChaCha8Rng; use std::net::SocketAddr; use std::sync::Arc; @@ -33,7 +34,7 @@ use tracing::{debug, info, trace}; use crate::topology::{GatewayInfo, SpeedtestTopology}; use nym_ip_packet_requests::v8::request::IpPacketRequest; use nym_lp::packet::version; -use nym_lp::peer::LpRemotePeer; +use nym_lp::peer::{DHKeyPair, LpRemotePeer}; use nym_sphinx::forwarding::packet::MixPacket; /// Conv ID for KCP - hash of source and destination addresses @@ -51,6 +52,8 @@ pub struct SpeedtestClient { identity_keypair: Arc, /// Client's x25519 encryption keypair (for SURBs) encryption_keypair: Arc, + /// Client's LP keypair + lp_keypair: Arc, /// Target gateway gateway: GatewayInfo, /// Network topology for routing @@ -86,11 +89,14 @@ impl SpeedtestClient { pub fn new(gateway: GatewayInfo, topology: Arc) -> Self { let identity_keypair = Arc::new(ed25519::KeyPair::new(&mut rand::rngs::OsRng)); let encryption_keypair = Arc::new(x25519::KeyPair::new(&mut rand::rngs::OsRng)); + let mut rng09 = rand09::rngs::StdRng::from_os_rng(); + let lp_keypair = DHKeyPair::new(&mut rng09); let rng = ChaCha8Rng::from_entropy(); Self { identity_keypair, encryption_keypair, + lp_keypair: Arc::new(lp_keypair), gateway, topology, socket: None, @@ -118,16 +124,14 @@ impl SpeedtestClient { self.gateway.lp_address ); - let gw_peer = LpRemotePeer::new(self.gateway.identity, self.gateway.identity.to_x25519()?) - .with_key_digests( - self.gateway.kem_key_hashes.clone(), - self.gateway.signing_key_hashes.clone(), - ); + let gw_peer = LpRemotePeer::new(self.gateway.lp_key) + .with_key_digests(self.gateway.kem_key_hashes.clone()); let mut lp_client = LpRegistrationClient::::new_with_default_config( - self.identity_keypair.clone(), + self.lp_keypair.clone(), gw_peer, self.gateway.lp_address, + self.gateway.ciphersuite, self.gateway.lp_version, ); @@ -165,16 +169,14 @@ impl SpeedtestClient { self.gateway.lp_address ); - let gw_peer = LpRemotePeer::new(self.gateway.identity, self.gateway.identity.to_x25519()?) - .with_key_digests( - self.gateway.kem_key_hashes.clone(), - self.gateway.signing_key_hashes.clone(), - ); + let gw_peer = LpRemotePeer::new(self.gateway.lp_key) + .with_key_digests(self.gateway.kem_key_hashes.clone()); let mut lp_client = LpRegistrationClient::new_with_default_config( - self.identity_keypair.clone(), + self.lp_keypair.clone(), gw_peer, self.gateway.lp_address, + self.gateway.ciphersuite, self.gateway.lp_version, ); @@ -440,61 +442,65 @@ impl SpeedtestClient { if !self.has_lp_session() { bail!("LP session not initialized - call init_lp_session() first"); } + let _ = payload; + let _ = num_surbs; + bail!("lp transport channel is not yet implemented"); - let prepared = self.prepare_sphinx_fragments(payload, num_surbs).await?; - - // Now get mutable references after prepare_sphinx_fragments is done - let lp_client = self.lp_client.as_mut().unwrap(); // safe: checked above - let socket = self.socket.as_ref().context("socket not initialized")?; - let lp_data_address = self.gateway.lp_data_address; - - let mut total_sent = 0usize; - let fragment_count = prepared.fragments.len(); - - for fragment in prepared.fragments { - let nym_packet = NymPacket::sphinx_build( - false, - PacketSize::RegularPacket.payload_size(), - fragment.into_bytes(), - &prepared.route, - &prepared.destination, - &prepared.delays, - )?; - - // Wrap in MixPacket v2: packet_type || key_rotation || next_hop || sphinx_data - let mix_packet = MixPacket::new( - prepared.first_hop_addr, - nym_packet, - PacketType::Mix, - SphinxKeyRotation::Unknown, - ); - - let mix_bytes = mix_packet - .into_v2_bytes() - .context("failed to serialize MixPacket")?; - - // Wrap in LP for UDP data plane - let lp_packet = lp_client - .wrap_data(&mix_bytes) - .context("failed to wrap in LP")?; - - // Send to gateway's LP data port (51264) with timeout - tokio::time::timeout( - Duration::from_secs(5), - socket.send_to(&lp_packet, lp_data_address), - ) - .await - .context("UDP send timed out")? - .context("UDP send failed")?; - total_sent += lp_packet.len(); - } - - info!( - "Sent {} bytes via LP ({} fragments, {} SURBs) to {}", - total_sent, fragment_count, num_surbs, lp_data_address - ); - - Ok(prepared.encryption_keys) + // leave the code for future reference + // let prepared = self.prepare_sphinx_fragments(payload, num_surbs).await?; + // + // // Now get mutable references after prepare_sphinx_fragments is done + // let lp_client = self.lp_client.as_mut().unwrap(); // safe: checked above + // let socket = self.socket.as_ref().context("socket not initialized")?; + // let lp_data_address = self.gateway.lp_data_address; + // + // let mut total_sent = 0usize; + // let fragment_count = prepared.fragments.len(); + // + // for fragment in prepared.fragments { + // let nym_packet = NymPacket::sphinx_build( + // false, + // PacketSize::RegularPacket.payload_size(), + // fragment.into_bytes(), + // &prepared.route, + // &prepared.destination, + // &prepared.delays, + // )?; + // + // // Wrap in MixPacket v2: packet_type || key_rotation || next_hop || sphinx_data + // let mix_packet = MixPacket::new( + // prepared.first_hop_addr, + // nym_packet, + // PacketType::Mix, + // SphinxKeyRotation::Unknown, + // ); + // + // let mix_bytes = mix_packet + // .into_v2_bytes() + // .context("failed to serialize MixPacket")?; + // + // // Wrap in LP for UDP data plane + // let lp_packet = lp_client + // .wrap_data(&mix_bytes) + // .context("failed to wrap in LP")?; + // + // // Send to gateway's LP data port (51264) with timeout + // tokio::time::timeout( + // Duration::from_secs(5), + // socket.send_to(&lp_packet, lp_data_address), + // ) + // .await + // .context("UDP send timed out")? + // .context("UDP send failed")?; + // total_sent += lp_packet.len(); + // } + // + // info!( + // "Sent {} bytes via LP ({} fragments, {} SURBs) to {}", + // total_sent, fragment_count, num_surbs, lp_data_address + // ); + // + // Ok(prepared.encryption_keys) } /// Receive UDP data with timeout diff --git a/tools/nym-lp-client/src/topology.rs b/tools/nym-lp-client/src/topology.rs index 3e0f76b6ff..91fccc9b8c 100644 --- a/tools/nym-lp-client/src/topology.rs +++ b/tools/nym-lp-client/src/topology.rs @@ -10,13 +10,14 @@ use nym_api_requests::models::{LPHashFunction, LPKEM}; use nym_api_requests::nym_nodes::SkimmedNode; use nym_crypto::asymmetric::ed25519; use nym_http_api_client::UserAgent; -use nym_kkt_ciphersuite::{KEMKeyDigests, SignatureScheme, SigningKeyDigests, KEM}; +use nym_kkt_ciphersuite::{Ciphersuite, KEMKeyDigests, SignatureScheme, KEM}; +use nym_lp::peer::DHPublicKey; use nym_sphinx_types::Node as SphinxNode; use nym_topology::{NymRouteProvider, NymTopology, NymTopologyMetadata}; use nym_validator_client::nym_api::NymApiClientExt; use rand::prelude::IteratorRandom; use rand::{CryptoRng, Rng}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::net::SocketAddr; use tracing::{debug, info}; use url::Url; @@ -30,8 +31,9 @@ const LP_DATA_PORT: u16 = 51264; #[derive(Debug, Clone)] pub struct GatewayInfo { pub identity: ed25519::PublicKey, - pub kem_key_hashes: HashMap, - pub signing_key_hashes: HashMap, + pub lp_key: DHPublicKey, + pub kem_key_hashes: BTreeMap, + pub ciphersuite: Ciphersuite, pub sphinx_key: nym_crypto::asymmetric::x25519::PublicKey, /// Mix host (IP:port for Sphinx mixing) diff --git a/wasm/client/.cargo/config.toml b/wasm/client/.cargo/config.toml index a7f24f6737..f506045a7f 100644 --- a/wasm/client/.cargo/config.toml +++ b/wasm/client/.cargo/config.toml @@ -1,6 +1,8 @@ [build] target = "wasm32-unknown-unknown" target_arch = "wasm32" +rustflags = ["--cfg=getrandom_backend=\"wasm_js\""] + [target.wasm32-unknown-unknown] runner = 'wasm-bindgen-test-runner' \ No newline at end of file diff --git a/wasm/client/Cargo.toml b/wasm/client/Cargo.toml index 3bc15e7ade..2377ddcb6d 100644 --- a/wasm/client/Cargo.toml +++ b/wasm/client/Cargo.toml @@ -44,6 +44,7 @@ nym-node-tester-wasm = { path = "../node-tester", optional = true } tokio_with_wasm = { workspace = true, features = ["full"] } + [dev-dependencies] wasm-bindgen-test = { workspace = true } diff --git a/wasm/full-nym-wasm/.cargo/config.toml b/wasm/full-nym-wasm/.cargo/config.toml index bdbe517658..e9c3438b6e 100644 --- a/wasm/full-nym-wasm/.cargo/config.toml +++ b/wasm/full-nym-wasm/.cargo/config.toml @@ -1,3 +1,4 @@ [build] target = "wasm32-unknown-unknown" target_arch = "wasm32" +rustflags = ["--cfg=getrandom_backend=\"wasm_js\""] \ No newline at end of file diff --git a/wasm/mix-fetch/.cargo/config.toml b/wasm/mix-fetch/.cargo/config.toml index bdbe517658..e9c3438b6e 100644 --- a/wasm/mix-fetch/.cargo/config.toml +++ b/wasm/mix-fetch/.cargo/config.toml @@ -1,3 +1,4 @@ [build] target = "wasm32-unknown-unknown" target_arch = "wasm32" +rustflags = ["--cfg=getrandom_backend=\"wasm_js\""] \ No newline at end of file diff --git a/wasm/mix-fetch/src/helpers.rs b/wasm/mix-fetch/src/helpers.rs index 9476d61715..719ab64559 100644 --- a/wasm/mix-fetch/src/helpers.rs +++ b/wasm/mix-fetch/src/helpers.rs @@ -25,6 +25,7 @@ pub(crate) async fn get_network_requester( url::Url::parse(&nym_api_url.unwrap_or(NYM_API_URL.to_string()))?, None, ); + #[allow(deprecated)] let nodes = client.get_all_described_nodes().await?; let providers: Vec<_> = nodes .iter() diff --git a/wasm/node-tester/.cargo/config.toml b/wasm/node-tester/.cargo/config.toml index bdbe517658..e9c3438b6e 100644 --- a/wasm/node-tester/.cargo/config.toml +++ b/wasm/node-tester/.cargo/config.toml @@ -1,3 +1,4 @@ [build] target = "wasm32-unknown-unknown" target_arch = "wasm32" +rustflags = ["--cfg=getrandom_backend=\"wasm_js\""] \ No newline at end of file diff --git a/wasm/zknym-lib/.cargo/config.toml b/wasm/zknym-lib/.cargo/config.toml index bdbe517658..e9c3438b6e 100644 --- a/wasm/zknym-lib/.cargo/config.toml +++ b/wasm/zknym-lib/.cargo/config.toml @@ -1,3 +1,4 @@ [build] target = "wasm32-unknown-unknown" target_arch = "wasm32" +rustflags = ["--cfg=getrandom_backend=\"wasm_js\""] \ No newline at end of file diff --git a/wasm/zknym-lib/Cargo.toml b/wasm/zknym-lib/Cargo.toml index b9d086535e..385ed467f1 100644 --- a/wasm/zknym-lib/Cargo.toml +++ b/wasm/zknym-lib/Cargo.toml @@ -16,31 +16,21 @@ crate-type = ["cdylib", "rlib"] [dependencies] async-trait.workspace = true -bs58.workspace = true -getrandom = { workspace = true, features = ["js"] } js-sys.workspace = true wasm-bindgen.workspace = true serde = { workspace = true, features = ["derive"] } thiserror.workspace = true tsify = { workspace = true, features = ["js"] } uuid = { workspace = true, features = ["serde"] } -reqwest = { workspace = true } wasmtimer = { workspace = true } zeroize.workspace = true -rand = { workspace = true } - - nym-bin-common = { workspace = true } nym-compact-ecash = { workspace = true } -nym-credentials = { workspace = true } -nym-crypto = { workspace = true, features = ["asymmetric", "rand"] } nym-http-api-client = { workspace = true } nym-wasm-utils = { workspace = true } [dev-dependencies] -anyhow = { workspace = true } -tokio = { workspace = true, features = ["full"] } [package.metadata.wasm-pack.profile.release] From 5093450004617bd65cb56de15ef2cb0bed122394 Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Mon, 2 Mar 2026 10:44:54 +0100 Subject: [PATCH 46/51] bump versions --- Cargo.lock | 14 +++++++------- clients/native/Cargo.toml | 2 +- clients/socks5/Cargo.toml | 2 +- nym-api/Cargo.toml | 2 +- nym-node/Cargo.toml | 2 +- service-providers/network-requester/Cargo.toml | 2 +- tools/nym-cli/Cargo.toml | 2 +- tools/nymvisor/Cargo.toml | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d6366ccc6..937a30240a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5346,7 +5346,7 @@ dependencies = [ [[package]] name = "nym-api" -version = "1.1.74" +version = "1.1.75" dependencies = [ "anyhow", "async-trait", @@ -5591,7 +5591,7 @@ dependencies = [ [[package]] name = "nym-cli" -version = "1.1.71" +version = "1.1.72" dependencies = [ "anyhow", "base64 0.22.1", @@ -5674,7 +5674,7 @@ dependencies = [ [[package]] name = "nym-client" -version = "1.1.71" +version = "1.1.72" dependencies = [ "bs58", "clap", @@ -7076,7 +7076,7 @@ dependencies = [ [[package]] name = "nym-network-requester" -version = "1.1.72" +version = "1.1.73" dependencies = [ "addr", "anyhow", @@ -7126,7 +7126,7 @@ dependencies = [ [[package]] name = "nym-node" -version = "1.26.0" +version = "1.27.0" dependencies = [ "anyhow", "arc-swap", @@ -7672,7 +7672,7 @@ dependencies = [ [[package]] name = "nym-socks5-client" -version = "1.1.71" +version = "1.1.72" dependencies = [ "bs58", "clap", @@ -8469,7 +8469,7 @@ dependencies = [ [[package]] name = "nymvisor" -version = "0.1.36" +version = "0.1.37" dependencies = [ "anyhow", "bytes", diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index e0ac34bec8..912ad2f1a3 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-client" -version = "1.1.71" +version = "1.1.72" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] description = "Implementation of the Nym Client" edition = "2021" diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index e1bded672c..ab8edde745 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-socks5-client" -version = "1.1.71" +version = "1.1.72" authors = ["Dave Hrycyszyn "] description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" edition = "2021" diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 5357e1e94e..d80dbe1916 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "nym-api" license = "GPL-3.0" -version = "1.1.74" +version = "1.1.75" authors.workspace = true edition = "2021" rust-version.workspace = true diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index 4845997d7b..b17f5272f5 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-node" -version = "1.26.0" +version = "1.27.0" authors.workspace = true repository.workspace = true homepage.workspace = true diff --git a/service-providers/network-requester/Cargo.toml b/service-providers/network-requester/Cargo.toml index abf59b66f3..652a870dd1 100644 --- a/service-providers/network-requester/Cargo.toml +++ b/service-providers/network-requester/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "nym-network-requester" license = "GPL-3.0" -version = "1.1.72" +version = "1.1.73" authors.workspace = true edition.workspace = true rust-version = "1.85" diff --git a/tools/nym-cli/Cargo.toml b/tools/nym-cli/Cargo.toml index bc102b46bd..e3e448a386 100644 --- a/tools/nym-cli/Cargo.toml +++ b/tools/nym-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-cli" -version = "1.1.71" +version = "1.1.72" authors.workspace = true edition = "2021" license.workspace = true diff --git a/tools/nymvisor/Cargo.toml b/tools/nymvisor/Cargo.toml index e5a62b3d63..d03fd308c9 100644 --- a/tools/nymvisor/Cargo.toml +++ b/tools/nymvisor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nymvisor" -version = "0.1.36" +version = "0.1.37" authors.workspace = true repository.workspace = true homepage.workspace = true From 05b6f5e2821d465a0c395d939d64784f972271b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Tue, 3 Mar 2026 13:58:47 +0000 Subject: [PATCH 47/51] removed redundant LP states (#6509) --- common/nym-lp/src/codec.rs | 21 +- common/nym-lp/src/packet/error.rs | 8 +- common/nym-lp/src/packet/header.rs | 21 +- common/nym-lp/src/packet/message.rs | 450 ++++-------------- common/nym-lp/src/packet/mod.rs | 12 +- common/nym-lp/src/session.rs | 13 +- common/nym-lp/src/session_integration/mod.rs | 26 +- common/nym-lp/src/session_manager.rs | 10 +- common/nym-lp/src/state_machine.rs | 115 +---- integration-tests/src/lp_registration.rs | 3 +- nym-node/src/node/lp/error.rs | 5 +- nym-node/src/node/lp/handler.rs | 28 +- .../src/lp_client/error.rs | 5 +- .../src/lp_client/helpers.rs | 27 +- .../src/lp_client/nested_session/mod.rs | 11 +- .../src/lp_client/state_machine_helpers.rs | 7 +- 16 files changed, 211 insertions(+), 551 deletions(-) diff --git a/common/nym-lp/src/codec.rs b/common/nym-lp/src/codec.rs index 276df6c26a..794f7a2b49 100644 --- a/common/nym-lp/src/codec.rs +++ b/common/nym-lp/src/codec.rs @@ -48,7 +48,7 @@ pub(crate) fn encrypt_lp_packet( ) -> Result { let mut plaintext = BytesMut::with_capacity(InnerHeader::SIZE + packet.message().len()); packet.header().inner.encode(&mut plaintext); - packet.message().encode_content(&mut plaintext); + packet.message().encode(&mut plaintext); let ciphertext = encrypt_data(plaintext.as_ref(), transport)?; @@ -67,7 +67,7 @@ pub(crate) fn decrypt_lp_packet( let inner_header = InnerHeader::parse(&plaintext)?; let payload = &plaintext[InnerHeader::SIZE..]; - let message = LpMessage::decode_content(payload, inner_header.message_type)?; + let message = LpMessage::decode(payload)?; Ok(LpPacket::new( LpHeader { @@ -82,7 +82,7 @@ pub(crate) fn decrypt_lp_packet( mod tests { use crate::LpError; use crate::codec::{decrypt_data, decrypt_lp_packet, encrypt_data, encrypt_lp_packet}; - use crate::packet::{EncryptedLpPacket, LpHeader, LpMessage, LpPacket, MessageType}; + use crate::packet::{EncryptedLpPacket, LpHeader, LpMessage, LpPacket}; use crate::peer::mock_peers; use crate::psq::initiator::{build_psq_ciphersuite, build_psq_principal}; use crate::psq::{PSQ_MSG2_SIZE, psq_msg1_size, responder}; @@ -259,7 +259,10 @@ mod tests { let (mut init_transport, mut resp_transport) = mock_transport(); // happy path - let packet = LpPacket::new(LpHeader::new(123, 0, 1, MessageType::Busy), LpMessage::Busy); + let packet = LpPacket::new( + LpHeader::new(123, 0, 1), + LpMessage::new_opaque(b"foomp".to_vec()), + ); let ciphertext = encrypt_lp_packet(packet.clone(), &mut init_transport).unwrap(); assert_eq!(packet.header().outer, ciphertext.outer_header()); @@ -268,7 +271,10 @@ mod tests { assert_eq!(packet, plaintext); // incomplete ciphertext - let packet = LpPacket::new(LpHeader::new(123, 1, 1, MessageType::Busy), LpMessage::Busy); + let packet = LpPacket::new( + LpHeader::new(123, 1, 1), + LpMessage::new_opaque(b"foomp".to_vec()), + ); let ciphertext2 = encrypt_lp_packet(packet, &mut init_transport).unwrap(); let l = ciphertext2.ciphertext().len(); let malformed_content = ciphertext2.ciphertext()[..l - 1].to_vec(); @@ -277,7 +283,10 @@ mod tests { assert!(matches!(dec_err, LpError::PSQSessionFailure { .. })); // too small buffer - let packet = LpPacket::new(LpHeader::new(123, 1, 1, MessageType::Busy), LpMessage::Busy); + let packet = LpPacket::new( + LpHeader::new(123, 1, 1), + LpMessage::new_opaque(b"foomp".to_vec()), + ); let ciphertext3 = encrypt_lp_packet(packet, &mut resp_transport).unwrap(); let malformed = EncryptedLpPacket::new(ciphertext3.outer_header(), vec![]); let dec_err = decrypt_lp_packet(malformed, &mut init_transport).unwrap_err(); diff --git a/common/nym-lp/src/packet/error.rs b/common/nym-lp/src/packet/error.rs index 15578f4b4f..baf1146cbb 100644 --- a/common/nym-lp/src/packet/error.rs +++ b/common/nym-lp/src/packet/error.rs @@ -11,8 +11,8 @@ pub enum MalformedLpPacketError { #[error("provided insufficient data to fully deserialise the struct")] InsufficientData, - #[error("{0} is not a valid MessageType")] - InvalidMessageType(u32), + #[error("{0} is not a valid LpDataKind")] + InvalidLpDataKind(u16), #[error("invalid payload size: expected {expected}, got {actual}")] InvalidPayloadSize { expected: usize, actual: usize }, @@ -27,7 +27,7 @@ pub enum MalformedLpPacketError { } impl MalformedLpPacketError { - pub fn invalid_message_type(message_type: u32) -> Self { - MalformedLpPacketError::InvalidMessageType(message_type) + pub fn invalid_data_kind(message_type: u16) -> Self { + MalformedLpPacketError::InvalidLpDataKind(message_type) } } diff --git a/common/nym-lp/src/packet/header.rs b/common/nym-lp/src/packet/header.rs index fb4653efb8..4055284f58 100644 --- a/common/nym-lp/src/packet/header.rs +++ b/common/nym-lp/src/packet/header.rs @@ -1,11 +1,9 @@ // Copyright 2026 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::packet::message::MessageType; use crate::packet::version; use crate::{packet::error::MalformedLpPacketError, peer_config::LpReceiverIndex}; use bytes::{BufMut, BytesMut}; -// use nym_lp::peer_config::LpReceiverIndex; use tracing::warn; /// Outer header (12 bytes) - always cleartext, used for routing. @@ -58,11 +56,10 @@ impl OuterHeader { pub struct InnerHeader { pub protocol_version: u8, pub reserved: [u8; 3], - pub message_type: MessageType, } impl InnerHeader { - pub const SIZE: usize = 8; // protocol_version(1) + reserved(3) + message_type(4) + pub const SIZE: usize = 4; // protocol_version(1) + reserved(3) pub fn encode(&self, dst: &mut BytesMut) { // protocol version @@ -70,9 +67,6 @@ impl InnerHeader { // reserved dst.put_slice(&self.reserved); - - // message type - dst.put_slice(&(self.message_type as u32).to_le_bytes()); } pub fn parse(src: &[u8]) -> Result { @@ -104,14 +98,9 @@ impl InnerHeader { warn!("received non-zero reserved bytes. got: {reserved:?}"); } - let msg_type_raw = u32::from_le_bytes([src[4], src[5], src[6], src[7]]); - let message_type = MessageType::from_u32(msg_type_raw) - .ok_or_else(|| MalformedLpPacketError::invalid_message_type(msg_type_raw))?; - Ok(InnerHeader { protocol_version, reserved, - message_type, }) } } @@ -129,12 +118,7 @@ pub struct LpHeader { } impl LpHeader { - pub fn new( - receiver_idx: LpReceiverIndex, - counter: u64, - protocol_version: u8, - message_type: MessageType, - ) -> Self { + pub fn new(receiver_idx: LpReceiverIndex, counter: u64, protocol_version: u8) -> Self { Self { outer: OuterHeader { receiver_idx, @@ -143,7 +127,6 @@ impl LpHeader { inner: InnerHeader { protocol_version, reserved: [0u8; 3], - message_type, }, } } diff --git a/common/nym-lp/src/packet/message.rs b/common/nym-lp/src/packet/message.rs index 84574d1b93..97a2c94fb9 100644 --- a/common/nym-lp/src/packet/message.rs +++ b/common/nym-lp/src/packet/message.rs @@ -2,109 +2,119 @@ // SPDX-License-Identifier: Apache-2.0 use crate::packet::error::MalformedLpPacketError; -use bytes::{BufMut, BytesMut}; +use bytes::{BufMut, Bytes, BytesMut}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use std::fmt; -use std::fmt::Display; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -#[derive(Debug, Copy, Clone, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] -#[repr(u32)] -pub enum MessageType { - /// The party is busy - Busy = 0x0000, - - /// Encrypted payload - EncryptedData = 0x0001, - - /// Receiver should forward this message via telescoping - ForwardPacket = 0x0002, - - /// Receiver index collision - client should retry with new index - Collision = 0x0003, - - /// Acknowledgment - gateway confirms receipt of message - Ack = 0x0004, - - /// General error - Error = 0x00FF, +#[derive(Debug, Clone, PartialEq)] +pub struct LpMessageHeader { + pub kind: LpMessageType, + pub message_attributes: [u8; 14], } -impl MessageType { - pub(crate) fn from_u32(value: u32) -> Option { - MessageType::try_from(value).ok() - } +impl LpMessageHeader { + pub const SIZE: usize = 16; // message_kind(2) + message_attributes(14) - pub fn to_u32(&self) -> u32 { - u32::from(*self) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ApplicationData(pub Vec); - -impl ApplicationData { - pub fn new(bytes: Vec) -> Self { - Self(bytes) - } - - fn len(&self) -> usize { - self.0.len() - } - - fn encode(&self, dst: &mut BytesMut) { - dst.put_slice(&self.0); - } - - fn decode(bytes: &[u8]) -> Result { - Ok(ApplicationData(bytes.to_vec())) - } -} - -/// General human-readable error message -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ErrorPacketData { - pub message: String, -} - -impl ErrorPacketData { - pub fn new(message: impl Into) -> Self { - ErrorPacketData { - message: message.into(), + pub fn new(kind: LpMessageType, message_attributes: [u8; 14]) -> Self { + Self { + kind, + message_attributes, } } - fn len(&self) -> usize { - // length-encoding + message - 4 + self.message.len() - } - - fn encode(&self, dst: &mut BytesMut) { - dst.put_u32_le(self.message.len() as u32); - dst.put_slice(self.message.as_bytes()); - } - - fn decode(bytes: &[u8]) -> Result { - if bytes.len() < 4 { - return Err(MalformedLpPacketError::DeserialisationFailure(format!( - "Too few bytes to deserialise ErrorPacketData. got {}", - bytes.len() - ))); + pub fn new_no_attributes(kind: LpMessageType) -> Self { + Self { + kind, + message_attributes: [0; 14], } - - let message_len = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize; - if bytes[4..].len() != message_len { - return Err(MalformedLpPacketError::DeserialisationFailure(format!( - "Wrong number of bytes to deserialise ErrorPacketData. got {}. Expected {}", - bytes.len(), - 4 + message_len - ))); - } - - let message = String::from_utf8_lossy(&bytes[4..]).to_string(); - - Ok(ErrorPacketData { message }) } + + /// Encode directly into a BytesMut buffer + pub fn encode(&self, dst: &mut BytesMut) { + dst.put_u16_le(self.kind as u16); + dst.put_slice(&self.message_attributes); + } + + pub fn parse(src: &[u8]) -> Result { + if src.len() < Self::SIZE { + return Err(MalformedLpPacketError::InsufficientData); + } + let raw_kind = u16::from_le_bytes([src[0], src[1]]); + + let kind = LpMessageType::try_from(raw_kind) + .map_err(|_| MalformedLpPacketError::invalid_data_kind(raw_kind))?; + + #[allow(clippy::unwrap_used)] + let message_attributes = src[2..16].try_into().unwrap(); + Ok(Self { + kind, + message_attributes, + }) + } +} + +/// Represent application data being sent in Transport mode +#[derive(Debug, Clone, PartialEq)] +pub struct LpMessage { + pub header: LpMessageHeader, + pub content: Bytes, +} + +impl AsRef<[u8]> for LpMessage { + fn as_ref(&self) -> &[u8] { + &self.content + } +} + +impl LpMessage { + pub fn new(kind: LpMessageType, content: impl Into) -> Self { + Self { + header: LpMessageHeader::new_no_attributes(kind), + content: content.into(), + } + } + + pub fn encode(&self, dst: &mut BytesMut) { + self.header.encode(dst); + + dst.put_slice(&self.content); + } + + pub fn decode(src: &[u8]) -> Result { + let header = LpMessageHeader::parse(src)?; + let content = src[LpMessageHeader::SIZE..].to_vec().into(); + + Ok(Self { header, content }) + } + + pub fn kind(&self) -> LpMessageType { + self.header.kind + } + + pub fn new_opaque(content: impl Into) -> Self { + Self::new(LpMessageType::Opaque, content) + } + + pub fn new_registration(data: impl Into) -> Self { + Self::new(LpMessageType::Registration, data) + } + + pub fn new_forward(data: impl Into) -> Self { + Self::new(LpMessageType::Forward, data) + } + + pub(crate) fn len(&self) -> usize { + LpMessageHeader::SIZE + self.content.len() + } +} + +/// Represent kind of application data being sent in Transport mode +#[derive(Clone, Copy, PartialEq, Eq, Debug, IntoPrimitive, TryFromPrimitive)] +#[repr(u16)] +pub enum LpMessageType { + Opaque = 0, + Registration = 1, + Forward = 2, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -163,24 +173,6 @@ impl ForwardPacketData { } } - fn len(&self) -> usize { - // 1 byte length of target lp address type - // + - // {4,16} target_lp_address IPv{4,6} - // + - // 2 bytes target_lp_address port - // + - // 4 bytes for expected response size - // + - // 4 bytes of length of inner packet bytes - // + - // inner_packet_bytes.len() - match self.target_lp_address { - SocketAddr::V4(_) => 1 + 4 + 2 + 4 + 4 + self.inner_packet_bytes.len(), - SocketAddr::V6(_) => 1 + 16 + 2 + 4 + 4 + self.inner_packet_bytes.len(), - } - } - // 0 || [4B ipv4] || [2B port] || [4B res size] || [4B plen] || payload // 1 || [16B ipv6] || [2B port] || [4B res size] || [4B plen] || payload fn encode(&self, dst: &mut BytesMut) { @@ -261,241 +253,3 @@ impl ForwardPacketData { }) } } - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum LpMessage { - /// The party is busy - Busy, - - /// Application payload is being sent - ApplicationData(ApplicationData), - - /// Receiver should forward this message via telescoping - ForwardPacket(ForwardPacketData), - - /// Receiver index collision - client should retry with new receiver_index - Collision, - - /// Acknowledgment - gateway confirms receipt of message - Ack, - - /// An error has occurred - Error(ErrorPacketData), -} - -impl From for LpMessage { - fn from(value: ApplicationData) -> Self { - LpMessage::ApplicationData(value) - } -} - -impl From for LpMessage { - fn from(value: ForwardPacketData) -> Self { - LpMessage::ForwardPacket(value) - } -} - -impl Display for LpMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - LpMessage::Busy => write!(f, "Busy"), - LpMessage::ApplicationData(_) => write!(f, "EncryptedData"), - LpMessage::ForwardPacket(_) => write!(f, "ForwardPacket"), - LpMessage::Collision => write!(f, "Collision"), - LpMessage::Ack => write!(f, "Ack"), - LpMessage::Error(_) => write!(f, "Error"), - } - } -} - -impl LpMessage { - #[deprecated(note = "is it actually needed?")] - pub fn payload(&self) -> &[u8] { - match self { - LpMessage::Busy => &[], - LpMessage::ApplicationData(payload) => payload.0.as_slice(), - LpMessage::ForwardPacket(_) => &[], // Structured data, serialized in encode_content - LpMessage::Collision => &[], - LpMessage::Ack => &[], - LpMessage::Error(_) => &[], // Structured data, serialized in encode_content (?) - } - } - - #[deprecated(note = "is it actually needed?")] - pub fn is_empty(&self) -> bool { - match self { - LpMessage::Busy => true, - LpMessage::ApplicationData(payload) => payload.0.is_empty(), - LpMessage::ForwardPacket(_) => false, // Always has data - LpMessage::Collision => true, - LpMessage::Ack => true, - LpMessage::Error(_) => false, - } - } - - pub fn len(&self) -> usize { - match self { - LpMessage::Busy => 0, - LpMessage::ApplicationData(payload) => payload.len(), - LpMessage::ForwardPacket(payload) => payload.len(), - LpMessage::Collision => 0, - LpMessage::Ack => 0, - LpMessage::Error(payload) => payload.len(), - } - } - - pub fn typ(&self) -> MessageType { - match self { - LpMessage::Busy => MessageType::Busy, - LpMessage::ApplicationData(_) => MessageType::EncryptedData, - LpMessage::ForwardPacket(_) => MessageType::ForwardPacket, - LpMessage::Collision => MessageType::Collision, - LpMessage::Ack => MessageType::Ack, - LpMessage::Error(_) => MessageType::Error, - } - } - - pub fn encode_content(&self, dst: &mut BytesMut) { - match self { - LpMessage::Busy => { /* No content */ } - LpMessage::ApplicationData(payload) => payload.encode(dst), - LpMessage::ForwardPacket(data) => data.encode(dst), - LpMessage::Collision => { /* No content */ } - LpMessage::Ack => { /* No content */ } - LpMessage::Error(data) => data.encode(dst), - } - } - - /// Parse message from its type and content bytes. - /// - /// Used when decrypting outer-encrypted packets where the message type - /// was encrypted along with the content. - pub fn decode_content( - content: &[u8], - message_type: MessageType, - ) -> Result { - match message_type { - MessageType::Busy => { - content.ensure_empty()?; - Ok(LpMessage::Busy) - } - MessageType::EncryptedData => Ok(LpMessage::ApplicationData(ApplicationData::decode( - content, - )?)), - MessageType::ForwardPacket => Ok(LpMessage::ForwardPacket(ForwardPacketData::decode( - content, - )?)), - MessageType::Collision => { - content.ensure_empty()?; - Ok(LpMessage::Collision) - } - MessageType::Ack => { - content.ensure_empty()?; - Ok(LpMessage::Ack) - } - MessageType::Error => Ok(LpMessage::Error(ErrorPacketData::decode(content)?)), - } - } -} - -/// Helper trait for improving readability to return error if bytes content is not empty -trait EnsureEmptyContent { - fn ensure_empty(&self) -> Result<(), MalformedLpPacketError>; -} - -impl EnsureEmptyContent for &[u8] { - fn ensure_empty(&self) -> Result<(), MalformedLpPacketError> { - if !self.is_empty() { - return Err(MalformedLpPacketError::InvalidPayloadSize { - expected: 0, - actual: self.len(), - }); - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::packet::{InnerHeader, LpHeader, LpPacket, OuterHeader}; - - #[test] - fn encoding() { - let message = LpMessage::ApplicationData(ApplicationData(vec![11u8; 124])); - - let resp_header = LpHeader { - outer: OuterHeader { - receiver_idx: 456, - counter: 123, - }, - inner: InnerHeader { - protocol_version: 1, - reserved: [0u8; 3], - message_type: MessageType::EncryptedData, - }, - }; - - let packet = LpPacket { - header: resp_header, - message, - }; - - // Just print packet for debug, will be captured in test output - println!("{packet:?}"); - - // Verify message type - assert!(matches!(packet.message.typ(), MessageType::EncryptedData)); - - // Verify correct data in message - match &packet.message { - LpMessage::ApplicationData(data) => { - assert_eq!(*data, ApplicationData(vec![11u8; 124])); - } - _ => panic!("Wrong message type"), - } - } - - #[test] - fn forward_message_encoding() { - let msg1 = ForwardPacketData { - target_lp_address: "1.2.3.4:5678".parse().unwrap(), - expected_response_size: ExpectedResponseSize::Transport, - inner_packet_bytes: vec![], - }; - - let msg2 = ForwardPacketData { - target_lp_address: "1.2.3.4:5678".parse().unwrap(), - expected_response_size: ExpectedResponseSize::Handshake(250), - inner_packet_bytes: vec![42u8; 64], - }; - - let msg3 = ForwardPacketData { - target_lp_address: "[2001:db8::1]:8080".parse().unwrap(), - expected_response_size: ExpectedResponseSize::Transport, - inner_packet_bytes: vec![], - }; - - let msg4 = ForwardPacketData { - target_lp_address: "[2001:db8::1]:8080".parse().unwrap(), - expected_response_size: ExpectedResponseSize::Handshake(250), - inner_packet_bytes: vec![42u8; 64], - }; - - let b = msg1.to_bytes(); - let msg1_r = ForwardPacketData::decode(&b).unwrap(); - assert_eq!(msg1_r, msg1); - - let b = msg2.to_bytes(); - let msg2_r = ForwardPacketData::decode(&b).unwrap(); - assert_eq!(msg2_r, msg2); - - let b = msg3.to_bytes(); - let msg3_r = ForwardPacketData::decode(&b).unwrap(); - assert_eq!(msg3_r, msg3); - - let b = msg4.to_bytes(); - let msg4_r = ForwardPacketData::decode(&b).unwrap(); - assert_eq!(msg4_r, msg4); - } -} diff --git a/common/nym-lp/src/packet/mod.rs b/common/nym-lp/src/packet/mod.rs index 016ae24b69..6ece484603 100644 --- a/common/nym-lp/src/packet/mod.rs +++ b/common/nym-lp/src/packet/mod.rs @@ -7,7 +7,7 @@ use std::fmt::{Debug, Formatter}; pub use error::MalformedLpPacketError; pub use header::{InnerHeader, LpHeader, OuterHeader}; -pub use message::{ApplicationData, ForwardPacketData, LpMessage, MessageType}; +pub use message::{ForwardPacketData, LpMessage}; pub mod error; pub mod header; @@ -78,7 +78,7 @@ impl EncryptedLpPacket { } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq)] pub struct LpPacket { pub(crate) header: LpHeader, pub(crate) message: LpMessage, @@ -95,10 +95,6 @@ impl LpPacket { Self { header, message } } - pub fn typ(&self) -> MessageType { - self.message.typ() - } - pub fn message(&self) -> &LpMessage { &self.message } @@ -119,8 +115,6 @@ impl LpPacket { pub(crate) fn dbg_encode(&self, dst: &mut BytesMut) { self.header.dbg_encode(dst); - - dst.put_slice(&(self.message.typ() as u16).to_le_bytes()); - self.message.encode_content(dst); + self.message.encode(dst) } } diff --git a/common/nym-lp/src/session.rs b/common/nym-lp/src/session.rs index c92d967dee..4ed5e0fb02 100644 --- a/common/nym-lp/src/session.rs +++ b/common/nym-lp/src/session.rs @@ -6,7 +6,7 @@ //! This module implements session management functionality, including replay protection use crate::codec::{decrypt_lp_packet, encrypt_lp_packet}; -use crate::packet::{ApplicationData, EncryptedLpPacket, LpHeader, LpMessage, LpPacket}; +use crate::packet::{EncryptedLpPacket, LpHeader, LpMessage, LpPacket}; use crate::peer::{LpLocalPeer, LpRemotePeer}; use crate::peer_config::LpReceiverIndex; use crate::psq::{ @@ -174,12 +174,7 @@ impl LpSession { pub fn next_packet(&mut self, message: LpMessage) -> Result { let counter = self.next_counter(); - let header = LpHeader::new( - self.receiver_index(), - counter, - self.protocol_version, - message.typ(), - ); + let header = LpHeader::new(self.receiver_index(), counter, self.protocol_version); let packet = LpPacket::new(header, message); Ok(packet) } @@ -255,9 +250,9 @@ impl LpSession { /// * `Err(LpError)` if the session is not in transport mode or encryption fails. pub(crate) fn encrypt_application_data( &mut self, - data: Vec, + data: LpMessage, ) -> Result { - let packet = self.next_packet(LpMessage::ApplicationData(ApplicationData::new(data)))?; + let packet = self.next_packet(data)?; encrypt_lp_packet(packet, &mut self.active_transport) } diff --git a/common/nym-lp/src/session_integration/mod.rs b/common/nym-lp/src/session_integration/mod.rs index 00486a1fb8..ae2387a745 100644 --- a/common/nym-lp/src/session_integration/mod.rs +++ b/common/nym-lp/src/session_integration/mod.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { - use crate::packet::EncryptedLpPacket; - use crate::state_machine::{LpAction, LpData, LpInput, LpStateBare}; + use crate::packet::{EncryptedLpPacket, LpMessage}; + use crate::state_machine::{LpAction, LpInput, LpStateBare}; use crate::{LpError, SessionManager, SessionsMock}; use nym_kkt_ciphersuite::{IntoEnumIterator, KEM}; @@ -9,7 +9,7 @@ mod tests { trait ActionExtract { fn ciphertext(self) -> EncryptedLpPacket; - fn data(self) -> LpData; + fn data(self) -> LpMessage; } impl ActionExtract for LpAction { @@ -21,7 +21,7 @@ mod tests { } } - fn data(self) -> LpData { + fn data(self) -> LpMessage { if let LpAction::DeliverData(data) = self { data } else { @@ -54,7 +54,7 @@ mod tests { // --- A sends to B --- let plaintext_a = format!("A->B Message {i}").into_bytes(); let ciphertext_a = session_manager_1 - .send_data(peer_a_sm, LpData::new_opaque(plaintext_a.clone())) + .send_data(peer_a_sm, LpMessage::new_opaque(plaintext_a.clone())) .unwrap() .ciphertext(); @@ -69,7 +69,7 @@ mod tests { // --- B sends to A --- let plaintext_b = format!("B->A Message {i}").into_bytes(); let ciphertext_b = session_manager_2 - .send_data(peer_b_sm, LpData::new_opaque(plaintext_b.clone())) + .send_data(peer_b_sm, LpMessage::new_opaque(plaintext_b.clone())) .unwrap() .ciphertext(); @@ -195,8 +195,10 @@ mod tests { // --- 3. Simulate Data Transfer via process_input --- println!("Starting data transfer simulation via process_input..."); - let plaintext_a_to_b = LpData::new_opaque(b"Hello from A via process_input!".to_vec()); - let plaintext_b_to_a = LpData::new_opaque(b"Hello from B via process_input!".to_vec()); + let plaintext_a_to_b = + LpMessage::new_opaque(b"Hello from A via process_input!".to_vec()); + let plaintext_b_to_a = + LpMessage::new_opaque(b"Hello from B via process_input!".to_vec()); // --- A sends to B --- println!(" A sends to B"); @@ -272,8 +274,8 @@ mod tests { println!("Testing out-of-order reception via process_input..."); // A prepares N+1 then N - let data_n_plus_1 = LpData::new_opaque(b"Message N+1".to_vec()); - let data_n = LpData::new_opaque(b"Message N".to_vec()); + let data_n_plus_1 = LpMessage::new_opaque(b"Message N+1".to_vec()); + let data_n = LpMessage::new_opaque(b"Message N".to_vec()); let action_send_n1 = session_manager_1 .process_input(session_id, LpInput::SendData(data_n_plus_1.clone())) @@ -344,7 +346,7 @@ mod tests { // Further actions on A fail let send_after_close_a = session_manager_1.process_input( session_id, - LpInput::SendData(LpData::new_opaque(b"fail".to_vec())), + LpInput::SendData(LpMessage::new_opaque(b"fail".to_vec())), ); assert!(send_after_close_a.is_err()); assert!(matches!( @@ -366,7 +368,7 @@ mod tests { // Further actions on B fail let send_after_close_b = session_manager_2.process_input( session_id, - LpInput::SendData(LpData::new_opaque(b"fail".to_vec())), + LpInput::SendData(LpMessage::new_opaque(b"fail".to_vec())), ); assert!(send_after_close_b.is_err()); assert!(matches!( diff --git a/common/nym-lp/src/session_manager.rs b/common/nym-lp/src/session_manager.rs index 0ad99b4c85..30e1cc7921 100644 --- a/common/nym-lp/src/session_manager.rs +++ b/common/nym-lp/src/session_manager.rs @@ -6,9 +6,9 @@ //! This module implements session lifecycle management functionality, handling //! creation, retrieval, and storage of sessions. -use crate::packet::EncryptedLpPacket; +use crate::packet::{EncryptedLpPacket, LpMessage}; use crate::peer_config::LpReceiverIndex; -use crate::state_machine::{LpAction, LpData, LpInput, LpStateBare}; +use crate::state_machine::{LpAction, LpInput, LpStateBare}; use crate::{LpError, LpSession, LpStateMachine}; use std::collections::HashMap; @@ -44,7 +44,11 @@ impl SessionManager { self.with_state_machine_mut(lp_id, |sm| sm.process_input(input).transpose())? } - pub fn send_data(&mut self, lp_id: LpReceiverIndex, data: LpData) -> Result { + pub fn send_data( + &mut self, + lp_id: LpReceiverIndex, + data: LpMessage, + ) -> Result { self.process_input(lp_id, LpInput::SendData(data))? .ok_or(LpError::NotInTransport) } diff --git a/common/nym-lp/src/state_machine.rs b/common/nym-lp/src/state_machine.rs index 733f48b694..442bff94b7 100644 --- a/common/nym-lp/src/state_machine.rs +++ b/common/nym-lp/src/state_machine.rs @@ -5,12 +5,11 @@ //! State machine ensures protocol steps execute in correct order. Invalid transitions //! return LpError, preventing protocol violations. -use crate::packet::{EncryptedLpPacket, LpMessage}; +use crate::packet::EncryptedLpPacket; +use crate::packet::message::LpMessage; use crate::peer_config::LpReceiverIndex; use crate::session::SessionId; use crate::{LpError, session::LpSession}; -use bytes::{Buf, Bytes}; -use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::mem; #[derive(Debug)] @@ -58,7 +57,7 @@ pub enum LpInput { ReceivePacket(EncryptedLpPacket), /// Application wants to send data (only valid in Transport state). - SendData(LpData), + SendData(LpMessage), /// Close the connection. Close, @@ -71,81 +70,12 @@ pub enum LpAction { SendPacket(EncryptedLpPacket), /// Deliver decrypted application data received from the peer. - DeliverData(LpData), + DeliverData(LpMessage), /// Inform the environment that the connection is closed. ConnectionClosed, } -/// Represent application data being sent in Transport mode -#[derive(Debug, Clone, PartialEq)] -pub struct LpData { - pub kind: LpDataKind, - pub content: Bytes, -} - -impl AsRef<[u8]> for LpData { - fn as_ref(&self) -> &[u8] { - &self.content - } -} - -impl LpData { - pub fn new(kind: LpDataKind, content: impl Into) -> Self { - Self { - kind, - content: content.into(), - } - } - pub fn new_opaque(content: impl Into) -> Self { - Self::new(LpDataKind::Opaque, content) - } - - pub fn new_registration(data: impl Into) -> Self { - Self::new(LpDataKind::Registration, data) - } - - pub fn new_forward(data: impl Into) -> Self { - Self::new(LpDataKind::Forward, data) - } - - pub fn to_vec(self) -> Vec { - self.into() - } -} - -impl From for Vec { - fn from(data: LpData) -> Self { - let mut out = Vec::with_capacity(data.content.len() + 1); - out.push(data.kind as u8); - out.extend_from_slice(data.content.as_ref()); - out - } -} - -impl TryFrom> for LpData { - type Error = LpError; - - fn try_from(value: Vec) -> Result { - let kind = LpDataKind::try_from(value[0]).map_err(|_| { - LpError::DeserializationError(format!("unknown data type: {}", value[0])) - })?; - let mut content = Bytes::from(value); - content.advance(1); - - Ok(LpData::new(kind, content)) - } -} - -/// Represent kind of application data being sent in Transport mode -#[derive(Clone, Copy, PartialEq, Eq, Debug, IntoPrimitive, TryFromPrimitive)] -#[repr(u8)] -pub enum LpDataKind { - Opaque = 0, - Registration = 1, - Forward = 2, -} - /// The Lewes Protocol State Machine. pub struct LpStateMachine { pub state: LpState, @@ -234,31 +164,10 @@ impl LpStateMachine { return (LpState::Transport(state), Some(Err(e))); } - // Check message type - match packet.into_message() { - // Normal encrypted data - LpMessage::ApplicationData(payload) => { - // Deliver data - match payload.0.try_into() { - Ok(data) => { - let result_action = Some(Ok(LpAction::DeliverData(data))); - (LpState::Transport(state), result_action) - } - Err(e) => { - let reason = e.to_string(); - (LpState::Closed { reason }, Some(Err(e))) - } - } - } - other => { - // Unexpected message type in Transport state - let err = LpError::InvalidStateTransition { - state: "Transport".to_string(), - input: format!("Unexpected message type: {other}"), - }; - (LpState::Transport(state), Some(Err(err))) - } - } + // 4. deliver the message + let message = packet.message; + let result_action = Some(Ok(LpAction::DeliverData(message))); + (LpState::Transport(state), result_action) } LpInput::SendData(data) => { // Encrypt and send application data @@ -339,9 +248,9 @@ impl LpStateMachine { fn prepare_data_packet( &self, session: &mut LpSession, - data: LpData, + data: LpMessage, ) -> Result { - session.encrypt_application_data(data.to_vec()) + session.encrypt_application_data(data) } } @@ -386,7 +295,7 @@ mod tests { // --- Transport Phase --- println!("--- Step 1: Initiator sends data ---"); - let data_to_send_1 = LpData::new_opaque(b"hello responder".to_vec()); + let data_to_send_1 = LpMessage::new_opaque(b"hello responder".to_vec()); let init_actions_4 = initiator.process_input(LpInput::SendData(data_to_send_1.clone())); let data_packet_1 = if let Some(Ok(LpAction::SendPacket(packet))) = init_actions_4 { packet.clone() @@ -405,7 +314,7 @@ mod tests { assert_eq!(resp_data_1, data_to_send_1); println!("--- Step 3: Responder sends data ---"); - let data_to_send_2 = LpData::new_opaque(b"hello initiator".to_vec()); + let data_to_send_2 = LpMessage::new_opaque(b"hello initiator".to_vec()); let resp_actions_6 = responder.process_input(LpInput::SendData(data_to_send_2.clone())); let data_packet_2 = if let Some(Ok(LpAction::SendPacket(packet))) = resp_actions_6 { packet.clone() diff --git a/integration-tests/src/lp_registration.rs b/integration-tests/src/lp_registration.rs index 444104f538..84fd3f2f14 100644 --- a/integration-tests/src/lp_registration.rs +++ b/integration-tests/src/lp_registration.rs @@ -393,10 +393,11 @@ mod tests { #[tokio::test] async fn test_basic_lp_entry_registration() -> anyhow::Result<()> { + // nym_test_utils::helpers::setup_test_logger(); + for kem in KEM::iter() { let ciphersuite = Ciphersuite::default().with_kem(kem); - // nym_test_utils::helpers::setup_test_logger(); // initialise random, but deterministic, keys, addresses, etc. for the parties let mut client_rng = u64_seeded_rng_09(0); let mut gateway_rng = u64_seeded_rng_09(1); diff --git a/nym-node/src/node/lp/error.rs b/nym-node/src/node/lp/error.rs index d8c9da439a..c5291c45c9 100644 --- a/nym-node/src/node/lp/error.rs +++ b/nym-node/src/node/lp/error.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::node::lp::LpReceiverIndex; -use nym_lp::state_machine::{LpAction, LpDataKind}; +use nym_lp::packet::message::LpMessageType; +use nym_lp::state_machine::LpAction; use nym_lp::transport::LpTransportError; use nym_lp::{LpError, packet::MalformedLpPacketError}; use std::net::SocketAddr; @@ -44,7 +45,7 @@ pub enum LpHandlerError { MalformedLpPacket(#[from] MalformedLpPacketError), #[error("received payload type of an unexpected type: {typ:?}")] - UnexpectedLpPayload { typ: LpDataKind }, + UnexpectedLpPayload { typ: LpMessageType }, #[error("timed out while attempting to send to/receive from the connection")] ConnectionTimeout, diff --git a/nym-node/src/node/lp/handler.rs b/nym-node/src/node/lp/handler.rs index 7adbeb57e6..e8c655d435 100644 --- a/nym-node/src/node/lp/handler.rs +++ b/nym-node/src/node/lp/handler.rs @@ -4,8 +4,9 @@ use super::{LpHandlerState, LpReceiverIndex, TimestampedState}; use crate::node::lp::error::LpHandlerError; use dashmap::mapref::one::RefMut; -use nym_lp::packet::{EncryptedLpPacket, ForwardPacketData}; -use nym_lp::state_machine::{LpAction, LpData, LpDataKind, LpInput}; +use nym_lp::packet::message::LpMessageType; +use nym_lp::packet::{EncryptedLpPacket, ForwardPacketData, LpMessage}; +use nym_lp::state_machine::{LpAction, LpInput}; use nym_lp::transport::LpHandshakeChannel; use nym_lp::transport::traits::LpTransportChannel; use nym_lp::{LpSession, LpStateMachine, packet::message::ExpectedResponseSize}; @@ -301,13 +302,14 @@ where async fn handle_decrypted_payload( &mut self, receiver_idx: LpReceiverIndex, - decrypted_data: LpData, + decrypted_data: LpMessage, ) -> Result<(), LpHandlerError> { let remote = self.remote_addr; + let header = decrypted_data.header; let bytes = decrypted_data.content; - match decrypted_data.kind { - LpDataKind::Registration => { + match header.kind { + LpMessageType::Registration => { let request = LpRegistrationRequest::try_deserialise(&bytes) .map_err(|source| LpHandlerError::MalformedRegistrationRequest { source })?; @@ -319,13 +321,13 @@ where self.handle_registration_request(receiver_idx, request) .await } - LpDataKind::Forward => { + LpMessageType::Forward => { let forward_data = ForwardPacketData::decode(&bytes)?; self.handle_forwarding_request(receiver_idx, forward_data) .await } - typ @ LpDataKind::Opaque => { + typ @ LpMessageType::Opaque => { // Neither registration nor forwarding - unknown payload type warn!( "Unknown transport payload type from {remote} (receiver_idx={receiver_idx}). dropping {} bytes", @@ -341,14 +343,14 @@ where async fn send_response_packet( &mut self, serialised_response: Vec, - response_kind: LpDataKind, + response_kind: LpMessageType, ) -> Result<(), LpHandlerError> { let mut state_entry = self.state_entry_mut()?; // Access session via state machine for subsession support let state_machine = &mut state_entry.value_mut().state; - let wrapped_lp_data = LpData::new(response_kind, serialised_response); + let wrapped_lp_data = LpMessage::new(response_kind, serialised_response); // Process packet through state machine let action = state_machine @@ -378,7 +380,7 @@ where .serialise() .map_err(|source| LpHandlerError::MalformedRegistrationRequest { source })?; - self.send_response_packet(response_bytes, LpDataKind::Registration) + self.send_response_packet(response_bytes, LpMessageType::Registration) .await?; match response.status { @@ -415,7 +417,7 @@ where // Forward the packet to the target gateway and retrieve its response let response_bytes = self.handle_forward_packet(forward_data).await?; - self.send_response_packet(response_bytes, LpDataKind::Forward) + self.send_response_packet(response_bytes, LpMessageType::Forward) .await?; debug!( @@ -750,7 +752,7 @@ mod tests { // Send a valid packet from client side let LpAction::SendPacket(packet) = init_sm - .send_data(id, LpData::new_opaque(b"foomp".to_vec())) + .send_data(id, LpMessage::new_opaque(b"foomp".to_vec())) .unwrap() else { panic!("illegal state") @@ -786,7 +788,7 @@ mod tests { let (mut stream, _) = listener.accept().await.unwrap(); let LpAction::SendPacket(packet) = resp_sm - .send_data(id, LpData::new_opaque(b"foomp".to_vec())) + .send_data(id, LpMessage::new_opaque(b"foomp".to_vec())) .unwrap() else { panic!("illegal state") diff --git a/nym-registration-client/src/lp_client/error.rs b/nym-registration-client/src/lp_client/error.rs index 681d34d01e..16bd1d66c3 100644 --- a/nym-registration-client/src/lp_client/error.rs +++ b/nym-registration-client/src/lp_client/error.rs @@ -5,7 +5,8 @@ use nym_lp::LpError; use nym_lp::packet::MalformedLpPacketError; -use nym_lp::state_machine::{LpAction, LpDataKind}; +use nym_lp::packet::message::LpMessageType; +use nym_lp::state_machine::LpAction; use nym_lp::transport::LpTransportError; use thiserror::Error; @@ -45,7 +46,7 @@ pub enum LpClientError { MalformedLpPacket(#[from] MalformedLpPacketError), #[error("received payload type of an unexpected type: {typ:?}")] - UnexpectedLpPayload { typ: LpDataKind }, + UnexpectedLpPayload { typ: LpMessageType }, #[error("timed out while attempting to finish the KKT/PSQ handshake")] HandshakeTimeout, diff --git a/nym-registration-client/src/lp_client/helpers.rs b/nym-registration-client/src/lp_client/helpers.rs index 9de8cb5a19..2ed712f9a0 100644 --- a/nym-registration-client/src/lp_client/helpers.rs +++ b/nym-registration-client/src/lp_client/helpers.rs @@ -4,23 +4,24 @@ #![allow(dead_code)] use crate::LpClientError; -use nym_lp::packet::ForwardPacketData; +use nym_lp::packet::message::LpMessageType; +use nym_lp::packet::{ForwardPacketData, LpMessage}; use nym_lp::peer::LpRemotePeer; -use nym_lp::state_machine::{LpAction, LpData, LpDataKind, LpInput}; +use nym_lp::state_machine::{LpAction, LpInput}; use nym_registration_common::{ LpRegistrationRequest, LpRegistrationResponse, NymNodeLPInformation, }; pub(crate) trait LpDataSendExt { - fn to_lp_data(&self) -> Result; + fn to_lp_data(&self) -> Result; } pub(crate) trait LpDataDeliverExt: Sized { - fn from_lp_data(data: LpData) -> Result; + fn from_lp_data(data: LpMessage) -> Result; } impl LpDataSendExt for LpRegistrationRequest { - fn to_lp_data(&self) -> Result { + fn to_lp_data(&self) -> Result { let request_bytes = self.serialise().map_err(|e| { LpClientError::SendRegistrationRequest(format!("Failed to serialize request: {e}")) })?; @@ -30,14 +31,14 @@ impl LpDataSendExt for LpRegistrationRequest { request_bytes.len() ); - Ok(LpData::new_registration(request_bytes)) + Ok(LpMessage::new_registration(request_bytes)) } } impl LpDataDeliverExt for LpRegistrationResponse { - fn from_lp_data(data: LpData) -> Result { - if data.kind != LpDataKind::Registration { - return Err(LpClientError::UnexpectedLpPayload { typ: data.kind }); + fn from_lp_data(data: LpMessage) -> Result { + if data.kind() != LpMessageType::Registration { + return Err(LpClientError::UnexpectedLpPayload { typ: data.kind() }); } let response = LpRegistrationResponse::try_deserialise(&data.content) @@ -48,7 +49,7 @@ impl LpDataDeliverExt for LpRegistrationResponse { } impl LpDataSendExt for ForwardPacketData { - fn to_lp_data(&self) -> Result { + fn to_lp_data(&self) -> Result { let request_bytes = self.to_bytes(); tracing::trace!( @@ -56,7 +57,7 @@ impl LpDataSendExt for ForwardPacketData { request_bytes.len() ); - Ok(LpData::new_forward(request_bytes)) + Ok(LpMessage::new_forward(request_bytes)) } } @@ -70,9 +71,9 @@ pub(crate) fn try_convert_forward_response(action: LpAction) -> Result, action => return Err(LpClientError::UnexpectedStateMachineAction { action }), }; - if response_data.kind != LpDataKind::Forward { + if response_data.kind() != LpMessageType::Forward { return Err(LpClientError::UnexpectedLpPayload { - typ: response_data.kind, + typ: response_data.kind(), }); } diff --git a/nym-registration-client/src/lp_client/nested_session/mod.rs b/nym-registration-client/src/lp_client/nested_session/mod.rs index a4e1c5681c..f8150433fe 100644 --- a/nym-registration-client/src/lp_client/nested_session/mod.rs +++ b/nym-registration-client/src/lp_client/nested_session/mod.rs @@ -25,10 +25,10 @@ use crate::lp_client::state_machine_helpers::{extract_forwarded_response, prepar use nym_bandwidth_controller::{BandwidthTicketProvider, DEFAULT_TICKETS_TO_SPEND}; use nym_credentials_interface::TicketType; use nym_crypto::asymmetric::{ed25519, x25519}; -use nym_lp::packet::EncryptedLpPacket; use nym_lp::packet::version; +use nym_lp::packet::{EncryptedLpPacket, LpMessage}; use nym_lp::peer::{DHKeyPair, LpLocalPeer, LpRemotePeer}; -use nym_lp::state_machine::{LpData, LpStateMachine}; +use nym_lp::state_machine::LpStateMachine; use nym_lp::transport::LpHandshakeChannel; use nym_lp::transport::traits::LpTransportChannel; use nym_lp::{Ciphersuite, KEM, LpSession}; @@ -128,14 +128,17 @@ impl NestedLpSession { /// Attempt to wrap the provided `LpData` into a `EncryptedLpPacket` /// using the inner state machine. - fn prepare_transport_packet(&mut self, data: LpData) -> Result { + fn prepare_transport_packet(&mut self, data: LpMessage) -> Result { let state_machine = self.state_machine_mut()?; prepare_send_packet(data, state_machine) } /// Attempt to recover received `LpData` from the received `EncryptedLpPacket` /// using the inner state machine. - fn extract_forwarded_response(&mut self, response_packet: EncryptedLpPacket) -> Result { + fn extract_forwarded_response( + &mut self, + response_packet: EncryptedLpPacket, + ) -> Result { let state_machine = self.state_machine_mut()?; extract_forwarded_response(response_packet, state_machine) } diff --git a/nym-registration-client/src/lp_client/state_machine_helpers.rs b/nym-registration-client/src/lp_client/state_machine_helpers.rs index bfa3f9edc5..3b2a5e7f65 100644 --- a/nym-registration-client/src/lp_client/state_machine_helpers.rs +++ b/nym-registration-client/src/lp_client/state_machine_helpers.rs @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 use crate::LpClientError; -use nym_lp::state_machine::{LpAction, LpData, LpInput}; +use nym_lp::packet::LpMessage; +use nym_lp::state_machine::{LpAction, LpInput}; use nym_lp::{LpStateMachine, packet::EncryptedLpPacket}; /// Attempt to prepare the provided data for sending by wrapping it in appropriate `LpAction`, /// and attempting to extract `EncryptedLpPacket` from the provided state machine. pub(crate) fn prepare_send_packet( - data: LpData, + data: LpMessage, state_machine: &mut LpStateMachine, ) -> Result { let action = state_machine @@ -26,7 +27,7 @@ pub(crate) fn prepare_send_packet( pub(crate) fn extract_forwarded_response( response_packet: EncryptedLpPacket, state_machine: &mut LpStateMachine, -) -> Result { +) -> Result { let action = state_machine .process_input(LpInput::ReceivePacket(response_packet)) .ok_or(LpClientError::UnexpectedStateMachineHalt)??; From 0426adc94eba4ec744f5e374491c7a3d2ac4a802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 9 Mar 2026 13:37:33 +0000 Subject: [PATCH 48/51] chore: introduce additional prometheus metrics for registration times --- common/wireguard/src/peer_controller/mod.rs | 8 ++- .../wireguard/new_peer_registration/mod.rs | 16 +++++ .../src/prometheus_wrapper.rs | 60 ++++++++++++++++++- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/common/wireguard/src/peer_controller/mod.rs b/common/wireguard/src/peer_controller/mod.rs index 9aae772a8f..19f936b93b 100644 --- a/common/wireguard/src/peer_controller/mod.rs +++ b/common/wireguard/src/peer_controller/mod.rs @@ -1,6 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +pub use crate::ip_pool::IpPair; use crate::ip_pool::allocated_ip_pair; use crate::{ IpPool, @@ -23,6 +24,7 @@ use nym_credentials_interface::CredentialSpendingData; use nym_gateway_requests::models::CredentialSpendingRequest; use nym_gateway_storage::traits::BandwidthGatewayStorage; use nym_node_metrics::NymNodeMetrics; +use nym_node_metrics::prometheus_wrapper::{PROMETHEUS_METRICS, PrometheusMetric}; use nym_wireguard_types::{ DEFAULT_IP_CLEANUP_INTERVAL, DEFAULT_IP_STALE_AGE, DEFAULT_PEER_TIMEOUT_CHECK, }; @@ -35,8 +37,6 @@ use tokio::sync::{RwLock, mpsc}; use tokio_stream::{StreamExt, wrappers::IntervalStream}; use tracing::{debug, error, info, trace}; -pub use crate::ip_pool::IpPair; - #[cfg(feature = "mock")] pub mod mock; @@ -261,6 +261,10 @@ impl PeerController { } async fn handle_add_request(&mut self, peer: &Peer) -> Result<()> { + // observation will get automatically added once dropped + let _metric_timer = + PROMETHEUS_METRICS.start_timer(PrometheusMetric::WireguardDefguardPeerCreation); + nym_metrics::inc!("wg_peer_addition_attempts"); // confirm ip allocation so that it wouldn't be released for as long as the peer exists diff --git a/gateway/src/node/wireguard/new_peer_registration/mod.rs b/gateway/src/node/wireguard/new_peer_registration/mod.rs index b2f89accf7..ddb53b5517 100644 --- a/gateway/src/node/wireguard/new_peer_registration/mod.rs +++ b/gateway/src/node/wireguard/new_peer_registration/mod.rs @@ -32,6 +32,7 @@ use nym_crypto::asymmetric::x25519; use nym_gateway_requests::models::CredentialSpendingRequest; use nym_gateway_storage::models::PersistedBandwidth; use nym_lp::peer_config::LpReceiverIndex; +use nym_node_metrics::prometheus_wrapper::{PrometheusMetric, PROMETHEUS_METRICS}; use nym_registration_common::dvpn::{ LpDvpnRegistrationFinalisation, LpDvpnRegistrationInitialRequest, }; @@ -126,6 +127,9 @@ impl PeerRegistrator { credential: CredentialSpendingData, client_id: i64, ) -> Result { + let _metric_timer = PROMETHEUS_METRICS + .start_timer(PrometheusMetric::PeerRegistrationCredentialVerification); + let bandwidth = self.credential_storage_preparation(client_id).await?; let client_bandwidth = ClientBandwidth::new(bandwidth.into()); let mut verifier = CredentialVerifier::new( @@ -232,6 +236,9 @@ impl PeerRegistrator { request_id: u64, reply_to: Option, ) -> Result { + let _metric_timer = PROMETHEUS_METRICS + .start_timer(PrometheusMetric::DvpnAuthenticatorClientRegistrationMsg1); + let remote_public = init_message.pub_key(); // 1. check if there's any pending registration already in progress, @@ -269,6 +276,9 @@ impl PeerRegistrator { request_id: u64, reply_to: Option, ) -> Result { + let _metric_timer = PROMETHEUS_METRICS + .start_timer(PrometheusMetric::DvpnAuthenticatorClientRegistrationMsg2); + let peer = final_message.gateway_client_pub_key(); // 1. check if there's any pending registration associated with this peer let pending_data = self @@ -312,6 +322,9 @@ impl PeerRegistrator { init_msg: LpDvpnRegistrationInitialRequest, receiver_index: LpReceiverIndex, ) -> Result { + let _metric_timer = + PROMETHEUS_METRICS.start_timer(PrometheusMetric::DvpnLpClientRegistrationMsg1); + let remote_public = init_msg.wg_public_key; let psk = Key::new(init_msg.psk); @@ -343,6 +356,9 @@ impl PeerRegistrator { final_msg: LpDvpnRegistrationFinalisation, receiver_index: LpReceiverIndex, ) -> Result { + let _metric_timer = + PROMETHEUS_METRICS.start_timer(PrometheusMetric::DvpnLpClientRegistrationMsg2); + // 1. check if there's any pending registration associated with this peer let pending_data = self .pending_registrations diff --git a/nym-node/nym-node-metrics/src/prometheus_wrapper.rs b/nym-node/nym-node-metrics/src/prometheus_wrapper.rs index 4102e82c98..ffdb66e5c0 100644 --- a/nym-node/nym-node-metrics/src/prometheus_wrapper.rs +++ b/nym-node/nym-node-metrics/src/prometheus_wrapper.rs @@ -24,6 +24,18 @@ const CLIENT_SESSION_DURATION_BUCKETS: &[f64] = &[ 259200., // 72h+ (implicitly) ]; +const REG_LATENCY_BUCKETS: &[f64] = &[ + 0.001, // 1ms + 0.005, // 5ms + 0.01, // 10ms + 0.05, // 50ms + 0.1, // 100ms + 0.25, // 250ms + 0.5, // 500ms + 1.0, // 1s + 2.0, // 2s +]; + #[derive(Clone, Debug, EnumIter, Display, EnumProperty, EnumCount, Eq, Hash, PartialEq)] #[strum(serialize_all = "snake_case", prefix = "nym_node_")] pub enum PrometheusMetric { @@ -139,6 +151,34 @@ pub enum PrometheusMetric { #[strum(props(help = "The current receiving rate of wireguard"))] WireguardBytesRxRate, + #[strum(props(help = "The distribution of defguard peer creation time"))] + WireguardDefguardPeerCreation, + + #[strum(props( + help = "The distribution of time it takes to verify a credential during peer registration" + ))] + PeerRegistrationCredentialVerification, + + #[strum(props( + help = "The distribution of time for client dvpn registration through the authenticator for msg1" + ))] + DvpnAuthenticatorClientRegistrationMsg1, + + #[strum(props( + help = "The distribution of time for client dvpn registration through the authenticator for msg2" + ))] + DvpnAuthenticatorClientRegistrationMsg2, + + #[strum(props( + help = "The distribution of time for client dvpn registration through the lp for msg1" + ))] + DvpnLpClientRegistrationMsg1, + + #[strum(props( + help = "The distribution of time for client dvpn registration through the lp for msg2" + ))] + DvpnLpClientRegistrationMsg2, + // # NETWORK #[strum(props(help = "The number of active ingress mixnet connections"))] NetworkActiveIngressMixnetConnections, @@ -271,6 +311,24 @@ impl PrometheusMetric { PrometheusMetric::WireguardActivePeers => Metric::new_int_gauge(&name, help), PrometheusMetric::WireguardBytesTxRate => Metric::new_float_gauge(&name, help), PrometheusMetric::WireguardBytesRxRate => Metric::new_float_gauge(&name, help), + PrometheusMetric::WireguardDefguardPeerCreation => { + Metric::new_histogram(&name, help, Some(REG_LATENCY_BUCKETS)) + } + PrometheusMetric::DvpnAuthenticatorClientRegistrationMsg1 => { + Metric::new_histogram(&name, help, Some(REG_LATENCY_BUCKETS)) + } + PrometheusMetric::DvpnAuthenticatorClientRegistrationMsg2 => { + Metric::new_histogram(&name, help, Some(REG_LATENCY_BUCKETS)) + } + PrometheusMetric::DvpnLpClientRegistrationMsg1 => { + Metric::new_histogram(&name, help, Some(REG_LATENCY_BUCKETS)) + } + PrometheusMetric::DvpnLpClientRegistrationMsg2 => { + Metric::new_histogram(&name, help, Some(REG_LATENCY_BUCKETS)) + } + PrometheusMetric::PeerRegistrationCredentialVerification => { + Metric::new_histogram(&name, help, Some(REG_LATENCY_BUCKETS)) + } PrometheusMetric::NetworkActiveIngressMixnetConnections => { Metric::new_int_gauge(&name, help) } @@ -388,7 +446,7 @@ mod tests { // a sanity check for anyone adding new metrics. if this test fails, // make sure any methods on `PrometheusMetric` enum don't need updating // or require custom Display impl - assert_eq!(39, PrometheusMetric::COUNT) + assert_eq!(45, PrometheusMetric::COUNT) } #[test] From f00b18298ca3471c80bc3901c9b676c9634abb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 9 Mar 2026 14:10:24 +0000 Subject: [PATCH 49/51] bugfix: correctly populate gateway probe LP data --- Cargo.lock | 1 + nym-gateway-probe/Cargo.toml | 1 + nym-gateway-probe/src/common/nodes.rs | 39 +++++++++++++++++++-------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 937a30240a..29f9d86c89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6520,6 +6520,7 @@ dependencies = [ "rand 0.8.5", "rand 0.9.2", "reqwest 0.13.1", + "semver 1.0.26", "serde", "serde_json", "time", diff --git a/nym-gateway-probe/Cargo.toml b/nym-gateway-probe/Cargo.toml index b18927be15..7cebd4f98b 100644 --- a/nym-gateway-probe/Cargo.toml +++ b/nym-gateway-probe/Cargo.toml @@ -26,6 +26,7 @@ rand09.workspace = true reqwest = { workspace = true, features = ["socks"] } serde.workspace = true serde_json.workspace = true +semver.workspace = true time = { workspace = true } tokio = { workspace = true, features = [ "process", diff --git a/nym-gateway-probe/src/common/nodes.rs b/nym-gateway-probe/src/common/nodes.rs index 04b2b10cac..c36daff81b 100644 --- a/nym-gateway-probe/src/common/nodes.rs +++ b/nym-gateway-probe/src/common/nodes.rs @@ -12,6 +12,7 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned; use nym_http_api_client::UserAgent; use nym_kkt_ciphersuite::Ciphersuite; use nym_kkt_ciphersuite::{KEM, KEMKeyDigests}; +use nym_lp::packet::version; use nym_lp::peer::{DHPublicKey, LpRemotePeer}; use nym_network_defaults::DEFAULT_NYM_NODE_HTTP_PORT; use nym_node_requests::api::client::NymNodeApiClientExt; @@ -129,16 +130,32 @@ impl DirectoryNode { .copied() .ok_or_else(|| anyhow!("no ip address known"))?; - // let lp_data = description.lewes_protocol.as_ref().and_then(|lp_data| { - // Some(TestedNodeLpDetails { - // address: SocketAddr::new(ip_address, lp_data.control_port), - // expected_kem_key_hashes: lp_data.kem_keys().ok()?, - // expected_signing_key_hashes: lp_data.signing_keys().ok()?, - // x25519: lp_data.x25519, - // // \/ TODO: proper derivation from build version - // lp_version: version::CURRENT, - // }) - // }); + let identity = self.identity(); + + let lp_data = match description.lewes_protocol.as_ref() { + Some(lp_data) => { + // 1. verify the signature, if it's invalid, completely bail on the node + if !lp_data.verify(&identity) { + warn!("{identity} provided malformed lp data"); + bail!("{identity} provided malformed lp data"); + } + + let version: semver::Version = + description.build_information.build_version.parse()?; + let Some(ciphersuite) = Ciphersuite::from_node_version(version) else { + bail!("failed to identify valid ciphersuite for {identity}"); + }; + + Some(TestedNodeLpDetails { + address: SocketAddr::new(ip_address, lp_data.content.control_port), + expected_kem_key_hashes: lp_data.content.kem_keys()?, + x25519: lp_data.content.x25519, + lp_version: version::CURRENT, + ciphersuite, + }) + } + None => None, + }; Ok(TestedNodeDetails { identity: self.identity(), @@ -147,7 +164,7 @@ impl DirectoryNode { authenticator_address, authenticator_version, ip_address: Some(ip_address), - lp_data: None, + lp_data, }) } } From 02eec164f8ac5d6ad4a0a3ce3a2930bef687e52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 9 Mar 2026 14:56:31 +0000 Subject: [PATCH 50/51] bugfix: lp information to have proper snake_case on API endpoints (#6531) --- .../nym-api-requests/src/models/described/type_translation.rs | 3 +++ nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/nym-api/nym-api-requests/src/models/described/type_translation.rs b/nym-api/nym-api-requests/src/models/described/type_translation.rs index 37653a2d14..2cbc660aa2 100644 --- a/nym-api/nym-api-requests/src/models/described/type_translation.rs +++ b/nym-api/nym-api-requests/src/models/described/type_translation.rs @@ -296,6 +296,7 @@ fn translate_digests( ToSchema, Ord, )] +#[serde(rename_all = "snake_case")] #[strum(serialize_all = "lowercase")] #[non_exhaustive] pub enum LPKEM { @@ -319,6 +320,7 @@ pub enum LPKEM { ToSchema, Ord, )] +#[serde(rename_all = "snake_case")] #[strum(serialize_all = "lowercase")] #[non_exhaustive] pub enum LPHashFunction { @@ -343,6 +345,7 @@ pub enum LPHashFunction { EnumString, ToSchema, )] +#[serde(rename_all = "snake_case")] #[strum(serialize_all = "lowercase")] #[non_exhaustive] pub enum LPSignatureScheme { diff --git a/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs b/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs index fe1ea68bd5..9c8104e494 100644 --- a/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs +++ b/nym-node/nym-node-requests/src/api/v1/lewes_protocol/models.rs @@ -71,6 +71,7 @@ impl LewesProtocol { EnumString, Ord, )] +#[serde(rename_all = "snake_case")] #[strum(serialize_all = "lowercase")] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum LPKEM { @@ -112,6 +113,7 @@ impl From for LPKEM { EnumIter, Ord, )] +#[serde(rename_all = "snake_case")] #[strum(serialize_all = "lowercase")] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum LPHashFunction { @@ -158,6 +160,7 @@ impl From for LPHashFunction { EnumString, EnumIter, )] +#[serde(rename_all = "snake_case")] #[strum(serialize_all = "lowercase")] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum LPSignatureScheme { From 7cccf3cfff448f1b38324f7655930a2b621588c6 Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Tue, 10 Mar 2026 10:46:12 +0100 Subject: [PATCH 51/51] update changelog --- CHANGELOG.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11ed1bb017..e21f10f884 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,52 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// ## [Unreleased] +## [2026.5-raclette] (2026-03-10) + +- bugfix: correctly populate gateway probe LP data ([#6533]) +- chore: introduce additional prometheus metrics for registration times ([#6532]) +- bugfix: lp information to have proper snake_case on API endpoints ([#6531]) +- removed redundant LP states ([#6509]) +- chore: removed all matrix notifications from github actions ([#6495]) +- feat: Lewes Protocol with PSQv2 ([#6491]) +- build(deps): bump minimatch from 3.1.2 to 3.1.4 in /documentation/docs ([#6486]) +- build(deps): bump bn.js from 4.12.2 to 4.12.3 in /documentation/docs ([#6484]) +- build(deps): bump bn.js from 4.12.2 to 4.12.3 ([#6483]) +- build(deps): bump ajv from 8.17.1 to 8.18.0 in /clients/native/examples/js-examples/websocket ([#6478]) +- build(deps): bump ajv from 6.12.6 to 6.14.0 in /documentation/docs ([#6477]) +- build(deps): bump minimatch and glob in /documentation/scripts/post-process ([#6476]) +- build(deps): bump hono from 4.11.9 to 4.12.0 ([#6475]) +- build(deps): bump keccak from 0.1.5 to 0.1.6 ([#6472]) +- build(deps-dev): bump qs from 6.14.1 to 6.14.2 in /clients/native/examples/js-examples/websocket ([#6466]) +- build(deps): bump mikefarah/yq from 4.52.2 to 4.52.4 ([#6465]) +- Otel minimal v2 ([#6464]) +- build(deps): bump qs and express in /wasm/client/internal-dev ([#6461]) +- bugfix: restore 'latest_measurement' field for nym-node /verloc endpoint ([#6452]) +- build(deps-dev): bump webpack from 5.77.0 to 5.104.1 in /wasm/node-tester/internal-dev ([#6451]) +- Max/mixfetch concurrent test ([#6417]) + +[#6533]: https://github.com/nymtech/nym/pull/6533 +[#6532]: https://github.com/nymtech/nym/pull/6532 +[#6531]: https://github.com/nymtech/nym/pull/6531 +[#6509]: https://github.com/nymtech/nym/pull/6509 +[#6495]: https://github.com/nymtech/nym/pull/6495 +[#6491]: https://github.com/nymtech/nym/pull/6491 +[#6486]: https://github.com/nymtech/nym/pull/6486 +[#6484]: https://github.com/nymtech/nym/pull/6484 +[#6483]: https://github.com/nymtech/nym/pull/6483 +[#6478]: https://github.com/nymtech/nym/pull/6478 +[#6477]: https://github.com/nymtech/nym/pull/6477 +[#6476]: https://github.com/nymtech/nym/pull/6476 +[#6475]: https://github.com/nymtech/nym/pull/6475 +[#6472]: https://github.com/nymtech/nym/pull/6472 +[#6466]: https://github.com/nymtech/nym/pull/6466 +[#6465]: https://github.com/nymtech/nym/pull/6465 +[#6464]: https://github.com/nymtech/nym/pull/6464 +[#6461]: https://github.com/nymtech/nym/pull/6461 +[#6452]: https://github.com/nymtech/nym/pull/6452 +[#6451]: https://github.com/nymtech/nym/pull/6451 +[#6417]: https://github.com/nymtech/nym/pull/6417 + ## [2026.4-quark] (2026-02-24) - Enhance CI workflow with feature inputs ([#6462])