+1790
-161
Cargo.lock
+1790
-161
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]]
···
112
282
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
113
283
114
284
[[package]]
285
+
name = "aws-lc-rs"
286
+
version = "1.13.3"
287
+
source = "registry+https://github.com/rust-lang/crates.io-index"
288
+
checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
289
+
dependencies = [
290
+
"aws-lc-sys",
291
+
"untrusted 0.7.1",
292
+
"zeroize",
293
+
]
294
+
295
+
[[package]]
296
+
name = "aws-lc-sys"
297
+
version = "0.30.0"
298
+
source = "registry+https://github.com/rust-lang/crates.io-index"
299
+
checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
300
+
dependencies = [
301
+
"bindgen",
302
+
"cc",
303
+
"cmake",
304
+
"dunce",
305
+
"fs_extra",
306
+
]
307
+
308
+
[[package]]
115
309
name = "axum"
116
310
version = "0.8.4"
117
311
source = "registry+https://github.com/rust-lang/crates.io-index"
···
174
368
dependencies = [
175
369
"proc-macro2",
176
370
"quote",
177
-
"syn",
371
+
"syn 2.0.105",
178
372
]
179
373
180
374
[[package]]
···
205
399
]
206
400
207
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]]
208
424
name = "base64"
209
425
version = "0.22.1"
210
426
source = "registry+https://github.com/rust-lang/crates.io-index"
···
217
433
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
218
434
219
435
[[package]]
436
+
name = "bindgen"
437
+
version = "0.69.5"
438
+
source = "registry+https://github.com/rust-lang/crates.io-index"
439
+
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
440
+
dependencies = [
441
+
"bitflags",
442
+
"cexpr",
443
+
"clang-sys",
444
+
"itertools",
445
+
"lazy_static",
446
+
"lazycell",
447
+
"log",
448
+
"prettyplease",
449
+
"proc-macro2",
450
+
"quote",
451
+
"regex",
452
+
"rustc-hash 1.1.0",
453
+
"shlex",
454
+
"syn 2.0.105",
455
+
"which",
456
+
]
457
+
458
+
[[package]]
220
459
name = "bitflags"
221
460
version = "2.9.1"
222
461
source = "registry+https://github.com/rust-lang/crates.io-index"
···
235
474
]
236
475
237
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]]
238
524
name = "bstr"
239
525
version = "1.12.0"
240
526
source = "registry+https://github.com/rust-lang/crates.io-index"
···
245
531
]
246
532
247
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]]
248
558
name = "bumpalo"
249
559
version = "3.19.0"
250
560
source = "registry+https://github.com/rust-lang/crates.io-index"
···
261
571
version = "1.10.1"
262
572
source = "registry+https://github.com/rust-lang/crates.io-index"
263
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
+
]
264
586
265
587
[[package]]
266
588
name = "cc"
···
274
596
]
275
597
276
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]]
608
+
name = "cexpr"
609
+
version = "0.6.0"
610
+
source = "registry+https://github.com/rust-lang/crates.io-index"
611
+
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
612
+
dependencies = [
613
+
"nom 7.1.3",
614
+
]
615
+
616
+
[[package]]
277
617
name = "cfg-if"
278
618
version = "1.0.1"
279
619
source = "registry+https://github.com/rust-lang/crates.io-index"
280
620
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
281
621
282
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]]
283
629
name = "chrono"
284
-
version = "0.4.41"
630
+
version = "0.4.42"
285
631
source = "registry+https://github.com/rust-lang/crates.io-index"
286
-
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
632
+
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
287
633
dependencies = [
288
-
"android-tzdata",
289
634
"iana-time-zone",
290
635
"js-sys",
291
636
"num-traits",
637
+
"serde",
292
638
"wasm-bindgen",
293
-
"windows-link",
639
+
"windows-link 0.2.1",
294
640
]
295
641
296
642
[[package]]
···
331
677
]
332
678
333
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]]
334
694
name = "cipher"
335
695
version = "0.4.4"
336
696
source = "registry+https://github.com/rust-lang/crates.io-index"
···
341
701
]
342
702
343
703
[[package]]
704
+
name = "clang-sys"
705
+
version = "1.8.1"
706
+
source = "registry+https://github.com/rust-lang/crates.io-index"
707
+
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
708
+
dependencies = [
709
+
"glob",
710
+
"libc",
711
+
"libloading",
712
+
]
713
+
714
+
[[package]]
715
+
name = "cmake"
716
+
version = "0.1.54"
717
+
source = "registry+https://github.com/rust-lang/crates.io-index"
718
+
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
719
+
dependencies = [
720
+
"cc",
721
+
]
722
+
723
+
[[package]]
344
724
name = "concurrent-queue"
345
725
version = "2.5.0"
346
726
source = "registry+https://github.com/rust-lang/crates.io-index"
···
356
736
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
357
737
358
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]]
359
745
name = "core-foundation"
360
746
version = "0.9.4"
361
747
source = "registry+https://github.com/rust-lang/crates.io-index"
···
372
758
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
373
759
374
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]]
375
770
name = "cpufeatures"
376
771
version = "0.2.17"
377
772
source = "registry+https://github.com/rust-lang/crates.io-index"
···
396
791
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
397
792
398
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]]
399
803
name = "crossbeam-queue"
400
804
version = "0.3.12"
401
805
source = "registry+https://github.com/rust-lang/crates.io-index"
···
417
821
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
418
822
419
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
+
]
834
+
835
+
[[package]]
420
836
name = "crypto-common"
421
837
version = "0.1.6"
422
838
source = "registry+https://github.com/rust-lang/crates.io-index"
···
432
848
source = "registry+https://github.com/rust-lang/crates.io-index"
433
849
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
434
850
dependencies = [
435
-
"darling_core",
436
-
"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",
437
863
]
438
864
439
865
[[package]]
···
447
873
"proc-macro2",
448
874
"quote",
449
875
"strsim",
450
-
"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",
451
891
]
452
892
453
893
[[package]]
···
456
896
source = "registry+https://github.com/rust-lang/crates.io-index"
457
897
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
458
898
dependencies = [
459
-
"darling_core",
899
+
"darling_core 0.20.11",
460
900
"quote",
461
-
"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",
462
913
]
463
914
464
915
[[package]]
···
476
927
]
477
928
478
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]]
479
956
name = "der"
480
957
version = "0.7.10"
481
958
source = "registry+https://github.com/rust-lang/crates.io-index"
···
487
964
]
488
965
489
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]]
490
977
name = "derive_builder"
491
978
version = "0.20.2"
492
979
source = "registry+https://github.com/rust-lang/crates.io-index"
···
501
988
source = "registry+https://github.com/rust-lang/crates.io-index"
502
989
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
503
990
dependencies = [
504
-
"darling",
991
+
"darling 0.20.11",
505
992
"proc-macro2",
506
993
"quote",
507
-
"syn",
994
+
"syn 2.0.105",
508
995
]
509
996
510
997
[[package]]
···
514
1001
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
515
1002
dependencies = [
516
1003
"derive_builder_core",
517
-
"syn",
1004
+
"syn 2.0.105",
518
1005
]
519
1006
520
1007
[[package]]
···
537
1024
dependencies = [
538
1025
"proc-macro2",
539
1026
"quote",
540
-
"syn",
1027
+
"syn 2.0.105",
541
1028
]
542
1029
543
1030
[[package]]
···
547
1034
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
548
1035
549
1036
[[package]]
1037
+
name = "dunce"
1038
+
version = "1.0.5"
1039
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1040
+
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
1041
+
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]]
550
1063
name = "either"
551
1064
version = "1.15.0"
552
1065
source = "registry+https://github.com/rust-lang/crates.io-index"
···
556
1069
]
557
1070
558
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]]
559
1092
name = "email-encoding"
560
1093
version = "0.4.1"
561
1094
source = "registry+https://github.com/rust-lang/crates.io-index"
···
572
1105
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
573
1106
574
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
+
]
1115
+
1116
+
[[package]]
575
1117
name = "equivalent"
576
1118
version = "1.0.2"
577
1119
source = "registry+https://github.com/rust-lang/crates.io-index"
···
600
1142
601
1143
[[package]]
602
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"
603
1151
version = "5.4.1"
604
1152
source = "registry+https://github.com/rust-lang/crates.io-index"
605
1153
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
···
610
1158
]
611
1159
612
1160
[[package]]
1161
+
name = "event-listener-strategy"
1162
+
version = "0.5.4"
1163
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1164
+
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
1165
+
dependencies = [
1166
+
"event-listener 5.4.1",
1167
+
"pin-project-lite",
1168
+
]
1169
+
1170
+
[[package]]
613
1171
name = "fastrand"
614
1172
version = "2.3.0"
615
1173
source = "registry+https://github.com/rust-lang/crates.io-index"
616
1174
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
617
1175
618
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]]
619
1197
name = "flume"
620
1198
version = "0.11.1"
621
1199
source = "registry+https://github.com/rust-lang/crates.io-index"
···
655
1233
656
1234
[[package]]
657
1235
name = "form_urlencoded"
658
-
version = "1.2.1"
1236
+
version = "1.2.2"
659
1237
source = "registry+https://github.com/rust-lang/crates.io-index"
660
-
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
1238
+
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
661
1239
dependencies = [
662
1240
"percent-encoding",
663
1241
]
···
673
1251
]
674
1252
675
1253
[[package]]
1254
+
name = "fs_extra"
1255
+
version = "1.3.0"
1256
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1257
+
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
1258
+
1259
+
[[package]]
676
1260
name = "futures-channel"
677
1261
version = "0.3.31"
678
1262
source = "registry+https://github.com/rust-lang/crates.io-index"
···
717
1301
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
718
1302
719
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]]
720
1328
name = "futures-sink"
721
1329
version = "0.3.31"
722
1330
source = "registry+https://github.com/rust-lang/crates.io-index"
···
742
1350
dependencies = [
743
1351
"futures-core",
744
1352
"futures-io",
1353
+
"futures-macro",
745
1354
"futures-sink",
746
1355
"futures-task",
747
1356
"memchr",
···
758
1367
dependencies = [
759
1368
"typenum",
760
1369
"version_check",
1370
+
"zeroize",
761
1371
]
762
1372
763
1373
[[package]]
···
767
1377
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
768
1378
dependencies = [
769
1379
"cfg-if",
1380
+
"js-sys",
770
1381
"libc",
771
-
"wasi 0.11.1+wasi-snapshot-preview1",
1382
+
"wasi",
1383
+
"wasm-bindgen",
772
1384
]
773
1385
774
1386
[[package]]
775
1387
name = "getrandom"
776
-
version = "0.3.3"
1388
+
version = "0.3.4"
777
1389
source = "registry+https://github.com/rust-lang/crates.io-index"
778
-
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
1390
+
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
779
1391
dependencies = [
780
1392
"cfg-if",
781
1393
"js-sys",
782
1394
"libc",
783
1395
"r-efi",
784
-
"wasi 0.14.2+wasi-0.2.4",
1396
+
"wasip2",
785
1397
"wasm-bindgen",
786
1398
]
787
1399
···
792
1404
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
793
1405
794
1406
[[package]]
1407
+
name = "glob"
1408
+
version = "0.3.3"
1409
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1410
+
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
1411
+
1412
+
[[package]]
795
1413
name = "globset"
796
1414
version = "0.4.16"
797
1415
source = "registry+https://github.com/rust-lang/crates.io-index"
···
800
1418
"aho-corasick",
801
1419
"bstr",
802
1420
"log",
803
-
"regex-automata 0.4.9",
1421
+
"regex-automata 0.4.13",
804
1422
"regex-syntax 0.8.5",
805
1423
]
806
1424
807
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]]
808
1438
name = "governor"
809
1439
version = "0.10.1"
810
1440
source = "registry+https://github.com/rust-lang/crates.io-index"
···
815
1445
"futures-sink",
816
1446
"futures-timer",
817
1447
"futures-util",
818
-
"getrandom 0.3.3",
1448
+
"getrandom 0.3.4",
819
1449
"hashbrown 0.15.5",
820
1450
"nonzero_ext",
821
1451
"parking_lot",
···
828
1458
]
829
1459
830
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]]
831
1472
name = "h2"
832
1473
version = "0.4.12"
833
1474
source = "registry+https://github.com/rust-lang/crates.io-index"
···
839
1480
"futures-core",
840
1481
"futures-sink",
841
1482
"http",
842
-
"indexmap",
1483
+
"indexmap 2.10.0",
843
1484
"slab",
844
1485
"tokio",
845
1486
"tokio-util",
···
875
1516
876
1517
[[package]]
877
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"
878
1525
version = "0.14.5"
879
1526
source = "registry+https://github.com/rust-lang/crates.io-index"
880
1527
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
···
905
1552
906
1553
[[package]]
907
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"
908
1561
version = "0.5.0"
909
1562
source = "registry+https://github.com/rust-lang/crates.io-index"
910
1563
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
911
1564
912
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]]
913
1572
name = "hex"
914
1573
version = "0.4.3"
915
1574
source = "registry+https://github.com/rust-lang/crates.io-index"
916
1575
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
1576
+
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"
917
1582
918
1583
[[package]]
919
1584
name = "hkdf"
···
943
1608
]
944
1609
945
1610
[[package]]
946
-
name = "hostname"
947
-
version = "0.4.1"
1611
+
name = "html-escape"
1612
+
version = "0.2.13"
948
1613
source = "registry+https://github.com/rust-lang/crates.io-index"
949
-
checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65"
1614
+
checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
950
1615
dependencies = [
951
-
"cfg-if",
952
-
"libc",
953
-
"windows-link",
1616
+
"utf8-width",
954
1617
]
955
1618
956
1619
[[package]]
···
1021
1684
]
1022
1685
1023
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]]
1024
1704
name = "hyper-timeout"
1025
1705
version = "0.5.2"
1026
1706
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1039
1719
source = "registry+https://github.com/rust-lang/crates.io-index"
1040
1720
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
1041
1721
dependencies = [
1722
+
"base64",
1042
1723
"bytes",
1043
1724
"futures-channel",
1044
1725
"futures-core",
···
1046
1727
"http",
1047
1728
"http-body",
1048
1729
"hyper",
1730
+
"ipnet",
1049
1731
"libc",
1732
+
"percent-encoding",
1050
1733
"pin-project-lite",
1051
1734
"socket2",
1735
+
"system-configuration",
1052
1736
"tokio",
1053
1737
"tower-service",
1054
1738
"tracing",
1739
+
"windows-registry",
1055
1740
]
1056
1741
1057
1742
[[package]]
···
1172
1857
1173
1858
[[package]]
1174
1859
name = "idna"
1175
-
version = "1.0.3"
1860
+
version = "1.1.0"
1176
1861
source = "registry+https://github.com/rust-lang/crates.io-index"
1177
-
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
1862
+
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
1178
1863
dependencies = [
1179
1864
"idna_adapter",
1180
1865
"smallvec",
···
1193
1878
1194
1879
[[package]]
1195
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"
1196
1892
version = "2.10.0"
1197
1893
source = "registry+https://github.com/rust-lang/crates.io-index"
1198
1894
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
1199
1895
dependencies = [
1200
1896
"equivalent",
1201
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",
1202
1908
]
1203
1909
1204
1910
[[package]]
···
1211
1917
]
1212
1918
1213
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]]
1214
1929
name = "io-uring"
1215
1930
version = "0.7.9"
1216
1931
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1222
1937
]
1223
1938
1224
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]]
1967
+
name = "itertools"
1968
+
version = "0.12.1"
1969
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1970
+
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
1971
+
dependencies = [
1972
+
"either",
1973
+
]
1974
+
1975
+
[[package]]
1225
1976
name = "itoa"
1226
1977
version = "1.0.15"
1227
1978
source = "registry+https://github.com/rust-lang/crates.io-index"
1228
1979
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1229
1980
1230
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]]
1231
2105
name = "jobserver"
1232
2106
version = "0.1.33"
1233
2107
source = "registry+https://github.com/rust-lang/crates.io-index"
1234
2108
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
1235
2109
dependencies = [
1236
-
"getrandom 0.3.3",
2110
+
"getrandom 0.3.4",
1237
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",
1238
2129
]
1239
2130
1240
2131
[[package]]
···
1270
2161
]
1271
2162
1272
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]]
1273
2196
name = "lazy_static"
1274
2197
version = "1.5.0"
1275
2198
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1277
2200
dependencies = [
1278
2201
"spin",
1279
2202
]
2203
+
2204
+
[[package]]
2205
+
name = "lazycell"
2206
+
version = "1.3.0"
2207
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2208
+
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
1280
2209
1281
2210
[[package]]
1282
2211
name = "lettre"
···
1284
2213
source = "registry+https://github.com/rust-lang/crates.io-index"
1285
2214
checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56"
1286
2215
dependencies = [
2216
+
"async-std",
1287
2217
"async-trait",
1288
2218
"base64",
1289
2219
"chumsky",
···
1292
2222
"fastrand",
1293
2223
"futures-io",
1294
2224
"futures-util",
1295
-
"hostname",
1296
2225
"httpdate",
1297
2226
"idna",
1298
2227
"mime",
1299
-
"native-tls",
1300
-
"nom",
2228
+
"nom 8.0.0",
1301
2229
"percent-encoding",
1302
2230
"quoted_printable",
2231
+
"rustls",
1303
2232
"socket2",
1304
2233
"tokio",
1305
-
"tokio-native-tls",
2234
+
"tokio-rustls",
1306
2235
"url",
2236
+
"webpki-roots 1.0.2",
1307
2237
]
1308
2238
1309
2239
[[package]]
···
1313
2243
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
1314
2244
1315
2245
[[package]]
2246
+
name = "libloading"
2247
+
version = "0.8.8"
2248
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2249
+
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
2250
+
dependencies = [
2251
+
"cfg-if",
2252
+
"windows-targets 0.52.6",
2253
+
]
2254
+
2255
+
[[package]]
1316
2256
name = "libm"
1317
2257
version = "0.2.15"
1318
2258
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1342
2282
1343
2283
[[package]]
1344
2284
name = "linux-raw-sys"
1345
-
version = "0.9.4"
2285
+
version = "0.4.15"
2286
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2287
+
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
2288
+
2289
+
[[package]]
2290
+
name = "linux-raw-sys"
2291
+
version = "0.11.0"
1346
2292
source = "registry+https://github.com/rust-lang/crates.io-index"
1347
-
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
2293
+
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
1348
2294
1349
2295
[[package]]
1350
2296
name = "litemap"
···
1367
2313
version = "0.4.27"
1368
2314
source = "registry+https://github.com/rust-lang/crates.io-index"
1369
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
+
]
1370
2336
1371
2337
[[package]]
1372
2338
name = "matchers"
···
1400
2366
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
1401
2367
1402
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]]
1403
2391
name = "mime"
1404
2392
version = "0.3.17"
1405
2393
source = "registry+https://github.com/rust-lang/crates.io-index"
1406
2394
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
1407
2395
1408
2396
[[package]]
2397
+
name = "minimal-lexical"
2398
+
version = "0.2.1"
2399
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2400
+
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
2401
+
2402
+
[[package]]
1409
2403
name = "miniz_oxide"
1410
2404
version = "0.8.9"
1411
2405
source = "registry+https://github.com/rust-lang/crates.io-index"
1412
2406
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
1413
2407
dependencies = [
1414
2408
"adler2",
2409
+
"simd-adler32",
1415
2410
]
1416
2411
1417
2412
[[package]]
···
1421
2416
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
1422
2417
dependencies = [
1423
2418
"libc",
1424
-
"wasi 0.11.1+wasi-snapshot-preview1",
2419
+
"wasi",
1425
2420
"windows-sys 0.59.0",
1426
2421
]
1427
2422
1428
2423
[[package]]
1429
-
name = "native-tls"
1430
-
version = "0.2.14"
2424
+
name = "multibase"
2425
+
version = "0.9.2"
1431
2426
source = "registry+https://github.com/rust-lang/crates.io-index"
1432
-
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
2427
+
checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77"
1433
2428
dependencies = [
1434
-
"libc",
1435
-
"log",
1436
-
"openssl",
1437
-
"openssl-probe",
1438
-
"openssl-sys",
1439
-
"schannel",
1440
-
"security-framework",
1441
-
"security-framework-sys",
1442
-
"tempfile",
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]]
2447
+
name = "nom"
2448
+
version = "7.1.3"
2449
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2450
+
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
2451
+
dependencies = [
2452
+
"memchr",
2453
+
"minimal-lexical",
1443
2454
]
1444
2455
1445
2456
[[package]]
···
1491
2502
]
1492
2503
1493
2504
[[package]]
2505
+
name = "num-conv"
2506
+
version = "0.1.0"
2507
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2508
+
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
2509
+
2510
+
[[package]]
1494
2511
name = "num-integer"
1495
2512
version = "0.1.46"
1496
2513
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1552
2569
1553
2570
[[package]]
1554
2571
name = "openssl"
1555
-
version = "0.10.73"
2572
+
version = "0.10.75"
1556
2573
source = "registry+https://github.com/rust-lang/crates.io-index"
1557
-
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
2574
+
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
1558
2575
dependencies = [
1559
2576
"bitflags",
1560
2577
"cfg-if",
···
1573
2590
dependencies = [
1574
2591
"proc-macro2",
1575
2592
"quote",
1576
-
"syn",
2593
+
"syn 2.0.105",
1577
2594
]
1578
2595
1579
2596
[[package]]
1580
-
name = "openssl-probe"
1581
-
version = "0.1.6"
1582
-
source = "registry+https://github.com/rust-lang/crates.io-index"
1583
-
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
1584
-
1585
-
[[package]]
1586
2597
name = "openssl-sys"
1587
-
version = "0.9.109"
2598
+
version = "0.9.111"
1588
2599
source = "registry+https://github.com/rust-lang/crates.io-index"
1589
-
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
2600
+
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
1590
2601
dependencies = [
1591
2602
"cc",
1592
2603
"libc",
···
1595
2606
]
1596
2607
1597
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]]
1598
2633
name = "overload"
1599
2634
version = "0.1.1"
1600
2635
source = "registry+https://github.com/rust-lang/crates.io-index"
1601
2636
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
2637
+
2638
+
[[package]]
2639
+
name = "p256"
2640
+
version = "0.13.2"
2641
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2642
+
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
2643
+
dependencies = [
2644
+
"ecdsa",
2645
+
"elliptic-curve",
2646
+
"primeorder",
2647
+
"sha2",
2648
+
]
1602
2649
1603
2650
[[package]]
1604
2651
name = "parking"
···
1652
2699
1653
2700
[[package]]
1654
2701
name = "pds_gatekeeper"
1655
-
version = "0.1.0"
2702
+
version = "0.1.2"
1656
2703
dependencies = [
1657
2704
"anyhow",
2705
+
"aws-lc-rs",
1658
2706
"axum",
1659
2707
"axum-template",
1660
2708
"chrono",
1661
2709
"dotenvy",
1662
2710
"handlebars",
1663
2711
"hex",
2712
+
"html-escape",
1664
2713
"hyper-util",
2714
+
"jacquard-common",
2715
+
"jacquard-identity",
2716
+
"josekit",
1665
2717
"jwt-compact",
1666
2718
"lettre",
2719
+
"multibase",
1667
2720
"rand 0.9.2",
2721
+
"reqwest",
1668
2722
"rust-embed",
2723
+
"rustls",
1669
2724
"scrypt",
1670
2725
"serde",
1671
2726
"serde_json",
···
1676
2731
"tower_governor",
1677
2732
"tracing",
1678
2733
"tracing-subscriber",
2734
+
"url",
2735
+
"urlencoding",
1679
2736
]
1680
2737
1681
2738
[[package]]
···
1689
2746
1690
2747
[[package]]
1691
2748
name = "percent-encoding"
1692
-
version = "2.3.1"
2749
+
version = "2.3.2"
1693
2750
source = "registry+https://github.com/rust-lang/crates.io-index"
1694
-
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
2751
+
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
1695
2752
1696
2753
[[package]]
1697
2754
name = "pest"
···
1724
2781
"pest_meta",
1725
2782
"proc-macro2",
1726
2783
"quote",
1727
-
"syn",
2784
+
"syn 2.0.105",
1728
2785
]
1729
2786
1730
2787
[[package]]
···
1754
2811
dependencies = [
1755
2812
"proc-macro2",
1756
2813
"quote",
1757
-
"syn",
2814
+
"syn 2.0.105",
1758
2815
]
1759
2816
1760
2817
[[package]]
···
1770
2827
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
1771
2828
1772
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]]
1773
2841
name = "pkcs1"
1774
2842
version = "0.7.5"
1775
2843
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1797
2865
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
1798
2866
1799
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]]
1800
2882
name = "portable-atomic"
1801
2883
version = "1.11.1"
1802
2884
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1810
2892
dependencies = [
1811
2893
"zerovec",
1812
2894
]
2895
+
2896
+
[[package]]
2897
+
name = "powerfmt"
2898
+
version = "0.2.0"
2899
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2900
+
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
1813
2901
1814
2902
[[package]]
1815
2903
name = "ppv-lite86"
···
1821
2909
]
1822
2910
1823
2911
[[package]]
2912
+
name = "prettyplease"
2913
+
version = "0.2.35"
2914
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2915
+
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
2916
+
dependencies = [
2917
+
"proc-macro2",
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",
2952
+
]
2953
+
2954
+
[[package]]
1824
2955
name = "proc-macro2"
1825
2956
version = "1.0.97"
1826
2957
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1830
2961
]
1831
2962
1832
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]]
1833
2977
name = "psm"
1834
2978
version = "0.1.26"
1835
2979
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1848
2992
"libc",
1849
2993
"once_cell",
1850
2994
"raw-cpuid",
1851
-
"wasi 0.11.1+wasi-snapshot-preview1",
2995
+
"wasi",
1852
2996
"web-sys",
1853
2997
"winapi",
1854
2998
]
1855
2999
1856
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]]
1857
3056
name = "quote"
1858
3057
version = "1.0.40"
1859
3058
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1930
3129
source = "registry+https://github.com/rust-lang/crates.io-index"
1931
3130
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
1932
3131
dependencies = [
1933
-
"getrandom 0.3.3",
3132
+
"getrandom 0.3.4",
1934
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"
1935
3140
1936
3141
[[package]]
1937
3142
name = "raw-cpuid"
···
1952
3157
]
1953
3158
1954
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]]
1955
3180
name = "regex"
1956
-
version = "1.11.1"
3181
+
version = "1.12.2"
1957
3182
source = "registry+https://github.com/rust-lang/crates.io-index"
1958
-
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
3183
+
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
1959
3184
dependencies = [
1960
3185
"aho-corasick",
1961
3186
"memchr",
1962
-
"regex-automata 0.4.9",
3187
+
"regex-automata 0.4.13",
1963
3188
"regex-syntax 0.8.5",
1964
3189
]
1965
3190
···
1974
3199
1975
3200
[[package]]
1976
3201
name = "regex-automata"
1977
-
version = "0.4.9"
3202
+
version = "0.4.13"
1978
3203
source = "registry+https://github.com/rust-lang/crates.io-index"
1979
-
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
3204
+
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
1980
3205
dependencies = [
1981
3206
"aho-corasick",
1982
3207
"memchr",
···
1984
3209
]
1985
3210
1986
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]]
1987
3218
name = "regex-syntax"
1988
3219
version = "0.6.29"
1989
3220
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1996
3227
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
1997
3228
1998
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]]
1999
3285
name = "ring"
2000
3286
version = "0.17.14"
2001
3287
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2005
3291
"cfg-if",
2006
3292
"getrandom 0.2.16",
2007
3293
"libc",
2008
-
"untrusted",
3294
+
"untrusted 0.9.0",
2009
3295
"windows-sys 0.52.0",
2010
3296
]
2011
3297
···
2049
3335
"proc-macro2",
2050
3336
"quote",
2051
3337
"rust-embed-utils",
2052
-
"syn",
3338
+
"syn 2.0.105",
2053
3339
"walkdir",
2054
3340
]
2055
3341
···
2069
3355
version = "0.1.26"
2070
3356
source = "registry+https://github.com/rust-lang/crates.io-index"
2071
3357
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
3358
+
3359
+
[[package]]
3360
+
name = "rustc-hash"
3361
+
version = "1.1.0"
3362
+
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
2072
3370
2073
3371
[[package]]
2074
3372
name = "rustix"
2075
-
version = "1.0.8"
3373
+
version = "0.38.44"
2076
3374
source = "registry+https://github.com/rust-lang/crates.io-index"
2077
-
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
3375
+
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
3376
+
dependencies = [
3377
+
"bitflags",
3378
+
"errno",
3379
+
"libc",
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"
2078
3389
dependencies = [
2079
3390
"bitflags",
2080
3391
"errno",
2081
3392
"libc",
2082
-
"linux-raw-sys",
3393
+
"linux-raw-sys 0.11.0",
2083
3394
"windows-sys 0.59.0",
2084
3395
]
2085
3396
···
2089
3400
source = "registry+https://github.com/rust-lang/crates.io-index"
2090
3401
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
2091
3402
dependencies = [
3403
+
"aws-lc-rs",
3404
+
"log",
2092
3405
"once_cell",
2093
3406
"ring",
2094
3407
"rustls-pki-types",
···
2103
3416
source = "registry+https://github.com/rust-lang/crates.io-index"
2104
3417
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
2105
3418
dependencies = [
3419
+
"web-time",
2106
3420
"zeroize",
2107
3421
]
2108
3422
···
2112
3426
source = "registry+https://github.com/rust-lang/crates.io-index"
2113
3427
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
2114
3428
dependencies = [
3429
+
"aws-lc-rs",
2115
3430
"ring",
2116
3431
"rustls-pki-types",
2117
-
"untrusted",
3432
+
"untrusted 0.9.0",
2118
3433
]
2119
3434
2120
3435
[[package]]
···
2148
3463
]
2149
3464
2150
3465
[[package]]
2151
-
name = "schannel"
2152
-
version = "0.1.27"
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"
2153
3480
source = "registry+https://github.com/rust-lang/crates.io-index"
2154
-
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
3481
+
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
2155
3482
dependencies = [
2156
-
"windows-sys 0.59.0",
3483
+
"dyn-clone",
3484
+
"ref-cast",
3485
+
"serde",
3486
+
"serde_json",
2157
3487
]
2158
3488
2159
3489
[[package]]
···
2175
3505
]
2176
3506
2177
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]]
2178
3522
name = "secp256k1"
2179
3523
version = "0.28.2"
2180
3524
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2193
3537
]
2194
3538
2195
3539
[[package]]
2196
-
name = "security-framework"
2197
-
version = "2.11.1"
3540
+
name = "serde"
3541
+
version = "1.0.228"
2198
3542
source = "registry+https://github.com/rust-lang/crates.io-index"
2199
-
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
3543
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
2200
3544
dependencies = [
2201
-
"bitflags",
2202
-
"core-foundation",
2203
-
"core-foundation-sys",
2204
-
"libc",
2205
-
"security-framework-sys",
3545
+
"serde_core",
3546
+
"serde_derive",
2206
3547
]
2207
3548
2208
3549
[[package]]
2209
-
name = "security-framework-sys"
2210
-
version = "2.14.0"
3550
+
name = "serde_bytes"
3551
+
version = "0.11.19"
2211
3552
source = "registry+https://github.com/rust-lang/crates.io-index"
2212
-
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
3553
+
checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
2213
3554
dependencies = [
2214
-
"core-foundation-sys",
2215
-
"libc",
3555
+
"serde",
3556
+
"serde_core",
2216
3557
]
2217
3558
2218
3559
[[package]]
2219
-
name = "serde"
2220
-
version = "1.0.219"
3560
+
name = "serde_core"
3561
+
version = "1.0.228"
2221
3562
source = "registry+https://github.com/rust-lang/crates.io-index"
2222
-
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
3563
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
2223
3564
dependencies = [
2224
3565
"serde_derive",
2225
3566
]
2226
3567
2227
3568
[[package]]
2228
3569
name = "serde_derive"
2229
-
version = "1.0.219"
3570
+
version = "1.0.228"
2230
3571
source = "registry+https://github.com/rust-lang/crates.io-index"
2231
-
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
3572
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
2232
3573
dependencies = [
2233
3574
"proc-macro2",
2234
3575
"quote",
2235
-
"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",
2236
3602
]
2237
3603
2238
3604
[[package]]
2239
3605
name = "serde_json"
2240
-
version = "1.0.142"
3606
+
version = "1.0.145"
2241
3607
source = "registry+https://github.com/rust-lang/crates.io-index"
2242
-
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
3608
+
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
2243
3609
dependencies = [
3610
+
"indexmap 2.10.0",
2244
3611
"itoa",
2245
3612
"memchr",
2246
3613
"ryu",
2247
3614
"serde",
3615
+
"serde_core",
2248
3616
]
2249
3617
2250
3618
[[package]]
···
2258
3626
]
2259
3627
2260
3628
[[package]]
3629
+
name = "serde_repr"
3630
+
version = "0.1.20"
3631
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3632
+
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
3633
+
dependencies = [
3634
+
"proc-macro2",
3635
+
"quote",
3636
+
"syn 2.0.105",
3637
+
]
3638
+
3639
+
[[package]]
2261
3640
name = "serde_urlencoded"
2262
3641
version = "0.7.1"
2263
3642
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2270
3649
]
2271
3650
2272
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]]
2273
3683
name = "sha1"
2274
3684
version = "0.10.6"
2275
3685
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2326
3736
]
2327
3737
2328
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]]
2329
3745
name = "slab"
2330
3746
version = "0.4.11"
2331
3747
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2341
3757
]
2342
3758
2343
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]]
2344
3770
name = "socket2"
2345
3771
version = "0.6.0"
2346
3772
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2403
3829
"crc",
2404
3830
"crossbeam-queue",
2405
3831
"either",
2406
-
"event-listener",
3832
+
"event-listener 5.4.1",
2407
3833
"futures-core",
2408
3834
"futures-intrusive",
2409
3835
"futures-io",
2410
3836
"futures-util",
2411
3837
"hashbrown 0.15.5",
2412
3838
"hashlink",
2413
-
"indexmap",
3839
+
"indexmap 2.10.0",
2414
3840
"log",
2415
3841
"memchr",
2416
3842
"once_cell",
···
2438
3864
"quote",
2439
3865
"sqlx-core",
2440
3866
"sqlx-macros-core",
2441
-
"syn",
3867
+
"syn 2.0.105",
2442
3868
]
2443
3869
2444
3870
[[package]]
···
2449
3875
dependencies = [
2450
3876
"dotenvy",
2451
3877
"either",
2452
-
"heck",
3878
+
"heck 0.5.0",
2453
3879
"hex",
2454
3880
"once_cell",
2455
3881
"proc-macro2",
···
2461
3887
"sqlx-mysql",
2462
3888
"sqlx-postgres",
2463
3889
"sqlx-sqlite",
2464
-
"syn",
3890
+
"syn 2.0.105",
2465
3891
"tokio",
2466
3892
"url",
2467
3893
]
···
2592
4018
]
2593
4019
2594
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]]
2595
4047
name = "stringprep"
2596
4048
version = "0.1.5"
2597
4049
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2616
4068
2617
4069
[[package]]
2618
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"
2619
4082
version = "2.0.105"
2620
4083
source = "registry+https://github.com/rust-lang/crates.io-index"
2621
4084
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
···
2630
4093
version = "1.0.2"
2631
4094
source = "registry+https://github.com/rust-lang/crates.io-index"
2632
4095
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
4096
+
dependencies = [
4097
+
"futures-core",
4098
+
]
2633
4099
2634
4100
[[package]]
2635
4101
name = "synstructure"
···
2639
4105
dependencies = [
2640
4106
"proc-macro2",
2641
4107
"quote",
2642
-
"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",
2643
4120
]
2644
4121
2645
4122
[[package]]
2646
-
name = "tempfile"
2647
-
version = "3.21.0"
4123
+
name = "system-configuration-sys"
4124
+
version = "0.6.0"
2648
4125
source = "registry+https://github.com/rust-lang/crates.io-index"
2649
-
checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
4126
+
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
2650
4127
dependencies = [
2651
-
"fastrand",
2652
-
"getrandom 0.3.3",
2653
-
"once_cell",
2654
-
"rustix",
2655
-
"windows-sys 0.59.0",
4128
+
"core-foundation-sys",
4129
+
"libc",
2656
4130
]
2657
4131
2658
4132
[[package]]
···
2681
4155
dependencies = [
2682
4156
"proc-macro2",
2683
4157
"quote",
2684
-
"syn",
4158
+
"syn 2.0.105",
2685
4159
]
2686
4160
2687
4161
[[package]]
···
2692
4166
dependencies = [
2693
4167
"proc-macro2",
2694
4168
"quote",
2695
-
"syn",
4169
+
"syn 2.0.105",
2696
4170
]
2697
4171
2698
4172
[[package]]
···
2705
4179
]
2706
4180
2707
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]]
2708
4213
name = "tinystr"
2709
4214
version = "0.8.1"
2710
4215
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2756
4261
dependencies = [
2757
4262
"proc-macro2",
2758
4263
"quote",
2759
-
"syn",
4264
+
"syn 2.0.105",
2760
4265
]
2761
4266
2762
4267
[[package]]
2763
-
name = "tokio-native-tls"
2764
-
version = "0.3.1"
4268
+
name = "tokio-rustls"
4269
+
version = "0.26.2"
2765
4270
source = "registry+https://github.com/rust-lang/crates.io-index"
2766
-
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
4271
+
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
2767
4272
dependencies = [
2768
-
"native-tls",
4273
+
"rustls",
2769
4274
"tokio",
2770
4275
]
2771
4276
···
2782
4287
2783
4288
[[package]]
2784
4289
name = "tokio-util"
2785
-
version = "0.7.15"
4290
+
version = "0.7.17"
2786
4291
source = "registry+https://github.com/rust-lang/crates.io-index"
2787
-
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
4292
+
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
2788
4293
dependencies = [
2789
4294
"bytes",
2790
4295
"futures-core",
···
2830
4335
dependencies = [
2831
4336
"futures-core",
2832
4337
"futures-util",
2833
-
"indexmap",
4338
+
"indexmap 2.10.0",
2834
4339
"pin-project-lite",
2835
4340
"slab",
2836
4341
"sync_wrapper",
···
2851
4356
"bitflags",
2852
4357
"bytes",
2853
4358
"futures-core",
4359
+
"futures-util",
2854
4360
"http",
2855
4361
"http-body",
4362
+
"iri-string",
2856
4363
"pin-project-lite",
2857
4364
"tokio",
2858
4365
"tokio-util",
4366
+
"tower",
2859
4367
"tower-layer",
2860
4368
"tower-service",
2861
4369
]
···
2909
4417
dependencies = [
2910
4418
"proc-macro2",
2911
4419
"quote",
2912
-
"syn",
4420
+
"syn 2.0.105",
2913
4421
]
2914
4422
2915
4423
[[package]]
···
2952
4460
]
2953
4461
2954
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]]
2955
4474
name = "try-lock"
2956
4475
version = "0.2.5"
2957
4476
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2997
4516
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
2998
4517
2999
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]]
4537
+
name = "untrusted"
4538
+
version = "0.7.1"
4539
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4540
+
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
4541
+
4542
+
[[package]]
3000
4543
name = "untrusted"
3001
4544
version = "0.9.0"
3002
4545
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3004
4547
3005
4548
[[package]]
3006
4549
name = "url"
3007
-
version = "2.5.4"
4550
+
version = "2.5.7"
3008
4551
source = "registry+https://github.com/rust-lang/crates.io-index"
3009
-
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
4552
+
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
3010
4553
dependencies = [
3011
4554
"form_urlencoded",
3012
4555
"idna",
3013
4556
"percent-encoding",
4557
+
"serde",
3014
4558
]
3015
4559
3016
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]]
3017
4573
name = "utf8_iter"
3018
4574
version = "1.0.4"
3019
4575
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3024
4580
version = "0.1.1"
3025
4581
source = "registry+https://github.com/rust-lang/crates.io-index"
3026
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"
3027
4589
3028
4590
[[package]]
3029
4591
name = "vcpkg"
···
3063
4625
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
3064
4626
3065
4627
[[package]]
3066
-
name = "wasi"
3067
-
version = "0.14.2+wasi-0.2.4"
4628
+
name = "wasip2"
4629
+
version = "1.0.1+wasi-0.2.4"
3068
4630
source = "registry+https://github.com/rust-lang/crates.io-index"
3069
-
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
4631
+
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
3070
4632
dependencies = [
3071
-
"wit-bindgen-rt",
4633
+
"wit-bindgen",
3072
4634
]
3073
4635
3074
4636
[[package]]
···
3099
4661
"log",
3100
4662
"proc-macro2",
3101
4663
"quote",
3102
-
"syn",
4664
+
"syn 2.0.105",
3103
4665
"wasm-bindgen-shared",
3104
4666
]
3105
4667
3106
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]]
3107
4682
name = "wasm-bindgen-macro"
3108
4683
version = "0.2.100"
3109
4684
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3121
4696
dependencies = [
3122
4697
"proc-macro2",
3123
4698
"quote",
3124
-
"syn",
4699
+
"syn 2.0.105",
3125
4700
"wasm-bindgen-backend",
3126
4701
"wasm-bindgen-shared",
3127
4702
]
···
3136
4711
]
3137
4712
3138
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]]
3139
4727
name = "web-sys"
3140
4728
version = "0.3.77"
3141
4729
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3171
4759
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
3172
4760
dependencies = [
3173
4761
"rustls-pki-types",
4762
+
]
4763
+
4764
+
[[package]]
4765
+
name = "which"
4766
+
version = "4.4.2"
4767
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4768
+
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
4769
+
dependencies = [
4770
+
"either",
4771
+
"home",
4772
+
"once_cell",
4773
+
"rustix 0.38.44",
3174
4774
]
3175
4775
3176
4776
[[package]]
···
3222
4822
dependencies = [
3223
4823
"windows-implement",
3224
4824
"windows-interface",
3225
-
"windows-link",
4825
+
"windows-link 0.1.3",
3226
4826
"windows-result",
3227
4827
"windows-strings",
3228
4828
]
···
3235
4835
dependencies = [
3236
4836
"proc-macro2",
3237
4837
"quote",
3238
-
"syn",
4838
+
"syn 2.0.105",
3239
4839
]
3240
4840
3241
4841
[[package]]
···
3246
4846
dependencies = [
3247
4847
"proc-macro2",
3248
4848
"quote",
3249
-
"syn",
4849
+
"syn 2.0.105",
3250
4850
]
3251
4851
3252
4852
[[package]]
···
3256
4856
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
3257
4857
3258
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]]
3259
4876
name = "windows-result"
3260
4877
version = "0.3.4"
3261
4878
source = "registry+https://github.com/rust-lang/crates.io-index"
3262
4879
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
3263
4880
dependencies = [
3264
-
"windows-link",
4881
+
"windows-link 0.1.3",
3265
4882
]
3266
4883
3267
4884
[[package]]
···
3270
4887
source = "registry+https://github.com/rust-lang/crates.io-index"
3271
4888
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
3272
4889
dependencies = [
3273
-
"windows-link",
4890
+
"windows-link 0.1.3",
3274
4891
]
3275
4892
3276
4893
[[package]]
···
3301
4918
]
3302
4919
3303
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]]
3304
4930
name = "windows-targets"
3305
4931
version = "0.48.5"
3306
4932
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3422
5048
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
3423
5049
3424
5050
[[package]]
3425
-
name = "wit-bindgen-rt"
3426
-
version = "0.39.0"
5051
+
name = "wit-bindgen"
5052
+
version = "0.46.0"
3427
5053
source = "registry+https://github.com/rust-lang/crates.io-index"
3428
-
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
3429
-
dependencies = [
3430
-
"bitflags",
3431
-
]
5054
+
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
3432
5055
3433
5056
[[package]]
3434
5057
name = "writeable"
···
3437
5060
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
3438
5061
3439
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]]
3440
5069
name = "yoke"
3441
5070
version = "0.8.0"
3442
5071
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3456
5085
dependencies = [
3457
5086
"proc-macro2",
3458
5087
"quote",
3459
-
"syn",
5088
+
"syn 2.0.105",
3460
5089
"synstructure",
3461
5090
]
3462
5091
···
3477
5106
dependencies = [
3478
5107
"proc-macro2",
3479
5108
"quote",
3480
-
"syn",
5109
+
"syn 2.0.105",
3481
5110
]
3482
5111
3483
5112
[[package]]
···
3497
5126
dependencies = [
3498
5127
"proc-macro2",
3499
5128
"quote",
3500
-
"syn",
5129
+
"syn 2.0.105",
3501
5130
"synstructure",
3502
5131
]
3503
5132
···
3518
5147
dependencies = [
3519
5148
"proc-macro2",
3520
5149
"quote",
3521
-
"syn",
5150
+
"syn 2.0.105",
3522
5151
]
3523
5152
3524
5153
[[package]]
···
3551
5180
dependencies = [
3552
5181
"proc-macro2",
3553
5182
"quote",
3554
-
"syn",
5183
+
"syn 2.0.105",
3555
5184
]
3556
5185
3557
5186
[[package]]
+16
-4
Cargo.toml
+16
-4
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
+
license = "MIT"
5
6
6
7
[dependencies]
7
8
axum = { version = "0.8.4", features = ["macros", "json"] }
···
14
15
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
15
16
hyper-util = { version = "0.1.16", features = ["client", "client-legacy"] }
16
17
tower-http = { version = "0.6", features = ["cors", "compression-zstd"] }
17
-
tower_governor = "0.8.0"
18
+
tower_governor = { version = "0.8.0", features = ["axum", "tracing"] }
18
19
hex = "0.4"
19
20
jwt-compact = { version = "0.8.0", features = ["es256k"] }
20
21
scrypt = "0.11"
21
-
lettre = { version = "0.11.18", features = ["tokio1", "pool", "tokio1-native-tls"] }
22
+
#Leaveing these two cause I think it is needed by the email crate for ssl
23
+
aws-lc-rs = "1.13.0"
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", "sendmail-transport", "tokio1", "tokio1-rustls"] }
22
26
handlebars = { version = "6.3.2", features = ["rust-embed"] }
23
27
rust-embed = "8.7.2"
24
28
axum-template = { version = "3.0.0", features = ["handlebars"] }
25
29
rand = "0.9.2"
26
30
anyhow = "1.0.99"
27
-
chrono = "0.4.41"
31
+
chrono = { version = "0.4.42", features = ["default", "serde"] }
28
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"
+10
Dockerfile
+10
Dockerfile
···
1
+
FROM rust:1.89.0-bookworm AS builder
2
+
WORKDIR /app
3
+
COPY ../ /app
4
+
RUN cargo build --release
5
+
#
6
+
FROM rust:1.89-slim-bookworm AS api
7
+
RUN apt-get update
8
+
RUN apt-get install -y ca-certificates
9
+
COPY --from=builder /app/target/release/pds_gatekeeper /usr/local/bin/pds_gatekeeper
10
+
CMD ["pds_gatekeeper"]
+21
LICENSE.md
+21
LICENSE.md
···
1
+
MIT License
2
+
3
+
Copyright (c) 2025 Bailey Townsend
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy
6
+
of this software and associated documentation files (the "Software"), to deal
7
+
in the Software without restriction, including without limitation the rights
8
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+
copies of the Software, and to permit persons to whom the Software is
10
+
furnished to do so, subject to the following conditions:
11
+
12
+
The above copyright notice and this permission notice shall be included in all
13
+
copies or substantial portions of the Software.
14
+
15
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+
SOFTWARE.
+171
-12
README.md
+171
-12
README.md
···
15
15
- Overrides The login endpoint to add 2FA for both Bluesky client logged in and OAuth logins
16
16
- Overrides the settings endpoints as well. As long as you have a confirmed email you can turn on 2FA
17
17
18
-
## Captcha on Create Account
18
+
## Captcha on account creation
19
19
20
-
Future feature?
20
+
Require a `verificationCode` set on the `createAccount` request. This is gotten from completing a captcha challenge
21
+
hosted on the
22
+
PDS mimicking what the Bluesky Entryway does. Migration tools will need to support this, but social-apps will support
23
+
and redirect to `GATEKEEPER_DEFAULT_CAPTCHA_REDIRECT`. This is how the clients know to get the code to prove a captcha
24
+
was successful.
25
+
26
+
- Requires `GATEKEEPER_CREATE_ACCOUNT_CAPTCHA` to be set to true.
27
+
- Requires `PDS_HCAPTCHA_SITE_KEY` and `PDS_HCAPTCHA_SECRET_KEY` to be set. Can sign up at https://www.hcaptcha.com/
28
+
- Requires proxying `/xrpc/com.atproto.server.describeServer`, `/xrpc/com.atproto.server.createAccount` and `/gate/*` to
29
+
PDS
30
+
Gatekeeper
31
+
- Optional `GATEKEEPER_JWE_KEY` key to encrypt the captcha verification code. Defaults to a random 32 byte key. Not
32
+
strictly needed unless you're scaling
33
+
- Optional`GATEKEEPER_DEFAULT_CAPTCHA_REDIRECT` default redirect on captcha success. Defaults to `https://bsky.app`.
34
+
- Optional `GATEKEEPER_CAPTCHA_SUCCESS_REDIRECTS` allowed redirect urls for captcha success. You want these to match the
35
+
url showing the captcha. Defaults are:
36
+
- https://bsky.app
37
+
- https://pdsmoover.com
38
+
- https://blacksky.community
39
+
- https://tektite.cc
40
+
41
+
## Block account creation unless it's a migration
42
+
43
+
You can set `GATEKEEPER_ALLOW_ONLY_MIGRATIONS` to block createAccount unless it's via a migration. This does not require
44
+
a change for migration tools, but social-apps create a new account will no longer work and to create a brand new account
45
+
users will need to do this via the Oauth account create screen on the PDS. We recommend setting `PDS_HCAPTCHA_SITE_KEY`
46
+
and `PDS_HCAPTCHA_SECRET_KEY` so the OAuth screen is protected by a captcha if you use this with invite codes turned
47
+
off.
21
48
22
49
# Setup
23
50
24
-
We are getting close! Testing now
51
+
PDS Gatekeeper has 2 parts to its setup, docker compose file and a reverse proxy (Caddy in this case). I will be
52
+
assuming you setup the PDS following the directions
53
+
found [here](https://atproto.com/guides/self-hosting), but if yours is different, or you have questions, feel free to
54
+
let
55
+
me know, and we can figure it out.
56
+
57
+
## Docker compose
58
+
59
+
The pds gatekeeper container can be found on docker hub under the name `fatfingers23/pds_gatekeeper`. The container does
60
+
need access to the `/pds` root folder to access the same db's as your PDS. The part you need to add would look a bit
61
+
like below. You can find a full example of what I use for my pds at [./examples/compose.yml](./examples/compose.yml).
62
+
This is usually found at `/pds/compose.yaml`on your PDS>
63
+
64
+
```yml
65
+
gatekeeper:
66
+
container_name: gatekeeper
67
+
image: fatfingers23/pds_gatekeeper:latest
68
+
network_mode: host
69
+
restart: unless-stopped
70
+
#This gives the container to the access to the PDS folder. Source is the location on your server of that directory
71
+
volumes:
72
+
- type: bind
73
+
source: /pds
74
+
target: /pds
75
+
depends_on:
76
+
- pds
77
+
```
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
+
```
25
123
26
-
Nothing here yet! If you are brave enough to try before full release, let me know and I'll help you set it up.
27
-
But I want to run it locally on my own PDS first to test run it a bit.
124
+
## Caddy setup
28
125
29
-
Example Caddyfile (mostly so I don't lose it for now. Will have a better one in the future)
126
+
For the reverse proxy I use caddy. This part is what overwrites the endpoints and proxies them to PDS gatekeeper to add
127
+
in extra functionality. The main part is below, for a full example see [./examples/Caddyfile](./examples/Caddyfile).
128
+
This is usually found at `/pds/caddy/etc/caddy/Caddyfile` on your PDS.
30
129
31
-
```caddyfile
32
-
http://localhost {
130
+
```
131
+
@gatekeeper {
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/*
139
+
}
140
+
141
+
handle @gatekeeper {
142
+
reverse_proxy http://localhost:8080
143
+
}
33
144
145
+
reverse_proxy http://localhost:3000
146
+
```
147
+
148
+
If you use a cloudflare tunnel then your caddyfile would look a bit more like below with your tunnel proxying to
149
+
`localhost:8081` (or w/e port you want).
150
+
151
+
```
152
+
http://*.localhost:8082, http://localhost:8082 {
34
153
@gatekeeper {
35
154
path /xrpc/com.atproto.server.getSession
155
+
path /xrpc/com.atproto.server.describeServer
36
156
path /xrpc/com.atproto.server.updateEmail
37
157
path /xrpc/com.atproto.server.createSession
158
+
path /xrpc/com.atproto.server.createAccount
38
159
path /@atproto/oauth-provider/~api/sign-in
160
+
path /gate/*
39
161
}
40
162
41
163
handle @gatekeeper {
42
-
reverse_proxy http://localhost:8080
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}
43
168
}
169
+
reverse_proxy http://localhost:3000
170
+
}
171
+
172
+
```
173
+
174
+
# Environment variables and bonuses
175
+
176
+
Every environment variable can be set in the `pds.env` and shared between PDS and gatekeeper and the PDS, with the
177
+
exception of `PDS_ENV_LOCATION`. This can be set to load the pds.env, by default it checks `/pds/pds.env` and is
178
+
recommended to mount the `/pds` folder on the server to `/pds` in the pds gatekeeper container.
179
+
180
+
`PDS_DATA_DIRECTORY` - Root directory of the PDS. Same as the one found in `pds.env` this is how pds gatekeeper knows
181
+
knows the rest of the environment variables.
182
+
183
+
`GATEKEEPER_EMAIL_TEMPLATES_DIRECTORY` - The folder for templates of the emails PDS gatekeeper sends. You can find them
184
+
in [./email_templates](./email_templates). You are free to edit them as you please and set this variable to a location
185
+
in the pds gateekeper container and it will use them in place of the default ones. Just make sure ot keep the names the
186
+
same.
44
187
45
-
reverse_proxy /* http://localhost:3000
46
-
}
188
+
`GATEKEEPER_TWO_FACTOR_EMAIL_SUBJECT` - Subject of the email sent to the user when they turn on 2FA. Defaults to
189
+
`Sign in to Bluesky`
190
+
191
+
`PDS_BASE_URL` - Base url of the PDS. You most likely want `https://localhost:3000` which is also the default
192
+
193
+
`GATEKEEPER_HOST` - Host for pds gatekeeper. Defaults to `127.0.0.1`
194
+
195
+
`GATEKEEPER_PORT` - Port for pds gatekeeper. Defaults to `8080`
47
196
48
-
```
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
+
+30
examples/Caddyfile
+30
examples/Caddyfile
···
1
+
{
2
+
email youremail@myemail.com
3
+
on_demand_tls {
4
+
ask http://localhost:3000/tls-check
5
+
}
6
+
}
7
+
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.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
+
}
22
+
23
+
handle @gatekeeper {
24
+
#This is the address for PDS gatekeeper, default is 8080
25
+
reverse_proxy http://localhost:8080
26
+
}
27
+
28
+
reverse_proxy http://localhost:3000
29
+
#..here. Copy and paste this replacing the reverse_proxy http://localhost:3000 line
30
+
}
+51
examples/compose.yml
+51
examples/compose.yml
···
1
+
version: '3.9'
2
+
services:
3
+
caddy:
4
+
container_name: caddy
5
+
image: caddy:2
6
+
network_mode: host
7
+
depends_on:
8
+
- pds
9
+
restart: unless-stopped
10
+
volumes:
11
+
- type: bind
12
+
source: /pds/caddy/data
13
+
target: /data
14
+
- type: bind
15
+
source: /pds/caddy/etc/caddy
16
+
target: /etc/caddy
17
+
pds:
18
+
container_name: pds
19
+
image: ghcr.io/bluesky-social/pds:0.4
20
+
network_mode: host
21
+
restart: unless-stopped
22
+
volumes:
23
+
- type: bind
24
+
source: /pds
25
+
target: /pds
26
+
env_file:
27
+
- /pds/pds.env
28
+
watchtower:
29
+
container_name: watchtower
30
+
image: containrrr/watchtower:latest
31
+
network_mode: host
32
+
volumes:
33
+
- type: bind
34
+
source: /var/run/docker.sock
35
+
target: /var/run/docker.sock
36
+
restart: unless-stopped
37
+
environment:
38
+
WATCHTOWER_CLEANUP: true
39
+
WATCHTOWER_SCHEDULE: "@midnight"
40
+
gatekeeper:
41
+
container_name: gatekeeper
42
+
image: fatfingers23/pds_gatekeeper:latest
43
+
network_mode: host
44
+
restart: unless-stopped
45
+
#This gives the container to the access to the PDS folder. Source is the location on your server of that directory
46
+
volumes:
47
+
- type: bind
48
+
source: /pds
49
+
target: /pds
50
+
depends_on:
51
+
- pds
+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>
+6
justfile
+6
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
+
}
+177
-14
src/helpers.rs
+177
-14
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;
28
+
use std::sync::Arc;
18
29
use tracing::{error, log};
19
30
20
31
///Used to generate the email 2fa code
···
39
50
where
40
51
T: DeserializeOwned,
41
52
{
42
-
let uri = format!("{}{}", state.pds_base_url, path);
53
+
let uri = format!("{}{}", state.app_config.pds_base_url, path);
43
54
*req.uri_mut() = Uri::try_from(uri).map_err(|_| StatusCode::BAD_REQUEST)?;
44
55
45
56
let result = state
···
134
145
full_code.push(UPPERCASE_BASE32_CHARS[idx] as char);
135
146
}
136
147
137
-
//The PDS implementation creates in lowercase, then converts to uppercase.
138
-
//Just going a head and doing uppercase here.
139
-
let slice_one = &full_code[0..5].to_ascii_uppercase();
140
-
let slice_two = &full_code[5..10].to_ascii_uppercase();
148
+
let slice_one = &full_code[0..5];
149
+
let slice_two = &full_code[5..10];
141
150
format!("{slice_one}-{slice_two}")
142
151
}
143
152
···
337
346
338
347
let email_message = Message::builder()
339
348
//TODO prob get the proper type in the state
340
-
.from(state.mailer_from.parse()?)
349
+
.from(state.app_config.mailer_from.parse()?)
341
350
.to(email.parse()?)
342
-
.subject("Sign in to Bluesky")
351
+
.subject(&state.app_config.email_subject)
343
352
.multipart(
344
353
MultiPart::alternative() // This is composed of two parts.
345
354
.singlepart(
···
522
531
523
532
format!("{masked_local}@{masked_domain}")
524
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
+
}
+254
-40
src/main.rs
+254
-40
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_session, get_session, update_email};
4
-
use axum::body::Body;
5
-
use axum::handler::Handler;
6
-
use axum::http::{Method, header};
7
-
use axum::middleware as ax_middleware;
8
-
use axum::routing::post;
9
-
use axum::{Router, routing::get};
4
+
use crate::xrpc::com_atproto_server::{
5
+
create_account, create_session, describe_server, get_session, update_email,
6
+
};
7
+
use anyhow::{Result, Context};
8
+
use axum::{
9
+
Router,
10
+
body::Body,
11
+
handler::Handler,
12
+
http::{Method, header},
13
+
middleware as ax_middleware,
14
+
routing::get,
15
+
routing::post,
16
+
};
10
17
use axum_template::engine::Engine;
11
18
use handlebars::Handlebars;
12
-
use hyper_util::client::legacy::connect::HttpConnector;
13
-
use hyper_util::rt::TokioExecutor;
14
-
use lettre::{AsyncSmtpTransport, Tokio1Executor};
19
+
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
20
+
use jacquard_common::types::did::Did;
21
+
use jacquard_identity::{PublicResolver, resolver::PlcSource};
22
+
use lettre::{AsyncTransport, AsyncSmtpTransport, AsyncSendmailTransport, Message, Tokio1Executor};
23
+
use rand::Rng;
15
24
use rust_embed::RustEmbed;
16
25
use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode};
17
26
use sqlx::{SqlitePool, sqlite::SqlitePoolOptions};
18
27
use std::path::Path;
28
+
use std::sync::Arc;
19
29
use std::time::Duration;
20
30
use std::{env, net::SocketAddr};
21
-
use tower_governor::GovernorLayer;
22
-
use tower_governor::governor::GovernorConfigBuilder;
23
-
use tower_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 {
···
88
237
#[tokio::main]
89
238
async fn main() -> Result<(), Box<dyn std::error::Error>> {
90
239
setup_tracing();
91
-
//TODO may need to change where this reads from? Like an env variable for it's location? Or arg?
92
-
dotenvy::from_path(Path::new("./pds.env"))?;
93
-
let pds_root = env::var("PDS_DATA_DIRECTORY")?;
240
+
let pds_env_location =
241
+
env::var("PDS_ENV_LOCATION").unwrap_or_else(|_| "/pds/pds.env".to_string());
242
+
243
+
let result_of_finding_pds_env = dotenvy::from_path(Path::new(&pds_env_location));
244
+
if let Err(e) = result_of_finding_pds_env {
245
+
log::error!(
246
+
"Error loading pds.env file (ignore if you loaded your variables in the environment somehow else): {e}"
247
+
);
248
+
}
249
+
250
+
let pds_root =
251
+
env::var("PDS_DATA_DIRECTORY").expect("PDS_DATA_DIRECTORY is not set in your pds.env file");
94
252
let account_db_url = format!("{pds_root}/account.sqlite");
95
253
96
254
let account_options = SqliteConnectOptions::new()
···
125
283
.build(HttpConnector::new());
126
284
127
285
//Emailer set up
128
-
let smtp_url =
129
-
env::var("PDS_EMAIL_SMTP_URL").expect("PDS_EMAIL_SMTP_URL is not set in your pds.env file");
130
-
let sent_from = env::var("PDS_EMAIL_FROM_ADDRESS")
131
-
.expect("PDS_EMAIL_FROM_ADDRESS is not set in your pds.env file");
132
-
let mailer: AsyncSmtpTransport<Tokio1Executor> =
133
-
AsyncSmtpTransport::<Tokio1Executor>::from_url(smtp_url.as_str())?.build();
286
+
let mailer = Arc::new(build_mailer_from_env()?);
287
+
134
288
//Email templates setup
135
289
let mut hbs = Handlebars::new();
136
290
···
144
298
let _ = hbs.register_embed_templates::<EmailTemplates>();
145
299
}
146
300
147
-
let pds_base_url =
148
-
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());
149
311
150
312
let state = AppState {
151
313
account_pool,
152
314
pds_gatekeeper_pool,
153
315
reverse_proxy_client: client,
154
-
pds_base_url,
155
316
mailer,
156
-
mailer_from: sent_from,
157
317
template_engine: Engine::from(hbs),
318
+
resolver: Arc::new(resolver),
319
+
app_config: AppConfig::new(),
158
320
};
159
321
160
322
// Rate limiting
161
323
//Allows 5 within 60 seconds, and after 60 should drop one off? So hit 5, then goes to 4 after 60 seconds.
162
-
let create_session_governor_conf = GovernorConfigBuilder::default()
324
+
let captcha_governor_conf = GovernorConfigBuilder::default()
163
325
.per_second(60)
164
326
.burst_size(5)
327
+
.key_extractor(SmartIpKeyExtractor)
165
328
.finish()
166
-
.expect("failed to create governor config. this should not happen and is a bug");
329
+
.expect("failed to create governor config for create session. this should not happen and is a bug");
167
330
168
331
// Create a second config with the same settings for the other endpoint
169
332
let sign_in_governor_conf = GovernorConfigBuilder::default()
170
333
.per_second(60)
171
334
.burst_size(5)
335
+
.key_extractor(SmartIpKeyExtractor)
172
336
.finish()
173
-
.expect("failed to create governor config. this should not happen and is a bug");
337
+
.expect(
338
+
"failed to create governor config for sign in. this should not happen and is a bug",
339
+
);
340
+
341
+
let create_account_limiter_time: Option<String> =
342
+
env::var("GATEKEEPER_CREATE_ACCOUNT_PER_SECOND").ok();
343
+
let create_account_limiter_burst: Option<String> =
344
+
env::var("GATEKEEPER_CREATE_ACCOUNT_BURST").ok();
345
+
346
+
//Default should be 608 requests per 5 minutes, PDS is 300 per 500 so will never hit it ideally
347
+
let mut create_account_governor_conf = GovernorConfigBuilder::default();
348
+
if create_account_limiter_time.is_some() {
349
+
let time = create_account_limiter_time
350
+
.expect("GATEKEEPER_CREATE_ACCOUNT_PER_SECOND not set")
351
+
.parse::<u64>()
352
+
.expect("GATEKEEPER_CREATE_ACCOUNT_PER_SECOND must be a valid integer");
353
+
create_account_governor_conf.per_second(time);
354
+
}
355
+
356
+
if create_account_limiter_burst.is_some() {
357
+
let burst = create_account_limiter_burst
358
+
.expect("GATEKEEPER_CREATE_ACCOUNT_BURST not set")
359
+
.parse::<u32>()
360
+
.expect("GATEKEEPER_CREATE_ACCOUNT_BURST must be a valid integer");
361
+
create_account_governor_conf.burst_size(burst);
362
+
}
363
+
364
+
let create_account_governor_conf = create_account_governor_conf
365
+
.key_extractor(SmartIpKeyExtractor)
366
+
.finish().expect(
367
+
"failed to create governor config for create account. this should not happen and is a bug",
368
+
);
174
369
175
-
let create_session_governor_limiter = create_session_governor_conf.limiter().clone();
370
+
let captcha_governor_limiter = captcha_governor_conf.limiter().clone();
176
371
let sign_in_governor_limiter = sign_in_governor_conf.limiter().clone();
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);
375
+
177
376
let interval = Duration::from_secs(60);
178
377
// a separate background task to clean up
179
378
std::thread::spawn(move || {
180
379
loop {
181
380
std::thread::sleep(interval);
182
-
create_session_governor_limiter.retain_recent();
381
+
captcha_governor_limiter.retain_recent();
183
382
sign_in_governor_limiter.retain_recent();
383
+
create_account_governor_limiter.retain_recent();
184
384
}
185
385
});
186
386
···
189
389
.allow_methods([Method::GET, Method::OPTIONS, Method::POST])
190
390
.allow_headers(Any);
191
391
192
-
let app = Router::new()
392
+
let mut app = Router::new()
193
393
.route("/", get(root_handler))
394
+
.route("/xrpc/com.atproto.server.getSession", get(get_session))
194
395
.route(
195
-
"/xrpc/com.atproto.server.getSession",
196
-
get(get_session).layer(ax_middleware::from_fn(middleware::extract_did)),
396
+
"/xrpc/com.atproto.server.describeServer",
397
+
get(describe_server),
197
398
)
198
399
.route(
199
400
"/xrpc/com.atproto.server.updateEmail",
···
201
402
)
202
403
.route(
203
404
"/@atproto/oauth-provider/~api/sign-in",
204
-
post(sign_in).layer(GovernorLayer::new(sign_in_governor_conf)),
405
+
post(sign_in).layer(sign_in_governor_layer.clone()),
205
406
)
206
407
.route(
207
408
"/xrpc/com.atproto.server.createSession",
208
-
post(create_session.layer(GovernorLayer::new(create_session_governor_conf))),
409
+
post(create_session.layer(sign_in_governor_layer)),
209
410
)
411
+
.route(
412
+
"/xrpc/com.atproto.server.createAccount",
413
+
post(create_account).layer(GovernorLayer::new(create_account_governor_conf)),
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
210
424
.layer(CompressionLayer::new())
211
425
.layer(cors)
212
426
.with_state(state);
+73
-39
src/middleware.rs
+73
-39
src/middleware.rs
···
12
12
#[derive(Clone, Debug)]
13
13
pub struct Did(pub Option<String>);
14
14
15
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16
+
pub enum AuthScheme {
17
+
Bearer,
18
+
DPoP,
19
+
}
20
+
15
21
#[derive(Serialize, Deserialize)]
16
22
pub struct TokenClaims {
17
23
pub sub: String,
18
24
}
19
25
20
26
pub async fn extract_did(mut req: Request, next: Next) -> impl IntoResponse {
21
-
let token = extract_bearer(req.headers());
27
+
let auth = extract_auth(req.headers());
22
28
23
-
match token {
24
-
Ok(token) => {
25
-
match token {
29
+
match auth {
30
+
Ok(auth_opt) => {
31
+
match auth_opt {
26
32
None => json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "")
27
33
.expect("Error creating an error response"),
28
-
Some(token) => {
29
-
let token = UntrustedToken::new(&token);
30
-
if token.is_err() {
31
-
return json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "")
32
-
.expect("Error creating an error response");
33
-
}
34
-
let parsed_token = token.expect("Already checked for error");
35
-
let claims: Result<Claims<TokenClaims>, ValidationError> =
36
-
parsed_token.deserialize_claims_unchecked();
37
-
if claims.is_err() {
38
-
return json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "")
39
-
.expect("Error creating an error response");
40
-
}
34
+
Some((scheme, token_str)) => {
35
+
// For Bearer, validate JWT and extract DID from `sub`.
36
+
// For DPoP, we currently only pass through and do not validate here; insert None DID.
37
+
match scheme {
38
+
AuthScheme::Bearer => {
39
+
let token = UntrustedToken::new(&token_str);
40
+
if token.is_err() {
41
+
return json_error_response(
42
+
StatusCode::BAD_REQUEST,
43
+
"TokenRequired",
44
+
"",
45
+
)
46
+
.expect("Error creating an error response");
47
+
}
48
+
let parsed_token = token.expect("Already checked for error");
49
+
let claims: Result<Claims<TokenClaims>, ValidationError> =
50
+
parsed_token.deserialize_claims_unchecked();
51
+
if claims.is_err() {
52
+
return json_error_response(
53
+
StatusCode::BAD_REQUEST,
54
+
"TokenRequired",
55
+
"",
56
+
)
57
+
.expect("Error creating an error response");
58
+
}
41
59
42
-
let key = Hs256Key::new(
43
-
env::var("PDS_JWT_SECRET").expect("PDS_JWT_SECRET not set in the pds.env"),
44
-
);
45
-
let token: Result<Token<TokenClaims>, ValidationError> =
46
-
Hs256.validator(&key).validate(&parsed_token);
47
-
if token.is_err() {
48
-
return json_error_response(StatusCode::BAD_REQUEST, "InvalidToken", "")
49
-
.expect("Error creating an error response");
60
+
let key = Hs256Key::new(
61
+
env::var("PDS_JWT_SECRET")
62
+
.expect("PDS_JWT_SECRET not set in the pds.env"),
63
+
);
64
+
let token: Result<Token<TokenClaims>, ValidationError> =
65
+
Hs256.validator(&key).validate(&parsed_token);
66
+
if token.is_err() {
67
+
return json_error_response(
68
+
StatusCode::BAD_REQUEST,
69
+
"InvalidToken",
70
+
"",
71
+
)
72
+
.expect("Error creating an error response");
73
+
}
74
+
let token = token.expect("Already checked for error,");
75
+
req.extensions_mut()
76
+
.insert(Did(Some(token.claims().custom.sub.clone())));
77
+
}
78
+
AuthScheme::DPoP => {
79
+
//Not going to worry about oauth email update for now, just always forward to the PDS
80
+
req.extensions_mut().insert(Did(None));
81
+
}
50
82
}
51
-
let token = token.expect("Already checked for error,");
52
-
//Not going to worry about expiration since it still goes to the PDS
53
-
req.extensions_mut()
54
-
.insert(Did(Some(token.claims().custom.sub.clone())));
83
+
55
84
next.run(req).await
56
85
}
57
86
}
···
64
93
}
65
94
}
66
95
67
-
fn extract_bearer(headers: &HeaderMap) -> Result<Option<String>, String> {
96
+
fn extract_auth(headers: &HeaderMap) -> Result<Option<(AuthScheme, String)>, String> {
68
97
match headers.get(axum::http::header::AUTHORIZATION) {
69
98
None => Ok(None),
70
-
Some(hv) => match hv.to_str() {
71
-
Err(_) => Err("Authorization header is not valid".into()),
72
-
Ok(s) => {
73
-
// Accept forms like: "Bearer <token>" (case-sensitive for the scheme here)
74
-
let mut parts = s.splitn(2, ' ');
75
-
match (parts.next(), parts.next()) {
76
-
(Some("Bearer"), Some(tok)) if !tok.is_empty() => Ok(Some(tok.to_string())),
77
-
_ => Err("Authorization header must be in format 'Bearer <token>'".into()),
99
+
Some(hv) => {
100
+
match hv.to_str() {
101
+
Err(_) => Err("Authorization header is not valid".into()),
102
+
Ok(s) => {
103
+
// Accept forms like: "Bearer <token>" or "DPoP <token>" (case-sensitive for the scheme here)
104
+
let mut parts = s.splitn(2, ' ');
105
+
match (parts.next(), parts.next()) {
106
+
(Some("Bearer"), Some(tok)) if !tok.is_empty() =>
107
+
Ok(Some((AuthScheme::Bearer, tok.to_string()))),
108
+
(Some("DPoP"), Some(tok)) if !tok.is_empty() =>
109
+
Ok(Some((AuthScheme::DPoP, tok.to_string()))),
110
+
_ => Err("Authorization header must be in format 'Bearer <token>' or 'DPoP <token>'".into()),
111
+
}
78
112
}
79
113
}
80
-
},
114
+
}
81
115
}
82
116
}
+4
-6
src/oauth_provider.rs
+4
-6
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>,
···
36
37
"Invalid identifier or password",
37
38
),
38
39
AuthResult::TwoFactorRequired(masked_email) => {
39
-
// Email sending step can be handled here if needed in the future.
40
-
41
-
// {"error":"second_authentication_factor_required","error_description":"emailOtp authentication factor required (hint: 2***0@p***m)","type":"emailOtp","hint":"2***0@p***m"}
42
40
let body_str = match serde_json::to_string(&serde_json::json!({
43
41
"error": "second_authentication_factor_required",
44
42
"error_description": format!("emailOtp authentication factor required (hint: {})", masked_email),
···
59
57
//No 2FA or already passed
60
58
let uri = format!(
61
59
"{}{}",
62
-
state.pds_base_url, "/@atproto/oauth-provider/~api/sign-in"
60
+
state.app_config.pds_base_url, "/@atproto/oauth-provider/~api/sign-in"
63
61
);
64
62
65
63
let mut req = axum::http::Request::post(uri);
···
97
95
},
98
96
Err(err) => {
99
97
log::error!(
100
-
"Error during pre-auth check. This happens on the create_session endpoint when trying to decide if the user has access:\n {err}"
98
+
"Error during pre-auth check. This happens on the oauth signin endpoint when trying to decide if the user has access:\n {err}"
101
99
);
102
100
oauth_json_error_response(
103
101
StatusCode::BAD_REQUEST,
+394
-53
src/xrpc/com_atproto_server.rs
+394
-53
src/xrpc/com_atproto_server.rs
···
1
1
use crate::AppState;
2
2
use crate::helpers::{
3
-
AuthResult, ProxiedResult, TokenCheckError, json_error_response, preauth_check, proxy_get_json,
3
+
AuthResult, ProxiedResult, TokenCheckError, VerifyServiceAuthError, json_error_response,
4
+
preauth_check, proxy_get_json, verify_gate_token, verify_service_auth,
4
5
};
5
6
use crate::middleware::Did;
6
-
use axum::body::Body;
7
+
use axum::body::{Body, to_bytes};
7
8
use axum::extract::State;
8
-
use axum::http::{HeaderMap, StatusCode};
9
+
use axum::http::{HeaderMap, StatusCode, header};
9
10
use axum::response::{IntoResponse, Response};
10
11
use axum::{Extension, Json, debug_handler, extract, extract::Request};
12
+
use chrono::{Duration, Utc};
13
+
use jacquard_common::types::did::Did as JacquardDid;
11
14
use serde::{Deserialize, Serialize};
12
15
use serde_json;
13
16
use tracing::log;
···
61
64
allow_takendown: Option<bool>,
62
65
}
63
66
67
+
#[derive(Deserialize, Serialize, Debug)]
68
+
#[serde(rename_all = "camelCase")]
69
+
pub struct CreateAccountRequest {
70
+
handle: String,
71
+
#[serde(skip_serializing_if = "Option::is_none")]
72
+
email: Option<String>,
73
+
#[serde(skip_serializing_if = "Option::is_none")]
74
+
password: Option<String>,
75
+
#[serde(skip_serializing_if = "Option::is_none")]
76
+
did: Option<String>,
77
+
#[serde(skip_serializing_if = "Option::is_none")]
78
+
invite_code: Option<String>,
79
+
#[serde(skip_serializing_if = "Option::is_none")]
80
+
verification_code: Option<String>,
81
+
#[serde(skip_serializing_if = "Option::is_none")]
82
+
plc_op: Option<serde_json::Value>,
83
+
}
84
+
85
+
#[derive(Deserialize, Serialize, Debug, Clone)]
86
+
#[serde(rename_all = "camelCase")]
87
+
pub struct DescribeServerContact {
88
+
#[serde(skip_serializing_if = "Option::is_none")]
89
+
email: Option<String>,
90
+
}
91
+
92
+
#[derive(Deserialize, Serialize, Debug, Clone)]
93
+
#[serde(rename_all = "camelCase")]
94
+
pub struct DescribeServerLinks {
95
+
#[serde(skip_serializing_if = "Option::is_none")]
96
+
privacy_policy: Option<String>,
97
+
#[serde(skip_serializing_if = "Option::is_none")]
98
+
terms_of_service: Option<String>,
99
+
}
100
+
101
+
#[derive(Deserialize, Serialize, Debug, Clone)]
102
+
#[serde(rename_all = "camelCase")]
103
+
pub struct DescribeServerResponse {
104
+
#[serde(skip_serializing_if = "Option::is_none")]
105
+
invite_code_required: Option<bool>,
106
+
#[serde(skip_serializing_if = "Option::is_none")]
107
+
phone_verification_required: Option<bool>,
108
+
#[serde(skip_serializing_if = "Option::is_none")]
109
+
available_user_domains: Option<Vec<String>>,
110
+
#[serde(skip_serializing_if = "Option::is_none")]
111
+
links: Option<DescribeServerLinks>,
112
+
#[serde(skip_serializing_if = "Option::is_none")]
113
+
contact: Option<DescribeServerContact>,
114
+
#[serde(skip_serializing_if = "Option::is_none")]
115
+
did: Option<String>,
116
+
}
117
+
64
118
pub async fn create_session(
65
119
State(state): State<AppState>,
66
120
headers: HeaderMap,
···
87
141
)
88
142
}
89
143
AuthResult::ProxyThrough => {
90
-
log::info!("Proxying through");
91
144
//No 2FA or already passed
92
145
let uri = format!(
93
146
"{}{}",
94
-
state.pds_base_url, "/xrpc/com.atproto.server.createSession"
147
+
state.app_config.pds_base_url, "/xrpc/com.atproto.server.createSession"
95
148
);
96
149
97
150
let mut req = axum::http::Request::post(uri);
···
148
201
//If email auth is set it is to either turn on or off 2fa
149
202
let email_auth_update = payload.email_auth_factor.unwrap_or(false);
150
203
151
-
// Email update asked for
152
-
if email_auth_update {
153
-
let email = payload.email.clone();
154
-
let email_confirmed = sqlx::query_as::<_, (String,)>(
155
-
"SELECT did FROM account WHERE emailConfirmedAt IS NOT NULL AND email = ?",
156
-
)
157
-
.bind(&email)
158
-
.fetch_optional(&state.account_pool)
159
-
.await
160
-
.map_err(|_| StatusCode::BAD_REQUEST)?;
161
-
162
-
//Since the email is already confirmed we can enable 2fa
163
-
return match email_confirmed {
164
-
None => Err(StatusCode::BAD_REQUEST),
165
-
Some(did_row) => {
166
-
let _ = sqlx::query(
167
-
"INSERT INTO two_factor_accounts (did, required) VALUES (?, 1) ON CONFLICT(did) DO UPDATE SET required = 1",
168
-
)
169
-
.bind(&did_row.0)
170
-
.execute(&state.pds_gatekeeper_pool)
171
-
.await
172
-
.map_err(|_| StatusCode::BAD_REQUEST)?;
173
-
174
-
Ok(StatusCode::OK.into_response())
175
-
}
176
-
};
177
-
}
204
+
//This means the middleware successfully extracted a did from the request, if not it just needs to be forward to the PDS
205
+
//This is also empty if it is an oauth request, which is not supported by gatekeeper turning on 2fa since the dpop stuff needs to be implemented
206
+
let did_is_not_empty = did.0.is_some();
178
207
179
-
// User wants auth turned off
180
-
if !email_auth_update && !email_auth_not_set {
181
-
//User wants auth turned off and has a token
182
-
if let Some(token) = &payload.token {
183
-
let token_found = sqlx::query_as::<_, (String,)>(
184
-
"SELECT token FROM email_token WHERE token = ? AND did = ? AND purpose = 'update_email'",
208
+
if did_is_not_empty {
209
+
// Email update asked for
210
+
if email_auth_update {
211
+
let email = payload.email.clone();
212
+
let email_confirmed = match sqlx::query_as::<_, (String,)>(
213
+
"SELECT did FROM account WHERE emailConfirmedAt IS NOT NULL AND email = ?",
185
214
)
186
-
.bind(token)
187
-
.bind(&did.0)
215
+
.bind(&email)
188
216
.fetch_optional(&state.account_pool)
189
217
.await
190
-
.map_err(|_| StatusCode::BAD_REQUEST)?;
218
+
{
219
+
Ok(row) => row,
220
+
Err(err) => {
221
+
log::error!("Error checking if email is confirmed: {err}");
222
+
return Err(StatusCode::BAD_REQUEST);
223
+
}
224
+
};
191
225
192
-
if token_found.is_some() {
193
-
let _ = sqlx::query(
194
-
"INSERT INTO two_factor_accounts (did, required) VALUES (?, 0) ON CONFLICT(did) DO UPDATE SET required = 0",
226
+
//Since the email is already confirmed we can enable 2fa
227
+
return match email_confirmed {
228
+
None => Err(StatusCode::BAD_REQUEST),
229
+
Some(did_row) => {
230
+
let _ = sqlx::query(
231
+
"INSERT INTO two_factor_accounts (did, required) VALUES (?, 1) ON CONFLICT(did) DO UPDATE SET required = 1",
232
+
)
233
+
.bind(&did_row.0)
234
+
.execute(&state.pds_gatekeeper_pool)
235
+
.await
236
+
.map_err(|_| StatusCode::BAD_REQUEST)?;
237
+
238
+
Ok(StatusCode::OK.into_response())
239
+
}
240
+
};
241
+
}
242
+
243
+
// User wants auth turned off
244
+
if !email_auth_update && !email_auth_not_set {
245
+
//User wants auth turned off and has a token
246
+
if let Some(token) = &payload.token {
247
+
let token_found = match sqlx::query_as::<_, (String,)>(
248
+
"SELECT token FROM email_token WHERE token = ? AND did = ? AND purpose = 'update_email'",
195
249
)
196
-
.bind(&did.0)
197
-
.execute(&state.pds_gatekeeper_pool)
198
-
.await
199
-
.map_err(|_| StatusCode::BAD_REQUEST)?;
250
+
.bind(token)
251
+
.bind(&did.0)
252
+
.fetch_optional(&state.account_pool)
253
+
.await{
254
+
Ok(token) => token,
255
+
Err(err) => {
256
+
log::error!("Error checking if token is valid: {err}");
257
+
return Err(StatusCode::BAD_REQUEST);
258
+
}
259
+
};
200
260
201
-
return Ok(StatusCode::OK.into_response());
202
-
} else {
203
-
return Err(StatusCode::BAD_REQUEST);
261
+
return if token_found.is_some() {
262
+
//TODO I think there may be a bug here and need to do some retry logic
263
+
// First try was erroring, seconds was allowing
264
+
match sqlx::query(
265
+
"INSERT INTO two_factor_accounts (did, required) VALUES (?, 0) ON CONFLICT(did) DO UPDATE SET required = 0",
266
+
)
267
+
.bind(&did.0)
268
+
.execute(&state.pds_gatekeeper_pool)
269
+
.await {
270
+
Ok(_) => {}
271
+
Err(err) => {
272
+
log::error!("Error updating email auth: {err}");
273
+
return Err(StatusCode::BAD_REQUEST);
274
+
}
275
+
}
276
+
277
+
Ok(StatusCode::OK.into_response())
278
+
} else {
279
+
Err(StatusCode::BAD_REQUEST)
280
+
};
204
281
}
205
282
}
206
283
}
207
-
208
284
// Updating the actual email address by sending it on to the PDS
209
285
let uri = format!(
210
286
"{}{}",
211
-
state.pds_base_url, "/xrpc/com.atproto.server.updateEmail"
287
+
state.app_config.pds_base_url, "/xrpc/com.atproto.server.updateEmail"
212
288
);
213
289
let mut req = axum::http::Request::post(uri);
214
290
if let Some(req_headers) = req.headers_mut() {
···
260
336
ProxiedResult::Passthrough(resp) => Ok(resp),
261
337
}
262
338
}
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
+
425
+
pub async fn create_account(
426
+
State(state): State<AppState>,
427
+
req: Request,
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
581
+
let uri = format!(
582
+
"{}{}",
583
+
state.app_config.pds_base_url, "/xrpc/com.atproto.server.createAccount"
584
+
);
585
+
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)?;
594
+
595
+
let proxied = state
596
+
.reverse_proxy_client
597
+
.request(new_req)
598
+
.await
599
+
.map_err(|_| StatusCode::BAD_REQUEST)?
600
+
.into_response();
601
+
602
+
Ok(proxied)
603
+
}