diff --git a/Cargo.lock b/Cargo.lock index bb3a9229..3c5b0e0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "acto" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "148541f13c28e3e840354ee4d6c99046c10be2c81068bbd23b9e3a38f95a917e" +checksum = "598381761ee991bf2f1455f700380e2191fb370dc9df1ee764f348b7f089d8b6" dependencies = [ "parking_lot", "pin-project-lite", @@ -17,6 +17,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array", +] + [[package]] name = "aes" version = "0.8.4" @@ -28,6 +38,20 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.12" @@ -67,9 +91,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -82,15 +106,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -117,9 +141,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "append-only-bytes" @@ -156,21 +180,21 @@ checksum = "2ccd462b64c3c72f1be8305905a85d85403d768e8690c9b8bd3b9009a5761679" [[package]] name = "ashpd" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618a409b91d5265798a99e3d1d0b226911605e581c4e7255e83c1e397b172bce" +checksum = "33a3c86f3fd70c0ffa500ed189abfa90b5a52398a45d5dc372fcc38ebeb7a645" dependencies = [ "async-fs 2.2.0", "async-net", "enumflags2", "futures-channel", "futures-util", - "rand 0.9.2", + "rand 0.9.4", "serde", "serde_repr", "tracing", "url", - "zbus 5.13.2", + "zbus 5.15.0", ] [[package]] @@ -207,28 +231,15 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-compat" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ba85bc55464dcbf728b56d97e119d673f4cf9062be330a9a26f3acf504a590" -dependencies = [ - "futures-core", - "futures-io", - "once_cell", - "pin-project-lite", - "tokio", -] - [[package]] name = "async-executor" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-lite 2.6.1", "pin-project-lite", "slab", @@ -290,7 +301,7 @@ dependencies = [ "futures-lite 2.6.1", "parking", "polling 3.11.0", - "rustix 1.1.3", + "rustix 1.1.4", "slab", "windows-sys 0.61.2", ] @@ -358,7 +369,7 @@ dependencies = [ "cfg-if", "event-listener 5.4.1", "futures-lite 2.6.1", - "rustix 1.1.3", + "rustix 1.1.4", ] [[package]] @@ -369,14 +380,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "async-signal" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" dependencies = [ "async-io 2.6.0", "async-lock 3.4.2", @@ -384,7 +395,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.1.3", + "rustix 1.1.4", "signal-hook-registry", "slab", "windows-sys 0.61.2", @@ -404,7 +415,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -442,18 +453,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attohttpc" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" -dependencies = [ - "base64 0.22.1", - "http", - "log", - "url", -] - [[package]] name = "autocfg" version = "1.5.0" @@ -466,17 +465,11 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ - "fastrand 2.3.0", + "fastrand 2.4.1", "gloo-timers", "tokio", ] -[[package]] -name = "base32" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" - [[package]] name = "base64" version = "0.21.7" @@ -503,9 +496,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" dependencies = [ "serde_core", ] @@ -521,16 +514,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.3" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", ] [[package]] @@ -550,9 +543,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ "hybrid-array", ] @@ -608,14 +601,14 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -638,7 +631,7 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01fe135c0bd16afe262b6dea349bd5ea30e6de50708cec639aae7c5c14cc7e4" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cairo-sys-rs", "glib", "libc", @@ -666,9 +659,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.55" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -676,9 +669,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.6" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cef5b5a1a6827c7322ae2a636368a573006b27cfa76c7ebd53e834daeaab6a" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" dependencies = [ "smallvec", "target-lexicon", @@ -704,14 +697,14 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -758,6 +751,12 @@ dependencies = [ "inout", ] +[[package]] +name = "cmov" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" + [[package]] name = "cobs" version = "0.3.0" @@ -769,9 +768,19 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] [[package]] name = "concurrent-queue" @@ -829,6 +838,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -864,9 +883,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "critical-section" @@ -925,13 +944,31 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "211f05e03c7d03754740fd9e585de910a095d6b99f8bcfffdef8319fa02a8331" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -950,16 +987,16 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "5.0.0-pre.1" +version = "5.0.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f9200d1d13637f15a6acb71e758f64624048d85b31a5fdbfd8eca1e2687d0b7" +checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "digest 0.11.0-rc.10", + "digest 0.11.3", "fiat-crypto 0.3.0", - "rand_core 0.9.5", + "rand_core 0.10.1", "rustc_version", "serde", "subtle", @@ -974,7 +1011,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -998,7 +1035,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1009,14 +1046,14 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "dashmap" -version = "6.1.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1028,9 +1065,29 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "data-encoding-macro" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3259c913752a86488b501ed8680446a5ed2d5aeac6e596cb23ba3800768ea32c" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" +dependencies = [ + "data-encoding", + "syn 2.0.117", +] [[package]] name = "der" @@ -1056,9 +1113,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -1082,7 +1139,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1103,7 +1160,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1113,7 +1170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1135,7 +1192,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.114", + "syn 2.0.117", "unicode-xid", ] @@ -1165,22 +1222,22 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.0-rc.10" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa94b64bfc6549e6e4b5a3216f22593224174083da7a90db47e951c4fb31725" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.11.0", + "block-buffer 0.12.0", "const-oid 0.10.2", - "crypto-common 0.2.0", + "crypto-common 0.2.2", ] [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "block2", "libc", "objc2", @@ -1194,41 +1251,26 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "dlopen2" -version = "0.5.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" dependencies = [ "libc", "once_cell", "winapi", ] -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - [[package]] name = "dotenvy" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - [[package]] name = "ed25519" version = "2.2.3" @@ -1247,7 +1289,7 @@ checksum = "c6e914c7c52decb085cea910552e24c63ac019e3ab8bf001ff736da9a9d9d890" dependencies = [ "pkcs8 0.11.0-rc.10", "serde", - "signature 3.0.0-rc.10", + "signature 3.0.0", ] [[package]] @@ -1267,25 +1309,25 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "3.0.0-pre.1" +version = "3.0.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad207ed88a133091f83224265eac21109930db09bedcad05d5252f2af2de20a1" +checksum = "053618a4c3d3bc24f188aa660ae75a46eeab74ef07fb415c61431e5e7cd4749b" dependencies = [ - "curve25519-dalek 5.0.0-pre.1", + "curve25519-dalek 5.0.0-pre.6", "ed25519 3.0.0-rc.4", - "rand_core 0.9.5", + "rand_core 0.10.1", "serde", - "sha2 0.11.0-rc.2", - "signature 3.0.0-rc.10", + "sha2 0.11.0-rc.5", + "signature 3.0.0", "subtle", "zeroize", ] [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" dependencies = [ "serde", ] @@ -1335,7 +1377,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1346,7 +1388,7 @@ checksum = "3ed8956bd5c1f0415200516e78ff07ec9e16415ade83c056c230d7b7ea0d55b7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1358,7 +1400,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1379,23 +1421,23 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "env_filter" -version = "0.1.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" dependencies = [ "log", ] [[package]] name = "env_logger" -version = "0.11.8" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ "anstream", "anstyle", @@ -1468,18 +1510,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fastbloom" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" -dependencies = [ - "getrandom 0.3.4", - "libm", - "rand 0.9.2", - "siphasher", -] - [[package]] name = "fastrand" version = "1.9.0" @@ -1491,9 +1521,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fiat-crypto" @@ -1590,9 +1620,9 @@ checksum = "d8866fac38f53fc87fa3ae1b09ddd723e0482f8fa74323518b4c59df2c55a00a" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1605,9 +1635,9 @@ dependencies = [ [[package]] name = "futures-buffered" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" +checksum = "4421cb78ee172b6b06080093479d3c50f058e7c81b7d577bbb8d118d551d4cd5" dependencies = [ "cordyceps", "diatomic-waker", @@ -1618,9 +1648,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1641,15 +1671,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1669,9 +1699,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1694,7 +1724,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-core", "futures-io", "parking", @@ -1703,32 +1733,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1738,7 +1768,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1831,7 +1860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c1bce85c110ab718fd139e0cc89c51b63bd647b14a767e24bdfc77c83df79b" dependencies = [ "arref", - "heapless 0.9.2", + "heapless 0.9.3", "itertools 0.11.0", "loro-thunderdome", "proc-macro2", @@ -1858,25 +1887,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", - "r-efi", - "rand_core 0.10.0", + "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -1899,6 +1928,16 @@ dependencies = [ "temp-dir", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gio" version = "0.21.5" @@ -1935,7 +1974,7 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "futures-channel", "futures-core", "futures-executor", @@ -1957,10 +1996,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17" dependencies = [ "heck 0.5.0", - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2077,10 +2116,10 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ccfb5a14a3d941244815d5f8101fa12d4577b59cc47245778d8d907b0003e42" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2104,9 +2143,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", @@ -2178,6 +2217,12 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "hashlink" version = "0.10.0" @@ -2213,9 +2258,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" dependencies = [ "hash32 0.3.1", "stable_deref_trait", @@ -2255,26 +2300,25 @@ dependencies = [ ] [[package]] -name = "hickory-proto" -version = "0.25.2" +name = "hickory-net" +version = "0.26.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +checksum = "1e232f503c4cfe3f4ea6594971255ecab9f6a0080c4c8e0e17630cc701322aa4" dependencies = [ "async-trait", "bytes", "cfg-if", "data-encoding", - "enum-as-inner 0.6.1", "futures-channel", "futures-io", "futures-util", "h2", + "hickory-proto", "http", "idna", "ipnet", - "once_cell", - "rand 0.9.2", - "ring", + "jni", + "rand 0.10.1", "rustls", "thiserror 2.0.18", "tinyvec", @@ -2284,23 +2328,48 @@ dependencies = [ "url", ] +[[package]] +name = "hickory-proto" +version = "0.26.0-beta.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcca12171ce774c549f35510be702f4da00ef12ca486f0f2acb2ee96f2f5ca0f" +dependencies = [ + "data-encoding", + "idna", + "ipnet", + "jni", + "once_cell", + "prefix-trie", + "rand 0.10.1", + "ring", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "url", +] + [[package]] name = "hickory-resolver" -version = "0.25.2" +version = "0.26.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +checksum = "1e7d2c928fa078e6640f26cf1b537b212e1688829c3944780025c7084e8bbbf6" dependencies = [ "cfg-if", "futures-util", + "hickory-net", "hickory-proto", "ipconfig", + "ipnet", + "jni", "moka", + "ndk-context", "once_cell", "parking_lot", - "rand 0.9.2", + "rand 0.10.1", "resolv-conf", "rustls", "smallvec", + "system-configuration", "thiserror 2.0.18", "tokio", "tokio-rustls", @@ -2381,31 +2450,29 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.7" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ "typenum", ] [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -2413,19 +2480,17 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http", "hyper", "hyper-util", "rustls", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] @@ -2445,7 +2510,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -2477,12 +2542,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -2490,9 +2556,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -2503,9 +2569,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -2517,15 +2583,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -2537,15 +2603,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -2587,35 +2653,14 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", ] -[[package]] -name = "igd-next" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516893339c97f6011282d5825ac94fc1c7aad5cad26bdc2d0cee068c0bf97f97" -dependencies = [ - "async-trait", - "attohttpc", - "bytes", - "futures", - "http", - "http-body-util", - "hyper", - "hyper-util", - "log", - "rand 0.9.2", - "tokio", - "url", - "xmltree", -] - [[package]] name = "im" version = "15.1.0" @@ -2633,12 +2678,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -2675,66 +2720,62 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.5.10", + "socket2 0.6.3", "widestring", - "windows-sys 0.48.0", - "winreg", + "windows-registry", + "windows-result", + "windows-sys 0.61.2", ] [[package]] name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.10" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" dependencies = [ - "memchr", "serde", ] [[package]] name = "iroh" -version = "0.96.1" +version = "0.98.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5236da4d5681f317ec393c8fe2b7e3d360d31c6bb40383991d0b7429ca5ad117" +checksum = "9881b221c7c645d90594cbd331012f7cccb914894288a6cf5538a9115f6d0f3e" dependencies = [ "backon", + "blake3", "bytes", "cfg_aliases", + "ctutils", "data-encoding", + "der 0.8.0-rc.10", "derive_more", - "ed25519-dalek 3.0.0-pre.1", + "ed25519-dalek 3.0.0-pre.6", "futures-util", - "getrandom 0.3.4", + "getrandom 0.4.2", "hickory-resolver", "http", - "igd-next", + "ipnet", "iroh-base", + "iroh-dns", "iroh-metrics", - "iroh-quinn", - "iroh-quinn-proto", - "iroh-quinn-udp", "iroh-relay", "n0-error", "n0-future", "n0-watcher", - "netdev", "netwatch", + "noq", + "noq-proto", + "noq-udp", "papaya", "pin-project", - "pkarr", "pkcs8 0.11.0-rc.10", - "portmapper", - "rand 0.9.2", + "portable-atomic", + "rand 0.10.1", "reqwest", "rustc-hash", "rustls", @@ -2742,9 +2783,8 @@ dependencies = [ "rustls-webpki", "serde", "smallvec", - "strum 0.27.2", + "strum", "swarm-discovery", - "sync_wrapper", "time", "tokio", "tokio-stream", @@ -2757,35 +2797,52 @@ dependencies = [ [[package]] name = "iroh-base" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c99d836a1c99e037e98d1bf3ef209c3a4df97555a00ce9510eb78eccdf5567" +checksum = "738865784637830fb14204ebd3047922db83bc1816a59027af29579b9c27bd99" dependencies = [ - "curve25519-dalek 5.0.0-pre.1", + "curve25519-dalek 5.0.0-pre.6", "data-encoding", + "data-encoding-macro", "derive_more", - "digest 0.11.0-rc.10", - "ed25519-dalek 3.0.0-pre.1", + "digest 0.11.3", + "ed25519-dalek 3.0.0-pre.6", + "getrandom 0.4.2", "n0-error", - "rand_core 0.9.5", + "rand 0.10.1", "serde", - "sha2 0.11.0-rc.2", + "sha2 0.11.0-rc.5", "url", "zeroize", "zeroize_derive", ] +[[package]] +name = "iroh-dns" +version = "0.98.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca474630d1e62ddef83149db6babe6a1055d901df9054349d31b22df99811b92" +dependencies = [ + "derive_more", + "iroh-base", + "n0-error", + "n0-future", + "simple-dns", + "strum", +] + [[package]] name = "iroh-gossip" -version = "0.96.0" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d04f83254c847ac61a9b2215b95a36d598d87af033ca12a546cd1c6a2e06dab" +checksum = "b349a9ab58e3b56cf41df693bc1812add192ad70ce7c8d0dbdc7d0319d71b11f" dependencies = [ "blake3", "bytes", + "constant_time_eq", "data-encoding", "derive_more", - "ed25519-dalek 3.0.0-pre.1", + "ed25519-dalek 3.0.0-pre.6", "futures-concurrency", "futures-lite 2.6.1", "futures-util", @@ -2798,7 +2855,7 @@ dependencies = [ "n0-error", "n0-future", "postcard", - "rand 0.9.2", + "rand 0.10.1", "serde", "tokio", "tokio-util", @@ -2807,13 +2864,14 @@ dependencies = [ [[package]] name = "iroh-metrics" -version = "0.38.2" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c946095f060e6e59b9ff30cc26c75cdb758e7fb0cde8312c89e2144654989fcb" +checksum = "761b45ba046134b11eb3e432fa501616b45c4bf3a30c21717578bc07aa6461dd" dependencies = [ "iroh-metrics-derive", "itoa", "n0-error", + "portable-atomic", "postcard", "ryu", "serde", @@ -2829,104 +2887,44 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", -] - -[[package]] -name = "iroh-quinn" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034ed21f34c657a123d39525d948c885aacba59508805e4dd67d71f022e7151b" -dependencies = [ - "bytes", - "cfg_aliases", - "iroh-quinn-proto", - "iroh-quinn-udp", - "pin-project-lite", - "rustc-hash", - "rustls", - "socket2 0.6.2", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tracing", - "web-time", -] - -[[package]] -name = "iroh-quinn-proto" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de99ad8adc878ee0e68509ad256152ce23b8bbe45f5539d04e179630aca40a9" -dependencies = [ - "bytes", - "derive_more", - "enum-assoc", - "fastbloom", - "getrandom 0.3.4", - "identity-hash", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "sorted-index-buffer", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "iroh-quinn-udp" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f981dadd5a072a9e0efcd24bdcc388e570073f7e51b33505ceb1ef4668c80c86" -dependencies = [ - "cfg_aliases", - "libc", - "socket2 0.6.2", - "tracing", - "windows-sys 0.61.2", + "syn 2.0.117", ] [[package]] name = "iroh-relay" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2b63e654b9dec799a73372cdc79b529ca6c7248c0c8de7da78a02e3a46f03c" +checksum = "4aa6e9a7277bfbb439739c52b57eb5f9288030983928412022b8e94a43d4d838" dependencies = [ "blake3", "bytes", "cfg_aliases", "data-encoding", "derive_more", - "getrandom 0.3.4", + "getrandom 0.4.2", "hickory-resolver", "http", "http-body-util", "hyper", "hyper-util", "iroh-base", + "iroh-dns", "iroh-metrics", - "iroh-quinn", - "iroh-quinn-proto", "lru", "n0-error", "n0-future", + "noq", + "noq-proto", "num_enum", "pin-project", - "pkarr", "postcard", - "rand 0.9.2", + "rand 0.10.1", "reqwest", "rustls", "rustls-pki-types", "serde", "serde_bytes", - "strum 0.27.2", + "strum", "tokio", "tokio-rustls", "tokio-util", @@ -2936,14 +2934,13 @@ dependencies = [ "vergen-gitcl", "webpki-roots", "ws_stream_wasm", - "z32", ] [[package]] name = "irpc" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bbc84aaeab13a6d7502bae4f40f2517b643924842e0230ea0bf807477cc208" +checksum = "26bacc8d71f54f16cb5ae82745cfca440ad8ecd09b4480d415b8d9dc78146432" dependencies = [ "futures-util", "irpc-derive", @@ -2957,13 +2954,13 @@ dependencies = [ [[package]] name = "irpc-derive" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58148196d2230183c9679431ac99b57e172000326d664e8456fa2cd27af6505a" +checksum = "4651422b9d7af09fa1437a5fabbd9e074162b502a1af7f5bae8b439eaf3e049f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2992,16 +2989,67 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -3016,7 +3064,7 @@ dependencies = [ "lazy_static", "linux-keyutils", "secret-service", - "security-framework", + "security-framework 2.11.1", "windows-sys 0.52.0", ] @@ -3031,9 +3079,9 @@ dependencies = [ [[package]] name = "leb128" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +checksum = "6cc46bac87ef8093eed6f272babb833b6443374399985ac8ed28471ee0918545" [[package]] name = "leb128fmt" @@ -3074,9 +3122,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.180" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -3086,13 +3134,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", - "redox_syscall 0.7.0", + "plain", + "redox_syscall 0.7.5", ] [[package]] @@ -3137,11 +3186,11 @@ dependencies = [ [[package]] name = "linux-keyutils" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" +checksum = "83270a18e9f90d0707c41e9f35efada77b64c0e6f3f1810e71c8368a864d5590" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", ] @@ -3159,21 +3208,15 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "litrs" -version = "1.0.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "locale_config" @@ -3298,7 +3341,7 @@ dependencies = [ "pest_derive", "postcard", "pretty_assertions", - "rand 0.8.5", + "rand 0.8.6", "rustc-hash", "serde", "serde_columnar", @@ -3352,15 +3395,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c8ea186958094052b971fe7e322a934b034c3bf62f0458ccea04fcd687ba1" dependencies = [ "once_cell", - "rand 0.8.5", + "rand 0.8.6", "serde", ] [[package]] name = "lru" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" dependencies = [ "hashbrown 0.16.1", ] @@ -3373,9 +3416,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lz4_flex" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" +checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a" dependencies = [ "twox-hash", ] @@ -3446,9 +3489,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -3457,9 +3500,9 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.13" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac832c50ced444ef6be0767a008b02c106a909ba79d1d830501e94b96f6b7e" +checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -3491,7 +3534,7 @@ checksum = "03755949235714b2b307e5ae89dd8c1c2531fb127d9b8b7b4adf9c876cd3ed18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3526,11 +3569,17 @@ dependencies = [ "n0-future", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "netdev" -version = "0.40.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9815643a243856e7bd84524e1ff739e901e846cfb06ad9627cd2b6d59bd737" +checksum = "e30af1a5073b82356d9317c18226826370b4288eba2f71c7e84e18bae51b3847" dependencies = [ "block2", "dispatch2", @@ -3539,13 +3588,13 @@ dependencies = [ "libc", "mac-addr", "netlink-packet-core", - "netlink-packet-route 0.25.1", + "netlink-packet-route 0.29.0", "netlink-sys", "objc2-core-foundation", "objc2-system-configuration", "once_cell", "plist", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3559,11 +3608,11 @@ dependencies = [ [[package]] name = "netlink-packet-route" -version = "0.25.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec2f5b6839be2a19d7fa5aab5bc444380f6311c2b693551cb80f45caaa7b5ef" +checksum = "df9854ea6ad14e3f4698a7f03b65bce0833dd2d81d594a0e4a984170537146b6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", "log", "netlink-packet-core", @@ -3571,11 +3620,11 @@ dependencies = [ [[package]] name = "netlink-packet-route" -version = "0.28.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce3636fa715e988114552619582b530481fd5ef176a1e5c1bf024077c2c9445" +checksum = "be8919612f6028ab4eacbbfe1234a9a43e3722c6e0915e7ff519066991905092" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", "log", "netlink-packet-core", @@ -3610,15 +3659,14 @@ dependencies = [ [[package]] name = "netwatch" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "454b8c0759b2097581f25ed5180b4a1d14c324fde6d0734932a288e044d06232" +checksum = "6fc0d4b4134425d9834e591b1a6f807ea365c6d941d738942215564af5f28a97" dependencies = [ "atomic-waker", "bytes", "cfg_aliases", "derive_more", - "iroh-quinn-udp", "js-sys", "libc", "n0-error", @@ -3626,14 +3674,15 @@ dependencies = [ "n0-watcher", "netdev", "netlink-packet-core", - "netlink-packet-route 0.28.0", + "netlink-packet-route 0.30.0", "netlink-proto", "netlink-sys", + "noq-udp", "objc2-core-foundation", "objc2-system-configuration", "pin-project-lite", "serde", - "socket2 0.6.2", + "socket2 0.6.3", "time", "tokio", "tokio-util", @@ -3663,18 +3712,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" [[package]] -name = "ntimestamp" -version = "1.0.0" +name = "noq" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50f94c405726d3e0095e89e72f75ce7f6587b94a8bd8dc8054b73f65c0fd68c" +checksum = "4b969bd157c3bd3bab239a1a8b14f67f2033fa012770367fcbd5b42d71ae3548" dependencies = [ - "base32", - "document-features", - "getrandom 0.2.17", - "httpdate", - "js-sys", - "once_cell", - "serde", + "bytes", + "cfg_aliases", + "derive_more", + "noq-proto", + "noq-udp", + "pin-project-lite", + "rustc-hash", + "rustls", + "socket2 0.6.3", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", + "web-time", +] + +[[package]] +name = "noq-proto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdec6f5039d98ee5377b2f532d495a555eb664c53161b1b5780dcaeac678b60e" +dependencies = [ + "aes-gcm", + "bytes", + "derive_more", + "enum-assoc", + "getrandom 0.4.2", + "identity-hash", + "lru-slab", + "rand 0.10.1", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "sorted-index-buffer", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "noq-udp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee91b05f4f3353290936ba1f3233518868fb4e2da99cb4c90d1f8cebb064e527" +dependencies = [ + "cfg_aliases", + "libc", + "socket2 0.6.3", + "tracing", + "windows-sys 0.61.2", ] [[package]] @@ -3721,7 +3816,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "serde", "smallvec", "zeroize", @@ -3738,9 +3833,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -3785,9 +3880,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -3795,14 +3890,14 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3836,9 +3931,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", ] @@ -3849,7 +3944,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "block2", "dispatch2", "libc", @@ -3868,7 +3963,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "objc2", "objc2-core-foundation", ] @@ -3879,7 +3974,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "dispatch2", "libc", "objc2", @@ -3898,9 +3993,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" dependencies = [ "critical-section", "portable-atomic", @@ -3930,26 +4025,31 @@ dependencies = [ "num", "num-bigint-dig", "openssl", - "rand 0.9.2", + "rand 0.9.4", "serde", "tracing", - "zbus 5.13.2", - "zbus_macros 5.13.2", + "zbus 5.15.0", + "zbus_macros 5.15.0", "zeroize", - "zvariant 5.9.2", + "zvariant 5.11.0", ] +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "foreign-types", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -3962,14 +4062,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" dependencies = [ "cc", "libc", @@ -3987,16 +4093,37 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "p2panda" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5544dd10dd9b9eaeda24327888fe6bf75ed530e8c601d2406ddf4bf9f30f3c4e" +dependencies = [ + "futures-util", + "p2panda-core", + "p2panda-net", + "p2panda-store", + "p2panda-stream", + "p2panda-sync", + "pin-project", + "serde", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "p2panda-core" -version = "0.5.1" -source = "git+https://github.com/p2panda/p2panda?rev=a1308c116c1bd345f2cd6af7df41d2a6e0a4a682#a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27a463e79633277c88888f3b2e7bc6eb10e240e9b21a8aa262eb1aa814e4bf08" dependencies = [ "blake3", "ciborium", "ed25519-dalek 2.2.0", "hex", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_bytes", "thiserror 2.0.18", @@ -4004,13 +4131,15 @@ dependencies = [ [[package]] name = "p2panda-discovery" -version = "0.5.1" -source = "git+https://github.com/p2panda/p2panda?rev=a1308c116c1bd345f2cd6af7df41d2a6e0a4a682#a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b941f35c251f1e38ba985b6628ba57e3403806a58a5db4968be707728354e56" dependencies = [ "blake3", "futures-util", - "rand 0.9.2", - "rand_chacha 0.9.0", + "p2panda-core", + "p2panda-store", + "rand 0.10.1", "serde", "thiserror 2.0.18", "tokio", @@ -4018,8 +4147,9 @@ dependencies = [ [[package]] name = "p2panda-net" -version = "0.5.1" -source = "git+https://github.com/p2panda/p2panda?rev=a1308c116c1bd345f2cd6af7df41d2a6e0a4a682#a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e268db7d67957a6d9e38affaee7508f63e47cec3e8a6d66f7e9afaa7199623e" dependencies = [ "ciborium", "futures-channel", @@ -4033,8 +4163,8 @@ dependencies = [ "p2panda-store", "p2panda-sync", "ractor", - "rand 0.9.2", - "rand_chacha 0.9.0", + "rand 0.10.1", + "rand_chacha 0.10.0", "serde", "thiserror 2.0.18", "tokio", @@ -4045,36 +4175,36 @@ dependencies = [ [[package]] name = "p2panda-store" -version = "0.5.1" -source = "git+https://github.com/p2panda/p2panda?rev=a1308c116c1bd345f2cd6af7df41d2a6e0a4a682#a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ddf71909d86291bad9650b3f47f04e26997aa71e1c4077ba697dd53980095a" dependencies = [ - "ciborium", - "hex", "p2panda-core", + "serde", "sqlx", "thiserror 2.0.18", - "trait-variant", + "tokio", ] [[package]] name = "p2panda-stream" -version = "0.5.1" -source = "git+https://github.com/p2panda/p2panda?rev=a1308c116c1bd345f2cd6af7df41d2a6e0a4a682#a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a09be2ae329764c49d299e81da615852028f7e59c54bac7e43a4d08f5286d9cc" dependencies = [ - "ciborium", - "futures-channel", - "futures-util", + "futures-core", "p2panda-core", "p2panda-store", "pin-project", - "pin-utils", "thiserror 2.0.18", + "tokio", ] [[package]] name = "p2panda-sync" -version = "0.5.1" -source = "git+https://github.com/p2panda/p2panda?rev=a1308c116c1bd345f2cd6af7df41d2a6e0a4a682#a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dbb323a05aa5c10f668f2515a8755981aba36c5cdeed9f860ca86ddd86bee8" dependencies = [ "futures", "futures-util", @@ -4114,9 +4244,9 @@ dependencies = [ [[package]] name = "papaya" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92dd0b07c53a0a0c764db2ace8c541dc47320dad97c2200c2a637ab9dd2328f" +checksum = "997ee03cd38c01469a7046643714f0ad28880bcb9e6679ff0666e24817ca19b7" dependencies = [ "equivalent", "seize", @@ -4211,7 +4341,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4236,78 +4366,41 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "piper" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.3.0", + "fastrand 2.4.1", "futures-io", ] -[[package]] -name = "pkarr" -version = "5.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d346b545765a0ef58b6a7e160e17ddaa7427f439b7b9a287df6c88c9e04bf2" -dependencies = [ - "async-compat", - "base32", - "bytes", - "cfg_aliases", - "document-features", - "dyn-clone", - "ed25519-dalek 3.0.0-pre.1", - "futures-buffered", - "futures-lite 2.6.1", - "getrandom 0.3.4", - "log", - "lru", - "ntimestamp", - "reqwest", - "self_cell", - "serde", - "sha1_smol", - "simple-dns", - "thiserror 2.0.18", - "tokio", - "tracing", - "url", - "wasm-bindgen-futures", -] - [[package]] name = "pkcs1" version = "0.7.5" @@ -4341,15 +4434,21 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "plist" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" dependencies = [ "base64 0.22.1", "indexmap", @@ -4384,44 +4483,29 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys 0.61.2", ] [[package]] -name = "portable-atomic" -version = "1.13.1" +name = "polyval" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] [[package]] -name = "portmapper" -version = "0.14.0" +name = "portable-atomic" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d2a8825353ace3285138da3378b1e21860d60351942f7aa3b99b13b41f80318" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" dependencies = [ - "base64 0.22.1", - "bytes", - "derive_more", - "futures-lite 2.6.1", - "futures-util", - "hyper-util", - "igd-next", - "iroh-metrics", - "libc", - "n0-error", - "netwatch", - "num_enum", - "rand 0.9.2", "serde", - "smallvec", - "socket2 0.6.2", - "time", - "tokio", - "tokio-util", - "tower-layer", - "tracing", - "url", ] [[package]] @@ -4446,14 +4530,14 @@ checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -4473,6 +4557,17 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prefix-trie" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf6e3177f0684016a5c209b00882e15f8bdd3f3bb48f0491df10cd102d0c6e7" +dependencies = [ + "either", + "ipnet", + "num-traits", +] + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -4490,7 +4585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4505,11 +4600,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.25.11+spec-1.1.0", ] [[package]] @@ -4523,18 +4618,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] [[package]] name = "quick_cache" -version = "0.6.18" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ada44a88ef953a3294f6eb55d2007ba44646015e18613d2f213016379203ef3" +checksum = "d1c821816e9b928e20e92ed59bb3ac4aab321d16ca2316871c9fe7ca739cd477" dependencies = [ "ahash", "equivalent", @@ -4542,66 +4637,11 @@ dependencies = [ "parking_lot", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2 0.6.2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.2", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -4612,18 +4652,24 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "ractor" -version = "0.15.10" +version = "0.15.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6102314f700f3e8df466c49110830b18cbfc172f88f27a9d7383e455663b1be7" +checksum = "f12c86deb2af198b10a04c4fb3fba73baf3bb300df765a29272f0e5583da7510" dependencies = [ "bon", "dashmap", "futures", "js-sys", "once_cell", - "strum 0.26.3", + "strum", "tokio", "tokio_with_wasm", "tracing", @@ -4634,9 +4680,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4645,9 +4691,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4655,13 +4701,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom 0.4.1", - "rand_core 0.10.0", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -4691,7 +4737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e6af7f3e25ded52c41df4e0b1af2d047e45896c2f3281792ed68a1c243daedb" dependencies = [ "ppv-lite86", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -4714,9 +4760,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_xoshiro" @@ -4733,16 +4779,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] @@ -4776,7 +4822,8 @@ dependencies = [ "hex", "indexmap", "loro", - "rand 0.10.0", + "p2panda-core", + "rand 0.10.1", "reflection-node", "serde", "test-log", @@ -4789,17 +4836,9 @@ name = "reflection-node" version = "0.3.0" dependencies = [ "chrono", - "ciborium", - "hex", - "p2panda-core", - "p2panda-discovery", - "p2panda-net", + "p2panda", "p2panda-store", - "p2panda-stream", - "p2panda-sync", - "rand_chacha 0.10.0", "serde", - "serde_bytes", "sqlx", "test-log", "thiserror 2.0.18", @@ -4833,15 +4872,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ "base64 0.22.1", "bytes", @@ -4857,12 +4896,9 @@ dependencies = [ "log", "percent-encoding", "pin-project-lite", - "quinn", "rustls", "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", + "rustls-platform-verifier", "sync_wrapper", "tokio", "tokio-rustls", @@ -4875,7 +4911,6 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", ] [[package]] @@ -4920,9 +4955,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -4953,7 +4988,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4962,22 +4997,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys 0.12.1", "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "log", "once_cell", @@ -4989,20 +5024,59 @@ dependencies = [ ] [[package]] -name = "rustls-pki-types" -version = "1.14.0" +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.7.0", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.7.0", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -5021,6 +5095,24 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -5046,7 +5138,7 @@ dependencies = [ "hkdf", "num", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "serde", "sha2 0.10.9", "zbus 3.15.2", @@ -5058,8 +5150,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.10.0", - "core-foundation", + "bitflags 2.11.1", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -5067,9 +5172,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -5085,17 +5190,11 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "self_cell" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" - [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "send_wrapper" @@ -5145,7 +5244,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5165,7 +5264,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5189,14 +5288,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -5243,13 +5342,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.11.0-rc.2" +version = "0.11.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" +checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest 0.11.0-rc.10", + "digest 0.11.3", ] [[package]] @@ -5289,9 +5388,19 @@ dependencies = [ [[package]] name = "signature" -version = "3.0.0-rc.10" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] [[package]] name = "simdutf8" @@ -5305,15 +5414,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - [[package]] name = "sized-chunks" version = "0.6.5" @@ -5357,22 +5460,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5424,7 +5517,7 @@ checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5520,7 +5613,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5543,7 +5636,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.114", + "syn 2.0.117", "tokio", "url", ] @@ -5556,7 +5649,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.1", "byteorder", "bytes", "chrono", @@ -5578,7 +5671,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand 0.8.5", + "rand 0.8.6", "rsa", "serde", "sha1", @@ -5599,7 +5692,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.1", "byteorder", "chrono", "crc", @@ -5617,7 +5710,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "sha2 0.10.9", @@ -5685,45 +5778,23 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.114", + "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5734,14 +5805,14 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "swarm-discovery" -version = "0.5.0" +version = "0.6.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5ab62937edac8b23fa40e55a358ea1924245b17fc1eb20d14929c8f11be98d" +checksum = "cf5ccbd3c5abd6e7314768de12649c1b0a29bea38fca4370f9408340c0f364a6" dependencies = [ "acto", "hickory-proto", - "rand 0.9.2", - "socket2 0.6.2", + "rand 0.10.1", + "socket2 0.6.3", "thiserror 2.0.18", "tokio", "tracing", @@ -5760,9 +5831,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -5786,14 +5857,35 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] name = "system-deps" -version = "7.0.7" +version = "7.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" dependencies = [ "cfg-expr", "heck 0.5.0", @@ -5822,22 +5914,22 @@ checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.3.0", - "getrandom 0.3.4", + "fastrand 2.4.1", + "getrandom 0.4.2", "once_cell", - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys 0.61.2", ] [[package]] name = "test-log" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" +checksum = "2f46bf474f0a4afebf92f076d54fd5e63423d9438b8c278a3d2ccb0f47f7cdb3" dependencies = [ "env_logger", "test-log-macros", @@ -5845,14 +5937,24 @@ dependencies = [ ] [[package]] -name = "test-log-macros" -version = "0.2.19" +name = "test-log-core" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" +checksum = "37d4d41320b48bc4a211a9021678fcc0c99569b594ea31c93735b8e517102b4c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", +] + +[[package]] +name = "test-log-macros" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9beb9249a81e430dffd42400a49019bcf548444f1968ff23080a625de0d4d320" +dependencies = [ + "syn 2.0.117", + "test-log-core", ] [[package]] @@ -5881,7 +5983,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5892,7 +5994,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5940,9 +6042,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -5950,9 +6052,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -5965,16 +6067,16 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.49.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2 0.6.3", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -5982,13 +6084,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6030,20 +6132,21 @@ dependencies = [ [[package]] name = "tokio-websockets" -version = "0.12.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b6348ebfaaecd771cecb69e832961d277f59845d4220a584701f72728152b7" +checksum = "dad543404f98bfc969aeb71994105c592acfc6c43323fddcd016bb208d1c65cb" dependencies = [ "base64 0.22.1", "bytes", "futures-core", "futures-sink", - "getrandom 0.3.4", + "getrandom 0.4.2", "http", "httparse", - "rand 0.9.2", + "rand 0.10.1", "ring", "rustls-pki-types", + "sha1_smol", "simdutf8", "tokio", "tokio-rustls", @@ -6071,22 +6174,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d01145a2c788d6aae4cd653afec1e8332534d7d783d01897cefcafe4428de992" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime 0.7.5+spec-1.1.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 0.7.14", + "winnow 1.0.3", ] [[package]] @@ -6097,9 +6200,9 @@ checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -6117,30 +6220,30 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap", - "toml_datetime 0.7.5+spec-1.1.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 0.7.14", + "winnow 1.0.3", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 0.7.14", + "winnow 1.0.3", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tower" @@ -6159,20 +6262,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -6207,7 +6310,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6233,9 +6336,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -6249,17 +6352,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trait-variant" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -6274,9 +6366,9 @@ checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -6286,13 +6378,13 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uds_windows" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset 0.9.1", "tempfile", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -6303,9 +6395,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" @@ -6324,9 +6416,9 @@ checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-xid" @@ -6334,6 +6426,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -6367,11 +6469,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.20.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", @@ -6455,6 +6557,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -6472,11 +6584,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -6485,7 +6597,7 @@ 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", + "wit-bindgen 0.51.0", ] [[package]] @@ -6496,9 +6608,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -6509,23 +6621,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6533,22 +6641,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -6577,9 +6685,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", @@ -6594,7 +6702,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap", "semver", @@ -6602,9 +6710,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -6620,11 +6728,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -6661,6 +6778,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -6720,7 +6846,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6731,7 +6857,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6750,6 +6876,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -6795,15 +6932,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -6837,30 +6965,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" version = "0.2.1" @@ -6882,12 +6993,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6900,12 +7005,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -6918,24 +7017,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -6948,12 +7035,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6966,12 +7047,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6984,12 +7059,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -7002,12 +7071,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.5.40" @@ -7019,23 +7082,13 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.14" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -7045,6 +7098,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -7066,7 +7125,7 @@ dependencies = [ "heck 0.5.0", "indexmap", "prettyplease", - "syn 2.0.114", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -7082,7 +7141,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -7094,7 +7153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags 2.11.1", "indexmap", "log", "serde", @@ -7126,9 +7185,9 @@ dependencies = [ [[package]] name = "wmi" -version = "0.18.1" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "746791db82f029aaefc774ccbb8e61306edba18ef2c8998337cadccc0b8067f7" +checksum = "7c81b85c57a57500e56669586496bf2abd5cf082b9d32995251185d105208b64" dependencies = [ "chrono", "futures", @@ -7141,9 +7200,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "ws_stream_wasm" @@ -7174,21 +7233,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "xml-rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" - -[[package]] -name = "xmltree" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" -dependencies = [ - "xml-rs", -] - [[package]] name = "xxhash-rust" version = "0.8.15" @@ -7203,9 +7247,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -7214,22 +7258,16 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] -[[package]] -name = "z32" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2164e798d9e3d84ee2c91139ace54638059a3b23e361f5c11781c2c6459bde0f" - [[package]] name = "zbus" version = "3.15.2" @@ -7257,7 +7295,7 @@ dependencies = [ "nix", "once_cell", "ordered-stream", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_repr", "sha1", @@ -7273,9 +7311,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.13.2" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1" +checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" dependencies = [ "async-broadcast 0.7.2", "async-executor", @@ -7293,17 +7331,17 @@ dependencies = [ "hex", "libc", "ordered-stream", - "rustix 1.1.3", + "rustix 1.1.4", "serde", "serde_repr", "tracing", "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.14", - "zbus_macros 5.13.2", - "zbus_names 4.3.1", - "zvariant 5.9.2", + "winnow 1.0.3", + "zbus_macros 5.15.0", + "zbus_names 4.3.2", + "zvariant 5.11.0", ] [[package]] @@ -7322,17 +7360,17 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.13.2" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1" +checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.114", - "zbus_names 4.3.1", - "zvariant 5.9.2", - "zvariant_utils 3.3.0", + "syn 2.0.117", + "zbus_names 4.3.2", + "zvariant 5.11.0", + "zvariant_utils 3.3.1", ] [[package]] @@ -7348,53 +7386,53 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "winnow 0.7.14", - "zvariant 5.9.2", + "winnow 1.0.3", + "zvariant 5.11.0", ] [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -7415,14 +7453,14 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -7431,9 +7469,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -7442,20 +7480,20 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zmij" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zvariant" @@ -7473,17 +7511,17 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.9.2" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4" +checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow 0.7.14", - "zvariant_derive 5.9.2", - "zvariant_utils 3.3.0", + "winnow 1.0.3", + "zvariant_derive 5.11.0", + "zvariant_utils 3.3.1", ] [[package]] @@ -7501,15 +7539,15 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.9.2" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c" +checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.114", - "zvariant_utils 3.3.0", + "syn 2.0.117", + "zvariant_utils 3.3.1", ] [[package]] @@ -7525,13 +7563,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.114", - "winnow 0.7.14", + "syn 2.0.117", + "winnow 1.0.3", ] diff --git a/reflection-app/src/application.rs b/reflection-app/src/application.rs index 92dcb675..e31a354d 100644 --- a/reflection-app/src/application.rs +++ b/reflection-app/src/application.rs @@ -22,7 +22,7 @@ use adw::prelude::*; use adw::subclass::prelude::*; use gettextrs::gettext; use gtk::{gdk, gio, glib, glib::Properties, glib::clone}; -use reflection_doc::{document::DocumentId, identity::PrivateKey, service::Service}; +use reflection_doc::{document::DocumentId, identity::SigningKey, service::Service}; use std::{cell::RefCell, fs}; use thiserror::Error; use tracing::error; @@ -258,15 +258,15 @@ impl ReflectionApplication { } async fn create_service(&self) -> Result { - let private_key = secret::get_or_create_identity().await?; + let signing_key = secret::get_or_create_identity().await?; let mut data_path = glib::user_data_dir(); data_path.push("Reflection"); - data_path.push(private_key.public_key().to_string()); + data_path.push(signing_key.verifying_key().to_string()); fs::create_dir_all(&data_path)?; let data_dir = gio::File::for_path(data_path); - let service = Service::new(&private_key, Some(&data_dir)); + let service = Service::new(&signing_key, Some(&data_dir)); service.startup().await?; Ok(service) @@ -406,8 +406,8 @@ impl ReflectionApplication { } async fn new_temporary_identity(&self) { - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); + let signing_key = SigningKey::generate(); + let service = Service::new(&signing_key, None); if let Err(error) = service.startup().await { let error = error.into(); diff --git a/reflection-app/src/secret.rs b/reflection-app/src/secret.rs index 26b6222a..0e6a836b 100644 --- a/reflection-app/src/secret.rs +++ b/reflection-app/src/secret.rs @@ -23,7 +23,7 @@ use tracing::info; #[cfg(target_os = "linux")] use crate::APP_ID; -use reflection_doc::identity::{IdentityError, PrivateKey}; +use reflection_doc::identity::{IdentityError, SigningKey}; #[cfg(target_os = "linux")] const XDG_SCHEMA: &str = "xdg:schema"; @@ -52,60 +52,60 @@ pub enum Error { } #[cfg(target_os = "linux")] -pub async fn get_or_create_identity() -> Result { +pub async fn get_or_create_identity() -> Result { let keyring = oo7::Keyring::new().await?; keyring.unlock().await?; - let private_key: PrivateKey = + let signing_key: SigningKey = if let Some(item) = keyring.search_items(&attributes()).await?.first() { item.unlock().await?; - let private_key = PrivateKey::try_from(item.secret().await?.as_bytes())?; - info!("Found existing identity: {}", private_key.public_key()); + let signing_key = SigningKey::try_from(item.secret().await?.as_bytes())?; + info!("Found existing identity: {}", signing_key.verifying_key()); - private_key + signing_key } else { - let private_key = PrivateKey::new(); + let signing_key = SigningKey::generate(); keyring - .create_item("Reflection", &attributes(), private_key.as_bytes(), true) + .create_item("Reflection", &attributes(), signing_key.as_bytes(), true) .await?; info!( "No existing identity found. Create new identity: {}", - private_key.public_key() + signing_key.verifying_key() ); - private_key + signing_key }; - Ok(private_key) + Ok(signing_key) } #[cfg(target_os = "macos")] -pub async fn get_or_create_identity() -> Result { +pub async fn get_or_create_identity() -> Result { let entry = keyring::Entry::new("Reflection Identity", "default user")?; - let private_key: PrivateKey = match entry.get_password() { + let signing_key: SigningKey = match entry.get_password() { Ok(password) => { - let private_key = PrivateKey::try_from( + let signing_key = SigningKey::try_from( Base64Engine .decode(password) .expect("Failed to decode base64 secret from keyring") .as_slice(), )?; - info!("Found existing identity: {}", private_key.public_key()); - private_key + info!("Found existing identity: {}", signing_key.verifying_key()); + signing_key } Err(keyring::Error::NoEntry) => { - let private_key = PrivateKey::new(); - entry.set_password(&Base64Engine.encode(private_key.as_bytes()))?; + let signing_key = SigningKey::generate(); + entry.set_password(&Base64Engine.encode(signing_key.as_bytes()))?; info!( "No existing identity found. Create new identity: {}", - private_key.public_key() + signing_key.verifying_key() ); - private_key + signing_key } Err(e) => return Err(Error::Service(e)), }; - Ok(private_key) + Ok(signing_key) } diff --git a/reflection-doc/Cargo.toml b/reflection-doc/Cargo.toml index d52206e7..7ead1613 100644 --- a/reflection-doc/Cargo.toml +++ b/reflection-doc/Cargo.toml @@ -13,7 +13,8 @@ gio = "0.21" glib = "0.21" hex = "0.4.3" indexmap = "2.13.0" -loro = "1.10.8" +loro = "1.12.0" +p2panda-core = "0.6.1" rand = "0.10.0" reflection-node = { path = "../reflection-node" } serde = { version = "1.0.228", features = ["derive"] } diff --git a/reflection-doc/src/author.rs b/reflection-doc/src/author.rs index ae4b80a6..f41bb062 100644 --- a/reflection-doc/src/author.rs +++ b/reflection-doc/src/author.rs @@ -5,7 +5,7 @@ use glib::Properties; use glib::prelude::*; use glib::subclass::prelude::*; -use crate::identity::PublicKey; +use crate::identity::VerifyingKey; pub const COLORS: [(&str, &str); 14] = [ ("Yellow", "#faf387"), @@ -78,15 +78,15 @@ mod imp { #[property(name = "emoji", get = Self::emoji, type = String)] #[property(name = "color", get = Self::color, type = String)] #[property(name = "hex-color", get = Self::hex_color, type = String)] - #[property(get, set, construct_only, type = PublicKey)] - public_key: OnceLock, + #[property(get, set, construct_only, type = VerifyingKey)] + verifying_key: OnceLock, #[property(get, set, construct_only)] pub last_seen: Mutex>, #[property(get, default = true)] pub is_online: Cell, #[property(get)] pub is_this_device: Cell, - pub last_cursor_update: Mutex>, + pub last_cursor_update: Mutex>, } #[glib::object_subclass] @@ -100,7 +100,7 @@ mod imp { impl Author { fn name(&self) -> String { - let bytes = self.public_key.get().unwrap().as_bytes(); + let bytes = self.verifying_key.get().unwrap().as_bytes(); let selector_color = bytes[..(bytes.len() / 2)] .iter() .fold(0u8, |acc, b| acc ^ b) as usize @@ -113,7 +113,7 @@ mod imp { } fn emoji(&self) -> String { - let bytes = self.public_key.get().unwrap().as_bytes(); + let bytes = self.verifying_key.get().unwrap().as_bytes(); let selector_emoji = bytes[(bytes.len() / 2)..] .iter() .fold(0u8, |acc, b| acc ^ b) as usize @@ -122,7 +122,7 @@ mod imp { } fn color(&self) -> String { - let bytes = self.public_key.get().unwrap().as_bytes(); + let bytes = self.verifying_key.get().unwrap().as_bytes(); let selector_color = bytes[..(bytes.len() / 2)] .iter() .fold(0u8, |acc, b| acc ^ b) as usize @@ -131,7 +131,7 @@ mod imp { } fn hex_color(&self) -> String { - let bytes = self.public_key.get().unwrap().as_bytes(); + let bytes = self.verifying_key.get().unwrap().as_bytes(); let selector_color = bytes[..(bytes.len() / 2)] .iter() .fold(0u8, |acc, b| acc ^ b) as usize @@ -145,24 +145,27 @@ glib::wrapper! { pub struct Author(ObjectSubclass); } impl Author { - pub(crate) fn new(public_key: &PublicKey) -> Self { + pub(crate) fn new(verifying_key: &VerifyingKey) -> Self { glib::Object::builder() - .property("public-key", public_key) + .property("verifying-key", verifying_key) .build() } - pub(crate) fn with_state(public_key: &PublicKey, last_seen: Option<&glib::DateTime>) -> Self { + pub(crate) fn with_state( + verifying_key: &VerifyingKey, + last_seen: Option<&glib::DateTime>, + ) -> Self { glib::Object::builder() - .property("public-key", public_key) + .property("verifying-key", verifying_key) .property("last-seen", last_seen) .build() } pub(crate) fn for_this_device( - public_key: &PublicKey, + verifying_key: &VerifyingKey, last_seen: Option<&glib::DateTime>, ) -> Self { - let obj = Self::with_state(public_key, last_seen); + let obj = Self::with_state(verifying_key, last_seen); obj.imp().is_this_device.set(true); obj @@ -178,7 +181,7 @@ impl Author { self.notify_is_online(); } - pub(crate) fn is_new_cursor_position(&self, timestamp: std::time::SystemTime) -> bool { + pub(crate) fn is_new_cursor_position(&self, timestamp: u64) -> bool { let mut last_cursor_update = self.imp().last_cursor_update.lock().unwrap(); if last_cursor_update.is_none() || timestamp >= last_cursor_update.unwrap() { diff --git a/reflection-doc/src/authors.rs b/reflection-doc/src/authors.rs index 0f58a3be..f0d2df66 100644 --- a/reflection-doc/src/authors.rs +++ b/reflection-doc/src/authors.rs @@ -6,14 +6,14 @@ use glib::subclass::prelude::*; use indexmap::IndexMap; use crate::author::Author; -use crate::identity::PublicKey; +use crate::identity::VerifyingKey; mod imp { use super::*; #[derive(Default)] pub struct Authors { - pub(super) list: RwLock>, + pub(super) list: RwLock>, } #[glib::object_subclass] @@ -70,9 +70,9 @@ impl Authors { assert_eq!(list.len(), 1); for author in authors { - let public_key = author.public_key(); - if !list.contains_key(&public_key) { - list.insert(public_key, author); + let verifying_key = author.verifying_key(); + if !list.contains_key(&verifying_key) { + list.insert(verifying_key, author); } } @@ -80,7 +80,7 @@ impl Authors { self.items_changed(1, 0, authors_len as u32); } - pub(crate) fn add_this_device(&self, author_key: PublicKey) { + pub(crate) fn add_this_device(&self, author_key: VerifyingKey) { let mut list = self.imp().list.write().unwrap(); let now = glib::DateTime::now_utc().ok(); @@ -92,7 +92,7 @@ impl Authors { self.items_changed(0, 0, 1); } - pub(crate) fn add(&self, author_key: PublicKey) -> Author { + pub(crate) fn add(&self, author_key: VerifyingKey) -> Author { let mut list = self.imp().list.write().unwrap(); let entry = list.entry(author_key); let index = entry.index(); @@ -108,7 +108,7 @@ impl Authors { author } - pub(crate) fn author(&self, author_key: &PublicKey) -> Option { + pub(crate) fn author(&self, author_key: &VerifyingKey) -> Option { let list = self.imp().list.read().unwrap(); list.get(author_key).cloned() } diff --git a/reflection-doc/src/document.rs b/reflection-doc/src/document.rs index d7581427..b0f776a4 100644 --- a/reflection-doc/src/document.rs +++ b/reflection-doc/src/document.rs @@ -9,16 +9,13 @@ use glib::{Properties, clone}; pub use hex::FromHexError; use loro::{ExportMode, LoroDoc, LoroText, event::Diff}; use p2panda_core::cbor::{decode_cbor, encode_cbor}; -use reflection_node::p2panda_core; -use reflection_node::topic::{ - SubscribableTopic, Subscription as TopicSubscription, - SubscriptionError as TopicSubscriptionError, -}; +use p2panda_core::{self, Topic}; +use reflection_node::{TopicStream, TopicSubscription, TopicSubscriptionError}; use tracing::error; use crate::author::Author; use crate::authors::Authors; -use crate::identity::PublicKey; +use crate::identity::VerifyingKey; use crate::service::Service; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, glib::Boxed)] @@ -37,6 +34,18 @@ impl From<[u8; 32]> for DocumentId { } } +impl From for DocumentId { + fn from(value: Topic) -> Self { + Self(value.to_bytes()) + } +} + +impl From for Topic { + fn from(value: DocumentId) -> Self { + Topic::from(value.0) + } +} + impl fmt::Display for DocumentId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.to_hex()) @@ -89,11 +98,15 @@ impl DocumentId { } #[derive(Debug, serde::Serialize, serde::Deserialize)] -enum EphemerialData { +#[serde(tag = "t", content = "d")] +enum EphemeralData { + #[serde(rename = "cursor")] Cursor { + #[serde(rename = "i")] insert_cursor: Option, + + #[serde(rename = "s")] selection_bound: Option, - timestamp: std::time::SystemTime, }, } @@ -130,7 +143,7 @@ mod imp { #[property(get, construct_only)] id: OnceCell, #[property(name = "subscribed", get = Self::subscribed, type = bool)] - pub(super) subscription: RwLock>>>, + pub(super) subscription: RwLock>>>, #[property(get = Self::service, set = Self::set_service, construct_only, type = Service)] service: glib::WeakRef, #[property(get)] @@ -282,10 +295,9 @@ mod imp { } pub fn brodcast_ephemeral(&self) { - let cursor_data = EphemerialData::Cursor { + let cursor_data = EphemeralData::Cursor { insert_cursor: self.insert_cursor.read().unwrap().clone(), selection_bound: self.selection_bound.read().unwrap().clone(), - timestamp: std::time::SystemTime::now(), }; let cursor_bytes = match encode_cbor(&cursor_data) { @@ -301,7 +313,7 @@ mod imp { #[weak] subscription, async move { - if let Err(error) = subscription.send_ephemeral(cursor_bytes).await { + if let Err(error) = subscription.publish_ephemeral(cursor_bytes).await { error!("Failed to send cursor position: {}", error); } } @@ -414,7 +426,7 @@ mod imp { } fn setup_loro_document(&self) { - let public_key = self.obj().service().private_key().public_key(); + let verifying_key = self.obj().service().signing_key().verifying_key(); let obj = self.obj(); let doc = LoroDoc::new(); // The peer id represents the identity of the author applying local changes (that's @@ -428,7 +440,7 @@ mod imp { // this should not really be a problem, but it would be nice if the Loro API would // change some day. let mut buf = [0u8; 8]; - buf[..8].copy_from_slice(&public_key.0.as_bytes()[..8]); + buf[..8].copy_from_slice(&verifying_key.0.as_bytes()[..8]); u64::from_be_bytes(buf) }) .expect("set peer id for new document"); @@ -498,7 +510,7 @@ mod imp { subscription, async move { // Broadcast a "text delta" to all peers - if let Err(error) = subscription.send_delta(delta_bytes).await { + if let Err(error) = subscription.publish_delta(delta_bytes).await { error!( "Failed to send delta of document to the network: {}", error @@ -589,12 +601,16 @@ mod imp { self.crdt_doc.set(doc).unwrap(); } - pub(super) fn handle_ephemeral_data(&self, author: Author, data: EphemerialData) { + pub(super) fn handle_ephemeral_data( + &self, + author: Author, + timestamp: u64, + data: EphemeralData, + ) { match data { - EphemerialData::Cursor { + EphemeralData::Cursor { insert_cursor, selection_bound, - timestamp, } => { let doc = self.crdt_doc.get().expect("crdt_doc to be set"); @@ -645,7 +661,7 @@ mod imp { } } - pub(super) fn subscription(&self) -> Option>> { + pub(super) fn subscription(&self) -> Option>> { self.subscription.read().unwrap().clone() } } @@ -688,7 +704,7 @@ mod imp { // Add ourself to the list of authors self.authors - .add_this_device(self.obj().service().private_key().public_key()); + .add_this_device(self.obj().service().signing_key().verifying_key()); } } } @@ -797,7 +813,7 @@ impl Document { } let handle = DocumentHandle(self.downgrade()); - match self.service().node().subscribe(self.id(), handle).await { + match self.service().node().stream(self.id(), handle).await { Ok(subscription) => { self.imp() .subscription @@ -831,7 +847,7 @@ impl Document { .export(ExportMode::Snapshot) .expect("encoded crdt snapshot"); - if let Err(error) = subscription.send_snapshot(snapshot_bytes).await { + if let Err(error) = subscription.publish_snapshot(snapshot_bytes).await { error!( "Failed to send snapshot of document to the network: {}", error @@ -875,7 +891,7 @@ impl Document { .expect("crdt_doc to be set") .export(ExportMode::Snapshot) .expect("encoded crdt snapshot"); - if let Err(error) = subscription.send_snapshot(snapshot_bytes).await { + if let Err(error) = subscription.publish_snapshot(snapshot_bytes).await { error!( "Failed to send snapshot of document to the network: {}", error @@ -899,20 +915,20 @@ unsafe impl Sync for Document {} struct DocumentHandle(glib::WeakRef); -impl SubscribableTopic for DocumentHandle { - fn bytes_received(&self, author: p2panda_core::PublicKey, data: Vec) { +impl TopicSubscription for DocumentHandle { + fn bytes_received(&self, author: p2panda_core::VerifyingKey, data: Vec) { if let Some(document) = self.0.upgrade() { document.main_context().invoke(move || { document.imp().on_remote_message(data); - document.authors().add(PublicKey(author)); + document.authors().add(VerifyingKey(author)); }); } } - fn author_joined(&self, author: p2panda_core::PublicKey) { + fn author_joined(&self, author: p2panda_core::VerifyingKey) { if let Some(document) = self.0.upgrade() { document.main_context().invoke(move || { - let author = document.authors().add(PublicKey(author)); + let author = document.authors().add(VerifyingKey(author)); author.set_online(true); // When a new author joins we need to send ephemeral messages again document.imp().brodcast_ephemeral(); @@ -920,22 +936,29 @@ impl SubscribableTopic for DocumentHandle { } } - fn author_left(&self, author: p2panda_core::PublicKey) { + fn author_left(&self, author: p2panda_core::VerifyingKey) { if let Some(document) = self.0.upgrade() { document.main_context().invoke(move || { - let author = document.authors().add(PublicKey(author)); + let author = document.authors().add(VerifyingKey(author)); author.set_online(false); }); } } - fn ephemeral_bytes_received(&self, author: p2panda_core::PublicKey, data: Vec) { + fn ephemeral_bytes_received( + &self, + author: p2panda_core::VerifyingKey, + timestamp: u64, + data: Vec, + ) { if let Some(document) = self.0.upgrade() { document.main_context().invoke(move || { if let Ok(data) = decode_cbor(&data[..]) - && let Some(author) = document.authors().author(&PublicKey(author)) + && let Some(author) = document.authors().author(&VerifyingKey(author)) { - document.imp().handle_ephemeral_data(author, data); + document + .imp() + .handle_ephemeral_data(author, timestamp, data); } }); } @@ -944,7 +967,7 @@ impl SubscribableTopic for DocumentHandle { fn error(&self, error: TopicSubscriptionError) { if let Some(document) = self.0.upgrade() { document.main_context().invoke(move || { - error!("Network error received for subscribed document: {error}"); + error!("error received for subscribed document: {error}"); }); } } diff --git a/reflection-doc/src/documents.rs b/reflection-doc/src/documents.rs index d9dc6096..dd56a75e 100644 --- a/reflection-doc/src/documents.rs +++ b/reflection-doc/src/documents.rs @@ -5,7 +5,7 @@ use gio::subclass::prelude::ListModelImpl; use glib::subclass::prelude::*; use indexmap::IndexMap; -use crate::identity::PublicKey; +use crate::identity::VerifyingKey; use crate::service::StartupError; use crate::{ author::Author, @@ -68,9 +68,9 @@ impl Documents { } pub(crate) async fn load(&self, service: &Service) -> Result<(), StartupError> { - let public_key = service.private_key().public_key(); + let verifying_key = service.signing_key().verifying_key(); - let documents = service.node().topics::().await?; + let documents = service.node().topics().await?; let mut list = self.imp().list.write().unwrap(); assert!(list.is_empty()); @@ -85,31 +85,34 @@ impl Documents { .authors .iter() .map(|author| { - let author_public_key = PublicKey(author.public_key); - if author_public_key == public_key { + let author_verifying_key = VerifyingKey(author.verifying_key); + if author_verifying_key == verifying_key { let last_seen = author.last_seen.and_then(|last_seen| { glib::DateTime::from_unix_utc(last_seen.timestamp()).ok() }); - Author::for_this_device(&PublicKey(author.public_key), last_seen.as_ref()) + Author::for_this_device( + &VerifyingKey(author.verifying_key), + last_seen.as_ref(), + ) } else { let last_seen = author.last_seen.and_then(|last_seen| { glib::DateTime::from_unix_utc(last_seen.timestamp()).ok() }); - Author::with_state(&author_public_key, last_seen.as_ref()) + Author::with_state(&author_verifying_key, last_seen.as_ref()) } }) .collect(); let obj = Document::with_state( service, - Some(&document.id), + Some(&document.topic.into()), document.name.as_deref(), last_accessed.as_ref(), ); obj.authors().load(authors); - list.insert(document.id, obj); + list.insert(document.topic.into(), obj); } drop(list); diff --git a/reflection-doc/src/lib.rs b/reflection-doc/src/lib.rs index 8fabd3ac..96537894 100644 --- a/reflection-doc/src/lib.rs +++ b/reflection-doc/src/lib.rs @@ -5,29 +5,29 @@ pub mod documents; pub mod service; pub mod identity { + use std::fmt; use std::hash::Hash; - use reflection_node::p2panda_core; - pub use reflection_node::p2panda_core::identity::IdentityError; - use std::fmt; + use p2panda_core; + pub use p2panda_core::identity::IdentityError; #[derive(Clone, Debug, glib::Boxed)] - #[boxed_type(name = "ReflectionPrivateKey", nullable)] - pub struct PrivateKey(pub(crate) p2panda_core::PrivateKey); + #[boxed_type(name = "ReflectionSigningKey", nullable)] + pub struct SigningKey(pub(crate) p2panda_core::SigningKey); - impl Default for PrivateKey { + impl Default for SigningKey { fn default() -> Self { - Self::new() + Self::generate() } } - impl PrivateKey { - pub fn new() -> PrivateKey { - PrivateKey(p2panda_core::PrivateKey::new()) + impl SigningKey { + pub fn generate() -> SigningKey { + SigningKey(p2panda_core::SigningKey::generate()) } - pub fn public_key(&self) -> PublicKey { - PublicKey(self.0.public_key()) + pub fn verifying_key(&self) -> VerifyingKey { + VerifyingKey(self.0.verifying_key()) } pub fn as_bytes(&self) -> &[u8] { @@ -35,43 +35,43 @@ pub mod identity { } } - impl TryFrom<&[u8]> for PrivateKey { + impl TryFrom<&[u8]> for SigningKey { type Error = p2panda_core::IdentityError; fn try_from(value: &[u8]) -> Result { - Ok(PrivateKey(p2panda_core::PrivateKey::try_from(value)?)) + Ok(SigningKey(p2panda_core::SigningKey::try_from(value)?)) } } - impl<'a> From<&'a PrivateKey> for &'a [u8] { - fn from(value: &PrivateKey) -> &[u8] { + impl<'a> From<&'a SigningKey> for &'a [u8] { + fn from(value: &SigningKey) -> &[u8] { value.0.as_bytes().as_slice() } } - impl fmt::Display for PrivateKey { + impl fmt::Display for SigningKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } #[derive(Clone, Debug, PartialEq, Hash, Eq, glib::Boxed)] - #[boxed_type(name = "ReflectionPublicKey", nullable)] - pub struct PublicKey(pub(crate) p2panda_core::PublicKey); + #[boxed_type(name = "ReflectionVerifyingKey", nullable)] + pub struct VerifyingKey(pub(crate) p2panda_core::VerifyingKey); - impl fmt::Display for PublicKey { + impl fmt::Display for VerifyingKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } - impl<'a> From<&'a PublicKey> for &'a [u8] { - fn from(value: &PublicKey) -> &[u8] { + impl<'a> From<&'a VerifyingKey> for &'a [u8] { + fn from(value: &VerifyingKey) -> &[u8] { value.0.as_bytes().as_slice() } } - impl PublicKey { + impl VerifyingKey { pub fn as_bytes(&self) -> &[u8] { self.0.as_bytes().as_slice() } @@ -81,7 +81,7 @@ pub mod identity { #[cfg(test)] mod tests { use crate::document::DocumentId; - use crate::identity::PrivateKey; + use crate::identity::SigningKey; use crate::service::Service; #[test_log::test(glib::async_test)] @@ -90,8 +90,8 @@ mod tests { let context = glib::MainContext::ref_thread_default(); - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); + let signing_key = SigningKey::generate(); + let service = Service::new(&signing_key, None); service.startup().await.unwrap(); let document = service.join_document_with_main_context(&DocumentId::new(), &context); @@ -109,16 +109,16 @@ mod tests { let context = glib::MainContext::ref_thread_default(); - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); + let signing_key = SigningKey::generate(); + let service = Service::new(&signing_key, None); service.startup().await.unwrap(); let document = service.join_document_with_main_context(&DocumentId::new(), &context); document.subscribe().await; let id = document.id(); - let private_key2 = PrivateKey::new(); - let service2 = Service::new(&private_key2, None); + let signing_key2 = SigningKey::generate(); + let service2 = Service::new(&signing_key2, None); service2.startup().await.unwrap(); let document2 = service2.join_document_with_main_context(&id, &context); @@ -150,16 +150,16 @@ mod tests { let context = glib::MainContext::ref_thread_default(); - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); + let signing_key = SigningKey::generate(); + let service = Service::new(&signing_key, None); service.startup().await.unwrap(); let document = service.join_document_with_main_context(&DocumentId::new(), &context); document.subscribe().await; let id = document.id(); - let private_key2 = PrivateKey::new(); - let service2 = Service::new(&private_key2, None); + let signing_key2 = SigningKey::generate(); + let service2 = Service::new(&signing_key2, None); service2.startup().await.unwrap(); let document2 = service2.join_document_with_main_context(&id, &context); @@ -201,8 +201,8 @@ mod tests { let context = glib::MainContext::ref_thread_default(); - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); + let signing_key = SigningKey::generate(); + let service = Service::new(&signing_key, None); service.startup().await.unwrap(); let document = service.join_document_with_main_context(&DocumentId::new(), &context); @@ -210,8 +210,8 @@ mod tests { document.subscribe().await; - let private_key2 = PrivateKey::new(); - let service2 = Service::new(&private_key2, None); + let signing_key2 = SigningKey::generate(); + let service2 = Service::new(&signing_key2, None); service2.startup().await.unwrap(); let document2 = service2.join_document_with_main_context(&id, &context); diff --git a/reflection-doc/src/service.rs b/reflection-doc/src/service.rs index 6b737f3b..cc188308 100644 --- a/reflection-doc/src/service.rs +++ b/reflection-doc/src/service.rs @@ -1,29 +1,28 @@ +use std::sync::{Mutex, OnceLock}; + use gio::prelude::{FileExt, ListModelExtManual, NetworkMonitorExt}; use glib::object::ObjectExt; use glib::subclass::prelude::*; use glib::{Properties, clone}; -use reflection_node::p2panda_core::Hash; -use std::sync::{Mutex, OnceLock}; +use p2panda_core::Hash; +use reflection_node::TopicStreamError; +use reflection_node::{Node, NodeError}; use thiserror::Error; use tracing::error; -use crate::identity::PrivateKey; -use crate::{ - document::{Document, DocumentId}, - documents::Documents, -}; -use reflection_node::{ - node, - node::{Node, NodeError}, - topic::TopicError, -}; +use crate::document::{Document, DocumentId}; +use crate::documents::Documents; +use crate::identity::SigningKey; + +static NETWORK_NAME: &[u8] = b"reflection-v2"; #[derive(Error, Debug)] pub enum StartupError { #[error(transparent)] Node(#[from] NodeError), + #[error(transparent)] - Topic(#[from] TopicError), + TopicStream(#[from] TopicStreamError), } #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum, Default)] @@ -36,12 +35,12 @@ pub enum ConnectionMode { Network, } -impl From for node::ConnectionMode { +impl From for reflection_node::ConnectionMode { fn from(value: ConnectionMode) -> Self { match value { - ConnectionMode::None => node::ConnectionMode::None, - ConnectionMode::Bluetooth => node::ConnectionMode::Bluetooth, - ConnectionMode::Network => node::ConnectionMode::Network, + ConnectionMode::None => reflection_node::ConnectionMode::None, + ConnectionMode::Bluetooth => reflection_node::ConnectionMode::Bluetooth, + ConnectionMode::Network => reflection_node::ConnectionMode::Network, } } } @@ -53,8 +52,8 @@ mod imp { #[properties(wrapper_type = super::Service)] pub struct Service { pub node: OnceLock, - #[property(get, set, construct_only, type = PrivateKey)] - pub private_key: OnceLock, + #[property(get, set, construct_only, type = SigningKey)] + pub signing_key: OnceLock, #[property(get, set, construct_only, nullable, type = Option)] pub data_dir: OnceLock>, #[property(get)] @@ -88,9 +87,9 @@ mod imp { monitor.is_network_available() }; let connection_mode = (*self.connection_mode.lock().unwrap()).into(); - let wants_network = connection_mode == node::ConnectionMode::Network; + let wants_network = connection_mode == reflection_node::ConnectionMode::Network; let real_connection_mode = if !network_available && wants_network { - node::ConnectionMode::None + reflection_node::ConnectionMode::None } else { connection_mode }; @@ -135,9 +134,9 @@ glib::wrapper! { } impl Service { - pub fn new(private_key: &PrivateKey, data_dir: Option<&gio::File>) -> Self { + pub fn new(signing_key: &SigningKey, data_dir: Option<&gio::File>) -> Self { glib::Object::builder() - .property("private-key", private_key) + .property("signing-key", signing_key) .property("data-dir", data_dir) .build() } @@ -171,10 +170,10 @@ impl Service { } pub async fn startup(&self) -> Result<(), StartupError> { - let private_key = self.private_key().0; - let network_id = Hash::new(b"reflection"); + let signing_key = self.signing_key().0; + let network_id = Hash::digest(NETWORK_NAME); let path = self.data_dir().and_then(|data_dir| data_dir.path()); - let node = Node::new(private_key, network_id, path.as_deref()).await?; + let node = Node::new(signing_key, network_id, path.as_deref()).await?; self.imp() .node diff --git a/reflection-node/Cargo.toml b/reflection-node/Cargo.toml index 6d05e9a6..486a8640 100644 --- a/reflection-node/Cargo.toml +++ b/reflection-node/Cargo.toml @@ -12,21 +12,15 @@ authors = [ test_utils = [] [dependencies] -thiserror = "2.0.18" chrono = "0.4.43" -ciborium = "0.2.2" -p2panda-core = { git = "https://github.com/p2panda/p2panda", rev = "a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" } -p2panda-discovery = { git = "https://github.com/p2panda/p2panda", rev = "a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" } -p2panda-net = { git = "https://github.com/p2panda/p2panda", rev = "a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" } -p2panda-store = { git = "https://github.com/p2panda/p2panda", rev = "a1308c116c1bd345f2cd6af7df41d2a6e0a4a682", features = ["sqlite"], default-features = false } -p2panda-stream = { git = "https://github.com/p2panda/p2panda", rev = "a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" } -p2panda-sync = { git = "https://github.com/p2panda/p2panda", rev = "a1308c116c1bd345f2cd6af7df41d2a6e0a4a682" } +p2panda = "0.6.1" +p2panda-store = "0.6.1" serde = { version = "1.0.228", features = ["derive"] } -serde_bytes = "0.11.19" sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite", "chrono"], default-features = false } -tokio = { version = "1.49.0", features = ["rt", "sync"] } +thiserror = "2.0.18" +tokio = { version = "1.52.3", features = ["rt", "sync"] } tokio-stream = "0.1.18" tracing = "0.1" + +[dev-dependencies] test-log = { version = "0.2.19", default-features = false, features = ["trace", "color"] } -hex = "0.4.3" -rand_chacha = "0.10.0" diff --git a/reflection-node/migrations/20250418140035_create_tables.sql b/reflection-node/migrations/20250418140035_create_tables.sql index 186eb4c8..fd8b8127 100644 --- a/reflection-node/migrations/20250418140035_create_tables.sql +++ b/reflection-node/migrations/20250418140035_create_tables.sql @@ -1,13 +1,14 @@ CREATE TABLE IF NOT EXISTS authors ( - public_key TEXT NOT NULL, - topic_id TEXT NOT NULL, - last_seen INTEGER, - UNIQUE(public_key, topic_id), - FOREIGN KEY(topic_id) REFERENCES topics(id) ON DELETE CASCADE + public_key TEXT NOT NULL, + topic_id TEXT NOT NULL, + last_seen INTEGER, + + UNIQUE(public_key, topic_id), + FOREIGN KEY(topic_id) REFERENCES topics(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS topics ( - id TEXT NOT NULL PRIMARY KEY, - name TEXT, - last_accessed INTEGER -); \ No newline at end of file + id TEXT NOT NULL PRIMARY KEY, + name TEXT, + last_accessed INTEGER +); diff --git a/reflection-node/migrations/20260521094840_rename_public_key_to_verifying_key.sql b/reflection-node/migrations/20260521094840_rename_public_key_to_verifying_key.sql new file mode 100644 index 00000000..60a372ee --- /dev/null +++ b/reflection-node/migrations/20260521094840_rename_public_key_to_verifying_key.sql @@ -0,0 +1,2 @@ +ALTER TABLE authors +RENAME COLUMN public_key to verifying_key; diff --git a/reflection-node/src/author_tracker.rs b/reflection-node/src/author_tracker.rs index f665f2b1..6109a247 100644 --- a/reflection-node/src/author_tracker.rs +++ b/reflection-node/src/author_tracker.rs @@ -1,56 +1,47 @@ use std::collections::HashMap; use std::ops::DerefMut; use std::sync::Arc; -use std::time::{Duration, Instant, SystemTime}; +use std::time::{Duration, Instant}; -use crate::ephemerial_operation::EphemerialOperation; -use crate::node_inner::MessageType; -use crate::node_inner::NodeInner; -use crate::topic::SubscribableTopic; use chrono::Utc; -use p2panda_core::cbor::{DecodeError, decode_cbor, encode_cbor}; -use p2panda_core::{PrivateKey, PublicKey}; -use p2panda_net::gossip::GossipHandle; +use p2panda::VerifyingKey; +use p2panda::streams::EphemeralStreamPublisher; +use serde::{Deserialize, Serialize}; use tokio::sync::{Mutex, RwLock}; -use tracing::error; +use tracing::{error, warn}; + +use crate::ephemeral_message::EphemeralMessage; +use crate::node::NodeInner; +use crate::traits::TopicSubscription; const OFFLINE_TIMEOUT: Duration = Duration::from_secs(60); -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub enum AuthorMessage { +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum AuthorTrackerMessage { Hello, Ping, Bye, } -impl std::fmt::Display for AuthorMessage { +impl std::fmt::Display for AuthorTrackerMessage { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - AuthorMessage::Hello => write!(f, "Hello message"), - AuthorMessage::Ping => write!(f, "Ping message"), - AuthorMessage::Bye => write!(f, "Bye message"), + AuthorTrackerMessage::Hello => write!(f, "Hello message"), + AuthorTrackerMessage::Ping => write!(f, "Ping message"), + AuthorTrackerMessage::Bye => write!(f, "Bye message"), } } } -impl TryFrom<&[u8]> for AuthorMessage { - type Error = DecodeError; - - fn try_from(value: &[u8]) -> Result { - let (res, _): (AuthorMessage, SystemTime) = decode_cbor(value)?; - - Ok(res) - } -} - pub struct AuthorTracker { - last_ping: Mutex>, + last_ping: Mutex>, subscribable_topic: Arc, node: Arc, - tx: RwLock>, + tx: RwLock>>, } -impl AuthorTracker { +impl AuthorTracker { pub fn new(node: Arc, subscribable_topic: Arc) -> Arc { Arc::new(Self { last_ping: Mutex::new(HashMap::new()), @@ -60,21 +51,23 @@ impl AuthorTracker { }) } - pub async fn set_topic_tx(&self, tx: Option) { + pub async fn set_topic_tx(&self, tx: Option>) { let mut tx_guard = self.tx.write().await; + // Send good bye message to the network if let Some(tx) = tx_guard.as_ref() { - send_message(&self.node.private_key, tx, AuthorMessage::Bye).await; + send_message(tx, AuthorTrackerMessage::Bye).await; } - // Set all authors that the tracker has seen to offline, authors the tracker hasn't seen are already offline + // Set all authors that the tracker has seen to offline, authors the tracker hasn't seen + // are already offline let old_authors = std::mem::take(self.last_ping.lock().await.deref_mut()); for author in old_authors.into_keys() { self.subscribable_topic.author_left(author); self.set_last_seen(author).await; } - let this_author = self.node.private_key.public_key(); + let this_author = self.node.verifying_key; if tx.is_some() { self.subscribable_topic.author_joined(this_author); } else { @@ -85,37 +78,37 @@ impl AuthorTracker { *tx_guard = tx; } - pub async fn received(&self, message: AuthorMessage, author: PublicKey) { + pub async fn received(&self, author: VerifyingKey, message: AuthorTrackerMessage) { match message { - AuthorMessage::Hello => { + AuthorTrackerMessage::Hello => { self.join(author).await; } - AuthorMessage::Ping => { + AuthorTrackerMessage::Ping => { self.ping(author).await; } - AuthorMessage::Bye => { + AuthorTrackerMessage::Bye => { self.left(author).await; } } } - async fn send(&self, message: AuthorMessage) { + async fn send(&self, message: AuthorTrackerMessage) { if let Some(tx) = self.tx.read().await.as_ref() { - send_message(&self.node.private_key, tx, message).await; + send_message(tx, message).await; } } - async fn join(&self, author: PublicKey) { + async fn join(&self, author: VerifyingKey) { self.last_ping.lock().await.insert(author, Instant::now()); self.subscribable_topic.author_joined(author); self.set_last_seen(author).await; // Send a ping to the network to ensure that the new author knows we exist // Normally we send a ping every `OFFLINE_TIMEOUT / 2` - self.send(AuthorMessage::Ping).await; + self.send(AuthorTrackerMessage::Ping).await; } - async fn ping(&self, author: PublicKey) { + async fn ping(&self, author: VerifyingKey) { let old = self.last_ping.lock().await.insert(author, Instant::now()); // If this is a new author emit author join @@ -125,7 +118,7 @@ impl AuthorTracker { self.set_last_seen(author).await; } - async fn left(&self, author: PublicKey) { + async fn left(&self, author: VerifyingKey) { self.last_ping.lock().await.remove(&author); self.subscribable_topic.author_left(author); self.set_last_seen(author).await; @@ -133,7 +126,7 @@ impl AuthorTracker { pub async fn spawn(&self) { // Send a hello to the network so other authors know we joined the topic - self.send(AuthorMessage::Hello).await; + self.send(AuthorTrackerMessage::Hello).await; let mut interval = tokio::time::interval(OFFLINE_TIMEOUT / 2); interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); @@ -144,7 +137,7 @@ impl AuthorTracker { interval.tick().await; // Send a ping to the network so that we won't be marked as offline - self.send(AuthorMessage::Ping).await; + self.send(AuthorTrackerMessage::Ping).await; let mut expired = Vec::new(); self.last_ping.lock().await.retain(|author, instant| { if instant.elapsed() > OFFLINE_TIMEOUT { @@ -162,7 +155,7 @@ impl AuthorTracker { } } - async fn set_last_seen(&self, author: PublicKey) { + async fn set_last_seen(&self, author: VerifyingKey) { if let Err(error) = self .node .topic_store @@ -174,25 +167,11 @@ impl AuthorTracker { } } -async fn send_message(private_key: &PrivateKey, tx: &GossipHandle, message: AuthorMessage) { - // FIXME: We need to add the current time to the message, - // because iroh doesn't broadcast twice the same message message. - let author_message = match encode_cbor(&(&message, SystemTime::now())) { - Ok(result) => result, - Err(error) => { - error!("Failed to encode {message} as CBOR: {error}"); - return; - } - }; - let operation = EphemerialOperation::new(author_message, private_key); - let bytes = match encode_cbor(&MessageType::AuthorEphemeral(operation)) { - Ok(result) => result, - Err(error) => { - error!("Failed to encode {message} as CBOR: {error}"); - return; - } - }; - if let Err(error) = tx.publish(bytes).await { - error!("Failed to sent {message} to the network: {error}"); +async fn send_message( + tx: &EphemeralStreamPublisher, + message: AuthorTrackerMessage, +) { + if let Err(err) = tx.publish(EphemeralMessage::AuthorTracker(message)).await { + warn!("error occurred when sending ephemeral message: {err}"); } } diff --git a/reflection-node/src/database.rs b/reflection-node/src/database.rs new file mode 100644 index 00000000..ef55390a --- /dev/null +++ b/reflection-node/src/database.rs @@ -0,0 +1,73 @@ +use std::path::PathBuf; +use std::pin::Pin; + +use p2panda_store::sqlite::migrations as p2panda_migrations; +use sqlx::error::BoxDynError; +use sqlx::migrate::{Migration, MigrationSource, Migrator}; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; +use tracing::info; + +/// Establishes a SQLite connection pool. +/// +/// If no database path is given the database is created in memory. +pub async fn database_pool(db_file: Option) -> Result { + let connection_options = SqliteConnectOptions::new() + .shared_cache(true) + .create_if_missing(true); + + let pool = if let Some(db_file) = db_file { + info!("database file location: {db_file:?}"); + let connection_options = connection_options.filename(db_file); + SqlitePool::connect_with(connection_options).await? + } else { + let connection_options = connection_options.in_memory(true); + // FIXME: we need to set max connection to 1 for in memory sqlite DB. Probably has to + // do something with this issue: https://github.com/launchbadge/sqlx/issues/2510 + let pool_options = SqlitePoolOptions::new().max_connections(1); + pool_options.connect_with(connection_options).await? + }; + + Ok(pool) +} + +/// Run migration for p2panda and for the our TopicStore. +pub async fn run_migrations(pool: &SqlitePool) -> Result<(), sqlx::migrate::MigrateError> { + Migrator::new(CombinedMigrationSource::new(vec![ + p2panda_migrations(), + sqlx::migrate!(), + ])) + .await? + .run(pool) + .await?; + + Ok(()) +} + +type BoxFuture<'a, T> = Pin + Send + 'a>>; + +/// Combine multiple `sqlx::migrate::Migrator` into a single `sqlx::migrate::MigrationSource` +/// +/// See for more details: https://github.com/launchbadge/sqlx/discussions/3407 +#[derive(Debug)] +pub struct CombinedMigrationSource { + migrators: Vec, +} + +impl CombinedMigrationSource { + pub fn new(migrators: Vec) -> CombinedMigrationSource { + Self { migrators } + } +} + +impl<'s> MigrationSource<'s> for CombinedMigrationSource { + fn resolve(self) -> BoxFuture<'s, Result, BoxDynError>> { + Box::pin(async move { + Ok(self + .migrators + .iter() + .flat_map(|migrator| migrator.iter()) + .cloned() + .collect()) + }) + } +} diff --git a/reflection-node/src/ephemeral_message.rs b/reflection-node/src/ephemeral_message.rs new file mode 100644 index 00000000..ed32603d --- /dev/null +++ b/reflection-node/src/ephemeral_message.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; + +use crate::author_tracker::AuthorTrackerMessage; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "t", content = "c")] +pub(crate) enum EphemeralMessage { + /// Custom message to be forwarded to the application-layer. + #[serde(rename = "app")] + Application(Vec), + + /// Message used to track online status of authors. + #[serde(rename = "author_tracker")] + AuthorTracker(AuthorTrackerMessage), +} diff --git a/reflection-node/src/ephemerial_operation.rs b/reflection-node/src/ephemerial_operation.rs deleted file mode 100644 index 1fd6aa3f..00000000 --- a/reflection-node/src/ephemerial_operation.rs +++ /dev/null @@ -1,34 +0,0 @@ -use p2panda_core::identity::{PrivateKey, PublicKey, Signature}; - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct EphemerialOperation { - #[serde(with = "serde_bytes")] - body: Vec, - author: PublicKey, - signature: Signature, -} - -impl EphemerialOperation { - pub fn new(body: Vec, author: &PrivateKey) -> Self { - Self { - signature: author.sign(&body), - body, - author: author.public_key(), - } - } - - /// Validates the signature and unpacks the operation - pub fn validate_and_unpack(self) -> Option<(PublicKey, Vec)> { - let EphemerialOperation { - body, - author, - signature, - } = self; - - if self.author.verify(&body[..], &signature) { - Some((author, body)) - } else { - None - } - } -} diff --git a/reflection-node/src/lib.rs b/reflection-node/src/lib.rs index d1cddf8a..84dd7705 100644 --- a/reflection-node/src/lib.rs +++ b/reflection-node/src/lib.rs @@ -1,58 +1,44 @@ +//! Peer-to-peer, local-first networking and sync "backend" of Reflection based on p2panda. +//! +//! Some features implemented in `reflection-node` add functionality on top of p2panda: +//! +//! - Author Presence: Indicate which authors have contributed to which topic and if they're +//! currently online. +//! - Persisted Topics: Store to persist all previously used topics. mod author_tracker; -mod ephemerial_operation; -mod network; -pub mod node; -mod node_inner; -mod operation; -mod operation_store; -mod subscription_inner; -pub mod topic; +mod database; +mod ephemeral_message; +mod node; mod topic_store; -mod utils; +mod topic_stream; +mod traits; -pub use p2panda_core; -pub use topic::SubscribableTopic; +#[doc(hidden)] // FIXME: We're currently not supporting this feature. +pub use node::ConnectionMode; +pub use node::{Node, NodeError, TrackedAuthor, TrackedTopic}; +pub use topic_stream::{PublishError, TopicStream, TopicStreamError}; +pub use traits::{TopicSubscription, TopicSubscriptionError}; #[cfg(test)] mod tests { use std::sync::Arc; - use p2panda_core::Hash; - use p2panda_core::PrivateKey; - use p2panda_core::PublicKey; + use p2panda::{Hash, SigningKey, Topic, VerifyingKey}; use tokio::sync::{Mutex, mpsc}; - use crate::node::ConnectionMode; - use crate::node::Node; - use crate::topic::SubscribableTopic; - - #[tokio::test] - #[test_log::test] - async fn create_topic() { - let private_key = PrivateKey::new(); - let network_id = Hash::new(b"reflection"); - let node = Node::new(private_key, network_id, None).await.unwrap(); - - let id: [u8; 32] = [0; 32]; - let _sub = node.subscribe(id, TestTopic::new()).await; - let topics = node.topics::<[u8; 32]>().await.unwrap(); - - assert_eq!(topics.len(), 1); - assert_eq!(topics.first().unwrap().id, id); - - node.shutdown().await.unwrap(); - } + use crate::node::{ConnectionMode, Node}; + use crate::traits::{TopicSubscription, TopicSubscriptionError}; #[derive(Clone)] - struct TestTopic { + struct TestDocument { tx: mpsc::UnboundedSender>, rx: Arc>>>, } - impl TestTopic { + impl TestDocument { fn new() -> Self { let (tx, rx) = mpsc::unbounded_channel::>(); - TestTopic { + TestDocument { tx, rx: Arc::new(Mutex::new(rx)), } @@ -63,55 +49,70 @@ mod tests { } } - impl SubscribableTopic for TestTopic { - fn bytes_received(&self, _author: PublicKey, data: Vec) { + impl TopicSubscription for TestDocument { + fn bytes_received(&self, _author: VerifyingKey, data: Vec) { self.tx.send(data).unwrap(); } - fn author_joined(&self, _author: PublicKey) {} - fn author_left(&self, _author: PublicKey) {} - fn ephemeral_bytes_received(&self, _author: PublicKey, _data: Vec) {} - fn error(&self, _error: crate::topic::SubscriptionError) {} + fn author_joined(&self, _author: VerifyingKey) {} + fn author_left(&self, _author: VerifyingKey) {} + fn ephemeral_bytes_received(&self, _author: VerifyingKey, _timestamp: u64, _data: Vec) { + } + fn error(&self, _error: TopicSubscriptionError) {} + } + + #[tokio::test] + #[test_log::test] + async fn create_topic() { + let signing_key = SigningKey::generate(); + let network_id = Hash::digest(b"reflection"); + let node = Node::new(signing_key, network_id, None).await.unwrap(); + + let id: [u8; 32] = [0; 32]; + let _sub = node.stream(id, TestDocument::new()).await; + let topics = node.topics().await.unwrap(); + + assert_eq!(topics.len(), 1); + assert_eq!(topics.first().unwrap().topic, id.into()); + + node.shutdown().await.unwrap(); } #[tokio::test] #[test_log::test] async fn subscribe_topic() { - let private_key = PrivateKey::new(); - let network_id = Hash::new(b"reflection"); - let node = Node::new(private_key, network_id, None).await.unwrap(); + let network_id = Hash::digest(b"reflection"); + let topic_id: Topic = [1; 32].into(); + + let node = Node::new(SigningKey::generate(), network_id, None) + .await + .unwrap(); node.set_connection_mode(ConnectionMode::Network) .await .unwrap(); - let test_topic = TestTopic::new(); + let test_topic = TestDocument::new(); - let id: [u8; 32] = [0; 32]; - let subscription = node.subscribe(id, test_topic).await.unwrap(); + let subscription = node.stream(topic_id, test_topic).await.unwrap(); - let topics = node.topics::<[u8; 32]>().await.unwrap(); - assert_eq!(topics.len(), 1); - assert_eq!(topics.first().unwrap().id, id); - - let private_key2 = PrivateKey::new(); - let network_id2 = Hash::new(b"reflection"); - let node2 = Node::new(private_key2, network_id2, None).await.unwrap(); + let node2 = Node::new(SigningKey::generate(), network_id, None) + .await + .unwrap(); node2 .set_connection_mode(ConnectionMode::Network) .await .unwrap(); - let test_topic2 = TestTopic::new(); + let test_topic2 = TestDocument::new(); - let _subscription2 = node2.subscribe(id, test_topic2.clone()).await.unwrap(); + let _subscription2 = node2.stream(topic_id, test_topic2.clone()).await.unwrap(); - let topics2 = node2.topics::<[u8; 32]>().await.unwrap(); - assert_eq!(topics2.len(), 1); - assert_eq!(topics2.first().unwrap().id, id); + // TODO: Need to sleep here to make sure tx already exists. + tokio::time::sleep(std::time::Duration::from_secs(3)).await; let test_snapshot = "test".as_bytes().to_vec(); subscription - .send_snapshot(test_snapshot.clone()) + .publish_snapshot(test_snapshot.clone()) .await .unwrap(); diff --git a/reflection-node/src/network.rs b/reflection-node/src/network.rs deleted file mode 100644 index c9aee3f2..00000000 --- a/reflection-node/src/network.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::sync::LazyLock; - -use p2panda_net::Discovery; -use p2panda_net::discovery::DiscoveryError; -use thiserror::Error; -use tracing::error; - -use p2panda_core::Hash; -use p2panda_core::PrivateKey; -use p2panda_net::address_book::{AddressBook, AddressBookError}; -use p2panda_net::addrs::NodeInfo; -use p2panda_net::gossip::{Gossip, GossipError}; -use p2panda_net::iroh_endpoint::{Endpoint, EndpointAddr, EndpointError, RelayUrl}; -use p2panda_net::iroh_mdns::{MdnsDiscovery, MdnsDiscoveryError, MdnsDiscoveryMode}; - -use crate::operation::ReflectionExtensions; -use crate::operation_store::OperationStore; -use crate::topic_store::{LogId, TopicStore}; - -static RELAY_URL: LazyLock = LazyLock::new(|| { - "https://euc1-1.relay.n0.iroh-canary.iroh.link" - .parse() - .expect("valid relay URL") -}); - -static BOOTSTRAP_NODE: LazyLock = LazyLock::new(|| { - let endpoint_addr = EndpointAddr::new( - "9f63a15ab95959a992af96bf72fbc3e7dc98eeb4799f788bb07b20125053e795" - .parse() - .expect("valid bootstrap node id"), - ) - .with_relay_url(RELAY_URL.clone()); - NodeInfo::from(endpoint_addr).bootstrap() -}); - -pub type LogSync = p2panda_net::sync::LogSync< - p2panda_store::SqliteStore, - LogId, - ReflectionExtensions, - TopicStore, ->; -pub type LogSyncError = p2panda_net::sync::LogSyncError; - -#[derive(Error, Debug)] -pub enum NetworkError { - #[error(transparent)] - Gossip(#[from] GossipError), - #[error(transparent)] - LogSync(#[from] LogSyncError), - #[error(transparent)] - AddressBook(#[from] AddressBookError), - #[error(transparent)] - MdnsDiscovery(#[from] MdnsDiscoveryError), - #[error(transparent)] - Discovery(#[from] DiscoveryError), - #[error(transparent)] - Endpoint(#[from] EndpointError), -} - -#[allow(dead_code)] -pub struct Network { - pub(crate) mdns_discovery: MdnsDiscovery, - pub(crate) discovery: Discovery, - pub(crate) gossip: Gossip, - pub(crate) log_sync: LogSync, - pub(crate) endpoint: Endpoint, -} - -// FIXME: Endpoint, LogSync, MdnsDiscovery, and Gossip should implement debug -impl std::fmt::Debug for Network { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Network").finish() - } -} - -impl Network { - pub async fn new( - private_key: &PrivateKey, - network_id: &Hash, - topic_store: &TopicStore, - operation_store: &OperationStore, - ) -> Result { - let address_book = AddressBook::builder().spawn().await?; - - if cfg!(not(any(test, feature = "test_utils"))) - && let Err(error) = address_book.insert_node_info(BOOTSTRAP_NODE.clone()).await - { - error!("Failed to add bootstrap node to the address book: {error}"); - } - - let mut builder = Endpoint::builder(address_book.clone()) - .network_id(network_id.into()) - .private_key(private_key.clone()); - - if cfg!(not(any(test, feature = "test_utils"))) { - builder = builder.relay_url(RELAY_URL.clone()); - } - - let endpoint = builder.spawn().await?; - - let mdns_discovery = MdnsDiscovery::builder(address_book.clone(), endpoint.clone()) - .mode(MdnsDiscoveryMode::Active) - .spawn() - .await?; - - let discovery = Discovery::builder(address_book.clone(), endpoint.clone()) - .spawn() - .await?; - - let gossip = Gossip::builder(address_book.clone(), endpoint.clone()) - .spawn() - .await?; - - let log_sync = LogSync::builder( - operation_store.clone_inner(), - topic_store.clone(), - endpoint.clone(), - gossip.clone(), - ) - .spawn() - .await?; - - Ok(Network { - mdns_discovery, - discovery, - gossip, - log_sync, - endpoint, - }) - } -} diff --git a/reflection-node/src/node.rs b/reflection-node/src/node.rs index d1b27025..b31c9ceb 100644 --- a/reflection-node/src/node.rs +++ b/reflection-node/src/node.rs @@ -1,30 +1,49 @@ -use std::path::Path; -use std::sync::Arc; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, LazyLock}; use chrono::{DateTime, Utc}; -use p2panda_core::{Hash, PrivateKey}; -use p2panda_net::TopicId; +use p2panda::node::SpawnError; +use p2panda::{NetworkId, RelayUrl, SigningKey, VerifyingKey}; use thiserror::Error; +use tokio::sync::{Notify, RwLock}; use tracing::info; -use crate::network::NetworkError; -use crate::node_inner::NodeInner; -use crate::topic::{SubscribableTopic, Subscription, TopicError}; -pub use crate::topic_store::Author; -use crate::topic_store::StoreTopic; +use crate::database::{database_pool, run_migrations}; +pub use crate::topic_store::TrackedAuthor; +use crate::topic_store::{TopicRow, TrackedTopicStore}; +use crate::topic_stream::{TopicStream, TopicStreamError, TopicStreamInner}; +use crate::traits::TopicSubscription; + +static DATABASE_FILE: &str = "database-v2.sqlite"; + +static RELAY_URL: LazyLock = LazyLock::new(|| { + "https://euc1-1.relay.n0.iroh-canary.iroh.link" + .parse() + .expect("valid relay URL") +}); + +static BOOTSTRAP_NODE_ID: LazyLock = LazyLock::new(|| { + "9f63a15ab95959a992af96bf72fbc3e7dc98eeb4799f788bb07b20125053e795" + .parse() + .expect("valid bootstrap node id") +}); #[derive(Debug, Error)] pub enum NodeError { #[error(transparent)] RuntimeStartup(#[from] std::io::Error), + #[error(transparent)] RuntimeSpawn(#[from] tokio::task::JoinError), + #[error(transparent)] - Datebase(#[from] sqlx::Error), + Database(#[from] sqlx::Error), + #[error(transparent)] - DatebaseMigration(#[from] sqlx::migrate::MigrateError), + DatabaseMigration(#[from] sqlx::migrate::MigrateError), + #[error(transparent)] - Network(#[from] NetworkError), + NodeSpawn(#[from] SpawnError), } #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] @@ -36,11 +55,11 @@ pub enum ConnectionMode { } #[derive(Clone, Debug)] -pub struct Topic { - pub id: ID, +pub struct TrackedTopic { + pub topic: p2panda::Topic, pub name: Option, pub last_accessed: Option>, - pub authors: Vec, + pub authors: Vec, } #[derive(Debug)] @@ -68,8 +87,8 @@ pub struct Node { impl Node { pub async fn new( - private_key: PrivateKey, - network_id: Hash, + signing_key: SigningKey, + network_id: impl Into, db_location: Option<&Path>, ) -> Result { let runtime = if let Ok(handle) = tokio::runtime::Handle::try_current() { @@ -82,10 +101,14 @@ impl Node { ) }; - let db_file = db_location.map(|location| location.join("database.sqlite")); - let inner = runtime - .spawn(async move { NodeInner::new(network_id, private_key, db_file).await }) - .await??; + let inner = { + let network_id = network_id.into(); + let db_file = db_location.map(|location| location.join(DATABASE_FILE)); + + runtime + .spawn(NodeInner::new(signing_key, network_id, db_file)) + .await?? + }; Ok(Self { inner: Arc::new(inner), @@ -97,43 +120,43 @@ impl Node { &self, connection_mode: ConnectionMode, ) -> Result<(), NodeError> { - let inner_clone = self.inner.clone(); + let inner = self.inner.clone(); self.runtime - .spawn(async move { inner_clone.set_connection_mode(connection_mode).await }) + .spawn(async move { inner.set_connection_mode(connection_mode).await }) .await??; Ok(()) } pub async fn shutdown(&self) -> Result<(), NodeError> { - let inner_clone = self.inner.clone(); + let inner = self.inner.clone(); self.runtime .spawn(async move { - inner_clone.shutdown().await; + inner.shutdown().await; }) .await?; Ok(()) } - pub async fn topics>(&self) -> Result>, TopicError> { - let inner_clone = self.inner.clone(); + pub async fn topics(&self) -> Result, TopicStreamError> { + let inner = self.inner.clone(); let topics = self .runtime - .spawn(async move { inner_clone.topic_store.topics().await }) + .spawn(async move { inner.topic_store.topics().await }) .await??; let topics = topics .into_iter() .map(|topic| { - let StoreTopic { + let TopicRow { id, name, last_accessed, authors, } = topic; - Topic { - id: id.into(), + TrackedTopic { + topic: id, name, last_accessed, authors, @@ -144,30 +167,127 @@ impl Node { Ok(topics) } - pub async fn subscribe, T: SubscribableTopic + 'static>( + pub async fn stream( &self, - id: ID, - topic_handle: T, - ) -> Result, TopicError> { - let id: TopicId = id.into(); - let topic_handle = Arc::new(topic_handle); - let inner_clone = self.inner.clone(); + topic: impl Into, + subscription: T, + ) -> Result, TopicStreamError> + where + T: TopicSubscription + 'static, + { + let topic = topic.into(); + let subscription = Arc::new(subscription); + let inner = self.inner.clone(); let inner_subscription = self .runtime - .spawn(async move { inner_clone.subscribe(id, topic_handle).await }) + .spawn(async move { inner.stream(topic, subscription).await }) .await??; - let subscription = Subscription::new(self.runtime.clone(), inner_subscription).await; - info!("Subscribed to topic {}", hex::encode(id)); + let subscription = TopicStream::new(self.runtime.clone(), inner_subscription).await; + info!(%topic, "subscribed to topic"); Ok(subscription) } - pub async fn delete_topic>(&self, id: ID) -> Result<(), TopicError> { - let id: TopicId = id.into(); - let inner_clone = self.inner.clone(); + pub async fn delete_topic( + &self, + topic: impl Into, + ) -> Result<(), TopicStreamError> { + let topic = topic.into(); + let inner = self.inner.clone(); self.runtime - .spawn(async move { inner_clone.delete_topic(id).await }) + .spawn(async move { inner.delete_topic(topic).await }) .await? } } + +#[derive(Debug)] +pub(crate) struct NodeInner { + pub(crate) network: RwLock, + pub(crate) shutdown_notifier: Notify, + pub(crate) topic_store: TrackedTopicStore, + pub(crate) verifying_key: VerifyingKey, +} + +impl NodeInner { + pub async fn new( + signing_key: SigningKey, + network_id: impl Into, + db_file: Option, + ) -> Result { + let verifying_key = signing_key.verifying_key(); + + let pool = database_pool(db_file).await?; + run_migrations(&pool).await?; + + let topic_store = TrackedTopicStore::from_pool(pool.clone()); + + let mut builder = p2panda::Node::builder() + .network_id(network_id.into()) + .signing_key(signing_key) + .database_pool(pool); + + // Don't connect to any servers during testing. + if cfg!(not(any(test, feature = "test_utils"))) { + builder = builder + .bootstrap(*BOOTSTRAP_NODE_ID, RELAY_URL.clone()) + .relay_url(RELAY_URL.clone()); + } + + let node = builder.spawn().await?; + + Ok(Self { + network: RwLock::new(node), + shutdown_notifier: Notify::new(), + topic_store, + verifying_key, + }) + } + + pub async fn set_connection_mode( + &self, + _connection_mode: ConnectionMode, + ) -> Result<(), NodeError> { + // TODO: This is a no-op currently and requires work in `p2panda-net` upstream. + // See related issue: https://github.com/p2panda/p2panda/issues/1093 + Ok(()) + } + + pub async fn shutdown(&self) { + // Wake up all subscriptions that may still exist. + self.shutdown_notifier.notify_waiters(); + } + + pub async fn stream( + self: Arc, + topic: impl Into, + subscribable_topic: Arc, + ) -> Result, TopicStreamError> + where + T: TopicSubscription + 'static, + { + let topic = topic.into(); + + self.topic_store.add_topic(&topic).await?; + + // Add ourselves as an author to the topic store. + self.topic_store + .add_author(&topic, &self.verifying_key) + .await?; + + Ok(TopicStreamInner::new( + self.clone(), + topic, + subscribable_topic, + )) + } + + pub async fn delete_topic( + self: Arc, + topic: impl Into, + ) -> Result<(), TopicStreamError> { + let topic = topic.into(); + self.topic_store.delete_topic(&topic).await?; + Ok(()) + } +} diff --git a/reflection-node/src/node_inner.rs b/reflection-node/src/node_inner.rs deleted file mode 100644 index ba951f82..00000000 --- a/reflection-node/src/node_inner.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::path::PathBuf; -use std::sync::Arc; - -use crate::ephemerial_operation::EphemerialOperation; -use crate::network::{Network, NetworkError}; -use crate::node::{ConnectionMode, NodeError}; -use crate::operation_store::OperationStore; -use crate::subscription_inner::SubscriptionInner; -use crate::topic::{SubscribableTopic, TopicError}; -use crate::topic_store::TopicStore; -use crate::utils::CombinedMigrationSource; - -use p2panda_core::{Hash, PrivateKey}; -use p2panda_net::TopicId; -use p2panda_store::sqlite::store::migrations as operation_store_migrations; -use sqlx::{migrate::Migrator, sqlite}; -use tokio::sync::{Notify, RwLock}; -use tracing::info; - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub(crate) enum MessageType { - Ephemeral(EphemerialOperation), - AuthorEphemeral(EphemerialOperation), -} - -#[derive(Debug)] -pub struct NodeInner { - pub(crate) operation_store: OperationStore, - pub(crate) topic_store: TopicStore, - pub(crate) private_key: PrivateKey, - pub(crate) network_id: Hash, - pub(crate) network: RwLock>, - pub(crate) network_notifier: Notify, -} - -impl NodeInner { - pub async fn new( - network_id: Hash, - private_key: PrivateKey, - db_file: Option, - ) -> Result { - let connection_options = sqlx::sqlite::SqliteConnectOptions::new() - .shared_cache(true) - .create_if_missing(true); - let pool = if let Some(db_file) = db_file { - info!("Database file location: {db_file:?}"); - let connection_options = connection_options.filename(db_file); - sqlx::sqlite::SqlitePool::connect_with(connection_options).await? - } else { - let connection_options = connection_options.in_memory(true); - // FIXME: we need to set max connection to 1 for in memory sqlite DB. - // Probably has to do something with this issue: https://github.com/launchbadge/sqlx/issues/2510 - let pool_options = sqlite::SqlitePoolOptions::new().max_connections(1); - pool_options.connect_with(connection_options).await? - }; - - // Run migration for p2panda OperationStore and for the our TopicStore - Migrator::new(CombinedMigrationSource::new(vec![ - operation_store_migrations(), - sqlx::migrate!(), - ])) - .await? - .run(&pool) - .await?; - - let operation_store = OperationStore::new(pool.clone()); - let topic_store = TopicStore::new(pool); - - Ok(Self { - operation_store, - topic_store, - private_key, - network_id, - network: RwLock::new(None), - network_notifier: Notify::new(), - }) - } - - pub async fn set_connection_mode( - &self, - connection_mode: ConnectionMode, - ) -> Result<(), NetworkError> { - // Subscriptions will tear down the network subscription and drop the read lock, - // so that we can acquire the write lock and then shutdown the network. - self.network_notifier.notify_waiters(); - - let mut network_guard = self.network.write().await; - - match connection_mode { - ConnectionMode::None => { - *network_guard = None; - } - ConnectionMode::Bluetooth => { - unimplemented!("Bluetooth is currently not implemented") - } - ConnectionMode::Network => { - let network = Network::new( - &self.private_key, - &self.network_id, - &self.topic_store, - &self.operation_store, - ) - .await?; - - *network_guard = Some(network); - } - } - - Ok(()) - } - - pub async fn shutdown(&self) { - // Wake up all subscriptions that may still exist - self.network_notifier.notify_waiters(); - self.network.write().await.take(); - } - - pub async fn subscribe( - self: Arc, - id: TopicId, - subscribable_topic: Arc, - ) -> Result, TopicError> { - self.topic_store.add_topic(&id).await?; - // Add ourselves as an author to the topic store. - self.topic_store - .add_author(&id, &self.private_key.public_key()) - .await?; - let stored_operations = self - .topic_store - .operations_for_topic(&self.operation_store, &id) - .await?; - - for operation in stored_operations { - // Send all stored operation bytes to the app, - // it doesn't matter if the app already knows some or all of them - if let Some(body) = operation.body { - subscribable_topic.bytes_received(operation.header.public_key, body.to_bytes()); - } - } - - Ok(SubscriptionInner::new(self.clone(), id, subscribable_topic)) - } - - pub async fn delete_topic(self: Arc, id: TopicId) -> Result<(), TopicError> { - self.topic_store.delete_topic(&id).await?; - Ok(()) - } -} diff --git a/reflection-node/src/operation.rs b/reflection-node/src/operation.rs deleted file mode 100644 index beb5c99b..00000000 --- a/reflection-node/src/operation.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::hash::Hash as StdHash; - -use p2panda_core::{Extension, Header, PruneFlag}; -use p2panda_net::TopicId; -use serde::{Deserialize, Serialize}; - -use crate::topic_store::LogId; - -/// Custom extensions for p2panda header. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ReflectionExtensions { - /// If flag is true we can remove all previous operations in this log. - /// - /// This usually indicates that a "snapshot" has been inserted into the body of this operation, - /// containing all required state to reconstruct the full version including all previous edits - /// of this topic. - /// - /// In our case of a text-editor, this would be the encoded payload of a state-based CRDT. - #[serde( - rename = "p", - skip_serializing_if = "PruneFlag::is_not_set", - default = "PruneFlag::default" - )] - pub prune_flag: PruneFlag, - - /// Operations can be organised in separate logs. With a "log id" we can declare where this - /// operation belongs to. - /// - /// We organise two logs per author per topic, one for "short lived" / ephemeral deltas - /// (small text changes) and one for persisted snapshots (full topic history). These are two - /// distinct "log types". - #[serde(rename = "t")] - pub log_type: LogType, - - /// Identifier of the topic this operation relates to. - #[serde(rename = "d")] - pub topic: TopicId, -} - -#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, StdHash, Serialize, Deserialize)] -pub enum LogType { - Snapshot, - #[default] - Delta, -} - -impl Extension for ReflectionExtensions { - fn extract(header: &Header) -> Option { - Some(header.extensions.prune_flag.clone()) - } -} - -impl Extension for ReflectionExtensions { - fn extract(header: &Header) -> Option { - Some(header.extensions.log_type) - } -} - -impl Extension for ReflectionExtensions { - fn extract(header: &Header) -> Option { - Some(header.extensions.topic) - } -} - -impl Extension for ReflectionExtensions { - fn extract(header: &Header) -> Option { - let log_type: LogType = header.extension()?; - let id: TopicId = header.extension()?; - - Some(LogId::new(log_type, &id)) - } -} diff --git a/reflection-node/src/operation_store.rs b/reflection-node/src/operation_store.rs deleted file mode 100644 index 44b72191..00000000 --- a/reflection-node/src/operation_store.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::sync::Arc; -use std::time::{SystemTime, SystemTimeError}; - -use crate::topic_store::LogId; -use p2panda_core::{Body, Header, Operation, PrivateKey, PruneFlag}; -use p2panda_net::TopicId; -use p2panda_store::{ - LogStore, OperationStore as TraitOperationStore, SqliteStore, SqliteStoreError, -}; -use thiserror::Error; -use tokio::sync::Semaphore; - -use crate::operation::{LogType, ReflectionExtensions}; - -#[derive(Debug, Error)] -pub enum CreationError { - #[error(transparent)] - SytemTime(#[from] SystemTimeError), - #[error(transparent)] - Store(#[from] SqliteStoreError), -} - -#[derive(Debug)] -pub struct OperationStore { - inner: SqliteStore, - // FIXME: This makes sure we only create one operation at the time and not in parallel - // Since we would mess up the sequence of operations - semaphore_operation_store: Arc, -} - -impl OperationStore { - pub fn new(pool: sqlx::SqlitePool) -> Self { - Self { - inner: SqliteStore::new(pool), - semaphore_operation_store: Arc::new(Semaphore::new(1)), - } - } - - pub fn clone_inner(&self) -> SqliteStore { - self.inner.clone() - } - - pub fn inner(&self) -> &SqliteStore { - &self.inner - } - - /// Creates, signs and stores new operation in the author's append-only log. - /// - /// If no topic is specified we create a new operation in a new log. The resulting hash of the - /// header can be used to identify that new topic. - pub async fn create_operation( - &self, - private_key: &PrivateKey, - log_type: LogType, - topic: TopicId, - body: Option<&[u8]>, - prune_flag: bool, - ) -> Result, CreationError> { - let _permit = self - .semaphore_operation_store - .acquire() - .await - .expect("OperationStore semaphore not to be closed"); - - let body = body.map(Body::new); - let public_key = private_key.public_key(); - - let log_id = LogId::new(log_type, &topic); - let latest_operation = self.inner.latest_operation(&public_key, &log_id).await?; - - let (seq_num, backlink) = match latest_operation { - Some((header, _)) => (header.seq_num + 1, Some(header.hash())), - None => (0, None), - }; - - let timestamp = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH)? - .as_secs(); - - let extensions = ReflectionExtensions { - prune_flag: PruneFlag::new(prune_flag), - log_type, - topic, - }; - - let mut header = Header { - version: 1, - public_key, - signature: None, - payload_size: body.as_ref().map_or(0, |body| body.size()), - payload_hash: body.as_ref().map(|body| body.hash()), - timestamp, - seq_num, - backlink, - previous: vec![], - extensions, - }; - header.sign(private_key); - - let operation = Operation { - hash: header.hash(), - header, - body, - }; - - let mut inner_clone = self.clone_inner(); - inner_clone - .insert_operation( - operation.hash, - &operation.header, - operation.body.as_ref(), - operation.header.to_bytes().as_slice(), - &log_id, - ) - .await?; - - if prune_flag { - inner_clone - .delete_operations( - &operation.header.public_key, - &log_id, - operation.header.seq_num, - ) - .await?; - } - - Ok(operation) - } -} diff --git a/reflection-node/src/persistent_operation.rs b/reflection-node/src/persistent_operation.rs deleted file mode 100644 index dd90a215..00000000 --- a/reflection-node/src/persistent_operation.rs +++ /dev/null @@ -1,64 +0,0 @@ -use p2panda_core::{ - Body, Header, Operation, - cbor::{DecodeError, decode_cbor}, -}; -use thiserror::Error; - -use crate::topic::TopicId; -use crate::operation::ReflectionExtensions; - -type OperationWithRawHeader = (Header, Option, Vec); - -#[derive(Debug, Error)] -pub enum UnpackError { - #[error(transparent)] - Cbor(#[from] DecodeError), - #[error("Operation with invalid topic id")] - InvalidTopicId, -} - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct PersistentOperation { - #[serde(with = "serde_bytes")] - header: Vec, - body: Option, -} - -impl PersistentOperation { - pub fn new(operation: Operation) -> Self { - Self { - header: operation.header.to_bytes(), - body: operation.body.map(|body| body.to_bytes().into()), - } - } - - pub fn from_serialized(header: Vec, body: Option>) -> Self { - Self { - header, - body: body.map(Into::into), - } - } - - /// Validates and unpacks the operation - pub fn validate_and_unpack( - self, - id: TopicId, - ) -> Result { - let PersistentOperation { header, body } = self; - - // The header is serialized by Header::to_bytes() as cbor - let header_deserialized: Header = decode_cbor(&header[..])?; - let body_deserialized = body.map(|body| Body::from(body.into_vec())); - - let Some(operation_id): Option = header_deserialized.extension() - else { - return Err(UnpackError::InvalidTopicId); - }; - - if operation_id != id { - return Err(UnpackError::InvalidTopicId); - } - - Ok((header_deserialized, body_deserialized, header)) - } -} diff --git a/reflection-node/src/subscription_inner.rs b/reflection-node/src/subscription_inner.rs deleted file mode 100644 index 931f2c7a..00000000 --- a/reflection-node/src/subscription_inner.rs +++ /dev/null @@ -1,469 +0,0 @@ -use std::mem::take; -use std::ops::{Deref, DerefMut, Drop}; -use std::sync::Arc; - -use chrono::Utc; -use p2panda_core::Operation; -use p2panda_core::{ - Body, Header, - cbor::{decode_cbor, encode_cbor}, -}; -use p2panda_net::{TopicId, gossip::GossipHandle}; -use p2panda_stream::IngestExt; -use p2panda_sync::protocols::TopicLogSyncEvent as Event; -use tokio::{ - sync::{RwLock, mpsc}, - task::{AbortHandle, spawn}, -}; -use tokio_stream::{StreamExt, wrappers::ReceiverStream}; -use tracing::{error, info, warn}; - -use crate::author_tracker::{AuthorMessage, AuthorTracker}; -use crate::ephemerial_operation::EphemerialOperation; -use crate::network::Network; -use crate::node_inner::MessageType; -use crate::node_inner::NodeInner; -use crate::operation::{LogType, ReflectionExtensions}; -use crate::topic::{SubscribableTopic, SubscriptionError, TopicError}; - -pub type SyncHandle = - p2panda_net::sync::SyncHandle, Event>; - -pub struct SubscriptionInner { - ephemeral_tx: RwLock>, - tx: RwLock>, - pub(crate) node: Arc, - pub(crate) id: TopicId, - pub(crate) subscribable_topic: Arc, - author_tracker: Arc>, - abort_handles: RwLock>, -} - -impl Drop for SubscriptionInner { - fn drop(&mut self) { - for handle in self.abort_handles.get_mut() { - handle.abort(); - } - } -} - -impl SubscriptionInner { - pub fn new(node: Arc, id: TopicId, subscribable_topic: Arc) -> Self { - let author_tracker = AuthorTracker::new(node.clone(), subscribable_topic.clone()); - SubscriptionInner { - tx: RwLock::new(None), - ephemeral_tx: RwLock::new(None), - node, - id, - abort_handles: RwLock::new(Vec::new()), - subscribable_topic, - author_tracker, - } - } - - pub async fn spawn_network_monitor(&self) { - // We need to hold a read lock to the network, so that the network won't be dropped - // or shutdown. - let mut notify = Some(self.node.network_notifier.notified()); - let mut network_guard = Some(self.node.network.read().await); - - let (tx, ephemeral_tx, abort_handles) = - if let Some(network) = network_guard.as_ref().unwrap().deref() { - match setup_network( - &self.node, - network, - self.id, - &self.subscribable_topic, - &self.author_tracker, - ) - .await - { - Ok((sync_handle, gossip_handle, abort_handles)) => { - (Some(sync_handle), Some(gossip_handle), abort_handles) - } - Err(error) => { - self.subscribable_topic.error(error); - (None, None, Vec::new()) - } - } - } else { - (None, None, Vec::new()) - }; - - *self.tx.write().await = tx; - *self.ephemeral_tx.write().await = ephemeral_tx; - *self.abort_handles.write().await = abort_handles; - - loop { - if let Some(notify) = notify { - notify.await; - } - - let mut abort_handles_guard = self.abort_handles.write().await; - let mut tx_guard = self.tx.write().await; - let mut ephemeral_tx_guard = self.ephemeral_tx.write().await; - - let old_tx = take(tx_guard.deref_mut()); - let old_ephemeral_tx = take(ephemeral_tx_guard.deref_mut()); - let old_abort_handles = take(abort_handles_guard.deref_mut()); - - teardown_network( - &self.id, - &self.author_tracker, - old_tx, - old_ephemeral_tx, - old_abort_handles, - ) - .await; - // Release network lock and get a new one, so that the network can be change between them - network_guard.take(); - notify = Some(self.node.network_notifier.notified()); - network_guard = Some(self.node.network.read().await); - - let (tx, ephemeral_tx, abort_handles) = - if let Some(network) = network_guard.as_ref().unwrap().deref() { - match setup_network( - &self.node, - network, - self.id, - &self.subscribable_topic, - &self.author_tracker, - ) - .await - { - Ok((sync_handle, gossip_handle, abort_handles)) => { - (Some(sync_handle), Some(gossip_handle), abort_handles) - } - Err(error) => { - self.subscribable_topic.error(error); - (None, None, Vec::new()) - } - } - } else { - (None, None, Vec::new()) - }; - - *tx_guard = tx; - *ephemeral_tx_guard = ephemeral_tx; - *abort_handles_guard = abort_handles; - } - } - - pub async fn unsubscribe(&self) -> Result<(), TopicError> { - let mut tx_guard = self.tx.write().await; - let mut ephemeral_tx_guard = self.ephemeral_tx.write().await; - let mut abort_handles_guard = self.abort_handles.write().await; - - let tx = take(tx_guard.deref_mut()); - let ephemeral_tx = take(ephemeral_tx_guard.deref_mut()); - let abort_handles = take(abort_handles_guard.deref_mut()); - - self.node - .topic_store - .set_last_accessed_for_topic(&self.id, Some(Utc::now())) - .await?; - - teardown_network( - &self.id, - &self.author_tracker, - tx, - ephemeral_tx, - abort_handles, - ) - .await; - - Ok(()) - } - - pub async fn send_delta(&self, data: Vec) -> Result<(), TopicError> { - let operation = - // Append one operation to our "ephemeral" delta log. - self.node.operation_store - .create_operation( - &self.node.private_key, - LogType::Delta, - self.id, - Some(&data), - false, - ) - .await?; - - info!( - "Delta operation sent for topic with id {}", - hex::encode(self.id) - ); - - if let Some(tx) = self.tx.read().await.as_ref() { - tx.publish(operation).await?; - } - - Ok(()) - } - - pub async fn send_snapshot(&self, data: Vec) -> Result<(), TopicError> { - // Append an operation to our "snapshot" log and set the prune flag to - // true. This will remove previous snapshots. - // - // Snapshots are not broadcasted on the gossip overlay as they would be - // too large. Peers will sync them up when they join the topic. - self.node - .operation_store - .create_operation( - &self.node.private_key, - LogType::Snapshot, - self.id, - Some(&data), - true, - ) - .await?; - - // Append an operation to our "ephemeral" delta log and set the prune - // flag to true. - // - // This signals removing all previous "delta" operations now. This is - // some sort of garbage collection whenever we snapshot. Snapshots - // already contain all history, there is no need to keep duplicate - // "delta" data around. - let operation = self - .node - .operation_store - .create_operation(&self.node.private_key, LogType::Delta, self.id, None, true) - .await?; - - info!("Snapshot saved for topic with id {}", hex::encode(self.id)); - - if let Some(tx) = self.tx.read().await.as_ref() { - tx.publish(operation).await?; - } - - Ok(()) - } - - pub async fn send_ephemeral(&self, data: Vec) -> Result<(), TopicError> { - if let Some(ephemeral_tx) = self.ephemeral_tx.read().await.as_ref() { - let operation = EphemerialOperation::new(data, &self.node.private_key); - let bytes = encode_cbor(&MessageType::Ephemeral(operation))?; - ephemeral_tx.publish(bytes).await?; - } - - Ok(()) - } - - /// Set the name for a given topic - /// - /// This information will be written to the database - pub async fn set_name(&self, name: Option) -> Result<(), TopicError> { - self.node - .topic_store - .set_name_for_topic(&self.id, name) - .await?; - - Ok(()) - } -} - -async fn setup_network( - node: &Arc, - network: &Network, - id: TopicId, - subscribable_topic: &Arc, - author_tracker: &Arc>, -) -> Result<(SyncHandle, GossipHandle, Vec), SubscriptionError> { - let mut abort_handles = Vec::with_capacity(3); - - let stream = network.log_sync.stream(id, true).await?; - let mut topic_rx = stream.subscribe().await?; - let topic_tx = stream; - - let (persistent_tx, persistent_rx) = - mpsc::channel::<(Header, Option, Vec)>(128); - - let abort_handle = spawn(async move { - while let Some(event) = topic_rx.next().await { - let event = match event { - Ok(event) => event, - Err(error) => { - error!("Error while receiving sync message: {error}"); - continue; - } - }; - match event.event() { - Event::Operation(operation) => { - match validate_and_unpack(operation.as_ref().to_owned(), id) { - Ok(data) => { - persistent_tx.send(data).await.unwrap(); - } - Err(err) => { - error!("Failed to unpack operation: {err}"); - } - } - } - _ => { - // TODO: Handle sync events - } - } - } - }) - .abort_handle(); - - abort_handles.push(abort_handle); - - let ephemeral_stream = network.gossip.stream(id).await?; - let mut ephemeral_rx = ephemeral_stream.subscribe(); - let ephemeral_tx = ephemeral_stream; - - author_tracker.set_topic_tx(Some(ephemeral_tx)).await; - - let author_tracker_clone = author_tracker.clone(); - let subscribable_topic_clone = subscribable_topic.clone(); - let abort_handle = spawn(async move { - while let Some(bytes) = ephemeral_rx.next().await { - let bytes = match bytes { - Ok(bytes) => bytes, - Err(error) => { - error!("Error while receiving ephemeral message: {error}"); - continue; - } - }; - match decode_cbor(&bytes[..]) { - Ok(MessageType::Ephemeral(operation)) => { - if let Some((author, body)) = operation.validate_and_unpack() { - subscribable_topic_clone.ephemeral_bytes_received(author, body); - } else { - warn!("Got ephemeral operation with a bad signature"); - } - } - Ok(MessageType::AuthorEphemeral(operation)) => { - if let Some((author, body)) = operation.validate_and_unpack() { - match AuthorMessage::try_from(&body[..]) { - Ok(message) => { - author_tracker_clone.received(message, author).await; - } - Err(error) => { - warn!("Failed to deserialize AuthorMessage: {error}"); - } - } - } else { - warn!("Got internal ephemeral operation with a bad signature"); - } - } - Err(err) => { - error!("Failed to decode gossip message: {err}"); - } - } - } - }) - .abort_handle(); - - abort_handles.push(abort_handle); - - let stream = ReceiverStream::new(persistent_rx); - - // Ingest does multiple things for us: - // - // - Validate operation- and log integrity and authenticity - // - De-duplicate already known operations - // - Out-of-order buffering - // - Pruning when flag is set - // - Persist operation in store - let mut stream = stream - // NOTE(adz): The persisting part should happen later, we want to check the payload on - // application layer first. In general "ingest" does too much at once and is - // inflexible. Related issue: https://github.com/p2panda/p2panda/issues/696 - .ingest(node.operation_store.clone_inner(), 128) - .filter_map(|result| match result { - Ok(operation) => Some(operation), - Err(err) => { - error!("ingesting operation failed: {err}"); - None - } - }); - - let node = node.clone(); - let subscribable_topic_clone = subscribable_topic.clone(); - // Send checked and ingested operations for this topic to application layer. - let abort_handle = spawn(async move { - while let Some(operation) = stream.next().await { - // When we discover a new author we need to add them to our topic store. - if let Err(error) = node - .topic_store - .add_author(&id, &operation.header.public_key) - .await - { - error!("Can't store author to database: {error}"); - } - - // Forward the payload up to the app. - if let Some(body) = operation.body { - subscribable_topic_clone - .bytes_received(operation.header.public_key, body.to_bytes()); - } - } - }) - .abort_handle(); - - abort_handles.push(abort_handle); - let author_tracker_clone = author_tracker.clone(); - let abort_handle = spawn(async move { - author_tracker_clone.spawn().await; - }) - .abort_handle(); - - abort_handles.push(abort_handle); - - info!("Network subscription set up for topic {}", hex::encode(id)); - - let ephemeral_tx = network.gossip.stream(id).await?; - - Ok((topic_tx, ephemeral_tx, abort_handles)) -} - -async fn teardown_network( - id: &TopicId, - author_tracker: &Arc>, - tx: Option, - ephemeral_tx: Option, - abort_handles: Vec, -) { - for handle in abort_handles { - handle.abort(); - } - - author_tracker.set_topic_tx(None).await; - - if tx.is_some() { - info!( - "Network subscription torn down for topic {}", - hex::encode(id) - ); - } - drop(tx); - drop(ephemeral_tx); -} - -type OperationWithRawHeader = (Header, Option, Vec); - -#[derive(Debug, thiserror::Error)] -pub enum UnpackError { - #[error(transparent)] - Cbor(#[from] p2panda_core::cbor::DecodeError), - #[error("Operation with invalid topic id")] - InvalidTopicId, -} - -fn validate_and_unpack( - operation: p2panda_core::Operation, - id: TopicId, -) -> Result { - let p2panda_core::Operation:: { header, body, .. } = operation; - - let Some(operation_id): Option = header.extension() else { - return Err(UnpackError::InvalidTopicId); - }; - - if operation_id != id { - return Err(UnpackError::InvalidTopicId); - } - - Ok((header.clone(), body, header.to_bytes())) -} diff --git a/reflection-node/src/topic.rs b/reflection-node/src/topic.rs deleted file mode 100644 index 20a6a4f4..00000000 --- a/reflection-node/src/topic.rs +++ /dev/null @@ -1,130 +0,0 @@ -use std::sync::Arc; - -use crate::operation::ReflectionExtensions; -use crate::operation_store::CreationError; - -use crate::network::LogSyncError; -use crate::subscription_inner::SubscriptionInner; -use p2panda_core::{Operation, PublicKey}; -use p2panda_sync::protocols::TopicLogSyncEvent; - -use p2panda_net::gossip::GossipError; -use thiserror::Error; -use tokio::sync::mpsc; -use tokio::task::{AbortHandle, JoinError}; -use tracing::info; - -pub type SyncHandleError = p2panda_net::sync::SyncHandleError< - Operation, - TopicLogSyncEvent, ->; - -#[derive(Debug, Error)] -pub enum TopicError { - #[error(transparent)] - TopicStore(#[from] sqlx::Error), - #[error(transparent)] - OperationStore(#[from] CreationError), - #[error(transparent)] - Encode(#[from] p2panda_core::cbor::EncodeError), - #[error(transparent)] - Publish(#[from] SyncHandleError), - #[error(transparent)] - PublishEphemeral(#[from] mpsc::error::SendError>), - #[error(transparent)] - Runtime(#[from] JoinError), -} - -#[derive(Debug, Error)] -pub enum SubscriptionError { - #[error(transparent)] - Gossip(#[from] GossipError), - #[error(transparent)] - LogSync(#[from] LogSyncError), - #[error(transparent)] - SyncHandle(#[from] SyncHandleError), -} - -pub trait SubscribableTopic: Sync + Send { - fn bytes_received(&self, author: PublicKey, data: Vec); - fn author_joined(&self, author: PublicKey); - fn author_left(&self, author: PublicKey); - fn ephemeral_bytes_received(&self, author: PublicKey, data: Vec); - fn error(&self, error: SubscriptionError); -} - -pub struct Subscription { - pub(crate) inner: Arc>, - pub(crate) runtime: tokio::runtime::Handle, - network_monitor_task: AbortHandle, -} - -impl Drop for Subscription { - fn drop(&mut self) { - self.network_monitor_task.abort(); - } -} - -impl Subscription { - pub(crate) async fn new(runtime: tokio::runtime::Handle, inner: SubscriptionInner) -> Self { - let inner = Arc::new(inner); - - let inner_clone = inner.clone(); - let network_monitor_task = runtime - .spawn(async move { - inner_clone.spawn_network_monitor().await; - }) - .abort_handle(); - - Subscription { - inner, - runtime, - network_monitor_task, - } - } - - pub async fn send_delta(&self, data: Vec) -> Result<(), TopicError> { - let inner = self.inner.clone(); - self.runtime - .spawn(async move { inner.send_delta(data).await }) - .await? - } - - pub async fn send_snapshot(&self, data: Vec) -> Result<(), TopicError> { - let inner = self.inner.clone(); - self.runtime - .spawn(async move { inner.send_snapshot(data).await }) - .await? - } - - pub async fn send_ephemeral(&self, data: Vec) -> Result<(), TopicError> { - let inner = self.inner.clone(); - self.runtime - .spawn(async move { inner.send_ephemeral(data).await }) - .await? - } - - pub async fn unsubscribe(self) -> Result<(), TopicError> { - let id = self.inner.id; - - self.network_monitor_task.abort(); - let inner = self.inner.clone(); - self.runtime - .spawn(async move { inner.unsubscribe().await }) - .await??; - - info!("Unsubscribed from topic {}", hex::encode(id)); - - Ok(()) - } - - /// Set the name for a given topic - /// - /// This information will be written to the database - pub async fn set_name(&self, name: Option) -> Result<(), TopicError> { - let inner = self.inner.clone(); - self.runtime - .spawn(async move { inner.set_name(name).await }) - .await? - } -} diff --git a/reflection-node/src/topic_store.rs b/reflection-node/src/topic_store.rs index b757abf1..dd0f2e3b 100644 --- a/reflection-node/src/topic_store.rs +++ b/reflection-node/src/topic_store.rs @@ -1,79 +1,58 @@ use std::collections::HashMap; -use std::hash::Hash as StdHash; use chrono::{DateTime, Utc}; -use p2panda_core::PublicKey; -use p2panda_net::TopicId; -use p2panda_store::LogStore; -use p2panda_sync::protocols::Logs; -use p2panda_sync::traits::TopicMap; -use serde::{Deserialize, Serialize}; +use p2panda::{Topic, VerifyingKey}; use sqlx::{FromRow, Row}; -use tracing::error; - -use crate::operation::{LogType, ReflectionExtensions}; -use crate::operation_store::OperationStore; #[derive(Debug, FromRow)] -pub struct StoreTopic { +pub struct TopicRow { #[sqlx(try_from = "Vec")] - pub id: TopicId, + pub id: Topic, #[sqlx(default)] pub name: Option, pub last_accessed: Option>, #[sqlx(skip)] - pub authors: Vec, + pub authors: Vec, } #[derive(Debug, Clone)] -pub struct Author { - pub public_key: PublicKey, +pub struct TrackedAuthor { + pub verifying_key: VerifyingKey, pub last_seen: Option>, } #[derive(Clone, Debug)] -pub struct TopicStore { +pub struct TrackedTopicStore { pool: sqlx::SqlitePool, } -impl TopicStore { - pub fn new(pool: sqlx::SqlitePool) -> Self { +impl TrackedTopicStore { + pub fn from_pool(pool: sqlx::SqlitePool) -> Self { Self { pool } } - async fn authors(&self, id: &TopicId) -> sqlx::Result> { - let list = sqlx::query("SELECT public_key FROM authors WHERE topic_id = ?") - .bind(id.as_slice()) - .fetch_all(&self.pool) - .await?; - - Ok(list - .iter() - .filter_map(|row| PublicKey::try_from(row.get::<&[u8], _>("public_key")).ok()) - .collect()) - } - - pub async fn topics(&self) -> sqlx::Result> { - let mut topics: Vec = + pub async fn topics(&self) -> sqlx::Result> { + let mut topics: Vec = sqlx::query_as("SELECT id, name, last_accessed FROM topics") .fetch_all(&self.pool) .await?; - let authors = sqlx::query("SELECT public_key, topic_id, last_seen FROM authors") + let authors = sqlx::query("SELECT verifying_key, topic_id, last_seen FROM authors") .fetch_all(&self.pool) .await?; let mut authors_per_topic = authors.iter().fold(HashMap::new(), |mut acc, row| { - let Ok(id) = TopicId::try_from(row.get::<&[u8], _>("topic_id")) else { + let Ok(id) = Topic::try_from(row.get::<&[u8], _>("topic_id")) else { return acc; }; - let Ok(public_key) = PublicKey::try_from(row.get::<&[u8], _>("public_key")) else { + let Ok(verifying_key) = VerifyingKey::try_from(row.get::<&[u8], _>("verifying_key")) + else { return acc; }; let Ok(last_seen) = row.try_get::>, _>("last_seen") else { return acc; }; - acc.entry(id).or_insert_with(Vec::new).push(Author { - public_key, + acc.entry(id).or_insert_with(Vec::new).push(TrackedAuthor { + verifying_key, last_seen, }); acc @@ -88,40 +67,45 @@ impl TopicStore { Ok(topics) } - pub async fn add_topic(&self, id: &TopicId) -> sqlx::Result<()> { - // The id is the primary key in the table therefore ignore insertion when the topic exists already + pub async fn add_topic(&self, topic: &Topic) -> sqlx::Result<()> { + // The id is the primary key in the table therefore ignore insertion when the topic exists + // already sqlx::query( " INSERT OR IGNORE INTO topics ( id ) VALUES ( ? ) ", ) - .bind(id.as_slice()) + .bind(topic.as_bytes().as_slice()) .execute(&self.pool) .await?; Ok(()) } - pub async fn delete_topic(&self, id: &TopicId) -> sqlx::Result<()> { + pub async fn delete_topic(&self, topic: &Topic) -> sqlx::Result<()> { sqlx::query("DELETE FROM topics WHERE id = ?") - .bind(id.as_slice()) + .bind(topic.as_bytes().as_slice()) .execute(&self.pool) .await?; Ok(()) } - pub async fn add_author(&self, id: &TopicId, public_key: &PublicKey) -> sqlx::Result<()> { + pub async fn add_author( + &self, + topic: &Topic, + verifying_key: &VerifyingKey, + ) -> sqlx::Result<()> { // The author/id pair is required to be unique therefore ignore if the insertion fails sqlx::query( " - INSERT OR IGNORE INTO authors ( public_key, topic_id ) + INSERT OR IGNORE INTO authors ( verifying_key, topic_id ) VALUES ( ?, ? ) ", ) - .bind(public_key.as_bytes().as_slice()) - .bind(id.as_slice()) + .bind(verifying_key.as_bytes().as_slice()) + .bind(topic.as_bytes().as_slice()) .execute(&self.pool) .await?; @@ -130,25 +114,29 @@ impl TopicStore { pub async fn set_last_seen_for_author( &self, - public_key: PublicKey, + verifying_key: VerifyingKey, last_seen: Option>, ) -> sqlx::Result<()> { sqlx::query( " UPDATE authors SET last_seen = ? - WHERE public_key = ? + WHERE verifying_key = ? ", ) .bind(last_seen) - .bind(public_key.as_bytes().as_slice()) + .bind(verifying_key.as_bytes().as_slice()) .execute(&self.pool) .await?; Ok(()) } - pub async fn set_name_for_topic(&self, id: &TopicId, name: Option) -> sqlx::Result<()> { + pub async fn set_name_for_topic( + &self, + topic: &Topic, + name: Option, + ) -> sqlx::Result<()> { sqlx::query( " UPDATE topics @@ -157,7 +145,7 @@ impl TopicStore { ", ) .bind(name) - .bind(id.as_slice()) + .bind(topic.as_bytes().as_slice()) .execute(&self.pool) .await?; @@ -166,7 +154,7 @@ impl TopicStore { pub async fn set_last_accessed_for_topic( &self, - id: &TopicId, + topic: &Topic, last_accessed: Option>, ) -> sqlx::Result<()> { sqlx::query( @@ -177,81 +165,10 @@ impl TopicStore { ", ) .bind(last_accessed) - .bind(id.as_slice()) + .bind(topic.as_bytes().as_slice()) .execute(&self.pool) .await?; Ok(()) } - - pub async fn operations_for_topic( - &self, - operation_store: &OperationStore, - id: &TopicId, - ) -> sqlx::Result>> { - let operation_store = operation_store.inner(); - let authors = self.authors(id).await?; - - let log_ids = [ - LogId::new(LogType::Delta, id), - LogId::new(LogType::Snapshot, id), - ]; - - let mut result = Vec::new(); - - for author in authors.iter() { - for log_id in &log_ids { - let operations = match operation_store.get_log(author, log_id, None).await { - Ok(Some(operations)) => { - operations - .into_iter() - .map(|(header, body)| p2panda_core::Operation { - hash: header.hash(), - header, - body, - }) - } - Ok(None) => { - continue; - } - Err(error) => { - error!( - "Failed to load operation for {author} with log type {log_id:?}: {error}" - ); - continue; - } - }; - - result.extend(operations); - } - } - - Ok(result) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, StdHash, Serialize, Deserialize)] -pub struct LogId(LogType, TopicId); - -impl LogId { - pub fn new(log_type: LogType, topic: &TopicId) -> Self { - Self(log_type, *topic) - } -} - -impl TopicMap> for TopicStore { - type Error = sqlx::Error; - - async fn get(&self, topic: &TopicId) -> Result, Self::Error> { - let authors = self.authors(topic).await?; - - let log_ids = [ - LogId::new(LogType::Delta, topic), - LogId::new(LogType::Snapshot, topic), - ]; - Ok(authors - .into_iter() - .map(|author| (author, log_ids.to_vec())) - .collect()) - } } diff --git a/reflection-node/src/topic_stream.rs b/reflection-node/src/topic_stream.rs new file mode 100644 index 00000000..7fe3e3be --- /dev/null +++ b/reflection-node/src/topic_stream.rs @@ -0,0 +1,464 @@ +use std::mem::take; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; + +use chrono::Utc; +use p2panda::Topic; +use p2panda::node::CreateStreamError; +use p2panda::streams::{EphemeralStreamPublisher, StreamEvent, StreamFrom, StreamPublisher}; +use thiserror::Error; +use tokio::sync::{RwLock, oneshot}; +use tokio::task::{AbortHandle, JoinError}; +use tokio_stream::StreamExt; +use tracing::{error, info, warn}; + +use crate::author_tracker::AuthorTracker; +use crate::ephemeral_message::EphemeralMessage; +use crate::node::NodeInner; +use crate::traits::TopicSubscription; + +#[derive(Debug, Error)] +pub enum PublishError { + #[error(transparent)] + Runtime(#[from] JoinError), + + #[error(transparent)] + StreamPublish(#[from] p2panda::streams::PublishError), + + #[error(transparent)] + EphemeralStreamPublish(#[from] p2panda::streams::EphemeralPublishError), + + #[error("streams to publish data into network are not available due to a setup error")] + BrokenStream, +} + +#[derive(Debug, Error)] +pub enum TopicStreamError { + #[error(transparent)] + Runtime(#[from] JoinError), + + #[error(transparent)] + Database(#[from] sqlx::Error), +} + +pub struct TopicStream { + inner: Arc>, + runtime: tokio::runtime::Handle, + network_monitor_task: AbortHandle, +} + +impl Drop for TopicStream { + fn drop(&mut self) { + self.network_monitor_task.abort(); + } +} + +impl TopicStream +where + T: TopicSubscription + 'static, +{ + pub(crate) async fn new(runtime: tokio::runtime::Handle, inner: TopicStreamInner) -> Self { + let (ready_tx, ready_rx) = oneshot::channel(); + + // Spawn task to establish streams to publish and subscribe to messages, the same task will + // also await a shutdown signal to drop the streams. + let inner = Arc::new(inner); + let inner_clone = inner.clone(); + let network_monitor_task = runtime + .spawn(async move { + inner_clone.spawn_network_monitor(ready_tx).await; + }) + .abort_handle(); + + // Wait until streams with network have been established. + let _ = ready_rx.await; + + TopicStream { + inner, + runtime, + network_monitor_task, + } + } + + pub async fn publish_delta(&self, data: Vec) -> Result<(), PublishError> { + let inner = self.inner.clone(); + self.runtime + .spawn(async move { inner.publish_delta(data).await }) + .await? + } + + pub async fn publish_snapshot(&self, data: Vec) -> Result<(), PublishError> { + let inner = self.inner.clone(); + self.runtime + .spawn(async move { inner.publish_snapshot(data).await }) + .await? + } + + pub async fn publish_ephemeral(&self, data: Vec) -> Result<(), PublishError> { + let inner = self.inner.clone(); + self.runtime + .spawn(async move { inner.publish_ephemeral(data).await }) + .await? + } + + pub async fn unsubscribe(self) -> Result<(), TopicStreamError> { + self.network_monitor_task.abort(); + + let inner = self.inner.clone(); + self.runtime + .spawn(async move { inner.unsubscribe().await }) + .await??; + + info!("unsubscribed from topic {}", self.inner.topic); + + Ok(()) + } + + /// Set the name for a given topic. + /// + /// This information will be written to the database. + pub async fn set_name(&self, name: Option) -> Result<(), TopicStreamError> { + let inner = self.inner.clone(); + self.runtime + .spawn(async move { inner.set_name(name).await }) + .await? + } +} + +pub(crate) struct TopicStreamInner { + tx: RwLock>>>, + ephemeral_tx: RwLock>>, + node: Arc, + topic: Topic, + subscription: Arc, + author_tracker: Arc>, + abort_handles: RwLock>, +} + +impl Drop for TopicStreamInner { + fn drop(&mut self) { + for handle in self.abort_handles.get_mut() { + handle.abort(); + } + } +} + +impl TopicStreamInner +where + T: TopicSubscription + 'static, +{ + pub fn new(node: Arc, topic: Topic, subscription: Arc) -> Self { + let author_tracker = AuthorTracker::new(node.clone(), subscription.clone()); + + TopicStreamInner { + tx: RwLock::new(None), + ephemeral_tx: RwLock::new(None), + node, + topic, + abort_handles: RwLock::new(Vec::new()), + subscription, + author_tracker, + } + } + + pub async fn spawn_network_monitor(&self, ready_signal: oneshot::Sender<()>) { + // Hold a read lock to the network, so that the network won't be dropped or shutdown. + let network_guard = self.node.network.read().await; + + let result = setup_streams( + &self.node, + network_guard.deref(), + self.topic, + &self.subscription, + &self.author_tracker, + ) + .await; + + match result { + Ok((tx, ephemeral_tx, abort_handles)) => { + *self.tx.write().await = Some(tx); + *self.ephemeral_tx.write().await = Some(ephemeral_tx); + *self.abort_handles.write().await = abort_handles; + } + Err(error) => { + self.subscription.error(error.into()); + } + } + + drop(network_guard); + + // Inform caller that we're done with setting up the streams. They are ready now to be used + // for publishing and receiving messages. + let _ = ready_signal.send(()); + + // Wait until we've received signal from node to shut down. + let shutdown_notification = self.node.shutdown_notifier.notified(); + shutdown_notification.await; + + let _ = self.unsubscribe().await; + } + + pub async fn unsubscribe(&self) -> Result<(), TopicStreamError> { + let mut tx_guard = self.tx.write().await; + let mut ephemeral_tx_guard = self.ephemeral_tx.write().await; + let mut abort_handles_guard = self.abort_handles.write().await; + + let tx = take(tx_guard.deref_mut()); + let ephemeral_tx = take(ephemeral_tx_guard.deref_mut()); + let abort_handles = take(abort_handles_guard.deref_mut()); + + self.node + .topic_store + .set_last_accessed_for_topic(&self.topic, Some(Utc::now())) + .await?; + + teardown_streams( + &self.topic, + &self.author_tracker, + tx, + ephemeral_tx, + abort_handles, + ) + .await; + + Ok(()) + } + + pub async fn publish_delta(&self, data: Vec) -> Result<(), PublishError> { + if let Some(tx) = self.tx.read().await.as_ref() { + info!("delta operation sent for topic with id {}", self.topic); + tx.publish(data).await?; + } else { + return Err(PublishError::BrokenStream); + } + + Ok(()) + } + + pub async fn publish_snapshot(&self, data: Vec) -> Result<(), PublishError> { + if let Some(tx) = self.tx.read().await.as_ref() { + info!("snapshot saved for topic with id {}", self.topic); + + // Append an operation to our log and set the prune flag to true. This will remove + // previous entries. + tx.prune(Some(data)).await?; + } else { + return Err(PublishError::BrokenStream); + } + + Ok(()) + } + + pub async fn publish_ephemeral(&self, data: Vec) -> Result<(), PublishError> { + if let Some(ephemeral_tx) = self.ephemeral_tx.read().await.as_ref() { + ephemeral_tx + .publish(EphemeralMessage::Application(data)) + .await?; + } else { + return Err(PublishError::BrokenStream); + } + + Ok(()) + } + + pub async fn set_name(&self, name: Option) -> Result<(), TopicStreamError> { + self.node + .topic_store + .set_name_for_topic(&self.topic, name) + .await?; + + Ok(()) + } +} + +async fn setup_streams( + node: &Arc, + network: &p2panda::Node, + id: Topic, + subscribable_topic: &Arc, + author_tracker: &Arc>, +) -> Result< + ( + StreamPublisher>, + EphemeralStreamPublisher, + Vec, + ), + CreateStreamError, +> +where + T: TopicSubscription + 'static, +{ + let mut abort_handles = Vec::with_capacity(3); + + // 1. Handle incoming operations from eventually consistent topic stream. + // ====================================================================== + + // Always start from re-playing _all_ operations in the beginning. This is due to Reflection not + // keeping materialised document state around and we need to repeat materialising the document + // at the beginning (in memory). + // + // This cost is acceptable since we're frequently pruning the log and the number of operations + // to process is rather small. + let stream_from = StreamFrom::Start; + + let (topic_tx, mut topic_rx) = network.stream_from::>(id, stream_from).await?; + + let node_clone = node.clone(); + let subscribable_topic_clone = subscribable_topic.clone(); + let abort_handle = tokio::spawn(async move { + while let Some(event) = topic_rx.next().await { + match event { + StreamEvent::Processed { operation, .. } => { + let author = operation.author(); + + info!( + author = &author.to_string()[0..8], + "processed operation with id {}", + operation.id() + ); + + // When we discover a new author we need to add them to our topic store. + if let Err(error) = node_clone.topic_store.add_author(&id, &author).await { + error!("can't store author to database: {error}"); + } + + // Forward the message payload up to the app layer. + subscribable_topic_clone.bytes_received(author, operation.message().to_owned()); + } + StreamEvent::SyncStarted { + remote_node_id, + session_id, + incoming_operations, + outgoing_operations, + incoming_bytes, + outgoing_bytes, + .. + } => { + info!( + %session_id, + remote_node_id = &remote_node_id.to_string()[0..8], + "sync started w. {} incoming ({} bytes) and {} outgoing operations ({} bytes)", + incoming_operations, + incoming_bytes, + outgoing_operations, + outgoing_bytes, + ); + } + StreamEvent::SyncEnded { + remote_node_id, + session_id, + error, + .. + } => { + match error { + Some(error) => { + warn!( + %session_id, + remote_node_id = &remote_node_id.to_string()[0..8], + "sync failed with error {error}", + ); + } + None => { + info!( + %session_id, + remote_node_id = &remote_node_id.to_string()[0..8], + "sync ended", + ); + }, + } + } + StreamEvent::DecodeFailed { error, .. } => { + error!("failed decoding incoming operation from stream: {error}"); + subscribable_topic_clone.error(error.into()); + } + StreamEvent::ReplayFailed { error, .. } => { + error!("error occurred while replaying operation stream: {error}"); + subscribable_topic_clone.error(crate::traits::TopicSubscriptionError::ReplayStream(error)); + } + StreamEvent::ProcessingFailed { event, error, .. } => { + error!("error occurred while processing operation {}: {error}", event.header().hash()); + subscribable_topic_clone.error(error.into()); + } + StreamEvent::AckFailed { error, .. } => { + error!("error occurred while acking event: {error}"); + subscribable_topic_clone.error(crate::traits::TopicSubscriptionError::AckedMessage(error)); + } + _ => (), + } + } + }) + .abort_handle(); + + abort_handles.push(abort_handle); + + // 2. Handle incoming messages from ephemeral topic stream. + // ======================================================== + + let (ephemeral_tx, mut ephemeral_rx) = network.ephemeral_stream::(id).await?; + + author_tracker + .set_topic_tx(Some(ephemeral_tx.clone())) + .await; + + let author_tracker_clone = author_tracker.clone(); + let subscribable_topic_clone = subscribable_topic.clone(); + let abort_handle = tokio::spawn(async move { + while let Some(message) = ephemeral_rx.next().await { + match message.body() { + EphemeralMessage::Application(bytes) => { + subscribable_topic_clone.ephemeral_bytes_received( + message.author(), + message.timestamp(), + bytes.to_owned(), + ); + } + EphemeralMessage::AuthorTracker(tracker) => { + author_tracker_clone + .received(message.author(), tracker.to_owned()) + .await; + } + } + } + }) + .abort_handle(); + + abort_handles.push(abort_handle); + + // 3. Run task to track online status of authors. + // ============================================== + + let author_tracker_clone = author_tracker.clone(); + let abort_handle = tokio::spawn(async move { + author_tracker_clone.spawn().await; + }) + .abort_handle(); + + abort_handles.push(abort_handle); + + info!("network streams set up for topic {}", id); + + Ok((topic_tx, ephemeral_tx, abort_handles)) +} + +async fn teardown_streams( + topic: &Topic, + author_tracker: &Arc>, + tx: Option>>, + ephemeral_tx: Option>, + abort_handles: Vec, +) where + T: TopicSubscription + 'static, +{ + for handle in abort_handles { + handle.abort(); + } + + author_tracker.set_topic_tx(None).await; + + if tx.is_some() { + info!("network streams torn down for topic {}", topic); + } + + drop(tx); + drop(ephemeral_tx); +} diff --git a/reflection-node/src/traits.rs b/reflection-node/src/traits.rs new file mode 100644 index 00000000..2aba5a60 --- /dev/null +++ b/reflection-node/src/traits.rs @@ -0,0 +1,48 @@ +use std::sync::Arc; + +use p2panda::VerifyingKey; +use p2panda::node::CreateStreamError; +use p2panda::processor::ProcessorError; +use p2panda::streams::{AckedError, DecodeError, ReplayError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum TopicSubscriptionError { + /// Broken / closed communication channel with the internal actor in `p2panda-net` prevented + /// creation of stream. This can be due to the actor crashing. + #[error(transparent)] + CreateStream(#[from] CreateStreamError), + + /// Topic stream could not re-play events due to an internal error. + #[error("{0}")] + ReplayStream(Arc), + + /// Acknowledgment of event failed due to critical error. + #[error("{0}")] + AckedMessage(Arc), + + /// Application payload could not be deserialized. + #[error(transparent)] + DecodeMessage(#[from] DecodeError), + + /// Operation failed during event processing of the system-level pipeline. + #[error(transparent)] + StreamProcessor(#[from] ProcessorError), +} + +pub trait TopicSubscription: Sync + Send { + /// Received bytes from eventually consistent topic stream (sync protocol). + fn bytes_received(&self, author: VerifyingKey, data: Vec); + + /// Received bytes from ephemeral stream (gossip overlay). + fn ephemeral_bytes_received(&self, author: VerifyingKey, timestamp: u64, data: Vec); + + /// Author went online. + fn author_joined(&self, author: VerifyingKey); + + /// Author went offline or timed-out. + fn author_left(&self, author: VerifyingKey); + + /// Error occurred in topic subscription. + fn error(&self, error: TopicSubscriptionError); +} diff --git a/reflection-node/src/utils.rs b/reflection-node/src/utils.rs deleted file mode 100644 index a2989333..00000000 --- a/reflection-node/src/utils.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::pin::Pin; - -use sqlx::error::BoxDynError; -use sqlx::migrate::{Migration, MigrationSource, Migrator}; - -type BoxFuture<'a, T> = Pin + Send + 'a>>; - -/// Combine multiple `sqlx::migrate::Migrator` into a single `sqlx::migrate::MigrationSource` -/// -/// See for more details: https://github.com/launchbadge/sqlx/discussions/3407 -#[derive(Debug)] -pub struct CombinedMigrationSource { - migrators: Vec, -} - -impl CombinedMigrationSource { - pub fn new(migrators: Vec) -> CombinedMigrationSource { - Self { migrators } - } -} - -impl<'s> MigrationSource<'s> for CombinedMigrationSource { - fn resolve(self) -> BoxFuture<'s, Result, BoxDynError>> { - Box::pin(async move { - Ok(self - .migrators - .iter() - .flat_map(|migrator| migrator.iter()) - .cloned() - .collect()) - }) - } -}