diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0a5df56 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +/target +/docs +/.github \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2f7896d..bab85c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target/ +config.toml diff --git a/.svg/pubky-core-logo.svg b/.svg/pubky-core-logo.svg new file mode 100644 index 0000000..6dc82d8 --- /dev/null +++ b/.svg/pubky-core-logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 8087df4..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,3137 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] -name = "anyhow" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" - -[[package]] -name = "argon2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" -dependencies = [ - "base64ct", - "blake2", - "cpufeatures", - "password-hash", -] - -[[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" - -[[package]] -name = "async-trait" -version = "0.1.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "authenticator" -version = "0.1.0" -dependencies = [ - "anyhow", - "base64 0.22.1", - "clap", - "pubky", - "pubky-common", - "rpassword", - "tokio", - "url", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "axum" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" -dependencies = [ - "async-trait", - "axum-core", - "axum-macros", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 1.0.1", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-extra" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" -dependencies = [ - "axum", - "axum-core", - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "serde", - "tokio", - "tokio-util", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base32" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -dependencies = [ - "serde", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - -[[package]] -name = "blake3" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" - -[[package]] -name = "cc" -version = "1.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bbb537bb4a30b90362caddba8f360c0a56bc13d3a5570028e7197204cb54a17" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[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 = "clap" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" - -[[package]] -name = "cobs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" - -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cookie_store" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4934e6b7e8419148b6ef56950d277af8561060b56afd59e2aadf98b59fce6baa" -dependencies = [ - "cookie", - "idna 0.5.0", - "log", - "publicsuffix", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[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.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "critical-section" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" - -[[package]] -name = "crossbeam-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "rand_core", - "typenum", -] - -[[package]] -name = "crypto_secretbox" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1" -dependencies = [ - "aead", - "cipher", - "generic-array", - "poly1305", - "salsa20", - "subtle", - "zeroize", -] - -[[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", -] - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[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 = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "document-features" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" -dependencies = [ - "litrs", -] - -[[package]] -name = "doxygen-rs" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415b6ec780d34dcf624666747194393603d0373b7141eef01d12ee58881507d9" -dependencies = [ - "phf", -] - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[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.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "embedded-io" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "fastrand" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "spin", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -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", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" - -[[package]] -name = "h2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "headers" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" -dependencies = [ - "base64 0.21.7", - "bytes", - "headers-core", - "http", - "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", -] - -[[package]] -name = "heapless" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "heed" -version = "0.20.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d4f449bab7320c56003d37732a917e18798e2f1709d80263face2b4f9436ddb" -dependencies = [ - "bitflags", - "byteorder", - "heed-traits", - "heed-types", - "libc", - "lmdb-master-sys", - "once_cell", - "page_size", - "serde", - "synchronoise", - "url", -] - -[[package]] -name = "heed-traits" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3130048d404c57ce5a1ac61a903696e8fcde7e8c2991e9fcfc1f27c3ef74ff" - -[[package]] -name = "heed-types" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d3f528b053a6d700b2734eabcd0fd49cb8230647aa72958467527b0b7917114" -dependencies = [ - "bincode", - "byteorder", - "heed-traits", - "serde", - "serde_json", -] - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "ipnet" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.159" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags", - "libc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "litrs" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" - -[[package]] -name = "lmdb-master-sys" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "472c3760e2a8d0f61f322fb36788021bb36d573c502b50fa3e2bcaac3ec326c9" -dependencies = [ - "cc", - "doxygen-rs", - "libc", -] - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "lru" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" - -[[package]] -name = "mainline" -version = "3.0.0" -source = "git+https://github.com/pubky/mainline?branch=dev#47edb4617f3ce25883b889064de5c7d257848b8f" -dependencies = [ - "bytes", - "crc", - "ed25519-dalek", - "flume", - "lru", - "rand", - "serde", - "serde_bencode", - "serde_bytes", - "sha1_smol", - "thiserror", - "tracing", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi", - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom", -] - -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "object" -version = "0.36.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "openssl" -version = "0.10.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "page_size" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkarr" -version = "3.0.0" -source = "git+https://github.com/Pubky/pkarr?branch=v3#0622003a851dfc34c4c3b44daaf13c99880d300a" -dependencies = [ - "base32", - "bytes", - "document-features", - "dyn-clone", - "ed25519-dalek", - "flume", - "futures", - "getrandom", - "js-sys", - "lru", - "mainline", - "once_cell", - "rand", - "reqwest", - "self_cell", - "serde", - "simple-dns", - "thiserror", - "tokio", - "tracing", -] - -[[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.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[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 = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - -[[package]] -name = "postcard" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" -dependencies = [ - "cobs", - "embedded-io 0.4.0", - "embedded-io 0.6.1", - "heapless", - "serde", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "psl-types" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" - -[[package]] -name = "pubky" -version = "0.1.0" -dependencies = [ - "base64 0.22.1", - "bytes", - "js-sys", - "pkarr", - "pubky-common", - "pubky_homeserver", - "reqwest", - "thiserror", - "tokio", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "pubky-common" -version = "0.1.0" -dependencies = [ - "argon2", - "base32", - "blake3", - "crypto_secretbox", - "ed25519-dalek", - "js-sys", - "once_cell", - "pkarr", - "postcard", - "rand", - "serde", - "thiserror", -] - -[[package]] -name = "pubky_homeserver" -version = "0.1.0" -dependencies = [ - "anyhow", - "axum", - "axum-extra", - "base32", - "bytes", - "clap", - "dirs-next", - "flume", - "futures-util", - "heed", - "hex", - "pkarr", - "postcard", - "pubky-common", - "serde", - "tokio", - "toml", - "tower-cookies", - "tower-http", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "publicsuffix" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" -dependencies = [ - "idna 0.3.0", - "psl-types", -] - -[[package]] -name = "quinn" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" -dependencies = [ - "bytes", - "rand", - "ring", - "rustc-hash", - "rustls", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-udp" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" -dependencies = [ - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[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", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.8", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "reqwest" -version = "0.12.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" -dependencies = [ - "base64 0.22.1", - "bytes", - "cookie", - "cookie_store", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "windows-registry", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rpassword" -version = "7.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" -dependencies = [ - "libc", - "rtoolbox", - "windows-sys 0.48.0", -] - -[[package]] -name = "rtoolbox" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" - -[[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.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "schannel" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "self_cell" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bencode" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a70dfc7b7438b99896e7f8992363ab8e2c4ba26aa5ec675d32d1c3c2c33d413e" -dependencies = [ - "serde", - "serde_bytes", -] - -[[package]] -name = "serde_bytes" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -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 = "simple-dns" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01607fe2e61894468c6dc0b26103abb073fb08b79a3d9e4b6d76a1a341549958" -dependencies = [ - "bitflags", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synchronoise" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dbc01390fc626ce8d1cffe3376ded2b72a11bb70e1c75f404a210e4daa4def2" -dependencies = [ - "crossbeam-queue", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "thiserror" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 0.1.2", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-cookies" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d" -dependencies = [ - "async-trait", - "axum-core", - "cookie", - "futures-util", - "http", - "parking_lot", - "pin-project-lite", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags", - "bytes", - "http", - "http-body", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -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]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - -[[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 = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" -dependencies = [ - "form_urlencoded", - "idna 0.5.0", - "percent-encoding", -] - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[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 = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "web-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.26.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 017397d..bf6e25b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,16 +3,12 @@ members = [ "pubky", "pubky-*", - "examples/authz/authenticator" + "examples" ] # See: https://github.com/rust-lang/rust/issues/90148#issuecomment-949194352 resolver = "2" -[workspace.dependencies] -pkarr = { git = "https://github.com/Pubky/pkarr", branch = "v3", package = "pkarr" } -serde = { version = "^1.0.209", features = ["derive"] } - [profile.release] lto = true opt-level = 'z' diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..62da9c6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,56 @@ +# ======================== +# Build Stage +# ======================== +FROM rust:1.82.0-alpine3.20 AS builder + +# Install build dependencies, including static OpenSSL libraries +RUN apk add --no-cache \ + musl-dev \ + openssl-dev \ + openssl-libs-static \ + pkgconfig \ + build-base \ + curl + +# Set environment variables for static linking with OpenSSL +ENV OPENSSL_STATIC=yes +ENV OPENSSL_LIB_DIR=/usr/lib +ENV OPENSSL_INCLUDE_DIR=/usr/include + +# Add the MUSL target for static linking +RUN rustup target add x86_64-unknown-linux-musl + +# Set the working directory +WORKDIR /usr/src/app + +# Copy over Cargo.toml and Cargo.lock for dependency caching +COPY Cargo.toml Cargo.lock ./ + +# Copy over all the source code +COPY . . + +# Build the project in release mode for the MUSL target +RUN cargo build --release --bin pubky_homeserver --target x86_64-unknown-linux-musl + +# Strip the binary to reduce size +RUN strip target/x86_64-unknown-linux-musl/release/pubky_homeserver + +# ======================== +# Runtime Stage +# ======================== +FROM alpine:3.20 + +# Install runtime dependencies (only ca-certificates) +RUN apk add --no-cache ca-certificates + +# Copy the compiled binary from the builder stage +COPY --from=builder /usr/src/app/target/x86_64-unknown-linux-musl/release/pubky_homeserver /usr/local/bin/homeserver + +# Set the working directory +WORKDIR /usr/local/bin + +# Expose the port the homeserver listens on (should match that of config.toml) +EXPOSE 6287 + +# Set the default command to run the homeserver binary +CMD ["homeserver"] diff --git a/README.md b/README.md index 23197ae..c4106bd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,61 @@ -# Pubky +

pubky

+ +

+ An open protocol for per-public-key backends for censorship resistant web applications. +

+ +
+

+ + Docs Site + + | + + Rust Client's Docs + + | + + Releases + + | + + JS bindings + +

+
> The Web, long centralized, must decentralize; Long decentralized, must centralize. -> [!WARNING] -> Pubky is still under heavy development and should be considered an alpha software. -> -> Features might be added, removed, or changed. Data might be lost. +## Overview + +Pubky-core combines a [censorship resistant public-key based alternative to DNS](https://pkarr.org) with conventional, tried and tested web technologies, to keep users in control of their identities and data, while enabling developers to build software with as much availability as web apps, without the costs of managing a central database. + +## Features +- Public key based authentication. +- Public key based 3rd party authorization. +- Key-value store through PUT/GET/DELETE HTTP API + pagination. + +## Getting started + +This repository contains a [Homeserver](./pubky-homeserver), and a [Client](./pubky) (both Rust and JS wasm bindings). +You can a run a local homeserver using `cargo run` with more instructions in the README. +Check the [Examples](./examples) directory for small feature-focesed examples of how to use the Pubky client. + +### JavaScript +If you prefer to use JavaScript in NodeJs/Browser or any runtime with Wasm support, you can either install from npm [`@synonymdev/pubky`](https://www.npmjs.com/package/@synonymdev/pubky) +or build the bindings yourself: +```bash +cd pubky/pkg +npm i +npm run build +``` + +#### Testing +There are unit tests for the JavaScript bindings in both NodeJs and headless web browser, but first you need to run a local temporary Homeserver +```bash +npm run testnet +``` +Then in a different terminal window: +```bash +npm test +``` diff --git a/examples/authz/authenticator/Cargo.toml b/examples/Cargo.toml similarity index 50% rename from examples/authz/authenticator/Cargo.toml rename to examples/Cargo.toml index 932701b..02edbbe 100644 --- a/examples/authz/authenticator/Cargo.toml +++ b/examples/Cargo.toml @@ -1,14 +1,27 @@ [package] -name = "authenticator" +name = "authn" version = "0.1.0" edition = "2021" +[[bin]] +name = "signup" +path = "./authn/signup.rs" + +[[bin]] +name = "authenticator" +path = "./authz/authenticator.rs" + +[[bin]] +name = "request" +path = "./request/main.rs" + [dependencies] anyhow = "1.0.86" base64 = "0.22.1" clap = { version = "4.5.16", features = ["derive"] } -pubky = { version = "0.1.0", path = "../../../pubky" } -pubky-common = { version = "0.1.0", path = "../../../pubky-common" } +pubky = { path = "../pubky" } +pubky-common = { version = "0.1.0", path = "../pubky-common" } +reqwest = "0.12.8" rpassword = "7.3.1" tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] } url = "2.5.2" diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..fb94edb --- /dev/null +++ b/examples/README.md @@ -0,0 +1,7 @@ +# Pubky examples + +Minimal examples for different flows and functions you might need to implement using Pubky. + +- [request](./request/README.md): shows how to make direct HTTP requests to Pubky URLs. +- [authentication](./authn/README.md): shows how to signup, signin or signout to and from a homeserver. +- [authorization flow](./authz/README.md): shows how to setup Pubky authz for a 3rd party application and how to implement an authenticator to sign in such app. diff --git a/examples/authn/README.md b/examples/authn/README.md new file mode 100644 index 0000000..162c19e --- /dev/null +++ b/examples/authn/README.md @@ -0,0 +1,13 @@ +# Authentication examples + +You can use these examples to test Signup or Signin to a provided homeserver using a keypair, +as opposed to using a the 3rd party [authorization flow](../authz). + + +## Usage + +### Signup + +```bash +cargo run --bin signup +``` diff --git a/examples/authn/signup.rs b/examples/authn/signup.rs new file mode 100644 index 0000000..ecafae5 --- /dev/null +++ b/examples/authn/signup.rs @@ -0,0 +1,49 @@ +use anyhow::Result; +use clap::Parser; +use pubky::PubkyClient; +use std::path::PathBuf; + +use pubky_common::crypto::PublicKey; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Cli { + /// Homeserver Pkarr Domain (for example `5jsjx1o6fzu6aeeo697r3i5rx15zq41kikcye8wtwdqm4nb4tryo`) + homeserver: String, + + /// Path to a recovery_file of the Pubky you want to sign in with + recovery_file: PathBuf, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + let recovery_file = std::fs::read(&cli.recovery_file)?; + println!("\nSuccessfully opened recovery file"); + + let homeserver = cli.homeserver; + + let client = PubkyClient::builder().build(); + + println!("Enter your recovery_file's passphrase to signup:"); + let passphrase = rpassword::read_password()?; + + let keypair = pubky_common::recovery_file::decrypt_recovery_file(&recovery_file, &passphrase)?; + + println!("Successfully decrypted the recovery file, signing up to the homeserver:"); + + client + .signup(&keypair, &PublicKey::try_from(homeserver).unwrap()) + .await?; + + println!("Successfully signed up. Checking session:"); + + let session = client.session(&keypair.public_key()).await?; + + println!("Successfully resolved current session at the homeserver."); + + println!("{:?}", session); + + Ok(()) +} diff --git a/examples/authz/README.md b/examples/authz/README.md index 905bda6..86adf64 100644 --- a/examples/authz/README.md +++ b/examples/authz/README.md @@ -5,7 +5,7 @@ This example shows 3rd party authorization in Pubky. It consists of 2 parts: 1. [3rd party app](./3rd-party-app): A web component showing the how to implement a Pubky Auth widget. -2. [Authenticator CLI](./authenticator): A CLI showing the authenticator (key chain) asking user for consent and generating the AuthToken. +2. [Authenticator CLI](./authenticator.rs): A CLI showing the authenticator (key chain) asking user for consent and generating the AuthToken. ## Usage @@ -26,4 +26,10 @@ Copy the Pubky Auth URL from the frontend. Finally run the CLI to paste the Pubky Auth in. +```bash +cargo run --bin authenticator "" [Testnet] +``` + +Where the auth url should be within qutoatino marks, and the Testnet is an option you can set to true to use the local homeserver + You should see the frontend reacting by showing the success of authorization and session details. diff --git a/examples/authz/authenticator/src/main.rs b/examples/authz/authenticator.rs similarity index 76% rename from examples/authz/authenticator/src/main.rs rename to examples/authz/authenticator.rs index 410b8f5..97999c0 100644 --- a/examples/authz/authenticator/src/main.rs +++ b/examples/authz/authenticator.rs @@ -17,6 +17,9 @@ struct Cli { /// Pubky Auth url url: Url, + + // Whether or not to use testnet Dht network (local testing) + testnet: Option, } #[tokio::main] @@ -62,14 +65,20 @@ async fn main() -> Result<()> { println!("Successfully decrypted recovery file..."); println!("PublicKey: {}", keypair.public_key()); - let client = PubkyClient::testnet(); + let client = if cli.testnet.unwrap_or_default() { + let client = PubkyClient::testnet(); + + // For the purposes of this demo, we need to make sure + // the user has an account on the local homeserver. + if client.signin(&keypair).await.is_err() { + client + .signup(&keypair, &PublicKey::try_from(HOMESERVER).unwrap()) + .await?; + }; - // For the purposes of this demo, we need to make sure - // the user has an account on the local homeserver. - if client.signin(&keypair).await.is_err() { client - .signup(&keypair, &PublicKey::try_from(HOMESERVER).unwrap()) - .await?; + } else { + PubkyClient::builder().build() }; println!("Sending AuthToken to the 3rd party app..."); diff --git a/examples/request/README.md b/examples/request/README.md new file mode 100644 index 0000000..f072f77 --- /dev/null +++ b/examples/request/README.md @@ -0,0 +1,17 @@ +# Request + +Make HTTP requests over for Pubky authority URL. + +## Usage + +Request data from a Pubky's data storage. + +```bash +cargo run --bin request GET pubky:///pub/ +``` + +Or make a direct HTTP request. + +```bash +cargo run --bin request GET https:///[path] +``` diff --git a/examples/request/main.rs b/examples/request/main.rs new file mode 100644 index 0000000..69d5400 --- /dev/null +++ b/examples/request/main.rs @@ -0,0 +1,38 @@ +use anyhow::Result; +use clap::Parser; +use reqwest::Method; +use url::Url; + +use pubky::PubkyClient; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Cli { + /// HTTP method to use + method: Method, + /// Pubky or HTTPS url + url: Url, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + let client = PubkyClient::builder().build(); + + match cli.url.scheme() { + "https" => { + unimplemented!(); + } + "pubky" => { + let response = client.get(cli.url).await.unwrap(); + + println!("Got a response: \n {:?}", response); + } + _ => { + panic!("Only https:// and pubky:// URL schemes are supported") + } + } + + Ok(()) +} diff --git a/pubky-common/Cargo.toml b/pubky-common/Cargo.toml index 9676fba..8c9142f 100644 --- a/pubky-common/Cargo.toml +++ b/pubky-common/Cargo.toml @@ -2,32 +2,29 @@ name = "pubky-common" version = "0.1.0" edition = "2021" +description = "Types and struct in common between Pubky client and homeserver" +license = "MIT" +repository = "https://github.com/pubky/pubky-core" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] base32 = "0.5.0" blake3 = "1.5.1" -ed25519-dalek = "2.1.1" +ed25519-dalek = { version = "2.1.1", features = ["serde"] } once_cell = "1.19.0" -pkarr = { workspace = true } rand = "0.8.5" thiserror = "1.0.60" postcard = { version = "1.0.8", features = ["alloc"] } crypto_secretbox = { version = "0.1.1", features = ["std"] } argon2 = { version = "0.5.3", features = ["std"] } -serde = { workspace = true, optional = true } +pubky-timestamp = { version = "0.2.0", features = ["full"] } +serde = { version = "1.0.213", features = ["derive"] } +pkarr = { version = "2.2.1-alpha.2", features = ["serde"] } [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = "0.3.69" [dev-dependencies] postcard = "1.0.8" - -[features] - -serde = ["dep:serde", "ed25519-dalek/serde", "pkarr/serde"] -full = ['serde'] - -default = ['full'] diff --git a/pubky-common/src/auth.rs b/pubky-common/src/auth.rs index 866fe5e..082b8e8 100644 --- a/pubky-common/src/auth.rs +++ b/pubky-common/src/auth.rs @@ -76,7 +76,7 @@ impl AuthToken { let now = Timestamp::now(); // Chcek timestamp; - let diff = token.timestamp.difference(&now); + let diff = token.timestamp.as_u64() as i64 - now.as_u64() as i64; if diff > TIMESTAMP_WINDOW { return Err(Error::TooFarInTheFuture); } @@ -155,7 +155,7 @@ impl AuthVerifier { /// Remove all tokens older than two time intervals in the past. fn gc(&self) { - let threshold = ((Timestamp::now().into_inner() / TIME_INTERVAL) - 2).to_be_bytes(); + let threshold = ((Timestamp::now().as_u64() / TIME_INTERVAL) - 2).to_be_bytes(); let mut inner = self.seen.lock().unwrap(); @@ -235,7 +235,7 @@ mod tests { let verifier = AuthVerifier::default(); - let timestamp = (&Timestamp::now()) - (TIMESTAMP_WINDOW as u64); + let timestamp = (Timestamp::now()) - (TIMESTAMP_WINDOW as u64); let mut signable = vec![]; signable.extend_from_slice(signer.public_key().as_bytes()); diff --git a/pubky-common/src/capabilities.rs b/pubky-common/src/capabilities.rs index 7929860..83848b9 100644 --- a/pubky-common/src/capabilities.rs +++ b/pubky-common/src/capabilities.rs @@ -9,7 +9,7 @@ pub struct Capability { } impl Capability { - /// Create a root [Capability] at the `/` path with all the available [PubkyAbility] + /// Create a root [Capability] at the `/` path with all the available [Action]s pub fn root() -> Self { Capability { scope: "/".to_string(), diff --git a/pubky-common/src/lib.rs b/pubky-common/src/lib.rs index cfb56f2..b19a187 100644 --- a/pubky-common/src/lib.rs +++ b/pubky-common/src/lib.rs @@ -4,4 +4,7 @@ pub mod crypto; pub mod namespaces; pub mod recovery_file; pub mod session; -pub mod timestamp; + +pub mod timestamp { + pub use pubky_timestamp::*; +} diff --git a/pubky-common/src/session.rs b/pubky-common/src/session.rs index 972652c..6b17735 100644 --- a/pubky-common/src/session.rs +++ b/pubky-common/src/session.rs @@ -26,7 +26,7 @@ impl Session { Self { version: 0, pubky: token.pubky().to_owned(), - created_at: Timestamp::now().into_inner(), + created_at: Timestamp::now().as_u64(), capabilities: token.capabilities().to_vec(), user_agent: user_agent.as_deref().unwrap_or("").to_string(), name: user_agent.as_deref().unwrap_or("").to_string(), diff --git a/pubky-homeserver/Cargo.toml b/pubky-homeserver/Cargo.toml index c8abfd5..7b37a20 100644 --- a/pubky-homeserver/Cargo.toml +++ b/pubky-homeserver/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pubky_homeserver" +name = "pubky-homeserver" version = "0.1.0" edition = "2021" @@ -15,13 +15,19 @@ flume = "0.11.0" futures-util = "0.3.30" heed = "0.20.3" hex = "0.4.3" -pkarr = { workspace = true } +httpdate = "1.0.3" +libc = "0.2.159" postcard = { version = "1.0.8", features = ["alloc"] } +pkarr = { version = "2.2.1-alpha.2", features = ["serde", "async"] } pubky-common = { version = "0.1.0", path = "../pubky-common" } -serde = { workspace = true } +serde = { version = "1.0.213", features = ["derive"] } tokio = { version = "1.37.0", features = ["full"] } toml = "0.8.19" tower-cookies = "0.10.0" tower-http = { version = "0.5.2", features = ["cors", "trace"] } tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +url = "2.5.2" + +[dev-dependencies] +reqwest = "0.12.8" diff --git a/pubky-homeserver/README.md b/pubky-homeserver/README.md index d1799a2..8aa2c26 100644 --- a/pubky-homeserver/README.md +++ b/pubky-homeserver/README.md @@ -1,5 +1,7 @@ # Pubky Homeserver +A pubky-core homeserver that acts as users' agent on the Internet, providing data availability and more.more.more.more. + ## Usage Use `cargo run` diff --git a/pubky-homeserver/src/config.toml b/pubky-homeserver/src/config.example.toml similarity index 100% rename from pubky-homeserver/src/config.toml rename to pubky-homeserver/src/config.example.toml diff --git a/pubky-homeserver/src/config.rs b/pubky-homeserver/src/config.rs index ba699b7..d0274f5 100644 --- a/pubky-homeserver/src/config.rs +++ b/pubky-homeserver/src/config.rs @@ -114,6 +114,8 @@ impl Config { return Ok(Config { bootstrap: testnet_config.bootstrap, + port: testnet_config.port, + keypair: testnet_config.keypair, ..config }); } @@ -139,6 +141,7 @@ impl Config { port: 15411, dht_request_timeout: None, db_map_size: DEFAULT_MAP_SIZE, + keypair: Keypair::from_secret_key(&[0; 32]), ..Self::test(&testnet) } } @@ -155,7 +158,6 @@ impl Config { bootstrap, storage, db_map_size: 10485760, - dht_request_timeout: Some(Duration::from_millis(10)), ..Default::default() } } @@ -276,7 +278,6 @@ mod tests { testnet: true, bootstrap: testnet.bootstrap.into(), db_map_size: 10485760, - dht_request_timeout: Some(Duration::from_millis(10)), storage: config.storage.clone(), keypair: config.keypair.clone(), @@ -302,4 +303,35 @@ mod tests { } ) } + + #[test] + fn parse_with_testnet_flag() { + let config = Config::try_from_str( + r#" + # Secret key (in hex) to generate the Homeserver's Keypair + secret_key = "0123000000000000000000000000000000000000000000000000000000000000" + # Domain to be published in Pkarr records for this server to be accessible by. + domain = "localhost" + # Port for the Homeserver to listen on. + port = 6287 + # Storage directory Defaults to + storage = "/homeserver" + testnet = true + + bootstrap = ["foo", "bar"] + + # event stream + default_list_limit = 500 + max_list_limit = 10000 + "#, + ) + .unwrap(); + + assert_eq!(config.keypair, Keypair::from_secret_key(&[0; 32])); + assert_eq!(config.port, 15411); + assert_ne!( + config.bootstrap, + Some(vec!["foo".to_string(), "bar".to_string()]) + ); + } } diff --git a/pubky-homeserver/src/database.rs b/pubky-homeserver/src/database.rs index 7f52e16..8ea4f48 100644 --- a/pubky-homeserver/src/database.rs +++ b/pubky-homeserver/src/database.rs @@ -1,4 +1,4 @@ -use std::fs; +use std::{fs, path::PathBuf}; use heed::{Env, EnvOpenOptions}; @@ -14,11 +14,17 @@ pub struct DB { pub(crate) env: Env, pub(crate) tables: Tables, pub(crate) config: Config, + pub(crate) buffers_dir: PathBuf, + pub(crate) max_chunk_size: usize, } impl DB { pub fn open(config: Config) -> anyhow::Result { - fs::create_dir_all(config.storage())?; + let buffers_dir = config.storage().clone().join("buffers"); + + // Cleanup buffers. + let _ = fs::remove_dir(&buffers_dir); + fs::create_dir_all(&buffers_dir)?; let env = unsafe { EnvOpenOptions::new() @@ -33,46 +39,25 @@ impl DB { env, tables, config, + buffers_dir, + max_chunk_size: max_chunk_size(), }; Ok(db) } } -#[cfg(test)] -mod tests { - use bytes::Bytes; - use pkarr::{mainline::Testnet, Keypair}; +/// calculate optimal chunk size: +/// - https://lmdb.readthedocs.io/en/release/#storage-efficiency-limits +/// - https://github.com/lmdbjava/benchmarks/blob/master/results/20160710/README.md#test-2-determine-24816-kb-byte-values +fn max_chunk_size() -> usize { + let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; - use crate::config::Config; - - use super::DB; - - #[tokio::test] - async fn entries() { - let db = DB::open(Config::test(&Testnet::new(0).unwrap())).unwrap(); - - let keypair = Keypair::random(); - let path = "/pub/foo.txt"; - - let (tx, rx) = flume::bounded::(0); - - let mut cloned = db.clone(); - let cloned_keypair = keypair.clone(); - - let done = tokio::task::spawn_blocking(move || { - cloned - .put_entry(&cloned_keypair.public_key(), path, rx) - .unwrap(); - }); - - tx.send(vec![1, 2, 3, 4, 5].into()).unwrap(); - drop(tx); - - done.await.unwrap(); - - let blob = db.get_blob(&keypair.public_key(), path).unwrap().unwrap(); - - assert_eq!(blob, Bytes::from(vec![1, 2, 3, 4, 5])); - } + // - 16 bytes Header per page (LMDB) + // - Each page has to contain 2 records + // - 8 bytes per record (LMDB) (imperically, it seems to be 10 not 8) + // - 12 bytes key: + // - timestamp : 8 bytes + // - chunk index: 4 bytes + ((page_size - 16) / 2) - (8 + 2) - 12 } diff --git a/pubky-homeserver/src/database/tables/blobs.rs b/pubky-homeserver/src/database/tables/blobs.rs index c430a58..18ec724 100644 --- a/pubky-homeserver/src/database/tables/blobs.rs +++ b/pubky-homeserver/src/database/tables/blobs.rs @@ -1,38 +1,24 @@ -use heed::{types::Bytes, Database}; -use pkarr::PublicKey; +use heed::{types::Bytes, Database, RoTxn}; use crate::database::DB; use super::entries::Entry; -/// hash of the blob => bytes. +/// (entry timestamp | chunk_index BE) => bytes pub type BlobsTable = Database; pub const BLOBS_TABLE: &str = "blobs"; impl DB { - pub fn get_blob( + pub fn read_entry_content<'txn>( &self, - public_key: &PublicKey, - path: &str, - ) -> anyhow::Result> { - let rtxn = self.env.read_txn()?; - - let key = format!("{public_key}/{path}"); - - let result = if let Some(bytes) = self.tables.entries.get(&rtxn, &key)? { - let entry = Entry::deserialize(bytes)?; - - self.tables - .blobs - .get(&rtxn, entry.content_hash())? - .map(|blob| bytes::Bytes::from(blob[8..].to_vec())) - } else { - None - }; - - rtxn.commit()?; - - Ok(result) + rtxn: &'txn RoTxn, + entry: &Entry, + ) -> anyhow::Result> + 'txn> { + Ok(self + .tables + .blobs + .prefix_iter(rtxn, &entry.timestamp().to_bytes())? + .map(|i| i.map(|(_, bytes)| bytes))) } } diff --git a/pubky-homeserver/src/database/tables/entries.rs b/pubky-homeserver/src/database/tables/entries.rs index b1c7039..0079a4f 100644 --- a/pubky-homeserver/src/database/tables/entries.rs +++ b/pubky-homeserver/src/database/tables/entries.rs @@ -1,6 +1,11 @@ use pkarr::PublicKey; use postcard::{from_bytes, to_allocvec}; use serde::{Deserialize, Serialize}; +use std::{ + fs::File, + io::{Read, Write}, + path::PathBuf, +}; use tracing::instrument; use heed::{ @@ -23,74 +28,12 @@ pub type EntriesTable = Database; pub const ENTRIES_TABLE: &str = "entries"; impl DB { - pub fn put_entry( + pub fn write_entry( &mut self, public_key: &PublicKey, path: &str, - rx: flume::Receiver, - ) -> anyhow::Result<()> { - let mut wtxn = self.env.write_txn()?; - - let mut hasher = Hasher::new(); - let mut bytes = vec![]; - let mut length = 0; - - while let Ok(chunk) = rx.recv() { - hasher.update(&chunk); - bytes.extend_from_slice(&chunk); - length += chunk.len(); - } - - let hash = hasher.finalize(); - - let key = hash.as_bytes(); - - let mut bytes_with_ref_count = Vec::with_capacity(bytes.len() + 8); - bytes_with_ref_count.extend_from_slice(&u64::to_be_bytes(0)); - bytes_with_ref_count.extend_from_slice(&bytes); - - // TODO: For now, we set the first 8 bytes to a reference counter - let exists = self - .tables - .blobs - .get(&wtxn, key)? - .unwrap_or(bytes_with_ref_count.as_slice()); - - let new_count = u64::from_be_bytes(exists[0..8].try_into().unwrap()) + 1; - - bytes_with_ref_count[0..8].copy_from_slice(&u64::to_be_bytes(new_count)); - - self.tables - .blobs - .put(&mut wtxn, hash.as_bytes(), &bytes_with_ref_count)?; - - let mut entry = Entry::new(); - - entry.set_content_hash(hash); - entry.set_content_length(length); - - let key = format!("{public_key}/{path}"); - - self.tables - .entries - .put(&mut wtxn, &key, &entry.serialize())?; - - if path.starts_with("pub/") { - let url = format!("pubky://{key}"); - let event = Event::put(&url); - let value = event.serialize(); - - let key = entry.timestamp.to_string(); - - self.tables.events.put(&mut wtxn, &key, &value)?; - - // TODO: delete older events. - // TODO: move to events.rs - } - - wtxn.commit()?; - - Ok(()) + ) -> anyhow::Result { + EntryWriter::new(self, public_key, path) } pub fn delete_entry(&mut self, public_key: &PublicKey, path: &str) -> anyhow::Result { @@ -101,28 +44,20 @@ impl DB { let deleted = if let Some(bytes) = self.tables.entries.get(&wtxn, &key)? { let entry = Entry::deserialize(bytes)?; - let mut bytes_with_ref_count = self - .tables - .blobs - .get(&wtxn, entry.content_hash())? - .map_or(vec![], |s| s.to_vec()); + let mut deleted_chunks = false; - let arr: [u8; 8] = bytes_with_ref_count[0..8].try_into().unwrap_or([0; 8]); - let reference_count = u64::from_be_bytes(arr); - - let deleted_blobs = if reference_count > 1 { - // decrement reference count - - bytes_with_ref_count[0..8].copy_from_slice(&(reference_count - 1).to_be_bytes()); - - self.tables + { + let mut iter = self + .tables .blobs - .put(&mut wtxn, entry.content_hash(), &bytes_with_ref_count)?; + .prefix_iter_mut(&mut wtxn, &entry.timestamp.to_bytes())?; - true - } else { - self.tables.blobs.delete(&mut wtxn, entry.content_hash())? - }; + while iter.next().is_some() { + unsafe { + deleted_chunks = iter.del_current()?; + } + } + } let deleted_entry = self.tables.entries.delete(&mut wtxn, &key)?; @@ -137,11 +72,11 @@ impl DB { self.tables.events.put(&mut wtxn, &key, &value)?; - // TODO: delete older events. + // TODO: delete events older than a threshold. // TODO: move to events.rs } - deleted_entry && deleted_blobs + deleted_entry && deleted_chunks } else { false }; @@ -151,6 +86,21 @@ impl DB { Ok(deleted) } + pub fn get_entry( + &self, + txn: &RoTxn, + public_key: &PublicKey, + path: &str, + ) -> anyhow::Result> { + let key = format!("{public_key}/{path}"); + + if let Some(bytes) = self.tables.entries.get(txn, &key)? { + return Ok(Some(Entry::deserialize(bytes)?)); + } + + Ok(None) + } + pub fn contains_directory(&self, txn: &RoTxn, path: &str) -> anyhow::Result { Ok(self.tables.entries.get_greater_than(txn, path)?.is_some()) } @@ -268,13 +218,40 @@ pub struct Entry { version: usize, /// Modified at timestamp: Timestamp, - content_hash: [u8; 32], + content_hash: EntryHash, content_length: usize, content_type: String, // user_metadata: ? } -// TODO: get headers like Etag +#[derive(Clone, Debug, Eq, PartialEq)] +struct EntryHash(Hash); + +impl Default for EntryHash { + fn default() -> Self { + Self(Hash::from_bytes([0; 32])) + } +} + +impl Serialize for EntryHash { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let bytes = self.0.as_bytes(); + bytes.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for EntryHash { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let bytes: [u8; 32] = Deserialize::deserialize(deserializer)?; + Ok(Self(Hash::from_bytes(bytes))) + } +} impl Entry { pub fn new() -> Self { @@ -283,8 +260,13 @@ impl Entry { // === Setters === + pub fn set_timestamp(&mut self, timestamp: &Timestamp) -> &mut Self { + self.timestamp = *timestamp; + self + } + pub fn set_content_hash(&mut self, content_hash: Hash) -> &mut Self { - content_hash.as_bytes().clone_into(&mut self.content_hash); + EntryHash(content_hash).clone_into(&mut self.content_hash); self } @@ -295,12 +277,32 @@ impl Entry { // === Getters === - pub fn content_hash(&self) -> &[u8; 32] { - &self.content_hash + pub fn timestamp(&self) -> &Timestamp { + &self.timestamp + } + + pub fn content_hash(&self) -> &Hash { + &self.content_hash.0 + } + + pub fn content_length(&self) -> usize { + self.content_length + } + + pub fn content_type(&self) -> &str { + &self.content_type } // === Public Method === + pub fn read_content<'txn>( + &self, + db: &'txn DB, + rtxn: &'txn RoTxn, + ) -> anyhow::Result> + 'txn> { + db.read_entry_content(rtxn, self) + } + pub fn serialize(&self) -> Vec { to_allocvec(self).expect("Session::serialize") } @@ -313,3 +315,226 @@ impl Entry { from_bytes(bytes) } } + +pub struct EntryWriter<'db> { + db: &'db DB, + buffer: File, + hasher: Hasher, + buffer_path: PathBuf, + entry_key: String, + timestamp: Timestamp, + is_public: bool, +} + +impl<'db> EntryWriter<'db> { + pub fn new(db: &'db DB, public_key: &PublicKey, path: &str) -> anyhow::Result { + let hasher = Hasher::new(); + + let timestamp = Timestamp::now(); + + let buffer_path = db.buffers_dir.join(timestamp.to_string()); + + let buffer = File::create(&buffer_path)?; + + let entry_key = format!("{public_key}/{path}"); + + Ok(Self { + db, + buffer, + hasher, + buffer_path, + entry_key, + timestamp, + is_public: path.starts_with("pub/"), + }) + } + + /// Same ase [EntryWriter::write_all] but returns a Result of a mutable reference of itself + /// to enable chaining with [Self::commit]. + pub fn update(&mut self, chunk: &[u8]) -> Result<&mut Self, std::io::Error> { + self.write_all(chunk)?; + + Ok(self) + } + + /// Commit blob from the filesystem buffer to LMDB, + /// write the [Entry], and commit the write transaction. + pub fn commit(&self) -> anyhow::Result { + let hash = self.hasher.finalize(); + + let mut buffer = File::open(&self.buffer_path)?; + + let mut wtxn = self.db.env.write_txn()?; + + let mut chunk_key = [0; 12]; + chunk_key[0..8].copy_from_slice(&self.timestamp.to_bytes()); + + let mut chunk_index: u32 = 0; + + loop { + let mut chunk = vec![0_u8; self.db.max_chunk_size]; + + let bytes_read = buffer.read(&mut chunk)?; + + if bytes_read == 0 { + break; // EOF reached + } + + chunk_key[8..].copy_from_slice(&chunk_index.to_be_bytes()); + + self.db + .tables + .blobs + .put(&mut wtxn, &chunk_key, &chunk[..bytes_read])?; + + chunk_index += 1; + } + + let mut entry = Entry::new(); + entry.set_timestamp(&self.timestamp); + + entry.set_content_hash(hash); + + let length = buffer.metadata()?.len(); + entry.set_content_length(length as usize); + + self.db + .tables + .entries + .put(&mut wtxn, &self.entry_key, &entry.serialize())?; + + // Write a public [Event]. + if self.is_public { + let url = format!("pubky://{}", self.entry_key); + let event = Event::put(&url); + let value = event.serialize(); + + let key = entry.timestamp.to_string(); + + self.db.tables.events.put(&mut wtxn, &key, &value)?; + + // TODO: delete events older than a threshold. + // TODO: move to events.rs + } + + wtxn.commit()?; + + std::fs::remove_file(&self.buffer_path)?; + + Ok(entry) + } +} + +impl<'db> std::io::Write for EntryWriter<'db> { + /// Write a chunk to a Filesystem based buffer. + #[inline] + fn write(&mut self, chunk: &[u8]) -> std::io::Result { + self.hasher.update(chunk); + self.buffer.write_all(chunk)?; + + Ok(chunk.len()) + } + + /// Does not do anything, you need to call [Self::commit] + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use bytes::Bytes; + use pkarr::{mainline::Testnet, Keypair}; + + use crate::config::Config; + + use super::DB; + + #[tokio::test] + async fn entries() -> anyhow::Result<()> { + let mut db = DB::open(Config::test(&Testnet::new(0))).unwrap(); + + let keypair = Keypair::random(); + let public_key = keypair.public_key(); + let path = "/pub/foo.txt"; + + let chunk = Bytes::from(vec![1, 2, 3, 4, 5]); + + db.write_entry(&public_key, path)? + .update(&chunk)? + .commit()?; + + let rtxn = db.env.read_txn().unwrap(); + let entry = db.get_entry(&rtxn, &public_key, path).unwrap().unwrap(); + + assert_eq!( + entry.content_hash(), + &[ + 2, 79, 103, 192, 66, 90, 61, 192, 47, 186, 245, 140, 185, 61, 229, 19, 46, 61, 117, + 197, 25, 250, 160, 186, 218, 33, 73, 29, 136, 201, 112, 87 + ] + ); + + let mut blob = vec![]; + + { + let mut iter = entry.read_content(&db, &rtxn).unwrap(); + + while let Some(Ok(chunk)) = iter.next() { + blob.extend_from_slice(&chunk); + } + } + + assert_eq!(blob, vec![1, 2, 3, 4, 5]); + + rtxn.commit().unwrap(); + + Ok(()) + } + + #[tokio::test] + async fn chunked_entry() -> anyhow::Result<()> { + let mut db = DB::open(Config::test(&Testnet::new(0))).unwrap(); + + let keypair = Keypair::random(); + let public_key = keypair.public_key(); + let path = "/pub/foo.txt"; + + let chunk = Bytes::from(vec![0; 1024 * 1024]); + + db.write_entry(&public_key, path)? + .update(&chunk)? + .commit()?; + + let rtxn = db.env.read_txn().unwrap(); + let entry = db.get_entry(&rtxn, &public_key, path).unwrap().unwrap(); + + assert_eq!( + entry.content_hash(), + &[ + 72, 141, 226, 2, 247, 59, 217, 118, 222, 78, 112, 72, 244, 225, 243, 154, 119, 109, + 134, 213, 130, 183, 52, 143, 245, 59, 244, 50, 185, 135, 252, 168 + ] + ); + + let mut blob = vec![]; + + { + let mut iter = entry.read_content(&db, &rtxn).unwrap(); + + while let Some(Ok(chunk)) = iter.next() { + blob.extend_from_slice(&chunk); + } + } + + assert_eq!(blob, vec![0; 1024 * 1024]); + + let stats = db.tables.blobs.stat(&rtxn).unwrap(); + assert_eq!(stats.overflow_pages, 0); + + rtxn.commit().unwrap(); + + Ok(()) + } +} diff --git a/pubky-homeserver/src/error.rs b/pubky-homeserver/src/error.rs index 8aa58d2..1a8f8a0 100644 --- a/pubky-homeserver/src/error.rs +++ b/pubky-homeserver/src/error.rs @@ -5,6 +5,7 @@ use axum::{ http::StatusCode, response::IntoResponse, }; +use tokio::task::JoinError; use tracing::debug; pub type Result = core::result::Result; @@ -126,3 +127,24 @@ impl From> for Error { Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) } } + +impl From for Error { + fn from(error: flume::RecvError) -> Self { + debug!(?error); + Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) + } +} + +impl From for Error { + fn from(error: JoinError) -> Self { + debug!(?error); + Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) + } +} + +impl From for Error { + fn from(error: axum::http::Error) -> Self { + debug!(?error); + Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.into()) + } +} diff --git a/pubky-homeserver/src/routes.rs b/pubky-homeserver/src/routes.rs index 7422f20..6404ffb 100644 --- a/pubky-homeserver/src/routes.rs +++ b/pubky-homeserver/src/routes.rs @@ -1,6 +1,6 @@ use axum::{ extract::DefaultBodyLimit, - routing::{delete, get, post, put}, + routing::{delete, get, head, post, put}, Router, }; use tower_cookies::CookieManagerLayer; @@ -25,12 +25,13 @@ fn base(state: AppState) -> Router { .route("/:pubky/session", delete(auth::signout)) .route("/:pubky/*path", put(public::put)) .route("/:pubky/*path", get(public::get)) + .route("/:pubky/*path", head(public::head)) .route("/:pubky/*path", delete(public::delete)) .route("/events/", get(feed::feed)) .layer(CookieManagerLayer::new()) // TODO: revisit if we enable streaming big payloads // TODO: maybe add to a separate router (drive router?). - .layer(DefaultBodyLimit::max(16 * 1024)) + .layer(DefaultBodyLimit::max(100 * 1024 * 1024)) .with_state(state) } diff --git a/pubky-homeserver/src/routes/auth.rs b/pubky-homeserver/src/routes/auth.rs index dbcffe4..eaf3a57 100644 --- a/pubky-homeserver/src/routes/auth.rs +++ b/pubky-homeserver/src/routes/auth.rs @@ -1,7 +1,6 @@ use axum::{ - debug_handler, - extract::State, - http::{uri::Scheme, StatusCode, Uri}, + extract::{Host, State}, + http::StatusCode, response::IntoResponse, }; use axum_extra::{headers::UserAgent, TypedHeader}; @@ -20,17 +19,16 @@ use crate::{ server::AppState, }; -#[debug_handler] pub async fn signup( State(state): State, user_agent: Option>, cookies: Cookies, - uri: Uri, + host: Host, body: Bytes, ) -> Result { // TODO: Verify invitation link. // TODO: add errors in case of already axisting user. - signin(State(state), user_agent, cookies, uri, body).await + signin(State(state), user_agent, cookies, host, body).await } pub async fn session( @@ -89,7 +87,7 @@ pub async fn signin( State(state): State, user_agent: Option>, cookies: Cookies, - uri: Uri, + Host(host): Host, body: Bytes, ) -> Result { let token = state.verifier.verify(&body)?; @@ -106,7 +104,7 @@ pub async fn signin( &mut wtxn, public_key, &User { - created_at: Timestamp::now().into_inner(), + created_at: Timestamp::now().as_u64(), }, )?; } @@ -124,7 +122,8 @@ pub async fn signin( let mut cookie = Cookie::new(public_key.to_string(), session_secret); cookie.set_path("/"); - if *uri.scheme().unwrap_or(&Scheme::HTTP) == Scheme::HTTPS { + + if is_secure(&host) { cookie.set_secure(true); cookie.set_same_site(SameSite::None); } @@ -136,3 +135,34 @@ pub async fn signin( Ok(session) } + +/// Assuming that if the server is addressed by anything other than +/// localhost, or IP addresses, it is not addressed from a browser in an +/// secure (HTTPs) window, thus it no need to `secure` and `same_site=none` to cookies +fn is_secure(host: &str) -> bool { + url::Host::parse(host) + .map(|host| match host { + url::Host::Domain(domain) => domain != "localhost", + _ => false, + }) + .unwrap_or_default() +} + +#[cfg(test)] +mod tests { + use pkarr::Keypair; + + use super::*; + + #[test] + fn test_is_secure() { + assert!(!is_secure("")); + assert!(!is_secure("127.0.0.1")); + assert!(!is_secure("167.86.102.121")); + assert!(!is_secure("[2001:0db8:0000:0000:0000:ff00:0042:8329]")); + assert!(!is_secure("localhost")); + assert!(!is_secure("localhost:23423")); + assert!(is_secure(&Keypair::random().public_key().to_string())); + assert!(is_secure("example.com")); + } +} diff --git a/pubky-homeserver/src/routes/feed.rs b/pubky-homeserver/src/routes/feed.rs index 6271aeb..a54b8a5 100644 --- a/pubky-homeserver/src/routes/feed.rs +++ b/pubky-homeserver/src/routes/feed.rs @@ -4,7 +4,7 @@ use axum::{ http::{header, Response, StatusCode}, response::IntoResponse, }; -use pubky_common::timestamp::{Timestamp, TimestampError}; +use pubky_common::timestamp::Timestamp; use crate::{ error::{Error, Result}, @@ -17,17 +17,11 @@ pub async fn feed( params: ListQueryParams, ) -> Result { if let Some(ref cursor) = params.cursor { - if let Err(timestmap_error) = Timestamp::try_from(cursor.to_string()) { - let cause = match timestmap_error { - TimestampError::InvalidEncoding => { - "Cursor should be valid base32 Crockford encoding of a timestamp" - } - TimestampError::InvalidBytesLength(size) => { - &format!("Cursor should be 13 characters long, got: {size}") - } - }; - - Err(Error::new(StatusCode::BAD_REQUEST, cause.into()))? + if Timestamp::try_from(cursor.to_string()).is_err() { + Err(Error::new( + StatusCode::BAD_REQUEST, + "Cursor should be valid base32 Crockford encoding of a timestamp".into(), + ))? } } diff --git a/pubky-homeserver/src/routes/public.rs b/pubky-homeserver/src/routes/public.rs index 8c6b2b9..3b9963f 100644 --- a/pubky-homeserver/src/routes/public.rs +++ b/pubky-homeserver/src/routes/public.rs @@ -1,14 +1,18 @@ use axum::{ - body::{Body, Bytes}, + body::Body, + debug_handler, extract::State, - http::{header, Response, StatusCode}, + http::{header, HeaderMap, HeaderValue, Response, StatusCode}, response::IntoResponse, }; use futures_util::stream::StreamExt; +use httpdate::HttpDate; use pkarr::PublicKey; +use std::{io::Write, str::FromStr}; use tower_cookies::Cookies; use crate::{ + database::tables::entries::Entry, error::{Error, Result}, extractors::{EntryPath, ListQueryParams, Pubky}, server::AppState, @@ -22,53 +26,37 @@ pub async fn put( body: Body, ) -> Result { let public_key = pubky.public_key().clone(); - let path = path.as_str(); + let path = path.as_str().to_string(); - verify(path)?; - authorize(&mut state, cookies, &public_key, path)?; + verify(&path)?; + authorize(&mut state, cookies, &public_key, &path)?; + + let mut entry_writer = state.db.write_entry(&public_key, &path)?; let mut stream = body.into_data_stream(); - - let (tx, rx) = flume::bounded::(1); - - let path = path.to_string(); - - // TODO: refactor Database to clean up this scope. - let done = tokio::task::spawn_blocking(move || -> Result<()> { - // TODO: this is a blocking operation, which is ok for small - // payloads (we have 16 kb limit for now) but later we need - // to stream this to filesystem, and keep track of any failed - // writes to GC these files later. - - state.db.put_entry(&public_key, &path, rx)?; - - Ok(()) - }); - while let Some(next) = stream.next().await { let chunk = next?; - - tx.send(chunk)?; + entry_writer.write_all(&chunk)?; } - drop(tx); - done.await.expect("join error")?; + let _entry = entry_writer.commit()?; // TODO: return relevant headers, like Etag? Ok(()) } +#[debug_handler] pub async fn get( State(state): State, + headers: HeaderMap, pubky: Pubky, path: EntryPath, params: ListQueryParams, ) -> Result { verify(path.as_str())?; - let public_key = pubky.public_key(); - - let path = path.as_str(); + let public_key = pubky.public_key().clone(); + let path = path.as_str().to_string(); if path.ends_with('/') { let txn = state.db.env.read_txn()?; @@ -95,16 +83,105 @@ pub async fn get( return Ok(Response::builder() .status(StatusCode::OK) .header(header::CONTENT_TYPE, "text/plain") - .body(Body::from(vec.join("\n"))) - .unwrap()); + .body(Body::from(vec.join("\n")))?); } - // TODO: Enable streaming + let (entry_tx, entry_rx) = flume::bounded::>(1); + let (chunks_tx, chunks_rx) = flume::unbounded::, heed::Error>>(); - match state.db.get_blob(public_key, path) { - Err(error) => Err(error)?, - Ok(Some(bytes)) => Ok(Response::builder().body(Body::from(bytes)).unwrap()), - Ok(None) => Err(Error::new(StatusCode::NOT_FOUND, "File Not Found".into())), + tokio::task::spawn_blocking(move || -> anyhow::Result<()> { + let rtxn = state.db.env.read_txn()?; + + let option = state.db.get_entry(&rtxn, &public_key, &path)?; + + if let Some(entry) = option { + let iter = entry.read_content(&state.db, &rtxn)?; + + entry_tx.send(Some(entry))?; + + for next in iter { + chunks_tx.send(next.map(|b| b.to_vec()))?; + } + }; + + entry_tx.send(None)?; + + Ok(()) + }); + + get_entry( + headers, + entry_rx.recv_async().await?, + Some(Body::from_stream(chunks_rx.into_stream())), + ) +} + +pub async fn head( + State(state): State, + headers: HeaderMap, + pubky: Pubky, + path: EntryPath, +) -> Result { + verify(path.as_str())?; + + let rtxn = state.db.env.read_txn()?; + + get_entry( + headers, + state + .db + .get_entry(&rtxn, pubky.public_key(), path.as_str())?, + None, + ) +} + +pub fn get_entry( + headers: HeaderMap, + entry: Option, + body: Option, +) -> Result> { + if let Some(entry) = entry { + // TODO: Enable seek API (range requests) + // TODO: Gzip? or brotli? + + let mut response = HeaderMap::from(&entry).into_response(); + + // Handle IF_MODIFIED_SINCE + if let Some(condition_http_date) = headers + .get(header::IF_MODIFIED_SINCE) + .and_then(|h| h.to_str().ok()) + .and_then(|s| HttpDate::from_str(s).ok()) + { + let entry_http_date: HttpDate = entry.timestamp().to_owned().into(); + + if condition_http_date >= entry_http_date { + *response.status_mut() = StatusCode::NOT_MODIFIED; + } + }; + + // Handle IF_NONE_MATCH + if let Some(str) = headers + .get(header::IF_NONE_MATCH) + .and_then(|h| h.to_str().ok()) + { + let etag = format!("\"{}\"", entry.content_hash()); + if str + .trim() + .split(',') + .collect::>() + .contains(&etag.as_str()) + { + *response.status_mut() = StatusCode::NOT_MODIFIED; + }; + } + + if let Some(body) = body { + *response.body_mut() = body; + }; + + Ok(response) + } else { + Err(Error::with_status(StatusCode::NOT_FOUND))? } } @@ -120,14 +197,13 @@ pub async fn delete( authorize(&mut state, cookies, &public_key, path)?; verify(path)?; + // TODO: should we wrap this with `tokio::task::spawn_blocking` in case it takes too long? let deleted = state.db.delete_entry(&public_key, path)?; if !deleted { // TODO: if the path ends with `/` return a `CONFLICT` error? return Err(Error::with_status(StatusCode::NOT_FOUND)); - } - - // TODO: return relevant headers, like Etag? + }; Ok(()) } @@ -172,3 +248,133 @@ fn verify(path: &str) -> Result<()> { Ok(()) } + +impl From<&Entry> for HeaderMap { + fn from(entry: &Entry) -> Self { + let mut headers = HeaderMap::new(); + headers.insert(header::CONTENT_LENGTH, entry.content_length().into()); + headers.insert( + header::LAST_MODIFIED, + HeaderValue::from_str(&entry.timestamp().format_http_date()) + .expect("http date is valid header value"), + ); + headers.insert( + header::CONTENT_TYPE, + // TODO: when setting content type from user input, we should validate it as a HeaderValue + entry + .content_type() + .try_into() + .or(HeaderValue::from_str("")) + .expect("valid header value"), + ); + headers.insert( + header::ETAG, + format!("\"{}\"", entry.content_hash()) + .try_into() + .expect("hex string is valid"), + ); + + headers + } +} + +#[cfg(test)] +mod tests { + use axum::http::header; + use pkarr::{mainline::Testnet, Keypair}; + use reqwest::{self, Method, StatusCode}; + + use crate::Homeserver; + + #[tokio::test] + async fn if_last_modified() -> anyhow::Result<()> { + let testnet = Testnet::new(3); + let mut server = Homeserver::start_test(&testnet).await?; + + let public_key = Keypair::random().public_key(); + + let data = &[1, 2, 3, 4, 5]; + + server + .database_mut() + .write_entry(&public_key, "pub/foo")? + .update(data)? + .commit()?; + + let client = reqwest::Client::builder().build()?; + + let url = format!("http://localhost:{}/{public_key}/pub/foo", server.port()); + + let response = client.request(Method::GET, &url).send().await?; + + let response = client + .request(Method::GET, &url) + .header( + header::IF_MODIFIED_SINCE, + response.headers().get(header::LAST_MODIFIED).unwrap(), + ) + .send() + .await?; + + assert_eq!(response.status(), StatusCode::NOT_MODIFIED); + + let response = client + .request(Method::HEAD, &url) + .header( + header::IF_MODIFIED_SINCE, + response.headers().get(header::LAST_MODIFIED).unwrap(), + ) + .send() + .await?; + + assert_eq!(response.status(), StatusCode::NOT_MODIFIED); + + Ok(()) + } + + #[tokio::test] + async fn if_none_match() -> anyhow::Result<()> { + let testnet = Testnet::new(3); + let mut server = Homeserver::start_test(&testnet).await?; + + let public_key = Keypair::random().public_key(); + + let data = &[1, 2, 3, 4, 5]; + + server + .database_mut() + .write_entry(&public_key, "pub/foo")? + .update(data)? + .commit()?; + + let client = reqwest::Client::builder().build()?; + + let url = format!("http://localhost:{}/{public_key}/pub/foo", server.port()); + + let response = client.request(Method::GET, &url).send().await?; + + let response = client + .request(Method::GET, &url) + .header( + header::IF_NONE_MATCH, + response.headers().get(header::ETAG).unwrap(), + ) + .send() + .await?; + + assert_eq!(response.status(), StatusCode::NOT_MODIFIED); + + let response = client + .request(Method::HEAD, &url) + .header( + header::IF_NONE_MATCH, + response.headers().get(header::ETAG).unwrap(), + ) + .send() + .await?; + + assert_eq!(response.status(), StatusCode::NOT_MODIFIED); + + Ok(()) + } +} diff --git a/pubky-homeserver/src/server.rs b/pubky-homeserver/src/server.rs index 15c4b3f..0bffc92 100644 --- a/pubky-homeserver/src/server.rs +++ b/pubky-homeserver/src/server.rs @@ -97,6 +97,11 @@ impl Homeserver { self.state.config.keypair().public_key() } + #[cfg(test)] + pub(crate) fn database_mut(&mut self) -> &mut DB { + &mut self.state.db + } + // === Public Methods === /// Shutdown the server and wait for all tasks to complete. diff --git a/pubky/Cargo.toml b/pubky/Cargo.toml index 31d15d2..365ba76 100644 --- a/pubky/Cargo.toml +++ b/pubky/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "pubky" -version = "0.1.0" +version = "0.3.0" edition = "2021" -description = "Pubky client" +description = "Pubky core client" license = "MIT" repository = "https://github.com/pubky/pubky" keywords = ["web", "dht", "dns", "decentralized", "identity"] @@ -17,8 +17,8 @@ url = "2.5.2" bytes = "^1.7.1" base64 = "0.22.1" +pkarr = { git = "https://github.com/Pubky/pkarr", branch = "v3", package = "pkarr" } pubky-common = { version = "0.1.0", path = "../pubky-common" } -pkarr = { workspace = true, features = ["endpoints"] } # Native dependencies [target.'cfg(not(target_arch = "wasm32"))'.dependencies] @@ -36,7 +36,7 @@ js-sys = "0.3.69" web-sys = "0.3.70" [dev-dependencies] -pubky_homeserver = { path = "../pubky-homeserver" } +pubky-homeserver = { path = "../pubky-homeserver" } tokio = "1.37.0" [features] diff --git a/pubky/README.md b/pubky/README.md new file mode 100644 index 0000000..d300281 --- /dev/null +++ b/pubky/README.md @@ -0,0 +1,53 @@ +# Pubky + +Rust implementation implementation of [Pubky](https://github.com/pubky/pubky-core) client. + +## Quick Start + +```rust +use pkarr::mainline::Testnet; +use pkarr::Keypair; +use pubky_homeserver::Homeserver; +use pubky::PubkyClient; + +#[tokio::main] +async fn main () { + // Mainline Dht testnet and a temporary homeserver for unit testing. + let testnet = Testnet::new(10); + let server = Homeserver::start_test(&testnet).await.unwrap(); + + let client = PubkyClient::test(&testnet); + + // Uncomment the following line instead if you are not just testing. + // let client PubkyClient::builder().build(); + + // Generate a keypair + let keypair = Keypair::random(); + + // Signup to a Homeserver + let keypair = Keypair::random(); + client.signup(&keypair, &server.public_key()).await.unwrap(); + + // Write data. + let url = format!("pubky://{}/pub/foo.txt", keypair.public_key()); + let url = url.as_str(); + + client.put(url, &[0, 1, 2, 3, 4]).await.unwrap(); + + // Read using a Public key based link + let response = client.get(url).await.unwrap().unwrap(); + + assert_eq!(response, bytes::Bytes::from(vec![0, 1, 2, 3, 4])); + + // Delet an entry. + client.delete(url).await.unwrap(); + + let response = client.get(url).await.unwrap(); + + assert_eq!(response, None); +} +``` + +## Example code + +Check more [examples](https://github.com/pubky/pubky-core/tree/main/examples) for using the Pubky client. diff --git a/pubky/pkg/README.md b/pubky/pkg/README.md index 81b2cf4..4704fff 100644 --- a/pubky/pkg/README.md +++ b/pubky/pkg/README.md @@ -1,6 +1,6 @@ # Pubky -JavaScript implementation of [Pubky](https://github.com/pubky/pubky). +JavaScript implementation of [Pubky](https://github.com/pubky/pubky-core) client. ## Table of Contents - [Install](#install) diff --git a/pubky/src/error.rs b/pubky/src/error.rs index ec6d125..92fae7f 100644 --- a/pubky/src/error.rs +++ b/pubky/src/error.rs @@ -6,7 +6,7 @@ use pkarr::dns::SimpleDnsError; pub type Result = core::result::Result; #[derive(thiserror::Error, Debug)] -/// Pk common Error +/// Pubky crate's common Error enum pub enum Error { /// For starter, to remove as code matures. #[error("Generic error: {0}")] diff --git a/pubky/src/lib.rs b/pubky/src/lib.rs index b251d3e..e503c34 100644 --- a/pubky/src/lib.rs +++ b/pubky/src/lib.rs @@ -1,3 +1,6 @@ +#![doc = include_str!("../README.md")] +//! + mod error; mod shared; @@ -14,6 +17,7 @@ pub use error::Error; #[cfg(not(target_arch = "wasm32"))] pub use crate::shared::list_builder::ListBuilder; +/// A client for Pubky homeserver API, as well as generic HTTP requests to Pubky urls. #[derive(Debug, Clone)] #[wasm_bindgen] pub struct PubkyClient { diff --git a/pubky/src/native.rs b/pubky/src/native.rs index fb163f3..e053b28 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -1,15 +1,12 @@ use std::time::Duration; -use std::{net::ToSocketAddrs, sync::Arc}; -use ::pkarr::mainline::dht::Testnet; +use pkarr::mainline::Testnet; use crate::PubkyClient; mod api; mod internals; -use internals::PkarrResolver; - static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); #[derive(Debug, Default)] @@ -55,12 +52,11 @@ impl Settings { // TODO: convert to Result let pkarr = pkarr::Client::new(self.pkarr_settings).unwrap(); - let dns_resolver: PkarrResolver = (&pkarr).into(); PubkyClient { http: reqwest::Client::builder() .cookie_store(true) - .dns_resolver(Arc::new(dns_resolver)) + // .dns_resolver(Arc::new(dns_resolver)) .user_agent(DEFAULT_USER_AGENT) .build() .unwrap(), @@ -94,7 +90,7 @@ impl PubkyClient { /// - DHT bootstrap nodes set to the `testnet` bootstrap nodes. /// - DHT request timout set to 500 milliseconds. (unless in CI, then it is left as default 2000) /// - /// For more control, you can use [PubkyClientBuilder::testnet] + /// For more control, you can use [PubkyClient::builder] testnet option. pub fn test(testnet: &Testnet) -> PubkyClient { let mut builder = PubkyClient::builder().testnet(testnet); diff --git a/pubky/src/native/internals.rs b/pubky/src/native/internals.rs index 4e9f593..69ef73c 100644 --- a/pubky/src/native/internals.rs +++ b/pubky/src/native/internals.rs @@ -3,38 +3,6 @@ use url::Url; use crate::PubkyClient; -use std::net::ToSocketAddrs; - -use pkarr::{Client, EndpointResolver, PublicKey}; -use reqwest::dns::{Addrs, Resolve}; - -pub struct PkarrResolver(Client); - -impl Resolve for PkarrResolver { - fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving { - let client = self.0.clone(); - - Box::pin(async move { - let name = name.as_str(); - - if PublicKey::try_from(name).is_ok() { - let endpoint = client.resolve_endpoint(name).await?; - - let addrs: Addrs = Box::new(endpoint.to_socket_addrs().into_iter()); - return Ok(addrs); - }; - - Ok(Box::new(format!("{name}:0").to_socket_addrs().unwrap())) - }) - } -} - -impl From<&pkarr::Client> for PkarrResolver { - fn from(pkarr: &pkarr::Client) -> Self { - PkarrResolver(pkarr.clone()) - } -} - impl PubkyClient { // === HTTP === diff --git a/pubky/src/shared/auth.rs b/pubky/src/shared/auth.rs index c545adc..ae96a0a 100644 --- a/pubky/src/shared/auth.rs +++ b/pubky/src/shared/auth.rs @@ -149,20 +149,7 @@ impl PubkyClient { Ok(()) } - pub async fn inner_third_party_signin( - &self, - encrypted_token: &[u8], - client_secret: &[u8; 32], - ) -> Result { - let decrypted = decrypt(encrypted_token, client_secret)?; - let token = AuthToken::deserialize(&decrypted)?; - - self.signin_with_authtoken(&token).await?; - - Ok(token.pubky().to_owned()) - } - - pub async fn signin_with_authtoken(&self, token: &AuthToken) -> Result { + pub(crate) async fn signin_with_authtoken(&self, token: &AuthToken) -> Result { let mut url = Url::parse(&format!("https://{}/session", token.pubky()))?; self.resolve_url(&mut url).await?; diff --git a/pubky/src/shared/list_builder.rs b/pubky/src/shared/list_builder.rs index b76fee4..851007f 100644 --- a/pubky/src/shared/list_builder.rs +++ b/pubky/src/shared/list_builder.rs @@ -3,6 +3,7 @@ use url::Url; use crate::{error::Result, PubkyClient}; +/// Helper struct to edit Pubky homeserver's list API options before sending them. #[derive(Debug)] pub struct ListBuilder<'a> { url: Url, diff --git a/pubky/src/shared/pkarr.rs b/pubky/src/shared/pkarr.rs index 9e2fba7..e202b12 100644 --- a/pubky/src/shared/pkarr.rs +++ b/pubky/src/shared/pkarr.rs @@ -37,7 +37,7 @@ impl PubkyClient { "_pubky".try_into().unwrap(), pkarr::dns::CLASS::IN, 60 * 60, - pkarr::dns::rdata::RData::SVCB(svcb), + pkarr::dns::rdata::RData::HTTPS(svcb.into()), )); let signed_packet = SignedPacket::from_packet(keypair, &packet)?; diff --git a/pubky/src/shared/public.rs b/pubky/src/shared/public.rs index 6ded72a..eb47b77 100644 --- a/pubky/src/shared/public.rs +++ b/pubky/src/shared/public.rs @@ -99,6 +99,7 @@ mod tests { use crate::*; + use bytes::Bytes; use pkarr::{mainline::Testnet, Keypair}; use pubky_homeserver::Homeserver; use reqwest::{Method, StatusCode}; @@ -751,7 +752,7 @@ mod tests { ); } - let get = client.get(url.as_str()).await.unwrap().unwrap(); + let get = client.get(url).await.unwrap().unwrap(); assert_eq!(get.as_ref(), &[0]); } @@ -808,4 +809,35 @@ mod tests { ] ) } + + #[tokio::test] + async fn stream() { + // TODO: test better streaming API + + let testnet = Testnet::new(10); + let server = Homeserver::start_test(&testnet).await.unwrap(); + + let client = PubkyClient::test(&testnet); + + let keypair = Keypair::random(); + + client.signup(&keypair, &server.public_key()).await.unwrap(); + + let url = format!("pubky://{}/pub/foo.txt", keypair.public_key()); + let url = url.as_str(); + + let bytes = Bytes::from(vec![0; 1024 * 1024]); + + client.put(url, &bytes).await.unwrap(); + + let response = client.get(url).await.unwrap().unwrap(); + + assert_eq!(response, bytes); + + client.delete(url).await.unwrap(); + + let response = client.get(url).await.unwrap(); + + assert_eq!(response, None); + } }