Microservice to bring 2FA to self hosted PDSes

Compare changes

Choose any two refs to compare.

+1692 -95
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 + name = "abnf" 7 + version = "0.13.0" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "087113bd50d9adce24850eed5d0476c7d199d532fce8fab5173650331e09033a" 10 + dependencies = [ 11 + "abnf-core", 12 + "nom 7.1.3", 13 + ] 14 + 15 + [[package]] 16 + name = "abnf-core" 17 + version = "0.5.0" 18 + source = "registry+https://github.com/rust-lang/crates.io-index" 19 + checksum = "c44e09c43ae1c368fb91a03a566472d0087c26cf7e1b9e8e289c14ede681dd7d" 20 + dependencies = [ 21 + "nom 7.1.3", 22 + ] 23 + 24 + [[package]] 6 25 name = "addr2line" 7 26 version = "0.24.2" 8 27 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 39 58 ] 40 59 41 60 [[package]] 42 - name = "allocator-api2" 43 - version = "0.2.21" 61 + name = "aliasable" 62 + version = "0.1.3" 44 63 source = "registry+https://github.com/rust-lang/crates.io-index" 45 - checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 64 + checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" 46 65 47 66 [[package]] 48 - name = "android-tzdata" 49 - version = "0.1.1" 67 + name = "allocator-api2" 68 + version = "0.2.21" 50 69 source = "registry+https://github.com/rust-lang/crates.io-index" 51 - checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 70 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 52 71 53 72 [[package]] 54 73 name = "android_system_properties" ··· 66 85 checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 67 86 68 87 [[package]] 88 + name = "async-channel" 89 + version = "1.9.0" 90 + source = "registry+https://github.com/rust-lang/crates.io-index" 91 + checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" 92 + dependencies = [ 93 + "concurrent-queue", 94 + "event-listener 2.5.3", 95 + "futures-core", 96 + ] 97 + 98 + [[package]] 99 + name = "async-channel" 100 + version = "2.5.0" 101 + source = "registry+https://github.com/rust-lang/crates.io-index" 102 + checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" 103 + dependencies = [ 104 + "concurrent-queue", 105 + "event-listener-strategy", 106 + "futures-core", 107 + "pin-project-lite", 108 + ] 109 + 110 + [[package]] 69 111 name = "async-compression" 70 112 version = "0.4.27" 71 113 source = "registry+https://github.com/rust-lang/crates.io-index" 72 114 checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" 73 115 dependencies = [ 116 + "flate2", 74 117 "futures-core", 75 118 "memchr", 76 119 "pin-project-lite", ··· 80 123 ] 81 124 82 125 [[package]] 126 + name = "async-executor" 127 + version = "1.13.3" 128 + source = "registry+https://github.com/rust-lang/crates.io-index" 129 + checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" 130 + dependencies = [ 131 + "async-task", 132 + "concurrent-queue", 133 + "fastrand", 134 + "futures-lite", 135 + "pin-project-lite", 136 + "slab", 137 + ] 138 + 139 + [[package]] 140 + name = "async-global-executor" 141 + version = "2.4.1" 142 + source = "registry+https://github.com/rust-lang/crates.io-index" 143 + checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" 144 + dependencies = [ 145 + "async-channel 2.5.0", 146 + "async-executor", 147 + "async-io", 148 + "async-lock", 149 + "blocking", 150 + "futures-lite", 151 + "once_cell", 152 + ] 153 + 154 + [[package]] 155 + name = "async-io" 156 + version = "2.6.0" 157 + source = "registry+https://github.com/rust-lang/crates.io-index" 158 + checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" 159 + dependencies = [ 160 + "autocfg", 161 + "cfg-if", 162 + "concurrent-queue", 163 + "futures-io", 164 + "futures-lite", 165 + "parking", 166 + "polling", 167 + "rustix 1.1.2", 168 + "slab", 169 + "windows-sys 0.61.2", 170 + ] 171 + 172 + [[package]] 173 + name = "async-lock" 174 + version = "3.4.1" 175 + source = "registry+https://github.com/rust-lang/crates.io-index" 176 + checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" 177 + dependencies = [ 178 + "event-listener 5.4.1", 179 + "event-listener-strategy", 180 + "pin-project-lite", 181 + ] 182 + 183 + [[package]] 184 + name = "async-process" 185 + version = "2.5.0" 186 + source = "registry+https://github.com/rust-lang/crates.io-index" 187 + checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" 188 + dependencies = [ 189 + "async-channel 2.5.0", 190 + "async-io", 191 + "async-lock", 192 + "async-signal", 193 + "async-task", 194 + "blocking", 195 + "cfg-if", 196 + "event-listener 5.4.1", 197 + "futures-lite", 198 + "rustix 1.1.2", 199 + ] 200 + 201 + [[package]] 202 + name = "async-signal" 203 + version = "0.2.13" 204 + source = "registry+https://github.com/rust-lang/crates.io-index" 205 + checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" 206 + dependencies = [ 207 + "async-io", 208 + "async-lock", 209 + "atomic-waker", 210 + "cfg-if", 211 + "futures-core", 212 + "futures-io", 213 + "rustix 1.1.2", 214 + "signal-hook-registry", 215 + "slab", 216 + "windows-sys 0.61.2", 217 + ] 218 + 219 + [[package]] 220 + name = "async-std" 221 + version = "1.13.2" 222 + source = "registry+https://github.com/rust-lang/crates.io-index" 223 + checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" 224 + dependencies = [ 225 + "async-channel 1.9.0", 226 + "async-global-executor", 227 + "async-io", 228 + "async-lock", 229 + "async-process", 230 + "crossbeam-utils", 231 + "futures-channel", 232 + "futures-core", 233 + "futures-io", 234 + "futures-lite", 235 + "gloo-timers", 236 + "kv-log-macro", 237 + "log", 238 + "memchr", 239 + "once_cell", 240 + "pin-project-lite", 241 + "pin-utils", 242 + "slab", 243 + "wasm-bindgen-futures", 244 + ] 245 + 246 + [[package]] 247 + name = "async-task" 248 + version = "4.7.1" 249 + source = "registry+https://github.com/rust-lang/crates.io-index" 250 + checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 251 + 252 + [[package]] 83 253 name = "async-trait" 84 254 version = "0.1.89" 85 255 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 87 257 dependencies = [ 88 258 "proc-macro2", 89 259 "quote", 90 - "syn", 260 + "syn 2.0.105", 91 261 ] 92 262 93 263 [[package]] ··· 198 368 dependencies = [ 199 369 "proc-macro2", 200 370 "quote", 201 - "syn", 371 + "syn 2.0.105", 202 372 ] 203 373 204 374 [[package]] ··· 229 399 ] 230 400 231 401 [[package]] 402 + name = "base-x" 403 + version = "0.2.11" 404 + source = "registry+https://github.com/rust-lang/crates.io-index" 405 + checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" 406 + 407 + [[package]] 408 + name = "base16ct" 409 + version = "0.2.0" 410 + source = "registry+https://github.com/rust-lang/crates.io-index" 411 + checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 412 + 413 + [[package]] 414 + name = "base256emoji" 415 + version = "1.0.2" 416 + source = "registry+https://github.com/rust-lang/crates.io-index" 417 + checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" 418 + dependencies = [ 419 + "const-str", 420 + "match-lookup", 421 + ] 422 + 423 + [[package]] 232 424 name = "base64" 233 425 version = "0.22.1" 234 426 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 257 449 "proc-macro2", 258 450 "quote", 259 451 "regex", 260 - "rustc-hash", 452 + "rustc-hash 1.1.0", 261 453 "shlex", 262 - "syn", 454 + "syn 2.0.105", 263 455 "which", 264 456 ] 265 457 ··· 282 474 ] 283 475 284 476 [[package]] 477 + name = "blocking" 478 + version = "1.6.2" 479 + source = "registry+https://github.com/rust-lang/crates.io-index" 480 + checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" 481 + dependencies = [ 482 + "async-channel 2.5.0", 483 + "async-task", 484 + "futures-io", 485 + "futures-lite", 486 + "piper", 487 + ] 488 + 489 + [[package]] 490 + name = "bon" 491 + version = "3.8.1" 492 + source = "registry+https://github.com/rust-lang/crates.io-index" 493 + checksum = "ebeb9aaf9329dff6ceb65c689ca3db33dbf15f324909c60e4e5eef5701ce31b1" 494 + dependencies = [ 495 + "bon-macros", 496 + "rustversion", 497 + ] 498 + 499 + [[package]] 500 + name = "bon-macros" 501 + version = "3.8.1" 502 + source = "registry+https://github.com/rust-lang/crates.io-index" 503 + checksum = "77e9d642a7e3a318e37c2c9427b5a6a48aa1ad55dcd986f3034ab2239045a645" 504 + dependencies = [ 505 + "darling 0.21.3", 506 + "ident_case", 507 + "prettyplease", 508 + "proc-macro2", 509 + "quote", 510 + "rustversion", 511 + "syn 2.0.105", 512 + ] 513 + 514 + [[package]] 515 + name = "borsh" 516 + version = "1.6.0" 517 + source = "registry+https://github.com/rust-lang/crates.io-index" 518 + checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" 519 + dependencies = [ 520 + "cfg_aliases", 521 + ] 522 + 523 + [[package]] 285 524 name = "bstr" 286 525 version = "1.12.0" 287 526 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 292 531 ] 293 532 294 533 [[package]] 534 + name = "btree-range-map" 535 + version = "0.7.2" 536 + source = "registry+https://github.com/rust-lang/crates.io-index" 537 + checksum = "1be5c9672446d3800bcbcaabaeba121fe22f1fb25700c4562b22faf76d377c33" 538 + dependencies = [ 539 + "btree-slab", 540 + "cc-traits", 541 + "range-traits", 542 + "serde", 543 + "slab", 544 + ] 545 + 546 + [[package]] 547 + name = "btree-slab" 548 + version = "0.6.1" 549 + source = "registry+https://github.com/rust-lang/crates.io-index" 550 + checksum = "7a2b56d3029f075c4fa892428a098425b86cef5c89ae54073137ece416aef13c" 551 + dependencies = [ 552 + "cc-traits", 553 + "slab", 554 + "smallvec", 555 + ] 556 + 557 + [[package]] 295 558 name = "bumpalo" 296 559 version = "3.19.0" 297 560 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 308 571 version = "1.10.1" 309 572 source = "registry+https://github.com/rust-lang/crates.io-index" 310 573 checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 574 + dependencies = [ 575 + "serde", 576 + ] 577 + 578 + [[package]] 579 + name = "cbor4ii" 580 + version = "0.2.14" 581 + source = "registry+https://github.com/rust-lang/crates.io-index" 582 + checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4" 583 + dependencies = [ 584 + "serde", 585 + ] 311 586 312 587 [[package]] 313 588 name = "cc" ··· 321 596 ] 322 597 323 598 [[package]] 599 + name = "cc-traits" 600 + version = "2.0.0" 601 + source = "registry+https://github.com/rust-lang/crates.io-index" 602 + checksum = "060303ef31ef4a522737e1b1ab68c67916f2a787bb2f4f54f383279adba962b5" 603 + dependencies = [ 604 + "slab", 605 + ] 606 + 607 + [[package]] 324 608 name = "cexpr" 325 609 version = "0.6.0" 326 610 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 336 620 checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 337 621 338 622 [[package]] 623 + name = "cfg_aliases" 624 + version = "0.2.1" 625 + source = "registry+https://github.com/rust-lang/crates.io-index" 626 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 627 + 628 + [[package]] 339 629 name = "chrono" 340 - version = "0.4.41" 630 + version = "0.4.42" 341 631 source = "registry+https://github.com/rust-lang/crates.io-index" 342 - checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 632 + checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" 343 633 dependencies = [ 344 - "android-tzdata", 345 634 "iana-time-zone", 346 635 "js-sys", 347 636 "num-traits", 637 + "serde", 348 638 "wasm-bindgen", 349 - "windows-link", 639 + "windows-link 0.2.1", 350 640 ] 351 641 352 642 [[package]] ··· 387 677 ] 388 678 389 679 [[package]] 680 + name = "cid" 681 + version = "0.11.1" 682 + source = "registry+https://github.com/rust-lang/crates.io-index" 683 + checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a" 684 + dependencies = [ 685 + "core2", 686 + "multibase", 687 + "multihash", 688 + "serde", 689 + "serde_bytes", 690 + "unsigned-varint", 691 + ] 692 + 693 + [[package]] 390 694 name = "cipher" 391 695 version = "0.4.4" 392 696 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 432 736 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 433 737 434 738 [[package]] 739 + name = "const-str" 740 + version = "0.4.3" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 743 + 744 + [[package]] 745 + name = "core-foundation" 746 + version = "0.9.4" 747 + source = "registry+https://github.com/rust-lang/crates.io-index" 748 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 749 + dependencies = [ 750 + "core-foundation-sys", 751 + "libc", 752 + ] 753 + 754 + [[package]] 435 755 name = "core-foundation-sys" 436 756 version = "0.8.7" 437 757 source = "registry+https://github.com/rust-lang/crates.io-index" 438 758 checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 439 759 440 760 [[package]] 761 + name = "core2" 762 + version = "0.4.0" 763 + source = "registry+https://github.com/rust-lang/crates.io-index" 764 + checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" 765 + dependencies = [ 766 + "memchr", 767 + ] 768 + 769 + [[package]] 441 770 name = "cpufeatures" 442 771 version = "0.2.17" 443 772 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 462 791 checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 463 792 464 793 [[package]] 794 + name = "crc32fast" 795 + version = "1.5.0" 796 + source = "registry+https://github.com/rust-lang/crates.io-index" 797 + checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" 798 + dependencies = [ 799 + "cfg-if", 800 + ] 801 + 802 + [[package]] 465 803 name = "crossbeam-queue" 466 804 version = "0.3.12" 467 805 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 481 819 version = "0.2.4" 482 820 source = "registry+https://github.com/rust-lang/crates.io-index" 483 821 checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 822 + 823 + [[package]] 824 + name = "crypto-bigint" 825 + version = "0.5.5" 826 + source = "registry+https://github.com/rust-lang/crates.io-index" 827 + checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" 828 + dependencies = [ 829 + "generic-array", 830 + "rand_core 0.6.4", 831 + "subtle", 832 + "zeroize", 833 + ] 484 834 485 835 [[package]] 486 836 name = "crypto-common" ··· 498 848 source = "registry+https://github.com/rust-lang/crates.io-index" 499 849 checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 500 850 dependencies = [ 501 - "darling_core", 502 - "darling_macro", 851 + "darling_core 0.20.11", 852 + "darling_macro 0.20.11", 853 + ] 854 + 855 + [[package]] 856 + name = "darling" 857 + version = "0.21.3" 858 + source = "registry+https://github.com/rust-lang/crates.io-index" 859 + checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" 860 + dependencies = [ 861 + "darling_core 0.21.3", 862 + "darling_macro 0.21.3", 503 863 ] 504 864 505 865 [[package]] ··· 513 873 "proc-macro2", 514 874 "quote", 515 875 "strsim", 516 - "syn", 876 + "syn 2.0.105", 877 + ] 878 + 879 + [[package]] 880 + name = "darling_core" 881 + version = "0.21.3" 882 + source = "registry+https://github.com/rust-lang/crates.io-index" 883 + checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" 884 + dependencies = [ 885 + "fnv", 886 + "ident_case", 887 + "proc-macro2", 888 + "quote", 889 + "strsim", 890 + "syn 2.0.105", 517 891 ] 518 892 519 893 [[package]] ··· 522 896 source = "registry+https://github.com/rust-lang/crates.io-index" 523 897 checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 524 898 dependencies = [ 525 - "darling_core", 899 + "darling_core 0.20.11", 900 + "quote", 901 + "syn 2.0.105", 902 + ] 903 + 904 + [[package]] 905 + name = "darling_macro" 906 + version = "0.21.3" 907 + source = "registry+https://github.com/rust-lang/crates.io-index" 908 + checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" 909 + dependencies = [ 910 + "darling_core 0.21.3", 526 911 "quote", 527 - "syn", 912 + "syn 2.0.105", 528 913 ] 529 914 530 915 [[package]] ··· 542 927 ] 543 928 544 929 [[package]] 930 + name = "data-encoding" 931 + version = "2.9.0" 932 + source = "registry+https://github.com/rust-lang/crates.io-index" 933 + checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 934 + 935 + [[package]] 936 + name = "data-encoding-macro" 937 + version = "0.1.18" 938 + source = "registry+https://github.com/rust-lang/crates.io-index" 939 + checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" 940 + dependencies = [ 941 + "data-encoding", 942 + "data-encoding-macro-internal", 943 + ] 944 + 945 + [[package]] 946 + name = "data-encoding-macro-internal" 947 + version = "0.1.16" 948 + source = "registry+https://github.com/rust-lang/crates.io-index" 949 + checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 950 + dependencies = [ 951 + "data-encoding", 952 + "syn 2.0.105", 953 + ] 954 + 955 + [[package]] 545 956 name = "der" 546 957 version = "0.7.10" 547 958 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 553 964 ] 554 965 555 966 [[package]] 967 + name = "deranged" 968 + version = "0.5.5" 969 + source = "registry+https://github.com/rust-lang/crates.io-index" 970 + checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" 971 + dependencies = [ 972 + "powerfmt", 973 + "serde_core", 974 + ] 975 + 976 + [[package]] 556 977 name = "derive_builder" 557 978 version = "0.20.2" 558 979 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 567 988 source = "registry+https://github.com/rust-lang/crates.io-index" 568 989 checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" 569 990 dependencies = [ 570 - "darling", 991 + "darling 0.20.11", 571 992 "proc-macro2", 572 993 "quote", 573 - "syn", 994 + "syn 2.0.105", 574 995 ] 575 996 576 997 [[package]] ··· 580 1001 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" 581 1002 dependencies = [ 582 1003 "derive_builder_core", 583 - "syn", 1004 + "syn 2.0.105", 584 1005 ] 585 1006 586 1007 [[package]] ··· 603 1024 dependencies = [ 604 1025 "proc-macro2", 605 1026 "quote", 606 - "syn", 1027 + "syn 2.0.105", 607 1028 ] 608 1029 609 1030 [[package]] ··· 619 1040 checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" 620 1041 621 1042 [[package]] 1043 + name = "dyn-clone" 1044 + version = "1.0.20" 1045 + source = "registry+https://github.com/rust-lang/crates.io-index" 1046 + checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" 1047 + 1048 + [[package]] 1049 + name = "ecdsa" 1050 + version = "0.16.9" 1051 + source = "registry+https://github.com/rust-lang/crates.io-index" 1052 + checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" 1053 + dependencies = [ 1054 + "der", 1055 + "digest", 1056 + "elliptic-curve", 1057 + "rfc6979", 1058 + "signature", 1059 + "spki", 1060 + ] 1061 + 1062 + [[package]] 622 1063 name = "either" 623 1064 version = "1.15.0" 624 1065 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 628 1069 ] 629 1070 630 1071 [[package]] 1072 + name = "elliptic-curve" 1073 + version = "0.13.8" 1074 + source = "registry+https://github.com/rust-lang/crates.io-index" 1075 + checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 1076 + dependencies = [ 1077 + "base16ct", 1078 + "crypto-bigint", 1079 + "digest", 1080 + "ff", 1081 + "generic-array", 1082 + "group", 1083 + "pem-rfc7468", 1084 + "pkcs8", 1085 + "rand_core 0.6.4", 1086 + "sec1", 1087 + "subtle", 1088 + "zeroize", 1089 + ] 1090 + 1091 + [[package]] 631 1092 name = "email-encoding" 632 1093 version = "0.4.1" 633 1094 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 642 1103 version = "0.2.9" 643 1104 source = "registry+https://github.com/rust-lang/crates.io-index" 644 1105 checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" 1106 + 1107 + [[package]] 1108 + name = "encoding_rs" 1109 + version = "0.8.35" 1110 + source = "registry+https://github.com/rust-lang/crates.io-index" 1111 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 1112 + dependencies = [ 1113 + "cfg-if", 1114 + ] 645 1115 646 1116 [[package]] 647 1117 name = "equivalent" ··· 672 1142 673 1143 [[package]] 674 1144 name = "event-listener" 1145 + version = "2.5.3" 1146 + source = "registry+https://github.com/rust-lang/crates.io-index" 1147 + checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 1148 + 1149 + [[package]] 1150 + name = "event-listener" 675 1151 version = "5.4.1" 676 1152 source = "registry+https://github.com/rust-lang/crates.io-index" 677 1153 checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" ··· 682 1158 ] 683 1159 684 1160 [[package]] 1161 + name = "event-listener-strategy" 1162 + version = "0.5.4" 1163 + source = "registry+https://github.com/rust-lang/crates.io-index" 1164 + checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" 1165 + dependencies = [ 1166 + "event-listener 5.4.1", 1167 + "pin-project-lite", 1168 + ] 1169 + 1170 + [[package]] 685 1171 name = "fastrand" 686 1172 version = "2.3.0" 687 1173 source = "registry+https://github.com/rust-lang/crates.io-index" 688 1174 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 689 1175 690 1176 [[package]] 1177 + name = "ff" 1178 + version = "0.13.1" 1179 + source = "registry+https://github.com/rust-lang/crates.io-index" 1180 + checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" 1181 + dependencies = [ 1182 + "rand_core 0.6.4", 1183 + "subtle", 1184 + ] 1185 + 1186 + [[package]] 1187 + name = "flate2" 1188 + version = "1.1.5" 1189 + source = "registry+https://github.com/rust-lang/crates.io-index" 1190 + checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" 1191 + dependencies = [ 1192 + "crc32fast", 1193 + "miniz_oxide", 1194 + ] 1195 + 1196 + [[package]] 691 1197 name = "flume" 692 1198 version = "0.11.1" 693 1199 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 711 1217 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 712 1218 713 1219 [[package]] 1220 + name = "foreign-types" 1221 + version = "0.3.2" 1222 + source = "registry+https://github.com/rust-lang/crates.io-index" 1223 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1224 + dependencies = [ 1225 + "foreign-types-shared", 1226 + ] 1227 + 1228 + [[package]] 1229 + name = "foreign-types-shared" 1230 + version = "0.1.1" 1231 + source = "registry+https://github.com/rust-lang/crates.io-index" 1232 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1233 + 1234 + [[package]] 714 1235 name = "form_urlencoded" 715 - version = "1.2.1" 1236 + version = "1.2.2" 716 1237 source = "registry+https://github.com/rust-lang/crates.io-index" 717 - checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1238 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 718 1239 dependencies = [ 719 1240 "percent-encoding", 720 1241 ] ··· 780 1301 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 781 1302 782 1303 [[package]] 1304 + name = "futures-lite" 1305 + version = "2.6.1" 1306 + source = "registry+https://github.com/rust-lang/crates.io-index" 1307 + checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" 1308 + dependencies = [ 1309 + "fastrand", 1310 + "futures-core", 1311 + "futures-io", 1312 + "parking", 1313 + "pin-project-lite", 1314 + ] 1315 + 1316 + [[package]] 1317 + name = "futures-macro" 1318 + version = "0.3.31" 1319 + source = "registry+https://github.com/rust-lang/crates.io-index" 1320 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1321 + dependencies = [ 1322 + "proc-macro2", 1323 + "quote", 1324 + "syn 2.0.105", 1325 + ] 1326 + 1327 + [[package]] 783 1328 name = "futures-sink" 784 1329 version = "0.3.31" 785 1330 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 805 1350 dependencies = [ 806 1351 "futures-core", 807 1352 "futures-io", 1353 + "futures-macro", 808 1354 "futures-sink", 809 1355 "futures-task", 810 1356 "memchr", ··· 821 1367 dependencies = [ 822 1368 "typenum", 823 1369 "version_check", 1370 + "zeroize", 824 1371 ] 825 1372 826 1373 [[package]] ··· 830 1377 checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 831 1378 dependencies = [ 832 1379 "cfg-if", 1380 + "js-sys", 833 1381 "libc", 834 - "wasi 0.11.1+wasi-snapshot-preview1", 1382 + "wasi", 1383 + "wasm-bindgen", 835 1384 ] 836 1385 837 1386 [[package]] 838 1387 name = "getrandom" 839 - version = "0.3.3" 1388 + version = "0.3.4" 840 1389 source = "registry+https://github.com/rust-lang/crates.io-index" 841 - checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 1390 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 842 1391 dependencies = [ 843 1392 "cfg-if", 844 1393 "js-sys", 845 1394 "libc", 846 1395 "r-efi", 847 - "wasi 0.14.2+wasi-0.2.4", 1396 + "wasip2", 848 1397 "wasm-bindgen", 849 1398 ] 850 1399 ··· 869 1418 "aho-corasick", 870 1419 "bstr", 871 1420 "log", 872 - "regex-automata 0.4.9", 1421 + "regex-automata 0.4.13", 873 1422 "regex-syntax 0.8.5", 874 1423 ] 875 1424 876 1425 [[package]] 1426 + name = "gloo-timers" 1427 + version = "0.3.0" 1428 + source = "registry+https://github.com/rust-lang/crates.io-index" 1429 + checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" 1430 + dependencies = [ 1431 + "futures-channel", 1432 + "futures-core", 1433 + "js-sys", 1434 + "wasm-bindgen", 1435 + ] 1436 + 1437 + [[package]] 877 1438 name = "governor" 878 1439 version = "0.10.1" 879 1440 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 884 1445 "futures-sink", 885 1446 "futures-timer", 886 1447 "futures-util", 887 - "getrandom 0.3.3", 1448 + "getrandom 0.3.4", 888 1449 "hashbrown 0.15.5", 889 1450 "nonzero_ext", 890 1451 "parking_lot", ··· 897 1458 ] 898 1459 899 1460 [[package]] 1461 + name = "group" 1462 + version = "0.13.0" 1463 + source = "registry+https://github.com/rust-lang/crates.io-index" 1464 + checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 1465 + dependencies = [ 1466 + "ff", 1467 + "rand_core 0.6.4", 1468 + "subtle", 1469 + ] 1470 + 1471 + [[package]] 900 1472 name = "h2" 901 1473 version = "0.4.12" 902 1474 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 908 1480 "futures-core", 909 1481 "futures-sink", 910 1482 "http", 911 - "indexmap", 1483 + "indexmap 2.10.0", 912 1484 "slab", 913 1485 "tokio", 914 1486 "tokio-util", ··· 944 1516 945 1517 [[package]] 946 1518 name = "hashbrown" 1519 + version = "0.12.3" 1520 + source = "registry+https://github.com/rust-lang/crates.io-index" 1521 + checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 1522 + 1523 + [[package]] 1524 + name = "hashbrown" 947 1525 version = "0.14.5" 948 1526 source = "registry+https://github.com/rust-lang/crates.io-index" 949 1527 checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" ··· 974 1552 975 1553 [[package]] 976 1554 name = "heck" 1555 + version = "0.4.1" 1556 + source = "registry+https://github.com/rust-lang/crates.io-index" 1557 + checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 1558 + 1559 + [[package]] 1560 + name = "heck" 977 1561 version = "0.5.0" 978 1562 source = "registry+https://github.com/rust-lang/crates.io-index" 979 1563 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 980 1564 981 1565 [[package]] 1566 + name = "hermit-abi" 1567 + version = "0.5.2" 1568 + source = "registry+https://github.com/rust-lang/crates.io-index" 1569 + checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 1570 + 1571 + [[package]] 982 1572 name = "hex" 983 1573 version = "0.4.3" 984 1574 source = "registry+https://github.com/rust-lang/crates.io-index" 985 1575 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 986 1576 987 1577 [[package]] 1578 + name = "hex_fmt" 1579 + version = "0.3.0" 1580 + source = "registry+https://github.com/rust-lang/crates.io-index" 1581 + checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" 1582 + 1583 + [[package]] 988 1584 name = "hkdf" 989 1585 version = "0.12.4" 990 1586 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1009 1605 checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 1010 1606 dependencies = [ 1011 1607 "windows-sys 0.59.0", 1608 + ] 1609 + 1610 + [[package]] 1611 + name = "html-escape" 1612 + version = "0.2.13" 1613 + source = "registry+https://github.com/rust-lang/crates.io-index" 1614 + checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" 1615 + dependencies = [ 1616 + "utf8-width", 1012 1617 ] 1013 1618 1014 1619 [[package]] ··· 1079 1684 ] 1080 1685 1081 1686 [[package]] 1687 + name = "hyper-rustls" 1688 + version = "0.27.7" 1689 + source = "registry+https://github.com/rust-lang/crates.io-index" 1690 + checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 1691 + dependencies = [ 1692 + "http", 1693 + "hyper", 1694 + "hyper-util", 1695 + "rustls", 1696 + "rustls-pki-types", 1697 + "tokio", 1698 + "tokio-rustls", 1699 + "tower-service", 1700 + "webpki-roots 1.0.2", 1701 + ] 1702 + 1703 + [[package]] 1082 1704 name = "hyper-timeout" 1083 1705 version = "0.5.2" 1084 1706 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1097 1719 source = "registry+https://github.com/rust-lang/crates.io-index" 1098 1720 checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 1099 1721 dependencies = [ 1722 + "base64", 1100 1723 "bytes", 1101 1724 "futures-channel", 1102 1725 "futures-core", ··· 1104 1727 "http", 1105 1728 "http-body", 1106 1729 "hyper", 1730 + "ipnet", 1107 1731 "libc", 1732 + "percent-encoding", 1108 1733 "pin-project-lite", 1109 1734 "socket2", 1735 + "system-configuration", 1110 1736 "tokio", 1111 1737 "tower-service", 1112 1738 "tracing", 1739 + "windows-registry", 1113 1740 ] 1114 1741 1115 1742 [[package]] ··· 1230 1857 1231 1858 [[package]] 1232 1859 name = "idna" 1233 - version = "1.0.3" 1860 + version = "1.1.0" 1234 1861 source = "registry+https://github.com/rust-lang/crates.io-index" 1235 - checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 1862 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 1236 1863 dependencies = [ 1237 1864 "idna_adapter", 1238 1865 "smallvec", ··· 1251 1878 1252 1879 [[package]] 1253 1880 name = "indexmap" 1881 + version = "1.9.3" 1882 + source = "registry+https://github.com/rust-lang/crates.io-index" 1883 + checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 1884 + dependencies = [ 1885 + "autocfg", 1886 + "hashbrown 0.12.3", 1887 + "serde", 1888 + ] 1889 + 1890 + [[package]] 1891 + name = "indexmap" 1254 1892 version = "2.10.0" 1255 1893 source = "registry+https://github.com/rust-lang/crates.io-index" 1256 1894 checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 1257 1895 dependencies = [ 1258 1896 "equivalent", 1259 1897 "hashbrown 0.15.5", 1898 + "serde", 1899 + ] 1900 + 1901 + [[package]] 1902 + name = "indoc" 1903 + version = "2.0.7" 1904 + source = "registry+https://github.com/rust-lang/crates.io-index" 1905 + checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" 1906 + dependencies = [ 1907 + "rustversion", 1260 1908 ] 1261 1909 1262 1910 [[package]] ··· 1269 1917 ] 1270 1918 1271 1919 [[package]] 1920 + name = "inventory" 1921 + version = "0.3.21" 1922 + source = "registry+https://github.com/rust-lang/crates.io-index" 1923 + checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" 1924 + dependencies = [ 1925 + "rustversion", 1926 + ] 1927 + 1928 + [[package]] 1272 1929 name = "io-uring" 1273 1930 version = "0.7.9" 1274 1931 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1280 1937 ] 1281 1938 1282 1939 [[package]] 1940 + name = "ipld-core" 1941 + version = "0.4.2" 1942 + source = "registry+https://github.com/rust-lang/crates.io-index" 1943 + checksum = "104718b1cc124d92a6d01ca9c9258a7df311405debb3408c445a36452f9bf8db" 1944 + dependencies = [ 1945 + "cid", 1946 + "serde", 1947 + "serde_bytes", 1948 + ] 1949 + 1950 + [[package]] 1951 + name = "ipnet" 1952 + version = "2.11.0" 1953 + source = "registry+https://github.com/rust-lang/crates.io-index" 1954 + checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 1955 + 1956 + [[package]] 1957 + name = "iri-string" 1958 + version = "0.7.9" 1959 + source = "registry+https://github.com/rust-lang/crates.io-index" 1960 + checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" 1961 + dependencies = [ 1962 + "memchr", 1963 + "serde", 1964 + ] 1965 + 1966 + [[package]] 1283 1967 name = "itertools" 1284 1968 version = "0.12.1" 1285 1969 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1295 1979 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1296 1980 1297 1981 [[package]] 1982 + name = "jacquard-api" 1983 + version = "0.9.2" 1984 + source = "registry+https://github.com/rust-lang/crates.io-index" 1985 + checksum = "bbbfd6e2b10fa1731f4d4e40c8f791956b0d4f804fb3efef891afec903f20597" 1986 + dependencies = [ 1987 + "bon", 1988 + "bytes", 1989 + "jacquard-common", 1990 + "jacquard-derive", 1991 + "jacquard-lexicon", 1992 + "miette", 1993 + "rustversion", 1994 + "serde", 1995 + "serde_ipld_dagcbor", 1996 + "thiserror 2.0.14", 1997 + "unicode-segmentation", 1998 + ] 1999 + 2000 + [[package]] 2001 + name = "jacquard-common" 2002 + version = "0.9.2" 2003 + source = "registry+https://github.com/rust-lang/crates.io-index" 2004 + checksum = "df86cb117d9f1c2b0251ba67c3f0e3f963fd22abc6cf8de0e02a7fc846c288ca" 2005 + dependencies = [ 2006 + "base64", 2007 + "bon", 2008 + "bytes", 2009 + "chrono", 2010 + "cid", 2011 + "getrandom 0.2.16", 2012 + "getrandom 0.3.4", 2013 + "http", 2014 + "ipld-core", 2015 + "k256", 2016 + "langtag", 2017 + "miette", 2018 + "multibase", 2019 + "multihash", 2020 + "ouroboros", 2021 + "p256", 2022 + "rand 0.9.2", 2023 + "regex", 2024 + "regex-lite", 2025 + "reqwest", 2026 + "serde", 2027 + "serde_html_form", 2028 + "serde_ipld_dagcbor", 2029 + "serde_json", 2030 + "signature", 2031 + "smol_str", 2032 + "thiserror 2.0.14", 2033 + "tokio", 2034 + "tokio-util", 2035 + "trait-variant", 2036 + "url", 2037 + ] 2038 + 2039 + [[package]] 2040 + name = "jacquard-derive" 2041 + version = "0.9.2" 2042 + source = "registry+https://github.com/rust-lang/crates.io-index" 2043 + checksum = "42ca61a69dc7aa8fb2d7163416514ff7df5d79f2e8b22e269f4610afa85572fe" 2044 + dependencies = [ 2045 + "heck 0.5.0", 2046 + "jacquard-lexicon", 2047 + "proc-macro2", 2048 + "quote", 2049 + "syn 2.0.105", 2050 + ] 2051 + 2052 + [[package]] 2053 + name = "jacquard-identity" 2054 + version = "0.9.2" 2055 + source = "registry+https://github.com/rust-lang/crates.io-index" 2056 + checksum = "1ef714cacebfca486558a9f8e205daf466bfba0466c4d0c450fd6d0252400a53" 2057 + dependencies = [ 2058 + "bon", 2059 + "bytes", 2060 + "http", 2061 + "jacquard-api", 2062 + "jacquard-common", 2063 + "jacquard-lexicon", 2064 + "miette", 2065 + "percent-encoding", 2066 + "reqwest", 2067 + "serde", 2068 + "serde_html_form", 2069 + "serde_json", 2070 + "thiserror 2.0.14", 2071 + "tokio", 2072 + "trait-variant", 2073 + "url", 2074 + "urlencoding", 2075 + ] 2076 + 2077 + [[package]] 2078 + name = "jacquard-lexicon" 2079 + version = "0.9.2" 2080 + source = "registry+https://github.com/rust-lang/crates.io-index" 2081 + checksum = "de87f2c938faea1b1f1b32d5b9e0c870e7b5bb5efbf96e3692ae2d8f6b2beb7a" 2082 + dependencies = [ 2083 + "cid", 2084 + "dashmap", 2085 + "heck 0.5.0", 2086 + "inventory", 2087 + "jacquard-common", 2088 + "miette", 2089 + "multihash", 2090 + "prettyplease", 2091 + "proc-macro2", 2092 + "quote", 2093 + "serde", 2094 + "serde_ipld_dagcbor", 2095 + "serde_json", 2096 + "serde_repr", 2097 + "serde_with", 2098 + "sha2", 2099 + "syn 2.0.105", 2100 + "thiserror 2.0.14", 2101 + "unicode-segmentation", 2102 + ] 2103 + 2104 + [[package]] 1298 2105 name = "jobserver" 1299 2106 version = "0.1.33" 1300 2107 source = "registry+https://github.com/rust-lang/crates.io-index" 1301 2108 checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 1302 2109 dependencies = [ 1303 - "getrandom 0.3.3", 2110 + "getrandom 0.3.4", 1304 2111 "libc", 2112 + ] 2113 + 2114 + [[package]] 2115 + name = "josekit" 2116 + version = "0.10.3" 2117 + source = "registry+https://github.com/rust-lang/crates.io-index" 2118 + checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c" 2119 + dependencies = [ 2120 + "anyhow", 2121 + "base64", 2122 + "flate2", 2123 + "openssl", 2124 + "regex", 2125 + "serde", 2126 + "serde_json", 2127 + "thiserror 2.0.14", 2128 + "time", 1305 2129 ] 1306 2130 1307 2131 [[package]] ··· 1337 2161 ] 1338 2162 1339 2163 [[package]] 2164 + name = "k256" 2165 + version = "0.13.4" 2166 + source = "registry+https://github.com/rust-lang/crates.io-index" 2167 + checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" 2168 + dependencies = [ 2169 + "cfg-if", 2170 + "ecdsa", 2171 + "elliptic-curve", 2172 + "sha2", 2173 + ] 2174 + 2175 + [[package]] 2176 + name = "kv-log-macro" 2177 + version = "1.0.7" 2178 + source = "registry+https://github.com/rust-lang/crates.io-index" 2179 + checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 2180 + dependencies = [ 2181 + "log", 2182 + ] 2183 + 2184 + [[package]] 2185 + name = "langtag" 2186 + version = "0.4.0" 2187 + source = "registry+https://github.com/rust-lang/crates.io-index" 2188 + checksum = "9ecb4c689a30e48ebeaa14237f34037e300dd072e6ad21a9ec72e810ff3c6600" 2189 + dependencies = [ 2190 + "serde", 2191 + "static-regular-grammar", 2192 + "thiserror 1.0.69", 2193 + ] 2194 + 2195 + [[package]] 1340 2196 name = "lazy_static" 1341 2197 version = "1.5.0" 1342 2198 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1357 2213 source = "registry+https://github.com/rust-lang/crates.io-index" 1358 2214 checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56" 1359 2215 dependencies = [ 2216 + "async-std", 1360 2217 "async-trait", 1361 2218 "base64", 1362 2219 "chumsky", ··· 1430 2287 checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 1431 2288 1432 2289 [[package]] 2290 + name = "linux-raw-sys" 2291 + version = "0.11.0" 2292 + source = "registry+https://github.com/rust-lang/crates.io-index" 2293 + checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 2294 + 2295 + [[package]] 1433 2296 name = "litemap" 1434 2297 version = "0.8.0" 1435 2298 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1450 2313 version = "0.4.27" 1451 2314 source = "registry+https://github.com/rust-lang/crates.io-index" 1452 2315 checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 2316 + dependencies = [ 2317 + "value-bag", 2318 + ] 2319 + 2320 + [[package]] 2321 + name = "lru-slab" 2322 + version = "0.1.2" 2323 + source = "registry+https://github.com/rust-lang/crates.io-index" 2324 + checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 2325 + 2326 + [[package]] 2327 + name = "match-lookup" 2328 + version = "0.1.1" 2329 + source = "registry+https://github.com/rust-lang/crates.io-index" 2330 + checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" 2331 + dependencies = [ 2332 + "proc-macro2", 2333 + "quote", 2334 + "syn 1.0.109", 2335 + ] 1453 2336 1454 2337 [[package]] 1455 2338 name = "matchers" ··· 1483 2366 checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 1484 2367 1485 2368 [[package]] 2369 + name = "miette" 2370 + version = "7.6.0" 2371 + source = "registry+https://github.com/rust-lang/crates.io-index" 2372 + checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" 2373 + dependencies = [ 2374 + "cfg-if", 2375 + "miette-derive", 2376 + "unicode-width", 2377 + ] 2378 + 2379 + [[package]] 2380 + name = "miette-derive" 2381 + version = "7.6.0" 2382 + source = "registry+https://github.com/rust-lang/crates.io-index" 2383 + checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" 2384 + dependencies = [ 2385 + "proc-macro2", 2386 + "quote", 2387 + "syn 2.0.105", 2388 + ] 2389 + 2390 + [[package]] 1486 2391 name = "mime" 1487 2392 version = "0.3.17" 1488 2393 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1501 2406 checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 1502 2407 dependencies = [ 1503 2408 "adler2", 2409 + "simd-adler32", 1504 2410 ] 1505 2411 1506 2412 [[package]] ··· 1510 2416 checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 1511 2417 dependencies = [ 1512 2418 "libc", 1513 - "wasi 0.11.1+wasi-snapshot-preview1", 2419 + "wasi", 1514 2420 "windows-sys 0.59.0", 1515 2421 ] 1516 2422 1517 2423 [[package]] 2424 + name = "multibase" 2425 + version = "0.9.2" 2426 + source = "registry+https://github.com/rust-lang/crates.io-index" 2427 + checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" 2428 + dependencies = [ 2429 + "base-x", 2430 + "base256emoji", 2431 + "data-encoding", 2432 + "data-encoding-macro", 2433 + ] 2434 + 2435 + [[package]] 2436 + name = "multihash" 2437 + version = "0.19.3" 2438 + source = "registry+https://github.com/rust-lang/crates.io-index" 2439 + checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" 2440 + dependencies = [ 2441 + "core2", 2442 + "serde", 2443 + "unsigned-varint", 2444 + ] 2445 + 2446 + [[package]] 1518 2447 name = "nom" 1519 2448 version = "7.1.3" 1520 2449 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1573 2502 ] 1574 2503 1575 2504 [[package]] 2505 + name = "num-conv" 2506 + version = "0.1.0" 2507 + source = "registry+https://github.com/rust-lang/crates.io-index" 2508 + checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 2509 + 2510 + [[package]] 1576 2511 name = "num-integer" 1577 2512 version = "0.1.46" 1578 2513 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1633 2568 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1634 2569 1635 2570 [[package]] 2571 + name = "openssl" 2572 + version = "0.10.75" 2573 + source = "registry+https://github.com/rust-lang/crates.io-index" 2574 + checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" 2575 + dependencies = [ 2576 + "bitflags", 2577 + "cfg-if", 2578 + "foreign-types", 2579 + "libc", 2580 + "once_cell", 2581 + "openssl-macros", 2582 + "openssl-sys", 2583 + ] 2584 + 2585 + [[package]] 2586 + name = "openssl-macros" 2587 + version = "0.1.1" 2588 + source = "registry+https://github.com/rust-lang/crates.io-index" 2589 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 2590 + dependencies = [ 2591 + "proc-macro2", 2592 + "quote", 2593 + "syn 2.0.105", 2594 + ] 2595 + 2596 + [[package]] 2597 + name = "openssl-sys" 2598 + version = "0.9.111" 2599 + source = "registry+https://github.com/rust-lang/crates.io-index" 2600 + checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" 2601 + dependencies = [ 2602 + "cc", 2603 + "libc", 2604 + "pkg-config", 2605 + "vcpkg", 2606 + ] 2607 + 2608 + [[package]] 2609 + name = "ouroboros" 2610 + version = "0.18.5" 2611 + source = "registry+https://github.com/rust-lang/crates.io-index" 2612 + checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" 2613 + dependencies = [ 2614 + "aliasable", 2615 + "ouroboros_macro", 2616 + "static_assertions", 2617 + ] 2618 + 2619 + [[package]] 2620 + name = "ouroboros_macro" 2621 + version = "0.18.5" 2622 + source = "registry+https://github.com/rust-lang/crates.io-index" 2623 + checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" 2624 + dependencies = [ 2625 + "heck 0.4.1", 2626 + "proc-macro2", 2627 + "proc-macro2-diagnostics", 2628 + "quote", 2629 + "syn 2.0.105", 2630 + ] 2631 + 2632 + [[package]] 1636 2633 name = "overload" 1637 2634 version = "0.1.1" 1638 2635 source = "registry+https://github.com/rust-lang/crates.io-index" 1639 2636 checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 2637 + 2638 + [[package]] 2639 + name = "p256" 2640 + version = "0.13.2" 2641 + source = "registry+https://github.com/rust-lang/crates.io-index" 2642 + checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" 2643 + dependencies = [ 2644 + "ecdsa", 2645 + "elliptic-curve", 2646 + "primeorder", 2647 + "sha2", 2648 + ] 1640 2649 1641 2650 [[package]] 1642 2651 name = "parking" ··· 1700 2709 "dotenvy", 1701 2710 "handlebars", 1702 2711 "hex", 2712 + "html-escape", 1703 2713 "hyper-util", 2714 + "jacquard-common", 2715 + "jacquard-identity", 2716 + "josekit", 1704 2717 "jwt-compact", 1705 2718 "lettre", 2719 + "multibase", 1706 2720 "rand 0.9.2", 2721 + "reqwest", 1707 2722 "rust-embed", 1708 2723 "rustls", 1709 2724 "scrypt", ··· 1716 2731 "tower_governor", 1717 2732 "tracing", 1718 2733 "tracing-subscriber", 2734 + "url", 2735 + "urlencoding", 1719 2736 ] 1720 2737 1721 2738 [[package]] ··· 1729 2746 1730 2747 [[package]] 1731 2748 name = "percent-encoding" 1732 - version = "2.3.1" 2749 + version = "2.3.2" 1733 2750 source = "registry+https://github.com/rust-lang/crates.io-index" 1734 - checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 2751 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 1735 2752 1736 2753 [[package]] 1737 2754 name = "pest" ··· 1764 2781 "pest_meta", 1765 2782 "proc-macro2", 1766 2783 "quote", 1767 - "syn", 2784 + "syn 2.0.105", 1768 2785 ] 1769 2786 1770 2787 [[package]] ··· 1794 2811 dependencies = [ 1795 2812 "proc-macro2", 1796 2813 "quote", 1797 - "syn", 2814 + "syn 2.0.105", 1798 2815 ] 1799 2816 1800 2817 [[package]] ··· 1810 2827 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1811 2828 1812 2829 [[package]] 2830 + name = "piper" 2831 + version = "0.2.4" 2832 + source = "registry+https://github.com/rust-lang/crates.io-index" 2833 + checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" 2834 + dependencies = [ 2835 + "atomic-waker", 2836 + "fastrand", 2837 + "futures-io", 2838 + ] 2839 + 2840 + [[package]] 1813 2841 name = "pkcs1" 1814 2842 version = "0.7.5" 1815 2843 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1837 2865 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1838 2866 1839 2867 [[package]] 2868 + name = "polling" 2869 + version = "3.11.0" 2870 + source = "registry+https://github.com/rust-lang/crates.io-index" 2871 + checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" 2872 + dependencies = [ 2873 + "cfg-if", 2874 + "concurrent-queue", 2875 + "hermit-abi", 2876 + "pin-project-lite", 2877 + "rustix 1.1.2", 2878 + "windows-sys 0.61.2", 2879 + ] 2880 + 2881 + [[package]] 1840 2882 name = "portable-atomic" 1841 2883 version = "1.11.1" 1842 2884 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1852 2894 ] 1853 2895 1854 2896 [[package]] 2897 + name = "powerfmt" 2898 + version = "0.2.0" 2899 + source = "registry+https://github.com/rust-lang/crates.io-index" 2900 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 2901 + 2902 + [[package]] 1855 2903 name = "ppv-lite86" 1856 2904 version = "0.2.21" 1857 2905 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1867 2915 checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" 1868 2916 dependencies = [ 1869 2917 "proc-macro2", 1870 - "syn", 2918 + "syn 2.0.105", 2919 + ] 2920 + 2921 + [[package]] 2922 + name = "primeorder" 2923 + version = "0.13.6" 2924 + source = "registry+https://github.com/rust-lang/crates.io-index" 2925 + checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" 2926 + dependencies = [ 2927 + "elliptic-curve", 2928 + ] 2929 + 2930 + [[package]] 2931 + name = "proc-macro-error" 2932 + version = "1.0.4" 2933 + source = "registry+https://github.com/rust-lang/crates.io-index" 2934 + checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 2935 + dependencies = [ 2936 + "proc-macro-error-attr", 2937 + "proc-macro2", 2938 + "quote", 2939 + "syn 1.0.109", 2940 + "version_check", 2941 + ] 2942 + 2943 + [[package]] 2944 + name = "proc-macro-error-attr" 2945 + version = "1.0.4" 2946 + source = "registry+https://github.com/rust-lang/crates.io-index" 2947 + checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 2948 + dependencies = [ 2949 + "proc-macro2", 2950 + "quote", 2951 + "version_check", 1871 2952 ] 1872 2953 1873 2954 [[package]] ··· 1880 2961 ] 1881 2962 1882 2963 [[package]] 2964 + name = "proc-macro2-diagnostics" 2965 + version = "0.10.1" 2966 + source = "registry+https://github.com/rust-lang/crates.io-index" 2967 + checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" 2968 + dependencies = [ 2969 + "proc-macro2", 2970 + "quote", 2971 + "syn 2.0.105", 2972 + "version_check", 2973 + "yansi", 2974 + ] 2975 + 2976 + [[package]] 1883 2977 name = "psm" 1884 2978 version = "0.1.26" 1885 2979 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1898 2992 "libc", 1899 2993 "once_cell", 1900 2994 "raw-cpuid", 1901 - "wasi 0.11.1+wasi-snapshot-preview1", 2995 + "wasi", 1902 2996 "web-sys", 1903 2997 "winapi", 1904 2998 ] 1905 2999 1906 3000 [[package]] 3001 + name = "quinn" 3002 + version = "0.11.9" 3003 + source = "registry+https://github.com/rust-lang/crates.io-index" 3004 + checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" 3005 + dependencies = [ 3006 + "bytes", 3007 + "cfg_aliases", 3008 + "pin-project-lite", 3009 + "quinn-proto", 3010 + "quinn-udp", 3011 + "rustc-hash 2.1.1", 3012 + "rustls", 3013 + "socket2", 3014 + "thiserror 2.0.14", 3015 + "tokio", 3016 + "tracing", 3017 + "web-time", 3018 + ] 3019 + 3020 + [[package]] 3021 + name = "quinn-proto" 3022 + version = "0.11.13" 3023 + source = "registry+https://github.com/rust-lang/crates.io-index" 3024 + checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" 3025 + dependencies = [ 3026 + "bytes", 3027 + "getrandom 0.3.4", 3028 + "lru-slab", 3029 + "rand 0.9.2", 3030 + "ring", 3031 + "rustc-hash 2.1.1", 3032 + "rustls", 3033 + "rustls-pki-types", 3034 + "slab", 3035 + "thiserror 2.0.14", 3036 + "tinyvec", 3037 + "tracing", 3038 + "web-time", 3039 + ] 3040 + 3041 + [[package]] 3042 + name = "quinn-udp" 3043 + version = "0.5.14" 3044 + source = "registry+https://github.com/rust-lang/crates.io-index" 3045 + checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 3046 + dependencies = [ 3047 + "cfg_aliases", 3048 + "libc", 3049 + "once_cell", 3050 + "socket2", 3051 + "tracing", 3052 + "windows-sys 0.59.0", 3053 + ] 3054 + 3055 + [[package]] 1907 3056 name = "quote" 1908 3057 version = "1.0.40" 1909 3058 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1980 3129 source = "registry+https://github.com/rust-lang/crates.io-index" 1981 3130 checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 1982 3131 dependencies = [ 1983 - "getrandom 0.3.3", 3132 + "getrandom 0.3.4", 1984 3133 ] 3134 + 3135 + [[package]] 3136 + name = "range-traits" 3137 + version = "0.3.2" 3138 + source = "registry+https://github.com/rust-lang/crates.io-index" 3139 + checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab" 1985 3140 1986 3141 [[package]] 1987 3142 name = "raw-cpuid" ··· 2002 3157 ] 2003 3158 2004 3159 [[package]] 3160 + name = "ref-cast" 3161 + version = "1.0.25" 3162 + source = "registry+https://github.com/rust-lang/crates.io-index" 3163 + checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" 3164 + dependencies = [ 3165 + "ref-cast-impl", 3166 + ] 3167 + 3168 + [[package]] 3169 + name = "ref-cast-impl" 3170 + version = "1.0.25" 3171 + source = "registry+https://github.com/rust-lang/crates.io-index" 3172 + checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" 3173 + dependencies = [ 3174 + "proc-macro2", 3175 + "quote", 3176 + "syn 2.0.105", 3177 + ] 3178 + 3179 + [[package]] 2005 3180 name = "regex" 2006 - version = "1.11.1" 3181 + version = "1.12.2" 2007 3182 source = "registry+https://github.com/rust-lang/crates.io-index" 2008 - checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 3183 + checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 2009 3184 dependencies = [ 2010 3185 "aho-corasick", 2011 3186 "memchr", 2012 - "regex-automata 0.4.9", 3187 + "regex-automata 0.4.13", 2013 3188 "regex-syntax 0.8.5", 2014 3189 ] 2015 3190 ··· 2024 3199 2025 3200 [[package]] 2026 3201 name = "regex-automata" 2027 - version = "0.4.9" 3202 + version = "0.4.13" 2028 3203 source = "registry+https://github.com/rust-lang/crates.io-index" 2029 - checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 3204 + checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 2030 3205 dependencies = [ 2031 3206 "aho-corasick", 2032 3207 "memchr", ··· 2034 3209 ] 2035 3210 2036 3211 [[package]] 3212 + name = "regex-lite" 3213 + version = "0.1.8" 3214 + source = "registry+https://github.com/rust-lang/crates.io-index" 3215 + checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" 3216 + 3217 + [[package]] 2037 3218 name = "regex-syntax" 2038 3219 version = "0.6.29" 2039 3220 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2046 3227 checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 2047 3228 2048 3229 [[package]] 3230 + name = "reqwest" 3231 + version = "0.12.24" 3232 + source = "registry+https://github.com/rust-lang/crates.io-index" 3233 + checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" 3234 + dependencies = [ 3235 + "async-compression", 3236 + "base64", 3237 + "bytes", 3238 + "encoding_rs", 3239 + "futures-core", 3240 + "futures-util", 3241 + "h2", 3242 + "http", 3243 + "http-body", 3244 + "http-body-util", 3245 + "hyper", 3246 + "hyper-rustls", 3247 + "hyper-util", 3248 + "js-sys", 3249 + "log", 3250 + "mime", 3251 + "percent-encoding", 3252 + "pin-project-lite", 3253 + "quinn", 3254 + "rustls", 3255 + "rustls-pki-types", 3256 + "serde", 3257 + "serde_json", 3258 + "serde_urlencoded", 3259 + "sync_wrapper", 3260 + "tokio", 3261 + "tokio-rustls", 3262 + "tokio-util", 3263 + "tower", 3264 + "tower-http", 3265 + "tower-service", 3266 + "url", 3267 + "wasm-bindgen", 3268 + "wasm-bindgen-futures", 3269 + "wasm-streams", 3270 + "web-sys", 3271 + "webpki-roots 1.0.2", 3272 + ] 3273 + 3274 + [[package]] 3275 + name = "rfc6979" 3276 + version = "0.4.0" 3277 + source = "registry+https://github.com/rust-lang/crates.io-index" 3278 + checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" 3279 + dependencies = [ 3280 + "hmac", 3281 + "subtle", 3282 + ] 3283 + 3284 + [[package]] 2049 3285 name = "ring" 2050 3286 version = "0.17.14" 2051 3287 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2099 3335 "proc-macro2", 2100 3336 "quote", 2101 3337 "rust-embed-utils", 2102 - "syn", 3338 + "syn 2.0.105", 2103 3339 "walkdir", 2104 3340 ] 2105 3341 ··· 2127 3363 checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 2128 3364 2129 3365 [[package]] 3366 + name = "rustc-hash" 3367 + version = "2.1.1" 3368 + source = "registry+https://github.com/rust-lang/crates.io-index" 3369 + checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3370 + 3371 + [[package]] 2130 3372 name = "rustix" 2131 3373 version = "0.38.44" 2132 3374 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2135 3377 "bitflags", 2136 3378 "errno", 2137 3379 "libc", 2138 - "linux-raw-sys", 3380 + "linux-raw-sys 0.4.15", 3381 + "windows-sys 0.59.0", 3382 + ] 3383 + 3384 + [[package]] 3385 + name = "rustix" 3386 + version = "1.1.2" 3387 + source = "registry+https://github.com/rust-lang/crates.io-index" 3388 + checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 3389 + dependencies = [ 3390 + "bitflags", 3391 + "errno", 3392 + "libc", 3393 + "linux-raw-sys 0.11.0", 2139 3394 "windows-sys 0.59.0", 2140 3395 ] 2141 3396 ··· 2161 3416 source = "registry+https://github.com/rust-lang/crates.io-index" 2162 3417 checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 2163 3418 dependencies = [ 3419 + "web-time", 2164 3420 "zeroize", 2165 3421 ] 2166 3422 ··· 2207 3463 ] 2208 3464 2209 3465 [[package]] 3466 + name = "schemars" 3467 + version = "0.9.0" 3468 + source = "registry+https://github.com/rust-lang/crates.io-index" 3469 + checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" 3470 + dependencies = [ 3471 + "dyn-clone", 3472 + "ref-cast", 3473 + "serde", 3474 + "serde_json", 3475 + ] 3476 + 3477 + [[package]] 3478 + name = "schemars" 3479 + version = "1.1.0" 3480 + source = "registry+https://github.com/rust-lang/crates.io-index" 3481 + checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" 3482 + dependencies = [ 3483 + "dyn-clone", 3484 + "ref-cast", 3485 + "serde", 3486 + "serde_json", 3487 + ] 3488 + 3489 + [[package]] 2210 3490 name = "scopeguard" 2211 3491 version = "1.2.0" 2212 3492 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2225 3505 ] 2226 3506 2227 3507 [[package]] 3508 + name = "sec1" 3509 + version = "0.7.3" 3510 + source = "registry+https://github.com/rust-lang/crates.io-index" 3511 + checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" 3512 + dependencies = [ 3513 + "base16ct", 3514 + "der", 3515 + "generic-array", 3516 + "pkcs8", 3517 + "subtle", 3518 + "zeroize", 3519 + ] 3520 + 3521 + [[package]] 2228 3522 name = "secp256k1" 2229 3523 version = "0.28.2" 2230 3524 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2244 3538 2245 3539 [[package]] 2246 3540 name = "serde" 2247 - version = "1.0.219" 3541 + version = "1.0.228" 2248 3542 source = "registry+https://github.com/rust-lang/crates.io-index" 2249 - checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 3543 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 3544 + dependencies = [ 3545 + "serde_core", 3546 + "serde_derive", 3547 + ] 3548 + 3549 + [[package]] 3550 + name = "serde_bytes" 3551 + version = "0.11.19" 3552 + source = "registry+https://github.com/rust-lang/crates.io-index" 3553 + checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" 3554 + dependencies = [ 3555 + "serde", 3556 + "serde_core", 3557 + ] 3558 + 3559 + [[package]] 3560 + name = "serde_core" 3561 + version = "1.0.228" 3562 + source = "registry+https://github.com/rust-lang/crates.io-index" 3563 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 2250 3564 dependencies = [ 2251 3565 "serde_derive", 2252 3566 ] 2253 3567 2254 3568 [[package]] 2255 3569 name = "serde_derive" 2256 - version = "1.0.219" 3570 + version = "1.0.228" 2257 3571 source = "registry+https://github.com/rust-lang/crates.io-index" 2258 - checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 3572 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 2259 3573 dependencies = [ 2260 3574 "proc-macro2", 2261 3575 "quote", 2262 - "syn", 3576 + "syn 2.0.105", 3577 + ] 3578 + 3579 + [[package]] 3580 + name = "serde_html_form" 3581 + version = "0.2.8" 3582 + source = "registry+https://github.com/rust-lang/crates.io-index" 3583 + checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3584 + dependencies = [ 3585 + "form_urlencoded", 3586 + "indexmap 2.10.0", 3587 + "itoa", 3588 + "ryu", 3589 + "serde_core", 3590 + ] 3591 + 3592 + [[package]] 3593 + name = "serde_ipld_dagcbor" 3594 + version = "0.6.4" 3595 + source = "registry+https://github.com/rust-lang/crates.io-index" 3596 + checksum = "46182f4f08349a02b45c998ba3215d3f9de826246ba02bb9dddfe9a2a2100778" 3597 + dependencies = [ 3598 + "cbor4ii", 3599 + "ipld-core", 3600 + "scopeguard", 3601 + "serde", 2263 3602 ] 2264 3603 2265 3604 [[package]] 2266 3605 name = "serde_json" 2267 - version = "1.0.142" 3606 + version = "1.0.145" 2268 3607 source = "registry+https://github.com/rust-lang/crates.io-index" 2269 - checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" 3608 + checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 2270 3609 dependencies = [ 3610 + "indexmap 2.10.0", 2271 3611 "itoa", 2272 3612 "memchr", 2273 3613 "ryu", 2274 3614 "serde", 3615 + "serde_core", 2275 3616 ] 2276 3617 2277 3618 [[package]] ··· 2285 3626 ] 2286 3627 2287 3628 [[package]] 3629 + name = "serde_repr" 3630 + version = "0.1.20" 3631 + source = "registry+https://github.com/rust-lang/crates.io-index" 3632 + checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" 3633 + dependencies = [ 3634 + "proc-macro2", 3635 + "quote", 3636 + "syn 2.0.105", 3637 + ] 3638 + 3639 + [[package]] 2288 3640 name = "serde_urlencoded" 2289 3641 version = "0.7.1" 2290 3642 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2297 3649 ] 2298 3650 2299 3651 [[package]] 3652 + name = "serde_with" 3653 + version = "3.16.0" 3654 + source = "registry+https://github.com/rust-lang/crates.io-index" 3655 + checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" 3656 + dependencies = [ 3657 + "base64", 3658 + "chrono", 3659 + "hex", 3660 + "indexmap 1.9.3", 3661 + "indexmap 2.10.0", 3662 + "schemars 0.9.0", 3663 + "schemars 1.1.0", 3664 + "serde_core", 3665 + "serde_json", 3666 + "serde_with_macros", 3667 + "time", 3668 + ] 3669 + 3670 + [[package]] 3671 + name = "serde_with_macros" 3672 + version = "3.16.0" 3673 + source = "registry+https://github.com/rust-lang/crates.io-index" 3674 + checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" 3675 + dependencies = [ 3676 + "darling 0.21.3", 3677 + "proc-macro2", 3678 + "quote", 3679 + "syn 2.0.105", 3680 + ] 3681 + 3682 + [[package]] 2300 3683 name = "sha1" 2301 3684 version = "0.10.6" 2302 3685 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2353 3736 ] 2354 3737 2355 3738 [[package]] 3739 + name = "simd-adler32" 3740 + version = "0.3.7" 3741 + source = "registry+https://github.com/rust-lang/crates.io-index" 3742 + checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 3743 + 3744 + [[package]] 2356 3745 name = "slab" 2357 3746 version = "0.4.11" 2358 3747 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2368 3757 ] 2369 3758 2370 3759 [[package]] 3760 + name = "smol_str" 3761 + version = "0.3.4" 3762 + source = "registry+https://github.com/rust-lang/crates.io-index" 3763 + checksum = "3498b0a27f93ef1402f20eefacfaa1691272ac4eca1cdc8c596cb0a245d6cbf5" 3764 + dependencies = [ 3765 + "borsh", 3766 + "serde_core", 3767 + ] 3768 + 3769 + [[package]] 2371 3770 name = "socket2" 2372 3771 version = "0.6.0" 2373 3772 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2430 3829 "crc", 2431 3830 "crossbeam-queue", 2432 3831 "either", 2433 - "event-listener", 3832 + "event-listener 5.4.1", 2434 3833 "futures-core", 2435 3834 "futures-intrusive", 2436 3835 "futures-io", 2437 3836 "futures-util", 2438 3837 "hashbrown 0.15.5", 2439 3838 "hashlink", 2440 - "indexmap", 3839 + "indexmap 2.10.0", 2441 3840 "log", 2442 3841 "memchr", 2443 3842 "once_cell", ··· 2465 3864 "quote", 2466 3865 "sqlx-core", 2467 3866 "sqlx-macros-core", 2468 - "syn", 3867 + "syn 2.0.105", 2469 3868 ] 2470 3869 2471 3870 [[package]] ··· 2476 3875 dependencies = [ 2477 3876 "dotenvy", 2478 3877 "either", 2479 - "heck", 3878 + "heck 0.5.0", 2480 3879 "hex", 2481 3880 "once_cell", 2482 3881 "proc-macro2", ··· 2488 3887 "sqlx-mysql", 2489 3888 "sqlx-postgres", 2490 3889 "sqlx-sqlite", 2491 - "syn", 3890 + "syn 2.0.105", 2492 3891 "tokio", 2493 3892 "url", 2494 3893 ] ··· 2619 4018 ] 2620 4019 2621 4020 [[package]] 4021 + name = "static-regular-grammar" 4022 + version = "2.0.2" 4023 + source = "registry+https://github.com/rust-lang/crates.io-index" 4024 + checksum = "4f4a6c40247579acfbb138c3cd7de3dab113ab4ac6227f1b7de7d626ee667957" 4025 + dependencies = [ 4026 + "abnf", 4027 + "btree-range-map", 4028 + "ciborium", 4029 + "hex_fmt", 4030 + "indoc", 4031 + "proc-macro-error", 4032 + "proc-macro2", 4033 + "quote", 4034 + "serde", 4035 + "sha2", 4036 + "syn 2.0.105", 4037 + "thiserror 1.0.69", 4038 + ] 4039 + 4040 + [[package]] 4041 + name = "static_assertions" 4042 + version = "1.1.0" 4043 + source = "registry+https://github.com/rust-lang/crates.io-index" 4044 + checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 4045 + 4046 + [[package]] 2622 4047 name = "stringprep" 2623 4048 version = "0.1.5" 2624 4049 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2643 4068 2644 4069 [[package]] 2645 4070 name = "syn" 4071 + version = "1.0.109" 4072 + source = "registry+https://github.com/rust-lang/crates.io-index" 4073 + checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 4074 + dependencies = [ 4075 + "proc-macro2", 4076 + "quote", 4077 + "unicode-ident", 4078 + ] 4079 + 4080 + [[package]] 4081 + name = "syn" 2646 4082 version = "2.0.105" 2647 4083 source = "registry+https://github.com/rust-lang/crates.io-index" 2648 4084 checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" ··· 2657 4093 version = "1.0.2" 2658 4094 source = "registry+https://github.com/rust-lang/crates.io-index" 2659 4095 checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 4096 + dependencies = [ 4097 + "futures-core", 4098 + ] 2660 4099 2661 4100 [[package]] 2662 4101 name = "synstructure" ··· 2666 4105 dependencies = [ 2667 4106 "proc-macro2", 2668 4107 "quote", 2669 - "syn", 4108 + "syn 2.0.105", 4109 + ] 4110 + 4111 + [[package]] 4112 + name = "system-configuration" 4113 + version = "0.6.1" 4114 + source = "registry+https://github.com/rust-lang/crates.io-index" 4115 + checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 4116 + dependencies = [ 4117 + "bitflags", 4118 + "core-foundation", 4119 + "system-configuration-sys", 4120 + ] 4121 + 4122 + [[package]] 4123 + name = "system-configuration-sys" 4124 + version = "0.6.0" 4125 + source = "registry+https://github.com/rust-lang/crates.io-index" 4126 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 4127 + dependencies = [ 4128 + "core-foundation-sys", 4129 + "libc", 2670 4130 ] 2671 4131 2672 4132 [[package]] ··· 2695 4155 dependencies = [ 2696 4156 "proc-macro2", 2697 4157 "quote", 2698 - "syn", 4158 + "syn 2.0.105", 2699 4159 ] 2700 4160 2701 4161 [[package]] ··· 2706 4166 dependencies = [ 2707 4167 "proc-macro2", 2708 4168 "quote", 2709 - "syn", 4169 + "syn 2.0.105", 2710 4170 ] 2711 4171 2712 4172 [[package]] ··· 2719 4179 ] 2720 4180 2721 4181 [[package]] 4182 + name = "time" 4183 + version = "0.3.44" 4184 + source = "registry+https://github.com/rust-lang/crates.io-index" 4185 + checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" 4186 + dependencies = [ 4187 + "deranged", 4188 + "itoa", 4189 + "num-conv", 4190 + "powerfmt", 4191 + "serde", 4192 + "time-core", 4193 + "time-macros", 4194 + ] 4195 + 4196 + [[package]] 4197 + name = "time-core" 4198 + version = "0.1.6" 4199 + source = "registry+https://github.com/rust-lang/crates.io-index" 4200 + checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" 4201 + 4202 + [[package]] 4203 + name = "time-macros" 4204 + version = "0.2.24" 4205 + source = "registry+https://github.com/rust-lang/crates.io-index" 4206 + checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" 4207 + dependencies = [ 4208 + "num-conv", 4209 + "time-core", 4210 + ] 4211 + 4212 + [[package]] 2722 4213 name = "tinystr" 2723 4214 version = "0.8.1" 2724 4215 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2770 4261 dependencies = [ 2771 4262 "proc-macro2", 2772 4263 "quote", 2773 - "syn", 4264 + "syn 2.0.105", 2774 4265 ] 2775 4266 2776 4267 [[package]] ··· 2796 4287 2797 4288 [[package]] 2798 4289 name = "tokio-util" 2799 - version = "0.7.15" 4290 + version = "0.7.17" 2800 4291 source = "registry+https://github.com/rust-lang/crates.io-index" 2801 - checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" 4292 + checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" 2802 4293 dependencies = [ 2803 4294 "bytes", 2804 4295 "futures-core", ··· 2844 4335 dependencies = [ 2845 4336 "futures-core", 2846 4337 "futures-util", 2847 - "indexmap", 4338 + "indexmap 2.10.0", 2848 4339 "pin-project-lite", 2849 4340 "slab", 2850 4341 "sync_wrapper", ··· 2865 4356 "bitflags", 2866 4357 "bytes", 2867 4358 "futures-core", 4359 + "futures-util", 2868 4360 "http", 2869 4361 "http-body", 4362 + "iri-string", 2870 4363 "pin-project-lite", 2871 4364 "tokio", 2872 4365 "tokio-util", 4366 + "tower", 2873 4367 "tower-layer", 2874 4368 "tower-service", 2875 4369 ] ··· 2923 4417 dependencies = [ 2924 4418 "proc-macro2", 2925 4419 "quote", 2926 - "syn", 4420 + "syn 2.0.105", 2927 4421 ] 2928 4422 2929 4423 [[package]] ··· 2966 4460 ] 2967 4461 2968 4462 [[package]] 4463 + name = "trait-variant" 4464 + version = "0.1.2" 4465 + source = "registry+https://github.com/rust-lang/crates.io-index" 4466 + checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" 4467 + dependencies = [ 4468 + "proc-macro2", 4469 + "quote", 4470 + "syn 2.0.105", 4471 + ] 4472 + 4473 + [[package]] 2969 4474 name = "try-lock" 2970 4475 version = "0.2.5" 2971 4476 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3011 4516 checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 3012 4517 3013 4518 [[package]] 4519 + name = "unicode-segmentation" 4520 + version = "1.12.0" 4521 + source = "registry+https://github.com/rust-lang/crates.io-index" 4522 + checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 4523 + 4524 + [[package]] 4525 + name = "unicode-width" 4526 + version = "0.1.14" 4527 + source = "registry+https://github.com/rust-lang/crates.io-index" 4528 + checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 4529 + 4530 + [[package]] 4531 + name = "unsigned-varint" 4532 + version = "0.8.0" 4533 + source = "registry+https://github.com/rust-lang/crates.io-index" 4534 + checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 4535 + 4536 + [[package]] 3014 4537 name = "untrusted" 3015 4538 version = "0.7.1" 3016 4539 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3024 4547 3025 4548 [[package]] 3026 4549 name = "url" 3027 - version = "2.5.4" 4550 + version = "2.5.7" 3028 4551 source = "registry+https://github.com/rust-lang/crates.io-index" 3029 - checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 4552 + checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 3030 4553 dependencies = [ 3031 4554 "form_urlencoded", 3032 4555 "idna", 3033 4556 "percent-encoding", 4557 + "serde", 3034 4558 ] 3035 4559 3036 4560 [[package]] 4561 + name = "urlencoding" 4562 + version = "2.1.3" 4563 + source = "registry+https://github.com/rust-lang/crates.io-index" 4564 + checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 4565 + 4566 + [[package]] 4567 + name = "utf8-width" 4568 + version = "0.1.8" 4569 + source = "registry+https://github.com/rust-lang/crates.io-index" 4570 + checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" 4571 + 4572 + [[package]] 3037 4573 name = "utf8_iter" 3038 4574 version = "1.0.4" 3039 4575 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3044 4580 version = "0.1.1" 3045 4581 source = "registry+https://github.com/rust-lang/crates.io-index" 3046 4582 checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 4583 + 4584 + [[package]] 4585 + name = "value-bag" 4586 + version = "1.12.0" 4587 + source = "registry+https://github.com/rust-lang/crates.io-index" 4588 + checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" 3047 4589 3048 4590 [[package]] 3049 4591 name = "vcpkg" ··· 3083 4625 checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 3084 4626 3085 4627 [[package]] 3086 - name = "wasi" 3087 - version = "0.14.2+wasi-0.2.4" 4628 + name = "wasip2" 4629 + version = "1.0.1+wasi-0.2.4" 3088 4630 source = "registry+https://github.com/rust-lang/crates.io-index" 3089 - checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 4631 + checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 3090 4632 dependencies = [ 3091 - "wit-bindgen-rt", 4633 + "wit-bindgen", 3092 4634 ] 3093 4635 3094 4636 [[package]] ··· 3119 4661 "log", 3120 4662 "proc-macro2", 3121 4663 "quote", 3122 - "syn", 4664 + "syn 2.0.105", 3123 4665 "wasm-bindgen-shared", 3124 4666 ] 3125 4667 3126 4668 [[package]] 4669 + name = "wasm-bindgen-futures" 4670 + version = "0.4.50" 4671 + source = "registry+https://github.com/rust-lang/crates.io-index" 4672 + checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 4673 + dependencies = [ 4674 + "cfg-if", 4675 + "js-sys", 4676 + "once_cell", 4677 + "wasm-bindgen", 4678 + "web-sys", 4679 + ] 4680 + 4681 + [[package]] 3127 4682 name = "wasm-bindgen-macro" 3128 4683 version = "0.2.100" 3129 4684 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3141 4696 dependencies = [ 3142 4697 "proc-macro2", 3143 4698 "quote", 3144 - "syn", 4699 + "syn 2.0.105", 3145 4700 "wasm-bindgen-backend", 3146 4701 "wasm-bindgen-shared", 3147 4702 ] ··· 3156 4711 ] 3157 4712 3158 4713 [[package]] 4714 + name = "wasm-streams" 4715 + version = "0.4.2" 4716 + source = "registry+https://github.com/rust-lang/crates.io-index" 4717 + checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 4718 + dependencies = [ 4719 + "futures-util", 4720 + "js-sys", 4721 + "wasm-bindgen", 4722 + "wasm-bindgen-futures", 4723 + "web-sys", 4724 + ] 4725 + 4726 + [[package]] 3159 4727 name = "web-sys" 3160 4728 version = "0.3.77" 3161 4729 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3202 4770 "either", 3203 4771 "home", 3204 4772 "once_cell", 3205 - "rustix", 4773 + "rustix 0.38.44", 3206 4774 ] 3207 4775 3208 4776 [[package]] ··· 3254 4822 dependencies = [ 3255 4823 "windows-implement", 3256 4824 "windows-interface", 3257 - "windows-link", 4825 + "windows-link 0.1.3", 3258 4826 "windows-result", 3259 4827 "windows-strings", 3260 4828 ] ··· 3267 4835 dependencies = [ 3268 4836 "proc-macro2", 3269 4837 "quote", 3270 - "syn", 4838 + "syn 2.0.105", 3271 4839 ] 3272 4840 3273 4841 [[package]] ··· 3278 4846 dependencies = [ 3279 4847 "proc-macro2", 3280 4848 "quote", 3281 - "syn", 4849 + "syn 2.0.105", 3282 4850 ] 3283 4851 3284 4852 [[package]] ··· 3288 4856 checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 3289 4857 3290 4858 [[package]] 4859 + name = "windows-link" 4860 + version = "0.2.1" 4861 + source = "registry+https://github.com/rust-lang/crates.io-index" 4862 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 4863 + 4864 + [[package]] 4865 + name = "windows-registry" 4866 + version = "0.5.3" 4867 + source = "registry+https://github.com/rust-lang/crates.io-index" 4868 + checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" 4869 + dependencies = [ 4870 + "windows-link 0.1.3", 4871 + "windows-result", 4872 + "windows-strings", 4873 + ] 4874 + 4875 + [[package]] 3291 4876 name = "windows-result" 3292 4877 version = "0.3.4" 3293 4878 source = "registry+https://github.com/rust-lang/crates.io-index" 3294 4879 checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 3295 4880 dependencies = [ 3296 - "windows-link", 4881 + "windows-link 0.1.3", 3297 4882 ] 3298 4883 3299 4884 [[package]] ··· 3302 4887 source = "registry+https://github.com/rust-lang/crates.io-index" 3303 4888 checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 3304 4889 dependencies = [ 3305 - "windows-link", 4890 + "windows-link 0.1.3", 3306 4891 ] 3307 4892 3308 4893 [[package]] ··· 3333 4918 ] 3334 4919 3335 4920 [[package]] 4921 + name = "windows-sys" 4922 + version = "0.61.2" 4923 + source = "registry+https://github.com/rust-lang/crates.io-index" 4924 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 4925 + dependencies = [ 4926 + "windows-link 0.2.1", 4927 + ] 4928 + 4929 + [[package]] 3336 4930 name = "windows-targets" 3337 4931 version = "0.48.5" 3338 4932 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3454 5048 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 3455 5049 3456 5050 [[package]] 3457 - name = "wit-bindgen-rt" 3458 - version = "0.39.0" 5051 + name = "wit-bindgen" 5052 + version = "0.46.0" 3459 5053 source = "registry+https://github.com/rust-lang/crates.io-index" 3460 - checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 3461 - dependencies = [ 3462 - "bitflags", 3463 - ] 5054 + checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 3464 5055 3465 5056 [[package]] 3466 5057 name = "writeable" ··· 3469 5060 checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 3470 5061 3471 5062 [[package]] 5063 + name = "yansi" 5064 + version = "1.0.1" 5065 + source = "registry+https://github.com/rust-lang/crates.io-index" 5066 + checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 5067 + 5068 + [[package]] 3472 5069 name = "yoke" 3473 5070 version = "0.8.0" 3474 5071 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3488 5085 dependencies = [ 3489 5086 "proc-macro2", 3490 5087 "quote", 3491 - "syn", 5088 + "syn 2.0.105", 3492 5089 "synstructure", 3493 5090 ] 3494 5091 ··· 3509 5106 dependencies = [ 3510 5107 "proc-macro2", 3511 5108 "quote", 3512 - "syn", 5109 + "syn 2.0.105", 3513 5110 ] 3514 5111 3515 5112 [[package]] ··· 3529 5126 dependencies = [ 3530 5127 "proc-macro2", 3531 5128 "quote", 3532 - "syn", 5129 + "syn 2.0.105", 3533 5130 "synstructure", 3534 5131 ] 3535 5132 ··· 3550 5147 dependencies = [ 3551 5148 "proc-macro2", 3552 5149 "quote", 3553 - "syn", 5150 + "syn 2.0.105", 3554 5151 ] 3555 5152 3556 5153 [[package]] ··· 3583 5180 dependencies = [ 3584 5181 "proc-macro2", 3585 5182 "quote", 3586 - "syn", 5183 + "syn 2.0.105", 3587 5184 ] 3588 5185 3589 5186 [[package]]
+10 -2
Cargo.toml
··· 22 22 #Leaveing these two cause I think it is needed by the email crate for ssl 23 23 aws-lc-rs = "1.13.0" 24 24 rustls = { version = "0.23", default-features = false, features = ["tls12", "std", "logging", "aws_lc_rs"] } 25 - lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "tokio1", "tokio1-rustls"] } 25 + lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "sendmail-transport", "tokio1", "tokio1-rustls"] } 26 26 handlebars = { version = "6.3.2", features = ["rust-embed"] } 27 27 rust-embed = "8.7.2" 28 28 axum-template = { version = "3.0.0", features = ["handlebars"] } 29 29 rand = "0.9.2" 30 30 anyhow = "1.0.99" 31 - chrono = "0.4.41" 31 + chrono = { version = "0.4.42", features = ["default", "serde"] } 32 32 sha2 = "0.10" 33 + jacquard-common = "0.9.2" 34 + jacquard-identity = "0.9.2" 35 + multibase = "0.9.2" 36 + reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } 37 + urlencoding = "2.1" 38 + url = "2.5.7" 39 + html-escape = "0.2.13" 40 + josekit = "0.10.3"
+108 -28
README.md
··· 15 15 - Overrides The login endpoint to add 2FA for both Bluesky client logged in and OAuth logins 16 16 - Overrides the settings endpoints as well. As long as you have a confirmed email you can turn on 2FA 17 17 18 - ## Captcha on Create Account 18 + ## Captcha on account creation 19 19 20 - Future feature? 20 + Require a `verificationCode` set on the `createAccount` request. This is gotten from completing a captcha challenge 21 + hosted on the 22 + PDS mimicking what the Bluesky Entryway does. Migration tools will need to support this, but social-apps will support 23 + and redirect to `GATEKEEPER_DEFAULT_CAPTCHA_REDIRECT`. This is how the clients know to get the code to prove a captcha 24 + was successful. 25 + 26 + - Requires `GATEKEEPER_CREATE_ACCOUNT_CAPTCHA` to be set to true. 27 + - Requires `PDS_HCAPTCHA_SITE_KEY` and `PDS_HCAPTCHA_SECRET_KEY` to be set. Can sign up at https://www.hcaptcha.com/ 28 + - Requires proxying `/xrpc/com.atproto.server.describeServer`, `/xrpc/com.atproto.server.createAccount` and `/gate/*` to 29 + PDS 30 + Gatekeeper 31 + - Optional `GATEKEEPER_JWE_KEY` key to encrypt the captcha verification code. Defaults to a random 32 byte key. Not 32 + strictly needed unless you're scaling 33 + - Optional`GATEKEEPER_DEFAULT_CAPTCHA_REDIRECT` default redirect on captcha success. Defaults to `https://bsky.app`. 34 + - Optional `GATEKEEPER_CAPTCHA_SUCCESS_REDIRECTS` allowed redirect urls for captcha success. You want these to match the 35 + url showing the captcha. Defaults are: 36 + - https://bsky.app 37 + - https://pdsmoover.com 38 + - https://blacksky.community 39 + - https://tektite.cc 40 + 41 + ## Block account creation unless it's a migration 42 + 43 + You can set `GATEKEEPER_ALLOW_ONLY_MIGRATIONS` to block createAccount unless it's via a migration. This does not require 44 + a change for migration tools, but social-apps create a new account will no longer work and to create a brand new account 45 + users will need to do this via the Oauth account create screen on the PDS. We recommend setting `PDS_HCAPTCHA_SITE_KEY` 46 + and `PDS_HCAPTCHA_SECRET_KEY` so the OAuth screen is protected by a captcha if you use this with invite codes turned 47 + off. 21 48 22 49 # Setup 23 50 ··· 49 76 - pds 50 77 ``` 51 78 79 + For Coolify, if you're using Traefik as your proxy you'll need to make sure the labels for the container are set up 80 + correctly. A full example can be found at [./examples/coolify-compose.yml](./examples/coolify-compose.yml). 81 + 82 + ```yml 83 + gatekeeper: 84 + container_name: gatekeeper 85 + image: 'fatfingers23/pds_gatekeeper:latest' 86 + restart: unless-stopped 87 + volumes: 88 + - '/pds:/pds' 89 + environment: 90 + - 'PDS_DATA_DIRECTORY=${PDS_DATA_DIRECTORY:-/pds}' 91 + - 'PDS_BASE_URL=http://pds:3000' 92 + - GATEKEEPER_HOST=0.0.0.0 93 + depends_on: 94 + - pds 95 + healthcheck: 96 + test: 97 + - CMD 98 + - timeout 99 + - '1' 100 + - bash 101 + - '-c' 102 + - 'cat < /dev/null > /dev/tcp/0.0.0.0/8080' 103 + interval: 10s 104 + timeout: 5s 105 + retries: 3 106 + start_period: 10s 107 + labels: 108 + - traefik.enable=true 109 + - 'traefik.http.routers.pds-gatekeeper.rule=Host(`yourpds.com`) && (Path(`/xrpc/com.atproto.server.getSession`) || Path(`/xrpc/com.atproto.server.updateEmail`) || Path(`/xrpc/com.atproto.server.createSession`) || Path(`/xrpc/com.atproto.server.createAccount`) || Path(`/@atproto/oauth-provider/~api/sign-in`))' 110 + - traefik.http.routers.pds-gatekeeper.entrypoints=https 111 + - traefik.http.routers.pds-gatekeeper.tls=true 112 + - traefik.http.routers.pds-gatekeeper.priority=100 113 + - traefik.http.routers.pds-gatekeeper.middlewares=gatekeeper-cors 114 + - traefik.http.services.pds-gatekeeper.loadbalancer.server.port=8080 115 + - traefik.http.services.pds-gatekeeper.loadbalancer.server.scheme=http 116 + - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE,OPTIONS,PATCH' 117 + - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowheaders=*' 118 + - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolalloworiginlist=*' 119 + - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolmaxage=100 120 + - traefik.http.middlewares.gatekeeper-cors.headers.addvaryheader=true 121 + - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowcredentials=true 122 + ``` 123 + 52 124 ## Caddy setup 53 125 54 126 For the reverse proxy I use caddy. This part is what overwrites the endpoints and proxies them to PDS gatekeeper to add 55 127 in extra functionality. The main part is below, for a full example see [./examples/Caddyfile](./examples/Caddyfile). 56 128 This is usually found at `/pds/caddy/etc/caddy/Caddyfile` on your PDS. 57 129 58 - ```caddyfile 130 + ``` 59 131 @gatekeeper { 60 - path /xrpc/com.atproto.server.getSession 61 - path /xrpc/com.atproto.server.updateEmail 62 - path /xrpc/com.atproto.server.createSession 63 - path /xrpc/com.atproto.server.createAccount 64 - path /@atproto/oauth-provider/~api/sign-in 132 + path /xrpc/com.atproto.server.getSession 133 + path /xrpc/com.atproto.server.describeServer 134 + path /xrpc/com.atproto.server.updateEmail 135 + path /xrpc/com.atproto.server.createSession 136 + path /xrpc/com.atproto.server.createAccount 137 + path /@atproto/oauth-provider/~api/sign-in 138 + path /gate/* 65 139 } 66 140 67 141 handle @gatekeeper { 68 - reverse_proxy http://localhost:8080 69 - } 142 + reverse_proxy http://localhost:8080 143 + } 70 144 71 - reverse_proxy http://localhost:3000 145 + reverse_proxy http://localhost:3000 72 146 ``` 73 147 74 148 If you use a cloudflare tunnel then your caddyfile would look a bit more like below with your tunnel proxying to 75 149 `localhost:8081` (or w/e port you want). 76 150 77 - ```caddyfile 151 + ``` 78 152 http://*.localhost:8082, http://localhost:8082 { 79 - @gatekeeper { 80 - path /xrpc/com.atproto.server.getSession 81 - path /xrpc/com.atproto.server.updateEmail 82 - path /xrpc/com.atproto.server.createSession 83 - path /xrpc/com.atproto.server.createAccount 84 - path /@atproto/oauth-provider/~api/sign-in 85 - } 153 + @gatekeeper { 154 + path /xrpc/com.atproto.server.getSession 155 + path /xrpc/com.atproto.server.describeServer 156 + path /xrpc/com.atproto.server.updateEmail 157 + path /xrpc/com.atproto.server.createSession 158 + path /xrpc/com.atproto.server.createAccount 159 + path /@atproto/oauth-provider/~api/sign-in 160 + path /gate/* 161 + } 86 162 87 - handle @gatekeeper { 88 - reverse_proxy http://localhost:8080 { 89 - #Makes sure the cloudflare ip is proxied and able to be picked up by pds gatekeeper 90 - header_up X-Forwarded-For {http.request.header.CF-Connecting-IP} 91 - } 92 - } 93 - 94 - reverse_proxy http://localhost:3000 163 + handle @gatekeeper { 164 + #This is the address for PDS gatekeeper, default is 8080 165 + reverse_proxy http://localhost:8080 166 + #Makes sure the cloudflare ip is proxied and able to be picked up by pds gatekeeper 167 + header_up X-Forwarded-For {http.request.header.CF-Connecting-IP} 168 + } 169 + reverse_proxy http://localhost:3000 95 170 } 96 171 97 172 ``` ··· 124 199 125 200 `GATEKEEPER_CREATE_ACCOUNT_BURST` - Sets how many requests can be made in a burst. In the prior example this is where 126 201 the 5 comes from. Example can set this to 10 to allow for 10 requests in a burst, and after 60 seconds it will drop one 127 - off. 202 + off. 203 + 204 + `GATEKEEPER_ALLOW_ONLY_MIGRATIONS` - Defaults false. If set to true, will only allow the 205 + `/xrpc/com.atproto.server.createAccount` endpoint to be used for migrations. Meaning it will check for the serviceAuth 206 + token and verify it is valid. 207 +
+22 -22
examples/Caddyfile
··· 1 1 { 2 - email youremail@myemail.com 3 - on_demand_tls { 4 - ask http://localhost:3000/tls-check 5 - } 2 + email youremail@myemail.com 3 + on_demand_tls { 4 + ask http://localhost:3000/tls-check 5 + } 6 6 } 7 7 8 8 *.yourpds.com, yourpds.com { 9 - tls { 10 - on_demand 11 - } 12 - # You'll most likely just want from here to.... 13 - @gatekeeper { 14 - path /xrpc/com.atproto.server.getSession 15 - path /xrpc/com.atproto.server.updateEmail 16 - path /xrpc/com.atproto.server.createSession 17 - path /xrpc/com.atproto.server.createAccount 18 - path /@atproto/oauth-provider/~api/sign-in 9 + tls { 10 + on_demand 19 11 } 12 + # You'll most likely just want from here to.... 13 + @gatekeeper { 14 + path /xrpc/com.atproto.server.getSession 15 + path /xrpc/com.atproto.server.describeServer 16 + path /xrpc/com.atproto.server.updateEmail 17 + path /xrpc/com.atproto.server.createSession 18 + path /xrpc/com.atproto.server.createAccount 19 + path /@atproto/oauth-provider/~api/sign-in 20 + path /gate/* 21 + } 20 22 21 - handle @gatekeeper { 22 - #This is the address for PDS gatekeeper, default is 8080 23 - reverse_proxy http://localhost:8080 24 - } 23 + handle @gatekeeper { 24 + #This is the address for PDS gatekeeper, default is 8080 25 + reverse_proxy http://localhost:8080 26 + } 25 27 26 - reverse_proxy http://localhost:3000 27 - #..here. Copy and paste this replacing the reverse_proxy http://localhost:3000 line 28 + reverse_proxy http://localhost:3000 29 + #..here. Copy and paste this replacing the reverse_proxy http://localhost:3000 line 28 30 } 29 - 30 -
+73
examples/coolify-compose.yml
··· 1 + services: 2 + pds: 3 + image: 'ghcr.io/bluesky-social/pds:0.4.182' 4 + volumes: 5 + - '/pds:/pds' 6 + environment: 7 + - SERVICE_URL_PDS_3000 8 + - 'PDS_HOSTNAME=${SERVICE_FQDN_PDS_3000}' 9 + - 'PDS_JWT_SECRET=${SERVICE_HEX_32_JWTSECRET}' 10 + - 'PDS_ADMIN_PASSWORD=${SERVICE_PASSWORD_ADMIN}' 11 + - 'PDS_ADMIN_EMAIL=${PDS_ADMIN_EMAIL}' 12 + - 'PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=${SERVICE_HEX_32_ROTATIONKEY}' 13 + - 'PDS_DATA_DIRECTORY=${PDS_DATA_DIRECTORY:-/pds}' 14 + - 'PDS_BLOBSTORE_DISK_LOCATION=${PDS_DATA_DIRECTORY:-/pds}/blocks' 15 + - 'PDS_BLOB_UPLOAD_LIMIT=${PDS_BLOB_UPLOAD_LIMIT:-104857600}' 16 + - 'PDS_DID_PLC_URL=${PDS_DID_PLC_URL:-https://plc.directory}' 17 + - 'PDS_EMAIL_FROM_ADDRESS=${PDS_EMAIL_FROM_ADDRESS}' 18 + - 'PDS_EMAIL_SMTP_URL=${PDS_EMAIL_SMTP_URL}' 19 + - 'PDS_BSKY_APP_VIEW_URL=${PDS_BSKY_APP_VIEW_URL:-https://api.bsky.app}' 20 + - 'PDS_BSKY_APP_VIEW_DID=${PDS_BSKY_APP_VIEW_DID:-did:web:api.bsky.app}' 21 + - 'PDS_REPORT_SERVICE_URL=${PDS_REPORT_SERVICE_URL:-https://mod.bsky.app/xrpc/com.atproto.moderation.createReport}' 22 + - 'PDS_REPORT_SERVICE_DID=${PDS_REPORT_SERVICE_DID:-did:plc:ar7c4by46qjdydhdevvrndac}' 23 + - 'PDS_CRAWLERS=${PDS_CRAWLERS:-https://bsky.network}' 24 + - 'LOG_ENABLED=${LOG_ENABLED:-true}' 25 + command: "sh -c '\n set -euo pipefail\n echo \"Installing required packages and pdsadmin...\"\n apk add --no-cache openssl curl bash jq coreutils gnupg util-linux-misc >/dev/null\n curl -o /usr/local/bin/pdsadmin.sh https://raw.githubusercontent.com/bluesky-social/pds/main/pdsadmin.sh\n chmod 700 /usr/local/bin/pdsadmin.sh\n ln -sf /usr/local/bin/pdsadmin.sh /usr/local/bin/pdsadmin\n echo \"Creating an empty pds.env file so pdsadmin works...\"\n touch ${PDS_DATA_DIRECTORY}/pds.env\n echo \"Launching PDS, enjoy!...\"\n exec node --enable-source-maps index.js\n'\n" 26 + healthcheck: 27 + test: 28 + - CMD 29 + - wget 30 + - '--spider' 31 + - 'http://127.0.0.1:3000/xrpc/_health' 32 + interval: 5s 33 + timeout: 10s 34 + retries: 10 35 + gatekeeper: 36 + container_name: gatekeeper 37 + image: 'fatfingers23/pds_gatekeeper:latest' 38 + restart: unless-stopped 39 + volumes: 40 + - '/pds:/pds' 41 + environment: 42 + - 'PDS_DATA_DIRECTORY=${PDS_DATA_DIRECTORY:-/pds}' 43 + - 'PDS_BASE_URL=http://pds:3000' 44 + - GATEKEEPER_HOST=0.0.0.0 45 + depends_on: 46 + - pds 47 + healthcheck: 48 + test: 49 + - CMD 50 + - timeout 51 + - '1' 52 + - bash 53 + - '-c' 54 + - 'cat < /dev/null > /dev/tcp/0.0.0.0/8080' 55 + interval: 10s 56 + timeout: 5s 57 + retries: 3 58 + start_period: 10s 59 + labels: 60 + - traefik.enable=true 61 + - 'traefik.http.routers.pds-gatekeeper.rule=Host(`yourpds.com`) && (Path(`/xrpc/com.atproto.server.getSession`) || Path(`/xrpc/com.atproto.server.describeServer`) || Path(`/xrpc/com.atproto.server.updateEmail`) || Path(`/xrpc/com.atproto.server.createSession`) || Path(`/xrpc/com.atproto.server.createAccount`) || Path(`/@atproto/oauth-provider/~api/sign-in`) || Path(`/gate`))' 62 + - traefik.http.routers.pds-gatekeeper.entrypoints=https 63 + - traefik.http.routers.pds-gatekeeper.tls=true 64 + - traefik.http.routers.pds-gatekeeper.priority=100 65 + - traefik.http.routers.pds-gatekeeper.middlewares=gatekeeper-cors 66 + - traefik.http.services.pds-gatekeeper.loadbalancer.server.port=8080 67 + - traefik.http.services.pds-gatekeeper.loadbalancer.server.scheme=http 68 + - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE,OPTIONS,PATCH' 69 + - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowheaders=*' 70 + - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolalloworiginlist=*' 71 + - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolmaxage=100 72 + - traefik.http.middlewares.gatekeeper-cors.headers.addvaryheader=true 73 + - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowcredentials=true
+166
html_templates/captcha.hbs
··· 1 + <html lang="en" class=" "> 2 + <head> 3 + <meta charset="utf-8"/> 4 + <meta 5 + name="viewport" 6 + content="width=device-width, initial-scale=1, minimum-scale=1, viewport-fit=cover" 7 + /> 8 + <meta name="referrer" content="origin-when-cross-origin"/> 9 + 10 + <title> 11 + {{pds}} - Captcha 12 + </title> 13 + <style> 14 + :root, 15 + :root.light-mode { 16 + --brand-color: rgb(16, 131, 254); 17 + --primary-color: rgb(7, 10, 13); 18 + --secondary-color: rgb(66, 86, 108); 19 + --bg-primary-color: rgb(255, 255, 255); 20 + --bg-secondary-color: rgb(240, 242, 245); 21 + } 22 + 23 + @media (prefers-color-scheme: dark) { 24 + :root { 25 + --brand-color: rgb(16, 131, 254); 26 + --primary-color: rgb(255, 255, 255); 27 + --secondary-color: rgb(133, 152, 173); 28 + --bg-primary-color: rgb(7, 10, 13); 29 + --bg-secondary-color: rgb(13, 18, 23); 30 + } 31 + } 32 + 33 + :root.dark-mode { 34 + --brand-color: rgb(16, 131, 254); 35 + --primary-color: rgb(255, 255, 255); 36 + --secondary-color: rgb(133, 152, 173); 37 + --bg-primary-color: rgb(7, 10, 13); 38 + --bg-secondary-color: rgb(13, 18, 23); 39 + } 40 + 41 + body { 42 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, 43 + Arial, sans-serif; 44 + background: var(--bg-primary-color); 45 + color: var(--primary-color); 46 + text-rendering: optimizeLegibility; 47 + -webkit-font-smoothing: antialiased; 48 + } 49 + 50 + .info { 51 + border-radius: 8px; 52 + padding: 12px 14px; 53 + letter-spacing: 0.25px; 54 + font-weight: 400; 55 + background-color: var(--bg-secondary-color); 56 + color: var(--secondary-color); 57 + } 58 + 59 + .gate-page, 60 + .error-page { 61 + margin: 0; 62 + margin-top: 10px; 63 + display: flex; 64 + justify-content: center; 65 + } 66 + 67 + .gate-page .main, 68 + .error-page .main { 69 + max-width: 600px; 70 + width: 100%; 71 + display: flex; 72 + flex-direction: column; 73 + } 74 + 75 + .gate-page .main { 76 + padding: 0; 77 + } 78 + 79 + .error-page .main { 80 + padding: 20px; 81 + } 82 + 83 + .gate-page .main > :not(:first-child), 84 + .error-page .main > :not(:first-child) { 85 + margin-top: 20px; 86 + } 87 + 88 + .gate-page #gate-form { 89 + margin: 0; 90 + } 91 + 92 + .gate-page #hcaptcha { 93 + display: flex; 94 + justify-content: center; 95 + } 96 + 97 + .pds-title { 98 + font-size: 2.25rem; 99 + font-weight: 700; 100 + line-height: 1.2; 101 + text-align: center; 102 + color: var(--primary-color); 103 + margin: 10px 0 16px; 104 + } 105 + 106 + </style> 107 + 108 + <script type="text/javascript"> 109 + function onCaptchaReady() { 110 + const theme = document.documentElement.classList.contains('dark-mode') ? 111 + 'dark' : 112 + document.documentElement.classList.contains('light-mode') ? 113 + 'light' : 114 + window.matchMedia('(prefers-color-scheme: dark)').matches ? 115 + 'dark' : 116 + 'light' 117 + hcaptcha.render('hcaptcha', {theme}); 118 + } 119 + 120 + function onCaptchaComplete() { 121 + setTimeout(function () { 122 + document.getElementById('gate-form').submit(); 123 + }, 1000); 124 + } 125 + 126 + function onCaptchaError() { 127 + const url = new URL(location.href); 128 + url.searchParams.set('error', 'true'); 129 + location.assign(url.search); 130 + } 131 + 132 + function onCaptchaExpired() { 133 + const url = new URL(location.href); 134 + url.searchParams.set('error', 'expired'); 135 + location.assign(url.search); 136 + } 137 + </script> 138 + <script 139 + src="https://js.hcaptcha.com/1/api.js?render=explicit&onload=onCaptchaReady" 140 + async 141 + defer 142 + ></script> 143 + <link rel="stylesheet" href="/gate/signup/assets/common.css"/> 144 + </head> 145 + 146 + <body class="gate-page"> 147 + <div class="main"> 148 + <div class="pds-title">{{pds}}</div> 149 + <form id="gate-form" action="" method="POST"> 150 + <div 151 + id="hcaptcha" 152 + data-sitekey="{{captcha_site_key}}" 153 + data-callback="onCaptchaComplete" 154 + data-error-callback="onCaptchaError" 155 + data-expired-callback="onCaptchaExpired" 156 + data-chalexpired-callback="onCaptchaExpired" 157 + ></div> 158 + <input type="hidden" name="redirect_url" value="{{redirect_url}}"/> 159 + </form> 160 + {{#if error_message }} 161 + <div class="info">{{error_message}}</div> 162 + {{/if}} 163 + 164 + </div> 165 + </body> 166 + </html>
+1 -1
justfile
··· 2 2 docker buildx build \ 3 3 --platform linux/arm64,linux/amd64 \ 4 4 --tag fatfingers23/pds_gatekeeper:latest \ 5 - --tag fatfingers23/pds_gatekeeper:0.1.0.3 \ 5 + --tag fatfingers23/pds_gatekeeper:0.1.0.5 \ 6 6 --push .
+10
migrations/20251126000000_gate_codes.sql
··· 1 + -- Add migration script here 2 + CREATE TABLE IF NOT EXISTS gate_codes 3 + ( 4 + code VARCHAR PRIMARY KEY, 5 + handle VARCHAR NOT NULL, 6 + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 7 + ); 8 + 9 + -- Index on created_at for efficient cleanup of expired codes 10 + CREATE INDEX IF NOT EXISTS idx_gate_codes_created_at ON gate_codes(created_at);
+247
src/gate.rs
··· 1 + use crate::AppState; 2 + use crate::helpers::{generate_gate_token, json_error_response}; 3 + use axum::Form; 4 + use axum::extract::{Query, State}; 5 + use axum::http::StatusCode; 6 + use axum::response::{IntoResponse, Redirect, Response}; 7 + use axum_template::RenderHtml; 8 + use chrono::{DateTime, Utc}; 9 + use serde::{Deserialize, Serialize}; 10 + use std::env; 11 + use tracing::log; 12 + 13 + #[derive(Deserialize)] 14 + pub struct GateQuery { 15 + handle: String, 16 + state: String, 17 + #[serde(default)] 18 + error: Option<String>, 19 + #[serde(default)] 20 + redirect_url: Option<String>, 21 + } 22 + 23 + #[derive(Deserialize, Serialize)] 24 + pub struct CaptchaPage { 25 + handle: String, 26 + state: String, 27 + captcha_site_key: String, 28 + error_message: Option<String>, 29 + pds: String, 30 + redirect_url: Option<String>, 31 + } 32 + 33 + #[derive(Deserialize)] 34 + pub struct CaptchaForm { 35 + #[serde(rename = "h-captcha-response")] 36 + h_captcha_response: String, 37 + #[serde(default)] 38 + redirect_url: Option<String>, 39 + } 40 + 41 + /// GET /gate - Display the captcha page 42 + pub async fn get_gate( 43 + Query(params): Query<GateQuery>, 44 + State(state): State<AppState>, 45 + ) -> impl IntoResponse { 46 + let hcaptcha_site_key = match env::var("PDS_HCAPTCHA_SITE_KEY") { 47 + Ok(key) => key, 48 + Err(_) => { 49 + return json_error_response( 50 + StatusCode::INTERNAL_SERVER_ERROR, 51 + "ServerError", 52 + "hCaptcha is not configured", 53 + ) 54 + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()); 55 + } 56 + }; 57 + 58 + let error_message = match params.error { 59 + None => None, 60 + Some(error) => Some(html_escape::encode_safe(&error).to_string()), 61 + }; 62 + 63 + RenderHtml( 64 + "captcha.hbs", 65 + state.template_engine, 66 + CaptchaPage { 67 + handle: params.handle, 68 + state: params.state, 69 + captcha_site_key: hcaptcha_site_key, 70 + error_message, 71 + pds: state.app_config.pds_service_did.replace("did:web:", ""), 72 + redirect_url: params.redirect_url, 73 + }, 74 + ) 75 + .into_response() 76 + } 77 + 78 + /// POST /gate - Verify captcha and redirect 79 + pub async fn post_gate( 80 + State(state): State<AppState>, 81 + Query(params): Query<GateQuery>, 82 + Form(form): Form<CaptchaForm>, 83 + ) -> Response { 84 + // Verify hCaptcha response 85 + let hcaptcha_secret = match env::var("PDS_HCAPTCHA_SECRET_KEY") { 86 + Ok(secret) => secret, 87 + Err(_) => { 88 + return json_error_response( 89 + StatusCode::INTERNAL_SERVER_ERROR, 90 + "ServerError", 91 + "hCaptcha is not configured", 92 + ) 93 + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()); 94 + } 95 + }; 96 + 97 + let client = match reqwest::Client::builder() 98 + .timeout(std::time::Duration::from_secs(10)) 99 + .build() 100 + { 101 + Ok(c) => c, 102 + Err(e) => { 103 + log::error!("Failed to create HTTP client: {}", e); 104 + return json_error_response( 105 + StatusCode::INTERNAL_SERVER_ERROR, 106 + "ServerError", 107 + "Failed to verify captcha", 108 + ) 109 + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()); 110 + } 111 + }; 112 + 113 + #[derive(Deserialize, Serialize)] 114 + struct HCaptchaResponse { 115 + success: bool, 116 + challenge_ts: DateTime<Utc>, 117 + hostname: String, 118 + #[serde(rename = "error-codes", default)] 119 + error_codes: Vec<String>, 120 + } 121 + 122 + let verification_result = client 123 + .post("https://api.hcaptcha.com/siteverify") 124 + .form(&[ 125 + ("secret", hcaptcha_secret.as_str()), 126 + ("response", form.h_captcha_response.as_str()), 127 + ]) 128 + .send() 129 + .await; 130 + 131 + let verification_response = match verification_result { 132 + Ok(resp) => resp, 133 + Err(e) => { 134 + log::error!("Failed to verify hCaptcha: {}", e); 135 + 136 + return Redirect::to(&format!( 137 + "/gate?handle={}&state={}&error={}", 138 + url_encode(&params.handle), 139 + url_encode(&params.state), 140 + url_encode("Verification failed. Please try again.") 141 + )) 142 + .into_response(); 143 + } 144 + }; 145 + 146 + let captcha_result: HCaptchaResponse = match verification_response.json().await { 147 + Ok(result) => result, 148 + Err(e) => { 149 + log::error!("Failed to parse hCaptcha response: {}", e); 150 + 151 + return Redirect::to(&format!( 152 + "/gate?handle={}&state={}&error={}", 153 + url_encode(&params.handle), 154 + url_encode(&params.state), 155 + url_encode("Verification failed. Please try again.") 156 + )) 157 + .into_response(); 158 + } 159 + }; 160 + 161 + if !captcha_result.success { 162 + log::warn!( 163 + "hCaptcha verification failed for handle {}: {:?}", 164 + params.handle, 165 + captcha_result.error_codes 166 + ); 167 + return Redirect::to(&format!( 168 + "/gate?handle={}&state={}&error={}", 169 + url_encode(&params.handle), 170 + url_encode(&params.state), 171 + url_encode("Verification failed. Please try again.") 172 + )) 173 + .into_response(); 174 + } 175 + 176 + // Generate secure JWE verification token 177 + let code = match generate_gate_token(&params.handle, &state.app_config.gate_jwe_key) { 178 + Ok(token) => token, 179 + Err(e) => { 180 + log::error!("Failed to generate gate token: {}", e); 181 + return json_error_response( 182 + StatusCode::INTERNAL_SERVER_ERROR, 183 + "ServerError", 184 + "Failed to create verification code", 185 + ) 186 + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()); 187 + } 188 + }; 189 + 190 + let now = Utc::now(); 191 + 192 + // Store the encrypted token in the database 193 + let result = sqlx::query( 194 + "INSERT INTO gate_codes (code, handle, created_at) 195 + VALUES (?, ?, ?)", 196 + ) 197 + .bind(&code) 198 + .bind(&params.handle) 199 + .bind(now) 200 + .execute(&state.pds_gatekeeper_pool) 201 + .await; 202 + 203 + if let Err(e) = result { 204 + log::error!("Failed to store gate code: {}", e); 205 + return json_error_response( 206 + StatusCode::INTERNAL_SERVER_ERROR, 207 + "ServerError", 208 + "Failed to create verification code", 209 + ) 210 + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()); 211 + } 212 + 213 + // Redirects by origin if it's found. If not redirect to the configured URL. 214 + let mut base_redirect = state.app_config.default_successful_redirect_url.clone(); 215 + if let Some(ref redirect_url) = form.redirect_url { 216 + let trimmed = redirect_url.trim(); 217 + if !trimmed.is_empty() 218 + && (trimmed.starts_with("https://") || trimmed.starts_with("http://")) 219 + { 220 + base_redirect = trimmed.trim_end_matches('/').to_string(); 221 + } 222 + } 223 + 224 + let base_redirect = match state 225 + .app_config 226 + .captcha_success_redirects 227 + .contains(&base_redirect) 228 + { 229 + true => base_redirect, 230 + false => state.app_config.default_successful_redirect_url.clone(), 231 + }; 232 + 233 + // Redirect to client app with code and state 234 + let redirect_url = format!( 235 + "{}/?code={}&state={}", 236 + base_redirect, 237 + url_encode(&code), 238 + url_encode(&params.state) 239 + ); 240 + 241 + Redirect::to(&redirect_url).into_response() 242 + } 243 + 244 + /// Simple URL encode function 245 + fn url_encode(s: &str) -> String { 246 + urlencoding::encode(s).to_string() 247 + }
+175 -13
src/helpers.rs
··· 1 1 use crate::AppState; 2 2 use crate::helpers::TokenCheckError::InvalidToken; 3 3 use anyhow::anyhow; 4 - use axum::body::{Body, to_bytes}; 5 - use axum::extract::Request; 6 - use axum::http::header::CONTENT_TYPE; 7 - use axum::http::{HeaderMap, StatusCode, Uri}; 8 - use axum::response::{IntoResponse, Response}; 4 + use axum::{ 5 + body::{Body, to_bytes}, 6 + extract::Request, 7 + http::header::CONTENT_TYPE, 8 + http::{HeaderMap, StatusCode, Uri}, 9 + response::{IntoResponse, Response}, 10 + }; 9 11 use axum_template::TemplateEngine; 10 12 use chrono::Utc; 11 - use lettre::message::{MultiPart, SinglePart, header}; 12 - use lettre::{AsyncTransport, Message}; 13 + use jacquard_common::{ 14 + service_auth, service_auth::PublicKey, types::did::Did, types::did_doc::VerificationMethod, 15 + types::nsid::Nsid, 16 + }; 17 + use jacquard_identity::{PublicResolver, resolver::IdentityResolver}; 18 + use josekit::jwe::alg::direct::DirectJweAlgorithm; 19 + use lettre::{ 20 + Message, 21 + message::{MultiPart, SinglePart, header}, 22 + }; 13 23 use rand::Rng; 14 24 use serde::de::DeserializeOwned; 15 25 use serde_json::{Map, Value}; 16 26 use sha2::{Digest, Sha256}; 17 27 use sqlx::SqlitePool; 18 - use std::env; 28 + use std::sync::Arc; 19 29 use tracing::{error, log}; 20 30 21 31 ///Used to generate the email 2fa code ··· 40 50 where 41 51 T: DeserializeOwned, 42 52 { 43 - let uri = format!("{}{}", state.pds_base_url, path); 53 + let uri = format!("{}{}", state.app_config.pds_base_url, path); 44 54 *req.uri_mut() = Uri::try_from(uri).map_err(|_| StatusCode::BAD_REQUEST)?; 45 55 46 56 let result = state ··· 333 343 let email_body = state 334 344 .template_engine 335 345 .render("two_factor_code.hbs", email_data)?; 336 - let email_subject = env::var("GATEKEEPER_TWO_FACTOR_EMAIL_SUBJECT") 337 - .unwrap_or("Sign in to Bluesky".to_string()); 338 346 339 347 let email_message = Message::builder() 340 348 //TODO prob get the proper type in the state 341 - .from(state.mailer_from.parse()?) 349 + .from(state.app_config.mailer_from.parse()?) 342 350 .to(email.parse()?) 343 - .subject(email_subject) 351 + .subject(&state.app_config.email_subject) 344 352 .multipart( 345 353 MultiPart::alternative() // This is composed of two parts. 346 354 .singlepart( ··· 523 531 524 532 format!("{masked_local}@{masked_domain}") 525 533 } 534 + 535 + pub enum VerifyServiceAuthError { 536 + AuthFailed, 537 + Error(anyhow::Error), 538 + } 539 + 540 + /// Verifies the service auth token that is appended to an XRPC proxy request 541 + pub async fn verify_service_auth( 542 + jwt: &str, 543 + lxm: &Nsid<'static>, 544 + public_resolver: Arc<PublicResolver>, 545 + service_did: &Did<'static>, 546 + //The did of the user wanting to create an account 547 + requested_did: &Did<'static>, 548 + ) -> Result<(), VerifyServiceAuthError> { 549 + let parsed = 550 + service_auth::parse_jwt(jwt).map_err(|e| VerifyServiceAuthError::Error(e.into()))?; 551 + 552 + let claims = parsed.claims(); 553 + 554 + let did_doc = public_resolver 555 + .resolve_did_doc(&requested_did) 556 + .await 557 + .map_err(|err| { 558 + log::error!("Error resolving the service auth for: {}", claims.iss); 559 + return VerifyServiceAuthError::Error(err.into()); 560 + })?; 561 + 562 + // Parse the DID document response to get verification methods 563 + let doc = did_doc.parse().map_err(|err| { 564 + log::error!("Error parsing the service auth did doc: {}", claims.iss); 565 + VerifyServiceAuthError::Error(anyhow::anyhow!(err)) 566 + })?; 567 + 568 + let verification_methods = doc.verification_method.as_deref().ok_or_else(|| { 569 + VerifyServiceAuthError::Error(anyhow::anyhow!( 570 + "No verification methods in did doc: {}", 571 + &claims.iss 572 + )) 573 + })?; 574 + 575 + let signing_key = extract_signing_key(verification_methods).ok_or_else(|| { 576 + VerifyServiceAuthError::Error(anyhow::anyhow!( 577 + "No signing key found in did doc: {}", 578 + &claims.iss 579 + )) 580 + })?; 581 + 582 + service_auth::verify_signature(&parsed, &signing_key).map_err(|err| { 583 + log::error!("Error verifying service auth signature: {}", err); 584 + VerifyServiceAuthError::AuthFailed 585 + })?; 586 + 587 + // Now validate claims (audience, expiration, etc.) 588 + claims.validate(service_did).map_err(|e| { 589 + log::error!("Error validating service auth claims: {}", e); 590 + VerifyServiceAuthError::AuthFailed 591 + })?; 592 + 593 + if claims.aud != *service_did { 594 + log::error!("Invalid audience (did:web): {}", claims.aud); 595 + return Err(VerifyServiceAuthError::AuthFailed); 596 + } 597 + 598 + let lxm_from_claims = claims.lxm.as_ref().ok_or_else(|| { 599 + VerifyServiceAuthError::Error(anyhow::anyhow!("No lxm claim in service auth JWT")) 600 + })?; 601 + 602 + if lxm_from_claims != lxm { 603 + return Err(VerifyServiceAuthError::Error(anyhow::anyhow!( 604 + "Invalid XRPC endpoint requested" 605 + ))); 606 + } 607 + Ok(()) 608 + } 609 + 610 + /// Ripped from Jacquard 611 + /// 612 + /// Extract the signing key from a DID document's verification methods. 613 + /// 614 + /// This looks for a key with type "atproto" or the first available key 615 + /// if no atproto-specific key is found. 616 + fn extract_signing_key(methods: &[VerificationMethod]) -> Option<PublicKey> { 617 + // First try to find an atproto-specific key 618 + let atproto_method = methods 619 + .iter() 620 + .find(|m| m.r#type.as_ref() == "Multikey" || m.r#type.as_ref() == "atproto"); 621 + 622 + let method = atproto_method.or_else(|| methods.first())?; 623 + 624 + // Parse the multikey 625 + let public_key_multibase = method.public_key_multibase.as_ref()?; 626 + 627 + // Decode multibase 628 + let (_, key_bytes) = multibase::decode(public_key_multibase.as_ref()).ok()?; 629 + 630 + // First two bytes are the multicodec prefix 631 + if key_bytes.len() < 2 { 632 + return None; 633 + } 634 + 635 + let codec = &key_bytes[..2]; 636 + let key_material = &key_bytes[2..]; 637 + 638 + match codec { 639 + // p256-pub (0x1200) 640 + [0x80, 0x24] => PublicKey::from_p256_bytes(key_material).ok(), 641 + // secp256k1-pub (0xe7) 642 + [0xe7, 0x01] => PublicKey::from_k256_bytes(key_material).ok(), 643 + _ => None, 644 + } 645 + } 646 + 647 + /// Payload for gate JWE tokens 648 + #[derive(serde::Serialize, serde::Deserialize, Debug)] 649 + pub struct GateTokenPayload { 650 + pub handle: String, 651 + pub created_at: String, 652 + } 653 + 654 + /// Generate a secure JWE token for gate verification 655 + pub fn generate_gate_token(handle: &str, encryption_key: &[u8]) -> Result<String, anyhow::Error> { 656 + use josekit::jwe::{JweHeader, alg::direct::DirectJweAlgorithm}; 657 + 658 + let payload = GateTokenPayload { 659 + handle: handle.to_string(), 660 + created_at: Utc::now().to_rfc3339(), 661 + }; 662 + 663 + let payload_json = serde_json::to_string(&payload)?; 664 + 665 + let mut header = JweHeader::new(); 666 + header.set_token_type("JWT"); 667 + header.set_content_encryption("A128CBC-HS256"); 668 + 669 + let encrypter = DirectJweAlgorithm::Dir.encrypter_from_bytes(encryption_key)?; 670 + 671 + // Encrypt 672 + let jwe = josekit::jwe::serialize_compact(payload_json.as_bytes(), &header, &encrypter)?; 673 + 674 + Ok(jwe) 675 + } 676 + 677 + /// Verify and decrypt a gate JWE token, returning the payload if valid 678 + pub fn verify_gate_token( 679 + token: &str, 680 + encryption_key: &[u8], 681 + ) -> Result<GateTokenPayload, anyhow::Error> { 682 + let decrypter = DirectJweAlgorithm::Dir.decrypter_from_bytes(encryption_key)?; 683 + let (payload_bytes, _header) = josekit::jwe::deserialize_compact(token, &decrypter)?; 684 + let payload: GateTokenPayload = serde_json::from_slice(&payload_bytes)?; 685 + 686 + Ok(payload) 687 + }
+201 -35
src/main.rs
··· 1 1 #![warn(clippy::unwrap_used)] 2 + use crate::gate::{get_gate, post_gate}; 2 3 use crate::oauth_provider::sign_in; 3 - use crate::xrpc::com_atproto_server::{create_account, create_session, get_session, update_email}; 4 - use axum::body::Body; 5 - use axum::handler::Handler; 6 - use axum::http::{Method, header}; 7 - use axum::middleware as ax_middleware; 8 - use axum::routing::post; 9 - use axum::{Router, routing::get}; 4 + use crate::xrpc::com_atproto_server::{ 5 + create_account, create_session, describe_server, get_session, update_email, 6 + }; 7 + use anyhow::{Result, Context}; 8 + use axum::{ 9 + Router, 10 + body::Body, 11 + handler::Handler, 12 + http::{Method, header}, 13 + middleware as ax_middleware, 14 + routing::get, 15 + routing::post, 16 + }; 10 17 use axum_template::engine::Engine; 11 18 use handlebars::Handlebars; 12 - use hyper_util::client::legacy::connect::HttpConnector; 13 - use hyper_util::rt::TokioExecutor; 14 - use lettre::{AsyncSmtpTransport, Tokio1Executor}; 19 + use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor}; 20 + use jacquard_common::types::did::Did; 21 + use jacquard_identity::{PublicResolver, resolver::PlcSource}; 22 + use lettre::{AsyncTransport, AsyncSmtpTransport, AsyncSendmailTransport, Message, Tokio1Executor}; 23 + use rand::Rng; 15 24 use rust_embed::RustEmbed; 16 25 use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode}; 17 26 use sqlx::{SqlitePool, sqlite::SqlitePoolOptions}; 18 27 use std::path::Path; 28 + use std::sync::Arc; 19 29 use std::time::Duration; 20 30 use std::{env, net::SocketAddr}; 21 - use tower_governor::GovernorLayer; 22 - use tower_governor::governor::GovernorConfigBuilder; 23 - use tower_governor::key_extractor::SmartIpKeyExtractor; 24 - use tower_http::compression::CompressionLayer; 25 - use tower_http::cors::{Any, CorsLayer}; 31 + use tower_governor::{ 32 + GovernorLayer, governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor, 33 + }; 34 + use tower_http::{ 35 + compression::CompressionLayer, 36 + cors::{Any, CorsLayer}, 37 + }; 26 38 use tracing::log; 27 39 use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 40 + use url::Url; 28 41 42 + mod gate; 29 43 pub mod helpers; 30 44 mod middleware; 31 45 mod oauth_provider; ··· 38 52 #[include = "*.hbs"] 39 53 struct EmailTemplates; 40 54 55 + #[derive(RustEmbed)] 56 + #[folder = "html_templates"] 57 + #[include = "*.hbs"] 58 + struct HtmlTemplates; 59 + 60 + /// Mostly the env variables that are used in the app 61 + #[derive(Clone, Debug)] 62 + pub struct AppConfig { 63 + pds_base_url: String, 64 + mailer_from: String, 65 + email_subject: String, 66 + allow_only_migrations: bool, 67 + use_captcha: bool, 68 + //The url to redirect to after a successful captcha. Defaults to https://bsky.app, but you may have another social-app fork you rather your users use 69 + //that need to capture this redirect url for creating an account 70 + default_successful_redirect_url: String, 71 + pds_service_did: Did<'static>, 72 + gate_jwe_key: Vec<u8>, 73 + captcha_success_redirects: Vec<String>, 74 + } 75 + 76 + impl AppConfig { 77 + pub fn new() -> Self { 78 + let pds_base_url = 79 + env::var("PDS_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".to_string()); 80 + let mailer_from = env::var("PDS_EMAIL_FROM_ADDRESS") 81 + .expect("PDS_EMAIL_FROM_ADDRESS is not set in your pds.env file"); 82 + //Hack not my favorite, but it does work 83 + let allow_only_migrations = env::var("GATEKEEPER_ALLOW_ONLY_MIGRATIONS") 84 + .map(|val| val.parse::<bool>().unwrap_or(false)) 85 + .unwrap_or(false); 86 + 87 + let use_captcha = env::var("GATEKEEPER_CREATE_ACCOUNT_CAPTCHA") 88 + .map(|val| val.parse::<bool>().unwrap_or(false)) 89 + .unwrap_or(false); 90 + 91 + // PDS_SERVICE_DID is the did:web if set, if not it's PDS_HOSTNAME 92 + let pds_service_did = 93 + env::var("PDS_SERVICE_DID").unwrap_or_else(|_| match env::var("PDS_HOSTNAME") { 94 + Ok(pds_hostname) => format!("did:web:{}", pds_hostname), 95 + Err(_) => { 96 + panic!("PDS_HOSTNAME or PDS_SERVICE_DID must be set in your pds.env file") 97 + } 98 + }); 99 + 100 + let email_subject = env::var("GATEKEEPER_TWO_FACTOR_EMAIL_SUBJECT") 101 + .unwrap_or("Sign in to Bluesky".to_string()); 102 + 103 + // Load or generate JWE encryption key (32 bytes for AES-256) 104 + let gate_jwe_key = env::var("GATEKEEPER_JWE_KEY") 105 + .ok() 106 + .and_then(|key_hex| hex::decode(key_hex).ok()) 107 + .unwrap_or_else(|| { 108 + // Generate a random 32-byte key if not provided 109 + let key: Vec<u8> = (0..32).map(|_| rand::rng().random()).collect(); 110 + log::warn!("WARNING: No GATEKEEPER_JWE_KEY found in the environment. Generated random key (hex): {}", hex::encode(&key)); 111 + log::warn!("This is not strictly needed unless you scale PDS Gatekeeper. Will not also be able to verify tokens between reboots, but they are short lived (5mins)."); 112 + key 113 + }); 114 + 115 + if gate_jwe_key.len() != 32 { 116 + panic!( 117 + "GATEKEEPER_JWE_KEY must be 32 bytes (64 hex characters) for AES-256 encryption" 118 + ); 119 + } 120 + 121 + let captcha_success_redirects = match env::var("GATEKEEPER_CAPTCHA_SUCCESS_REDIRECTS") { 122 + Ok(from_env) => from_env.split(",").map(|s| s.trim().to_string()).collect(), 123 + Err(_) => { 124 + vec![ 125 + String::from("https://bsky.app"), 126 + String::from("https://pdsmoover.com"), 127 + String::from("https://blacksky.community"), 128 + String::from("https://tektite.cc"), 129 + ] 130 + } 131 + }; 132 + 133 + AppConfig { 134 + pds_base_url, 135 + mailer_from, 136 + email_subject, 137 + allow_only_migrations, 138 + use_captcha, 139 + default_successful_redirect_url: env::var("GATEKEEPER_DEFAULT_CAPTCHA_REDIRECT") 140 + .unwrap_or("https://bsky.app".to_string()), 141 + pds_service_did: pds_service_did 142 + .parse() 143 + .expect("PDS_SERVICE_DID is not a valid did or could not infer from PDS_HOSTNAME"), 144 + gate_jwe_key, 145 + captcha_success_redirects, 146 + } 147 + } 148 + } 149 + 41 150 #[derive(Clone)] 42 151 pub struct AppState { 43 152 account_pool: SqlitePool, 44 153 pds_gatekeeper_pool: SqlitePool, 45 154 reverse_proxy_client: HyperUtilClient, 46 - pds_base_url: String, 47 - mailer: AsyncSmtpTransport<Tokio1Executor>, 48 - mailer_from: String, 155 + mailer: Arc<Mailer>, 49 156 template_engine: Engine<Handlebars<'static>>, 157 + resolver: Arc<PublicResolver>, 158 + app_config: AppConfig, 159 + } 160 + 161 + pub enum Mailer { 162 + Smtp(AsyncSmtpTransport<Tokio1Executor>), 163 + Sendmail(AsyncSendmailTransport<Tokio1Executor>), 164 + } 165 + 166 + impl Mailer { 167 + pub async fn send(&self, msg: Message) -> Result<()> { 168 + match self { 169 + Mailer::Smtp(m) => { 170 + m.send(msg).await.context("SMTP send failed")?; 171 + Ok(()) 172 + } 173 + Mailer::Sendmail(m) => { 174 + m.send(msg).await.context("sendmail send failed")?; 175 + Ok(()) 176 + } 177 + } 178 + } 179 + } 180 + 181 + fn build_mailer_from_env() -> Result<Mailer> { 182 + let raw = env::var("PDS_EMAIL_SMTP_URL") 183 + .context("PDS_EMAIL_SMTP_URL is not set in your pds.env file")?; 184 + 185 + let url = Url::parse(&raw).context("PDS_EMAIL_SMTP_URL is not a valid URL")?; 186 + 187 + let use_sendmail = url.scheme() == "sendmail" 188 + || url.query_pairs().any(|(k, v)| k == "sendmail" && v == "true"); 189 + 190 + if use_sendmail { 191 + Ok(Mailer::Sendmail(AsyncSendmailTransport::<Tokio1Executor>::new())) 192 + } else { 193 + Ok(Mailer::Smtp( 194 + AsyncSmtpTransport::<Tokio1Executor>::from_url(raw.as_str())? 195 + .build(), 196 + )) 197 + } 50 198 } 51 199 52 200 async fn root_handler() -> impl axum::response::IntoResponse { ··· 135 283 .build(HttpConnector::new()); 136 284 137 285 //Emailer set up 138 - let smtp_url = 139 - env::var("PDS_EMAIL_SMTP_URL").expect("PDS_EMAIL_SMTP_URL is not set in your pds.env file"); 140 - let sent_from = env::var("PDS_EMAIL_FROM_ADDRESS") 141 - .expect("PDS_EMAIL_FROM_ADDRESS is not set in your pds.env file"); 286 + let mailer = Arc::new(build_mailer_from_env()?); 142 287 143 - let mailer: AsyncSmtpTransport<Tokio1Executor> = 144 - AsyncSmtpTransport::<Tokio1Executor>::from_url(smtp_url.as_str())?.build(); 145 288 //Email templates setup 146 289 let mut hbs = Handlebars::new(); 147 290 ··· 155 298 let _ = hbs.register_embed_templates::<EmailTemplates>(); 156 299 } 157 300 158 - let pds_base_url = 159 - env::var("PDS_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".to_string()); 301 + let _ = hbs.register_embed_templates::<HtmlTemplates>(); 302 + 303 + //Reads the PLC source from the pds env's or defaults to ol faithful 304 + let plc_source_url = 305 + env::var("PDS_DID_PLC_URL").unwrap_or_else(|_| "https://plc.directory".to_string()); 306 + let plc_source = PlcSource::PlcDirectory { 307 + base: plc_source_url.parse().unwrap(), 308 + }; 309 + let mut resolver = PublicResolver::default(); 310 + resolver = resolver.with_plc_source(plc_source.clone()); 160 311 161 312 let state = AppState { 162 313 account_pool, 163 314 pds_gatekeeper_pool, 164 315 reverse_proxy_client: client, 165 - pds_base_url, 166 316 mailer, 167 - mailer_from: sent_from, 168 317 template_engine: Engine::from(hbs), 318 + resolver: Arc::new(resolver), 319 + app_config: AppConfig::new(), 169 320 }; 170 321 171 322 // Rate limiting 172 323 //Allows 5 within 60 seconds, and after 60 should drop one off? So hit 5, then goes to 4 after 60 seconds. 173 - let create_session_governor_conf = GovernorConfigBuilder::default() 324 + let captcha_governor_conf = GovernorConfigBuilder::default() 174 325 .per_second(60) 175 326 .burst_size(5) 176 327 .key_extractor(SmartIpKeyExtractor) ··· 216 367 "failed to create governor config for create account. this should not happen and is a bug", 217 368 ); 218 369 219 - let create_session_governor_limiter = create_session_governor_conf.limiter().clone(); 370 + let captcha_governor_limiter = captcha_governor_conf.limiter().clone(); 220 371 let sign_in_governor_limiter = sign_in_governor_conf.limiter().clone(); 221 372 let create_account_governor_limiter = create_account_governor_conf.limiter().clone(); 373 + 374 + let sign_in_governor_layer = GovernorLayer::new(sign_in_governor_conf); 222 375 223 376 let interval = Duration::from_secs(60); 224 377 // a separate background task to clean up 225 378 std::thread::spawn(move || { 226 379 loop { 227 380 std::thread::sleep(interval); 228 - create_session_governor_limiter.retain_recent(); 381 + captcha_governor_limiter.retain_recent(); 229 382 sign_in_governor_limiter.retain_recent(); 230 383 create_account_governor_limiter.retain_recent(); 231 384 } ··· 236 389 .allow_methods([Method::GET, Method::OPTIONS, Method::POST]) 237 390 .allow_headers(Any); 238 391 239 - let app = Router::new() 392 + let mut app = Router::new() 240 393 .route("/", get(root_handler)) 241 394 .route("/xrpc/com.atproto.server.getSession", get(get_session)) 242 395 .route( 396 + "/xrpc/com.atproto.server.describeServer", 397 + get(describe_server), 398 + ) 399 + .route( 243 400 "/xrpc/com.atproto.server.updateEmail", 244 401 post(update_email).layer(ax_middleware::from_fn(middleware::extract_did)), 245 402 ) 246 403 .route( 247 404 "/@atproto/oauth-provider/~api/sign-in", 248 - post(sign_in).layer(GovernorLayer::new(sign_in_governor_conf)), 405 + post(sign_in).layer(sign_in_governor_layer.clone()), 249 406 ) 250 407 .route( 251 408 "/xrpc/com.atproto.server.createSession", 252 - post(create_session.layer(GovernorLayer::new(create_session_governor_conf))), 409 + post(create_session.layer(sign_in_governor_layer)), 253 410 ) 254 411 .route( 255 412 "/xrpc/com.atproto.server.createAccount", 256 413 post(create_account).layer(GovernorLayer::new(create_account_governor_conf)), 257 - ) 414 + ); 415 + 416 + if state.app_config.use_captcha { 417 + app = app.route( 418 + "/gate/signup", 419 + get(get_gate).post(post_gate.layer(GovernorLayer::new(captcha_governor_conf))), 420 + ); 421 + } 422 + 423 + let app = app 258 424 .layer(CompressionLayer::new()) 259 425 .layer(cors) 260 426 .with_state(state);
+3 -2
src/oauth_provider.rs
··· 13 13 pub struct SignInRequest { 14 14 pub username: String, 15 15 pub password: String, 16 - pub remember: bool, 16 + #[serde(skip_serializing_if = "Option::is_none")] 17 + pub remember: Option<bool>, 17 18 pub locale: String, 18 19 #[serde(skip_serializing_if = "Option::is_none", rename = "emailOtp")] 19 20 pub email_otp: Option<String>, ··· 56 57 //No 2FA or already passed 57 58 let uri = format!( 58 59 "{}{}", 59 - state.pds_base_url, "/@atproto/oauth-provider/~api/sign-in" 60 + state.app_config.pds_base_url, "/@atproto/oauth-provider/~api/sign-in" 60 61 ); 61 62 62 63 let mut req = axum::http::Request::post(uri);
+306 -11
src/xrpc/com_atproto_server.rs
··· 1 1 use crate::AppState; 2 2 use crate::helpers::{ 3 - AuthResult, ProxiedResult, TokenCheckError, json_error_response, preauth_check, proxy_get_json, 3 + AuthResult, ProxiedResult, TokenCheckError, VerifyServiceAuthError, json_error_response, 4 + preauth_check, proxy_get_json, verify_gate_token, verify_service_auth, 4 5 }; 5 6 use crate::middleware::Did; 6 - use axum::body::Body; 7 + use axum::body::{Body, to_bytes}; 7 8 use axum::extract::State; 8 - use axum::http::{HeaderMap, StatusCode}; 9 + use axum::http::{HeaderMap, StatusCode, header}; 9 10 use axum::response::{IntoResponse, Response}; 10 11 use axum::{Extension, Json, debug_handler, extract, extract::Request}; 12 + use chrono::{Duration, Utc}; 13 + use jacquard_common::types::did::Did as JacquardDid; 11 14 use serde::{Deserialize, Serialize}; 12 15 use serde_json; 13 16 use tracing::log; ··· 61 64 allow_takendown: Option<bool>, 62 65 } 63 66 67 + #[derive(Deserialize, Serialize, Debug)] 68 + #[serde(rename_all = "camelCase")] 69 + pub struct CreateAccountRequest { 70 + handle: String, 71 + #[serde(skip_serializing_if = "Option::is_none")] 72 + email: Option<String>, 73 + #[serde(skip_serializing_if = "Option::is_none")] 74 + password: Option<String>, 75 + #[serde(skip_serializing_if = "Option::is_none")] 76 + did: Option<String>, 77 + #[serde(skip_serializing_if = "Option::is_none")] 78 + invite_code: Option<String>, 79 + #[serde(skip_serializing_if = "Option::is_none")] 80 + verification_code: Option<String>, 81 + #[serde(skip_serializing_if = "Option::is_none")] 82 + plc_op: Option<serde_json::Value>, 83 + } 84 + 85 + #[derive(Deserialize, Serialize, Debug, Clone)] 86 + #[serde(rename_all = "camelCase")] 87 + pub struct DescribeServerContact { 88 + #[serde(skip_serializing_if = "Option::is_none")] 89 + email: Option<String>, 90 + } 91 + 92 + #[derive(Deserialize, Serialize, Debug, Clone)] 93 + #[serde(rename_all = "camelCase")] 94 + pub struct DescribeServerLinks { 95 + #[serde(skip_serializing_if = "Option::is_none")] 96 + privacy_policy: Option<String>, 97 + #[serde(skip_serializing_if = "Option::is_none")] 98 + terms_of_service: Option<String>, 99 + } 100 + 101 + #[derive(Deserialize, Serialize, Debug, Clone)] 102 + #[serde(rename_all = "camelCase")] 103 + pub struct DescribeServerResponse { 104 + #[serde(skip_serializing_if = "Option::is_none")] 105 + invite_code_required: Option<bool>, 106 + #[serde(skip_serializing_if = "Option::is_none")] 107 + phone_verification_required: Option<bool>, 108 + #[serde(skip_serializing_if = "Option::is_none")] 109 + available_user_domains: Option<Vec<String>>, 110 + #[serde(skip_serializing_if = "Option::is_none")] 111 + links: Option<DescribeServerLinks>, 112 + #[serde(skip_serializing_if = "Option::is_none")] 113 + contact: Option<DescribeServerContact>, 114 + #[serde(skip_serializing_if = "Option::is_none")] 115 + did: Option<String>, 116 + } 117 + 64 118 pub async fn create_session( 65 119 State(state): State<AppState>, 66 120 headers: HeaderMap, ··· 90 144 //No 2FA or already passed 91 145 let uri = format!( 92 146 "{}{}", 93 - state.pds_base_url, "/xrpc/com.atproto.server.createSession" 147 + state.app_config.pds_base_url, "/xrpc/com.atproto.server.createSession" 94 148 ); 95 149 96 150 let mut req = axum::http::Request::post(uri); ··· 230 284 // Updating the actual email address by sending it on to the PDS 231 285 let uri = format!( 232 286 "{}{}", 233 - state.pds_base_url, "/xrpc/com.atproto.server.updateEmail" 287 + state.app_config.pds_base_url, "/xrpc/com.atproto.server.updateEmail" 234 288 ); 235 289 let mut req = axum::http::Request::post(uri); 236 290 if let Some(req_headers) = req.headers_mut() { ··· 283 337 } 284 338 } 285 339 340 + pub async fn describe_server( 341 + State(state): State<AppState>, 342 + req: Request, 343 + ) -> Result<Response<Body>, StatusCode> { 344 + match proxy_get_json::<DescribeServerResponse>( 345 + &state, 346 + req, 347 + "/xrpc/com.atproto.server.describeServer", 348 + ) 349 + .await? 350 + { 351 + ProxiedResult::Parsed { 352 + value: mut server_info, 353 + .. 354 + } => { 355 + //This signifies the server is configured for captcha verification 356 + server_info.phone_verification_required = Some(state.app_config.use_captcha); 357 + Ok(Json(server_info).into_response()) 358 + } 359 + ProxiedResult::Passthrough(resp) => Ok(resp), 360 + } 361 + } 362 + 363 + /// Verify a gate code matches the handle and is not expired 364 + async fn verify_gate_code( 365 + state: &AppState, 366 + code: &str, 367 + handle: &str, 368 + ) -> Result<bool, anyhow::Error> { 369 + // First, decrypt and verify the JWE token 370 + let payload = match verify_gate_token(code, &state.app_config.gate_jwe_key) { 371 + Ok(p) => p, 372 + Err(e) => { 373 + log::warn!("Failed to decrypt gate token: {}", e); 374 + return Ok(false); 375 + } 376 + }; 377 + 378 + // Verify the handle matches 379 + if payload.handle != handle { 380 + log::warn!( 381 + "Gate code handle mismatch: expected {}, got {}", 382 + handle, 383 + payload.handle 384 + ); 385 + return Ok(false); 386 + } 387 + 388 + let created_at = chrono::DateTime::parse_from_rfc3339(&payload.created_at) 389 + .map_err(|e| anyhow::anyhow!("Failed to parse created_at from token: {}", e))? 390 + .with_timezone(&Utc); 391 + 392 + let now = Utc::now(); 393 + let age = now - created_at; 394 + 395 + // Check if the token is expired (5 minutes) 396 + if age > Duration::minutes(5) { 397 + log::warn!("Gate code expired for handle {}", handle); 398 + return Ok(false); 399 + } 400 + 401 + // Verify the token exists in the database (to prevent reuse) 402 + let row: Option<(String,)> = 403 + sqlx::query_as("SELECT code FROM gate_codes WHERE code = ? and handle = ? LIMIT 1") 404 + .bind(code) 405 + .bind(handle) 406 + .fetch_optional(&state.pds_gatekeeper_pool) 407 + .await?; 408 + 409 + if row.is_none() { 410 + log::warn!("Gate code not found in database or already used"); 411 + return Ok(false); 412 + } 413 + 414 + // Token is valid, delete it so it can't be reused 415 + //TODO probably also delete expired codes? Will need to do that at some point probably altho the where is on code and handle 416 + 417 + sqlx::query("DELETE FROM gate_codes WHERE code = ?") 418 + .bind(code) 419 + .execute(&state.pds_gatekeeper_pool) 420 + .await?; 421 + 422 + Ok(true) 423 + } 424 + 286 425 pub async fn create_account( 287 426 State(state): State<AppState>, 288 - mut req: Request, 427 + req: Request, 289 428 ) -> Result<Response<Body>, StatusCode> { 290 - //TODO if I add the block of only accounts authenticated just take the body as json here and grab the lxm token. No middle ware is needed 429 + let headers = req.headers().clone(); 430 + let body_bytes = to_bytes(req.into_body(), usize::MAX) 431 + .await 432 + .map_err(|_| StatusCode::BAD_REQUEST)?; 433 + 434 + // Parse the body to check for verification code 435 + let account_request: CreateAccountRequest = 436 + serde_json::from_slice(&body_bytes).map_err(|e| { 437 + log::error!("Failed to parse create account request: {}", e); 438 + StatusCode::BAD_REQUEST 439 + })?; 440 + 441 + // Check for service auth (migrations) if configured 442 + if state.app_config.allow_only_migrations { 443 + // Expect Authorization: Bearer <jwt> 444 + let auth_header = headers 445 + .get(header::AUTHORIZATION) 446 + .and_then(|v| v.to_str().ok()) 447 + .map(str::to_string); 448 + 449 + let Some(value) = auth_header else { 450 + log::error!("No Authorization header found in the request"); 451 + return json_error_response( 452 + StatusCode::UNAUTHORIZED, 453 + "InvalidAuth", 454 + "This PDS is configured to only allow accounts created by migrations via this endpoint.", 455 + ); 456 + }; 457 + 458 + // Ensure Bearer prefix 459 + let token = value.strip_prefix("Bearer ").unwrap_or("").trim(); 460 + if token.is_empty() { 461 + log::error!("No Service Auth token found in the Authorization header"); 462 + return json_error_response( 463 + StatusCode::UNAUTHORIZED, 464 + "InvalidAuth", 465 + "This PDS is configured to only allow accounts created by migrations via this endpoint.", 466 + ); 467 + } 468 + 469 + // Ensure a non-empty DID was provided when migrations are enabled 470 + let requested_did_str = match account_request.did.as_deref() { 471 + Some(s) if !s.trim().is_empty() => s, 472 + _ => { 473 + return json_error_response( 474 + StatusCode::BAD_REQUEST, 475 + "InvalidRequest", 476 + "The 'did' field is required when migrations are enforced.", 477 + ); 478 + } 479 + }; 480 + 481 + // Parse the DID into the expected type for verification 482 + let requested_did: JacquardDid<'static> = match requested_did_str.parse() { 483 + Ok(d) => d, 484 + Err(e) => { 485 + log::error!( 486 + "Invalid DID format provided in createAccount: {} | error: {}", 487 + requested_did_str, 488 + e 489 + ); 490 + return json_error_response( 491 + StatusCode::BAD_REQUEST, 492 + "InvalidRequest", 493 + "The 'did' field is not a valid DID.", 494 + ); 495 + } 496 + }; 291 497 498 + let nsid = "com.atproto.server.createAccount".parse().unwrap(); 499 + match verify_service_auth( 500 + token, 501 + &nsid, 502 + state.resolver.clone(), 503 + &state.app_config.pds_service_did, 504 + &requested_did, 505 + ) 506 + .await 507 + { 508 + //Just do nothing if it passes so it continues. 509 + Ok(_) => {} 510 + Err(err) => match err { 511 + VerifyServiceAuthError::AuthFailed => { 512 + return json_error_response( 513 + StatusCode::UNAUTHORIZED, 514 + "InvalidAuth", 515 + "This PDS is configured to only allow accounts created by migrations via this endpoint.", 516 + ); 517 + } 518 + VerifyServiceAuthError::Error(err) => { 519 + log::error!("Error verifying service auth token: {err}"); 520 + return json_error_response( 521 + StatusCode::BAD_REQUEST, 522 + "InvalidRequest", 523 + "There has been an error, please contact your PDS administrator for help and for them to review the server logs.", 524 + ); 525 + } 526 + }, 527 + } 528 + } 529 + 530 + // Check for captcha verification if configured 531 + if state.app_config.use_captcha { 532 + if let Some(ref verification_code) = account_request.verification_code { 533 + match verify_gate_code(&state, verification_code, &account_request.handle).await { 534 + //TODO has a few errors to support 535 + 536 + //expired token 537 + // { 538 + // "error": "ExpiredToken", 539 + // "message": "Token has expired" 540 + // } 541 + 542 + //TODO ALSO add rate limits on the /gate endpoints so they can't be abused 543 + Ok(true) => { 544 + log::info!("Gate code verified for handle: {}", account_request.handle); 545 + } 546 + Ok(false) => { 547 + log::warn!( 548 + "Invalid or expired gate code for handle: {}", 549 + account_request.handle 550 + ); 551 + return json_error_response( 552 + StatusCode::BAD_REQUEST, 553 + "InvalidToken", 554 + "Token could not be verified", 555 + ); 556 + } 557 + Err(e) => { 558 + log::error!("Error verifying gate code: {}", e); 559 + return json_error_response( 560 + StatusCode::INTERNAL_SERVER_ERROR, 561 + "InvalidToken", 562 + "Token could not be verified", 563 + ); 564 + } 565 + } 566 + } else { 567 + // No verification code provided but captcha is required 568 + log::warn!( 569 + "No verification code provided for account creation: {}", 570 + account_request.handle 571 + ); 572 + return json_error_response( 573 + StatusCode::BAD_REQUEST, 574 + "InvalidRequest", 575 + "Verification is now required on this server.", 576 + ); 577 + } 578 + } 579 + 580 + // Rebuild the request with the same body and headers 292 581 let uri = format!( 293 582 "{}{}", 294 - state.pds_base_url, "/xrpc/com.atproto.server.createAccount" 583 + state.app_config.pds_base_url, "/xrpc/com.atproto.server.createAccount" 295 584 ); 296 585 297 - // Rewrite the URI to point at the upstream PDS; keep headers, method, and body intact 298 - *req.uri_mut() = uri.parse().map_err(|_| StatusCode::BAD_REQUEST)?; 586 + let mut new_req = axum::http::Request::post(&uri); 587 + if let Some(req_headers) = new_req.headers_mut() { 588 + *req_headers = headers; 589 + } 590 + 591 + let new_req = new_req 592 + .body(Body::from(body_bytes)) 593 + .map_err(|_| StatusCode::BAD_REQUEST)?; 299 594 300 595 let proxied = state 301 596 .reverse_proxy_client 302 - .request(req) 597 + .request(new_req) 303 598 .await 304 599 .map_err(|_| StatusCode::BAD_REQUEST)? 305 600 .into_response();