commit fc23911bd3d72fc8bd15d72b7c9b91c4415f07f8 Author: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri Mar 6 10:05:49 2026 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82dea9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/.DS_Store +.DS_Store +/libs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1153dec --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1821 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[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 = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chat-proto" +version = "0.1.0" +source = "git+https://github.com/logos-messaging/chat_proto#44d5360c41d721a011d20ee69a75a85357b33b0e" +dependencies = [ + "prost", +] + +[[package]] +name = "chat-rs" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "base64", + "chrono", + "libchat", + "mpsc", + "serde_json", + "tokio", + "tracing", +] + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto" +version = "0.1.0" +source = "git+https://github.com/logos-messaging/libchat.git#d006f20bcede00447fa4a09a42c0a18718057498" +dependencies = [ + "ed25519-dalek", + "generic-array 1.3.5", + "hkdf", + "rand_core", + "sha2", + "thiserror", + "x25519-dalek", + "xeddsa", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array 0.14.7", + "rand_core", + "typenum", +] + +[[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", + "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.106", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dequemap" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a8b815f6a66eb3d70d9af06b9b6a664d947d5ea4cb3374c63941b9bfc7ca24" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "double-ratchets" +version = "0.0.1" +source = "git+https://github.com/logos-messaging/libchat.git#d006f20bcede00447fa4a09a42c0a18718057498" +dependencies = [ + "blake2", + "chacha20poly1305", + "hkdf", + "rand", + "rand_core", + "safer-ffi", + "serde", + "storage", + "thiserror", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "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", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "ext-trait" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d772df1c1a777963712fb68e014235e80863d6a91a85c4e06ba2d16243a310e5" +dependencies = [ + "ext-trait-proc_macros", +] + +[[package]] +name = "ext-trait-proc_macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab7934152eaf26aa5aa9f7371408ad5af4c31357073c9e84c3b9d7f11ad639a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "extension-traits" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a296e5a895621edf9fa8329c83aa1cb69a964643e36cf54d8d7a69b789089537" +dependencies = [ + "ext-trait", +] + +[[package]] +name = "extern-c" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320bea982e85d42441eb25c49b41218e7eaa2657e8f90bc4eca7437376751e23" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "rustversion", + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libchat" +version = "0.1.0" +source = "git+https://github.com/logos-messaging/libchat.git#d006f20bcede00447fa4a09a42c0a18718057498" +dependencies = [ + "base64", + "blake2", + "chat-proto", + "crypto", + "double-ratchets", + "hex", + "prost", + "rand_core", + "safer-ffi", + "thiserror", + "x25519-dalek", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa" +dependencies = [ + "cc", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "macro_rules_attribute" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf0c9b980bf4f3a37fd7b1c066941dd1b1d0152ce6ee6e8fe8c49b9f6810d862" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58093314a45e00c77d5c508f76e77c3396afbbc0d01506e7fae47b018bac2b1d" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mpsc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09063ffbeb8d9545183d05bd5097ec8f0c7a0d6f81638bbbddfadee10ec0229f" +dependencies = [ + "ahash", + "crossbeam-queue", + "dashmap", + "futures", + "log", + "parking_lot", + "queue-ext", + "rand", + "std-ext", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-src" +version = "300.5.5+3.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "queue-ext" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120b0b73691c04d1b3895c9c1131b1e553f0f93f09ffe2ba594992db585624c8" +dependencies = [ + "futures", + "log", + "pin-project", + "pin-project-lite", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rusqlite" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "safer-ffi" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435fdd58b61a6f1d8545274c1dfa458e905ff68c166e65e294a0130ef5e675bd" +dependencies = [ + "extern-c", + "libc", + "macro_rules_attribute", + "paste", + "safer_ffi-proc_macros", + "scopeguard", + "stabby", + "uninit", + "unwind_safe", + "with_builtin_macros", +] + +[[package]] +name = "safer_ffi-proc_macros" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f25be5ba5f319542edb31925517e0380245ae37df50a9752cdbc05ef948156" +dependencies = [ + "macro_rules_attribute", + "prettyplease", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stabby" +version = "36.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b7e94eaf470c2e76b5f15fb2fb49714471a36cc512df5ee231e62e82ec79f8" +dependencies = [ + "rustversion", + "stabby-abi", +] + +[[package]] +name = "stabby-abi" +version = "36.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc7a63b8276b54e51bfffe3d85da56e7906b2dcfcb29018a8ab666c06734c1a" +dependencies = [ + "rustc_version", + "rustversion", + "sha2-const-stable", + "stabby-macros", +] + +[[package]] +name = "stabby-macros" +version = "36.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eecb7ec5611ec93ec79d120fbe55f31bea234dc1bed1001d4a071bb688651615" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "rand", + "syn 1.0.109", +] + +[[package]] +name = "std-ext" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1842963116222c15799928d092cbd4db7f4e471e20c41b966ee35b1c72b71a" +dependencies = [ + "dequemap", +] + +[[package]] +name = "storage" +version = "0.1.0" +source = "git+https://github.com/logos-messaging/libchat.git#d006f20bcede00447fa4a09a42c0a18718057498" +dependencies = [ + "rusqlite", + "thiserror", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "uninit" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e130f2ed46ca5d8ec13c7ff95836827f92f5f5f37fd2b2bf16f33c408d98bb6" +dependencies = [ + "extension-traits", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unwind_safe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0976c77def3f1f75c4ef892a292c31c0bbe9e3d0702c63044d7c76db298171a3" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "with_builtin_macros" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a59d55032495429b87f9d69954c6c8602e4d3f3e0a747a12dea6b0b23de685da" +dependencies = [ + "with_builtin_macros-proc_macros", +] + +[[package]] +name = "with_builtin_macros-proc_macros" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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", + "serde", + "zeroize", +] + +[[package]] +name = "xeddsa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2460c9a9c9d1331ff6801e87badb517faa6b6758e5fb585eb27daf7622c6d5ad" +dependencies = [ + "curve25519-dalek", + "derive_more", + "ed25519", + "ed25519-dalek", + "rand", + "sha2", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8a1cb52 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["chat"] + +# optional but nice +resolver = "2" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..037c1b7 --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +# Config +REPO_URL = https://github.com/logos-messaging/logos-messaging-nim +REPO_DIR = logos-messaging-nim +OUTPUT_DIR = libs +LIBWAKU_NIM_PARAMS ?= --undef:metrics + +# Platform-specific library name +ifeq ($(shell uname),Darwin) + LIB_NAME = libwaku.dylib + # macOS needs SDKROOT for Clang to find and friends. + export SDKROOT ?= $(shell xcrun --show-sdk-path) +else + LIB_NAME = libwaku.so +endif + +.PHONY: all clean setup build copy + +all: setup build copy + +# 1. Setup: Clone and initialize submodules +setup: + @echo "--- [1/3] Checking dependencies ---" + @if ! command -v nim > /dev/null; then \ + echo "Error: Nim is not installed. Please run: brew install nim"; \ + exit 1; \ + fi + @echo "--- Checking repository ---" + @if [ ! -d "$(REPO_DIR)" ]; then \ + echo "Cloning logos-messaging-nim..."; \ + git clone $(REPO_URL) $(REPO_DIR); \ + else \ + echo "Repository exists. Updating..."; \ + cd $(REPO_DIR) && git pull; \ + fi + @echo "--- Initializing Submodules ---" + cd $(REPO_DIR) && git submodule update --init --recursive + +# 2. Build: Use the repo's internal 'make' +build: + @echo "--- [2/3] Building libwaku ---" + @echo "Using SDKROOT: $(SDKROOT)" + @# Update vendored deps + cd $(REPO_DIR) && $(MAKE) update + @# Compile + cd $(REPO_DIR) && $(MAKE) libwaku BUILD_COMMAND="libwakuDynamic $(LIBWAKU_NIM_PARAMS)" + +# 3. Retrieve: Copy the result +copy: + @echo "--- [3/3] Retrieving library ---" + @mkdir -p $(OUTPUT_DIR) + @if [ -f "$(REPO_DIR)/build/$(LIB_NAME)" ]; then \ + cp "$(REPO_DIR)/build/$(LIB_NAME)" "$(OUTPUT_DIR)/$(LIB_NAME)"; \ + else \ + echo "Error: Could not find $(LIB_NAME) in $(REPO_DIR)/build/"; \ + exit 1; \ + fi + @echo "Success! Library located at: ./$(OUTPUT_DIR)/$(LIB_NAME)" + +clean: + rm -rf $(OUTPUT_DIR) + rm -rf $(REPO_DIR) diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..4182800 --- /dev/null +++ b/build.rs @@ -0,0 +1,18 @@ +fn main() -> Result<(), std::io::Error> { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let libs_dir = std::path::Path::new(&manifest_dir).join("libs"); + println!("cargo:rustc-link-search=native={}", libs_dir.display()); + + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + match target_os.as_str() { + "macos" | "linux" => { + println!("cargo:rustc-link-lib=dylib=waku"); + println!("cargo:rustc-link-arg=-Wl,-rpath,{}", libs_dir.display()); + } + other => { + panic!("Unsupported target OS: {other}. Only macOS and Linux are supported."); + } + } + + Ok(()) +} diff --git a/chat/Cargo.toml b/chat/Cargo.toml new file mode 100644 index 0000000..7ae34d0 --- /dev/null +++ b/chat/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "chat-rs" +version = "0.1.0" +edition = "2024" + +[lib] +name = "chat_rs" +path = "src/lib.rs" + +[[bin]] +name = "dev" +path = "src/main.rs" + +[dependencies] +anyhow = "1" +async-trait = "0.1.89" +base64 = "0.22" +chrono = "0.4.42" +mpsc = "0.2.6" +serde_json = "1" +tokio = { version = "1.48.0", features = ["full", "tokio-macros"] } +tracing = "0.1" +libchat = {git = "https://github.com/logos-messaging/libchat.git"} diff --git a/chat/build.rs b/chat/build.rs new file mode 100644 index 0000000..2ab1d28 --- /dev/null +++ b/chat/build.rs @@ -0,0 +1,12 @@ +fn main() { + // libwaku.dylib lives in /libs/ + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let libs_dir = std::path::Path::new(&manifest_dir) + .parent() + .expect("chat package should have a parent directory") + .join("libs"); + + println!("cargo:rustc-link-search=native={}", libs_dir.display()); + println!("cargo:rustc-link-lib=dylib=waku"); + println!("cargo:rustc-link-arg=-Wl,-rpath,{}", libs_dir.display()); +} diff --git a/chat/src/client.rs b/chat/src/client.rs new file mode 100644 index 0000000..e69de29 diff --git a/chat/src/ds.rs b/chat/src/ds.rs new file mode 100644 index 0000000..36508f7 --- /dev/null +++ b/chat/src/ds.rs @@ -0,0 +1,47 @@ +use std::sync::mpsc; + +#[derive(Debug)] +pub enum DeliveryServiceError { + WakuNodeAlreadyInitialized(String), + WakuPublishMessageError(String), + Other(anyhow::Error), +} + +impl std::fmt::Display for DeliveryServiceError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::WakuNodeAlreadyInitialized(s) => write!(f, "waku node already initialized: {s}"), + Self::WakuPublishMessageError(s) => write!(f, "waku publish error: {s}"), + Self::Other(e) => write!(f, "{e}"), + } + } +} + +impl std::error::Error for DeliveryServiceError {} + +pub mod transport { + use std::sync::mpsc; + + use super::DeliveryServiceError; + + #[derive(Clone)] + pub struct InboundPacket { + pub payload: Vec, + pub subtopic: String, + pub group_id: String, + pub app_id: Vec, + pub timestamp: i64, + } + + pub struct OutboundPacket { + pub group_id: String, + pub subtopic: String, + pub payload: Vec, + pub app_id: Vec, + } + + pub trait DeliveryService { + fn send(&self, pkt: OutboundPacket) -> Result; + fn subscribe(&self) -> mpsc::Receiver; + } +} diff --git a/chat/src/lib.rs b/chat/src/lib.rs new file mode 100644 index 0000000..762514f --- /dev/null +++ b/chat/src/lib.rs @@ -0,0 +1,6 @@ +pub mod client; +pub mod ds; +pub mod waku; + +pub const GROUP_VERSION: &str = "1"; +pub const SUBTOPICS: &[&str] = &["chat"]; diff --git a/chat/src/main.rs b/chat/src/main.rs new file mode 100644 index 0000000..d632e5a --- /dev/null +++ b/chat/src/main.rs @@ -0,0 +1,26 @@ +mod ds; +mod waku; + +use ds::transport::DeliveryService; +use waku::{WakuConfig, WakuDeliveryService}; + +const GROUP_VERSION: &str = "1"; +const SUBTOPICS: &[&str] = &["chat"]; + +fn main() { + let result = WakuDeliveryService::start(WakuConfig::default()) + .expect("failed to start waku node"); + + println!("Waku node started. Listening for messages..."); + + let rx = result.service.subscribe(); + for pkt in rx { + println!( + "--- received {} bytes (group={}, subtopic={}) ---", + pkt.payload.len(), + pkt.group_id, + pkt.subtopic + ); + println!("raw bytes: {:?}", pkt.payload); + } +} diff --git a/chat/src/waku/mod.rs b/chat/src/waku/mod.rs new file mode 100644 index 0000000..fa3264c --- /dev/null +++ b/chat/src/waku/mod.rs @@ -0,0 +1,360 @@ +//! Waku transport implementation and Waku-backed `DeliveryService`. + +pub(crate) mod sys; +pub(crate) mod wrapper; + +use std::sync::{Arc, Mutex, mpsc}; +use std::thread; +use std::time::Duration; + +use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use tracing::{debug, error, info}; + +use crate::ds::{ + DeliveryServiceError, + transport::{DeliveryService, InboundPacket, OutboundPacket}, +}; +use wrapper::WakuNodeCtx; + +use super::{GROUP_VERSION, SUBTOPICS}; + +/// The pubsub topic for the Waku Node. +pub fn pubsub_topic() -> String { + "/waku/2/rs/15/1".to_string() +} + +/// Build the content topics for a group. +pub fn build_content_topics(group_name: &str) -> Vec { + SUBTOPICS + .iter() + .map(|subtopic| build_content_topic(group_name, GROUP_VERSION, subtopic)) + .collect() +} + +/// Build the content topic string: `/{group_name}/{version}/{subtopic}/proto` +pub fn build_content_topic(group_name: &str, group_version: &str, subtopic: &str) -> String { + format!("/{group_name}/{group_version}/{subtopic}/proto") +} + +// ── Outbound command ──────────────────────────────────────────────────────── +struct OutboundCommand { + pkt: OutboundPacket, + reply: mpsc::SyncSender>, +} + +// ── Subscriber registry ───────────────────────────────────────────────────── +type SubscriberList = Arc>>>; + +/// Result returned by [`WakuDeliveryService::start`]. +pub struct WakuStartResult { + pub service: WakuDeliveryService, + /// The local ENR (Ethereum Node Record) if discv5 is enabled. + /// Pass this to other nodes via `WakuConfig::discv5_bootstrap_enrs`. + pub enr: Option, +} + +/// Waku-backed delivery service. +/// +/// The service starts an embedded Waku node on a dedicated `std::thread`. +/// All interaction is via synchronous `std::sync::mpsc` channels. +/// +/// Use [`WakuDeliveryService::start`] to create an instance. Call +/// [`shutdown`](WakuDeliveryService::shutdown) for explicit cleanup, or +/// simply drop all clones to stop the background thread. +#[derive(Clone)] +pub struct WakuDeliveryService { + outbound: mpsc::SyncSender, + subscribers: SubscriberList, + enr: Option, +} + +#[derive(Debug, Clone)] +pub struct WakuConfig { + pub node_port: u16, + /// Enable discv5 peer discovery. Nodes on the same clusterId/shard + /// will find each other automatically. + pub discv5: bool, + /// UDP port for discv5 discovery (default: 9000). + pub discv5_udp_port: u16, + /// Bootstrap ENR strings for discv5. If empty and discv5 is enabled, + /// this node acts as a bootstrap node (others must know its ENR). + pub discv5_bootstrap_enrs: Vec, +} + +impl Default for WakuConfig { + fn default() -> Self { + Self { + node_port: 60000, + discv5: false, + discv5_udp_port: 9000, + discv5_bootstrap_enrs: Vec::new(), + } + } +} + +impl WakuDeliveryService { + /// Start a Waku node and return both the delivery service and the local ENR + /// (if discv5 is enabled). + pub fn start(cfg: WakuConfig) -> Result { + let (out_tx, out_rx) = mpsc::sync_channel::(256); + let subscribers: SubscriberList = Arc::new(Mutex::new(Vec::new())); + let (ready_tx, ready_rx) = mpsc::channel::, DeliveryServiceError>>(); + + let subs_for_thread = subscribers.clone(); + + thread::Builder::new() + .name("waku-node".into()) + .spawn(move || { + if let Err(panic) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + Self::node_thread(cfg, out_rx, subs_for_thread, ready_tx); + })) { + let msg = panic + .downcast_ref::<&str>() + .map(|s| s.to_string()) + .or_else(|| panic.downcast_ref::().cloned()) + .unwrap_or_else(|| "unknown panic".to_string()); + error!("waku-node thread panicked: {msg}"); + } + }) + .map_err(|e| DeliveryServiceError::Other(anyhow::anyhow!(e)))?; + + // Wait for the node to either start or fail. + let enr = ready_rx + .recv() + .map_err(|e| DeliveryServiceError::Other(anyhow::anyhow!(e)))??; + + let service = Self { + outbound: out_tx, + subscribers, + enr: enr.clone(), + }; + Ok(WakuStartResult { service, enr }) + } + + /// The local ENR (Ethereum Node Record), available when discv5 is enabled. + pub fn enr(&self) -> Option<&str> { + self.enr.as_deref() + } + + /// Explicitly shut down the background Waku node thread. + /// + /// After calling this, all subsequent [`send`](DeliveryService::send) calls + /// will return an error. Alternatively, dropping all clones of this service + /// achieves the same effect. + pub fn shutdown(self) { + drop(self.outbound); + } + + fn node_thread( + cfg: WakuConfig, + out_rx: mpsc::Receiver, + subscribers: SubscriberList, + ready_tx: mpsc::Sender, DeliveryServiceError>>, + ) { + let mut config = serde_json::json!({ + "host": "0.0.0.0", + "port": cfg.node_port, + "relay": true, + "clusterId": 15, + "shards": [1], + "numShardsInNetwork": 8, + "logLevel": "ERROR", + // Keep metrics disabled for libwaku runtime to avoid exposing + // Prometheus/logging endpoints from embedded nodes. + "metricsServer": false, + "metricsLogging": false, + }); + + if cfg.discv5 { + config["discv5Discovery"] = serde_json::json!(true); + config["discv5UdpPort"] = serde_json::json!(cfg.discv5_udp_port); + if !cfg.discv5_bootstrap_enrs.is_empty() { + config["discv5BootstrapNodes"] = serde_json::json!(cfg.discv5_bootstrap_enrs); + } + } + + let config_json = config.to_string(); + + // Create node + let waku = match WakuNodeCtx::new(&config_json) { + Ok(w) => w, + Err(e) => { + let _ = ready_tx.send(Err(DeliveryServiceError::WakuNodeAlreadyInitialized(e))); + return; + } + }; + + // Start node + if let Err(e) = waku.start() { + let _ = ready_tx.send(Err(DeliveryServiceError::WakuNodeAlreadyInitialized(e))); + return; + } + info!("Waku node started"); + + thread::sleep(Duration::from_secs(2)); + + // Retrieve ENR for discv5 bootstrapping (discv5 is started automatically + // by waku_start when discv5Discovery=true is in the config JSON). + let local_enr = if cfg.discv5 { + match waku.get_enr() { + Ok(enr) => { + info!("Local ENR: {enr}"); + Some(enr) + } + Err(e) => { + info!("Could not retrieve ENR: {e}"); + None + } + } + } else { + None + }; + + // Explicit relay subscribe as safety net (config shards may auto-subscribe, + // but this ensures we're subscribed regardless of libwaku version behavior). + let topic = pubsub_topic(); + if let Err(e) = waku.relay_subscribe(&topic) { + // Non-fatal: some libwaku versions reject duplicate subscribe + info!("relay_subscribe returned (may already be subscribed): {e}"); + } + + // Set event callback — this closure must live for the node lifetime. + let subs_for_cb = subscribers.clone(); + let event_closure = move |_ret: i32, data: &str| { + if let Some(pkt) = Self::parse_event(data) { + let guard = subs_for_cb.lock(); + // If the mutex is poisoned, subscribers are lost — log and skip. + let mut guard = match guard { + Ok(g) => g, + Err(e) => { + error!("Subscriber mutex poisoned: {e}"); + return; + } + }; + guard.retain(|tx| match tx.try_send(pkt.clone()) { + Ok(()) => true, + Err(mpsc::TrySendError::Full(_)) => true, // keep — just slow + Err(mpsc::TrySendError::Disconnected(_)) => false, // drop — dead + }); + } + }; + // Heap-allocated closure — must stay alive until node stops. + let _event_cb_guard = waku.set_event_callback(event_closure); + + // Signal ready (with ENR) + let _ = ready_tx.send(Ok(local_enr)); + + // Outbound loop — blocks until all senders drop + let topic = pubsub_topic(); + while let Ok(cmd) = out_rx.recv() { + let res = Self::do_publish(&waku, &topic, cmd.pkt); + let _ = cmd.reply.try_send(res); + } + + // _event_cb_guard dropped here, then waku dropped (calls waku_stop via Drop) + info!("Waku outbound loop finished"); + } + + fn do_publish( + waku: &WakuNodeCtx, + pubsub_topic: &str, + pkt: OutboundPacket, + ) -> Result { + let content_topic = build_content_topic(&pkt.group_id, GROUP_VERSION, &pkt.subtopic); + let payload_b64 = BASE64.encode(&pkt.payload); + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_nanos(); + let meta_b64 = BASE64.encode(&pkt.app_id); + + let msg_json = serde_json::json!({ + "payload": payload_b64, + "contentTopic": content_topic, + "timestamp": timestamp as u64, + "version": 2, + "meta": meta_b64, + }) + .to_string(); + + waku.relay_publish(pubsub_topic, &msg_json, 10_000) + .map_err(|e| { + error!("Failed to relay publish: {e}"); + DeliveryServiceError::WakuPublishMessageError(e) + }) + } + + /// Parse a waku event JSON into an InboundPacket (if it's a message event). + fn parse_event(data: &str) -> Option { + let v: serde_json::Value = serde_json::from_str(data).ok()?; + + let waku_msg = v.get("wakuMessage")?; + let payload_b64 = waku_msg.get("payload")?.as_str()?; + let payload = BASE64.decode(payload_b64).ok()?; + let content_topic = waku_msg.get("contentTopic")?.as_str()?; + let timestamp = waku_msg + .get("timestamp") + .and_then(|t| t.as_i64()) + .unwrap_or(0); + + let meta = waku_msg + .get("meta") + .and_then(|m| m.as_str()) + .and_then(|m| BASE64.decode(m).ok()) + .unwrap_or_default(); + + // Parse content topic: /{group_name}/{version}/{subtopic}/proto + let (group_id, subtopic) = Self::parse_content_topic(content_topic)?; + + debug!("Inbound message: group={group_id} subtopic={subtopic}"); + + Some(InboundPacket { + payload, + subtopic, + group_id, + app_id: meta, + timestamp, + }) + } + + /// Parse `/{group_name}/{version}/{subtopic}/proto` into (group_id, subtopic). + fn parse_content_topic(ct: &str) -> Option<(String, String)> { + // Expected: "/{group_name}/{version}/{subtopic}/proto" + let mut parts = ct.split('/'); + let _empty = parts.next()?; // leading "" + let group_id = parts.next()?; + let _version = parts.next()?; + let subtopic = parts.next()?; + if group_id.is_empty() || subtopic.is_empty() { + return None; + } + Some((group_id.to_owned(), subtopic.to_owned())) + } +} + +impl DeliveryService for WakuDeliveryService { + fn send(&self, pkt: OutboundPacket) -> Result { + let (reply_tx, reply_rx) = mpsc::sync_channel(1); + self.outbound + .send(OutboundCommand { + pkt, + reply: reply_tx, + }) + .map_err(|e| DeliveryServiceError::Other(anyhow::anyhow!(e)))?; + + reply_rx + .recv() + .map_err(|e| DeliveryServiceError::Other(anyhow::anyhow!(e)))? + } + + fn subscribe(&self) -> mpsc::Receiver { + let (tx, rx) = mpsc::sync_channel(256); + match self.subscribers.lock() { + Ok(mut g) => g.push(tx), + Err(e) => { + error!("Subscriber mutex poisoned, subscription lost: {e}"); + } + } + rx + } +} diff --git a/chat/src/waku/sys.rs b/chat/src/waku/sys.rs new file mode 100644 index 0000000..246c25f --- /dev/null +++ b/chat/src/waku/sys.rs @@ -0,0 +1,96 @@ +//! Raw FFI declarations matching libwaku.h (trampoline pattern). +#![allow(unused)] + +use std::os::raw::{c_char, c_int, c_uint, c_void}; +use std::slice; + +pub type FFICallBack = unsafe extern "C" fn(c_int, *const c_char, usize, *const c_void); + +#[link(name = "waku")] +unsafe extern "C" { + pub fn waku_new( + config_json: *const c_char, + cb: FFICallBack, + user_data: *const c_void, + ) -> *mut c_void; + + pub fn waku_start(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; + + pub fn waku_stop(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; + + pub fn waku_version(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; + + pub fn set_event_callback(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void); + + pub fn waku_relay_publish( + ctx: *mut c_void, + cb: FFICallBack, + user_data: *const c_void, + pubsub_topic: *const c_char, + json_message: *const c_char, + timeout_ms: c_uint, + ) -> c_int; + + pub fn waku_relay_subscribe( + ctx: *mut c_void, + cb: FFICallBack, + user_data: *const c_void, + pubsub_topic: *const c_char, + ) -> c_int; + + pub fn waku_connect( + ctx: *mut c_void, + cb: FFICallBack, + user_data: *const c_void, + peer_multi_addr: *const c_char, + timeout_ms: c_uint, + ) -> c_int; + + pub fn waku_get_my_peerid(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void) + -> c_int; + + pub fn waku_start_discv5(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; + + pub fn waku_stop_discv5(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; + + pub fn waku_get_my_enr(ctx: *mut c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; + + pub fn waku_discv5_update_bootnodes( + ctx: *mut c_void, + cb: FFICallBack, + user_data: *const c_void, + bootnodes: *const c_char, + ) -> c_int; +} + +// ── Trampoline pattern ────────────────────────────────────────────────────── + +pub unsafe extern "C" fn trampoline( + return_val: c_int, + buffer: *const c_char, + buffer_len: usize, + data: *const c_void, +) where + C: FnMut(i32, &str), +{ + if data.is_null() { + return; + } + let closure = unsafe { &mut *(data as *mut C) }; + if buffer.is_null() || buffer_len == 0 { + closure(return_val, ""); + return; + } + let bytes = unsafe { slice::from_raw_parts(buffer as *const u8, buffer_len) }; + let buffer_str = String::from_utf8_lossy(bytes); + closure(return_val, &buffer_str); +} + +pub fn get_trampoline(_closure: &C) -> FFICallBack +where + C: FnMut(i32, &str), +{ + trampoline:: +} + +pub const RET_OK: i32 = 0; diff --git a/chat/src/waku/wrapper.rs b/chat/src/waku/wrapper.rs new file mode 100644 index 0000000..e207736 --- /dev/null +++ b/chat/src/waku/wrapper.rs @@ -0,0 +1,282 @@ +//! Safe synchronous wrapper around the raw libwaku FFI. +#![allow(unused)] +use std::cell::OnceCell; +use std::ffi::CString; +use std::os::raw::c_void; + +use super::sys::{self as waku_sys, RET_OK, get_trampoline}; + +/// Opaque handle to a libwaku node context. +pub struct WakuNodeCtx { + ctx: *mut c_void, +} + +// The libwaku ctx pointer is thread-safe (single node, serialized calls inside C). +unsafe impl Send for WakuNodeCtx {} +unsafe impl Sync for WakuNodeCtx {} + +impl WakuNodeCtx { + /// Create a new waku node. `config_json` is the JSON string for node configuration. + pub fn new(config_json: &str) -> Result { + let config_cstr = CString::new(config_json).map_err(|e| e.to_string())?; + + let mut err: Option = None; + let mut closure = |ret: i32, data: &str| { + if ret != RET_OK { + err = Some(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ctx = unsafe { + waku_sys::waku_new( + config_cstr.as_ptr(), + cb, + &mut closure as *mut _ as *const c_void, + ) + }; + + if ctx.is_null() || err.is_some() { + return Err(err.unwrap_or_else(|| "waku_new returned null".into())); + } + + Ok(Self { ctx }) + } + + /// Start the node. + pub fn start(&self) -> Result<(), String> { + let mut err: Option = None; + let mut closure = |ret: i32, data: &str| { + if ret != RET_OK { + err = Some(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = + unsafe { waku_sys::waku_start(self.ctx, cb, &mut closure as *mut _ as *const c_void) }; + + if ret != RET_OK || err.is_some() { + return Err(err.unwrap_or_else(|| format!("waku_start returned {ret}"))); + } + Ok(()) + } + + /// Get the node version string. + pub fn version(&self) -> Result { + let version: OnceCell = OnceCell::new(); + let mut closure = |ret: i32, data: &str| { + if ret == RET_OK { + let _ = version.set(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = unsafe { + waku_sys::waku_version(self.ctx, cb, &mut closure as *mut _ as *const c_void) + }; + + if ret != RET_OK { + return Err(format!("waku_version returned {ret}")); + } + version + .into_inner() + .ok_or_else(|| "no version returned".into()) + } + + /// Get the local peer ID. + pub fn get_peer_id(&self) -> Result { + let peer_id: OnceCell = OnceCell::new(); + let mut closure = |ret: i32, data: &str| { + if ret == RET_OK { + let _ = peer_id.set(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = unsafe { + waku_sys::waku_get_my_peerid(self.ctx, cb, &mut closure as *mut _ as *const c_void) + }; + + if ret != RET_OK { + return Err(format!("waku_get_my_peerid returned {ret}")); + } + peer_id + .into_inner() + .ok_or_else(|| "no peer id returned".into()) + } + + /// Connect to a peer by multiaddr. + pub fn connect(&self, peer_multi_addr: &str, timeout_ms: u32) -> Result<(), String> { + let addr_cstr = CString::new(peer_multi_addr).map_err(|e| e.to_string())?; + + let mut err: Option = None; + let mut closure = |ret: i32, data: &str| { + if ret != RET_OK { + err = Some(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = unsafe { + waku_sys::waku_connect( + self.ctx, + cb, + &mut closure as *mut _ as *const c_void, + addr_cstr.as_ptr(), + timeout_ms, + ) + }; + + if ret != RET_OK || err.is_some() { + return Err(err.unwrap_or_else(|| format!("waku_connect returned {ret}"))); + } + Ok(()) + } + + /// Publish a message via relay. `json_message` is the waku message JSON. + pub fn relay_publish( + &self, + pubsub_topic: &str, + json_message: &str, + timeout_ms: u32, + ) -> Result { + let topic_cstr = CString::new(pubsub_topic).map_err(|e| e.to_string())?; + let msg_cstr = CString::new(json_message).map_err(|e| e.to_string())?; + + let result: OnceCell = OnceCell::new(); + let mut err: Option = None; + let mut closure = |ret: i32, data: &str| { + if ret == RET_OK { + let _ = result.set(data.to_string()); + } else { + err = Some(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = unsafe { + waku_sys::waku_relay_publish( + self.ctx, + cb, + &mut closure as *mut _ as *const c_void, + topic_cstr.as_ptr(), + msg_cstr.as_ptr(), + timeout_ms, + ) + }; + + if ret != RET_OK || err.is_some() { + return Err(err.unwrap_or_else(|| format!("waku_relay_publish returned {ret}"))); + } + Ok(result.into_inner().unwrap_or_default()) + } + + /// Subscribe to a relay pubsub topic. + pub fn relay_subscribe(&self, pubsub_topic: &str) -> Result<(), String> { + let topic_cstr = CString::new(pubsub_topic).map_err(|e| e.to_string())?; + + let mut err: Option = None; + let mut closure = |ret: i32, data: &str| { + if ret != RET_OK { + err = Some(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = unsafe { + waku_sys::waku_relay_subscribe( + self.ctx, + cb, + &mut closure as *mut _ as *const c_void, + topic_cstr.as_ptr(), + ) + }; + + if ret != RET_OK || err.is_some() { + return Err(err.unwrap_or_else(|| format!("waku_relay_subscribe returned {ret}"))); + } + Ok(()) + } + + /// Register the event callback. Returns the boxed closure — caller must keep + /// it alive for the lifetime of the node (dropping it invalidates the FFI pointer). + pub fn set_event_callback(&self, closure: C) -> Box + where + C: FnMut(i32, &str), + { + let mut boxed = Box::new(closure); + let cb = get_trampoline(&*boxed); + unsafe { + waku_sys::set_event_callback(self.ctx, cb, &mut *boxed as *mut C as *const c_void); + } + boxed + } + + /// Stop the node. Should be called before dropping to cleanly release resources. + pub fn stop(&self) -> Result<(), String> { + let mut err: Option = None; + let mut closure = |ret: i32, data: &str| { + if ret != RET_OK { + err = Some(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = + unsafe { waku_sys::waku_stop(self.ctx, cb, &mut closure as *mut _ as *const c_void) }; + + if ret != RET_OK || err.is_some() { + return Err(err.unwrap_or_else(|| format!("waku_stop returned {ret}"))); + } + Ok(()) + } + + /// Start the discv5 discovery service. + pub fn start_discv5(&self) -> Result<(), String> { + let mut err: Option = None; + let mut closure = |ret: i32, data: &str| { + if ret != RET_OK { + err = Some(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = unsafe { + waku_sys::waku_start_discv5(self.ctx, cb, &mut closure as *mut _ as *const c_void) + }; + + if ret != RET_OK || err.is_some() { + return Err(err.unwrap_or_else(|| format!("waku_start_discv5 returned {ret}"))); + } + Ok(()) + } + + /// Get the local ENR (Ethereum Node Record) string. + pub fn get_enr(&self) -> Result { + let enr: OnceCell = OnceCell::new(); + let mut closure = |ret: i32, data: &str| { + if ret == RET_OK { + let _ = enr.set(data.to_string()); + } + }; + let cb = get_trampoline(&closure); + + let ret = unsafe { + waku_sys::waku_get_my_enr(self.ctx, cb, &mut closure as *mut _ as *const c_void) + }; + + if ret != RET_OK { + return Err(format!("waku_get_my_enr returned {ret}")); + } + enr.into_inner().ok_or_else(|| "no ENR returned".into()) + } +} + +impl Drop for WakuNodeCtx { + fn drop(&mut self) { + if let Err(e) = self.stop() { + tracing::warn!("waku_stop failed during drop: {e}"); + } + } +}