+1398
-84
Cargo.lock
+1398
-84
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]]
61
+
name = "aliasable"
62
+
version = "0.1.3"
63
+
source = "registry+https://github.com/rust-lang/crates.io-index"
64
+
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
65
+
66
+
[[package]]
42
67
name = "allocator-api2"
43
68
version = "0.2.21"
44
69
source = "registry+https://github.com/rust-lang/crates.io-index"
45
70
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
46
71
47
72
[[package]]
48
-
name = "android-tzdata"
49
-
version = "0.1.1"
50
-
source = "registry+https://github.com/rust-lang/crates.io-index"
51
-
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
52
-
53
-
[[package]]
54
73
name = "android_system_properties"
55
74
version = "0.1.5"
56
75
source = "registry+https://github.com/rust-lang/crates.io-index"
···
71
90
source = "registry+https://github.com/rust-lang/crates.io-index"
72
91
checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8"
73
92
dependencies = [
93
+
"flate2",
74
94
"futures-core",
75
95
"memchr",
76
96
"pin-project-lite",
···
87
107
dependencies = [
88
108
"proc-macro2",
89
109
"quote",
90
-
"syn",
110
+
"syn 2.0.105",
91
111
]
92
112
93
113
[[package]]
···
198
218
dependencies = [
199
219
"proc-macro2",
200
220
"quote",
201
-
"syn",
221
+
"syn 2.0.105",
202
222
]
203
223
204
224
[[package]]
···
229
249
]
230
250
231
251
[[package]]
252
+
name = "base-x"
253
+
version = "0.2.11"
254
+
source = "registry+https://github.com/rust-lang/crates.io-index"
255
+
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
256
+
257
+
[[package]]
258
+
name = "base16ct"
259
+
version = "0.2.0"
260
+
source = "registry+https://github.com/rust-lang/crates.io-index"
261
+
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
262
+
263
+
[[package]]
264
+
name = "base256emoji"
265
+
version = "1.0.2"
266
+
source = "registry+https://github.com/rust-lang/crates.io-index"
267
+
checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c"
268
+
dependencies = [
269
+
"const-str",
270
+
"match-lookup",
271
+
]
272
+
273
+
[[package]]
232
274
name = "base64"
233
275
version = "0.22.1"
234
276
source = "registry+https://github.com/rust-lang/crates.io-index"
···
257
299
"proc-macro2",
258
300
"quote",
259
301
"regex",
260
-
"rustc-hash",
302
+
"rustc-hash 1.1.0",
261
303
"shlex",
262
-
"syn",
304
+
"syn 2.0.105",
263
305
"which",
264
306
]
265
307
···
282
324
]
283
325
284
326
[[package]]
327
+
name = "bon"
328
+
version = "3.8.1"
329
+
source = "registry+https://github.com/rust-lang/crates.io-index"
330
+
checksum = "ebeb9aaf9329dff6ceb65c689ca3db33dbf15f324909c60e4e5eef5701ce31b1"
331
+
dependencies = [
332
+
"bon-macros",
333
+
"rustversion",
334
+
]
335
+
336
+
[[package]]
337
+
name = "bon-macros"
338
+
version = "3.8.1"
339
+
source = "registry+https://github.com/rust-lang/crates.io-index"
340
+
checksum = "77e9d642a7e3a318e37c2c9427b5a6a48aa1ad55dcd986f3034ab2239045a645"
341
+
dependencies = [
342
+
"darling 0.21.3",
343
+
"ident_case",
344
+
"prettyplease",
345
+
"proc-macro2",
346
+
"quote",
347
+
"rustversion",
348
+
"syn 2.0.105",
349
+
]
350
+
351
+
[[package]]
352
+
name = "borsh"
353
+
version = "1.6.0"
354
+
source = "registry+https://github.com/rust-lang/crates.io-index"
355
+
checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f"
356
+
dependencies = [
357
+
"cfg_aliases",
358
+
]
359
+
360
+
[[package]]
285
361
name = "bstr"
286
362
version = "1.12.0"
287
363
source = "registry+https://github.com/rust-lang/crates.io-index"
···
292
368
]
293
369
294
370
[[package]]
371
+
name = "btree-range-map"
372
+
version = "0.7.2"
373
+
source = "registry+https://github.com/rust-lang/crates.io-index"
374
+
checksum = "1be5c9672446d3800bcbcaabaeba121fe22f1fb25700c4562b22faf76d377c33"
375
+
dependencies = [
376
+
"btree-slab",
377
+
"cc-traits",
378
+
"range-traits",
379
+
"serde",
380
+
"slab",
381
+
]
382
+
383
+
[[package]]
384
+
name = "btree-slab"
385
+
version = "0.6.1"
386
+
source = "registry+https://github.com/rust-lang/crates.io-index"
387
+
checksum = "7a2b56d3029f075c4fa892428a098425b86cef5c89ae54073137ece416aef13c"
388
+
dependencies = [
389
+
"cc-traits",
390
+
"slab",
391
+
"smallvec",
392
+
]
393
+
394
+
[[package]]
295
395
name = "bumpalo"
296
396
version = "3.19.0"
297
397
source = "registry+https://github.com/rust-lang/crates.io-index"
···
308
408
version = "1.10.1"
309
409
source = "registry+https://github.com/rust-lang/crates.io-index"
310
410
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
411
+
dependencies = [
412
+
"serde",
413
+
]
414
+
415
+
[[package]]
416
+
name = "cbor4ii"
417
+
version = "0.2.14"
418
+
source = "registry+https://github.com/rust-lang/crates.io-index"
419
+
checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4"
420
+
dependencies = [
421
+
"serde",
422
+
]
311
423
312
424
[[package]]
313
425
name = "cc"
···
321
433
]
322
434
323
435
[[package]]
436
+
name = "cc-traits"
437
+
version = "2.0.0"
438
+
source = "registry+https://github.com/rust-lang/crates.io-index"
439
+
checksum = "060303ef31ef4a522737e1b1ab68c67916f2a787bb2f4f54f383279adba962b5"
440
+
dependencies = [
441
+
"slab",
442
+
]
443
+
444
+
[[package]]
324
445
name = "cexpr"
325
446
version = "0.6.0"
326
447
source = "registry+https://github.com/rust-lang/crates.io-index"
···
336
457
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
337
458
338
459
[[package]]
460
+
name = "cfg_aliases"
461
+
version = "0.2.1"
462
+
source = "registry+https://github.com/rust-lang/crates.io-index"
463
+
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
464
+
465
+
[[package]]
339
466
name = "chrono"
340
-
version = "0.4.41"
467
+
version = "0.4.42"
341
468
source = "registry+https://github.com/rust-lang/crates.io-index"
342
-
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
469
+
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
343
470
dependencies = [
344
-
"android-tzdata",
345
471
"iana-time-zone",
346
472
"js-sys",
347
473
"num-traits",
474
+
"serde",
348
475
"wasm-bindgen",
349
-
"windows-link",
476
+
"windows-link 0.2.1",
350
477
]
351
478
352
479
[[package]]
···
387
514
]
388
515
389
516
[[package]]
517
+
name = "cid"
518
+
version = "0.11.1"
519
+
source = "registry+https://github.com/rust-lang/crates.io-index"
520
+
checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a"
521
+
dependencies = [
522
+
"core2",
523
+
"multibase",
524
+
"multihash",
525
+
"serde",
526
+
"serde_bytes",
527
+
"unsigned-varint",
528
+
]
529
+
530
+
[[package]]
390
531
name = "cipher"
391
532
version = "0.4.4"
392
533
source = "registry+https://github.com/rust-lang/crates.io-index"
···
432
573
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
433
574
434
575
[[package]]
576
+
name = "const-str"
577
+
version = "0.4.3"
578
+
source = "registry+https://github.com/rust-lang/crates.io-index"
579
+
checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3"
580
+
581
+
[[package]]
582
+
name = "core-foundation"
583
+
version = "0.9.4"
584
+
source = "registry+https://github.com/rust-lang/crates.io-index"
585
+
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
586
+
dependencies = [
587
+
"core-foundation-sys",
588
+
"libc",
589
+
]
590
+
591
+
[[package]]
435
592
name = "core-foundation-sys"
436
593
version = "0.8.7"
437
594
source = "registry+https://github.com/rust-lang/crates.io-index"
438
595
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
439
596
440
597
[[package]]
598
+
name = "core2"
599
+
version = "0.4.0"
600
+
source = "registry+https://github.com/rust-lang/crates.io-index"
601
+
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
602
+
dependencies = [
603
+
"memchr",
604
+
]
605
+
606
+
[[package]]
441
607
name = "cpufeatures"
442
608
version = "0.2.17"
443
609
source = "registry+https://github.com/rust-lang/crates.io-index"
···
462
628
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
463
629
464
630
[[package]]
631
+
name = "crc32fast"
632
+
version = "1.5.0"
633
+
source = "registry+https://github.com/rust-lang/crates.io-index"
634
+
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
635
+
dependencies = [
636
+
"cfg-if",
637
+
]
638
+
639
+
[[package]]
465
640
name = "crossbeam-queue"
466
641
version = "0.3.12"
467
642
source = "registry+https://github.com/rust-lang/crates.io-index"
···
483
658
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
484
659
485
660
[[package]]
661
+
name = "crypto-bigint"
662
+
version = "0.5.5"
663
+
source = "registry+https://github.com/rust-lang/crates.io-index"
664
+
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
665
+
dependencies = [
666
+
"generic-array",
667
+
"rand_core 0.6.4",
668
+
"subtle",
669
+
"zeroize",
670
+
]
671
+
672
+
[[package]]
486
673
name = "crypto-common"
487
674
version = "0.1.6"
488
675
source = "registry+https://github.com/rust-lang/crates.io-index"
···
498
685
source = "registry+https://github.com/rust-lang/crates.io-index"
499
686
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
500
687
dependencies = [
501
-
"darling_core",
502
-
"darling_macro",
688
+
"darling_core 0.20.11",
689
+
"darling_macro 0.20.11",
690
+
]
691
+
692
+
[[package]]
693
+
name = "darling"
694
+
version = "0.21.3"
695
+
source = "registry+https://github.com/rust-lang/crates.io-index"
696
+
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
697
+
dependencies = [
698
+
"darling_core 0.21.3",
699
+
"darling_macro 0.21.3",
503
700
]
504
701
505
702
[[package]]
···
513
710
"proc-macro2",
514
711
"quote",
515
712
"strsim",
516
-
"syn",
713
+
"syn 2.0.105",
714
+
]
715
+
716
+
[[package]]
717
+
name = "darling_core"
718
+
version = "0.21.3"
719
+
source = "registry+https://github.com/rust-lang/crates.io-index"
720
+
checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
721
+
dependencies = [
722
+
"fnv",
723
+
"ident_case",
724
+
"proc-macro2",
725
+
"quote",
726
+
"strsim",
727
+
"syn 2.0.105",
517
728
]
518
729
519
730
[[package]]
···
522
733
source = "registry+https://github.com/rust-lang/crates.io-index"
523
734
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
524
735
dependencies = [
525
-
"darling_core",
736
+
"darling_core 0.20.11",
526
737
"quote",
527
-
"syn",
738
+
"syn 2.0.105",
739
+
]
740
+
741
+
[[package]]
742
+
name = "darling_macro"
743
+
version = "0.21.3"
744
+
source = "registry+https://github.com/rust-lang/crates.io-index"
745
+
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
746
+
dependencies = [
747
+
"darling_core 0.21.3",
748
+
"quote",
749
+
"syn 2.0.105",
528
750
]
529
751
530
752
[[package]]
···
542
764
]
543
765
544
766
[[package]]
767
+
name = "data-encoding"
768
+
version = "2.9.0"
769
+
source = "registry+https://github.com/rust-lang/crates.io-index"
770
+
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
771
+
772
+
[[package]]
773
+
name = "data-encoding-macro"
774
+
version = "0.1.18"
775
+
source = "registry+https://github.com/rust-lang/crates.io-index"
776
+
checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d"
777
+
dependencies = [
778
+
"data-encoding",
779
+
"data-encoding-macro-internal",
780
+
]
781
+
782
+
[[package]]
783
+
name = "data-encoding-macro-internal"
784
+
version = "0.1.16"
785
+
source = "registry+https://github.com/rust-lang/crates.io-index"
786
+
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
787
+
dependencies = [
788
+
"data-encoding",
789
+
"syn 2.0.105",
790
+
]
791
+
792
+
[[package]]
545
793
name = "der"
546
794
version = "0.7.10"
547
795
source = "registry+https://github.com/rust-lang/crates.io-index"
···
553
801
]
554
802
555
803
[[package]]
804
+
name = "deranged"
805
+
version = "0.5.5"
806
+
source = "registry+https://github.com/rust-lang/crates.io-index"
807
+
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
808
+
dependencies = [
809
+
"powerfmt",
810
+
"serde_core",
811
+
]
812
+
813
+
[[package]]
556
814
name = "derive_builder"
557
815
version = "0.20.2"
558
816
source = "registry+https://github.com/rust-lang/crates.io-index"
···
567
825
source = "registry+https://github.com/rust-lang/crates.io-index"
568
826
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
569
827
dependencies = [
570
-
"darling",
828
+
"darling 0.20.11",
571
829
"proc-macro2",
572
830
"quote",
573
-
"syn",
831
+
"syn 2.0.105",
574
832
]
575
833
576
834
[[package]]
···
580
838
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
581
839
dependencies = [
582
840
"derive_builder_core",
583
-
"syn",
841
+
"syn 2.0.105",
584
842
]
585
843
586
844
[[package]]
···
603
861
dependencies = [
604
862
"proc-macro2",
605
863
"quote",
606
-
"syn",
864
+
"syn 2.0.105",
607
865
]
608
866
609
867
[[package]]
···
619
877
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
620
878
621
879
[[package]]
880
+
name = "dyn-clone"
881
+
version = "1.0.20"
882
+
source = "registry+https://github.com/rust-lang/crates.io-index"
883
+
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
884
+
885
+
[[package]]
886
+
name = "ecdsa"
887
+
version = "0.16.9"
888
+
source = "registry+https://github.com/rust-lang/crates.io-index"
889
+
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
890
+
dependencies = [
891
+
"der",
892
+
"digest",
893
+
"elliptic-curve",
894
+
"rfc6979",
895
+
"signature",
896
+
"spki",
897
+
]
898
+
899
+
[[package]]
622
900
name = "either"
623
901
version = "1.15.0"
624
902
source = "registry+https://github.com/rust-lang/crates.io-index"
···
628
906
]
629
907
630
908
[[package]]
909
+
name = "elliptic-curve"
910
+
version = "0.13.8"
911
+
source = "registry+https://github.com/rust-lang/crates.io-index"
912
+
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
913
+
dependencies = [
914
+
"base16ct",
915
+
"crypto-bigint",
916
+
"digest",
917
+
"ff",
918
+
"generic-array",
919
+
"group",
920
+
"pem-rfc7468",
921
+
"pkcs8",
922
+
"rand_core 0.6.4",
923
+
"sec1",
924
+
"subtle",
925
+
"zeroize",
926
+
]
927
+
928
+
[[package]]
631
929
name = "email-encoding"
632
930
version = "0.4.1"
633
931
source = "registry+https://github.com/rust-lang/crates.io-index"
···
644
942
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
645
943
646
944
[[package]]
945
+
name = "encoding_rs"
946
+
version = "0.8.35"
947
+
source = "registry+https://github.com/rust-lang/crates.io-index"
948
+
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
949
+
dependencies = [
950
+
"cfg-if",
951
+
]
952
+
953
+
[[package]]
647
954
name = "equivalent"
648
955
version = "1.0.2"
649
956
source = "registry+https://github.com/rust-lang/crates.io-index"
···
688
995
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
689
996
690
997
[[package]]
998
+
name = "ff"
999
+
version = "0.13.1"
1000
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1001
+
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
1002
+
dependencies = [
1003
+
"rand_core 0.6.4",
1004
+
"subtle",
1005
+
]
1006
+
1007
+
[[package]]
1008
+
name = "flate2"
1009
+
version = "1.1.5"
1010
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1011
+
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
1012
+
dependencies = [
1013
+
"crc32fast",
1014
+
"miniz_oxide",
1015
+
]
1016
+
1017
+
[[package]]
691
1018
name = "flume"
692
1019
version = "0.11.1"
693
1020
source = "registry+https://github.com/rust-lang/crates.io-index"
···
709
1036
version = "0.1.5"
710
1037
source = "registry+https://github.com/rust-lang/crates.io-index"
711
1038
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
1039
+
1040
+
[[package]]
1041
+
name = "foreign-types"
1042
+
version = "0.3.2"
1043
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1044
+
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
1045
+
dependencies = [
1046
+
"foreign-types-shared",
1047
+
]
1048
+
1049
+
[[package]]
1050
+
name = "foreign-types-shared"
1051
+
version = "0.1.1"
1052
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1053
+
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
712
1054
713
1055
[[package]]
714
1056
name = "form_urlencoded"
···
780
1122
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
781
1123
782
1124
[[package]]
1125
+
name = "futures-macro"
1126
+
version = "0.3.31"
1127
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1128
+
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
1129
+
dependencies = [
1130
+
"proc-macro2",
1131
+
"quote",
1132
+
"syn 2.0.105",
1133
+
]
1134
+
1135
+
[[package]]
783
1136
name = "futures-sink"
784
1137
version = "0.3.31"
785
1138
source = "registry+https://github.com/rust-lang/crates.io-index"
···
805
1158
dependencies = [
806
1159
"futures-core",
807
1160
"futures-io",
1161
+
"futures-macro",
808
1162
"futures-sink",
809
1163
"futures-task",
810
1164
"memchr",
···
821
1175
dependencies = [
822
1176
"typenum",
823
1177
"version_check",
1178
+
"zeroize",
824
1179
]
825
1180
826
1181
[[package]]
···
830
1185
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
831
1186
dependencies = [
832
1187
"cfg-if",
1188
+
"js-sys",
833
1189
"libc",
834
-
"wasi 0.11.1+wasi-snapshot-preview1",
1190
+
"wasi",
1191
+
"wasm-bindgen",
835
1192
]
836
1193
837
1194
[[package]]
838
1195
name = "getrandom"
839
-
version = "0.3.3"
1196
+
version = "0.3.4"
840
1197
source = "registry+https://github.com/rust-lang/crates.io-index"
841
-
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
1198
+
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
842
1199
dependencies = [
843
1200
"cfg-if",
844
1201
"js-sys",
845
1202
"libc",
846
1203
"r-efi",
847
-
"wasi 0.14.2+wasi-0.2.4",
1204
+
"wasip2",
848
1205
"wasm-bindgen",
849
1206
]
850
1207
···
869
1226
"aho-corasick",
870
1227
"bstr",
871
1228
"log",
872
-
"regex-automata 0.4.9",
1229
+
"regex-automata 0.4.13",
873
1230
"regex-syntax 0.8.5",
874
1231
]
875
1232
···
884
1241
"futures-sink",
885
1242
"futures-timer",
886
1243
"futures-util",
887
-
"getrandom 0.3.3",
1244
+
"getrandom 0.3.4",
888
1245
"hashbrown 0.15.5",
889
1246
"nonzero_ext",
890
1247
"parking_lot",
···
897
1254
]
898
1255
899
1256
[[package]]
1257
+
name = "group"
1258
+
version = "0.13.0"
1259
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1260
+
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
1261
+
dependencies = [
1262
+
"ff",
1263
+
"rand_core 0.6.4",
1264
+
"subtle",
1265
+
]
1266
+
1267
+
[[package]]
900
1268
name = "h2"
901
1269
version = "0.4.12"
902
1270
source = "registry+https://github.com/rust-lang/crates.io-index"
···
908
1276
"futures-core",
909
1277
"futures-sink",
910
1278
"http",
911
-
"indexmap",
1279
+
"indexmap 2.10.0",
912
1280
"slab",
913
1281
"tokio",
914
1282
"tokio-util",
···
944
1312
945
1313
[[package]]
946
1314
name = "hashbrown"
1315
+
version = "0.12.3"
1316
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1317
+
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
1318
+
1319
+
[[package]]
1320
+
name = "hashbrown"
947
1321
version = "0.14.5"
948
1322
source = "registry+https://github.com/rust-lang/crates.io-index"
949
1323
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
···
974
1348
975
1349
[[package]]
976
1350
name = "heck"
1351
+
version = "0.4.1"
1352
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1353
+
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
1354
+
1355
+
[[package]]
1356
+
name = "heck"
977
1357
version = "0.5.0"
978
1358
source = "registry+https://github.com/rust-lang/crates.io-index"
979
1359
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
···
985
1365
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
986
1366
987
1367
[[package]]
1368
+
name = "hex_fmt"
1369
+
version = "0.3.0"
1370
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1371
+
checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f"
1372
+
1373
+
[[package]]
988
1374
name = "hkdf"
989
1375
version = "0.12.4"
990
1376
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1009
1395
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
1010
1396
dependencies = [
1011
1397
"windows-sys 0.59.0",
1398
+
]
1399
+
1400
+
[[package]]
1401
+
name = "html-escape"
1402
+
version = "0.2.13"
1403
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1404
+
checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
1405
+
dependencies = [
1406
+
"utf8-width",
1012
1407
]
1013
1408
1014
1409
[[package]]
···
1079
1474
]
1080
1475
1081
1476
[[package]]
1477
+
name = "hyper-rustls"
1478
+
version = "0.27.7"
1479
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1480
+
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
1481
+
dependencies = [
1482
+
"http",
1483
+
"hyper",
1484
+
"hyper-util",
1485
+
"rustls",
1486
+
"rustls-pki-types",
1487
+
"tokio",
1488
+
"tokio-rustls",
1489
+
"tower-service",
1490
+
"webpki-roots 1.0.2",
1491
+
]
1492
+
1493
+
[[package]]
1082
1494
name = "hyper-timeout"
1083
1495
version = "0.5.2"
1084
1496
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1097
1509
source = "registry+https://github.com/rust-lang/crates.io-index"
1098
1510
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
1099
1511
dependencies = [
1512
+
"base64",
1100
1513
"bytes",
1101
1514
"futures-channel",
1102
1515
"futures-core",
···
1104
1517
"http",
1105
1518
"http-body",
1106
1519
"hyper",
1520
+
"ipnet",
1107
1521
"libc",
1522
+
"percent-encoding",
1108
1523
"pin-project-lite",
1109
1524
"socket2",
1525
+
"system-configuration",
1110
1526
"tokio",
1111
1527
"tower-service",
1112
1528
"tracing",
1529
+
"windows-registry",
1113
1530
]
1114
1531
1115
1532
[[package]]
···
1251
1668
1252
1669
[[package]]
1253
1670
name = "indexmap"
1671
+
version = "1.9.3"
1672
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1673
+
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
1674
+
dependencies = [
1675
+
"autocfg",
1676
+
"hashbrown 0.12.3",
1677
+
"serde",
1678
+
]
1679
+
1680
+
[[package]]
1681
+
name = "indexmap"
1254
1682
version = "2.10.0"
1255
1683
source = "registry+https://github.com/rust-lang/crates.io-index"
1256
1684
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
1257
1685
dependencies = [
1258
1686
"equivalent",
1259
1687
"hashbrown 0.15.5",
1688
+
"serde",
1689
+
]
1690
+
1691
+
[[package]]
1692
+
name = "indoc"
1693
+
version = "2.0.7"
1694
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1695
+
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
1696
+
dependencies = [
1697
+
"rustversion",
1260
1698
]
1261
1699
1262
1700
[[package]]
···
1269
1707
]
1270
1708
1271
1709
[[package]]
1710
+
name = "inventory"
1711
+
version = "0.3.21"
1712
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1713
+
checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e"
1714
+
dependencies = [
1715
+
"rustversion",
1716
+
]
1717
+
1718
+
[[package]]
1272
1719
name = "io-uring"
1273
1720
version = "0.7.9"
1274
1721
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1280
1727
]
1281
1728
1282
1729
[[package]]
1730
+
name = "ipld-core"
1731
+
version = "0.4.2"
1732
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1733
+
checksum = "104718b1cc124d92a6d01ca9c9258a7df311405debb3408c445a36452f9bf8db"
1734
+
dependencies = [
1735
+
"cid",
1736
+
"serde",
1737
+
"serde_bytes",
1738
+
]
1739
+
1740
+
[[package]]
1741
+
name = "ipnet"
1742
+
version = "2.11.0"
1743
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1744
+
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
1745
+
1746
+
[[package]]
1747
+
name = "iri-string"
1748
+
version = "0.7.9"
1749
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1750
+
checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397"
1751
+
dependencies = [
1752
+
"memchr",
1753
+
"serde",
1754
+
]
1755
+
1756
+
[[package]]
1283
1757
name = "itertools"
1284
1758
version = "0.12.1"
1285
1759
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1295
1769
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1296
1770
1297
1771
[[package]]
1772
+
name = "jacquard-api"
1773
+
version = "0.9.2"
1774
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1775
+
checksum = "bbbfd6e2b10fa1731f4d4e40c8f791956b0d4f804fb3efef891afec903f20597"
1776
+
dependencies = [
1777
+
"bon",
1778
+
"bytes",
1779
+
"jacquard-common",
1780
+
"jacquard-derive",
1781
+
"jacquard-lexicon",
1782
+
"miette",
1783
+
"rustversion",
1784
+
"serde",
1785
+
"serde_ipld_dagcbor",
1786
+
"thiserror 2.0.14",
1787
+
"unicode-segmentation",
1788
+
]
1789
+
1790
+
[[package]]
1791
+
name = "jacquard-common"
1792
+
version = "0.9.2"
1793
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1794
+
checksum = "df86cb117d9f1c2b0251ba67c3f0e3f963fd22abc6cf8de0e02a7fc846c288ca"
1795
+
dependencies = [
1796
+
"base64",
1797
+
"bon",
1798
+
"bytes",
1799
+
"chrono",
1800
+
"cid",
1801
+
"getrandom 0.2.16",
1802
+
"getrandom 0.3.4",
1803
+
"http",
1804
+
"ipld-core",
1805
+
"k256",
1806
+
"langtag",
1807
+
"miette",
1808
+
"multibase",
1809
+
"multihash",
1810
+
"ouroboros",
1811
+
"p256",
1812
+
"rand 0.9.2",
1813
+
"regex",
1814
+
"regex-lite",
1815
+
"reqwest",
1816
+
"serde",
1817
+
"serde_html_form",
1818
+
"serde_ipld_dagcbor",
1819
+
"serde_json",
1820
+
"signature",
1821
+
"smol_str",
1822
+
"thiserror 2.0.14",
1823
+
"tokio",
1824
+
"tokio-util",
1825
+
"trait-variant",
1826
+
"url",
1827
+
]
1828
+
1829
+
[[package]]
1830
+
name = "jacquard-derive"
1831
+
version = "0.9.2"
1832
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1833
+
checksum = "42ca61a69dc7aa8fb2d7163416514ff7df5d79f2e8b22e269f4610afa85572fe"
1834
+
dependencies = [
1835
+
"heck 0.5.0",
1836
+
"jacquard-lexicon",
1837
+
"proc-macro2",
1838
+
"quote",
1839
+
"syn 2.0.105",
1840
+
]
1841
+
1842
+
[[package]]
1843
+
name = "jacquard-identity"
1844
+
version = "0.9.2"
1845
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1846
+
checksum = "1ef714cacebfca486558a9f8e205daf466bfba0466c4d0c450fd6d0252400a53"
1847
+
dependencies = [
1848
+
"bon",
1849
+
"bytes",
1850
+
"http",
1851
+
"jacquard-api",
1852
+
"jacquard-common",
1853
+
"jacquard-lexicon",
1854
+
"miette",
1855
+
"percent-encoding",
1856
+
"reqwest",
1857
+
"serde",
1858
+
"serde_html_form",
1859
+
"serde_json",
1860
+
"thiserror 2.0.14",
1861
+
"tokio",
1862
+
"trait-variant",
1863
+
"url",
1864
+
"urlencoding",
1865
+
]
1866
+
1867
+
[[package]]
1868
+
name = "jacquard-lexicon"
1869
+
version = "0.9.2"
1870
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1871
+
checksum = "de87f2c938faea1b1f1b32d5b9e0c870e7b5bb5efbf96e3692ae2d8f6b2beb7a"
1872
+
dependencies = [
1873
+
"cid",
1874
+
"dashmap",
1875
+
"heck 0.5.0",
1876
+
"inventory",
1877
+
"jacquard-common",
1878
+
"miette",
1879
+
"multihash",
1880
+
"prettyplease",
1881
+
"proc-macro2",
1882
+
"quote",
1883
+
"serde",
1884
+
"serde_ipld_dagcbor",
1885
+
"serde_json",
1886
+
"serde_repr",
1887
+
"serde_with",
1888
+
"sha2",
1889
+
"syn 2.0.105",
1890
+
"thiserror 2.0.14",
1891
+
"unicode-segmentation",
1892
+
]
1893
+
1894
+
[[package]]
1298
1895
name = "jobserver"
1299
1896
version = "0.1.33"
1300
1897
source = "registry+https://github.com/rust-lang/crates.io-index"
1301
1898
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
1302
1899
dependencies = [
1303
-
"getrandom 0.3.3",
1900
+
"getrandom 0.3.4",
1304
1901
"libc",
1305
1902
]
1306
1903
1307
1904
[[package]]
1905
+
name = "josekit"
1906
+
version = "0.10.3"
1907
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1908
+
checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c"
1909
+
dependencies = [
1910
+
"anyhow",
1911
+
"base64",
1912
+
"flate2",
1913
+
"openssl",
1914
+
"regex",
1915
+
"serde",
1916
+
"serde_json",
1917
+
"thiserror 2.0.14",
1918
+
"time",
1919
+
]
1920
+
1921
+
[[package]]
1308
1922
name = "js-sys"
1309
1923
version = "0.3.77"
1310
1924
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1337
1951
]
1338
1952
1339
1953
[[package]]
1954
+
name = "k256"
1955
+
version = "0.13.4"
1956
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1957
+
checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
1958
+
dependencies = [
1959
+
"cfg-if",
1960
+
"ecdsa",
1961
+
"elliptic-curve",
1962
+
"sha2",
1963
+
]
1964
+
1965
+
[[package]]
1966
+
name = "langtag"
1967
+
version = "0.4.0"
1968
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1969
+
checksum = "9ecb4c689a30e48ebeaa14237f34037e300dd072e6ad21a9ec72e810ff3c6600"
1970
+
dependencies = [
1971
+
"serde",
1972
+
"static-regular-grammar",
1973
+
"thiserror 1.0.69",
1974
+
]
1975
+
1976
+
[[package]]
1340
1977
name = "lazy_static"
1341
1978
version = "1.5.0"
1342
1979
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1452
2089
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
1453
2090
1454
2091
[[package]]
2092
+
name = "lru-slab"
2093
+
version = "0.1.2"
2094
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2095
+
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
2096
+
2097
+
[[package]]
2098
+
name = "match-lookup"
2099
+
version = "0.1.1"
2100
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2101
+
checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e"
2102
+
dependencies = [
2103
+
"proc-macro2",
2104
+
"quote",
2105
+
"syn 1.0.109",
2106
+
]
2107
+
2108
+
[[package]]
1455
2109
name = "matchers"
1456
2110
version = "0.1.0"
1457
2111
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1483
2137
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
1484
2138
1485
2139
[[package]]
2140
+
name = "miette"
2141
+
version = "7.6.0"
2142
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2143
+
checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7"
2144
+
dependencies = [
2145
+
"cfg-if",
2146
+
"miette-derive",
2147
+
"unicode-width",
2148
+
]
2149
+
2150
+
[[package]]
2151
+
name = "miette-derive"
2152
+
version = "7.6.0"
2153
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2154
+
checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
2155
+
dependencies = [
2156
+
"proc-macro2",
2157
+
"quote",
2158
+
"syn 2.0.105",
2159
+
]
2160
+
2161
+
[[package]]
1486
2162
name = "mime"
1487
2163
version = "0.3.17"
1488
2164
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1501
2177
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
1502
2178
dependencies = [
1503
2179
"adler2",
2180
+
"simd-adler32",
1504
2181
]
1505
2182
1506
2183
[[package]]
···
1510
2187
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
1511
2188
dependencies = [
1512
2189
"libc",
1513
-
"wasi 0.11.1+wasi-snapshot-preview1",
2190
+
"wasi",
1514
2191
"windows-sys 0.59.0",
1515
2192
]
1516
2193
1517
2194
[[package]]
2195
+
name = "multibase"
2196
+
version = "0.9.2"
2197
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2198
+
checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77"
2199
+
dependencies = [
2200
+
"base-x",
2201
+
"base256emoji",
2202
+
"data-encoding",
2203
+
"data-encoding-macro",
2204
+
]
2205
+
2206
+
[[package]]
2207
+
name = "multihash"
2208
+
version = "0.19.3"
2209
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2210
+
checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d"
2211
+
dependencies = [
2212
+
"core2",
2213
+
"serde",
2214
+
"unsigned-varint",
2215
+
]
2216
+
2217
+
[[package]]
1518
2218
name = "nom"
1519
2219
version = "7.1.3"
1520
2220
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1573
2273
]
1574
2274
1575
2275
[[package]]
2276
+
name = "num-conv"
2277
+
version = "0.1.0"
2278
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2279
+
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
2280
+
2281
+
[[package]]
1576
2282
name = "num-integer"
1577
2283
version = "0.1.46"
1578
2284
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1633
2339
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
1634
2340
1635
2341
[[package]]
2342
+
name = "openssl"
2343
+
version = "0.10.75"
2344
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2345
+
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
2346
+
dependencies = [
2347
+
"bitflags",
2348
+
"cfg-if",
2349
+
"foreign-types",
2350
+
"libc",
2351
+
"once_cell",
2352
+
"openssl-macros",
2353
+
"openssl-sys",
2354
+
]
2355
+
2356
+
[[package]]
2357
+
name = "openssl-macros"
2358
+
version = "0.1.1"
2359
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2360
+
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
2361
+
dependencies = [
2362
+
"proc-macro2",
2363
+
"quote",
2364
+
"syn 2.0.105",
2365
+
]
2366
+
2367
+
[[package]]
2368
+
name = "openssl-sys"
2369
+
version = "0.9.111"
2370
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2371
+
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
2372
+
dependencies = [
2373
+
"cc",
2374
+
"libc",
2375
+
"pkg-config",
2376
+
"vcpkg",
2377
+
]
2378
+
2379
+
[[package]]
2380
+
name = "ouroboros"
2381
+
version = "0.18.5"
2382
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2383
+
checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
2384
+
dependencies = [
2385
+
"aliasable",
2386
+
"ouroboros_macro",
2387
+
"static_assertions",
2388
+
]
2389
+
2390
+
[[package]]
2391
+
name = "ouroboros_macro"
2392
+
version = "0.18.5"
2393
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2394
+
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
2395
+
dependencies = [
2396
+
"heck 0.4.1",
2397
+
"proc-macro2",
2398
+
"proc-macro2-diagnostics",
2399
+
"quote",
2400
+
"syn 2.0.105",
2401
+
]
2402
+
2403
+
[[package]]
1636
2404
name = "overload"
1637
2405
version = "0.1.1"
1638
2406
source = "registry+https://github.com/rust-lang/crates.io-index"
1639
2407
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
1640
2408
1641
2409
[[package]]
2410
+
name = "p256"
2411
+
version = "0.13.2"
2412
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2413
+
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
2414
+
dependencies = [
2415
+
"ecdsa",
2416
+
"elliptic-curve",
2417
+
"primeorder",
2418
+
"sha2",
2419
+
]
2420
+
2421
+
[[package]]
1642
2422
name = "parking"
1643
2423
version = "2.2.1"
1644
2424
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1700
2480
"dotenvy",
1701
2481
"handlebars",
1702
2482
"hex",
2483
+
"html-escape",
1703
2484
"hyper-util",
2485
+
"jacquard-common",
2486
+
"jacquard-identity",
2487
+
"josekit",
1704
2488
"jwt-compact",
1705
2489
"lettre",
2490
+
"multibase",
1706
2491
"rand 0.9.2",
2492
+
"reqwest",
1707
2493
"rust-embed",
1708
2494
"rustls",
1709
2495
"scrypt",
···
1716
2502
"tower_governor",
1717
2503
"tracing",
1718
2504
"tracing-subscriber",
2505
+
"urlencoding",
1719
2506
]
1720
2507
1721
2508
[[package]]
···
1764
2551
"pest_meta",
1765
2552
"proc-macro2",
1766
2553
"quote",
1767
-
"syn",
2554
+
"syn 2.0.105",
1768
2555
]
1769
2556
1770
2557
[[package]]
···
1794
2581
dependencies = [
1795
2582
"proc-macro2",
1796
2583
"quote",
1797
-
"syn",
2584
+
"syn 2.0.105",
1798
2585
]
1799
2586
1800
2587
[[package]]
···
1852
2639
]
1853
2640
1854
2641
[[package]]
2642
+
name = "powerfmt"
2643
+
version = "0.2.0"
2644
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2645
+
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
2646
+
2647
+
[[package]]
1855
2648
name = "ppv-lite86"
1856
2649
version = "0.2.21"
1857
2650
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1867
2660
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
1868
2661
dependencies = [
1869
2662
"proc-macro2",
1870
-
"syn",
2663
+
"syn 2.0.105",
2664
+
]
2665
+
2666
+
[[package]]
2667
+
name = "primeorder"
2668
+
version = "0.13.6"
2669
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2670
+
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
2671
+
dependencies = [
2672
+
"elliptic-curve",
2673
+
]
2674
+
2675
+
[[package]]
2676
+
name = "proc-macro-error"
2677
+
version = "1.0.4"
2678
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2679
+
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
2680
+
dependencies = [
2681
+
"proc-macro-error-attr",
2682
+
"proc-macro2",
2683
+
"quote",
2684
+
"syn 1.0.109",
2685
+
"version_check",
2686
+
]
2687
+
2688
+
[[package]]
2689
+
name = "proc-macro-error-attr"
2690
+
version = "1.0.4"
2691
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2692
+
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
2693
+
dependencies = [
2694
+
"proc-macro2",
2695
+
"quote",
2696
+
"version_check",
1871
2697
]
1872
2698
1873
2699
[[package]]
···
1880
2706
]
1881
2707
1882
2708
[[package]]
2709
+
name = "proc-macro2-diagnostics"
2710
+
version = "0.10.1"
2711
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2712
+
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
2713
+
dependencies = [
2714
+
"proc-macro2",
2715
+
"quote",
2716
+
"syn 2.0.105",
2717
+
"version_check",
2718
+
"yansi",
2719
+
]
2720
+
2721
+
[[package]]
1883
2722
name = "psm"
1884
2723
version = "0.1.26"
1885
2724
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1898
2737
"libc",
1899
2738
"once_cell",
1900
2739
"raw-cpuid",
1901
-
"wasi 0.11.1+wasi-snapshot-preview1",
2740
+
"wasi",
1902
2741
"web-sys",
1903
2742
"winapi",
1904
2743
]
1905
2744
1906
2745
[[package]]
2746
+
name = "quinn"
2747
+
version = "0.11.9"
2748
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2749
+
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
2750
+
dependencies = [
2751
+
"bytes",
2752
+
"cfg_aliases",
2753
+
"pin-project-lite",
2754
+
"quinn-proto",
2755
+
"quinn-udp",
2756
+
"rustc-hash 2.1.1",
2757
+
"rustls",
2758
+
"socket2",
2759
+
"thiserror 2.0.14",
2760
+
"tokio",
2761
+
"tracing",
2762
+
"web-time",
2763
+
]
2764
+
2765
+
[[package]]
2766
+
name = "quinn-proto"
2767
+
version = "0.11.13"
2768
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2769
+
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
2770
+
dependencies = [
2771
+
"bytes",
2772
+
"getrandom 0.3.4",
2773
+
"lru-slab",
2774
+
"rand 0.9.2",
2775
+
"ring",
2776
+
"rustc-hash 2.1.1",
2777
+
"rustls",
2778
+
"rustls-pki-types",
2779
+
"slab",
2780
+
"thiserror 2.0.14",
2781
+
"tinyvec",
2782
+
"tracing",
2783
+
"web-time",
2784
+
]
2785
+
2786
+
[[package]]
2787
+
name = "quinn-udp"
2788
+
version = "0.5.14"
2789
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2790
+
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
2791
+
dependencies = [
2792
+
"cfg_aliases",
2793
+
"libc",
2794
+
"once_cell",
2795
+
"socket2",
2796
+
"tracing",
2797
+
"windows-sys 0.59.0",
2798
+
]
2799
+
2800
+
[[package]]
1907
2801
name = "quote"
1908
2802
version = "1.0.40"
1909
2803
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1980
2874
source = "registry+https://github.com/rust-lang/crates.io-index"
1981
2875
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
1982
2876
dependencies = [
1983
-
"getrandom 0.3.3",
2877
+
"getrandom 0.3.4",
1984
2878
]
2879
+
2880
+
[[package]]
2881
+
name = "range-traits"
2882
+
version = "0.3.2"
2883
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2884
+
checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab"
1985
2885
1986
2886
[[package]]
1987
2887
name = "raw-cpuid"
···
2002
2902
]
2003
2903
2004
2904
[[package]]
2905
+
name = "ref-cast"
2906
+
version = "1.0.25"
2907
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2908
+
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
2909
+
dependencies = [
2910
+
"ref-cast-impl",
2911
+
]
2912
+
2913
+
[[package]]
2914
+
name = "ref-cast-impl"
2915
+
version = "1.0.25"
2916
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2917
+
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
2918
+
dependencies = [
2919
+
"proc-macro2",
2920
+
"quote",
2921
+
"syn 2.0.105",
2922
+
]
2923
+
2924
+
[[package]]
2005
2925
name = "regex"
2006
-
version = "1.11.1"
2926
+
version = "1.12.2"
2007
2927
source = "registry+https://github.com/rust-lang/crates.io-index"
2008
-
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
2928
+
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
2009
2929
dependencies = [
2010
2930
"aho-corasick",
2011
2931
"memchr",
2012
-
"regex-automata 0.4.9",
2932
+
"regex-automata 0.4.13",
2013
2933
"regex-syntax 0.8.5",
2014
2934
]
2015
2935
···
2024
2944
2025
2945
[[package]]
2026
2946
name = "regex-automata"
2027
-
version = "0.4.9"
2947
+
version = "0.4.13"
2028
2948
source = "registry+https://github.com/rust-lang/crates.io-index"
2029
-
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
2949
+
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
2030
2950
dependencies = [
2031
2951
"aho-corasick",
2032
2952
"memchr",
···
2034
2954
]
2035
2955
2036
2956
[[package]]
2957
+
name = "regex-lite"
2958
+
version = "0.1.8"
2959
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2960
+
checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da"
2961
+
2962
+
[[package]]
2037
2963
name = "regex-syntax"
2038
2964
version = "0.6.29"
2039
2965
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2046
2972
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
2047
2973
2048
2974
[[package]]
2975
+
name = "reqwest"
2976
+
version = "0.12.24"
2977
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2978
+
checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
2979
+
dependencies = [
2980
+
"async-compression",
2981
+
"base64",
2982
+
"bytes",
2983
+
"encoding_rs",
2984
+
"futures-core",
2985
+
"futures-util",
2986
+
"h2",
2987
+
"http",
2988
+
"http-body",
2989
+
"http-body-util",
2990
+
"hyper",
2991
+
"hyper-rustls",
2992
+
"hyper-util",
2993
+
"js-sys",
2994
+
"log",
2995
+
"mime",
2996
+
"percent-encoding",
2997
+
"pin-project-lite",
2998
+
"quinn",
2999
+
"rustls",
3000
+
"rustls-pki-types",
3001
+
"serde",
3002
+
"serde_json",
3003
+
"serde_urlencoded",
3004
+
"sync_wrapper",
3005
+
"tokio",
3006
+
"tokio-rustls",
3007
+
"tokio-util",
3008
+
"tower",
3009
+
"tower-http",
3010
+
"tower-service",
3011
+
"url",
3012
+
"wasm-bindgen",
3013
+
"wasm-bindgen-futures",
3014
+
"wasm-streams",
3015
+
"web-sys",
3016
+
"webpki-roots 1.0.2",
3017
+
]
3018
+
3019
+
[[package]]
3020
+
name = "rfc6979"
3021
+
version = "0.4.0"
3022
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3023
+
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
3024
+
dependencies = [
3025
+
"hmac",
3026
+
"subtle",
3027
+
]
3028
+
3029
+
[[package]]
2049
3030
name = "ring"
2050
3031
version = "0.17.14"
2051
3032
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2099
3080
"proc-macro2",
2100
3081
"quote",
2101
3082
"rust-embed-utils",
2102
-
"syn",
3083
+
"syn 2.0.105",
2103
3084
"walkdir",
2104
3085
]
2105
3086
···
2127
3108
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
2128
3109
2129
3110
[[package]]
3111
+
name = "rustc-hash"
3112
+
version = "2.1.1"
3113
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3114
+
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
3115
+
3116
+
[[package]]
2130
3117
name = "rustix"
2131
3118
version = "0.38.44"
2132
3119
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2161
3148
source = "registry+https://github.com/rust-lang/crates.io-index"
2162
3149
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
2163
3150
dependencies = [
3151
+
"web-time",
2164
3152
"zeroize",
2165
3153
]
2166
3154
···
2207
3195
]
2208
3196
2209
3197
[[package]]
3198
+
name = "schemars"
3199
+
version = "0.9.0"
3200
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3201
+
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
3202
+
dependencies = [
3203
+
"dyn-clone",
3204
+
"ref-cast",
3205
+
"serde",
3206
+
"serde_json",
3207
+
]
3208
+
3209
+
[[package]]
3210
+
name = "schemars"
3211
+
version = "1.1.0"
3212
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3213
+
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
3214
+
dependencies = [
3215
+
"dyn-clone",
3216
+
"ref-cast",
3217
+
"serde",
3218
+
"serde_json",
3219
+
]
3220
+
3221
+
[[package]]
2210
3222
name = "scopeguard"
2211
3223
version = "1.2.0"
2212
3224
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2222
3234
"pbkdf2",
2223
3235
"salsa20",
2224
3236
"sha2",
3237
+
]
3238
+
3239
+
[[package]]
3240
+
name = "sec1"
3241
+
version = "0.7.3"
3242
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3243
+
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
3244
+
dependencies = [
3245
+
"base16ct",
3246
+
"der",
3247
+
"generic-array",
3248
+
"pkcs8",
3249
+
"subtle",
3250
+
"zeroize",
2225
3251
]
2226
3252
2227
3253
[[package]]
···
2244
3270
2245
3271
[[package]]
2246
3272
name = "serde"
2247
-
version = "1.0.219"
3273
+
version = "1.0.228"
2248
3274
source = "registry+https://github.com/rust-lang/crates.io-index"
2249
-
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
3275
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
3276
+
dependencies = [
3277
+
"serde_core",
3278
+
"serde_derive",
3279
+
]
3280
+
3281
+
[[package]]
3282
+
name = "serde_bytes"
3283
+
version = "0.11.19"
3284
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3285
+
checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
3286
+
dependencies = [
3287
+
"serde",
3288
+
"serde_core",
3289
+
]
3290
+
3291
+
[[package]]
3292
+
name = "serde_core"
3293
+
version = "1.0.228"
3294
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3295
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
2250
3296
dependencies = [
2251
3297
"serde_derive",
2252
3298
]
2253
3299
2254
3300
[[package]]
2255
3301
name = "serde_derive"
2256
-
version = "1.0.219"
3302
+
version = "1.0.228"
2257
3303
source = "registry+https://github.com/rust-lang/crates.io-index"
2258
-
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
3304
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
2259
3305
dependencies = [
2260
3306
"proc-macro2",
2261
3307
"quote",
2262
-
"syn",
3308
+
"syn 2.0.105",
3309
+
]
3310
+
3311
+
[[package]]
3312
+
name = "serde_html_form"
3313
+
version = "0.2.8"
3314
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3315
+
checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f"
3316
+
dependencies = [
3317
+
"form_urlencoded",
3318
+
"indexmap 2.10.0",
3319
+
"itoa",
3320
+
"ryu",
3321
+
"serde_core",
3322
+
]
3323
+
3324
+
[[package]]
3325
+
name = "serde_ipld_dagcbor"
3326
+
version = "0.6.4"
3327
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3328
+
checksum = "46182f4f08349a02b45c998ba3215d3f9de826246ba02bb9dddfe9a2a2100778"
3329
+
dependencies = [
3330
+
"cbor4ii",
3331
+
"ipld-core",
3332
+
"scopeguard",
3333
+
"serde",
2263
3334
]
2264
3335
2265
3336
[[package]]
2266
3337
name = "serde_json"
2267
-
version = "1.0.142"
3338
+
version = "1.0.145"
2268
3339
source = "registry+https://github.com/rust-lang/crates.io-index"
2269
-
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
3340
+
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
2270
3341
dependencies = [
3342
+
"indexmap 2.10.0",
2271
3343
"itoa",
2272
3344
"memchr",
2273
3345
"ryu",
2274
3346
"serde",
3347
+
"serde_core",
2275
3348
]
2276
3349
2277
3350
[[package]]
···
2285
3358
]
2286
3359
2287
3360
[[package]]
3361
+
name = "serde_repr"
3362
+
version = "0.1.20"
3363
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3364
+
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
3365
+
dependencies = [
3366
+
"proc-macro2",
3367
+
"quote",
3368
+
"syn 2.0.105",
3369
+
]
3370
+
3371
+
[[package]]
2288
3372
name = "serde_urlencoded"
2289
3373
version = "0.7.1"
2290
3374
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2297
3381
]
2298
3382
2299
3383
[[package]]
3384
+
name = "serde_with"
3385
+
version = "3.16.0"
3386
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3387
+
checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1"
3388
+
dependencies = [
3389
+
"base64",
3390
+
"chrono",
3391
+
"hex",
3392
+
"indexmap 1.9.3",
3393
+
"indexmap 2.10.0",
3394
+
"schemars 0.9.0",
3395
+
"schemars 1.1.0",
3396
+
"serde_core",
3397
+
"serde_json",
3398
+
"serde_with_macros",
3399
+
"time",
3400
+
]
3401
+
3402
+
[[package]]
3403
+
name = "serde_with_macros"
3404
+
version = "3.16.0"
3405
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3406
+
checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b"
3407
+
dependencies = [
3408
+
"darling 0.21.3",
3409
+
"proc-macro2",
3410
+
"quote",
3411
+
"syn 2.0.105",
3412
+
]
3413
+
3414
+
[[package]]
2300
3415
name = "sha1"
2301
3416
version = "0.10.6"
2302
3417
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2353
3468
]
2354
3469
2355
3470
[[package]]
3471
+
name = "simd-adler32"
3472
+
version = "0.3.7"
3473
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3474
+
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
3475
+
3476
+
[[package]]
2356
3477
name = "slab"
2357
3478
version = "0.4.11"
2358
3479
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2368
3489
]
2369
3490
2370
3491
[[package]]
3492
+
name = "smol_str"
3493
+
version = "0.3.4"
3494
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3495
+
checksum = "3498b0a27f93ef1402f20eefacfaa1691272ac4eca1cdc8c596cb0a245d6cbf5"
3496
+
dependencies = [
3497
+
"borsh",
3498
+
"serde_core",
3499
+
]
3500
+
3501
+
[[package]]
2371
3502
name = "socket2"
2372
3503
version = "0.6.0"
2373
3504
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2437
3568
"futures-util",
2438
3569
"hashbrown 0.15.5",
2439
3570
"hashlink",
2440
-
"indexmap",
3571
+
"indexmap 2.10.0",
2441
3572
"log",
2442
3573
"memchr",
2443
3574
"once_cell",
···
2465
3596
"quote",
2466
3597
"sqlx-core",
2467
3598
"sqlx-macros-core",
2468
-
"syn",
3599
+
"syn 2.0.105",
2469
3600
]
2470
3601
2471
3602
[[package]]
···
2476
3607
dependencies = [
2477
3608
"dotenvy",
2478
3609
"either",
2479
-
"heck",
3610
+
"heck 0.5.0",
2480
3611
"hex",
2481
3612
"once_cell",
2482
3613
"proc-macro2",
···
2488
3619
"sqlx-mysql",
2489
3620
"sqlx-postgres",
2490
3621
"sqlx-sqlite",
2491
-
"syn",
3622
+
"syn 2.0.105",
2492
3623
"tokio",
2493
3624
"url",
2494
3625
]
···
2619
3750
]
2620
3751
2621
3752
[[package]]
3753
+
name = "static-regular-grammar"
3754
+
version = "2.0.2"
3755
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3756
+
checksum = "4f4a6c40247579acfbb138c3cd7de3dab113ab4ac6227f1b7de7d626ee667957"
3757
+
dependencies = [
3758
+
"abnf",
3759
+
"btree-range-map",
3760
+
"ciborium",
3761
+
"hex_fmt",
3762
+
"indoc",
3763
+
"proc-macro-error",
3764
+
"proc-macro2",
3765
+
"quote",
3766
+
"serde",
3767
+
"sha2",
3768
+
"syn 2.0.105",
3769
+
"thiserror 1.0.69",
3770
+
]
3771
+
3772
+
[[package]]
3773
+
name = "static_assertions"
3774
+
version = "1.1.0"
3775
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3776
+
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
3777
+
3778
+
[[package]]
2622
3779
name = "stringprep"
2623
3780
version = "0.1.5"
2624
3781
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2643
3800
2644
3801
[[package]]
2645
3802
name = "syn"
3803
+
version = "1.0.109"
3804
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3805
+
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
3806
+
dependencies = [
3807
+
"proc-macro2",
3808
+
"quote",
3809
+
"unicode-ident",
3810
+
]
3811
+
3812
+
[[package]]
3813
+
name = "syn"
2646
3814
version = "2.0.105"
2647
3815
source = "registry+https://github.com/rust-lang/crates.io-index"
2648
3816
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
···
2657
3825
version = "1.0.2"
2658
3826
source = "registry+https://github.com/rust-lang/crates.io-index"
2659
3827
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
3828
+
dependencies = [
3829
+
"futures-core",
3830
+
]
2660
3831
2661
3832
[[package]]
2662
3833
name = "synstructure"
···
2666
3837
dependencies = [
2667
3838
"proc-macro2",
2668
3839
"quote",
2669
-
"syn",
3840
+
"syn 2.0.105",
3841
+
]
3842
+
3843
+
[[package]]
3844
+
name = "system-configuration"
3845
+
version = "0.6.1"
3846
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3847
+
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
3848
+
dependencies = [
3849
+
"bitflags",
3850
+
"core-foundation",
3851
+
"system-configuration-sys",
3852
+
]
3853
+
3854
+
[[package]]
3855
+
name = "system-configuration-sys"
3856
+
version = "0.6.0"
3857
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3858
+
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
3859
+
dependencies = [
3860
+
"core-foundation-sys",
3861
+
"libc",
2670
3862
]
2671
3863
2672
3864
[[package]]
···
2695
3887
dependencies = [
2696
3888
"proc-macro2",
2697
3889
"quote",
2698
-
"syn",
3890
+
"syn 2.0.105",
2699
3891
]
2700
3892
2701
3893
[[package]]
···
2706
3898
dependencies = [
2707
3899
"proc-macro2",
2708
3900
"quote",
2709
-
"syn",
3901
+
"syn 2.0.105",
2710
3902
]
2711
3903
2712
3904
[[package]]
···
2719
3911
]
2720
3912
2721
3913
[[package]]
3914
+
name = "time"
3915
+
version = "0.3.44"
3916
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3917
+
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
3918
+
dependencies = [
3919
+
"deranged",
3920
+
"itoa",
3921
+
"num-conv",
3922
+
"powerfmt",
3923
+
"serde",
3924
+
"time-core",
3925
+
"time-macros",
3926
+
]
3927
+
3928
+
[[package]]
3929
+
name = "time-core"
3930
+
version = "0.1.6"
3931
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3932
+
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
3933
+
3934
+
[[package]]
3935
+
name = "time-macros"
3936
+
version = "0.2.24"
3937
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3938
+
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
3939
+
dependencies = [
3940
+
"num-conv",
3941
+
"time-core",
3942
+
]
3943
+
3944
+
[[package]]
2722
3945
name = "tinystr"
2723
3946
version = "0.8.1"
2724
3947
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2770
3993
dependencies = [
2771
3994
"proc-macro2",
2772
3995
"quote",
2773
-
"syn",
3996
+
"syn 2.0.105",
2774
3997
]
2775
3998
2776
3999
[[package]]
···
2796
4019
2797
4020
[[package]]
2798
4021
name = "tokio-util"
2799
-
version = "0.7.15"
4022
+
version = "0.7.17"
2800
4023
source = "registry+https://github.com/rust-lang/crates.io-index"
2801
-
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
4024
+
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
2802
4025
dependencies = [
2803
4026
"bytes",
2804
4027
"futures-core",
···
2844
4067
dependencies = [
2845
4068
"futures-core",
2846
4069
"futures-util",
2847
-
"indexmap",
4070
+
"indexmap 2.10.0",
2848
4071
"pin-project-lite",
2849
4072
"slab",
2850
4073
"sync_wrapper",
···
2865
4088
"bitflags",
2866
4089
"bytes",
2867
4090
"futures-core",
4091
+
"futures-util",
2868
4092
"http",
2869
4093
"http-body",
4094
+
"iri-string",
2870
4095
"pin-project-lite",
2871
4096
"tokio",
2872
4097
"tokio-util",
4098
+
"tower",
2873
4099
"tower-layer",
2874
4100
"tower-service",
2875
4101
]
···
2923
4149
dependencies = [
2924
4150
"proc-macro2",
2925
4151
"quote",
2926
-
"syn",
4152
+
"syn 2.0.105",
2927
4153
]
2928
4154
2929
4155
[[package]]
···
2966
4192
]
2967
4193
2968
4194
[[package]]
4195
+
name = "trait-variant"
4196
+
version = "0.1.2"
4197
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4198
+
checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7"
4199
+
dependencies = [
4200
+
"proc-macro2",
4201
+
"quote",
4202
+
"syn 2.0.105",
4203
+
]
4204
+
4205
+
[[package]]
2969
4206
name = "try-lock"
2970
4207
version = "0.2.5"
2971
4208
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3011
4248
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
3012
4249
3013
4250
[[package]]
4251
+
name = "unicode-segmentation"
4252
+
version = "1.12.0"
4253
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4254
+
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
4255
+
4256
+
[[package]]
4257
+
name = "unicode-width"
4258
+
version = "0.1.14"
4259
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4260
+
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
4261
+
4262
+
[[package]]
4263
+
name = "unsigned-varint"
4264
+
version = "0.8.0"
4265
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4266
+
checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06"
4267
+
4268
+
[[package]]
3014
4269
name = "untrusted"
3015
4270
version = "0.7.1"
3016
4271
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3031
4286
"form_urlencoded",
3032
4287
"idna",
3033
4288
"percent-encoding",
4289
+
"serde",
3034
4290
]
3035
4291
3036
4292
[[package]]
4293
+
name = "urlencoding"
4294
+
version = "2.1.3"
4295
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4296
+
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
4297
+
4298
+
[[package]]
4299
+
name = "utf8-width"
4300
+
version = "0.1.8"
4301
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4302
+
checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091"
4303
+
4304
+
[[package]]
3037
4305
name = "utf8_iter"
3038
4306
version = "1.0.4"
3039
4307
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3083
4351
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
3084
4352
3085
4353
[[package]]
3086
-
name = "wasi"
3087
-
version = "0.14.2+wasi-0.2.4"
4354
+
name = "wasip2"
4355
+
version = "1.0.1+wasi-0.2.4"
3088
4356
source = "registry+https://github.com/rust-lang/crates.io-index"
3089
-
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
4357
+
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
3090
4358
dependencies = [
3091
-
"wit-bindgen-rt",
4359
+
"wit-bindgen",
3092
4360
]
3093
4361
3094
4362
[[package]]
···
3119
4387
"log",
3120
4388
"proc-macro2",
3121
4389
"quote",
3122
-
"syn",
4390
+
"syn 2.0.105",
3123
4391
"wasm-bindgen-shared",
3124
4392
]
3125
4393
3126
4394
[[package]]
4395
+
name = "wasm-bindgen-futures"
4396
+
version = "0.4.50"
4397
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4398
+
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
4399
+
dependencies = [
4400
+
"cfg-if",
4401
+
"js-sys",
4402
+
"once_cell",
4403
+
"wasm-bindgen",
4404
+
"web-sys",
4405
+
]
4406
+
4407
+
[[package]]
3127
4408
name = "wasm-bindgen-macro"
3128
4409
version = "0.2.100"
3129
4410
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3141
4422
dependencies = [
3142
4423
"proc-macro2",
3143
4424
"quote",
3144
-
"syn",
4425
+
"syn 2.0.105",
3145
4426
"wasm-bindgen-backend",
3146
4427
"wasm-bindgen-shared",
3147
4428
]
···
3153
4434
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
3154
4435
dependencies = [
3155
4436
"unicode-ident",
4437
+
]
4438
+
4439
+
[[package]]
4440
+
name = "wasm-streams"
4441
+
version = "0.4.2"
4442
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4443
+
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
4444
+
dependencies = [
4445
+
"futures-util",
4446
+
"js-sys",
4447
+
"wasm-bindgen",
4448
+
"wasm-bindgen-futures",
4449
+
"web-sys",
3156
4450
]
3157
4451
3158
4452
[[package]]
···
3254
4548
dependencies = [
3255
4549
"windows-implement",
3256
4550
"windows-interface",
3257
-
"windows-link",
4551
+
"windows-link 0.1.3",
3258
4552
"windows-result",
3259
4553
"windows-strings",
3260
4554
]
···
3267
4561
dependencies = [
3268
4562
"proc-macro2",
3269
4563
"quote",
3270
-
"syn",
4564
+
"syn 2.0.105",
3271
4565
]
3272
4566
3273
4567
[[package]]
···
3278
4572
dependencies = [
3279
4573
"proc-macro2",
3280
4574
"quote",
3281
-
"syn",
4575
+
"syn 2.0.105",
3282
4576
]
3283
4577
3284
4578
[[package]]
···
3288
4582
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
3289
4583
3290
4584
[[package]]
4585
+
name = "windows-link"
4586
+
version = "0.2.1"
4587
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4588
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
4589
+
4590
+
[[package]]
4591
+
name = "windows-registry"
4592
+
version = "0.5.3"
4593
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4594
+
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
4595
+
dependencies = [
4596
+
"windows-link 0.1.3",
4597
+
"windows-result",
4598
+
"windows-strings",
4599
+
]
4600
+
4601
+
[[package]]
3291
4602
name = "windows-result"
3292
4603
version = "0.3.4"
3293
4604
source = "registry+https://github.com/rust-lang/crates.io-index"
3294
4605
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
3295
4606
dependencies = [
3296
-
"windows-link",
4607
+
"windows-link 0.1.3",
3297
4608
]
3298
4609
3299
4610
[[package]]
···
3302
4613
source = "registry+https://github.com/rust-lang/crates.io-index"
3303
4614
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
3304
4615
dependencies = [
3305
-
"windows-link",
4616
+
"windows-link 0.1.3",
3306
4617
]
3307
4618
3308
4619
[[package]]
···
3454
4765
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
3455
4766
3456
4767
[[package]]
3457
-
name = "wit-bindgen-rt"
3458
-
version = "0.39.0"
4768
+
name = "wit-bindgen"
4769
+
version = "0.46.0"
3459
4770
source = "registry+https://github.com/rust-lang/crates.io-index"
3460
-
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
3461
-
dependencies = [
3462
-
"bitflags",
3463
-
]
4771
+
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
3464
4772
3465
4773
[[package]]
3466
4774
name = "writeable"
···
3469
4777
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
3470
4778
3471
4779
[[package]]
4780
+
name = "yansi"
4781
+
version = "1.0.1"
4782
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4783
+
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
4784
+
4785
+
[[package]]
3472
4786
name = "yoke"
3473
4787
version = "0.8.0"
3474
4788
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3488
4802
dependencies = [
3489
4803
"proc-macro2",
3490
4804
"quote",
3491
-
"syn",
4805
+
"syn 2.0.105",
3492
4806
"synstructure",
3493
4807
]
3494
4808
···
3509
4823
dependencies = [
3510
4824
"proc-macro2",
3511
4825
"quote",
3512
-
"syn",
4826
+
"syn 2.0.105",
3513
4827
]
3514
4828
3515
4829
[[package]]
···
3529
4843
dependencies = [
3530
4844
"proc-macro2",
3531
4845
"quote",
3532
-
"syn",
4846
+
"syn 2.0.105",
3533
4847
"synstructure",
3534
4848
]
3535
4849
···
3550
4864
dependencies = [
3551
4865
"proc-macro2",
3552
4866
"quote",
3553
-
"syn",
4867
+
"syn 2.0.105",
3554
4868
]
3555
4869
3556
4870
[[package]]
···
3583
4897
dependencies = [
3584
4898
"proc-macro2",
3585
4899
"quote",
3586
-
"syn",
4900
+
"syn 2.0.105",
3587
4901
]
3588
4902
3589
4903
[[package]]
+8
-1
Cargo.toml
+8
-1
Cargo.toml
···
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
+
html-escape = "0.2.13"
39
+
josekit = "0.10.3"
+103
-67
README.md
+103
-67
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
+
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
19
42
20
-
Future feature?
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
52
-
For Coolify, if you're using Traefik as your proxy you'll need to make sure the labels for the container are set up correctly. A full example can be found at [./examples/coolify-compose.yml](./examples/coolify-compose.yml).
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).
53
81
54
82
```yml
55
83
gatekeeper:
56
-
container_name: gatekeeper
57
-
image: 'fatfingers23/pds_gatekeeper:latest'
58
-
restart: unless-stopped
59
-
volumes:
60
-
- '/pds:/pds'
61
-
environment:
62
-
- 'PDS_DATA_DIRECTORY=${PDS_DATA_DIRECTORY:-/pds}'
63
-
- 'PDS_BASE_URL=http://pds:3000'
64
-
- GATEKEEPER_HOST=0.0.0.0
65
-
depends_on:
66
-
- pds
67
-
healthcheck:
68
-
test:
69
-
- CMD
70
-
- timeout
71
-
- '1'
72
-
- bash
73
-
- '-c'
74
-
- 'cat < /dev/null > /dev/tcp/0.0.0.0/8080'
75
-
interval: 10s
76
-
timeout: 5s
77
-
retries: 3
78
-
start_period: 10s
79
-
labels:
80
-
- traefik.enable=true
81
-
- '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`))'
82
-
- traefik.http.routers.pds-gatekeeper.entrypoints=https
83
-
- traefik.http.routers.pds-gatekeeper.tls=true
84
-
- traefik.http.routers.pds-gatekeeper.priority=100
85
-
- traefik.http.routers.pds-gatekeeper.middlewares=gatekeeper-cors
86
-
- traefik.http.services.pds-gatekeeper.loadbalancer.server.port=8080
87
-
- traefik.http.services.pds-gatekeeper.loadbalancer.server.scheme=http
88
-
- 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE,OPTIONS,PATCH'
89
-
- 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowheaders=*'
90
-
- 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolalloworiginlist=*'
91
-
- traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolmaxage=100
92
-
- traefik.http.middlewares.gatekeeper-cors.headers.addvaryheader=true
93
-
- traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowcredentials=true
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
94
122
```
95
123
96
124
## Caddy setup
···
99
127
in extra functionality. The main part is below, for a full example see [./examples/Caddyfile](./examples/Caddyfile).
100
128
This is usually found at `/pds/caddy/etc/caddy/Caddyfile` on your PDS.
101
129
102
-
```caddyfile
130
+
```
103
131
@gatekeeper {
104
-
path /xrpc/com.atproto.server.getSession
105
-
path /xrpc/com.atproto.server.updateEmail
106
-
path /xrpc/com.atproto.server.createSession
107
-
path /xrpc/com.atproto.server.createAccount
108
-
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/*
109
139
}
110
140
111
141
handle @gatekeeper {
112
-
reverse_proxy http://localhost:8080
113
-
}
142
+
reverse_proxy http://localhost:8080
143
+
}
114
144
115
-
reverse_proxy http://localhost:3000
145
+
reverse_proxy http://localhost:3000
116
146
```
117
147
118
148
If you use a cloudflare tunnel then your caddyfile would look a bit more like below with your tunnel proxying to
119
149
`localhost:8081` (or w/e port you want).
120
150
121
-
```caddyfile
151
+
```
122
152
http://*.localhost:8082, http://localhost:8082 {
123
-
@gatekeeper {
124
-
path /xrpc/com.atproto.server.getSession
125
-
path /xrpc/com.atproto.server.updateEmail
126
-
path /xrpc/com.atproto.server.createSession
127
-
path /xrpc/com.atproto.server.createAccount
128
-
path /@atproto/oauth-provider/~api/sign-in
129
-
}
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
+
}
130
162
131
-
handle @gatekeeper {
132
-
reverse_proxy http://localhost:8080 {
133
-
#Makes sure the cloudflare ip is proxied and able to be picked up by pds gatekeeper
134
-
header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
135
-
}
136
-
}
137
-
138
-
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
139
170
}
140
171
141
172
```
···
168
199
169
200
`GATEKEEPER_CREATE_ACCOUNT_BURST` - Sets how many requests can be made in a burst. In the prior example this is where
170
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
171
-
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
+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
-
+1
-1
examples/coolify-compose.yml
+1
-1
examples/coolify-compose.yml
···
58
58
start_period: 10s
59
59
labels:
60
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.updateEmail`) || Path(`/xrpc/com.atproto.server.createSession`) || Path(`/xrpc/com.atproto.server.createAccount`) || Path(`/@atproto/oauth-provider/~api/sign-in`))'
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
62
- traefik.http.routers.pds-gatekeeper.entrypoints=https
63
63
- traefik.http.routers.pds-gatekeeper.tls=true
64
64
- traefik.http.routers.pds-gatekeeper.priority=100
+166
html_templates/captcha.hbs
+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
+1
-1
justfile
+10
migrations/20251126000000_gate_codes.sql
+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
+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(¶ms.handle),
139
+
url_encode(¶ms.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(¶ms.handle),
154
+
url_encode(¶ms.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(¶ms.handle),
170
+
url_encode(¶ms.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(¶ms.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(¶ms.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(¶ms.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
+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
+
AsyncTransport, 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
+
}
+157
-29
src/main.rs
+157
-29
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 axum::{
8
+
Router,
9
+
body::Body,
10
+
handler::Handler,
11
+
http::{Method, header},
12
+
middleware as ax_middleware,
13
+
routing::get,
14
+
routing::post,
15
+
};
10
16
use axum_template::engine::Engine;
11
17
use handlebars::Handlebars;
12
-
use hyper_util::client::legacy::connect::HttpConnector;
13
-
use hyper_util::rt::TokioExecutor;
18
+
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
19
+
use jacquard_common::types::did::Did;
20
+
use jacquard_identity::{PublicResolver, resolver::PlcSource};
14
21
use lettre::{AsyncSmtpTransport, Tokio1Executor};
22
+
use rand::Rng;
15
23
use rust_embed::RustEmbed;
16
24
use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode};
17
25
use sqlx::{SqlitePool, sqlite::SqlitePoolOptions};
18
26
use std::path::Path;
27
+
use std::sync::Arc;
19
28
use std::time::Duration;
20
29
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};
30
+
use tower_governor::{
31
+
GovernorLayer, governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor,
32
+
};
33
+
use tower_http::{
34
+
compression::CompressionLayer,
35
+
cors::{Any, CorsLayer},
36
+
};
26
37
use tracing::log;
27
38
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
28
39
40
+
mod gate;
29
41
pub mod helpers;
30
42
mod middleware;
31
43
mod oauth_provider;
···
38
50
#[include = "*.hbs"]
39
51
struct EmailTemplates;
40
52
53
+
#[derive(RustEmbed)]
54
+
#[folder = "html_templates"]
55
+
#[include = "*.hbs"]
56
+
struct HtmlTemplates;
57
+
58
+
/// Mostly the env variables that are used in the app
59
+
#[derive(Clone, Debug)]
60
+
pub struct AppConfig {
61
+
pds_base_url: String,
62
+
mailer_from: String,
63
+
email_subject: String,
64
+
allow_only_migrations: bool,
65
+
use_captcha: bool,
66
+
//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
67
+
//that need to capture this redirect url for creating an account
68
+
default_successful_redirect_url: String,
69
+
pds_service_did: Did<'static>,
70
+
gate_jwe_key: Vec<u8>,
71
+
captcha_success_redirects: Vec<String>,
72
+
}
73
+
74
+
impl AppConfig {
75
+
pub fn new() -> Self {
76
+
let pds_base_url =
77
+
env::var("PDS_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".to_string());
78
+
let mailer_from = env::var("PDS_EMAIL_FROM_ADDRESS")
79
+
.expect("PDS_EMAIL_FROM_ADDRESS is not set in your pds.env file");
80
+
//Hack not my favorite, but it does work
81
+
let allow_only_migrations = env::var("GATEKEEPER_ALLOW_ONLY_MIGRATIONS")
82
+
.map(|val| val.parse::<bool>().unwrap_or(false))
83
+
.unwrap_or(false);
84
+
85
+
let use_captcha = env::var("GATEKEEPER_CREATE_ACCOUNT_CAPTCHA")
86
+
.map(|val| val.parse::<bool>().unwrap_or(false))
87
+
.unwrap_or(false);
88
+
89
+
// PDS_SERVICE_DID is the did:web if set, if not it's PDS_HOSTNAME
90
+
let pds_service_did =
91
+
env::var("PDS_SERVICE_DID").unwrap_or_else(|_| match env::var("PDS_HOSTNAME") {
92
+
Ok(pds_hostname) => format!("did:web:{}", pds_hostname),
93
+
Err(_) => {
94
+
panic!("PDS_HOSTNAME or PDS_SERVICE_DID must be set in your pds.env file")
95
+
}
96
+
});
97
+
98
+
let email_subject = env::var("GATEKEEPER_TWO_FACTOR_EMAIL_SUBJECT")
99
+
.unwrap_or("Sign in to Bluesky".to_string());
100
+
101
+
// Load or generate JWE encryption key (32 bytes for AES-256)
102
+
let gate_jwe_key = env::var("GATEKEEPER_JWE_KEY")
103
+
.ok()
104
+
.and_then(|key_hex| hex::decode(key_hex).ok())
105
+
.unwrap_or_else(|| {
106
+
// Generate a random 32-byte key if not provided
107
+
let key: Vec<u8> = (0..32).map(|_| rand::rng().random()).collect();
108
+
log::warn!("WARNING: No GATEKEEPER_JWE_KEY found in the environment. Generated random key (hex): {}", hex::encode(&key));
109
+
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).");
110
+
key
111
+
});
112
+
113
+
if gate_jwe_key.len() != 32 {
114
+
panic!(
115
+
"GATEKEEPER_JWE_KEY must be 32 bytes (64 hex characters) for AES-256 encryption"
116
+
);
117
+
}
118
+
119
+
let captcha_success_redirects = match env::var("GATEKEEPER_CAPTCHA_SUCCESS_REDIRECTS") {
120
+
Ok(from_env) => from_env.split(",").map(|s| s.trim().to_string()).collect(),
121
+
Err(_) => {
122
+
vec![
123
+
String::from("https://bsky.app"),
124
+
String::from("https://pdsmoover.com"),
125
+
String::from("https://blacksky.community"),
126
+
String::from("https://tektite.cc"),
127
+
]
128
+
}
129
+
};
130
+
131
+
AppConfig {
132
+
pds_base_url,
133
+
mailer_from,
134
+
email_subject,
135
+
allow_only_migrations,
136
+
use_captcha,
137
+
default_successful_redirect_url: env::var("GATEKEEPER_DEFAULT_CAPTCHA_REDIRECT")
138
+
.unwrap_or("https://bsky.app".to_string()),
139
+
pds_service_did: pds_service_did
140
+
.parse()
141
+
.expect("PDS_SERVICE_DID is not a valid did or could not infer from PDS_HOSTNAME"),
142
+
gate_jwe_key,
143
+
captcha_success_redirects,
144
+
}
145
+
}
146
+
}
147
+
41
148
#[derive(Clone)]
42
149
pub struct AppState {
43
150
account_pool: SqlitePool,
44
151
pds_gatekeeper_pool: SqlitePool,
45
152
reverse_proxy_client: HyperUtilClient,
46
-
pds_base_url: String,
47
153
mailer: AsyncSmtpTransport<Tokio1Executor>,
48
-
mailer_from: String,
49
154
template_engine: Engine<Handlebars<'static>>,
155
+
resolver: Arc<PublicResolver>,
156
+
app_config: AppConfig,
50
157
}
51
158
52
159
async fn root_handler() -> impl axum::response::IntoResponse {
···
137
244
//Emailer set up
138
245
let smtp_url =
139
246
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");
142
247
143
248
let mailer: AsyncSmtpTransport<Tokio1Executor> =
144
249
AsyncSmtpTransport::<Tokio1Executor>::from_url(smtp_url.as_str())?.build();
···
155
260
let _ = hbs.register_embed_templates::<EmailTemplates>();
156
261
}
157
262
158
-
let pds_base_url =
159
-
env::var("PDS_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".to_string());
263
+
let _ = hbs.register_embed_templates::<HtmlTemplates>();
264
+
265
+
//Reads the PLC source from the pds env's or defaults to ol faithful
266
+
let plc_source_url =
267
+
env::var("PDS_DID_PLC_URL").unwrap_or_else(|_| "https://plc.directory".to_string());
268
+
let plc_source = PlcSource::PlcDirectory {
269
+
base: plc_source_url.parse().unwrap(),
270
+
};
271
+
let mut resolver = PublicResolver::default();
272
+
resolver = resolver.with_plc_source(plc_source.clone());
160
273
161
274
let state = AppState {
162
275
account_pool,
163
276
pds_gatekeeper_pool,
164
277
reverse_proxy_client: client,
165
-
pds_base_url,
166
278
mailer,
167
-
mailer_from: sent_from,
168
279
template_engine: Engine::from(hbs),
280
+
resolver: Arc::new(resolver),
281
+
app_config: AppConfig::new(),
169
282
};
170
283
171
284
// Rate limiting
172
285
//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()
286
+
let captcha_governor_conf = GovernorConfigBuilder::default()
174
287
.per_second(60)
175
288
.burst_size(5)
176
289
.key_extractor(SmartIpKeyExtractor)
···
216
329
"failed to create governor config for create account. this should not happen and is a bug",
217
330
);
218
331
219
-
let create_session_governor_limiter = create_session_governor_conf.limiter().clone();
332
+
let captcha_governor_limiter = captcha_governor_conf.limiter().clone();
220
333
let sign_in_governor_limiter = sign_in_governor_conf.limiter().clone();
221
334
let create_account_governor_limiter = create_account_governor_conf.limiter().clone();
335
+
336
+
let sign_in_governor_layer = GovernorLayer::new(sign_in_governor_conf);
222
337
223
338
let interval = Duration::from_secs(60);
224
339
// a separate background task to clean up
225
340
std::thread::spawn(move || {
226
341
loop {
227
342
std::thread::sleep(interval);
228
-
create_session_governor_limiter.retain_recent();
343
+
captcha_governor_limiter.retain_recent();
229
344
sign_in_governor_limiter.retain_recent();
230
345
create_account_governor_limiter.retain_recent();
231
346
}
···
236
351
.allow_methods([Method::GET, Method::OPTIONS, Method::POST])
237
352
.allow_headers(Any);
238
353
239
-
let app = Router::new()
354
+
let mut app = Router::new()
240
355
.route("/", get(root_handler))
241
356
.route("/xrpc/com.atproto.server.getSession", get(get_session))
242
357
.route(
358
+
"/xrpc/com.atproto.server.describeServer",
359
+
get(describe_server),
360
+
)
361
+
.route(
243
362
"/xrpc/com.atproto.server.updateEmail",
244
363
post(update_email).layer(ax_middleware::from_fn(middleware::extract_did)),
245
364
)
246
365
.route(
247
366
"/@atproto/oauth-provider/~api/sign-in",
248
-
post(sign_in).layer(GovernorLayer::new(sign_in_governor_conf)),
367
+
post(sign_in).layer(sign_in_governor_layer.clone()),
249
368
)
250
369
.route(
251
370
"/xrpc/com.atproto.server.createSession",
252
-
post(create_session.layer(GovernorLayer::new(create_session_governor_conf))),
371
+
post(create_session.layer(sign_in_governor_layer)),
253
372
)
254
373
.route(
255
374
"/xrpc/com.atproto.server.createAccount",
256
375
post(create_account).layer(GovernorLayer::new(create_account_governor_conf)),
257
-
)
376
+
);
377
+
378
+
if state.app_config.use_captcha {
379
+
app = app.route(
380
+
"/gate/signup",
381
+
get(get_gate).post(post_gate.layer(GovernorLayer::new(captcha_governor_conf))),
382
+
);
383
+
}
384
+
385
+
let app = app
258
386
.layer(CompressionLayer::new())
259
387
.layer(cors)
260
388
.with_state(state);
+1
-1
src/oauth_provider.rs
+1
-1
src/oauth_provider.rs
+306
-11
src/xrpc/com_atproto_server.rs
+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();