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