diff --git a/Cargo.lock b/Cargo.lock index 76277e5..1965ac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "accessory" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28e416a3ab45838bac2ab2d81b1088d738d7b2d2c5272a54d39366565a29bd80" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "adler2" version = "2.0.1" @@ -136,6 +148,26 @@ version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "aquamarine" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "ar_archive_writer" version = "0.5.1" @@ -145,6 +177,39 @@ dependencies = [ "object 0.37.3", ] +[[package]] +name = "archery" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "as_variant" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbc3a507a82b17ba0d98f6ce8fd6954ea0c8152e98009d36a40d8dcc8ce078a" + +[[package]] +name = "assign" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" + [[package]] name = "async-channel" version = "1.9.0" @@ -221,6 +286,28 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -229,7 +316,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -319,12 +406,29 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backon" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" +dependencies = [ + "fastrand", + "gloo-timers", + "tokio", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bincode" version = "2.0.1" @@ -362,7 +466,7 @@ checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -380,6 +484,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "bitmaps" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" + [[package]] name = "bitvec" version = "1.0.1" @@ -392,6 +502,20 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -410,6 +534,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.19.1" @@ -439,7 +572,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -454,6 +587,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "bytesize" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" + [[package]] name = "cast" version = "0.3.0" @@ -626,7 +765,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -668,6 +807,7 @@ checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" dependencies = [ "compression-core", "flate2", + "memchr", ] [[package]] @@ -698,6 +838,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_panic" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" +dependencies = [ + "typewit", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + [[package]] name = "cookie" version = "0.16.2" @@ -884,6 +1045,43 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "darling" version = "0.20.11" @@ -905,7 +1103,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.116", ] [[package]] @@ -916,7 +1114,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -925,6 +1123,52 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +[[package]] +name = "date_header" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c03c416ed1a30fbb027ef484ba6ab6f80e1eada675e1a2b92fd673c045a1f1d" + +[[package]] +name = "deadpool" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" +dependencies = [ + "deadpool-runtime", + "lazy_static", + "num_cpus", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" +dependencies = [ + "tokio", +] + +[[package]] +name = "deadpool-sync" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524bc3df0d57e98ecd022e21ba31166c2625e7d3e5bcc4510efaeeab4abcab04" +dependencies = [ + "deadpool-runtime", +] + +[[package]] +name = "decancer" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9244323129647178bf41ac861a2cdb9d9c81b9b09d3d0d1de9cd302b33b8a1d" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "deku" version = "0.18.1" @@ -947,7 +1191,31 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "delegate-display" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9926686c832494164c33a36bf65118f4bd6e704000b58c94681bf62e9ad67a74" +dependencies = [ + "impartial-ord", + "itoa", + "macroific", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", ] [[package]] @@ -959,6 +1227,59 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl 2.1.1", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.116", + "unicode-xid", +] + [[package]] name = "dialoguer" version = "0.12.0" @@ -1042,7 +1363,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1062,7 +1383,7 @@ checksum = "11772ed3eb3db124d826f3abeadf5a791a557f62c19b123e3f07288158a71fdd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1080,6 +1401,32 @@ dependencies = [ "cipher", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "serde", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.15.0" @@ -1134,7 +1481,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1231,6 +1578,32 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "eyeball" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93bd0ebf93d61d6332d3c09a96e97975968a44e19a64c947bde06e6baff383f" +dependencies = [ + "futures-core", + "readlock", + "readlock-tokio", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "eyeball-im" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4790c03df183c2b46665c1a58118c04fd3e3976ec2fe16a0aa00e00c9eea7754" +dependencies = [ + "futures-core", + "imbl", + "tokio", + "tracing", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1249,6 +1622,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy_constructor" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a27643a5d05f3a22f5afd6e0d0e6e354f92d37907006f97b84b9cb79082198" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "fantoccini" version = "0.22.0" @@ -1278,6 +1663,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1306,12 +1697,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1333,6 +1718,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.32" @@ -1399,7 +1794,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1507,6 +1902,62 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "growable-bloom-filter" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "xxhash-rust", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.7.1" @@ -1543,7 +1994,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash 0.1.5", + "foldhash", ] [[package]] @@ -1551,9 +2002,6 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash 0.2.0", -] [[package]] name = "hashify" @@ -1563,16 +2011,40 @@ checksum = "149e3ea90eb5a26ad354cfe3cb7f7401b9329032d0235f2687d03a35f30e5d4c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] name = "hashlink" -version = "0.11.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.15.5", +] + +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64", + "bytes", + "headers-core", + "http 1.4.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.4.0", ] [[package]] @@ -1624,6 +2096,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1644,6 +2125,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "html5ever" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4" +dependencies = [ + "log", + "markup5ever", + "match_token", +] + [[package]] name = "http" version = "0.2.12" @@ -1665,6 +2157,15 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-auth" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" +dependencies = [ + "memchr", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1710,6 +2211,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http 1.4.0", "http-body", "httparse", @@ -1918,7 +2420,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1991,6 +2493,60 @@ dependencies = [ "nom 7.1.3", ] +[[package]] +name = "imbl" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fade8ae6828627ad1fa094a891eccfb25150b383047190a3648d66d06186501" +dependencies = [ + "archery", + "bitmaps", + "imbl-sized-chunks", + "rand_core 0.9.5", + "rand_xoshiro", + "serde", + "version_check", +] + +[[package]] +name = "imbl-sized-chunks" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4241005618a62f8d57b2febd02510fb96e0137304728543dfc5fd6f052c22d" +dependencies = [ + "bitmaps", +] + +[[package]] +name = "impartial-ord" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab604ee7085efba6efc65e4ebca0e9533e3aff6cb501d7d77b211e3a781c6d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -2120,6 +2676,44 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "js_int" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d937f95470b270ce8b8950207715d71aa8e153c0d44c6684d59397ed4949160a" +dependencies = [ + "serde", +] + +[[package]] +name = "js_option" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7dd3e281add16813cf673bf74a32249b0aa0d1c8117519a17b3ada5e8552b3c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "konst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" +dependencies = [ + "const_panic", + "konst_kernel", + "typewit", +] + +[[package]] +name = "konst_kernel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" +dependencies = [ + "typewit", +] + [[package]] name = "landlock" version = "0.4.4" @@ -2131,6 +2725,12 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.5.0" @@ -2191,9 +2791,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.36.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ "cc", "pkg-config", @@ -2289,6 +2889,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + [[package]] name = "mach2" version = "0.4.3" @@ -2298,6 +2904,54 @@ dependencies = [ "libc", ] +[[package]] +name = "macroific" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f276537b4b8f981bf1c13d79470980f71134b7bdcc5e6e911e910e556b0285" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "macroific_macro", +] + +[[package]] +name = "macroific_attr_parse" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad4023761b45fcd36abed8fb7ae6a80456b0a38102d55e89a57d9a594a236be9" +dependencies = [ + "proc-macro2", + "quote", + "sealed", + "syn 2.0.116", +] + +[[package]] +name = "macroific_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a7594d3c14916fa55bef7e9d18c5daa9ed410dd37504251e4b75bbdeec33e3" +dependencies = [ + "proc-macro2", + "quote", + "sealed", + "syn 2.0.116", +] + +[[package]] +name = "macroific_macro" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da6f2ed796261b0a74e2b52b42c693bb6dee1effba3a482c49592659f824b3b" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "mail-parser" version = "0.11.2" @@ -2307,6 +2961,34 @@ dependencies = [ "hashify", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "markup5ever" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "match_token" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "matchers" version = "0.2.0" @@ -2322,6 +3004,298 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "matrix-pickle" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c34e6db65145740459f2ca56623b40cd4e6000ffae2a7d91515fa82aa935dbf" +dependencies = [ + "matrix-pickle-derive", + "thiserror 2.0.18", +] + +[[package]] +name = "matrix-pickle-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a962fc9981f823f6555416dcb2ae9ae67ca412d767ee21ecab5150113ee6285b" +dependencies = [ + "proc-macro-crate", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "matrix-sdk" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33f9bc45edd7f8e25161521fdd30654da5c55e6749be6afa1aa9d6cf838ace0" +dependencies = [ + "anymap2", + "aquamarine", + "as_variant", + "async-channel 2.5.0", + "async-stream", + "async-trait", + "backon", + "bytes", + "bytesize", + "cfg-if", + "event-listener 5.4.1", + "eyeball", + "eyeball-im", + "futures-core", + "futures-util", + "gloo-timers", + "http 1.4.0", + "imbl", + "indexmap", + "itertools 0.14.0", + "js_int", + "language-tags", + "matrix-sdk-base", + "matrix-sdk-common", + "matrix-sdk-indexeddb", + "matrix-sdk-sqlite", + "mime", + "mime2ext", + "oauth2", + "once_cell", + "percent-encoding", + "pin-project-lite", + "reqwest", + "ruma", + "serde", + "serde_html_form", + "serde_json", + "sha2", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "url", + "urlencoding", + "vodozemac", + "zeroize", +] + +[[package]] +name = "matrix-sdk-base" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f404a390ff98a73c426b1496b169be60ce6a93723a9a664e579d978a84c5e4" +dependencies = [ + "as_variant", + "async-trait", + "bitflags 2.11.0", + "decancer", + "eyeball", + "eyeball-im", + "futures-util", + "growable-bloom-filter", + "matrix-sdk-common", + "matrix-sdk-crypto", + "matrix-sdk-store-encryption", + "once_cell", + "regex", + "ruma", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "unicode-normalization", +] + +[[package]] +name = "matrix-sdk-common" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54fae2bdfc3d760d21a84d6d2036b5db5c48d9a3dee3794119e3fb9c4cc4ccc5" +dependencies = [ + "eyeball-im", + "futures-core", + "futures-executor", + "futures-util", + "gloo-timers", + "imbl", + "ruma", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-subscriber", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "matrix-sdk-crypto" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304fc576810a9618bb831c4ad6403c758ec424f677668a49a196e3cde4b8f99f" +dependencies = [ + "aes", + "aquamarine", + "as_variant", + "async-trait", + "bs58", + "byteorder", + "cfg-if", + "ctr", + "eyeball", + "futures-core", + "futures-util", + "hkdf", + "hmac", + "itertools 0.14.0", + "js_option", + "matrix-sdk-common", + "pbkdf2", + "rand 0.8.5", + "rmp-serde", + "ruma", + "serde", + "serde_json", + "sha2", + "subtle", + "thiserror 2.0.18", + "time", + "tokio", + "tokio-stream", + "tracing", + "ulid", + "url", + "vodozemac", + "zeroize", +] + +[[package]] +name = "matrix-sdk-indexeddb" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6096084cc8d339c03e269ca25534d0f1e88d0097c35a215eb8c311797ec3e9" +dependencies = [ + "async-trait", + "base64", + "futures-util", + "getrandom 0.2.17", + "gloo-utils", + "hkdf", + "js-sys", + "matrix-sdk-base", + "matrix-sdk-crypto", + "matrix-sdk-store-encryption", + "matrix_indexed_db_futures", + "rmp-serde", + "ruma", + "serde", + "serde-wasm-bindgen", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tokio", + "tracing", + "uuid", + "wasm-bindgen", + "web-sys", + "zeroize", +] + +[[package]] +name = "matrix-sdk-sqlite" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4325742fc06b7f75c80eec39e8fb32b06ea4b09b7aa1d432b67b01d08fbacc28" +dependencies = [ + "as_variant", + "async-trait", + "deadpool", + "deadpool-sync", + "itertools 0.14.0", + "matrix-sdk-base", + "matrix-sdk-crypto", + "matrix-sdk-store-encryption", + "num_cpus", + "rmp-serde", + "ruma", + "rusqlite", + "serde", + "serde_json", + "serde_path_to_error", + "thiserror 2.0.18", + "tokio", + "tracing", + "vodozemac", + "zeroize", +] + +[[package]] +name = "matrix-sdk-store-encryption" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162a93e83114d5cef25c0ebaea72aa01b9f233df6ec4a2af45f175d01ec26323" +dependencies = [ + "base64", + "blake3", + "chacha20poly1305", + "getrandom 0.2.17", + "hmac", + "pbkdf2", + "rand 0.8.5", + "rmp-serde", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "zeroize", +] + +[[package]] +name = "matrix_indexed_db_futures" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245ff6a224b4df7b0c90dda2dd5a6eb46112708d49e8bdd8b007fccb09fea8e4" +dependencies = [ + "accessory", + "cfg-if", + "delegate-display", + "derive_more 2.1.1", + "fancy_constructor", + "futures-core", + "js-sys", + "matrix_indexed_db_futures_macros_internal", + "sealed", + "serde", + "serde-wasm-bindgen", + "smallvec", + "thiserror 2.0.18", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm_evt_listener", + "web-sys", + "web-time", +] + +[[package]] +name = "matrix_indexed_db_futures_macros_internal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b428aee5c0fe9e5babd29e99d289b7f64718c444989aac0442d1fd6d3e3f66d1" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "md-5" version = "0.10.6" @@ -2357,7 +3331,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -2366,6 +3340,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime2ext" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf6f36070878c42c5233846cd3de24cf9016828fd47bc22957a687298bb21fc" + [[package]] name = "mime_guess" version = "2.0.5" @@ -2417,6 +3397,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.26.4" @@ -2526,6 +3512,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc", +] + [[package]] name = "nusb" version = "0.1.14" @@ -2563,6 +3559,26 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64", + "chrono", + "getrandom 0.2.17", + "http 1.4.0", + "rand 0.8.5", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.69", + "url", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" @@ -2668,7 +3684,7 @@ dependencies = [ "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", - "prost", + "prost 0.14.3", "reqwest", "thiserror 2.0.18", ] @@ -2681,7 +3697,7 @@ checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ "opentelemetry", "opentelemetry_sdk", - "prost", + "prost 0.14.3", "tonic", "tonic-prost", ] @@ -2745,6 +3761,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "pdf-extract" version = "0.10.0" @@ -2768,6 +3794,15 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared 0.11.3", +] + [[package]] name = "phf" version = "0.12.1" @@ -2787,6 +3822,35 @@ dependencies = [ "serde", ] +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.12.1" @@ -2822,7 +3886,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -2837,6 +3901,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -2976,6 +4050,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "prettyplease" version = "0.2.37" @@ -2983,7 +4063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.116", ] [[package]] @@ -3045,6 +4125,27 @@ dependencies = [ "toml_edit 0.23.10+spec-1.0.0", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -3068,6 +4169,16 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive 0.13.5", +] + [[package]] name = "prost" version = "0.14.3" @@ -3075,7 +4186,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.14.3", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.116", ] [[package]] @@ -3088,7 +4212,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -3242,6 +4366,15 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_xoshiro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "rangemap" version = "1.7.1" @@ -3268,6 +4401,21 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "readlock" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6da6f291b23556edd9edaf655a0be2ad8ef8002ff5f1bca62b264f3f58b53f34" + +[[package]] +name = "readlock-tokio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7e264f9ec4f3d112e8e2f214e8e7cb5cf3b83278f3570b7e00bfe13d3bd8ff" +dependencies = [ + "tokio", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -3339,6 +4487,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", + "h2", "http 1.4.0", "http-body", "http-body-util", @@ -3423,20 +4572,185 @@ dependencies = [ ] [[package]] -name = "rsqlite-vfs" -version = "0.1.0" +name = "ruma" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d" +checksum = "a9f620a2116d0d3082f9256e61dcdf67f2ec266d3f6bb9d2f9c8a20ec5a1fabb" dependencies = [ - "hashbrown 0.16.1", + "assign", + "js_int", + "js_option", + "ruma-client-api", + "ruma-common", + "ruma-events", + "ruma-federation-api", + "ruma-html", + "web-time", +] + +[[package]] +name = "ruma-client-api" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc977d1a91ea15dcf896cbd7005ed4a253784468833638998109ffceaee53e7" +dependencies = [ + "as_variant", + "assign", + "bytes", + "date_header", + "http 1.4.0", + "js_int", + "js_option", + "maplit", + "ruma-common", + "ruma-events", + "serde", + "serde_html_form", + "serde_json", + "thiserror 2.0.18", + "url", + "web-time", +] + +[[package]] +name = "ruma-common" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a01993f22d291320b7c9267675e7395775e95269ff526e2c8c3ed5e13175b" +dependencies = [ + "as_variant", + "base64", + "bytes", + "form_urlencoded", + "getrandom 0.2.17", + "http 1.4.0", + "indexmap", + "js-sys", + "js_int", + "konst", + "percent-encoding", + "rand 0.8.5", + "regex", + "ruma-identifiers-validation", + "ruma-macros", + "serde", + "serde_html_form", + "serde_json", + "thiserror 2.0.18", + "time", + "tracing", + "url", + "uuid", + "web-time", + "wildmatch", + "zeroize", +] + +[[package]] +name = "ruma-events" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dbdeccb62cb4ffe3282325de8ba28cbc0fdce7c78a3f11b7241fbfdb9cb9907" +dependencies = [ + "as_variant", + "indexmap", + "js_int", + "js_option", + "percent-encoding", + "regex", + "ruma-common", + "ruma-identifiers-validation", + "ruma-macros", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", + "url", + "web-time", + "wildmatch", + "zeroize", +] + +[[package]] +name = "ruma-federation-api" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb45c15badbf4299c6113a6b90df3e7cb64edbe756bbd8e0224144b56b38305" +dependencies = [ + "headers", + "http 1.4.0", + "http-auth", + "js_int", + "mime", + "ruma-common", + "ruma-events", + "ruma-signatures", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "ruma-html" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6dcd6e9823e177d15460d3cd3a413f38a2beea381f26aca1001c05cd6954ff" +dependencies = [ + "as_variant", + "html5ever", + "tracing", + "wildmatch", +] + +[[package]] +name = "ruma-identifiers-validation" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c6b5643060beec0fc9d7acfb41d2c5d91e1591db440ff62361d178e77c35fe" +dependencies = [ + "js_int", + "thiserror 2.0.18", +] + +[[package]] +name = "ruma-macros" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0753312ad577ac462de1742bf2e326b6ba9856ff6f13343aeb17d423fd5426" +dependencies = [ + "as_variant", + "cfg-if", + "proc-macro-crate", + "proc-macro2", + "quote", + "ruma-identifiers-validation", + "serde", + "syn 2.0.116", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "ruma-signatures" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146ace2cd59b60ec80d3e801a84e7e6a91e3e01d18a9f5d896ea7ca16a6b8e08" +dependencies = [ + "base64", + "ed25519-dalek", + "pkcs8", + "rand 0.8.5", + "ruma-common", + "serde_json", + "sha2", "thiserror 2.0.18", ] [[package]] name = "rusqlite" -version = "0.38.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ "bitflags 2.11.0", "fallible-iterator 0.3.0", @@ -3444,7 +4758,6 @@ dependencies = [ "hashlink", "libsqlite3-sys", "smallvec", - "sqlite-wasm-rs", ] [[package]] @@ -3453,6 +4766,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -3580,6 +4902,17 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" +[[package]] +name = "sealed" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "security-framework" version = "3.6.0" @@ -3625,6 +4958,27 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3642,7 +4996,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "serde_html_form" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" +dependencies = [ + "form_urlencoded", + "indexmap", + "itoa", + "ryu", + "serde_core", ] [[package]] @@ -3816,6 +5183,15 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.8" @@ -3839,6 +5215,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -3851,15 +5230,13 @@ dependencies = [ ] [[package]] -name = "sqlite-wasm-rs" -version = "0.5.2" +name = "spki" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4206ed3a67690b9c29b77d728f6acc3ce78f16bf846d83c94f76400320181b" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ - "cc", - "js-sys", - "rsqlite-vfs", - "wasm-bindgen", + "base64ct", + "der", ] [[package]] @@ -3893,6 +5270,31 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + [[package]] name = "stringprep" version = "0.1.5" @@ -3928,7 +5330,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -3937,6 +5339,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.116" @@ -3965,7 +5378,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -3987,6 +5400,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -4013,7 +5437,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -4024,7 +5448,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -4135,7 +5559,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -4197,6 +5621,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -4263,6 +5688,19 @@ dependencies = [ "toml_edit 0.22.27", ] +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "serde_core", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.14", +] + [[package]] name = "toml" version = "1.0.2+spec-1.1.0" @@ -4380,7 +5818,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f86539c0089bfd09b1f8c0ab0239d80392af74c21bc9e0f15e1b4aca4c1647f" dependencies = [ "bytes", - "prost", + "prost 0.14.3", "tonic", ] @@ -4405,8 +5843,10 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ + "async-compression", "bitflags 2.11.0", "bytes", + "futures-core", "futures-util", "http 1.4.0", "http-body", @@ -4414,6 +5854,7 @@ dependencies = [ "iri-string", "pin-project-lite", "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -4450,7 +5891,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -4460,6 +5901,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", ] [[package]] @@ -4473,9 +5926,11 @@ dependencies = [ "once_cell", "regex-automata", "sharded-slab", + "smallvec", "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] @@ -4548,6 +6003,21 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "typewit" +version = "1.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" + [[package]] name = "udev" version = "0.8.0" @@ -4566,6 +6036,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca77d41ab27e3fa45df42043f96c79b80c6d8632eed906b54681d8d47ab00623" +[[package]] +name = "ulid" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" +dependencies = [ + "rand 0.9.2", + "web-time", +] + [[package]] name = "unescaper" version = "0.1.8" @@ -4699,9 +6179,16 @@ checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ "getrandom 0.4.1", "js-sys", + "serde_core", "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" @@ -4720,6 +6207,36 @@ version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" +[[package]] +name = "vodozemac" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c022a277687e4e8685d72b95a7ca3ccfec907daa946678e715f8badaa650883d" +dependencies = [ + "aes", + "arrayvec", + "base64", + "base64ct", + "cbc", + "chacha20poly1305", + "curve25519-dalek", + "ed25519-dalek", + "getrandom 0.2.17", + "hkdf", + "hmac", + "matrix-pickle", + "prost 0.13.5", + "rand 0.8.5", + "serde", + "serde_bytes", + "serde_json", + "sha2", + "subtle", + "thiserror 2.0.18", + "x25519-dalek", + "zeroize", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -4827,7 +6344,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.116", "wasm-bindgen-shared", ] @@ -4875,6 +6392,24 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm_evt_listener" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc92d6378b411ed94839112a36d9dbc77143451d85b05dfb0cce93a78dab1963" +dependencies = [ + "accessory", + "derivative", + "derive_more 1.0.0", + "fancy_constructor", + "futures-core", + "js-sys", + "smallvec", + "tokio", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -4904,9 +6439,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", + "serde", "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" +dependencies = [ + "phf 0.11.3", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "webdriver" version = "0.53.0" @@ -4964,6 +6512,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wildmatch" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29333c3ea1ba8b17211763463ff24ee84e41c78224c16b001cd907e663a38c68" + [[package]] name = "winapi" version = "0.3.9" @@ -5016,7 +6570,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -5027,7 +6581,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -5333,7 +6887,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.116", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -5349,7 +6903,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.116", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -5412,6 +6966,24 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yoke" version = "0.7.5" @@ -5443,7 +7015,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", "synstructure", ] @@ -5455,7 +7027,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", "synstructure", ] @@ -5488,6 +7060,7 @@ dependencies = [ "landlock", "lettre", "mail-parser", + "matrix-sdk", "nusb 0.2.1", "opentelemetry", "opentelemetry-otlp", @@ -5497,7 +7070,7 @@ dependencies = [ "postgres", "probe-rs", "prometheus", - "prost", + "prost 0.14.3", "rand 0.9.2", "regex", "reqwest", @@ -5565,7 +7138,7 @@ checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -5585,7 +7158,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", "synstructure", ] @@ -5594,6 +7167,20 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] [[package]] name = "zerotrie" @@ -5636,7 +7223,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -5647,7 +7234,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index aa9c245..7b133ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,9 @@ tokio-util = { version = "0.7", default-features = false } # HTTP client - minimal features reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "blocking", "multipart", "stream", "socks"] } +# Matrix client + E2EE decryption +matrix-sdk = { version = "0.16", default-features = false, features = ["e2e-encryption", "rustls-tls"] } + # Serialization serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false, features = ["std"] } @@ -81,7 +84,7 @@ ring = "0.17" prost = { version = "0.14", default-features = false } # Memory / persistence -rusqlite = { version = "0.38", features = ["bundled"] } +rusqlite = { version = "0.37", features = ["bundled"] } postgres = { version = "0.19", features = ["with-chrono-0_4"] } chrono = { version = "0.4", default-features = false, features = ["clock", "std", "serde"] } chrono-tz = "0.10" diff --git a/README.md b/README.md index 4065d08..03ed554 100644 --- a/README.md +++ b/README.md @@ -401,6 +401,8 @@ Inbound sender policy is now consistent: This keeps accidental exposure low by default. +Full channel configuration reference: [docs/channels-reference.md](docs/channels-reference.md). + Recommended low-friction setup (secure + fast): - **Telegram:** allowlist your own `@username` (without `@`) and/or your numeric Telegram user ID. @@ -834,9 +836,11 @@ Start from the docs hub for a task-based map: Core collaboration references: -- Documentation index: [docs/README.md](docs/README.md) +- Documentation hub: [docs/README.md](docs/README.md) - Documentation template: [docs/doc-template.md](docs/doc-template.md) - Documentation change checklist: [docs/README.md#4-documentation-change-checklist](docs/README.md#4-documentation-change-checklist) +- Channel configuration reference: [docs/channels-reference.md](docs/channels-reference.md) +- Matrix encrypted-room operations: [docs/matrix-e2ee-guide.md](docs/matrix-e2ee-guide.md) - Contribution guide: [CONTRIBUTING.md](CONTRIBUTING.md) - PR workflow policy: [docs/pr-workflow.md](docs/pr-workflow.md) - Reviewer playbook (triage + deep review): [docs/reviewer-playbook.md](docs/reviewer-playbook.md) @@ -867,7 +871,7 @@ We're building in the open because the best ideas come from everywhere. If you'r ## License -MIT — see [LICENSE](LICENSE) and [NOTICE](NOTICE) for contributor attribution +MIT — see [LICENSE](LICENSE) for license terms and attribution baseline ## Contributing diff --git a/docs/README.md b/docs/README.md index 8806f1f..92d7b09 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,6 +16,7 @@ Localized hubs: [简体中文](README.zh-CN.md) · [日本語](README.ja.md) · | Check config defaults and keys quickly | [config-reference.md](config-reference.md) | | Operate runtime (day-2 runbook) | [operations-runbook.md](operations-runbook.md) | | Troubleshoot install/runtime/channel issues | [troubleshooting.md](troubleshooting.md) | +| Run Matrix encrypted-room setup and diagnostics | [matrix-e2ee-guide.md](matrix-e2ee-guide.md) | | Browse docs by category | [SUMMARY.md](SUMMARY.md) | | See project PR/issue docs snapshot | [project-triage-snapshot-2026-02-18.md](project-triage-snapshot-2026-02-18.md) | @@ -47,6 +48,7 @@ Localized hubs: [简体中文](README.zh-CN.md) · [日本語](README.ja.md) · - [commands-reference.md](commands-reference.md) — command lookup by workflow - [providers-reference.md](providers-reference.md) — provider IDs, aliases, credential env vars - [channels-reference.md](channels-reference.md) — channel capabilities and setup paths +- [matrix-e2ee-guide.md](matrix-e2ee-guide.md) — Matrix encrypted-room (E2EE) setup and no-response diagnostics - [config-reference.md](config-reference.md) — high-signal config keys and secure defaults - [operations-runbook.md](operations-runbook.md) — day-2 runtime operations and rollback flow - [troubleshooting.md](troubleshooting.md) — common failure signatures and recovery steps diff --git a/docs/channels-reference.md b/docs/channels-reference.md index 31ed8ef..e0cdb54 100644 --- a/docs/channels-reference.md +++ b/docs/channels-reference.md @@ -1,18 +1,41 @@ -# ZeroClaw Channels Reference +# Channels Reference -This reference maps channel capabilities, config blocks, allowlist behavior, and setup paths. +This document is the canonical reference for channel configuration in ZeroClaw. -Last verified: **February 18, 2026**. +For encrypted Matrix rooms, also read the dedicated runbook: +- [Matrix E2EE Guide](./matrix-e2ee-guide.md) -## Quick Commands +## Quick Paths -```bash -zeroclaw channel list -zeroclaw channel start -zeroclaw channel doctor -zeroclaw channel bind-telegram +- Need a full config reference by channel: jump to [Per-Channel Config Examples](#4-per-channel-config-examples). +- Need a no-response diagnosis flow: jump to [Troubleshooting Checklist](#6-troubleshooting-checklist). +- Need Matrix encrypted-room help: use [Matrix E2EE Guide](./matrix-e2ee-guide.md). +- Need deployment/network assumptions (polling vs webhook): use [Network Deployment](./network-deployment.md). + +## FAQ: Matrix setup passes but no reply + +This is the most common symptom (same class as issue #499). Check these in order: + +1. **Allowlist mismatch**: `allowed_users` does not include the sender (or is empty). +2. **Wrong room target**: bot is not joined to the configured `room_id` / alias target room. +3. **Token/account mismatch**: token is valid but belongs to another Matrix account. +4. **E2EE device identity gap**: `whoami` does not return `device_id` and config does not provide one. +5. **Key sharing/trust gap**: room keys were not shared to the bot device, so encrypted events cannot be decrypted. +6. **Stale runtime state**: config changed but `zeroclaw daemon` was not restarted. + +--- + +## 1. Configuration Namespace + +All channel settings live under `channels_config` in `~/.zeroclaw/config.toml`. + +```toml +[channels_config] +cli = true ``` +Each channel is enabled by creating its sub-table (for example, `[channels_config.telegram]`). + ## In-Chat Runtime Model Switching (Telegram / Discord) When running `zeroclaw channel start` (or daemon mode), Telegram and Discord now support sender-scoped runtime switching: @@ -30,91 +53,287 @@ Notes: ## Channel Matrix -| Channel | Config section | Access control field | Setup path | -|---|---|---|---| -| `CLI` | n/a (always enabled) | n/a | Built-in | -| `Telegram` | `[channels_config.telegram]` | `allowed_users` | `zeroclaw onboard` | -| `Discord` | `[channels_config.discord]` | `allowed_users` | `zeroclaw onboard` | -| `Slack` | `[channels_config.slack]` | `allowed_users` | `zeroclaw onboard` | -| `Mattermost` | `[channels_config.mattermost]` | `allowed_users` | Manual config | -| `Webhook` | `[channels_config.webhook]` | n/a (`secret` optional) | `zeroclaw onboard` or manual | -| `iMessage` | `[channels_config.imessage]` | `allowed_contacts` | `zeroclaw onboard` (macOS) | -| `Matrix` | `[channels_config.matrix]` | `allowed_users` | `zeroclaw onboard` | -| `Signal` | `[channels_config.signal]` | `allowed_from` | Manual config | -| `WhatsApp` | `[channels_config.whatsapp]` | `allowed_numbers` | `zeroclaw onboard` | -| `Email` | `[channels_config.email]` | `allowed_senders` | Manual config | -| `IRC` | `[channels_config.irc]` | `allowed_users` | `zeroclaw onboard` | -| `Lark` | `[channels_config.lark]` | `allowed_users` | Manual config | -| `DingTalk` | `[channels_config.dingtalk]` | `allowed_users` | `zeroclaw onboard` | -| `QQ` | `[channels_config.qq]` | `allowed_users` | `zeroclaw onboard` | +--- -## Deny-by-Default Rules +## 2. Delivery Modes at a Glance -For channel allowlists, the runtime behavior is intentionally strict: +| Channel | Receive mode | Public inbound port required? | +|---|---|---| +| CLI | local stdin/stdout | No | +| Telegram | polling | No | +| Discord | gateway/websocket | No | +| Slack | events API | No (token-based channel flow) | +| Mattermost | polling | No | +| Matrix | sync API (supports E2EE) | No | +| Signal | signal-cli HTTP bridge | No (local bridge endpoint) | +| WhatsApp | webhook | Yes (public HTTPS callback) | +| Webhook | gateway endpoint (`/webhook`) | Usually yes | +| Email | IMAP polling + SMTP send | No | +| IRC | IRC socket | No | +| Lark/Feishu | websocket (default) or webhook | Webhook mode only | +| DingTalk | stream mode | No | +| QQ | bot gateway | No | +| iMessage | local integration | No | -- Empty allowlist (`[]`) means **deny all**. -- Wildcard (`["*"]`) means **allow all**. -- Explicit IDs are exact matches unless channel-specific docs state otherwise. +--- -### Telegram pairing bootstrap +## 3. Allowlist Semantics -Telegram has a secure bootstrap flow: +For channels with inbound sender allowlists: -- Keep `allowed_users = []` to start in pairing mode. -- Run `zeroclaw channel bind-telegram ` to add one identity safely. -- After binding, restart long-running channel processes if needed (`daemon` / `channel start`). +- Empty allowlist: deny all inbound messages. +- `"*"`: allow all inbound senders (use for temporary verification only). +- Explicit list: allow only listed senders. -## Minimal Config Examples +Field names differ by channel: -### Telegram +- `allowed_users` (Telegram/Discord/Slack/Mattermost/Matrix/IRC/Lark/DingTalk/QQ) +- `allowed_from` (Signal) +- `allowed_numbers` (WhatsApp) +- `allowed_senders` (Email) +- `allowed_contacts` (iMessage) + +--- + +## 4. Per-Channel Config Examples + +### 4.1 Telegram ```toml [channels_config.telegram] -bot_token = "123456:ABCDEF" -allowed_users = [] +bot_token = "123456:telegram-token" +allowed_users = ["*"] ``` -### WhatsApp +### 4.2 Discord ```toml -[channels_config.whatsapp] -access_token = "EAABx..." -phone_number_id = "123456789012345" -verify_token = "your-verify-token" -allowed_numbers = ["+1234567890"] +[channels_config.discord] +bot_token = "discord-bot-token" +guild_id = "123456789012345678" # optional +allowed_users = ["*"] +listen_to_bots = false +mention_only = false ``` -### Signal +### 4.3 Slack + +```toml +[channels_config.slack] +bot_token = "xoxb-..." +app_token = "xapp-..." # optional +channel_id = "C1234567890" # optional +allowed_users = ["*"] +``` + +### 4.4 Mattermost + +```toml +[channels_config.mattermost] +url = "https://mm.example.com" +bot_token = "mattermost-token" +channel_id = "channel-id" # required for listening +allowed_users = ["*"] +``` + +### 4.5 Matrix + +```toml +[channels_config.matrix] +homeserver = "https://matrix.example.com" +access_token = "syt_..." +user_id = "@zeroclaw:matrix.example.com" # optional, recommended for E2EE +device_id = "DEVICEID123" # optional, recommended for E2EE +room_id = "!room:matrix.example.com" # or room alias (#ops:matrix.example.com) +allowed_users = ["*"] +``` + +See [Matrix E2EE Guide](./matrix-e2ee-guide.md) for encrypted-room troubleshooting. + +### 4.6 Signal ```toml [channels_config.signal] http_url = "http://127.0.0.1:8686" account = "+1234567890" -allowed_from = ["+1987654321"] -ignore_attachments = true +group_id = "dm" # optional: "dm" / group id / omitted +allowed_from = ["*"] +ignore_attachments = false ignore_stories = true ``` -### Lark +### 4.7 WhatsApp + +```toml +[channels_config.whatsapp] +access_token = "EAAB..." +phone_number_id = "123456789012345" +verify_token = "your-verify-token" +app_secret = "your-app-secret" # optional but recommended +allowed_numbers = ["*"] +``` + +### 4.8 Webhook Channel Config (Gateway) + +`channels_config.webhook` enables webhook-specific gateway behavior. + +```toml +[channels_config.webhook] +port = 8080 +secret = "optional-shared-secret" +``` + +Run with gateway/daemon and verify `/health`. + +### 4.9 Email + +```toml +[channels_config.email] +imap_host = "imap.example.com" +imap_port = 993 +imap_folder = "INBOX" +smtp_host = "smtp.example.com" +smtp_port = 465 +smtp_tls = true +username = "bot@example.com" +password = "email-password" +from_address = "bot@example.com" +poll_interval_secs = 60 +allowed_senders = ["*"] +``` + +### 4.10 IRC + +```toml +[channels_config.irc] +server = "irc.libera.chat" +port = 6697 +nickname = "zeroclaw-bot" +username = "zeroclaw" # optional +channels = ["#zeroclaw"] +allowed_users = ["*"] +server_password = "" # optional +nickserv_password = "" # optional +sasl_password = "" # optional +verify_tls = true +``` + +### 4.11 Lark / Feishu ```toml [channels_config.lark] app_id = "cli_xxx" app_secret = "xxx" -allowed_users = ["ou_abc"] -receive_mode = "websocket" # or "webhook" -# port = 3100 # required only when receive_mode = "webhook" +encrypt_key = "" # optional +verification_token = "" # optional +allowed_users = ["*"] +use_feishu = false +receive_mode = "websocket" # or "webhook" +port = 8081 # required for webhook mode ``` -## Operational Notes +### 4.12 DingTalk -- `zeroclaw channel add/remove` is intentionally not a full config mutator yet; use `zeroclaw onboard` or edit `~/.zeroclaw/config.toml`. -- `zeroclaw channel doctor` validates configured channel health and prints timeout/unhealthy status. -- If `webhook` is configured, doctor guidance points to gateway health check (`GET /health`). +```toml +[channels_config.dingtalk] +client_id = "ding-app-key" +client_secret = "ding-app-secret" +allowed_users = ["*"] +``` -## Related Docs +### 4.13 QQ + +```toml +[channels_config.qq] +app_id = "qq-app-id" +app_secret = "qq-app-secret" +allowed_users = ["*"] +``` + +### 4.14 iMessage + +```toml +[channels_config.imessage] +allowed_contacts = ["*"] +``` + +--- + +## 5. Validation Workflow + +1. Configure one channel with permissive allowlist (`"*"`) for initial verification. +2. Run: + +```bash +zeroclaw onboard --channels-only +zeroclaw daemon +``` + +3. Send a message from an expected sender. +4. Confirm a reply arrives. +5. Tighten allowlist from `"*"` to explicit IDs. + +--- + +## 6. Troubleshooting Checklist + +If a channel appears connected but does not respond: + +1. Confirm the sender identity is allowed by the correct allowlist field. +2. Confirm bot account membership/permissions in target room/channel. +3. Confirm tokens/secrets are valid (and not expired/revoked). +4. Confirm transport mode assumptions: + - polling/websocket channels do not need public inbound HTTP + - webhook channels do need reachable HTTPS callback +5. Restart `zeroclaw daemon` after config changes. + +For Matrix encrypted rooms specifically, use: +- [Matrix E2EE Guide](./matrix-e2ee-guide.md) + +--- + +## 7. Operations Appendix: Log Keywords Matrix + +Use this appendix for fast triage. Match log keywords first, then follow the troubleshooting steps above. + +### 7.1 Recommended capture command + +```bash +RUST_LOG=info zeroclaw daemon 2>&1 | tee /tmp/zeroclaw.log +``` + +Then filter channel/gateway events: + +```bash +rg -n "Matrix|Telegram|Discord|Slack|Mattermost|Signal|WhatsApp|Email|IRC|Lark|DingTalk|QQ|iMessage|Webhook|Channel" /tmp/zeroclaw.log +``` + +### 7.2 Keyword table + +| Component | Startup / healthy signal | Authorization / policy signal | Transport / failure signal | +|---|---|---|---| +| Telegram | `Telegram channel listening for messages...` | `Telegram: ignoring message from unauthorized user:` | `Telegram poll error:` / `Telegram parse error:` / `Telegram polling conflict (409):` | +| Discord | `Discord: connected and identified` | `Discord: ignoring message from unauthorized user:` | `Discord: received Reconnect (op 7)` / `Discord: received Invalid Session (op 9)` | +| Slack | `Slack channel listening on #` | `Slack: ignoring message from unauthorized user:` | `Slack poll error:` / `Slack parse error:` | +| Mattermost | `Mattermost channel listening on` | `Mattermost: ignoring message from unauthorized user:` | `Mattermost poll error:` / `Mattermost parse error:` | +| Matrix | `Matrix channel listening on room` / `Matrix room ... is encrypted; E2EE decryption is enabled via matrix-sdk.` | `Matrix whoami failed; falling back to configured session hints for E2EE session restore:` / `Matrix whoami failed while resolving listener user_id; using configured user_id hint:` | `Matrix sync error: ... retrying...` | +| Signal | `Signal channel listening via SSE on` | (allowlist checks are enforced by `allowed_from`) | `Signal SSE returned ...` / `Signal SSE connect error:` | +| WhatsApp (channel) | `WhatsApp channel active (webhook mode).` | `WhatsApp: ignoring message from unauthorized number:` | `WhatsApp send failed:` | +| Webhook / WhatsApp (gateway) | `WhatsApp webhook verified successfully` | `Webhook: rejected — not paired / invalid bearer token` / `Webhook: rejected request — invalid or missing X-Webhook-Secret` / `WhatsApp webhook verification failed — token mismatch` | `Webhook JSON parse error:` | +| Email | `Email polling every ...` / `Email sent to ...` | `Blocked email from ...` | `Email poll failed:` / `Email poll task panicked:` | +| IRC | `IRC channel connecting to ...` / `IRC registered as ...` | (allowlist checks are enforced by `allowed_users`) | `IRC SASL authentication failed (...)` / `IRC server does not support SASL...` / `IRC nickname ... is in use, trying ...` | +| Lark / Feishu | `Lark: WS connected` / `Lark event callback server listening on` | `Lark WS: ignoring ... (not in allowed_users)` / `Lark: ignoring message from unauthorized user:` | `Lark: ping failed, reconnecting` / `Lark: heartbeat timeout, reconnecting` / `Lark: WS read error:` | +| DingTalk | `DingTalk: connected and listening for messages...` | `DingTalk: ignoring message from unauthorized user:` | `DingTalk WebSocket error:` / `DingTalk: message channel closed` | +| QQ | `QQ: connected and identified` | `QQ: ignoring C2C message from unauthorized user:` / `QQ: ignoring group message from unauthorized user:` | `QQ: received Reconnect (op 7)` / `QQ: received Invalid Session (op 9)` / `QQ: message channel closed` | +| iMessage | `iMessage channel listening (AppleScript bridge)...` | (contact allowlist enforced by `allowed_contacts`) | `iMessage poll error:` | + +### 7.3 Runtime supervisor keywords + +If a specific channel task crashes or exits, the channel supervisor in `channels/mod.rs` emits: + +- `Channel exited unexpectedly; restarting` +- `Channel error: ...; restarting` +- `Channel message worker crashed:` + +These messages indicate automatic restart behavior is active, and you should inspect preceding logs for root cause. -- [README.md (Channel allowlists)](../README.md#channel-allowlists-deny-by-default) -- [network-deployment.md](network-deployment.md) -- [mattermost-setup.md](mattermost-setup.md) -- [commands-reference.md](commands-reference.md) diff --git a/docs/matrix-e2ee-guide.md b/docs/matrix-e2ee-guide.md new file mode 100644 index 0000000..e927410 --- /dev/null +++ b/docs/matrix-e2ee-guide.md @@ -0,0 +1,133 @@ +# Matrix E2EE Guide + +This guide explains how to run ZeroClaw reliably in Matrix rooms, including end-to-end encrypted (E2EE) rooms. + +It focuses on the common failure mode reported by users: + +> “Matrix is configured correctly, checks pass, but the bot does not respond.” + +## 0. Fast FAQ (#499-class symptom) + +If Matrix appears connected but there is no reply, validate these first: + +1. Sender is allowed by `allowed_users` (for testing: `["*"]`). +2. Bot account has joined the exact target room. +3. Token belongs to the same bot account (`whoami` check). +4. Encrypted room has usable device identity (`device_id`) and key sharing. +5. Daemon is restarted after config changes. + +--- + +## 1. Requirements + +Before testing message flow, make sure all of the following are true: + +1. The bot account is joined to the target room. +2. The access token belongs to the same bot account. +3. `room_id` is correct: + - preferred: canonical room ID (`!room:server`) + - supported: room alias (`#alias:server`) and ZeroClaw will resolve it +4. `allowed_users` allows the sender (`["*"]` for open testing). +5. For E2EE rooms, the bot device has received encryption keys for the room. + +--- + +## 2. Configuration + +Use `~/.zeroclaw/config.toml`: + +```toml +[channels_config.matrix] +homeserver = "https://matrix.example.com" +access_token = "syt_your_token" + +# Optional but recommended for E2EE stability: +user_id = "@zeroclaw:matrix.example.com" +device_id = "DEVICEID123" + +# Room ID or alias +room_id = "!xtHhdHIIVEZbDPvTvZ:matrix.example.com" +# room_id = "#ops:matrix.example.com" + +# Use ["*"] during initial verification, then tighten. +allowed_users = ["*"] +``` + +### About `user_id` and `device_id` + +- ZeroClaw attempts to read identity from Matrix `/_matrix/client/v3/account/whoami`. +- If `whoami` does not return `device_id`, set `device_id` manually. +- These hints are especially important for E2EE session restore. + +--- + +## 3. Quick Validation Flow + +1. Run channel setup and daemon: + +```bash +zeroclaw onboard --channels-only +zeroclaw daemon +``` + +2. Send a plain text message in the configured Matrix room. + +3. Confirm ZeroClaw logs contain Matrix listener startup and no repeated sync/auth errors. + +4. In an encrypted room, verify the bot can read and reply to encrypted messages from allowed users. + +--- + +## 4. Troubleshooting “No Response” + +Use this checklist in order. + +### A. Room and membership + +- Ensure the bot account has joined the room. +- If using alias (`#...`), verify it resolves to the expected canonical room. + +### B. Sender allowlist + +- If `allowed_users = []`, all inbound messages are denied. +- For diagnosis, temporarily set `allowed_users = ["*"]`. + +### C. Token and identity + +- Validate token with: + +```bash +curl -sS -H "Authorization: Bearer $MATRIX_TOKEN" \ + "https://matrix.example.com/_matrix/client/v3/account/whoami" +``` + +- Check that returned `user_id` matches the bot account. +- If `device_id` is missing, set `channels_config.matrix.device_id` manually. + +### D. E2EE-specific checks + +- The bot device must receive room keys from trusted devices. +- If keys are not shared to this device, encrypted events cannot be decrypted. +- Verify device trust and key sharing in your Matrix client/admin workflow. + +### E. Fresh start test + +After updating config, restart daemon and send a new message (not just old timeline history). + +--- + +## 5. Operational Notes + +- Keep Matrix tokens out of logs and screenshots. +- Start with permissive `allowed_users`, then tighten to explicit user IDs. +- Prefer canonical room IDs in production to avoid alias drift. + +--- + +## 6. Related Docs + +- [Channels Reference](./channels-reference.md) +- [Operations log keyword appendix](./channels-reference.md#7-operations-appendix-log-keywords-matrix) +- [Network Deployment](./network-deployment.md) +- [Agnostic Security](./agnostic-security.md) +- [Reviewer Playbook](./reviewer-playbook.md) diff --git a/docs/network-deployment.md b/docs/network-deployment.md index aeae926..28bd99e 100644 --- a/docs/network-deployment.md +++ b/docs/network-deployment.md @@ -9,6 +9,7 @@ This document covers deploying ZeroClaw on a Raspberry Pi or other host on your | Mode | Inbound port needed? | Use case | |------|----------------------|----------| | **Telegram polling** | No | ZeroClaw polls Telegram API; works from anywhere | +| **Matrix sync (including E2EE)** | No | ZeroClaw syncs via Matrix client API; no inbound webhook required | | **Discord/Slack** | No | Same — outbound only | | **Gateway webhook** | Yes | POST /webhook, WhatsApp, etc. need a public URL | | **Gateway pairing** | Yes | If you pair clients via the gateway | @@ -199,5 +200,7 @@ Configure Cloudflare Tunnel to forward to `127.0.0.1:3000`, then set your webhoo ## 7. References +- [channels-reference.md](./channels-reference.md) — Channel configuration overview +- [matrix-e2ee-guide.md](./matrix-e2ee-guide.md) — Matrix setup and encrypted-room troubleshooting - [hardware-peripherals-design.md](./hardware-peripherals-design.md) — Peripherals design - [adding-boards-and-tools.md](./adding-boards-and-tools.md) — Hardware setup and adding boards diff --git a/src/channels/matrix.rs b/src/channels/matrix.rs index 6b1ea2d..fda0e9c 100644 --- a/src/channels/matrix.rs +++ b/src/channels/matrix.rs @@ -1,17 +1,34 @@ use crate::channels::traits::{Channel, ChannelMessage, SendMessage}; use async_trait::async_trait; +use matrix_sdk::{ + authentication::matrix::MatrixSession, + config::SyncSettings, + ruma::{ + events::room::message::{ + MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent, + }, + OwnedRoomId, OwnedUserId, + }, + Client as MatrixSdkClient, LoopCtrl, Room, RoomState, SessionMeta, SessionTokens, +}; use reqwest::Client; use serde::Deserialize; -use tokio::sync::mpsc; +use std::sync::Arc; +use tokio::sync::{mpsc, Mutex, OnceCell, RwLock}; -/// Matrix channel using the Client-Server API (no SDK needed). -/// Connects to any Matrix homeserver (Element, Synapse, etc.). +/// Matrix channel for Matrix Client-Server API. +/// Uses matrix-sdk for reliable sync and encrypted-room decryption. #[derive(Clone)] pub struct MatrixChannel { homeserver: String, access_token: String, room_id: String, allowed_users: Vec, + session_user_id_hint: Option, + session_device_id_hint: Option, + resolved_room_id_cache: Arc>>, + sdk_client: Arc>, + http_client: Client, } #[derive(Debug, Deserialize)] @@ -45,6 +62,8 @@ struct TimelineEvent { event_type: String, sender: String, #[serde(default)] + event_id: Option, + #[serde(default)] content: EventContent, } @@ -59,47 +78,150 @@ struct EventContent { #[derive(Debug, Deserialize)] struct WhoAmIResponse { user_id: String, + #[serde(default)] + device_id: Option, +} + +#[derive(Debug, Deserialize)] +struct RoomAliasResponse { + room_id: String, } impl MatrixChannel { + fn normalize_optional_field(value: Option) -> Option { + value + .map(|entry| entry.trim().to_string()) + .filter(|entry| !entry.is_empty()) + } + pub fn new( homeserver: String, access_token: String, room_id: String, allowed_users: Vec, ) -> Self { - let homeserver = if homeserver.ends_with('/') { - homeserver[..homeserver.len() - 1].to_string() - } else { - homeserver - }; + Self::new_with_session_hint(homeserver, access_token, room_id, allowed_users, None, None) + } + + pub fn new_with_session_hint( + homeserver: String, + access_token: String, + room_id: String, + allowed_users: Vec, + user_id_hint: Option, + device_id_hint: Option, + ) -> Self { + let homeserver = homeserver.trim_end_matches('/').to_string(); + let access_token = access_token.trim().to_string(); + let room_id = room_id.trim().to_string(); + let allowed_users = allowed_users + .into_iter() + .map(|user| user.trim().to_string()) + .filter(|user| !user.is_empty()) + .collect(); + Self { homeserver, access_token, room_id, allowed_users, + session_user_id_hint: Self::normalize_optional_field(user_id_hint), + session_device_id_hint: Self::normalize_optional_field(device_id_hint), + resolved_room_id_cache: Arc::new(RwLock::new(None)), + sdk_client: Arc::new(OnceCell::new()), + http_client: Client::new(), } } - fn http_client(&self) -> Client { - crate::config::build_runtime_proxy_client("channel.matrix") + fn encode_path_segment(value: &str) -> String { + fn should_encode(byte: u8) -> bool { + !matches!( + byte, + b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' + ) + } + + let mut encoded = String::with_capacity(value.len()); + for byte in value.bytes() { + if should_encode(byte) { + use std::fmt::Write; + let _ = write!(&mut encoded, "%{byte:02X}"); + } else { + encoded.push(byte as char); + } + } + + encoded + } + + fn auth_header_value(&self) -> String { + format!("Bearer {}", self.access_token) } fn is_user_allowed(&self, sender: &str) -> bool { - if self.allowed_users.iter().any(|u| u == "*") { - return true; - } - self.allowed_users - .iter() - .any(|u| u.eq_ignore_ascii_case(sender)) + Self::is_sender_allowed(&self.allowed_users, sender) } - async fn get_my_user_id(&self) -> anyhow::Result { + fn is_sender_allowed(allowed_users: &[String], sender: &str) -> bool { + if allowed_users.iter().any(|u| u == "*") { + return true; + } + + allowed_users.iter().any(|u| u.eq_ignore_ascii_case(sender)) + } + + fn is_supported_message_type(msgtype: &str) -> bool { + matches!(msgtype, "m.text" | "m.notice") + } + + fn has_non_empty_body(body: &str) -> bool { + !body.trim().is_empty() + } + + fn cache_event_id( + event_id: &str, + recent_order: &mut std::collections::VecDeque, + recent_lookup: &mut std::collections::HashSet, + ) -> bool { + const MAX_RECENT_EVENT_IDS: usize = 2048; + + if recent_lookup.contains(event_id) { + return true; + } + + let event_id_owned = event_id.to_string(); + recent_lookup.insert(event_id_owned.clone()); + recent_order.push_back(event_id_owned); + + if recent_order.len() > MAX_RECENT_EVENT_IDS { + if let Some(evicted) = recent_order.pop_front() { + recent_lookup.remove(&evicted); + } + } + + false + } + + async fn target_room_id(&self) -> anyhow::Result { + if self.room_id.starts_with('!') { + return Ok(self.room_id.clone()); + } + + if let Some(cached) = self.resolved_room_id_cache.read().await.clone() { + return Ok(cached); + } + + let resolved = self.resolve_room_id().await?; + *self.resolved_room_id_cache.write().await = Some(resolved.clone()); + Ok(resolved) + } + + async fn get_my_identity(&self) -> anyhow::Result { let url = format!("{}/_matrix/client/v3/account/whoami", self.homeserver); let resp = self - .http_client() + .http_client .get(&url) - .header("Authorization", format!("Bearer {}", self.access_token)) + .header("Authorization", self.auth_header_value()) .send() .await?; @@ -108,8 +230,213 @@ impl MatrixChannel { anyhow::bail!("Matrix whoami failed: {err}"); } - let who: WhoAmIResponse = resp.json().await?; - Ok(who.user_id) + Ok(resp.json().await?) + } + + async fn get_my_user_id(&self) -> anyhow::Result { + Ok(self.get_my_identity().await?.user_id) + } + + async fn matrix_client(&self) -> anyhow::Result { + let client = self + .sdk_client + .get_or_try_init(|| async { + let identity = self.get_my_identity().await; + let whoami = match identity { + Ok(whoami) => Some(whoami), + Err(error) => { + if self.session_user_id_hint.is_some() && self.session_device_id_hint.is_some() + { + tracing::warn!( + "Matrix whoami failed; falling back to configured session hints for E2EE session restore: {error}" + ); + None + } else { + return Err(error); + } + } + }; + + let resolved_user_id = if let Some(whoami) = whoami.as_ref() { + if let Some(hinted) = self.session_user_id_hint.as_ref() { + if hinted != &whoami.user_id { + tracing::warn!( + "Matrix configured user_id '{}' does not match whoami '{}'; using whoami.", + hinted, + whoami.user_id + ); + } + } + whoami.user_id.clone() + } else { + self.session_user_id_hint.clone().ok_or_else(|| { + anyhow::anyhow!( + "Matrix session restore requires user_id when whoami is unavailable" + ) + })? + }; + + let resolved_device_id = match (whoami.as_ref(), self.session_device_id_hint.as_ref()) { + (Some(whoami), Some(hinted)) => { + if let Some(whoami_device_id) = whoami.device_id.as_ref() { + if whoami_device_id != hinted { + tracing::warn!( + "Matrix configured device_id '{}' does not match whoami '{}'; using whoami.", + hinted, + whoami_device_id + ); + } + whoami_device_id.clone() + } else { + hinted.clone() + } + } + (Some(whoami), None) => whoami.device_id.clone().ok_or_else(|| { + anyhow::anyhow!( + "Matrix whoami response did not include device_id. Set channels.matrix.device_id to enable E2EE session restore." + ) + })?, + (None, Some(hinted)) => hinted.clone(), + (None, None) => { + return Err(anyhow::anyhow!( + "Matrix E2EE session restore requires device_id when whoami is unavailable" + )); + } + }; + + let client = MatrixSdkClient::builder() + .homeserver_url(&self.homeserver) + .build() + .await?; + + let user_id: OwnedUserId = resolved_user_id.parse()?; + let session = MatrixSession { + meta: SessionMeta { + user_id, + device_id: resolved_device_id.into(), + }, + tokens: SessionTokens { + access_token: self.access_token.clone(), + refresh_token: None, + }, + }; + + client.restore_session(session).await?; + + Ok::(client) + }) + .await?; + + Ok(client.clone()) + } + + async fn resolve_room_id(&self) -> anyhow::Result { + let configured = self.room_id.trim(); + + if configured.starts_with('!') { + return Ok(configured.to_string()); + } + + if configured.starts_with('#') { + let encoded_alias = Self::encode_path_segment(configured); + let url = format!( + "{}/_matrix/client/v3/directory/room/{}", + self.homeserver, encoded_alias + ); + + let resp = self + .http_client + .get(&url) + .header("Authorization", self.auth_header_value()) + .send() + .await?; + + if !resp.status().is_success() { + let err = resp.text().await.unwrap_or_default(); + anyhow::bail!("Matrix room alias resolution failed for '{configured}': {err}"); + } + + let resolved: RoomAliasResponse = resp.json().await?; + return Ok(resolved.room_id); + } + + anyhow::bail!( + "Matrix room reference must start with '!' (room ID) or '#' (room alias), got: {configured}" + ) + } + + async fn ensure_room_accessible(&self, room_id: &str) -> anyhow::Result<()> { + let encoded_room = Self::encode_path_segment(room_id); + let url = format!( + "{}/_matrix/client/v3/rooms/{}/joined_members", + self.homeserver, encoded_room + ); + + let resp = self + .http_client + .get(&url) + .header("Authorization", self.auth_header_value()) + .send() + .await?; + + if !resp.status().is_success() { + let err = resp.text().await.unwrap_or_default(); + anyhow::bail!("Matrix room access check failed for '{room_id}': {err}"); + } + + Ok(()) + } + + async fn room_is_encrypted(&self, room_id: &str) -> anyhow::Result { + let encoded_room = Self::encode_path_segment(room_id); + let url = format!( + "{}/_matrix/client/v3/rooms/{}/state/m.room.encryption", + self.homeserver, encoded_room + ); + + let resp = self + .http_client + .get(&url) + .header("Authorization", self.auth_header_value()) + .send() + .await?; + + if resp.status().is_success() { + return Ok(true); + } + + if resp.status() == reqwest::StatusCode::NOT_FOUND { + return Ok(false); + } + + let err = resp.text().await.unwrap_or_default(); + anyhow::bail!("Matrix room encryption check failed for '{room_id}': {err}"); + } + + async fn ensure_room_supported(&self, room_id: &str) -> anyhow::Result<()> { + self.ensure_room_accessible(room_id).await?; + + if self.room_is_encrypted(room_id).await? { + tracing::info!( + "Matrix room {} is encrypted; E2EE decryption is enabled via matrix-sdk.", + room_id + ); + } + + Ok(()) + } + + fn sync_filter_for_room(room_id: &str, timeline_limit: usize) -> String { + let timeline_limit = timeline_limit.max(1); + serde_json::json!({ + "room": { + "rooms": [room_id], + "timeline": { + "limit": timeline_limit + } + } + }) + .to_string() } } @@ -120,148 +447,157 @@ impl Channel for MatrixChannel { } async fn send(&self, message: &SendMessage) -> anyhow::Result<()> { - let txn_id = format!("zc_{}", chrono::Utc::now().timestamp_millis()); - let url = format!( - "{}/_matrix/client/v3/rooms/{}/send/m.room.message/{}", - self.homeserver, self.room_id, txn_id - ); + let client = self.matrix_client().await?; + let target_room_id = self.target_room_id().await?; + let target_room: OwnedRoomId = target_room_id.parse()?; - let body = serde_json::json!({ - "msgtype": "m.text", - "body": message.content - }); - - let resp = self - .http_client() - .put(&url) - .header("Authorization", format!("Bearer {}", self.access_token)) - .json(&body) - .send() - .await?; - - if !resp.status().is_success() { - let err = resp.text().await?; - anyhow::bail!("Matrix send failed: {err}"); + let mut room = client.get_room(&target_room); + if room.is_none() { + let _ = client.sync_once(SyncSettings::new()).await; + room = client.get_room(&target_room); } + let Some(room) = room else { + anyhow::bail!("Matrix room '{}' not found in joined rooms", target_room_id); + }; + + if room.state() != RoomState::Joined { + anyhow::bail!("Matrix room '{}' is not in joined state", target_room_id); + } + + room.send(RoomMessageEventContent::text_plain(&message.content)) + .await?; + Ok(()) } async fn listen(&self, tx: mpsc::Sender) -> anyhow::Result<()> { - tracing::info!("Matrix channel listening on room {}...", self.room_id); + let target_room_id = self.target_room_id().await?; + self.ensure_room_supported(&target_room_id).await?; - let my_user_id = self.get_my_user_id().await?; + let target_room: OwnedRoomId = target_room_id.parse()?; + let my_user_id: OwnedUserId = match self.get_my_user_id().await { + Ok(user_id) => user_id.parse()?, + Err(error) => { + if let Some(hinted) = self.session_user_id_hint.as_ref() { + tracing::warn!( + "Matrix whoami failed while resolving listener user_id; using configured user_id hint: {error}" + ); + hinted.parse()? + } else { + return Err(error); + } + } + }; + let client = self.matrix_client().await?; - // Initial sync to get the since token - let url = format!( - "{}/_matrix/client/v3/sync?timeout=30000&filter={{\"room\":{{\"timeline\":{{\"limit\":1}}}}}}", - self.homeserver + let _ = client.sync_once(SyncSettings::new()).await; + + tracing::info!( + "Matrix channel listening on room {} (configured as {})...", + target_room_id, + self.room_id ); - let resp = self - .http_client() - .get(&url) - .header("Authorization", format!("Bearer {}", self.access_token)) - .send() + let recent_event_cache = Arc::new(Mutex::new(( + std::collections::VecDeque::new(), + std::collections::HashSet::new(), + ))); + + let tx_handler = tx.clone(); + let target_room_for_handler = target_room.clone(); + let my_user_id_for_handler = my_user_id.clone(); + let allowed_users_for_handler = self.allowed_users.clone(); + let dedupe_for_handler = Arc::clone(&recent_event_cache); + + client.add_event_handler(move |event: OriginalSyncRoomMessageEvent, room: Room| { + let tx = tx_handler.clone(); + let target_room = target_room_for_handler.clone(); + let my_user_id = my_user_id_for_handler.clone(); + let allowed_users = allowed_users_for_handler.clone(); + let dedupe = Arc::clone(&dedupe_for_handler); + + async move { + if room.room_id().as_str() != target_room.as_str() { + return; + } + + if event.sender == my_user_id { + return; + } + + let sender = event.sender.to_string(); + if !MatrixChannel::is_sender_allowed(&allowed_users, &sender) { + return; + } + + let body = match &event.content.msgtype { + MessageType::Text(content) => content.body.clone(), + MessageType::Notice(content) => content.body.clone(), + _ => return, + }; + + if !MatrixChannel::has_non_empty_body(&body) { + return; + } + + let event_id = event.event_id.to_string(); + { + let mut guard = dedupe.lock().await; + let (recent_order, recent_lookup) = &mut *guard; + if MatrixChannel::cache_event_id(&event_id, recent_order, recent_lookup) { + return; + } + } + + let msg = ChannelMessage { + id: event_id, + sender: sender.clone(), + reply_target: sender, + content: body, + channel: "matrix".to_string(), + timestamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(), + }; + + let _ = tx.send(msg).await; + } + }); + + let sync_settings = SyncSettings::new().timeout(std::time::Duration::from_secs(30)); + client + .sync_with_result_callback(sync_settings, |sync_result| { + let tx = tx.clone(); + async move { + if tx.is_closed() { + return Ok::(LoopCtrl::Break); + } + + if let Err(error) = sync_result { + tracing::warn!("Matrix sync error: {error}, retrying..."); + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + } + + Ok::(LoopCtrl::Continue) + } + }) .await?; - if !resp.status().is_success() { - let err = resp.text().await?; - anyhow::bail!("Matrix initial sync failed: {err}"); - } - - let sync: SyncResponse = resp.json().await?; - let mut since = sync.next_batch; - - // Long-poll loop - loop { - let url = format!( - "{}/_matrix/client/v3/sync?since={}&timeout=30000", - self.homeserver, since - ); - - let resp = self - .http_client() - .get(&url) - .header("Authorization", format!("Bearer {}", self.access_token)) - .send() - .await; - - let resp = match resp { - Ok(r) => r, - Err(e) => { - tracing::warn!("Matrix sync error: {e}, retrying..."); - tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; - continue; - } - }; - - if !resp.status().is_success() { - tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; - continue; - } - - let sync: SyncResponse = resp.json().await?; - since = sync.next_batch; - - // Process events from our room - if let Some(room) = sync.rooms.join.get(&self.room_id) { - for event in &room.timeline.events { - // Skip our own messages - if event.sender == my_user_id { - continue; - } - - // Only process text messages - if event.event_type != "m.room.message" { - continue; - } - - if event.content.msgtype.as_deref() != Some("m.text") { - continue; - } - - let Some(ref body) = event.content.body else { - continue; - }; - - if !self.is_user_allowed(&event.sender) { - continue; - } - - let msg = ChannelMessage { - id: format!("mx_{}", chrono::Utc::now().timestamp_millis()), - sender: event.sender.clone(), - reply_target: event.sender.clone(), - content: body.clone(), - channel: "matrix".to_string(), - timestamp: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_secs(), - }; - - if tx.send(msg).await.is_err() { - return Ok(()); - } - } - } - } + Ok(()) } async fn health_check(&self) -> bool { - let url = format!("{}/_matrix/client/v3/account/whoami", self.homeserver); - let Ok(resp) = self - .http_client() - .get(&url) - .header("Authorization", format!("Bearer {}", self.access_token)) - .send() - .await - else { + let Ok(room_id) = self.target_room_id().await else { return false; }; - resp.status().is_success() + if self.ensure_room_supported(&room_id).await.is_err() { + return false; + } + + self.matrix_client().await.is_ok() } } @@ -310,14 +646,139 @@ mod tests { } #[test] - fn multiple_trailing_slashes_strips_one() { + fn multiple_trailing_slashes_strip_all() { let ch = MatrixChannel::new( "https://matrix.org//".to_string(), "tok".to_string(), "!r:m".to_string(), vec![], ); - assert_eq!(ch.homeserver, "https://matrix.org/"); + assert_eq!(ch.homeserver, "https://matrix.org"); + } + + #[test] + fn trims_access_token() { + let ch = MatrixChannel::new( + "https://matrix.org".to_string(), + " syt_test_token ".to_string(), + "!r:m".to_string(), + vec![], + ); + assert_eq!(ch.access_token, "syt_test_token"); + } + + #[test] + fn session_hints_are_normalized() { + let ch = MatrixChannel::new_with_session_hint( + "https://matrix.org".to_string(), + "tok".to_string(), + "!r:m".to_string(), + vec![], + Some(" @bot:matrix.org ".to_string()), + Some(" DEVICE123 ".to_string()), + ); + + assert_eq!(ch.session_user_id_hint.as_deref(), Some("@bot:matrix.org")); + assert_eq!(ch.session_device_id_hint.as_deref(), Some("DEVICE123")); + } + + #[test] + fn empty_session_hints_are_ignored() { + let ch = MatrixChannel::new_with_session_hint( + "https://matrix.org".to_string(), + "tok".to_string(), + "!r:m".to_string(), + vec![], + Some(" ".to_string()), + Some("".to_string()), + ); + + assert!(ch.session_user_id_hint.is_none()); + assert!(ch.session_device_id_hint.is_none()); + } + + #[test] + fn encode_path_segment_encodes_room_refs() { + assert_eq!( + MatrixChannel::encode_path_segment("#ops:matrix.example.com"), + "%23ops%3Amatrix.example.com" + ); + assert_eq!( + MatrixChannel::encode_path_segment("!room:matrix.example.com"), + "%21room%3Amatrix.example.com" + ); + } + + #[test] + fn supported_message_type_detection() { + assert!(MatrixChannel::is_supported_message_type("m.text")); + assert!(MatrixChannel::is_supported_message_type("m.notice")); + assert!(!MatrixChannel::is_supported_message_type("m.image")); + assert!(!MatrixChannel::is_supported_message_type("m.file")); + } + + #[test] + fn body_presence_detection() { + assert!(MatrixChannel::has_non_empty_body("hello")); + assert!(MatrixChannel::has_non_empty_body(" hello ")); + assert!(!MatrixChannel::has_non_empty_body("")); + assert!(!MatrixChannel::has_non_empty_body(" \n\t ")); + } + + #[test] + fn sync_filter_for_room_targets_requested_room() { + let filter = MatrixChannel::sync_filter_for_room("!room:matrix.org", 0); + let value: serde_json::Value = serde_json::from_str(&filter).unwrap(); + + assert_eq!(value["room"]["rooms"][0], "!room:matrix.org"); + assert_eq!(value["room"]["timeline"]["limit"], 1); + } + + #[test] + fn event_id_cache_deduplicates_and_evicts_old_entries() { + let mut recent_order = std::collections::VecDeque::new(); + let mut recent_lookup = std::collections::HashSet::new(); + + assert!(!MatrixChannel::cache_event_id( + "$first:event", + &mut recent_order, + &mut recent_lookup + )); + assert!(MatrixChannel::cache_event_id( + "$first:event", + &mut recent_order, + &mut recent_lookup + )); + + for i in 0..2050 { + let event_id = format!("$event-{i}:matrix"); + MatrixChannel::cache_event_id(&event_id, &mut recent_order, &mut recent_lookup); + } + + assert!(!MatrixChannel::cache_event_id( + "$first:event", + &mut recent_order, + &mut recent_lookup + )); + } + + #[test] + fn trims_room_id_and_allowed_users() { + let ch = MatrixChannel::new( + "https://matrix.org".to_string(), + "tok".to_string(), + " !room:matrix.org ".to_string(), + vec![ + " @user:matrix.org ".to_string(), + " ".to_string(), + "@other:matrix.org".to_string(), + ], + ); + + assert_eq!(ch.room_id, "!room:matrix.org"); + assert_eq!(ch.allowed_users.len(), 2); + assert!(ch.allowed_users.contains(&"@user:matrix.org".to_string())); + assert!(ch.allowed_users.contains(&"@other:matrix.org".to_string())); } #[test] @@ -393,6 +854,7 @@ mod tests { "events": [ { "type": "m.room.message", + "event_id": "$event:matrix.org", "sender": "@user:matrix.org", "content": { "msgtype": "m.text", @@ -410,6 +872,10 @@ mod tests { let room = resp.rooms.join.get("!room:matrix.org").unwrap(); assert_eq!(room.timeline.events.len(), 1); assert_eq!(room.timeline.events[0].sender, "@user:matrix.org"); + assert_eq!( + room.timeline.events[0].event_id.as_deref(), + Some("$event:matrix.org") + ); assert_eq!( room.timeline.events[0].content.body.as_deref(), Some("Hello!") @@ -461,6 +927,62 @@ mod tests { assert!(event.content.msgtype.is_none()); } + #[test] + fn event_content_supports_notice_msgtype() { + let json = r#"{ + "type":"m.room.message", + "sender":"@u:m", + "event_id":"$notice:m", + "content":{"msgtype":"m.notice","body":"Heads up"} + }"#; + let event: TimelineEvent = serde_json::from_str(json).unwrap(); + assert_eq!(event.content.msgtype.as_deref(), Some("m.notice")); + assert_eq!(event.content.body.as_deref(), Some("Heads up")); + assert_eq!(event.event_id.as_deref(), Some("$notice:m")); + } + + #[tokio::test] + async fn invalid_room_reference_fails_fast() { + let ch = MatrixChannel::new( + "https://matrix.org".to_string(), + "tok".to_string(), + "room_without_prefix".to_string(), + vec![], + ); + + let err = ch.resolve_room_id().await.unwrap_err(); + assert!(err + .to_string() + .contains("must start with '!' (room ID) or '#' (room alias)")); + } + + #[tokio::test] + async fn target_room_id_keeps_canonical_room_id_without_lookup() { + let ch = MatrixChannel::new( + "https://matrix.org".to_string(), + "tok".to_string(), + "!canonical:matrix.org".to_string(), + vec![], + ); + + let room_id = ch.target_room_id().await.unwrap(); + assert_eq!(room_id, "!canonical:matrix.org"); + } + + #[tokio::test] + async fn target_room_id_uses_cached_alias_resolution() { + let ch = MatrixChannel::new( + "https://matrix.org".to_string(), + "tok".to_string(), + "#ops:matrix.org".to_string(), + vec![], + ); + + *ch.resolved_room_id_cache.write().await = Some("!cached:matrix.org".to_string()); + let room_id = ch.target_room_id().await.unwrap(); + assert_eq!(room_id, "!cached:matrix.org"); + } + #[test] fn sync_response_missing_rooms_defaults() { let json = r#"{"next_batch":"s0"}"#; diff --git a/src/channels/mod.rs b/src/channels/mod.rs index ab2e8cf..0fff1ec 100644 --- a/src/channels/mod.rs +++ b/src/channels/mod.rs @@ -1351,11 +1351,13 @@ pub async fn doctor_channels(config: Config) -> Result<()> { if let Some(ref mx) = config.channels_config.matrix { channels.push(( "Matrix", - Arc::new(MatrixChannel::new( + Arc::new(MatrixChannel::new_with_session_hint( mx.homeserver.clone(), mx.access_token.clone(), mx.room_id.clone(), mx.allowed_users.clone(), + mx.user_id.clone(), + mx.device_id.clone(), )), )); } @@ -1676,11 +1678,13 @@ pub async fn start_channels(config: Config) -> Result<()> { } if let Some(ref mx) = config.channels_config.matrix { - channels.push(Arc::new(MatrixChannel::new( + channels.push(Arc::new(MatrixChannel::new_with_session_hint( mx.homeserver.clone(), mx.access_token.clone(), mx.room_id.clone(), mx.allowed_users.clone(), + mx.user_id.clone(), + mx.device_id.clone(), ))); } diff --git a/src/config/schema.rs b/src/config/schema.rs index 24a0a00..85c7e60 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -2091,6 +2091,10 @@ pub struct IMessageConfig { pub struct MatrixConfig { pub homeserver: String, pub access_token: String, + #[serde(default)] + pub user_id: Option, + #[serde(default)] + pub device_id: Option, pub room_id: String, pub allowed_users: Vec, } @@ -3621,6 +3625,8 @@ tool_dispatcher = "xml" let mc = MatrixConfig { homeserver: "https://matrix.org".into(), access_token: "syt_token_abc".into(), + user_id: Some("@bot:matrix.org".into()), + device_id: Some("DEVICE123".into()), room_id: "!room123:matrix.org".into(), allowed_users: vec!["@user:matrix.org".into()], }; @@ -3628,6 +3634,8 @@ tool_dispatcher = "xml" let parsed: MatrixConfig = serde_json::from_str(&json).unwrap(); assert_eq!(parsed.homeserver, "https://matrix.org"); assert_eq!(parsed.access_token, "syt_token_abc"); + assert_eq!(parsed.user_id.as_deref(), Some("@bot:matrix.org")); + assert_eq!(parsed.device_id.as_deref(), Some("DEVICE123")); assert_eq!(parsed.room_id, "!room123:matrix.org"); assert_eq!(parsed.allowed_users.len(), 1); } @@ -3637,6 +3645,8 @@ tool_dispatcher = "xml" let mc = MatrixConfig { homeserver: "https://synapse.local:8448".into(), access_token: "tok".into(), + user_id: None, + device_id: None, room_id: "!abc:synapse.local".into(), allowed_users: vec!["@admin:synapse.local".into(), "*".into()], }; @@ -3646,6 +3656,21 @@ tool_dispatcher = "xml" assert_eq!(parsed.allowed_users.len(), 2); } + #[test] + fn matrix_config_backward_compatible_without_session_hints() { + let toml = r#" +homeserver = "https://matrix.org" +access_token = "tok" +room_id = "!ops:matrix.org" +allowed_users = ["@ops:matrix.org"] +"#; + + let parsed: MatrixConfig = toml::from_str(toml).unwrap(); + assert_eq!(parsed.homeserver, "https://matrix.org"); + assert!(parsed.user_id.is_none()); + assert!(parsed.device_id.is_none()); + } + #[test] fn signal_config_serde() { let sc = SignalConfig { @@ -3709,6 +3734,8 @@ tool_dispatcher = "xml" matrix: Some(MatrixConfig { homeserver: "https://m.org".into(), access_token: "tok".into(), + user_id: None, + device_id: None, room_id: "!r:m".into(), allowed_users: vec!["@u:m".into()], }), diff --git a/src/integrations/registry.rs b/src/integrations/registry.rs index 615ace0..cc91082 100644 --- a/src/integrations/registry.rs +++ b/src/integrations/registry.rs @@ -836,6 +836,8 @@ mod tests { config.channels_config.matrix = Some(MatrixConfig { homeserver: "https://m.org".into(), access_token: "tok".into(), + user_id: None, + device_id: None, room_id: "!r:m".into(), allowed_users: vec![], }); diff --git a/src/onboard/wizard.rs b/src/onboard/wizard.rs index 6ddd225..5a162d8 100644 --- a/src/onboard/wizard.rs +++ b/src/onboard/wizard.rs @@ -3015,14 +3015,44 @@ fn setup_channels() -> Result { .header("Authorization", format!("Bearer {access_token_clone}")) .send()?; let ok = resp.status().is_success(); - Ok::<_, reqwest::Error>(ok) + + if !ok { + return Ok::<_, reqwest::Error>((false, None, None)); + } + + let payload: Value = match resp.json() { + Ok(payload) => payload, + Err(_) => Value::Null, + }; + let user_id = payload + .get("user_id") + .and_then(|value| value.as_str()) + .map(|value| value.to_string()); + let device_id = payload + .get("device_id") + .and_then(|value| value.as_str()) + .map(|value| value.to_string()); + + Ok::<_, reqwest::Error>((true, user_id, device_id)) }) .join(); - match thread_result { - Ok(Ok(true)) => println!( - "\r {} Connection verified ", - style("✅").green().bold() - ), + + let (detected_user_id, detected_device_id) = match thread_result { + Ok(Ok((true, user_id, device_id))) => { + println!( + "\r {} Connection verified ", + style("✅").green().bold() + ); + + if device_id.is_none() { + println!( + " {} Homeserver did not return device_id from whoami. If E2EE decryption fails, set channels.matrix.device_id manually in config.toml.", + style("⚠️").yellow().bold() + ); + } + + (user_id, device_id) + } _ => { println!( "\r {} Connection failed — check homeserver URL and token", @@ -3030,7 +3060,7 @@ fn setup_channels() -> Result { ); continue; } - } + }; let room_id: String = Input::new() .with_prompt(" Room ID (e.g. !abc123:matrix.org)") @@ -3050,6 +3080,8 @@ fn setup_channels() -> Result { config.matrix = Some(MatrixConfig { homeserver: homeserver.trim_end_matches('/').to_string(), access_token, + user_id: detected_user_id, + device_id: detected_device_id, room_id, allowed_users, });