implements a mirror using fjall, todo signature verification
+1151
-568
Cargo.lock
+1151
-568
Cargo.lock
···
3
version = 4
4
5
[[package]]
6
-
name = "addr2line"
7
-
version = "0.24.2"
8
-
source = "registry+https://github.com/rust-lang/crates.io-index"
9
-
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
10
-
dependencies = [
11
-
"gimli",
12
-
]
13
-
14
-
[[package]]
15
name = "adler2"
16
version = "2.0.1"
17
source = "registry+https://github.com/rust-lang/crates.io-index"
···
19
20
[[package]]
21
name = "aho-corasick"
22
-
version = "1.1.3"
23
source = "registry+https://github.com/rust-lang/crates.io-index"
24
-
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
25
dependencies = [
26
"memchr",
27
]
···
32
dependencies = [
33
"anyhow",
34
"async-compression",
35
"chrono",
36
"clap",
37
"futures",
38
"governor",
39
"http-body-util",
40
"log",
41
"native-tls",
42
"opentelemetry",
43
"opentelemetry-otlp",
···
47
"reqwest",
48
"reqwest-middleware",
49
"reqwest-retry",
50
"rustls",
51
"serde",
52
"serde_json",
53
-
"thiserror 2.0.16",
54
"tokio",
55
"tokio-postgres",
56
"tokio-stream",
···
76
]
77
78
[[package]]
79
name = "allocator-api2"
80
version = "0.2.21"
81
source = "registry+https://github.com/rust-lang/crates.io-index"
···
91
]
92
93
[[package]]
94
name = "anstream"
95
-
version = "0.6.20"
96
source = "registry+https://github.com/rust-lang/crates.io-index"
97
-
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
98
dependencies = [
99
"anstyle",
100
"anstyle-parse",
···
107
108
[[package]]
109
name = "anstyle"
110
-
version = "1.0.11"
111
source = "registry+https://github.com/rust-lang/crates.io-index"
112
-
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
113
114
[[package]]
115
name = "anstyle-parse"
···
122
123
[[package]]
124
name = "anstyle-query"
125
-
version = "1.1.4"
126
source = "registry+https://github.com/rust-lang/crates.io-index"
127
-
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
128
dependencies = [
129
-
"windows-sys 0.60.2",
130
]
131
132
[[package]]
133
name = "anstyle-wincon"
134
-
version = "3.0.10"
135
source = "registry+https://github.com/rust-lang/crates.io-index"
136
-
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
137
dependencies = [
138
"anstyle",
139
"once_cell_polyfill",
140
-
"windows-sys 0.60.2",
141
]
142
143
[[package]]
144
name = "anyhow"
145
-
version = "1.0.99"
146
source = "registry+https://github.com/rust-lang/crates.io-index"
147
-
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
148
149
[[package]]
150
name = "asn1-rs"
···
158
"nom",
159
"num-traits",
160
"rusticata-macros",
161
-
"thiserror 2.0.16",
162
"time",
163
]
164
···
187
188
[[package]]
189
name = "async-compression"
190
-
version = "0.4.30"
191
source = "registry+https://github.com/rust-lang/crates.io-index"
192
-
checksum = "977eb15ea9efd848bb8a4a1a2500347ed7f0bf794edf0dc3ddcf439f43d36b23"
193
dependencies = [
194
"compression-codecs",
195
"compression-core",
196
-
"futures-core",
197
"futures-io",
198
"pin-project-lite",
199
"tokio",
···
224
225
[[package]]
226
name = "aws-lc-rs"
227
-
version = "1.14.0"
228
source = "registry+https://github.com/rust-lang/crates.io-index"
229
-
checksum = "94b8ff6c09cd57b16da53641caa860168b88c172a5ee163b0288d3d6eea12786"
230
dependencies = [
231
"aws-lc-sys",
232
"zeroize",
···
234
235
[[package]]
236
name = "aws-lc-sys"
237
-
version = "0.31.0"
238
source = "registry+https://github.com/rust-lang/crates.io-index"
239
-
checksum = "0e44d16778acaf6a9ec9899b92cebd65580b83f685446bf2e1f5d3d732f99dcd"
240
dependencies = [
241
-
"bindgen",
242
"cc",
243
"cmake",
244
"dunce",
···
246
]
247
248
[[package]]
249
-
name = "backtrace"
250
-
version = "0.3.75"
251
source = "registry+https://github.com/rust-lang/crates.io-index"
252
-
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
253
dependencies = [
254
-
"addr2line",
255
-
"cfg-if",
256
-
"libc",
257
-
"miniz_oxide",
258
-
"object",
259
-
"rustc-demangle",
260
-
"windows-targets 0.52.6",
261
]
262
263
[[package]]
···
267
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
268
269
[[package]]
270
-
name = "bindgen"
271
-
version = "0.72.1"
272
source = "registry+https://github.com/rust-lang/crates.io-index"
273
-
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
274
dependencies = [
275
-
"bitflags 2.9.4",
276
-
"cexpr",
277
-
"clang-sys",
278
-
"itertools",
279
-
"log",
280
-
"prettyplease",
281
-
"proc-macro2",
282
-
"quote",
283
-
"regex",
284
-
"rustc-hash",
285
-
"shlex",
286
-
"syn",
287
]
288
289
[[package]]
···
294
295
[[package]]
296
name = "bitflags"
297
-
version = "2.9.4"
298
source = "registry+https://github.com/rust-lang/crates.io-index"
299
-
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
300
301
[[package]]
302
name = "block-buffer"
···
330
331
[[package]]
332
name = "bumpalo"
333
-
version = "3.19.0"
334
source = "registry+https://github.com/rust-lang/crates.io-index"
335
-
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
336
337
[[package]]
338
name = "byteorder"
···
341
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
342
343
[[package]]
344
name = "bytes"
345
-
version = "1.10.1"
346
source = "registry+https://github.com/rust-lang/crates.io-index"
347
-
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
348
349
[[package]]
350
name = "cc"
351
-
version = "1.2.37"
352
source = "registry+https://github.com/rust-lang/crates.io-index"
353
-
checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
354
dependencies = [
355
"find-msvc-tools",
356
"jobserver",
···
359
]
360
361
[[package]]
362
-
name = "cexpr"
363
-
version = "0.6.0"
364
-
source = "registry+https://github.com/rust-lang/crates.io-index"
365
-
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
366
-
dependencies = [
367
-
"nom",
368
-
]
369
-
370
-
[[package]]
371
name = "cfg-if"
372
-
version = "1.0.3"
373
source = "registry+https://github.com/rust-lang/crates.io-index"
374
-
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
375
376
[[package]]
377
name = "cfg_aliases"
···
381
382
[[package]]
383
name = "chrono"
384
-
version = "0.4.42"
385
source = "registry+https://github.com/rust-lang/crates.io-index"
386
-
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
387
dependencies = [
388
"iana-time-zone",
389
"js-sys",
390
"num-traits",
391
"serde",
392
"wasm-bindgen",
393
-
"windows-link 0.2.0",
394
]
395
396
[[package]]
397
-
name = "clang-sys"
398
-
version = "1.8.1"
399
source = "registry+https://github.com/rust-lang/crates.io-index"
400
-
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
401
dependencies = [
402
-
"glob",
403
-
"libc",
404
-
"libloading",
405
]
406
407
[[package]]
408
name = "clap"
409
-
version = "4.5.47"
410
source = "registry+https://github.com/rust-lang/crates.io-index"
411
-
checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931"
412
dependencies = [
413
"clap_builder",
414
"clap_derive",
···
416
417
[[package]]
418
name = "clap_builder"
419
-
version = "4.5.47"
420
source = "registry+https://github.com/rust-lang/crates.io-index"
421
-
checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6"
422
dependencies = [
423
"anstream",
424
"anstyle",
···
428
429
[[package]]
430
name = "clap_derive"
431
-
version = "4.5.47"
432
source = "registry+https://github.com/rust-lang/crates.io-index"
433
-
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
434
dependencies = [
435
"heck",
436
"proc-macro2",
···
440
441
[[package]]
442
name = "clap_lex"
443
-
version = "0.7.5"
444
source = "registry+https://github.com/rust-lang/crates.io-index"
445
-
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
446
447
[[package]]
448
name = "cmake"
449
-
version = "0.1.54"
450
source = "registry+https://github.com/rust-lang/crates.io-index"
451
-
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
452
dependencies = [
453
"cc",
454
]
···
460
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
461
462
[[package]]
463
name = "compression-codecs"
464
-
version = "0.4.30"
465
source = "registry+https://github.com/rust-lang/crates.io-index"
466
-
checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64"
467
dependencies = [
468
"brotli",
469
"compression-core",
···
475
476
[[package]]
477
name = "compression-core"
478
-
version = "0.4.29"
479
source = "registry+https://github.com/rust-lang/crates.io-index"
480
-
checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb"
481
482
[[package]]
483
name = "core-foundation"
···
506
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
507
508
[[package]]
509
name = "cpufeatures"
510
version = "0.2.17"
511
source = "registry+https://github.com/rust-lang/crates.io-index"
···
524
]
525
526
[[package]]
527
name = "crossbeam-utils"
528
version = "0.8.21"
529
source = "registry+https://github.com/rust-lang/crates.io-index"
530
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
531
532
[[package]]
533
name = "crypto-common"
534
-
version = "0.1.6"
535
source = "registry+https://github.com/rust-lang/crates.io-index"
536
-
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
537
dependencies = [
538
"generic-array",
539
"typenum",
···
550
"hashbrown 0.14.5",
551
"lock_api",
552
"once_cell",
553
-
"parking_lot_core 0.9.11",
554
]
555
556
[[package]]
557
name = "data-encoding"
558
-
version = "2.9.0"
559
source = "registry+https://github.com/rust-lang/crates.io-index"
560
-
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
561
562
[[package]]
563
name = "der-parser"
···
575
576
[[package]]
577
name = "deranged"
578
-
version = "0.5.3"
579
source = "registry+https://github.com/rust-lang/crates.io-index"
580
-
checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc"
581
dependencies = [
582
"powerfmt",
583
]
···
626
]
627
628
[[package]]
629
name = "equivalent"
630
version = "1.0.2"
631
source = "registry+https://github.com/rust-lang/crates.io-index"
···
638
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
639
dependencies = [
640
"libc",
641
-
"windows-sys 0.61.0",
642
]
643
644
[[package]]
···
655
656
[[package]]
657
name = "find-msvc-tools"
658
-
version = "0.1.1"
659
source = "registry+https://github.com/rust-lang/crates.io-index"
660
-
checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
661
662
[[package]]
663
name = "flate2"
664
-
version = "1.1.2"
665
source = "registry+https://github.com/rust-lang/crates.io-index"
666
-
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
667
dependencies = [
668
"crc32fast",
669
"miniz_oxide",
670
]
671
672
[[package]]
···
682
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
683
684
[[package]]
685
name = "foreign-types"
686
version = "0.3.2"
687
source = "registry+https://github.com/rust-lang/crates.io-index"
···
713
714
[[package]]
715
name = "futures"
716
-
version = "0.3.31"
717
source = "registry+https://github.com/rust-lang/crates.io-index"
718
-
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
719
dependencies = [
720
"futures-channel",
721
"futures-core",
···
728
729
[[package]]
730
name = "futures-channel"
731
-
version = "0.3.31"
732
source = "registry+https://github.com/rust-lang/crates.io-index"
733
-
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
734
dependencies = [
735
"futures-core",
736
"futures-sink",
···
738
739
[[package]]
740
name = "futures-core"
741
-
version = "0.3.31"
742
source = "registry+https://github.com/rust-lang/crates.io-index"
743
-
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
744
745
[[package]]
746
name = "futures-executor"
747
-
version = "0.3.31"
748
source = "registry+https://github.com/rust-lang/crates.io-index"
749
-
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
750
dependencies = [
751
"futures-core",
752
"futures-task",
···
755
756
[[package]]
757
name = "futures-io"
758
-
version = "0.3.31"
759
source = "registry+https://github.com/rust-lang/crates.io-index"
760
-
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
761
762
[[package]]
763
name = "futures-macro"
764
-
version = "0.3.31"
765
source = "registry+https://github.com/rust-lang/crates.io-index"
766
-
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
767
dependencies = [
768
"proc-macro2",
769
"quote",
···
772
773
[[package]]
774
name = "futures-sink"
775
-
version = "0.3.31"
776
source = "registry+https://github.com/rust-lang/crates.io-index"
777
-
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
778
779
[[package]]
780
name = "futures-task"
781
-
version = "0.3.31"
782
source = "registry+https://github.com/rust-lang/crates.io-index"
783
-
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
784
785
[[package]]
786
name = "futures-timer"
···
790
791
[[package]]
792
name = "futures-util"
793
-
version = "0.3.31"
794
source = "registry+https://github.com/rust-lang/crates.io-index"
795
-
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
796
dependencies = [
797
"futures-channel",
798
"futures-core",
···
802
"futures-task",
803
"memchr",
804
"pin-project-lite",
805
-
"pin-utils",
806
"slab",
807
]
808
···
818
819
[[package]]
820
name = "getrandom"
821
-
version = "0.2.16"
822
source = "registry+https://github.com/rust-lang/crates.io-index"
823
-
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
824
dependencies = [
825
"cfg-if",
826
"js-sys",
···
831
832
[[package]]
833
name = "getrandom"
834
-
version = "0.3.3"
835
source = "registry+https://github.com/rust-lang/crates.io-index"
836
-
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
837
dependencies = [
838
"cfg-if",
839
"js-sys",
840
"libc",
841
"r-efi",
842
-
"wasi 0.14.5+wasi-0.2.4",
843
"wasm-bindgen",
844
]
845
846
[[package]]
847
-
name = "gimli"
848
-
version = "0.31.1"
849
source = "registry+https://github.com/rust-lang/crates.io-index"
850
-
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
851
-
852
-
[[package]]
853
-
name = "glob"
854
-
version = "0.3.3"
855
-
source = "registry+https://github.com/rust-lang/crates.io-index"
856
-
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
857
858
[[package]]
859
name = "governor"
860
-
version = "0.10.1"
861
source = "registry+https://github.com/rust-lang/crates.io-index"
862
-
checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19"
863
dependencies = [
864
"cfg-if",
865
"dashmap",
866
"futures-sink",
867
"futures-timer",
868
"futures-util",
869
-
"getrandom 0.3.3",
870
-
"hashbrown 0.15.5",
871
"nonzero_ext",
872
-
"parking_lot 0.12.4",
873
"portable-atomic",
874
"quanta",
875
"rand 0.9.2",
···
880
881
[[package]]
882
name = "h2"
883
-
version = "0.4.12"
884
source = "registry+https://github.com/rust-lang/crates.io-index"
885
-
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
886
dependencies = [
887
"atomic-waker",
888
"bytes",
···
895
"tokio",
896
"tokio-util",
897
"tracing",
898
]
899
900
[[package]]
···
909
source = "registry+https://github.com/rust-lang/crates.io-index"
910
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
911
dependencies = [
912
"allocator-api2",
913
"equivalent",
914
-
"foldhash",
915
]
916
917
[[package]]
···
955
956
[[package]]
957
name = "http"
958
-
version = "1.3.1"
959
source = "registry+https://github.com/rust-lang/crates.io-index"
960
-
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
961
dependencies = [
962
"bytes",
963
-
"fnv",
964
"itoa",
965
]
966
···
1001
1002
[[package]]
1003
name = "hyper"
1004
-
version = "1.7.0"
1005
source = "registry+https://github.com/rust-lang/crates.io-index"
1006
-
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
1007
dependencies = [
1008
"atomic-waker",
1009
"bytes",
···
1057
1058
[[package]]
1059
name = "hyper-util"
1060
-
version = "0.1.16"
1061
source = "registry+https://github.com/rust-lang/crates.io-index"
1062
-
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
1063
dependencies = [
1064
"base64",
1065
"bytes",
1066
"futures-channel",
1067
-
"futures-core",
1068
"futures-util",
1069
"http",
1070
"http-body",
···
1073
"libc",
1074
"percent-encoding",
1075
"pin-project-lite",
1076
-
"socket2 0.6.0",
1077
"system-configuration",
1078
"tokio",
1079
"tower-service",
···
1083
1084
[[package]]
1085
name = "iana-time-zone"
1086
-
version = "0.1.63"
1087
source = "registry+https://github.com/rust-lang/crates.io-index"
1088
-
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
1089
dependencies = [
1090
"android_system_properties",
1091
"core-foundation-sys",
···
1107
1108
[[package]]
1109
name = "icu_collections"
1110
-
version = "2.0.0"
1111
source = "registry+https://github.com/rust-lang/crates.io-index"
1112
-
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
1113
dependencies = [
1114
"displaydoc",
1115
"potential_utf",
···
1120
1121
[[package]]
1122
name = "icu_locale_core"
1123
-
version = "2.0.0"
1124
source = "registry+https://github.com/rust-lang/crates.io-index"
1125
-
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
1126
dependencies = [
1127
"displaydoc",
1128
"litemap",
···
1133
1134
[[package]]
1135
name = "icu_normalizer"
1136
-
version = "2.0.0"
1137
source = "registry+https://github.com/rust-lang/crates.io-index"
1138
-
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
1139
dependencies = [
1140
-
"displaydoc",
1141
"icu_collections",
1142
"icu_normalizer_data",
1143
"icu_properties",
···
1148
1149
[[package]]
1150
name = "icu_normalizer_data"
1151
-
version = "2.0.0"
1152
source = "registry+https://github.com/rust-lang/crates.io-index"
1153
-
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
1154
1155
[[package]]
1156
name = "icu_properties"
1157
-
version = "2.0.1"
1158
source = "registry+https://github.com/rust-lang/crates.io-index"
1159
-
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
1160
dependencies = [
1161
-
"displaydoc",
1162
"icu_collections",
1163
"icu_locale_core",
1164
"icu_properties_data",
1165
"icu_provider",
1166
-
"potential_utf",
1167
"zerotrie",
1168
"zerovec",
1169
]
1170
1171
[[package]]
1172
name = "icu_properties_data"
1173
-
version = "2.0.1"
1174
source = "registry+https://github.com/rust-lang/crates.io-index"
1175
-
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
1176
1177
[[package]]
1178
name = "icu_provider"
1179
-
version = "2.0.0"
1180
source = "registry+https://github.com/rust-lang/crates.io-index"
1181
-
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
1182
dependencies = [
1183
"displaydoc",
1184
"icu_locale_core",
1185
-
"stable_deref_trait",
1186
-
"tinystr",
1187
"writeable",
1188
"yoke",
1189
"zerofrom",
···
1192
]
1193
1194
[[package]]
1195
name = "idna"
1196
version = "1.1.0"
1197
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1214
1215
[[package]]
1216
name = "indexmap"
1217
-
version = "2.11.4"
1218
source = "registry+https://github.com/rust-lang/crates.io-index"
1219
-
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
1220
dependencies = [
1221
"equivalent",
1222
-
"hashbrown 0.15.5",
1223
]
1224
1225
[[package]]
···
1235
]
1236
1237
[[package]]
1238
-
name = "io-uring"
1239
-
version = "0.7.10"
1240
source = "registry+https://github.com/rust-lang/crates.io-index"
1241
-
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
1242
dependencies = [
1243
-
"bitflags 2.9.4",
1244
-
"cfg-if",
1245
-
"libc",
1246
]
1247
1248
[[package]]
···
1253
1254
[[package]]
1255
name = "iri-string"
1256
-
version = "0.7.8"
1257
source = "registry+https://github.com/rust-lang/crates.io-index"
1258
-
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
1259
dependencies = [
1260
"memchr",
1261
"serde",
···
1263
1264
[[package]]
1265
name = "is_terminal_polyfill"
1266
-
version = "1.70.1"
1267
source = "registry+https://github.com/rust-lang/crates.io-index"
1268
-
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
1269
1270
[[package]]
1271
name = "itertools"
···
1277
]
1278
1279
[[package]]
1280
name = "itoa"
1281
-
version = "1.0.15"
1282
source = "registry+https://github.com/rust-lang/crates.io-index"
1283
-
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1284
1285
[[package]]
1286
name = "jobserver"
···
1288
source = "registry+https://github.com/rust-lang/crates.io-index"
1289
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
1290
dependencies = [
1291
-
"getrandom 0.3.3",
1292
"libc",
1293
]
1294
1295
[[package]]
1296
name = "js-sys"
1297
-
version = "0.3.78"
1298
source = "registry+https://github.com/rust-lang/crates.io-index"
1299
-
checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
1300
dependencies = [
1301
"once_cell",
1302
"wasm-bindgen",
···
1309
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
1310
1311
[[package]]
1312
-
name = "libc"
1313
-
version = "0.2.175"
1314
source = "registry+https://github.com/rust-lang/crates.io-index"
1315
-
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
1316
1317
[[package]]
1318
-
name = "libloading"
1319
-
version = "0.8.9"
1320
source = "registry+https://github.com/rust-lang/crates.io-index"
1321
-
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
1322
-
dependencies = [
1323
-
"cfg-if",
1324
-
"windows-link 0.2.0",
1325
-
]
1326
1327
[[package]]
1328
name = "libredox"
1329
-
version = "0.1.9"
1330
source = "registry+https://github.com/rust-lang/crates.io-index"
1331
-
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
1332
dependencies = [
1333
-
"bitflags 2.9.4",
1334
"libc",
1335
-
"redox_syscall 0.5.17",
1336
]
1337
1338
[[package]]
···
1343
1344
[[package]]
1345
name = "litemap"
1346
-
version = "0.8.0"
1347
source = "registry+https://github.com/rust-lang/crates.io-index"
1348
-
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
1349
1350
[[package]]
1351
name = "lock_api"
1352
-
version = "0.4.13"
1353
source = "registry+https://github.com/rust-lang/crates.io-index"
1354
-
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
1355
dependencies = [
1356
-
"autocfg",
1357
"scopeguard",
1358
]
1359
1360
[[package]]
1361
name = "log"
1362
-
version = "0.4.28"
1363
source = "registry+https://github.com/rust-lang/crates.io-index"
1364
-
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
1365
1366
[[package]]
1367
name = "lru-slab"
···
1370
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
1371
1372
[[package]]
1373
name = "matchers"
1374
version = "0.2.0"
1375
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1390
1391
[[package]]
1392
name = "memchr"
1393
-
version = "2.7.5"
1394
source = "registry+https://github.com/rust-lang/crates.io-index"
1395
-
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
1396
1397
[[package]]
1398
name = "mime"
···
1413
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
1414
dependencies = [
1415
"adler2",
1416
]
1417
1418
[[package]]
1419
name = "mio"
1420
-
version = "1.0.4"
1421
source = "registry+https://github.com/rust-lang/crates.io-index"
1422
-
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
1423
dependencies = [
1424
"libc",
1425
"wasi 0.11.1+wasi-snapshot-preview1",
1426
-
"windows-sys 0.59.0",
1427
]
1428
1429
[[package]]
1430
name = "native-tls"
1431
-
version = "0.2.14"
1432
source = "registry+https://github.com/rust-lang/crates.io-index"
1433
-
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
1434
dependencies = [
1435
"libc",
1436
"log",
···
1438
"openssl-probe",
1439
"openssl-sys",
1440
"schannel",
1441
-
"security-framework 2.11.1",
1442
"security-framework-sys",
1443
"tempfile",
1444
]
···
1449
source = "registry+https://github.com/rust-lang/crates.io-index"
1450
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
1451
dependencies = [
1452
-
"bitflags 2.9.4",
1453
"cfg-if",
1454
"cfg_aliases",
1455
"libc",
···
1473
1474
[[package]]
1475
name = "nu-ansi-term"
1476
-
version = "0.50.1"
1477
source = "registry+https://github.com/rust-lang/crates.io-index"
1478
-
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
1479
dependencies = [
1480
-
"windows-sys 0.52.0",
1481
]
1482
1483
[[package]]
···
1492
1493
[[package]]
1494
name = "num-conv"
1495
-
version = "0.1.0"
1496
source = "registry+https://github.com/rust-lang/crates.io-index"
1497
-
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
1498
1499
[[package]]
1500
name = "num-integer"
···
1515
]
1516
1517
[[package]]
1518
-
name = "object"
1519
-
version = "0.36.7"
1520
source = "registry+https://github.com/rust-lang/crates.io-index"
1521
-
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
1522
dependencies = [
1523
-
"memchr",
1524
]
1525
1526
[[package]]
···
1540
1541
[[package]]
1542
name = "once_cell_polyfill"
1543
-
version = "1.70.1"
1544
source = "registry+https://github.com/rust-lang/crates.io-index"
1545
-
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
1546
1547
[[package]]
1548
name = "openssl"
1549
-
version = "0.10.73"
1550
source = "registry+https://github.com/rust-lang/crates.io-index"
1551
-
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
1552
dependencies = [
1553
-
"bitflags 2.9.4",
1554
"cfg-if",
1555
"foreign-types",
1556
"libc",
···
1572
1573
[[package]]
1574
name = "openssl-probe"
1575
-
version = "0.1.6"
1576
source = "registry+https://github.com/rust-lang/crates.io-index"
1577
-
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
1578
1579
[[package]]
1580
name = "openssl-sys"
1581
-
version = "0.9.109"
1582
source = "registry+https://github.com/rust-lang/crates.io-index"
1583
-
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
1584
dependencies = [
1585
"cc",
1586
"libc",
···
1598
"futures-sink",
1599
"js-sys",
1600
"pin-project-lite",
1601
-
"thiserror 2.0.16",
1602
"tracing",
1603
]
1604
···
1628
"opentelemetry_sdk",
1629
"prost",
1630
"reqwest",
1631
-
"thiserror 2.0.16",
1632
"tracing",
1633
]
1634
···
1657
"percent-encoding",
1658
"rand 0.9.2",
1659
"serde_json",
1660
-
"thiserror 2.0.16",
1661
"tokio",
1662
"tokio-stream",
1663
]
1664
1665
[[package]]
1666
name = "parking_lot"
1667
version = "0.11.2"
1668
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1675
1676
[[package]]
1677
name = "parking_lot"
1678
-
version = "0.12.4"
1679
source = "registry+https://github.com/rust-lang/crates.io-index"
1680
-
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
1681
dependencies = [
1682
"lock_api",
1683
-
"parking_lot_core 0.9.11",
1684
]
1685
1686
[[package]]
···
1699
1700
[[package]]
1701
name = "parking_lot_core"
1702
-
version = "0.9.11"
1703
source = "registry+https://github.com/rust-lang/crates.io-index"
1704
-
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
1705
dependencies = [
1706
"cfg-if",
1707
"libc",
1708
-
"redox_syscall 0.5.17",
1709
"smallvec",
1710
-
"windows-targets 0.52.6",
1711
]
1712
1713
[[package]]
1714
name = "pem"
1715
-
version = "3.0.5"
1716
source = "registry+https://github.com/rust-lang/crates.io-index"
1717
-
checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3"
1718
dependencies = [
1719
"base64",
1720
-
"serde",
1721
]
1722
1723
[[package]]
···
1728
1729
[[package]]
1730
name = "phf"
1731
-
version = "0.11.3"
1732
source = "registry+https://github.com/rust-lang/crates.io-index"
1733
-
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
1734
dependencies = [
1735
"phf_shared",
1736
]
1737
1738
[[package]]
1739
name = "phf_shared"
1740
-
version = "0.11.3"
1741
source = "registry+https://github.com/rust-lang/crates.io-index"
1742
-
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
1743
dependencies = [
1744
"siphasher",
1745
]
···
1783
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
1784
1785
[[package]]
1786
name = "poem"
1787
version = "3.1.12"
1788
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1800
"hyper-util",
1801
"mime",
1802
"nix",
1803
-
"parking_lot 0.12.4",
1804
"percent-encoding",
1805
"pin-project-lite",
1806
"poem-derive",
···
1815
"serde_urlencoded",
1816
"smallvec",
1817
"sync_wrapper",
1818
-
"thiserror 2.0.16",
1819
"tokio",
1820
"tokio-rustls",
1821
"tokio-util",
···
1838
1839
[[package]]
1840
name = "portable-atomic"
1841
-
version = "1.11.1"
1842
source = "registry+https://github.com/rust-lang/crates.io-index"
1843
-
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
1844
1845
[[package]]
1846
name = "postgres-native-tls"
1847
-
version = "0.5.1"
1848
source = "registry+https://github.com/rust-lang/crates.io-index"
1849
-
checksum = "a1f39498473c92f7b6820ae970382c1d83178a3454c618161cb772e8598d9f6f"
1850
dependencies = [
1851
"native-tls",
1852
"tokio",
···
1856
1857
[[package]]
1858
name = "postgres-protocol"
1859
-
version = "0.6.8"
1860
source = "registry+https://github.com/rust-lang/crates.io-index"
1861
-
checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54"
1862
dependencies = [
1863
"base64",
1864
"byteorder",
···
1874
1875
[[package]]
1876
name = "postgres-types"
1877
-
version = "0.2.9"
1878
source = "registry+https://github.com/rust-lang/crates.io-index"
1879
-
checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48"
1880
dependencies = [
1881
"bytes",
1882
"chrono",
1883
"fallible-iterator",
1884
"postgres-protocol",
1885
-
"serde",
1886
"serde_json",
1887
]
1888
1889
[[package]]
1890
name = "potential_utf"
1891
-
version = "0.1.3"
1892
source = "registry+https://github.com/rust-lang/crates.io-index"
1893
-
checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
1894
dependencies = [
1895
"zerovec",
1896
]
···
1931
1932
[[package]]
1933
name = "proc-macro2"
1934
-
version = "1.0.101"
1935
source = "registry+https://github.com/rust-lang/crates.io-index"
1936
-
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
1937
dependencies = [
1938
"unicode-ident",
1939
]
···
1955
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
1956
dependencies = [
1957
"anyhow",
1958
-
"itertools",
1959
"proc-macro2",
1960
"quote",
1961
"syn",
···
1977
]
1978
1979
[[package]]
1980
name = "quinn"
1981
version = "0.11.9"
1982
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1989
"quinn-udp",
1990
"rustc-hash",
1991
"rustls",
1992
-
"socket2 0.6.0",
1993
-
"thiserror 2.0.16",
1994
"tokio",
1995
"tracing",
1996
"web-time",
···
2003
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
2004
dependencies = [
2005
"bytes",
2006
-
"getrandom 0.3.3",
2007
"lru-slab",
2008
"rand 0.9.2",
2009
"ring",
···
2011
"rustls",
2012
"rustls-pki-types",
2013
"slab",
2014
-
"thiserror 2.0.16",
2015
"tinyvec",
2016
"tracing",
2017
"web-time",
···
2026
"cfg_aliases",
2027
"libc",
2028
"once_cell",
2029
-
"socket2 0.6.0",
2030
"tracing",
2031
"windows-sys 0.60.2",
2032
]
2033
2034
[[package]]
2035
name = "quote"
2036
-
version = "1.0.40"
2037
source = "registry+https://github.com/rust-lang/crates.io-index"
2038
-
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
2039
dependencies = [
2040
"proc-macro2",
2041
]
···
2064
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
2065
dependencies = [
2066
"rand_chacha 0.9.0",
2067
-
"rand_core 0.9.3",
2068
]
2069
2070
[[package]]
···
2084
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
2085
dependencies = [
2086
"ppv-lite86",
2087
-
"rand_core 0.9.3",
2088
]
2089
2090
[[package]]
···
2093
source = "registry+https://github.com/rust-lang/crates.io-index"
2094
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
2095
dependencies = [
2096
-
"getrandom 0.2.16",
2097
]
2098
2099
[[package]]
2100
name = "rand_core"
2101
-
version = "0.9.3"
2102
source = "registry+https://github.com/rust-lang/crates.io-index"
2103
-
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
2104
dependencies = [
2105
-
"getrandom 0.3.3",
2106
]
2107
2108
[[package]]
···
2111
source = "registry+https://github.com/rust-lang/crates.io-index"
2112
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
2113
dependencies = [
2114
-
"bitflags 2.9.4",
2115
]
2116
2117
[[package]]
···
2137
2138
[[package]]
2139
name = "redox_syscall"
2140
-
version = "0.5.17"
2141
source = "registry+https://github.com/rust-lang/crates.io-index"
2142
-
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
2143
dependencies = [
2144
-
"bitflags 2.9.4",
2145
]
2146
2147
[[package]]
2148
name = "regex"
2149
-
version = "1.11.2"
2150
source = "registry+https://github.com/rust-lang/crates.io-index"
2151
-
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
2152
dependencies = [
2153
"aho-corasick",
2154
"memchr",
···
2158
2159
[[package]]
2160
name = "regex-automata"
2161
-
version = "0.4.10"
2162
source = "registry+https://github.com/rust-lang/crates.io-index"
2163
-
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
2164
dependencies = [
2165
"aho-corasick",
2166
"memchr",
···
2169
2170
[[package]]
2171
name = "regex-syntax"
2172
-
version = "0.8.6"
2173
source = "registry+https://github.com/rust-lang/crates.io-index"
2174
-
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
2175
2176
[[package]]
2177
name = "reqwest"
2178
-
version = "0.12.23"
2179
source = "registry+https://github.com/rust-lang/crates.io-index"
2180
-
checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
2181
dependencies = [
2182
-
"async-compression",
2183
"base64",
2184
"bytes",
2185
"encoding_rs",
···
2246
"anyhow",
2247
"async-trait",
2248
"futures",
2249
-
"getrandom 0.2.16",
2250
"http",
2251
"hyper",
2252
"parking_lot 0.11.2",
···
2285
dependencies = [
2286
"cc",
2287
"cfg-if",
2288
-
"getrandom 0.2.16",
2289
"libc",
2290
"untrusted",
2291
"windows-sys 0.52.0",
2292
]
2293
2294
[[package]]
2295
-
name = "rustc-demangle"
2296
-
version = "0.1.26"
2297
source = "registry+https://github.com/rust-lang/crates.io-index"
2298
-
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
2299
2300
[[package]]
2301
name = "rustc-hash"
···
2314
2315
[[package]]
2316
name = "rustix"
2317
-
version = "1.1.2"
2318
source = "registry+https://github.com/rust-lang/crates.io-index"
2319
-
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
2320
dependencies = [
2321
-
"bitflags 2.9.4",
2322
"errno",
2323
"libc",
2324
"linux-raw-sys",
2325
-
"windows-sys 0.61.0",
2326
]
2327
2328
[[package]]
2329
name = "rustls"
2330
-
version = "0.23.32"
2331
source = "registry+https://github.com/rust-lang/crates.io-index"
2332
-
checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40"
2333
dependencies = [
2334
"aws-lc-rs",
2335
"log",
···
2343
2344
[[package]]
2345
name = "rustls-native-certs"
2346
-
version = "0.8.1"
2347
source = "registry+https://github.com/rust-lang/crates.io-index"
2348
-
checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3"
2349
dependencies = [
2350
"openssl-probe",
2351
"rustls-pki-types",
2352
"schannel",
2353
-
"security-framework 3.5.0",
2354
]
2355
2356
[[package]]
···
2364
2365
[[package]]
2366
name = "rustls-pki-types"
2367
-
version = "1.12.0"
2368
source = "registry+https://github.com/rust-lang/crates.io-index"
2369
-
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
2370
dependencies = [
2371
"web-time",
2372
"zeroize",
···
2374
2375
[[package]]
2376
name = "rustls-webpki"
2377
-
version = "0.103.5"
2378
source = "registry+https://github.com/rust-lang/crates.io-index"
2379
-
checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8"
2380
dependencies = [
2381
"aws-lc-rs",
2382
"ring",
···
2392
2393
[[package]]
2394
name = "ryu"
2395
-
version = "1.0.20"
2396
source = "registry+https://github.com/rust-lang/crates.io-index"
2397
-
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
2398
2399
[[package]]
2400
name = "schannel"
···
2402
source = "registry+https://github.com/rust-lang/crates.io-index"
2403
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
2404
dependencies = [
2405
-
"windows-sys 0.61.0",
2406
]
2407
2408
[[package]]
···
2413
2414
[[package]]
2415
name = "security-framework"
2416
-
version = "2.11.1"
2417
source = "registry+https://github.com/rust-lang/crates.io-index"
2418
-
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
2419
dependencies = [
2420
-
"bitflags 2.9.4",
2421
-
"core-foundation 0.9.4",
2422
"core-foundation-sys",
2423
"libc",
2424
"security-framework-sys",
2425
]
2426
2427
[[package]]
2428
-
name = "security-framework"
2429
-
version = "3.5.0"
2430
source = "registry+https://github.com/rust-lang/crates.io-index"
2431
-
checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a"
2432
dependencies = [
2433
-
"bitflags 2.9.4",
2434
-
"core-foundation 0.10.1",
2435
"core-foundation-sys",
2436
"libc",
2437
-
"security-framework-sys",
2438
]
2439
2440
[[package]]
2441
-
name = "security-framework-sys"
2442
-
version = "2.15.0"
2443
source = "registry+https://github.com/rust-lang/crates.io-index"
2444
-
checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
2445
-
dependencies = [
2446
-
"core-foundation-sys",
2447
-
"libc",
2448
-
]
2449
2450
[[package]]
2451
name = "serde"
2452
-
version = "1.0.226"
2453
source = "registry+https://github.com/rust-lang/crates.io-index"
2454
-
checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
2455
dependencies = [
2456
"serde_core",
2457
"serde_derive",
2458
]
2459
2460
[[package]]
2461
name = "serde_core"
2462
-
version = "1.0.226"
2463
source = "registry+https://github.com/rust-lang/crates.io-index"
2464
-
checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
2465
dependencies = [
2466
"serde_derive",
2467
]
2468
2469
[[package]]
2470
name = "serde_derive"
2471
-
version = "1.0.226"
2472
source = "registry+https://github.com/rust-lang/crates.io-index"
2473
-
checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
2474
dependencies = [
2475
"proc-macro2",
2476
"quote",
···
2479
2480
[[package]]
2481
name = "serde_json"
2482
-
version = "1.0.143"
2483
source = "registry+https://github.com/rust-lang/crates.io-index"
2484
-
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
2485
dependencies = [
2486
"itoa",
2487
"memchr",
2488
-
"ryu",
2489
"serde",
2490
]
2491
2492
[[package]]
···
2502
]
2503
2504
[[package]]
2505
name = "sha1"
2506
version = "0.10.6"
2507
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2540
2541
[[package]]
2542
name = "signal-hook-registry"
2543
-
version = "1.4.6"
2544
source = "registry+https://github.com/rust-lang/crates.io-index"
2545
-
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
2546
dependencies = [
2547
"libc",
2548
]
2549
2550
[[package]]
2551
name = "siphasher"
2552
-
version = "1.0.1"
2553
source = "registry+https://github.com/rust-lang/crates.io-index"
2554
-
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
2555
2556
[[package]]
2557
name = "slab"
2558
-
version = "0.4.11"
2559
source = "registry+https://github.com/rust-lang/crates.io-index"
2560
-
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
2561
2562
[[package]]
2563
name = "smallvec"
···
2567
2568
[[package]]
2569
name = "socket2"
2570
-
version = "0.5.10"
2571
source = "registry+https://github.com/rust-lang/crates.io-index"
2572
-
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
2573
dependencies = [
2574
"libc",
2575
-
"windows-sys 0.52.0",
2576
]
2577
2578
[[package]]
2579
-
name = "socket2"
2580
-
version = "0.6.0"
2581
source = "registry+https://github.com/rust-lang/crates.io-index"
2582
-
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
2583
dependencies = [
2584
-
"libc",
2585
-
"windows-sys 0.59.0",
2586
]
2587
2588
[[package]]
···
2596
2597
[[package]]
2598
name = "stable_deref_trait"
2599
-
version = "1.2.0"
2600
source = "registry+https://github.com/rust-lang/crates.io-index"
2601
-
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
2602
2603
[[package]]
2604
name = "stringprep"
···
2625
2626
[[package]]
2627
name = "syn"
2628
-
version = "2.0.106"
2629
source = "registry+https://github.com/rust-lang/crates.io-index"
2630
-
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
2631
dependencies = [
2632
"proc-macro2",
2633
"quote",
···
2656
2657
[[package]]
2658
name = "system-configuration"
2659
-
version = "0.6.1"
2660
source = "registry+https://github.com/rust-lang/crates.io-index"
2661
-
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
2662
dependencies = [
2663
-
"bitflags 2.9.4",
2664
"core-foundation 0.9.4",
2665
"system-configuration-sys",
2666
]
···
2677
2678
[[package]]
2679
name = "tempfile"
2680
-
version = "3.22.0"
2681
source = "registry+https://github.com/rust-lang/crates.io-index"
2682
-
checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53"
2683
dependencies = [
2684
"fastrand",
2685
-
"getrandom 0.3.3",
2686
"once_cell",
2687
"rustix",
2688
-
"windows-sys 0.61.0",
2689
]
2690
2691
[[package]]
···
2699
2700
[[package]]
2701
name = "thiserror"
2702
-
version = "2.0.16"
2703
source = "registry+https://github.com/rust-lang/crates.io-index"
2704
-
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
2705
dependencies = [
2706
-
"thiserror-impl 2.0.16",
2707
]
2708
2709
[[package]]
···
2719
2720
[[package]]
2721
name = "thiserror-impl"
2722
-
version = "2.0.16"
2723
source = "registry+https://github.com/rust-lang/crates.io-index"
2724
-
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
2725
dependencies = [
2726
"proc-macro2",
2727
"quote",
···
2739
2740
[[package]]
2741
name = "time"
2742
-
version = "0.3.44"
2743
source = "registry+https://github.com/rust-lang/crates.io-index"
2744
-
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
2745
dependencies = [
2746
"deranged",
2747
"itoa",
2748
"num-conv",
2749
"powerfmt",
2750
-
"serde",
2751
"time-core",
2752
"time-macros",
2753
]
2754
2755
[[package]]
2756
name = "time-core"
2757
-
version = "0.1.6"
2758
source = "registry+https://github.com/rust-lang/crates.io-index"
2759
-
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
2760
2761
[[package]]
2762
name = "time-macros"
2763
-
version = "0.2.24"
2764
source = "registry+https://github.com/rust-lang/crates.io-index"
2765
-
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
2766
dependencies = [
2767
"num-conv",
2768
"time-core",
···
2770
2771
[[package]]
2772
name = "tinystr"
2773
-
version = "0.8.1"
2774
source = "registry+https://github.com/rust-lang/crates.io-index"
2775
-
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
2776
dependencies = [
2777
"displaydoc",
2778
"zerovec",
2779
]
2780
2781
[[package]]
2782
name = "tinyvec"
2783
version = "1.10.0"
2784
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2795
2796
[[package]]
2797
name = "tokio"
2798
-
version = "1.47.1"
2799
source = "registry+https://github.com/rust-lang/crates.io-index"
2800
-
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
2801
dependencies = [
2802
-
"backtrace",
2803
"bytes",
2804
-
"io-uring",
2805
"libc",
2806
"mio",
2807
-
"parking_lot 0.12.4",
2808
"pin-project-lite",
2809
"signal-hook-registry",
2810
-
"slab",
2811
-
"socket2 0.6.0",
2812
"tokio-macros",
2813
-
"windows-sys 0.59.0",
2814
]
2815
2816
[[package]]
2817
name = "tokio-macros"
2818
-
version = "2.5.0"
2819
source = "registry+https://github.com/rust-lang/crates.io-index"
2820
-
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
2821
dependencies = [
2822
"proc-macro2",
2823
"quote",
···
2836
2837
[[package]]
2838
name = "tokio-postgres"
2839
-
version = "0.7.13"
2840
source = "registry+https://github.com/rust-lang/crates.io-index"
2841
-
checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0"
2842
dependencies = [
2843
"async-trait",
2844
"byteorder",
···
2847
"futures-channel",
2848
"futures-util",
2849
"log",
2850
-
"parking_lot 0.12.4",
2851
"percent-encoding",
2852
"phf",
2853
"pin-project-lite",
2854
"postgres-protocol",
2855
"postgres-types",
2856
"rand 0.9.2",
2857
-
"socket2 0.5.10",
2858
"tokio",
2859
"tokio-util",
2860
"whoami",
···
2862
2863
[[package]]
2864
name = "tokio-rustls"
2865
-
version = "0.26.2"
2866
source = "registry+https://github.com/rust-lang/crates.io-index"
2867
-
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
2868
dependencies = [
2869
"rustls",
2870
"tokio",
···
2872
2873
[[package]]
2874
name = "tokio-stream"
2875
-
version = "0.1.17"
2876
source = "registry+https://github.com/rust-lang/crates.io-index"
2877
-
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
2878
dependencies = [
2879
"futures-core",
2880
"pin-project-lite",
···
2883
2884
[[package]]
2885
name = "tokio-util"
2886
-
version = "0.7.16"
2887
source = "registry+https://github.com/rust-lang/crates.io-index"
2888
-
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
2889
dependencies = [
2890
"bytes",
2891
"futures-core",
···
2897
2898
[[package]]
2899
name = "toml_datetime"
2900
-
version = "0.7.2"
2901
source = "registry+https://github.com/rust-lang/crates.io-index"
2902
-
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
2903
dependencies = [
2904
"serde_core",
2905
]
2906
2907
[[package]]
2908
name = "toml_edit"
2909
-
version = "0.23.6"
2910
source = "registry+https://github.com/rust-lang/crates.io-index"
2911
-
checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b"
2912
dependencies = [
2913
"indexmap",
2914
"toml_datetime",
···
2918
2919
[[package]]
2920
name = "toml_parser"
2921
-
version = "1.0.3"
2922
source = "registry+https://github.com/rust-lang/crates.io-index"
2923
-
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
2924
dependencies = [
2925
"winnow",
2926
]
···
2948
2949
[[package]]
2950
name = "tower"
2951
-
version = "0.5.2"
2952
source = "registry+https://github.com/rust-lang/crates.io-index"
2953
-
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
2954
dependencies = [
2955
"futures-core",
2956
"futures-util",
···
2963
2964
[[package]]
2965
name = "tower-http"
2966
-
version = "0.6.6"
2967
source = "registry+https://github.com/rust-lang/crates.io-index"
2968
-
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
2969
dependencies = [
2970
-
"bitflags 2.9.4",
2971
"bytes",
2972
"futures-util",
2973
"http",
2974
"http-body",
2975
"iri-string",
2976
"pin-project-lite",
2977
"tower",
2978
"tower-layer",
2979
"tower-service",
···
2993
2994
[[package]]
2995
name = "tracing"
2996
-
version = "0.1.41"
2997
source = "registry+https://github.com/rust-lang/crates.io-index"
2998
-
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
2999
dependencies = [
3000
"pin-project-lite",
3001
"tracing-attributes",
···
3004
3005
[[package]]
3006
name = "tracing-attributes"
3007
-
version = "0.1.30"
3008
source = "registry+https://github.com/rust-lang/crates.io-index"
3009
-
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
3010
dependencies = [
3011
"proc-macro2",
3012
"quote",
···
3015
3016
[[package]]
3017
name = "tracing-core"
3018
-
version = "0.1.34"
3019
source = "registry+https://github.com/rust-lang/crates.io-index"
3020
-
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
3021
dependencies = [
3022
"once_cell",
3023
"valuable",
···
3054
3055
[[package]]
3056
name = "tracing-subscriber"
3057
-
version = "0.3.20"
3058
source = "registry+https://github.com/rust-lang/crates.io-index"
3059
-
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
3060
dependencies = [
3061
"matchers",
3062
"nu-ansi-term",
···
3077
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
3078
3079
[[package]]
3080
name = "typenum"
3081
-
version = "1.18.0"
3082
source = "registry+https://github.com/rust-lang/crates.io-index"
3083
-
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
3084
3085
[[package]]
3086
name = "uncased"
···
3099
3100
[[package]]
3101
name = "unicode-ident"
3102
-
version = "1.0.19"
3103
source = "registry+https://github.com/rust-lang/crates.io-index"
3104
-
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
3105
3106
[[package]]
3107
name = "unicode-normalization"
3108
-
version = "0.1.24"
3109
source = "registry+https://github.com/rust-lang/crates.io-index"
3110
-
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
3111
dependencies = [
3112
"tinyvec",
3113
]
3114
3115
[[package]]
3116
name = "unicode-properties"
3117
-
version = "0.1.3"
3118
source = "registry+https://github.com/rust-lang/crates.io-index"
3119
-
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
3120
3121
[[package]]
3122
name = "untrusted"
···
3126
3127
[[package]]
3128
name = "url"
3129
-
version = "2.5.7"
3130
source = "registry+https://github.com/rust-lang/crates.io-index"
3131
-
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
3132
dependencies = [
3133
"form_urlencoded",
3134
"idna",
···
3155
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
3156
3157
[[package]]
3158
name = "vcpkg"
3159
version = "0.2.15"
3160
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3167
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
3168
3169
[[package]]
3170
name = "want"
3171
version = "0.3.1"
3172
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3183
3184
[[package]]
3185
name = "wasi"
3186
-
version = "0.14.5+wasi-0.2.4"
3187
source = "registry+https://github.com/rust-lang/crates.io-index"
3188
-
checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4"
3189
dependencies = [
3190
"wasip2",
3191
]
3192
3193
[[package]]
3194
name = "wasip2"
3195
-
version = "1.0.0+wasi-0.2.4"
3196
source = "registry+https://github.com/rust-lang/crates.io-index"
3197
-
checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24"
3198
dependencies = [
3199
"wit-bindgen",
3200
]
3201
3202
[[package]]
3203
name = "wasite"
3204
-
version = "0.1.0"
3205
source = "registry+https://github.com/rust-lang/crates.io-index"
3206
-
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
3207
3208
[[package]]
3209
name = "wasm-bindgen"
3210
-
version = "0.2.101"
3211
source = "registry+https://github.com/rust-lang/crates.io-index"
3212
-
checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
3213
dependencies = [
3214
"cfg-if",
3215
"once_cell",
···
3219
]
3220
3221
[[package]]
3222
-
name = "wasm-bindgen-backend"
3223
-
version = "0.2.101"
3224
-
source = "registry+https://github.com/rust-lang/crates.io-index"
3225
-
checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
3226
-
dependencies = [
3227
-
"bumpalo",
3228
-
"log",
3229
-
"proc-macro2",
3230
-
"quote",
3231
-
"syn",
3232
-
"wasm-bindgen-shared",
3233
-
]
3234
-
3235
-
[[package]]
3236
name = "wasm-bindgen-futures"
3237
-
version = "0.4.51"
3238
source = "registry+https://github.com/rust-lang/crates.io-index"
3239
-
checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe"
3240
dependencies = [
3241
"cfg-if",
3242
"js-sys",
3243
"once_cell",
3244
"wasm-bindgen",
···
3247
3248
[[package]]
3249
name = "wasm-bindgen-macro"
3250
-
version = "0.2.101"
3251
source = "registry+https://github.com/rust-lang/crates.io-index"
3252
-
checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
3253
dependencies = [
3254
"quote",
3255
"wasm-bindgen-macro-support",
···
3257
3258
[[package]]
3259
name = "wasm-bindgen-macro-support"
3260
-
version = "0.2.101"
3261
source = "registry+https://github.com/rust-lang/crates.io-index"
3262
-
checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
3263
dependencies = [
3264
"proc-macro2",
3265
"quote",
3266
"syn",
3267
-
"wasm-bindgen-backend",
3268
"wasm-bindgen-shared",
3269
]
3270
3271
[[package]]
3272
name = "wasm-bindgen-shared"
3273
-
version = "0.2.101"
3274
source = "registry+https://github.com/rust-lang/crates.io-index"
3275
-
checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
3276
dependencies = [
3277
"unicode-ident",
3278
]
3279
3280
[[package]]
3281
name = "wasm-streams"
3282
version = "0.4.2"
3283
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3306
]
3307
3308
[[package]]
3309
name = "web-sys"
3310
-
version = "0.3.78"
3311
source = "registry+https://github.com/rust-lang/crates.io-index"
3312
-
checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12"
3313
dependencies = [
3314
"js-sys",
3315
"wasm-bindgen",
···
3327
3328
[[package]]
3329
name = "whoami"
3330
-
version = "1.6.1"
3331
source = "registry+https://github.com/rust-lang/crates.io-index"
3332
-
checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d"
3333
dependencies = [
3334
"libredox",
3335
"wasite",
3336
"web-sys",
3337
]
3338
3339
[[package]]
3340
name = "wildmatch"
3341
-
version = "2.5.0"
3342
source = "registry+https://github.com/rust-lang/crates.io-index"
3343
-
checksum = "39b7d07a236abaef6607536ccfaf19b396dbe3f5110ddb73d39f4562902ed382"
3344
3345
[[package]]
3346
name = "winapi"
···
3359
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
3360
3361
[[package]]
3362
name = "winapi-x86_64-pc-windows-gnu"
3363
version = "0.4.0"
3364
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3366
3367
[[package]]
3368
name = "windows-core"
3369
-
version = "0.61.2"
3370
source = "registry+https://github.com/rust-lang/crates.io-index"
3371
-
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
3372
dependencies = [
3373
"windows-implement",
3374
"windows-interface",
3375
-
"windows-link 0.1.3",
3376
"windows-result",
3377
"windows-strings",
3378
]
3379
3380
[[package]]
3381
name = "windows-implement"
3382
-
version = "0.60.0"
3383
source = "registry+https://github.com/rust-lang/crates.io-index"
3384
-
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
3385
dependencies = [
3386
"proc-macro2",
3387
"quote",
···
3390
3391
[[package]]
3392
name = "windows-interface"
3393
-
version = "0.59.1"
3394
source = "registry+https://github.com/rust-lang/crates.io-index"
3395
-
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
3396
dependencies = [
3397
"proc-macro2",
3398
"quote",
···
3401
3402
[[package]]
3403
name = "windows-link"
3404
-
version = "0.1.3"
3405
source = "registry+https://github.com/rust-lang/crates.io-index"
3406
-
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
3407
-
3408
-
[[package]]
3409
-
name = "windows-link"
3410
-
version = "0.2.0"
3411
-
source = "registry+https://github.com/rust-lang/crates.io-index"
3412
-
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
3413
3414
[[package]]
3415
name = "windows-registry"
3416
-
version = "0.5.3"
3417
source = "registry+https://github.com/rust-lang/crates.io-index"
3418
-
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
3419
dependencies = [
3420
-
"windows-link 0.1.3",
3421
"windows-result",
3422
"windows-strings",
3423
]
3424
3425
[[package]]
3426
name = "windows-result"
3427
-
version = "0.3.4"
3428
source = "registry+https://github.com/rust-lang/crates.io-index"
3429
-
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
3430
dependencies = [
3431
-
"windows-link 0.1.3",
3432
]
3433
3434
[[package]]
3435
name = "windows-strings"
3436
-
version = "0.4.2"
3437
source = "registry+https://github.com/rust-lang/crates.io-index"
3438
-
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
3439
dependencies = [
3440
-
"windows-link 0.1.3",
3441
]
3442
3443
[[package]]
···
3451
3452
[[package]]
3453
name = "windows-sys"
3454
-
version = "0.59.0"
3455
-
source = "registry+https://github.com/rust-lang/crates.io-index"
3456
-
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
3457
-
dependencies = [
3458
-
"windows-targets 0.52.6",
3459
-
]
3460
-
3461
-
[[package]]
3462
-
name = "windows-sys"
3463
version = "0.60.2"
3464
source = "registry+https://github.com/rust-lang/crates.io-index"
3465
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
3466
dependencies = [
3467
-
"windows-targets 0.53.3",
3468
]
3469
3470
[[package]]
3471
name = "windows-sys"
3472
-
version = "0.61.0"
3473
source = "registry+https://github.com/rust-lang/crates.io-index"
3474
-
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
3475
dependencies = [
3476
-
"windows-link 0.2.0",
3477
]
3478
3479
[[package]]
···
3494
3495
[[package]]
3496
name = "windows-targets"
3497
-
version = "0.53.3"
3498
source = "registry+https://github.com/rust-lang/crates.io-index"
3499
-
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
3500
dependencies = [
3501
-
"windows-link 0.1.3",
3502
-
"windows_aarch64_gnullvm 0.53.0",
3503
-
"windows_aarch64_msvc 0.53.0",
3504
-
"windows_i686_gnu 0.53.0",
3505
-
"windows_i686_gnullvm 0.53.0",
3506
-
"windows_i686_msvc 0.53.0",
3507
-
"windows_x86_64_gnu 0.53.0",
3508
-
"windows_x86_64_gnullvm 0.53.0",
3509
-
"windows_x86_64_msvc 0.53.0",
3510
]
3511
3512
[[package]]
···
3517
3518
[[package]]
3519
name = "windows_aarch64_gnullvm"
3520
-
version = "0.53.0"
3521
source = "registry+https://github.com/rust-lang/crates.io-index"
3522
-
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
3523
3524
[[package]]
3525
name = "windows_aarch64_msvc"
···
3529
3530
[[package]]
3531
name = "windows_aarch64_msvc"
3532
-
version = "0.53.0"
3533
source = "registry+https://github.com/rust-lang/crates.io-index"
3534
-
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
3535
3536
[[package]]
3537
name = "windows_i686_gnu"
···
3541
3542
[[package]]
3543
name = "windows_i686_gnu"
3544
-
version = "0.53.0"
3545
source = "registry+https://github.com/rust-lang/crates.io-index"
3546
-
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
3547
3548
[[package]]
3549
name = "windows_i686_gnullvm"
···
3553
3554
[[package]]
3555
name = "windows_i686_gnullvm"
3556
-
version = "0.53.0"
3557
source = "registry+https://github.com/rust-lang/crates.io-index"
3558
-
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
3559
3560
[[package]]
3561
name = "windows_i686_msvc"
···
3565
3566
[[package]]
3567
name = "windows_i686_msvc"
3568
-
version = "0.53.0"
3569
source = "registry+https://github.com/rust-lang/crates.io-index"
3570
-
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
3571
3572
[[package]]
3573
name = "windows_x86_64_gnu"
···
3577
3578
[[package]]
3579
name = "windows_x86_64_gnu"
3580
-
version = "0.53.0"
3581
source = "registry+https://github.com/rust-lang/crates.io-index"
3582
-
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
3583
3584
[[package]]
3585
name = "windows_x86_64_gnullvm"
···
3589
3590
[[package]]
3591
name = "windows_x86_64_gnullvm"
3592
-
version = "0.53.0"
3593
source = "registry+https://github.com/rust-lang/crates.io-index"
3594
-
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
3595
3596
[[package]]
3597
name = "windows_x86_64_msvc"
···
3601
3602
[[package]]
3603
name = "windows_x86_64_msvc"
3604
-
version = "0.53.0"
3605
source = "registry+https://github.com/rust-lang/crates.io-index"
3606
-
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
3607
3608
[[package]]
3609
name = "winnow"
3610
-
version = "0.7.13"
3611
source = "registry+https://github.com/rust-lang/crates.io-index"
3612
-
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
3613
dependencies = [
3614
"memchr",
3615
]
3616
3617
[[package]]
3618
name = "wit-bindgen"
3619
-
version = "0.45.1"
3620
source = "registry+https://github.com/rust-lang/crates.io-index"
3621
-
checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36"
3622
3623
[[package]]
3624
name = "writeable"
3625
-
version = "0.6.1"
3626
source = "registry+https://github.com/rust-lang/crates.io-index"
3627
-
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
3628
3629
[[package]]
3630
name = "x509-parser"
···
3639
"nom",
3640
"oid-registry",
3641
"rusticata-macros",
3642
-
"thiserror 2.0.16",
3643
"time",
3644
]
3645
3646
[[package]]
3647
name = "yasna"
···
3654
3655
[[package]]
3656
name = "yoke"
3657
-
version = "0.8.0"
3658
source = "registry+https://github.com/rust-lang/crates.io-index"
3659
-
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
3660
dependencies = [
3661
-
"serde",
3662
"stable_deref_trait",
3663
"yoke-derive",
3664
"zerofrom",
···
3666
3667
[[package]]
3668
name = "yoke-derive"
3669
-
version = "0.8.0"
3670
source = "registry+https://github.com/rust-lang/crates.io-index"
3671
-
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
3672
dependencies = [
3673
"proc-macro2",
3674
"quote",
···
3678
3679
[[package]]
3680
name = "zerocopy"
3681
-
version = "0.8.27"
3682
source = "registry+https://github.com/rust-lang/crates.io-index"
3683
-
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
3684
dependencies = [
3685
"zerocopy-derive",
3686
]
3687
3688
[[package]]
3689
name = "zerocopy-derive"
3690
-
version = "0.8.27"
3691
source = "registry+https://github.com/rust-lang/crates.io-index"
3692
-
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
3693
dependencies = [
3694
"proc-macro2",
3695
"quote",
···
3719
3720
[[package]]
3721
name = "zeroize"
3722
-
version = "1.8.1"
3723
source = "registry+https://github.com/rust-lang/crates.io-index"
3724
-
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
3725
3726
[[package]]
3727
name = "zerotrie"
3728
-
version = "0.2.2"
3729
source = "registry+https://github.com/rust-lang/crates.io-index"
3730
-
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
3731
dependencies = [
3732
"displaydoc",
3733
"yoke",
···
3736
3737
[[package]]
3738
name = "zerovec"
3739
-
version = "0.11.4"
3740
source = "registry+https://github.com/rust-lang/crates.io-index"
3741
-
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
3742
dependencies = [
3743
"yoke",
3744
"zerofrom",
···
3747
3748
[[package]]
3749
name = "zerovec-derive"
3750
-
version = "0.11.1"
3751
source = "registry+https://github.com/rust-lang/crates.io-index"
3752
-
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
3753
dependencies = [
3754
"proc-macro2",
3755
"quote",
3756
"syn",
3757
]
3758
3759
[[package]]
3760
name = "zstd"
···
3
version = 4
4
5
[[package]]
6
name = "adler2"
7
version = "2.0.1"
8
source = "registry+https://github.com/rust-lang/crates.io-index"
···
10
11
[[package]]
12
name = "aho-corasick"
13
+
version = "1.1.4"
14
source = "registry+https://github.com/rust-lang/crates.io-index"
15
+
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
16
dependencies = [
17
"memchr",
18
]
···
23
dependencies = [
24
"anyhow",
25
"async-compression",
26
+
"async-trait",
27
+
"bincode",
28
"chrono",
29
+
"cid",
30
"clap",
31
+
"criterion",
32
+
"data-encoding",
33
+
"fjall",
34
"futures",
35
"governor",
36
"http-body-util",
37
"log",
38
+
"multibase",
39
"native-tls",
40
"opentelemetry",
41
"opentelemetry-otlp",
···
45
"reqwest",
46
"reqwest-middleware",
47
"reqwest-retry",
48
+
"rmp-serde",
49
"rustls",
50
"serde",
51
+
"serde_bytes",
52
"serde_json",
53
+
"tempfile",
54
+
"thiserror 2.0.18",
55
"tokio",
56
"tokio-postgres",
57
"tokio-stream",
···
77
]
78
79
[[package]]
80
+
name = "alloca"
81
+
version = "0.4.0"
82
+
source = "registry+https://github.com/rust-lang/crates.io-index"
83
+
checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
84
+
dependencies = [
85
+
"cc",
86
+
]
87
+
88
+
[[package]]
89
name = "allocator-api2"
90
version = "0.2.21"
91
source = "registry+https://github.com/rust-lang/crates.io-index"
···
101
]
102
103
[[package]]
104
+
name = "anes"
105
+
version = "0.1.6"
106
+
source = "registry+https://github.com/rust-lang/crates.io-index"
107
+
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
108
+
109
+
[[package]]
110
name = "anstream"
111
+
version = "0.6.21"
112
source = "registry+https://github.com/rust-lang/crates.io-index"
113
+
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
114
dependencies = [
115
"anstyle",
116
"anstyle-parse",
···
123
124
[[package]]
125
name = "anstyle"
126
+
version = "1.0.13"
127
source = "registry+https://github.com/rust-lang/crates.io-index"
128
+
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
129
130
[[package]]
131
name = "anstyle-parse"
···
138
139
[[package]]
140
name = "anstyle-query"
141
+
version = "1.1.5"
142
source = "registry+https://github.com/rust-lang/crates.io-index"
143
+
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
144
dependencies = [
145
+
"windows-sys 0.61.2",
146
]
147
148
[[package]]
149
name = "anstyle-wincon"
150
+
version = "3.0.11"
151
source = "registry+https://github.com/rust-lang/crates.io-index"
152
+
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
153
dependencies = [
154
"anstyle",
155
"once_cell_polyfill",
156
+
"windows-sys 0.61.2",
157
]
158
159
[[package]]
160
name = "anyhow"
161
+
version = "1.0.102"
162
source = "registry+https://github.com/rust-lang/crates.io-index"
163
+
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
164
165
[[package]]
166
name = "asn1-rs"
···
174
"nom",
175
"num-traits",
176
"rusticata-macros",
177
+
"thiserror 2.0.18",
178
"time",
179
]
180
···
203
204
[[package]]
205
name = "async-compression"
206
+
version = "0.4.40"
207
source = "registry+https://github.com/rust-lang/crates.io-index"
208
+
checksum = "7d67d43201f4d20c78bcda740c142ca52482d81da80681533d33bf3f0596c8e2"
209
dependencies = [
210
"compression-codecs",
211
"compression-core",
212
"futures-io",
213
"pin-project-lite",
214
"tokio",
···
239
240
[[package]]
241
name = "aws-lc-rs"
242
+
version = "1.16.0"
243
source = "registry+https://github.com/rust-lang/crates.io-index"
244
+
checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9"
245
dependencies = [
246
"aws-lc-sys",
247
"zeroize",
···
249
250
[[package]]
251
name = "aws-lc-sys"
252
+
version = "0.37.1"
253
source = "registry+https://github.com/rust-lang/crates.io-index"
254
+
checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549"
255
dependencies = [
256
"cc",
257
"cmake",
258
"dunce",
···
260
]
261
262
[[package]]
263
+
name = "base-x"
264
+
version = "0.2.11"
265
source = "registry+https://github.com/rust-lang/crates.io-index"
266
+
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
267
+
268
+
[[package]]
269
+
name = "base256emoji"
270
+
version = "1.0.2"
271
+
source = "registry+https://github.com/rust-lang/crates.io-index"
272
+
checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c"
273
dependencies = [
274
+
"const-str",
275
+
"match-lookup",
276
]
277
278
[[package]]
···
282
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
283
284
[[package]]
285
+
name = "bincode"
286
+
version = "1.3.3"
287
source = "registry+https://github.com/rust-lang/crates.io-index"
288
+
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
289
dependencies = [
290
+
"serde",
291
]
292
293
[[package]]
···
298
299
[[package]]
300
name = "bitflags"
301
+
version = "2.11.0"
302
source = "registry+https://github.com/rust-lang/crates.io-index"
303
+
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
304
305
[[package]]
306
name = "block-buffer"
···
334
335
[[package]]
336
name = "bumpalo"
337
+
version = "3.20.2"
338
source = "registry+https://github.com/rust-lang/crates.io-index"
339
+
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
340
341
[[package]]
342
name = "byteorder"
···
345
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
346
347
[[package]]
348
+
name = "byteorder-lite"
349
+
version = "0.1.0"
350
+
source = "registry+https://github.com/rust-lang/crates.io-index"
351
+
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
352
+
353
+
[[package]]
354
name = "bytes"
355
+
version = "1.11.1"
356
source = "registry+https://github.com/rust-lang/crates.io-index"
357
+
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
358
+
359
+
[[package]]
360
+
name = "byteview"
361
+
version = "0.10.1"
362
+
source = "registry+https://github.com/rust-lang/crates.io-index"
363
+
checksum = "1c53ba0f290bfc610084c05582d9c5d421662128fc69f4bf236707af6fd321b9"
364
+
365
+
[[package]]
366
+
name = "cast"
367
+
version = "0.3.0"
368
+
source = "registry+https://github.com/rust-lang/crates.io-index"
369
+
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
370
371
[[package]]
372
name = "cc"
373
+
version = "1.2.56"
374
source = "registry+https://github.com/rust-lang/crates.io-index"
375
+
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
376
dependencies = [
377
"find-msvc-tools",
378
"jobserver",
···
381
]
382
383
[[package]]
384
name = "cfg-if"
385
+
version = "1.0.4"
386
source = "registry+https://github.com/rust-lang/crates.io-index"
387
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
388
389
[[package]]
390
name = "cfg_aliases"
···
394
395
[[package]]
396
name = "chrono"
397
+
version = "0.4.43"
398
source = "registry+https://github.com/rust-lang/crates.io-index"
399
+
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
400
dependencies = [
401
"iana-time-zone",
402
"js-sys",
403
"num-traits",
404
"serde",
405
"wasm-bindgen",
406
+
"windows-link",
407
+
]
408
+
409
+
[[package]]
410
+
name = "ciborium"
411
+
version = "0.2.2"
412
+
source = "registry+https://github.com/rust-lang/crates.io-index"
413
+
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
414
+
dependencies = [
415
+
"ciborium-io",
416
+
"ciborium-ll",
417
+
"serde",
418
+
]
419
+
420
+
[[package]]
421
+
name = "ciborium-io"
422
+
version = "0.2.2"
423
+
source = "registry+https://github.com/rust-lang/crates.io-index"
424
+
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
425
+
426
+
[[package]]
427
+
name = "ciborium-ll"
428
+
version = "0.2.2"
429
+
source = "registry+https://github.com/rust-lang/crates.io-index"
430
+
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
431
+
dependencies = [
432
+
"ciborium-io",
433
+
"half",
434
]
435
436
[[package]]
437
+
name = "cid"
438
+
version = "0.11.1"
439
source = "registry+https://github.com/rust-lang/crates.io-index"
440
+
checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a"
441
dependencies = [
442
+
"core2",
443
+
"multibase",
444
+
"multihash",
445
+
"unsigned-varint",
446
]
447
448
[[package]]
449
name = "clap"
450
+
version = "4.5.60"
451
source = "registry+https://github.com/rust-lang/crates.io-index"
452
+
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
453
dependencies = [
454
"clap_builder",
455
"clap_derive",
···
457
458
[[package]]
459
name = "clap_builder"
460
+
version = "4.5.60"
461
source = "registry+https://github.com/rust-lang/crates.io-index"
462
+
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
463
dependencies = [
464
"anstream",
465
"anstyle",
···
469
470
[[package]]
471
name = "clap_derive"
472
+
version = "4.5.55"
473
source = "registry+https://github.com/rust-lang/crates.io-index"
474
+
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
475
dependencies = [
476
"heck",
477
"proc-macro2",
···
481
482
[[package]]
483
name = "clap_lex"
484
+
version = "1.0.0"
485
source = "registry+https://github.com/rust-lang/crates.io-index"
486
+
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
487
488
[[package]]
489
name = "cmake"
490
+
version = "0.1.57"
491
source = "registry+https://github.com/rust-lang/crates.io-index"
492
+
checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d"
493
dependencies = [
494
"cc",
495
]
···
501
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
502
503
[[package]]
504
+
name = "compare"
505
+
version = "0.0.6"
506
+
source = "registry+https://github.com/rust-lang/crates.io-index"
507
+
checksum = "ea0095f6103c2a8b44acd6fd15960c801dafebf02e21940360833e0673f48ba7"
508
+
509
+
[[package]]
510
name = "compression-codecs"
511
+
version = "0.4.37"
512
source = "registry+https://github.com/rust-lang/crates.io-index"
513
+
checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7"
514
dependencies = [
515
"brotli",
516
"compression-core",
···
522
523
[[package]]
524
name = "compression-core"
525
+
version = "0.4.31"
526
source = "registry+https://github.com/rust-lang/crates.io-index"
527
+
checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d"
528
+
529
+
[[package]]
530
+
name = "const-str"
531
+
version = "0.4.3"
532
+
source = "registry+https://github.com/rust-lang/crates.io-index"
533
+
checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3"
534
535
[[package]]
536
name = "core-foundation"
···
559
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
560
561
[[package]]
562
+
name = "core2"
563
+
version = "0.4.0"
564
+
source = "registry+https://github.com/rust-lang/crates.io-index"
565
+
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
566
+
dependencies = [
567
+
"memchr",
568
+
]
569
+
570
+
[[package]]
571
name = "cpufeatures"
572
version = "0.2.17"
573
source = "registry+https://github.com/rust-lang/crates.io-index"
···
586
]
587
588
[[package]]
589
+
name = "criterion"
590
+
version = "0.8.2"
591
+
source = "registry+https://github.com/rust-lang/crates.io-index"
592
+
checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3"
593
+
dependencies = [
594
+
"alloca",
595
+
"anes",
596
+
"cast",
597
+
"ciborium",
598
+
"clap",
599
+
"criterion-plot",
600
+
"itertools 0.13.0",
601
+
"num-traits",
602
+
"oorandom",
603
+
"page_size",
604
+
"plotters",
605
+
"rayon",
606
+
"regex",
607
+
"serde",
608
+
"serde_json",
609
+
"tinytemplate",
610
+
"walkdir",
611
+
]
612
+
613
+
[[package]]
614
+
name = "criterion-plot"
615
+
version = "0.8.2"
616
+
source = "registry+https://github.com/rust-lang/crates.io-index"
617
+
checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea"
618
+
dependencies = [
619
+
"cast",
620
+
"itertools 0.13.0",
621
+
]
622
+
623
+
[[package]]
624
+
name = "crossbeam-deque"
625
+
version = "0.8.6"
626
+
source = "registry+https://github.com/rust-lang/crates.io-index"
627
+
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
628
+
dependencies = [
629
+
"crossbeam-epoch",
630
+
"crossbeam-utils",
631
+
]
632
+
633
+
[[package]]
634
+
name = "crossbeam-epoch"
635
+
version = "0.9.18"
636
+
source = "registry+https://github.com/rust-lang/crates.io-index"
637
+
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
638
+
dependencies = [
639
+
"crossbeam-utils",
640
+
]
641
+
642
+
[[package]]
643
+
name = "crossbeam-skiplist"
644
+
version = "0.1.3"
645
+
source = "registry+https://github.com/rust-lang/crates.io-index"
646
+
checksum = "df29de440c58ca2cc6e587ec3d22347551a32435fbde9d2bff64e78a9ffa151b"
647
+
dependencies = [
648
+
"crossbeam-epoch",
649
+
"crossbeam-utils",
650
+
]
651
+
652
+
[[package]]
653
name = "crossbeam-utils"
654
version = "0.8.21"
655
source = "registry+https://github.com/rust-lang/crates.io-index"
656
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
657
658
[[package]]
659
+
name = "crunchy"
660
+
version = "0.2.4"
661
+
source = "registry+https://github.com/rust-lang/crates.io-index"
662
+
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
663
+
664
+
[[package]]
665
name = "crypto-common"
666
+
version = "0.1.7"
667
source = "registry+https://github.com/rust-lang/crates.io-index"
668
+
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
669
dependencies = [
670
"generic-array",
671
"typenum",
···
682
"hashbrown 0.14.5",
683
"lock_api",
684
"once_cell",
685
+
"parking_lot_core 0.9.12",
686
]
687
688
[[package]]
689
name = "data-encoding"
690
+
version = "2.10.0"
691
source = "registry+https://github.com/rust-lang/crates.io-index"
692
+
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
693
+
694
+
[[package]]
695
+
name = "data-encoding-macro"
696
+
version = "0.1.19"
697
+
source = "registry+https://github.com/rust-lang/crates.io-index"
698
+
checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb"
699
+
dependencies = [
700
+
"data-encoding",
701
+
"data-encoding-macro-internal",
702
+
]
703
+
704
+
[[package]]
705
+
name = "data-encoding-macro-internal"
706
+
version = "0.1.17"
707
+
source = "registry+https://github.com/rust-lang/crates.io-index"
708
+
checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de"
709
+
dependencies = [
710
+
"data-encoding",
711
+
"syn",
712
+
]
713
714
[[package]]
715
name = "der-parser"
···
727
728
[[package]]
729
name = "deranged"
730
+
version = "0.5.6"
731
source = "registry+https://github.com/rust-lang/crates.io-index"
732
+
checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4"
733
dependencies = [
734
"powerfmt",
735
]
···
778
]
779
780
[[package]]
781
+
name = "enum_dispatch"
782
+
version = "0.3.13"
783
+
source = "registry+https://github.com/rust-lang/crates.io-index"
784
+
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
785
+
dependencies = [
786
+
"once_cell",
787
+
"proc-macro2",
788
+
"quote",
789
+
"syn",
790
+
]
791
+
792
+
[[package]]
793
name = "equivalent"
794
version = "1.0.2"
795
source = "registry+https://github.com/rust-lang/crates.io-index"
···
802
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
803
dependencies = [
804
"libc",
805
+
"windows-sys 0.61.2",
806
]
807
808
[[package]]
···
819
820
[[package]]
821
name = "find-msvc-tools"
822
+
version = "0.1.9"
823
source = "registry+https://github.com/rust-lang/crates.io-index"
824
+
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
825
+
826
+
[[package]]
827
+
name = "fjall"
828
+
version = "3.0.2"
829
+
source = "registry+https://github.com/rust-lang/crates.io-index"
830
+
checksum = "5a2799b4198427a08c774838e44d0b77f677208f19a1927671cd2cd36bb30d69"
831
+
dependencies = [
832
+
"byteorder-lite",
833
+
"byteview",
834
+
"dashmap",
835
+
"flume",
836
+
"log",
837
+
"lsm-tree",
838
+
"lz4_flex",
839
+
"tempfile",
840
+
"xxhash-rust",
841
+
]
842
843
[[package]]
844
name = "flate2"
845
+
version = "1.1.9"
846
source = "registry+https://github.com/rust-lang/crates.io-index"
847
+
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
848
dependencies = [
849
"crc32fast",
850
"miniz_oxide",
851
+
]
852
+
853
+
[[package]]
854
+
name = "flume"
855
+
version = "0.12.0"
856
+
source = "registry+https://github.com/rust-lang/crates.io-index"
857
+
checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
858
+
dependencies = [
859
+
"spin",
860
]
861
862
[[package]]
···
872
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
873
874
[[package]]
875
+
name = "foldhash"
876
+
version = "0.2.0"
877
+
source = "registry+https://github.com/rust-lang/crates.io-index"
878
+
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
879
+
880
+
[[package]]
881
name = "foreign-types"
882
version = "0.3.2"
883
source = "registry+https://github.com/rust-lang/crates.io-index"
···
909
910
[[package]]
911
name = "futures"
912
+
version = "0.3.32"
913
source = "registry+https://github.com/rust-lang/crates.io-index"
914
+
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
915
dependencies = [
916
"futures-channel",
917
"futures-core",
···
924
925
[[package]]
926
name = "futures-channel"
927
+
version = "0.3.32"
928
source = "registry+https://github.com/rust-lang/crates.io-index"
929
+
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
930
dependencies = [
931
"futures-core",
932
"futures-sink",
···
934
935
[[package]]
936
name = "futures-core"
937
+
version = "0.3.32"
938
source = "registry+https://github.com/rust-lang/crates.io-index"
939
+
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
940
941
[[package]]
942
name = "futures-executor"
943
+
version = "0.3.32"
944
source = "registry+https://github.com/rust-lang/crates.io-index"
945
+
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
946
dependencies = [
947
"futures-core",
948
"futures-task",
···
951
952
[[package]]
953
name = "futures-io"
954
+
version = "0.3.32"
955
source = "registry+https://github.com/rust-lang/crates.io-index"
956
+
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
957
958
[[package]]
959
name = "futures-macro"
960
+
version = "0.3.32"
961
source = "registry+https://github.com/rust-lang/crates.io-index"
962
+
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
963
dependencies = [
964
"proc-macro2",
965
"quote",
···
968
969
[[package]]
970
name = "futures-sink"
971
+
version = "0.3.32"
972
source = "registry+https://github.com/rust-lang/crates.io-index"
973
+
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
974
975
[[package]]
976
name = "futures-task"
977
+
version = "0.3.32"
978
source = "registry+https://github.com/rust-lang/crates.io-index"
979
+
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
980
981
[[package]]
982
name = "futures-timer"
···
986
987
[[package]]
988
name = "futures-util"
989
+
version = "0.3.32"
990
source = "registry+https://github.com/rust-lang/crates.io-index"
991
+
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
992
dependencies = [
993
"futures-channel",
994
"futures-core",
···
998
"futures-task",
999
"memchr",
1000
"pin-project-lite",
1001
"slab",
1002
]
1003
···
1013
1014
[[package]]
1015
name = "getrandom"
1016
+
version = "0.2.17"
1017
source = "registry+https://github.com/rust-lang/crates.io-index"
1018
+
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
1019
dependencies = [
1020
"cfg-if",
1021
"js-sys",
···
1026
1027
[[package]]
1028
name = "getrandom"
1029
+
version = "0.3.4"
1030
source = "registry+https://github.com/rust-lang/crates.io-index"
1031
+
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
1032
dependencies = [
1033
"cfg-if",
1034
"js-sys",
1035
"libc",
1036
"r-efi",
1037
+
"wasip2",
1038
"wasm-bindgen",
1039
]
1040
1041
[[package]]
1042
+
name = "getrandom"
1043
+
version = "0.4.1"
1044
source = "registry+https://github.com/rust-lang/crates.io-index"
1045
+
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
1046
+
dependencies = [
1047
+
"cfg-if",
1048
+
"libc",
1049
+
"r-efi",
1050
+
"wasip2",
1051
+
"wasip3",
1052
+
]
1053
1054
[[package]]
1055
name = "governor"
1056
+
version = "0.10.4"
1057
source = "registry+https://github.com/rust-lang/crates.io-index"
1058
+
checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8"
1059
dependencies = [
1060
"cfg-if",
1061
"dashmap",
1062
"futures-sink",
1063
"futures-timer",
1064
"futures-util",
1065
+
"getrandom 0.3.4",
1066
+
"hashbrown 0.16.1",
1067
"nonzero_ext",
1068
+
"parking_lot 0.12.5",
1069
"portable-atomic",
1070
"quanta",
1071
"rand 0.9.2",
···
1076
1077
[[package]]
1078
name = "h2"
1079
+
version = "0.4.13"
1080
source = "registry+https://github.com/rust-lang/crates.io-index"
1081
+
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
1082
dependencies = [
1083
"atomic-waker",
1084
"bytes",
···
1091
"tokio",
1092
"tokio-util",
1093
"tracing",
1094
+
]
1095
+
1096
+
[[package]]
1097
+
name = "half"
1098
+
version = "2.7.1"
1099
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1100
+
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
1101
+
dependencies = [
1102
+
"cfg-if",
1103
+
"crunchy",
1104
+
"zerocopy",
1105
]
1106
1107
[[package]]
···
1116
source = "registry+https://github.com/rust-lang/crates.io-index"
1117
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
1118
dependencies = [
1119
+
"foldhash 0.1.5",
1120
+
]
1121
+
1122
+
[[package]]
1123
+
name = "hashbrown"
1124
+
version = "0.16.1"
1125
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1126
+
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
1127
+
dependencies = [
1128
"allocator-api2",
1129
"equivalent",
1130
+
"foldhash 0.2.0",
1131
]
1132
1133
[[package]]
···
1171
1172
[[package]]
1173
name = "http"
1174
+
version = "1.4.0"
1175
source = "registry+https://github.com/rust-lang/crates.io-index"
1176
+
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
1177
dependencies = [
1178
"bytes",
1179
"itoa",
1180
]
1181
···
1216
1217
[[package]]
1218
name = "hyper"
1219
+
version = "1.8.1"
1220
source = "registry+https://github.com/rust-lang/crates.io-index"
1221
+
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
1222
dependencies = [
1223
"atomic-waker",
1224
"bytes",
···
1272
1273
[[package]]
1274
name = "hyper-util"
1275
+
version = "0.1.20"
1276
source = "registry+https://github.com/rust-lang/crates.io-index"
1277
+
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
1278
dependencies = [
1279
"base64",
1280
"bytes",
1281
"futures-channel",
1282
"futures-util",
1283
"http",
1284
"http-body",
···
1287
"libc",
1288
"percent-encoding",
1289
"pin-project-lite",
1290
+
"socket2",
1291
"system-configuration",
1292
"tokio",
1293
"tower-service",
···
1297
1298
[[package]]
1299
name = "iana-time-zone"
1300
+
version = "0.1.65"
1301
source = "registry+https://github.com/rust-lang/crates.io-index"
1302
+
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
1303
dependencies = [
1304
"android_system_properties",
1305
"core-foundation-sys",
···
1321
1322
[[package]]
1323
name = "icu_collections"
1324
+
version = "2.1.1"
1325
source = "registry+https://github.com/rust-lang/crates.io-index"
1326
+
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
1327
dependencies = [
1328
"displaydoc",
1329
"potential_utf",
···
1334
1335
[[package]]
1336
name = "icu_locale_core"
1337
+
version = "2.1.1"
1338
source = "registry+https://github.com/rust-lang/crates.io-index"
1339
+
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
1340
dependencies = [
1341
"displaydoc",
1342
"litemap",
···
1347
1348
[[package]]
1349
name = "icu_normalizer"
1350
+
version = "2.1.1"
1351
source = "registry+https://github.com/rust-lang/crates.io-index"
1352
+
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
1353
dependencies = [
1354
"icu_collections",
1355
"icu_normalizer_data",
1356
"icu_properties",
···
1361
1362
[[package]]
1363
name = "icu_normalizer_data"
1364
+
version = "2.1.1"
1365
source = "registry+https://github.com/rust-lang/crates.io-index"
1366
+
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
1367
1368
[[package]]
1369
name = "icu_properties"
1370
+
version = "2.1.2"
1371
source = "registry+https://github.com/rust-lang/crates.io-index"
1372
+
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
1373
dependencies = [
1374
"icu_collections",
1375
"icu_locale_core",
1376
"icu_properties_data",
1377
"icu_provider",
1378
"zerotrie",
1379
"zerovec",
1380
]
1381
1382
[[package]]
1383
name = "icu_properties_data"
1384
+
version = "2.1.2"
1385
source = "registry+https://github.com/rust-lang/crates.io-index"
1386
+
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
1387
1388
[[package]]
1389
name = "icu_provider"
1390
+
version = "2.1.1"
1391
source = "registry+https://github.com/rust-lang/crates.io-index"
1392
+
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
1393
dependencies = [
1394
"displaydoc",
1395
"icu_locale_core",
1396
"writeable",
1397
"yoke",
1398
"zerofrom",
···
1401
]
1402
1403
[[package]]
1404
+
name = "id-arena"
1405
+
version = "2.3.0"
1406
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1407
+
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
1408
+
1409
+
[[package]]
1410
name = "idna"
1411
version = "1.1.0"
1412
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1429
1430
[[package]]
1431
name = "indexmap"
1432
+
version = "2.13.0"
1433
source = "registry+https://github.com/rust-lang/crates.io-index"
1434
+
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
1435
dependencies = [
1436
"equivalent",
1437
+
"hashbrown 0.16.1",
1438
+
"serde",
1439
+
"serde_core",
1440
]
1441
1442
[[package]]
···
1452
]
1453
1454
[[package]]
1455
+
name = "interval-heap"
1456
+
version = "0.0.5"
1457
source = "registry+https://github.com/rust-lang/crates.io-index"
1458
+
checksum = "11274e5e8e89b8607cfedc2910b6626e998779b48a019151c7604d0adcb86ac6"
1459
dependencies = [
1460
+
"compare",
1461
]
1462
1463
[[package]]
···
1468
1469
[[package]]
1470
name = "iri-string"
1471
+
version = "0.7.10"
1472
source = "registry+https://github.com/rust-lang/crates.io-index"
1473
+
checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
1474
dependencies = [
1475
"memchr",
1476
"serde",
···
1478
1479
[[package]]
1480
name = "is_terminal_polyfill"
1481
+
version = "1.70.2"
1482
source = "registry+https://github.com/rust-lang/crates.io-index"
1483
+
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
1484
1485
[[package]]
1486
name = "itertools"
···
1492
]
1493
1494
[[package]]
1495
+
name = "itertools"
1496
+
version = "0.14.0"
1497
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1498
+
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
1499
+
dependencies = [
1500
+
"either",
1501
+
]
1502
+
1503
+
[[package]]
1504
name = "itoa"
1505
+
version = "1.0.17"
1506
source = "registry+https://github.com/rust-lang/crates.io-index"
1507
+
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
1508
1509
[[package]]
1510
name = "jobserver"
···
1512
source = "registry+https://github.com/rust-lang/crates.io-index"
1513
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
1514
dependencies = [
1515
+
"getrandom 0.3.4",
1516
"libc",
1517
]
1518
1519
[[package]]
1520
name = "js-sys"
1521
+
version = "0.3.86"
1522
source = "registry+https://github.com/rust-lang/crates.io-index"
1523
+
checksum = "d36139f1c97c42c0c86a411910b04e48d4939a0376e6e0f989420cbdee0120e5"
1524
dependencies = [
1525
"once_cell",
1526
"wasm-bindgen",
···
1533
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
1534
1535
[[package]]
1536
+
name = "leb128fmt"
1537
+
version = "0.1.0"
1538
source = "registry+https://github.com/rust-lang/crates.io-index"
1539
+
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
1540
1541
[[package]]
1542
+
name = "libc"
1543
+
version = "0.2.182"
1544
source = "registry+https://github.com/rust-lang/crates.io-index"
1545
+
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
1546
1547
[[package]]
1548
name = "libredox"
1549
+
version = "0.1.12"
1550
source = "registry+https://github.com/rust-lang/crates.io-index"
1551
+
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
1552
dependencies = [
1553
+
"bitflags 2.11.0",
1554
"libc",
1555
]
1556
1557
[[package]]
···
1562
1563
[[package]]
1564
name = "litemap"
1565
+
version = "0.8.1"
1566
source = "registry+https://github.com/rust-lang/crates.io-index"
1567
+
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
1568
1569
[[package]]
1570
name = "lock_api"
1571
+
version = "0.4.14"
1572
source = "registry+https://github.com/rust-lang/crates.io-index"
1573
+
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
1574
dependencies = [
1575
"scopeguard",
1576
]
1577
1578
[[package]]
1579
name = "log"
1580
+
version = "0.4.29"
1581
source = "registry+https://github.com/rust-lang/crates.io-index"
1582
+
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
1583
1584
[[package]]
1585
name = "lru-slab"
···
1588
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
1589
1590
[[package]]
1591
+
name = "lsm-tree"
1592
+
version = "3.0.2"
1593
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1594
+
checksum = "86e8d0b8e0cf2531a437788ce94d95570dbaabfe9888db20022c2d5ccec9b221"
1595
+
dependencies = [
1596
+
"byteorder-lite",
1597
+
"byteview",
1598
+
"crossbeam-skiplist",
1599
+
"enum_dispatch",
1600
+
"interval-heap",
1601
+
"log",
1602
+
"lz4_flex",
1603
+
"quick_cache",
1604
+
"rustc-hash",
1605
+
"self_cell",
1606
+
"sfa",
1607
+
"tempfile",
1608
+
"varint-rs",
1609
+
"xxhash-rust",
1610
+
]
1611
+
1612
+
[[package]]
1613
+
name = "lz4_flex"
1614
+
version = "0.11.5"
1615
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1616
+
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
1617
+
dependencies = [
1618
+
"twox-hash",
1619
+
]
1620
+
1621
+
[[package]]
1622
+
name = "match-lookup"
1623
+
version = "0.1.2"
1624
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1625
+
checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771"
1626
+
dependencies = [
1627
+
"proc-macro2",
1628
+
"quote",
1629
+
"syn",
1630
+
]
1631
+
1632
+
[[package]]
1633
name = "matchers"
1634
version = "0.2.0"
1635
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1650
1651
[[package]]
1652
name = "memchr"
1653
+
version = "2.8.0"
1654
source = "registry+https://github.com/rust-lang/crates.io-index"
1655
+
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
1656
1657
[[package]]
1658
name = "mime"
···
1673
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
1674
dependencies = [
1675
"adler2",
1676
+
"simd-adler32",
1677
]
1678
1679
[[package]]
1680
name = "mio"
1681
+
version = "1.1.1"
1682
source = "registry+https://github.com/rust-lang/crates.io-index"
1683
+
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
1684
dependencies = [
1685
"libc",
1686
"wasi 0.11.1+wasi-snapshot-preview1",
1687
+
"windows-sys 0.61.2",
1688
+
]
1689
+
1690
+
[[package]]
1691
+
name = "multibase"
1692
+
version = "0.9.2"
1693
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1694
+
checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77"
1695
+
dependencies = [
1696
+
"base-x",
1697
+
"base256emoji",
1698
+
"data-encoding",
1699
+
"data-encoding-macro",
1700
+
]
1701
+
1702
+
[[package]]
1703
+
name = "multihash"
1704
+
version = "0.19.3"
1705
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1706
+
checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d"
1707
+
dependencies = [
1708
+
"core2",
1709
+
"unsigned-varint",
1710
]
1711
1712
[[package]]
1713
name = "native-tls"
1714
+
version = "0.2.18"
1715
source = "registry+https://github.com/rust-lang/crates.io-index"
1716
+
checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
1717
dependencies = [
1718
"libc",
1719
"log",
···
1721
"openssl-probe",
1722
"openssl-sys",
1723
"schannel",
1724
+
"security-framework",
1725
"security-framework-sys",
1726
"tempfile",
1727
]
···
1732
source = "registry+https://github.com/rust-lang/crates.io-index"
1733
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
1734
dependencies = [
1735
+
"bitflags 2.11.0",
1736
"cfg-if",
1737
"cfg_aliases",
1738
"libc",
···
1756
1757
[[package]]
1758
name = "nu-ansi-term"
1759
+
version = "0.50.3"
1760
source = "registry+https://github.com/rust-lang/crates.io-index"
1761
+
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
1762
dependencies = [
1763
+
"windows-sys 0.61.2",
1764
]
1765
1766
[[package]]
···
1775
1776
[[package]]
1777
name = "num-conv"
1778
+
version = "0.2.0"
1779
source = "registry+https://github.com/rust-lang/crates.io-index"
1780
+
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
1781
1782
[[package]]
1783
name = "num-integer"
···
1798
]
1799
1800
[[package]]
1801
+
name = "objc2-core-foundation"
1802
+
version = "0.3.2"
1803
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1804
+
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
1805
+
dependencies = [
1806
+
"bitflags 2.11.0",
1807
+
]
1808
+
1809
+
[[package]]
1810
+
name = "objc2-system-configuration"
1811
+
version = "0.3.2"
1812
source = "registry+https://github.com/rust-lang/crates.io-index"
1813
+
checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396"
1814
dependencies = [
1815
+
"objc2-core-foundation",
1816
]
1817
1818
[[package]]
···
1832
1833
[[package]]
1834
name = "once_cell_polyfill"
1835
+
version = "1.70.2"
1836
source = "registry+https://github.com/rust-lang/crates.io-index"
1837
+
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
1838
+
1839
+
[[package]]
1840
+
name = "oorandom"
1841
+
version = "11.1.5"
1842
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1843
+
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
1844
1845
[[package]]
1846
name = "openssl"
1847
+
version = "0.10.75"
1848
source = "registry+https://github.com/rust-lang/crates.io-index"
1849
+
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
1850
dependencies = [
1851
+
"bitflags 2.11.0",
1852
"cfg-if",
1853
"foreign-types",
1854
"libc",
···
1870
1871
[[package]]
1872
name = "openssl-probe"
1873
+
version = "0.2.1"
1874
source = "registry+https://github.com/rust-lang/crates.io-index"
1875
+
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
1876
1877
[[package]]
1878
name = "openssl-sys"
1879
+
version = "0.9.111"
1880
source = "registry+https://github.com/rust-lang/crates.io-index"
1881
+
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
1882
dependencies = [
1883
"cc",
1884
"libc",
···
1896
"futures-sink",
1897
"js-sys",
1898
"pin-project-lite",
1899
+
"thiserror 2.0.18",
1900
"tracing",
1901
]
1902
···
1926
"opentelemetry_sdk",
1927
"prost",
1928
"reqwest",
1929
+
"thiserror 2.0.18",
1930
"tracing",
1931
]
1932
···
1955
"percent-encoding",
1956
"rand 0.9.2",
1957
"serde_json",
1958
+
"thiserror 2.0.18",
1959
"tokio",
1960
"tokio-stream",
1961
]
1962
1963
[[package]]
1964
+
name = "page_size"
1965
+
version = "0.6.0"
1966
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1967
+
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
1968
+
dependencies = [
1969
+
"libc",
1970
+
"winapi",
1971
+
]
1972
+
1973
+
[[package]]
1974
name = "parking_lot"
1975
version = "0.11.2"
1976
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1983
1984
[[package]]
1985
name = "parking_lot"
1986
+
version = "0.12.5"
1987
source = "registry+https://github.com/rust-lang/crates.io-index"
1988
+
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
1989
dependencies = [
1990
"lock_api",
1991
+
"parking_lot_core 0.9.12",
1992
]
1993
1994
[[package]]
···
2007
2008
[[package]]
2009
name = "parking_lot_core"
2010
+
version = "0.9.12"
2011
source = "registry+https://github.com/rust-lang/crates.io-index"
2012
+
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
2013
dependencies = [
2014
"cfg-if",
2015
"libc",
2016
+
"redox_syscall 0.5.18",
2017
"smallvec",
2018
+
"windows-link",
2019
]
2020
2021
[[package]]
2022
name = "pem"
2023
+
version = "3.0.6"
2024
source = "registry+https://github.com/rust-lang/crates.io-index"
2025
+
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
2026
dependencies = [
2027
"base64",
2028
+
"serde_core",
2029
]
2030
2031
[[package]]
···
2036
2037
[[package]]
2038
name = "phf"
2039
+
version = "0.13.1"
2040
source = "registry+https://github.com/rust-lang/crates.io-index"
2041
+
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
2042
dependencies = [
2043
"phf_shared",
2044
+
"serde",
2045
]
2046
2047
[[package]]
2048
name = "phf_shared"
2049
+
version = "0.13.1"
2050
source = "registry+https://github.com/rust-lang/crates.io-index"
2051
+
checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
2052
dependencies = [
2053
"siphasher",
2054
]
···
2092
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
2093
2094
[[package]]
2095
+
name = "plotters"
2096
+
version = "0.3.7"
2097
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2098
+
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
2099
+
dependencies = [
2100
+
"num-traits",
2101
+
"plotters-backend",
2102
+
"plotters-svg",
2103
+
"wasm-bindgen",
2104
+
"web-sys",
2105
+
]
2106
+
2107
+
[[package]]
2108
+
name = "plotters-backend"
2109
+
version = "0.3.7"
2110
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2111
+
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
2112
+
2113
+
[[package]]
2114
+
name = "plotters-svg"
2115
+
version = "0.3.7"
2116
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2117
+
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
2118
+
dependencies = [
2119
+
"plotters-backend",
2120
+
]
2121
+
2122
+
[[package]]
2123
name = "poem"
2124
version = "3.1.12"
2125
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2137
"hyper-util",
2138
"mime",
2139
"nix",
2140
+
"parking_lot 0.12.5",
2141
"percent-encoding",
2142
"pin-project-lite",
2143
"poem-derive",
···
2152
"serde_urlencoded",
2153
"smallvec",
2154
"sync_wrapper",
2155
+
"thiserror 2.0.18",
2156
"tokio",
2157
"tokio-rustls",
2158
"tokio-util",
···
2175
2176
[[package]]
2177
name = "portable-atomic"
2178
+
version = "1.13.1"
2179
source = "registry+https://github.com/rust-lang/crates.io-index"
2180
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
2181
2182
[[package]]
2183
name = "postgres-native-tls"
2184
+
version = "0.5.2"
2185
source = "registry+https://github.com/rust-lang/crates.io-index"
2186
+
checksum = "ac73153d92e4bde922bd6f1dfba7f1ab8132266c031153b55e20a1521cd36d49"
2187
dependencies = [
2188
"native-tls",
2189
"tokio",
···
2193
2194
[[package]]
2195
name = "postgres-protocol"
2196
+
version = "0.6.10"
2197
source = "registry+https://github.com/rust-lang/crates.io-index"
2198
+
checksum = "3ee9dd5fe15055d2b6806f4736aa0c9637217074e224bbec46d4041b91bb9491"
2199
dependencies = [
2200
"base64",
2201
"byteorder",
···
2211
2212
[[package]]
2213
name = "postgres-types"
2214
+
version = "0.2.12"
2215
source = "registry+https://github.com/rust-lang/crates.io-index"
2216
+
checksum = "54b858f82211e84682fecd373f68e1ceae642d8d751a1ebd13f33de6257b3e20"
2217
dependencies = [
2218
"bytes",
2219
"chrono",
2220
"fallible-iterator",
2221
"postgres-protocol",
2222
+
"serde_core",
2223
"serde_json",
2224
]
2225
2226
[[package]]
2227
name = "potential_utf"
2228
+
version = "0.1.4"
2229
source = "registry+https://github.com/rust-lang/crates.io-index"
2230
+
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
2231
dependencies = [
2232
"zerovec",
2233
]
···
2268
2269
[[package]]
2270
name = "proc-macro2"
2271
+
version = "1.0.106"
2272
source = "registry+https://github.com/rust-lang/crates.io-index"
2273
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
2274
dependencies = [
2275
"unicode-ident",
2276
]
···
2292
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
2293
dependencies = [
2294
"anyhow",
2295
+
"itertools 0.14.0",
2296
"proc-macro2",
2297
"quote",
2298
"syn",
···
2314
]
2315
2316
[[package]]
2317
+
name = "quick_cache"
2318
+
version = "0.6.18"
2319
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2320
+
checksum = "7ada44a88ef953a3294f6eb55d2007ba44646015e18613d2f213016379203ef3"
2321
+
dependencies = [
2322
+
"equivalent",
2323
+
"hashbrown 0.16.1",
2324
+
]
2325
+
2326
+
[[package]]
2327
name = "quinn"
2328
version = "0.11.9"
2329
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2336
"quinn-udp",
2337
"rustc-hash",
2338
"rustls",
2339
+
"socket2",
2340
+
"thiserror 2.0.18",
2341
"tokio",
2342
"tracing",
2343
"web-time",
···
2350
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
2351
dependencies = [
2352
"bytes",
2353
+
"getrandom 0.3.4",
2354
"lru-slab",
2355
"rand 0.9.2",
2356
"ring",
···
2358
"rustls",
2359
"rustls-pki-types",
2360
"slab",
2361
+
"thiserror 2.0.18",
2362
"tinyvec",
2363
"tracing",
2364
"web-time",
···
2373
"cfg_aliases",
2374
"libc",
2375
"once_cell",
2376
+
"socket2",
2377
"tracing",
2378
"windows-sys 0.60.2",
2379
]
2380
2381
[[package]]
2382
name = "quote"
2383
+
version = "1.0.44"
2384
source = "registry+https://github.com/rust-lang/crates.io-index"
2385
+
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
2386
dependencies = [
2387
"proc-macro2",
2388
]
···
2411
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
2412
dependencies = [
2413
"rand_chacha 0.9.0",
2414
+
"rand_core 0.9.5",
2415
]
2416
2417
[[package]]
···
2431
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
2432
dependencies = [
2433
"ppv-lite86",
2434
+
"rand_core 0.9.5",
2435
]
2436
2437
[[package]]
···
2440
source = "registry+https://github.com/rust-lang/crates.io-index"
2441
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
2442
dependencies = [
2443
+
"getrandom 0.2.17",
2444
]
2445
2446
[[package]]
2447
name = "rand_core"
2448
+
version = "0.9.5"
2449
source = "registry+https://github.com/rust-lang/crates.io-index"
2450
+
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
2451
dependencies = [
2452
+
"getrandom 0.3.4",
2453
]
2454
2455
[[package]]
···
2458
source = "registry+https://github.com/rust-lang/crates.io-index"
2459
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
2460
dependencies = [
2461
+
"bitflags 2.11.0",
2462
+
]
2463
+
2464
+
[[package]]
2465
+
name = "rayon"
2466
+
version = "1.11.0"
2467
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2468
+
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
2469
+
dependencies = [
2470
+
"either",
2471
+
"rayon-core",
2472
+
]
2473
+
2474
+
[[package]]
2475
+
name = "rayon-core"
2476
+
version = "1.13.0"
2477
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2478
+
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
2479
+
dependencies = [
2480
+
"crossbeam-deque",
2481
+
"crossbeam-utils",
2482
]
2483
2484
[[package]]
···
2504
2505
[[package]]
2506
name = "redox_syscall"
2507
+
version = "0.5.18"
2508
source = "registry+https://github.com/rust-lang/crates.io-index"
2509
+
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
2510
dependencies = [
2511
+
"bitflags 2.11.0",
2512
]
2513
2514
[[package]]
2515
name = "regex"
2516
+
version = "1.12.3"
2517
source = "registry+https://github.com/rust-lang/crates.io-index"
2518
+
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
2519
dependencies = [
2520
"aho-corasick",
2521
"memchr",
···
2525
2526
[[package]]
2527
name = "regex-automata"
2528
+
version = "0.4.14"
2529
source = "registry+https://github.com/rust-lang/crates.io-index"
2530
+
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
2531
dependencies = [
2532
"aho-corasick",
2533
"memchr",
···
2536
2537
[[package]]
2538
name = "regex-syntax"
2539
+
version = "0.8.9"
2540
source = "registry+https://github.com/rust-lang/crates.io-index"
2541
+
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
2542
2543
[[package]]
2544
name = "reqwest"
2545
+
version = "0.12.28"
2546
source = "registry+https://github.com/rust-lang/crates.io-index"
2547
+
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
2548
dependencies = [
2549
"base64",
2550
"bytes",
2551
"encoding_rs",
···
2612
"anyhow",
2613
"async-trait",
2614
"futures",
2615
+
"getrandom 0.2.17",
2616
"http",
2617
"hyper",
2618
"parking_lot 0.11.2",
···
2651
dependencies = [
2652
"cc",
2653
"cfg-if",
2654
+
"getrandom 0.2.17",
2655
"libc",
2656
"untrusted",
2657
"windows-sys 0.52.0",
2658
]
2659
2660
[[package]]
2661
+
name = "rmp"
2662
+
version = "0.8.15"
2663
source = "registry+https://github.com/rust-lang/crates.io-index"
2664
+
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
2665
+
dependencies = [
2666
+
"num-traits",
2667
+
]
2668
+
2669
+
[[package]]
2670
+
name = "rmp-serde"
2671
+
version = "1.3.1"
2672
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2673
+
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
2674
+
dependencies = [
2675
+
"rmp",
2676
+
"serde",
2677
+
]
2678
2679
[[package]]
2680
name = "rustc-hash"
···
2693
2694
[[package]]
2695
name = "rustix"
2696
+
version = "1.1.3"
2697
source = "registry+https://github.com/rust-lang/crates.io-index"
2698
+
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
2699
dependencies = [
2700
+
"bitflags 2.11.0",
2701
"errno",
2702
"libc",
2703
"linux-raw-sys",
2704
+
"windows-sys 0.61.2",
2705
]
2706
2707
[[package]]
2708
name = "rustls"
2709
+
version = "0.23.36"
2710
source = "registry+https://github.com/rust-lang/crates.io-index"
2711
+
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
2712
dependencies = [
2713
"aws-lc-rs",
2714
"log",
···
2722
2723
[[package]]
2724
name = "rustls-native-certs"
2725
+
version = "0.8.3"
2726
source = "registry+https://github.com/rust-lang/crates.io-index"
2727
+
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
2728
dependencies = [
2729
"openssl-probe",
2730
"rustls-pki-types",
2731
"schannel",
2732
+
"security-framework",
2733
]
2734
2735
[[package]]
···
2743
2744
[[package]]
2745
name = "rustls-pki-types"
2746
+
version = "1.14.0"
2747
source = "registry+https://github.com/rust-lang/crates.io-index"
2748
+
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
2749
dependencies = [
2750
"web-time",
2751
"zeroize",
···
2753
2754
[[package]]
2755
name = "rustls-webpki"
2756
+
version = "0.103.9"
2757
source = "registry+https://github.com/rust-lang/crates.io-index"
2758
+
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
2759
dependencies = [
2760
"aws-lc-rs",
2761
"ring",
···
2771
2772
[[package]]
2773
name = "ryu"
2774
+
version = "1.0.23"
2775
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2776
+
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
2777
+
2778
+
[[package]]
2779
+
name = "same-file"
2780
+
version = "1.0.6"
2781
source = "registry+https://github.com/rust-lang/crates.io-index"
2782
+
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
2783
+
dependencies = [
2784
+
"winapi-util",
2785
+
]
2786
2787
[[package]]
2788
name = "schannel"
···
2790
source = "registry+https://github.com/rust-lang/crates.io-index"
2791
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
2792
dependencies = [
2793
+
"windows-sys 0.61.2",
2794
]
2795
2796
[[package]]
···
2801
2802
[[package]]
2803
name = "security-framework"
2804
+
version = "3.7.0"
2805
source = "registry+https://github.com/rust-lang/crates.io-index"
2806
+
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
2807
dependencies = [
2808
+
"bitflags 2.11.0",
2809
+
"core-foundation 0.10.1",
2810
"core-foundation-sys",
2811
"libc",
2812
"security-framework-sys",
2813
]
2814
2815
[[package]]
2816
+
name = "security-framework-sys"
2817
+
version = "2.17.0"
2818
source = "registry+https://github.com/rust-lang/crates.io-index"
2819
+
checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
2820
dependencies = [
2821
"core-foundation-sys",
2822
"libc",
2823
]
2824
2825
[[package]]
2826
+
name = "self_cell"
2827
+
version = "1.2.2"
2828
source = "registry+https://github.com/rust-lang/crates.io-index"
2829
+
checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
2830
+
2831
+
[[package]]
2832
+
name = "semver"
2833
+
version = "1.0.27"
2834
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2835
+
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
2836
2837
[[package]]
2838
name = "serde"
2839
+
version = "1.0.228"
2840
source = "registry+https://github.com/rust-lang/crates.io-index"
2841
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
2842
dependencies = [
2843
"serde_core",
2844
"serde_derive",
2845
]
2846
2847
[[package]]
2848
+
name = "serde_bytes"
2849
+
version = "0.11.19"
2850
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2851
+
checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
2852
+
dependencies = [
2853
+
"serde",
2854
+
"serde_core",
2855
+
]
2856
+
2857
+
[[package]]
2858
name = "serde_core"
2859
+
version = "1.0.228"
2860
source = "registry+https://github.com/rust-lang/crates.io-index"
2861
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
2862
dependencies = [
2863
"serde_derive",
2864
]
2865
2866
[[package]]
2867
name = "serde_derive"
2868
+
version = "1.0.228"
2869
source = "registry+https://github.com/rust-lang/crates.io-index"
2870
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
2871
dependencies = [
2872
"proc-macro2",
2873
"quote",
···
2876
2877
[[package]]
2878
name = "serde_json"
2879
+
version = "1.0.149"
2880
source = "registry+https://github.com/rust-lang/crates.io-index"
2881
+
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
2882
dependencies = [
2883
"itoa",
2884
"memchr",
2885
"serde",
2886
+
"serde_core",
2887
+
"zmij",
2888
]
2889
2890
[[package]]
···
2900
]
2901
2902
[[package]]
2903
+
name = "sfa"
2904
+
version = "1.0.0"
2905
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2906
+
checksum = "a1296838937cab56cd6c4eeeb8718ec777383700c33f060e2869867bd01d1175"
2907
+
dependencies = [
2908
+
"byteorder-lite",
2909
+
"log",
2910
+
"xxhash-rust",
2911
+
]
2912
+
2913
+
[[package]]
2914
name = "sha1"
2915
version = "0.10.6"
2916
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2949
2950
[[package]]
2951
name = "signal-hook-registry"
2952
+
version = "1.4.8"
2953
source = "registry+https://github.com/rust-lang/crates.io-index"
2954
+
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
2955
dependencies = [
2956
+
"errno",
2957
"libc",
2958
]
2959
2960
[[package]]
2961
+
name = "simd-adler32"
2962
+
version = "0.3.8"
2963
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2964
+
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
2965
+
2966
+
[[package]]
2967
name = "siphasher"
2968
+
version = "1.0.2"
2969
source = "registry+https://github.com/rust-lang/crates.io-index"
2970
+
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
2971
2972
[[package]]
2973
name = "slab"
2974
+
version = "0.4.12"
2975
source = "registry+https://github.com/rust-lang/crates.io-index"
2976
+
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
2977
2978
[[package]]
2979
name = "smallvec"
···
2983
2984
[[package]]
2985
name = "socket2"
2986
+
version = "0.6.2"
2987
source = "registry+https://github.com/rust-lang/crates.io-index"
2988
+
checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
2989
dependencies = [
2990
"libc",
2991
+
"windows-sys 0.60.2",
2992
]
2993
2994
[[package]]
2995
+
name = "spin"
2996
+
version = "0.9.8"
2997
source = "registry+https://github.com/rust-lang/crates.io-index"
2998
+
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
2999
dependencies = [
3000
+
"lock_api",
3001
]
3002
3003
[[package]]
···
3011
3012
[[package]]
3013
name = "stable_deref_trait"
3014
+
version = "1.2.1"
3015
source = "registry+https://github.com/rust-lang/crates.io-index"
3016
+
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
3017
3018
[[package]]
3019
name = "stringprep"
···
3040
3041
[[package]]
3042
name = "syn"
3043
+
version = "2.0.117"
3044
source = "registry+https://github.com/rust-lang/crates.io-index"
3045
+
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
3046
dependencies = [
3047
"proc-macro2",
3048
"quote",
···
3071
3072
[[package]]
3073
name = "system-configuration"
3074
+
version = "0.7.0"
3075
source = "registry+https://github.com/rust-lang/crates.io-index"
3076
+
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
3077
dependencies = [
3078
+
"bitflags 2.11.0",
3079
"core-foundation 0.9.4",
3080
"system-configuration-sys",
3081
]
···
3092
3093
[[package]]
3094
name = "tempfile"
3095
+
version = "3.25.0"
3096
source = "registry+https://github.com/rust-lang/crates.io-index"
3097
+
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
3098
dependencies = [
3099
"fastrand",
3100
+
"getrandom 0.4.1",
3101
"once_cell",
3102
"rustix",
3103
+
"windows-sys 0.61.2",
3104
]
3105
3106
[[package]]
···
3114
3115
[[package]]
3116
name = "thiserror"
3117
+
version = "2.0.18"
3118
source = "registry+https://github.com/rust-lang/crates.io-index"
3119
+
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
3120
dependencies = [
3121
+
"thiserror-impl 2.0.18",
3122
]
3123
3124
[[package]]
···
3134
3135
[[package]]
3136
name = "thiserror-impl"
3137
+
version = "2.0.18"
3138
source = "registry+https://github.com/rust-lang/crates.io-index"
3139
+
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
3140
dependencies = [
3141
"proc-macro2",
3142
"quote",
···
3154
3155
[[package]]
3156
name = "time"
3157
+
version = "0.3.47"
3158
source = "registry+https://github.com/rust-lang/crates.io-index"
3159
+
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
3160
dependencies = [
3161
"deranged",
3162
"itoa",
3163
"num-conv",
3164
"powerfmt",
3165
+
"serde_core",
3166
"time-core",
3167
"time-macros",
3168
]
3169
3170
[[package]]
3171
name = "time-core"
3172
+
version = "0.1.8"
3173
source = "registry+https://github.com/rust-lang/crates.io-index"
3174
+
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
3175
3176
[[package]]
3177
name = "time-macros"
3178
+
version = "0.2.27"
3179
source = "registry+https://github.com/rust-lang/crates.io-index"
3180
+
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
3181
dependencies = [
3182
"num-conv",
3183
"time-core",
···
3185
3186
[[package]]
3187
name = "tinystr"
3188
+
version = "0.8.2"
3189
source = "registry+https://github.com/rust-lang/crates.io-index"
3190
+
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
3191
dependencies = [
3192
"displaydoc",
3193
"zerovec",
3194
]
3195
3196
[[package]]
3197
+
name = "tinytemplate"
3198
+
version = "1.2.1"
3199
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3200
+
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
3201
+
dependencies = [
3202
+
"serde",
3203
+
"serde_json",
3204
+
]
3205
+
3206
+
[[package]]
3207
name = "tinyvec"
3208
version = "1.10.0"
3209
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3220
3221
[[package]]
3222
name = "tokio"
3223
+
version = "1.49.0"
3224
source = "registry+https://github.com/rust-lang/crates.io-index"
3225
+
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
3226
dependencies = [
3227
"bytes",
3228
"libc",
3229
"mio",
3230
+
"parking_lot 0.12.5",
3231
"pin-project-lite",
3232
"signal-hook-registry",
3233
+
"socket2",
3234
"tokio-macros",
3235
+
"windows-sys 0.61.2",
3236
]
3237
3238
[[package]]
3239
name = "tokio-macros"
3240
+
version = "2.6.0"
3241
source = "registry+https://github.com/rust-lang/crates.io-index"
3242
+
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
3243
dependencies = [
3244
"proc-macro2",
3245
"quote",
···
3258
3259
[[package]]
3260
name = "tokio-postgres"
3261
+
version = "0.7.16"
3262
source = "registry+https://github.com/rust-lang/crates.io-index"
3263
+
checksum = "dcea47c8f71744367793f16c2db1f11cb859d28f436bdb4ca9193eb1f787ee42"
3264
dependencies = [
3265
"async-trait",
3266
"byteorder",
···
3269
"futures-channel",
3270
"futures-util",
3271
"log",
3272
+
"parking_lot 0.12.5",
3273
"percent-encoding",
3274
"phf",
3275
"pin-project-lite",
3276
"postgres-protocol",
3277
"postgres-types",
3278
"rand 0.9.2",
3279
+
"socket2",
3280
"tokio",
3281
"tokio-util",
3282
"whoami",
···
3284
3285
[[package]]
3286
name = "tokio-rustls"
3287
+
version = "0.26.4"
3288
source = "registry+https://github.com/rust-lang/crates.io-index"
3289
+
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
3290
dependencies = [
3291
"rustls",
3292
"tokio",
···
3294
3295
[[package]]
3296
name = "tokio-stream"
3297
+
version = "0.1.18"
3298
source = "registry+https://github.com/rust-lang/crates.io-index"
3299
+
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
3300
dependencies = [
3301
"futures-core",
3302
"pin-project-lite",
···
3305
3306
[[package]]
3307
name = "tokio-util"
3308
+
version = "0.7.18"
3309
source = "registry+https://github.com/rust-lang/crates.io-index"
3310
+
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
3311
dependencies = [
3312
"bytes",
3313
"futures-core",
···
3319
3320
[[package]]
3321
name = "toml_datetime"
3322
+
version = "0.7.5+spec-1.1.0"
3323
source = "registry+https://github.com/rust-lang/crates.io-index"
3324
+
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
3325
dependencies = [
3326
"serde_core",
3327
]
3328
3329
[[package]]
3330
name = "toml_edit"
3331
+
version = "0.23.10+spec-1.0.0"
3332
source = "registry+https://github.com/rust-lang/crates.io-index"
3333
+
checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
3334
dependencies = [
3335
"indexmap",
3336
"toml_datetime",
···
3340
3341
[[package]]
3342
name = "toml_parser"
3343
+
version = "1.0.9+spec-1.1.0"
3344
source = "registry+https://github.com/rust-lang/crates.io-index"
3345
+
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
3346
dependencies = [
3347
"winnow",
3348
]
···
3370
3371
[[package]]
3372
name = "tower"
3373
+
version = "0.5.3"
3374
source = "registry+https://github.com/rust-lang/crates.io-index"
3375
+
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
3376
dependencies = [
3377
"futures-core",
3378
"futures-util",
···
3385
3386
[[package]]
3387
name = "tower-http"
3388
+
version = "0.6.8"
3389
source = "registry+https://github.com/rust-lang/crates.io-index"
3390
+
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
3391
dependencies = [
3392
+
"async-compression",
3393
+
"bitflags 2.11.0",
3394
"bytes",
3395
+
"futures-core",
3396
"futures-util",
3397
"http",
3398
"http-body",
3399
+
"http-body-util",
3400
"iri-string",
3401
"pin-project-lite",
3402
+
"tokio",
3403
+
"tokio-util",
3404
"tower",
3405
"tower-layer",
3406
"tower-service",
···
3420
3421
[[package]]
3422
name = "tracing"
3423
+
version = "0.1.44"
3424
source = "registry+https://github.com/rust-lang/crates.io-index"
3425
+
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
3426
dependencies = [
3427
"pin-project-lite",
3428
"tracing-attributes",
···
3431
3432
[[package]]
3433
name = "tracing-attributes"
3434
+
version = "0.1.31"
3435
source = "registry+https://github.com/rust-lang/crates.io-index"
3436
+
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
3437
dependencies = [
3438
"proc-macro2",
3439
"quote",
···
3442
3443
[[package]]
3444
name = "tracing-core"
3445
+
version = "0.1.36"
3446
source = "registry+https://github.com/rust-lang/crates.io-index"
3447
+
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
3448
dependencies = [
3449
"once_cell",
3450
"valuable",
···
3481
3482
[[package]]
3483
name = "tracing-subscriber"
3484
+
version = "0.3.22"
3485
source = "registry+https://github.com/rust-lang/crates.io-index"
3486
+
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
3487
dependencies = [
3488
"matchers",
3489
"nu-ansi-term",
···
3504
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
3505
3506
[[package]]
3507
+
name = "twox-hash"
3508
+
version = "2.1.2"
3509
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3510
+
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
3511
+
3512
+
[[package]]
3513
name = "typenum"
3514
+
version = "1.19.0"
3515
source = "registry+https://github.com/rust-lang/crates.io-index"
3516
+
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
3517
3518
[[package]]
3519
name = "uncased"
···
3532
3533
[[package]]
3534
name = "unicode-ident"
3535
+
version = "1.0.24"
3536
source = "registry+https://github.com/rust-lang/crates.io-index"
3537
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
3538
3539
[[package]]
3540
name = "unicode-normalization"
3541
+
version = "0.1.25"
3542
source = "registry+https://github.com/rust-lang/crates.io-index"
3543
+
checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
3544
dependencies = [
3545
"tinyvec",
3546
]
3547
3548
[[package]]
3549
name = "unicode-properties"
3550
+
version = "0.1.4"
3551
source = "registry+https://github.com/rust-lang/crates.io-index"
3552
+
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
3553
+
3554
+
[[package]]
3555
+
name = "unicode-xid"
3556
+
version = "0.2.6"
3557
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3558
+
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
3559
+
3560
+
[[package]]
3561
+
name = "unsigned-varint"
3562
+
version = "0.8.0"
3563
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3564
+
checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06"
3565
3566
[[package]]
3567
name = "untrusted"
···
3571
3572
[[package]]
3573
name = "url"
3574
+
version = "2.5.8"
3575
source = "registry+https://github.com/rust-lang/crates.io-index"
3576
+
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
3577
dependencies = [
3578
"form_urlencoded",
3579
"idna",
···
3600
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
3601
3602
[[package]]
3603
+
name = "varint-rs"
3604
+
version = "2.2.0"
3605
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3606
+
checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
3607
+
3608
+
[[package]]
3609
name = "vcpkg"
3610
version = "0.2.15"
3611
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3618
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
3619
3620
[[package]]
3621
+
name = "walkdir"
3622
+
version = "2.5.0"
3623
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3624
+
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
3625
+
dependencies = [
3626
+
"same-file",
3627
+
"winapi-util",
3628
+
]
3629
+
3630
+
[[package]]
3631
name = "want"
3632
version = "0.3.1"
3633
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3644
3645
[[package]]
3646
name = "wasi"
3647
+
version = "0.14.7+wasi-0.2.4"
3648
source = "registry+https://github.com/rust-lang/crates.io-index"
3649
+
checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
3650
dependencies = [
3651
"wasip2",
3652
]
3653
3654
[[package]]
3655
name = "wasip2"
3656
+
version = "1.0.2+wasi-0.2.9"
3657
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3658
+
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
3659
+
dependencies = [
3660
+
"wit-bindgen",
3661
+
]
3662
+
3663
+
[[package]]
3664
+
name = "wasip3"
3665
+
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
3666
source = "registry+https://github.com/rust-lang/crates.io-index"
3667
+
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
3668
dependencies = [
3669
"wit-bindgen",
3670
]
3671
3672
[[package]]
3673
name = "wasite"
3674
+
version = "1.0.2"
3675
source = "registry+https://github.com/rust-lang/crates.io-index"
3676
+
checksum = "66fe902b4a6b8028a753d5424909b764ccf79b7a209eac9bf97e59cda9f71a42"
3677
+
dependencies = [
3678
+
"wasi 0.14.7+wasi-0.2.4",
3679
+
]
3680
3681
[[package]]
3682
name = "wasm-bindgen"
3683
+
version = "0.2.109"
3684
source = "registry+https://github.com/rust-lang/crates.io-index"
3685
+
checksum = "9ff9c7baef35ac3c0e17d8bfc9ad75eb62f85a2f02bccc906699dadb0aa9c622"
3686
dependencies = [
3687
"cfg-if",
3688
"once_cell",
···
3692
]
3693
3694
[[package]]
3695
name = "wasm-bindgen-futures"
3696
+
version = "0.4.59"
3697
source = "registry+https://github.com/rust-lang/crates.io-index"
3698
+
checksum = "d24699cd39db9966cf6e2ef10d2f72779c961ad905911f395ea201c3ec9f545d"
3699
dependencies = [
3700
"cfg-if",
3701
+
"futures-util",
3702
"js-sys",
3703
"once_cell",
3704
"wasm-bindgen",
···
3707
3708
[[package]]
3709
name = "wasm-bindgen-macro"
3710
+
version = "0.2.109"
3711
source = "registry+https://github.com/rust-lang/crates.io-index"
3712
+
checksum = "39455e84ad887a0bbc93c116d72403f1bb0a39e37dd6f235a43e2128a0c7f1fd"
3713
dependencies = [
3714
"quote",
3715
"wasm-bindgen-macro-support",
···
3717
3718
[[package]]
3719
name = "wasm-bindgen-macro-support"
3720
+
version = "0.2.109"
3721
source = "registry+https://github.com/rust-lang/crates.io-index"
3722
+
checksum = "dff4761f60b0b51fd13fec8764167b7bbcc34498ce3e52805fe1db6f2d56b6d6"
3723
dependencies = [
3724
+
"bumpalo",
3725
"proc-macro2",
3726
"quote",
3727
"syn",
3728
"wasm-bindgen-shared",
3729
]
3730
3731
[[package]]
3732
name = "wasm-bindgen-shared"
3733
+
version = "0.2.109"
3734
source = "registry+https://github.com/rust-lang/crates.io-index"
3735
+
checksum = "bc6a171c53d98021a93a474c4a4579d76ba97f9517d871bc12e27640f218b6dd"
3736
dependencies = [
3737
"unicode-ident",
3738
]
3739
3740
[[package]]
3741
+
name = "wasm-encoder"
3742
+
version = "0.244.0"
3743
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3744
+
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
3745
+
dependencies = [
3746
+
"leb128fmt",
3747
+
"wasmparser",
3748
+
]
3749
+
3750
+
[[package]]
3751
+
name = "wasm-metadata"
3752
+
version = "0.244.0"
3753
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3754
+
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
3755
+
dependencies = [
3756
+
"anyhow",
3757
+
"indexmap",
3758
+
"wasm-encoder",
3759
+
"wasmparser",
3760
+
]
3761
+
3762
+
[[package]]
3763
name = "wasm-streams"
3764
version = "0.4.2"
3765
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3788
]
3789
3790
[[package]]
3791
+
name = "wasmparser"
3792
+
version = "0.244.0"
3793
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3794
+
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
3795
+
dependencies = [
3796
+
"bitflags 2.11.0",
3797
+
"hashbrown 0.15.5",
3798
+
"indexmap",
3799
+
"semver",
3800
+
]
3801
+
3802
+
[[package]]
3803
name = "web-sys"
3804
+
version = "0.3.86"
3805
source = "registry+https://github.com/rust-lang/crates.io-index"
3806
+
checksum = "668fa5d00434e890a452ab060d24e3904d1be93f7bb01b70e5603baa2b8ab23b"
3807
dependencies = [
3808
"js-sys",
3809
"wasm-bindgen",
···
3821
3822
[[package]]
3823
name = "whoami"
3824
+
version = "2.1.1"
3825
source = "registry+https://github.com/rust-lang/crates.io-index"
3826
+
checksum = "d6a5b12f9df4f978d2cfdb1bd3bac52433f44393342d7ee9c25f5a1c14c0f45d"
3827
dependencies = [
3828
+
"libc",
3829
"libredox",
3830
+
"objc2-system-configuration",
3831
"wasite",
3832
"web-sys",
3833
]
3834
3835
[[package]]
3836
name = "wildmatch"
3837
+
version = "2.6.1"
3838
source = "registry+https://github.com/rust-lang/crates.io-index"
3839
+
checksum = "29333c3ea1ba8b17211763463ff24ee84e41c78224c16b001cd907e663a38c68"
3840
3841
[[package]]
3842
name = "winapi"
···
3855
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
3856
3857
[[package]]
3858
+
name = "winapi-util"
3859
+
version = "0.1.11"
3860
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3861
+
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
3862
+
dependencies = [
3863
+
"windows-sys 0.61.2",
3864
+
]
3865
+
3866
+
[[package]]
3867
name = "winapi-x86_64-pc-windows-gnu"
3868
version = "0.4.0"
3869
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3871
3872
[[package]]
3873
name = "windows-core"
3874
+
version = "0.62.2"
3875
source = "registry+https://github.com/rust-lang/crates.io-index"
3876
+
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
3877
dependencies = [
3878
"windows-implement",
3879
"windows-interface",
3880
+
"windows-link",
3881
"windows-result",
3882
"windows-strings",
3883
]
3884
3885
[[package]]
3886
name = "windows-implement"
3887
+
version = "0.60.2"
3888
source = "registry+https://github.com/rust-lang/crates.io-index"
3889
+
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
3890
dependencies = [
3891
"proc-macro2",
3892
"quote",
···
3895
3896
[[package]]
3897
name = "windows-interface"
3898
+
version = "0.59.3"
3899
source = "registry+https://github.com/rust-lang/crates.io-index"
3900
+
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
3901
dependencies = [
3902
"proc-macro2",
3903
"quote",
···
3906
3907
[[package]]
3908
name = "windows-link"
3909
+
version = "0.2.1"
3910
source = "registry+https://github.com/rust-lang/crates.io-index"
3911
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
3912
3913
[[package]]
3914
name = "windows-registry"
3915
+
version = "0.6.1"
3916
source = "registry+https://github.com/rust-lang/crates.io-index"
3917
+
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
3918
dependencies = [
3919
+
"windows-link",
3920
"windows-result",
3921
"windows-strings",
3922
]
3923
3924
[[package]]
3925
name = "windows-result"
3926
+
version = "0.4.1"
3927
source = "registry+https://github.com/rust-lang/crates.io-index"
3928
+
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
3929
dependencies = [
3930
+
"windows-link",
3931
]
3932
3933
[[package]]
3934
name = "windows-strings"
3935
+
version = "0.5.1"
3936
source = "registry+https://github.com/rust-lang/crates.io-index"
3937
+
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
3938
dependencies = [
3939
+
"windows-link",
3940
]
3941
3942
[[package]]
···
3950
3951
[[package]]
3952
name = "windows-sys"
3953
version = "0.60.2"
3954
source = "registry+https://github.com/rust-lang/crates.io-index"
3955
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
3956
dependencies = [
3957
+
"windows-targets 0.53.5",
3958
]
3959
3960
[[package]]
3961
name = "windows-sys"
3962
+
version = "0.61.2"
3963
source = "registry+https://github.com/rust-lang/crates.io-index"
3964
+
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
3965
dependencies = [
3966
+
"windows-link",
3967
]
3968
3969
[[package]]
···
3984
3985
[[package]]
3986
name = "windows-targets"
3987
+
version = "0.53.5"
3988
source = "registry+https://github.com/rust-lang/crates.io-index"
3989
+
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
3990
dependencies = [
3991
+
"windows-link",
3992
+
"windows_aarch64_gnullvm 0.53.1",
3993
+
"windows_aarch64_msvc 0.53.1",
3994
+
"windows_i686_gnu 0.53.1",
3995
+
"windows_i686_gnullvm 0.53.1",
3996
+
"windows_i686_msvc 0.53.1",
3997
+
"windows_x86_64_gnu 0.53.1",
3998
+
"windows_x86_64_gnullvm 0.53.1",
3999
+
"windows_x86_64_msvc 0.53.1",
4000
]
4001
4002
[[package]]
···
4007
4008
[[package]]
4009
name = "windows_aarch64_gnullvm"
4010
+
version = "0.53.1"
4011
source = "registry+https://github.com/rust-lang/crates.io-index"
4012
+
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
4013
4014
[[package]]
4015
name = "windows_aarch64_msvc"
···
4019
4020
[[package]]
4021
name = "windows_aarch64_msvc"
4022
+
version = "0.53.1"
4023
source = "registry+https://github.com/rust-lang/crates.io-index"
4024
+
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
4025
4026
[[package]]
4027
name = "windows_i686_gnu"
···
4031
4032
[[package]]
4033
name = "windows_i686_gnu"
4034
+
version = "0.53.1"
4035
source = "registry+https://github.com/rust-lang/crates.io-index"
4036
+
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
4037
4038
[[package]]
4039
name = "windows_i686_gnullvm"
···
4043
4044
[[package]]
4045
name = "windows_i686_gnullvm"
4046
+
version = "0.53.1"
4047
source = "registry+https://github.com/rust-lang/crates.io-index"
4048
+
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
4049
4050
[[package]]
4051
name = "windows_i686_msvc"
···
4055
4056
[[package]]
4057
name = "windows_i686_msvc"
4058
+
version = "0.53.1"
4059
source = "registry+https://github.com/rust-lang/crates.io-index"
4060
+
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
4061
4062
[[package]]
4063
name = "windows_x86_64_gnu"
···
4067
4068
[[package]]
4069
name = "windows_x86_64_gnu"
4070
+
version = "0.53.1"
4071
source = "registry+https://github.com/rust-lang/crates.io-index"
4072
+
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
4073
4074
[[package]]
4075
name = "windows_x86_64_gnullvm"
···
4079
4080
[[package]]
4081
name = "windows_x86_64_gnullvm"
4082
+
version = "0.53.1"
4083
source = "registry+https://github.com/rust-lang/crates.io-index"
4084
+
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
4085
4086
[[package]]
4087
name = "windows_x86_64_msvc"
···
4091
4092
[[package]]
4093
name = "windows_x86_64_msvc"
4094
+
version = "0.53.1"
4095
source = "registry+https://github.com/rust-lang/crates.io-index"
4096
+
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
4097
4098
[[package]]
4099
name = "winnow"
4100
+
version = "0.7.14"
4101
source = "registry+https://github.com/rust-lang/crates.io-index"
4102
+
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
4103
dependencies = [
4104
"memchr",
4105
]
4106
4107
[[package]]
4108
name = "wit-bindgen"
4109
+
version = "0.51.0"
4110
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4111
+
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
4112
+
dependencies = [
4113
+
"wit-bindgen-rust-macro",
4114
+
]
4115
+
4116
+
[[package]]
4117
+
name = "wit-bindgen-core"
4118
+
version = "0.51.0"
4119
source = "registry+https://github.com/rust-lang/crates.io-index"
4120
+
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
4121
+
dependencies = [
4122
+
"anyhow",
4123
+
"heck",
4124
+
"wit-parser",
4125
+
]
4126
+
4127
+
[[package]]
4128
+
name = "wit-bindgen-rust"
4129
+
version = "0.51.0"
4130
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4131
+
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
4132
+
dependencies = [
4133
+
"anyhow",
4134
+
"heck",
4135
+
"indexmap",
4136
+
"prettyplease",
4137
+
"syn",
4138
+
"wasm-metadata",
4139
+
"wit-bindgen-core",
4140
+
"wit-component",
4141
+
]
4142
+
4143
+
[[package]]
4144
+
name = "wit-bindgen-rust-macro"
4145
+
version = "0.51.0"
4146
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4147
+
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
4148
+
dependencies = [
4149
+
"anyhow",
4150
+
"prettyplease",
4151
+
"proc-macro2",
4152
+
"quote",
4153
+
"syn",
4154
+
"wit-bindgen-core",
4155
+
"wit-bindgen-rust",
4156
+
]
4157
+
4158
+
[[package]]
4159
+
name = "wit-component"
4160
+
version = "0.244.0"
4161
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4162
+
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
4163
+
dependencies = [
4164
+
"anyhow",
4165
+
"bitflags 2.11.0",
4166
+
"indexmap",
4167
+
"log",
4168
+
"serde",
4169
+
"serde_derive",
4170
+
"serde_json",
4171
+
"wasm-encoder",
4172
+
"wasm-metadata",
4173
+
"wasmparser",
4174
+
"wit-parser",
4175
+
]
4176
+
4177
+
[[package]]
4178
+
name = "wit-parser"
4179
+
version = "0.244.0"
4180
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4181
+
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
4182
+
dependencies = [
4183
+
"anyhow",
4184
+
"id-arena",
4185
+
"indexmap",
4186
+
"log",
4187
+
"semver",
4188
+
"serde",
4189
+
"serde_derive",
4190
+
"serde_json",
4191
+
"unicode-xid",
4192
+
"wasmparser",
4193
+
]
4194
4195
[[package]]
4196
name = "writeable"
4197
+
version = "0.6.2"
4198
source = "registry+https://github.com/rust-lang/crates.io-index"
4199
+
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
4200
4201
[[package]]
4202
name = "x509-parser"
···
4211
"nom",
4212
"oid-registry",
4213
"rusticata-macros",
4214
+
"thiserror 2.0.18",
4215
"time",
4216
]
4217
+
4218
+
[[package]]
4219
+
name = "xxhash-rust"
4220
+
version = "0.8.15"
4221
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4222
+
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
4223
4224
[[package]]
4225
name = "yasna"
···
4232
4233
[[package]]
4234
name = "yoke"
4235
+
version = "0.8.1"
4236
source = "registry+https://github.com/rust-lang/crates.io-index"
4237
+
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
4238
dependencies = [
4239
"stable_deref_trait",
4240
"yoke-derive",
4241
"zerofrom",
···
4243
4244
[[package]]
4245
name = "yoke-derive"
4246
+
version = "0.8.1"
4247
source = "registry+https://github.com/rust-lang/crates.io-index"
4248
+
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
4249
dependencies = [
4250
"proc-macro2",
4251
"quote",
···
4255
4256
[[package]]
4257
name = "zerocopy"
4258
+
version = "0.8.39"
4259
source = "registry+https://github.com/rust-lang/crates.io-index"
4260
+
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
4261
dependencies = [
4262
"zerocopy-derive",
4263
]
4264
4265
[[package]]
4266
name = "zerocopy-derive"
4267
+
version = "0.8.39"
4268
source = "registry+https://github.com/rust-lang/crates.io-index"
4269
+
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
4270
dependencies = [
4271
"proc-macro2",
4272
"quote",
···
4296
4297
[[package]]
4298
name = "zeroize"
4299
+
version = "1.8.2"
4300
source = "registry+https://github.com/rust-lang/crates.io-index"
4301
+
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
4302
4303
[[package]]
4304
name = "zerotrie"
4305
+
version = "0.2.3"
4306
source = "registry+https://github.com/rust-lang/crates.io-index"
4307
+
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
4308
dependencies = [
4309
"displaydoc",
4310
"yoke",
···
4313
4314
[[package]]
4315
name = "zerovec"
4316
+
version = "0.11.5"
4317
source = "registry+https://github.com/rust-lang/crates.io-index"
4318
+
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
4319
dependencies = [
4320
"yoke",
4321
"zerofrom",
···
4324
4325
[[package]]
4326
name = "zerovec-derive"
4327
+
version = "0.11.2"
4328
source = "registry+https://github.com/rust-lang/crates.io-index"
4329
+
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
4330
dependencies = [
4331
"proc-macro2",
4332
"quote",
4333
"syn",
4334
]
4335
+
4336
+
[[package]]
4337
+
name = "zmij"
4338
+
version = "1.0.21"
4339
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4340
+
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
4341
4342
[[package]]
4343
name = "zstd"
+15
Cargo.toml
+15
Cargo.toml
···
6
edition = "2024"
7
default-run = "allegedly"
8
9
[dependencies]
10
anyhow = "1.0.99"
11
async-compression = { version = "0.4.30", features = ["futures-io", "tokio", "gzip"] }
12
chrono = { version = "0.4.42", features = ["serde"] }
13
clap = { version = "4.5.47", features = ["derive", "env"] }
14
futures = "0.3.31"
15
governor = "0.10.1"
16
http-body-util = "0.1.3"
···
35
tracing = "0.1.41"
36
tracing-opentelemetry = "0.31.0"
37
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
···
6
edition = "2024"
7
default-run = "allegedly"
8
9
+
10
+
[dev-dependencies]
11
+
criterion = "0.8.2"
12
+
rmp-serde = "1.3.1"
13
+
tempfile = "3.10.1"
14
+
15
[dependencies]
16
anyhow = "1.0.99"
17
+
async-trait = "0.1"
18
async-compression = { version = "0.4.30", features = ["futures-io", "tokio", "gzip"] }
19
chrono = { version = "0.4.42", features = ["serde"] }
20
clap = { version = "4.5.47", features = ["derive", "env"] }
21
+
fjall = "3.0.2"
22
futures = "0.3.31"
23
governor = "0.10.1"
24
http-body-util = "0.1.3"
···
43
tracing = "0.1.41"
44
tracing-opentelemetry = "0.31.0"
45
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
46
+
data-encoding = "2.10.0"
47
+
cid = "0.11.1"
48
+
rmp-serde = "1.3.1"
49
+
bincode = "1.3.3"
50
+
serde_bytes = "0.11.19"
51
+
multibase = "0.9.2"
52
+
+61
flake.lock
+61
flake.lock
···
···
1
+
{
2
+
"nodes": {
3
+
"nixpkgs": {
4
+
"locked": {
5
+
"lastModified": 1771177547,
6
+
"narHash": "sha256-trTtk3WTOHz7hSw89xIIvahkgoFJYQ0G43IlqprFoMA=",
7
+
"owner": "nixos",
8
+
"repo": "nixpkgs",
9
+
"rev": "ac055f38c798b0d87695240c7b761b82fc7e5bc2",
10
+
"type": "github"
11
+
},
12
+
"original": {
13
+
"owner": "nixos",
14
+
"ref": "nixpkgs-unstable",
15
+
"repo": "nixpkgs",
16
+
"type": "github"
17
+
}
18
+
},
19
+
"nixpkgs-lib": {
20
+
"locked": {
21
+
"lastModified": 1769909678,
22
+
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
23
+
"owner": "nix-community",
24
+
"repo": "nixpkgs.lib",
25
+
"rev": "72716169fe93074c333e8d0173151350670b824c",
26
+
"type": "github"
27
+
},
28
+
"original": {
29
+
"owner": "nix-community",
30
+
"repo": "nixpkgs.lib",
31
+
"type": "github"
32
+
}
33
+
},
34
+
"parts": {
35
+
"inputs": {
36
+
"nixpkgs-lib": "nixpkgs-lib"
37
+
},
38
+
"locked": {
39
+
"lastModified": 1769996383,
40
+
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
41
+
"owner": "hercules-ci",
42
+
"repo": "flake-parts",
43
+
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
44
+
"type": "github"
45
+
},
46
+
"original": {
47
+
"owner": "hercules-ci",
48
+
"repo": "flake-parts",
49
+
"type": "github"
50
+
}
51
+
},
52
+
"root": {
53
+
"inputs": {
54
+
"nixpkgs": "nixpkgs",
55
+
"parts": "parts"
56
+
}
57
+
}
58
+
},
59
+
"root": "root",
60
+
"version": 7
61
+
}
+33
flake.nix
+33
flake.nix
···
···
1
+
{
2
+
inputs.parts.url = "github:hercules-ci/flake-parts";
3
+
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
4
+
5
+
outputs =
6
+
inp:
7
+
inp.parts.lib.mkFlake { inputs = inp; } {
8
+
systems = [ "x86_64-linux" ];
9
+
perSystem =
10
+
{
11
+
pkgs,
12
+
config,
13
+
...
14
+
}:
15
+
{
16
+
packages.default = pkgs.callPackage ./default.nix {};
17
+
devShells = {
18
+
default = pkgs.mkShell {
19
+
packages = with pkgs; [
20
+
rustPlatform.rustLibSrc
21
+
rust-analyzer
22
+
cargo
23
+
cargo-outdated
24
+
rustc
25
+
rustfmt
26
+
openssl
27
+
pkg-config
28
+
];
29
+
};
30
+
};
31
+
};
32
+
};
33
+
}
+11
-1
readme.md
+11
-1
readme.md
···
15
--wrap-pg "postgresql://user:pass@pg-host:5432/plc-db"
16
```
17
18
- Wrap a plc server, maximalist edition:
19
20
```bash
···
89
- [ ] experimental: websocket version of /export
90
- [x] experimental: accept writes by forwarding them upstream
91
- [ ] experimental: serve a tlog
92
-
- [ ] experimental: embed a log database directly for fast and efficient mirroring
93
- [ ] experimental: support multiple upstreams?
94
95
- [ ] new command todo: `zip` or `check` or `diff`: compare two plc logs over some time range
···
15
--wrap-pg "postgresql://user:pass@pg-host:5432/plc-db"
16
```
17
18
+
- Run a fully self-contained mirror using an embedded fjall database (no postgres needed):
19
+
20
+
```bash
21
+
# backfill first
22
+
allegedly backfill --to-fjall ./plc-data
23
+
24
+
# then run the mirror
25
+
allegedly mirror --wrap-fjall ./plc-data
26
+
```
27
+
28
- Wrap a plc server, maximalist edition:
29
30
```bash
···
99
- [ ] experimental: websocket version of /export
100
- [x] experimental: accept writes by forwarding them upstream
101
- [ ] experimental: serve a tlog
102
+
- [x] experimental: embed a log database directly for fast and efficient mirroring
103
- [ ] experimental: support multiple upstreams?
104
105
- [ ] new command todo: `zip` or `check` or `diff`: compare two plc logs over some time range
+49
-5
src/bin/backfill.rs
+49
-5
src/bin/backfill.rs
···
1
use allegedly::{
2
-
Db, Dt, ExportPage, FolderSource, HttpSource, backfill, backfill_to_pg,
3
bin::{GlobalArgs, bin_init},
4
-
full_pages, logo, pages_to_pg, pages_to_stdout, poll_upstream,
5
};
6
use clap::Parser;
7
use reqwest::Url;
···
22
/// Local folder to fetch bundles from (overrides `http`)
23
#[arg(long)]
24
dir: Option<PathBuf>,
25
/// Don't do weekly bulk-loading at all.
26
///
27
/// overrides `http` and `dir`, makes catch_up redundant
···
45
/// only used if `--to-postgres` is present
46
#[arg(long, action)]
47
postgres_reset: bool,
48
/// Stop at the week ending before this date
49
#[arg(long)]
50
until: Option<Dt>,
···
61
Args {
62
http,
63
dir,
64
no_bulk,
65
source_workers,
66
to_postgres,
67
postgres_cert,
68
postgres_reset,
69
until,
70
catch_up,
71
}: Args,
···
105
let throttle = Duration::from_millis(upstream_throttle_ms);
106
tasks.spawn(poll_upstream(None, upstream, throttle, poll_tx));
107
tasks.spawn(full_pages(poll_out, full_tx));
108
-
tasks.spawn(pages_to_stdout(full_out, None));
109
} else {
110
// fun mode
111
112
// set up bulk sources
113
-
if let Some(dir) = dir {
114
if http != DEFAULT_HTTP.parse()? {
115
anyhow::bail!(
116
"non-default bulk http setting can't be used with bulk dir setting ({dir:?})"
···
143
}
144
145
// set up sinks
146
-
if let Some(pg_url) = to_postgres {
147
log::trace!("connecting to postgres...");
148
let db = Db::new(pg_url.as_str(), postgres_cert).await?;
149
log::trace!("connected to postgres");
···
1
use allegedly::{
2
+
Db, Dt, ExportPage, FjallDb, FolderSource, HttpSource, backfill, backfill_to_fjall,
3
+
backfill_to_pg,
4
bin::{GlobalArgs, bin_init},
5
+
full_pages, logo, pages_to_fjall, pages_to_pg, pages_to_stdout, poll_upstream,
6
};
7
use clap::Parser;
8
use reqwest::Url;
···
23
/// Local folder to fetch bundles from (overrides `http`)
24
#[arg(long)]
25
dir: Option<PathBuf>,
26
+
/// Local fjall database to fetch raw ops from (overrides `http` and `dir`)
27
+
#[arg(long, conflicts_with_all = ["dir"])]
28
+
from_fjall: Option<PathBuf>,
29
/// Don't do weekly bulk-loading at all.
30
///
31
/// overrides `http` and `dir`, makes catch_up redundant
···
49
/// only used if `--to-postgres` is present
50
#[arg(long, action)]
51
postgres_reset: bool,
52
+
/// Bulk load into a local fjall embedded database
53
+
///
54
+
/// Pass a directory path for the fjall database
55
+
#[arg(long, conflicts_with_all = ["to_postgres", "postgres_cert", "postgres_reset"])]
56
+
to_fjall: Option<PathBuf>,
57
+
/// Delete all operations from the fjall db before starting
58
+
///
59
+
/// only used if `--to-fjall` is present
60
+
#[arg(long, action, requires = "to_fjall")]
61
+
fjall_reset: bool,
62
/// Stop at the week ending before this date
63
#[arg(long)]
64
until: Option<Dt>,
···
75
Args {
76
http,
77
dir,
78
+
from_fjall,
79
no_bulk,
80
source_workers,
81
to_postgres,
82
postgres_cert,
83
postgres_reset,
84
+
to_fjall,
85
+
fjall_reset,
86
until,
87
catch_up,
88
}: Args,
···
122
let throttle = Duration::from_millis(upstream_throttle_ms);
123
tasks.spawn(poll_upstream(None, upstream, throttle, poll_tx));
124
tasks.spawn(full_pages(poll_out, full_tx));
125
+
if let Some(fjall_path) = to_fjall {
126
+
log::trace!("opening fjall db at {fjall_path:?}...");
127
+
let db = FjallDb::open(&fjall_path)?;
128
+
log::trace!("opened fjall db");
129
+
130
+
tasks.spawn(pages_to_fjall(db, full_out));
131
+
} else {
132
+
tasks.spawn(pages_to_stdout(full_out, None));
133
+
}
134
} else {
135
// fun mode
136
137
// set up bulk sources
138
+
if let Some(fjall_path) = from_fjall {
139
+
log::trace!("opening source fjall db at {fjall_path:?}...");
140
+
let db = FjallDb::open(&fjall_path)?;
141
+
log::trace!("opened source fjall db");
142
+
tasks.spawn(backfill(db, bulk_tx, source_workers.unwrap_or(4), until));
143
+
} else if let Some(dir) = dir {
144
if http != DEFAULT_HTTP.parse()? {
145
anyhow::bail!(
146
"non-default bulk http setting can't be used with bulk dir setting ({dir:?})"
···
173
}
174
175
// set up sinks
176
+
if let Some(fjall_path) = to_fjall {
177
+
log::trace!("opening fjall db at {fjall_path:?}...");
178
+
let db = FjallDb::open(&fjall_path)?;
179
+
log::trace!("opened fjall db");
180
+
181
+
tasks.spawn(backfill_to_fjall(
182
+
db.clone(),
183
+
fjall_reset,
184
+
bulk_out,
185
+
found_last_tx,
186
+
));
187
+
if catch_up {
188
+
tasks.spawn(pages_to_fjall(db, full_out));
189
+
}
190
+
} else if let Some(pg_url) = to_postgres {
191
log::trace!("connecting to postgres...");
192
let db = Db::new(pg_url.as_str(), postgres_cert).await?;
193
log::trace!("connected to postgres");
+60
-25
src/bin/mirror.rs
+60
-25
src/bin/mirror.rs
···
1
use allegedly::{
2
-
Db, ExperimentalConf, ListenConf,
3
bin::{GlobalArgs, InstrumentationArgs, bin_init},
4
-
logo, pages_to_pg, poll_upstream, serve,
5
};
6
use clap::Parser;
7
use reqwest::Url;
···
10
11
#[derive(Debug, clap::Args)]
12
pub struct Args {
13
-
/// the wrapped did-method-plc server
14
#[arg(long, env = "ALLEGEDLY_WRAP")]
15
-
wrap: Url,
16
/// the wrapped did-method-plc server's database (write access required)
17
-
#[arg(long, env = "ALLEGEDLY_WRAP_PG")]
18
wrap_pg: Option<Url>,
19
/// path to tls cert for the wrapped postgres db, if needed
20
#[arg(long, env = "ALLEGEDLY_WRAP_PG_CERT")]
21
wrap_pg_cert: Option<PathBuf>,
22
/// wrapping server listen address
23
#[arg(short, long, env = "ALLEGEDLY_BIND")]
24
#[clap(default_value = "127.0.0.1:8000")]
···
70
wrap,
71
wrap_pg,
72
wrap_pg_cert,
73
bind,
74
acme_domain,
75
acme_cache_path,
···
106
107
let mut tasks = JoinSet::new();
108
109
-
let db = if sync {
110
-
let wrap_pg = wrap_pg.ok_or(anyhow::anyhow!(
111
-
"a wrapped reference postgres must be provided to sync"
112
-
))?;
113
-
let db = Db::new(wrap_pg.as_str(), wrap_pg_cert).await?;
114
115
-
// TODO: allow starting up with polling backfill from beginning?
116
-
log::debug!("getting the latest op from the db...");
117
let latest = db
118
-
.get_latest()
119
-
.await?
120
.expect("there to be at least one op in the db. did you backfill?");
121
122
let (send_page, recv_page) = mpsc::channel(8);
123
···
126
let throttle = Duration::from_millis(upstream_throttle_ms);
127
128
tasks.spawn(poll_upstream(Some(latest), poll_url, throttle, send_page));
129
-
tasks.spawn(pages_to_pg(db.clone(), recv_page));
130
-
Some(db)
131
} else {
132
-
None
133
-
};
134
135
-
tasks.spawn(serve(
136
-
upstream,
137
-
wrap,
138
-
listen_conf,
139
-
experimental_conf,
140
-
db.clone(),
141
-
));
142
143
while let Some(next) = tasks.join_next().await {
144
match next {
···
1
use allegedly::{
2
+
Db, ExperimentalConf, FjallDb, ListenConf,
3
bin::{GlobalArgs, InstrumentationArgs, bin_init},
4
+
logo, pages_to_fjall, pages_to_pg, poll_upstream, serve, serve_fjall,
5
};
6
use clap::Parser;
7
use reqwest::Url;
···
10
11
#[derive(Debug, clap::Args)]
12
pub struct Args {
13
+
/// the wrapped did-method-plc server (not needed when using --wrap-fjall)
14
#[arg(long, env = "ALLEGEDLY_WRAP")]
15
+
wrap: Option<Url>,
16
/// the wrapped did-method-plc server's database (write access required)
17
+
#[arg(long, env = "ALLEGEDLY_WRAP_PG", conflicts_with = "wrap_fjall")]
18
wrap_pg: Option<Url>,
19
/// path to tls cert for the wrapped postgres db, if needed
20
#[arg(long, env = "ALLEGEDLY_WRAP_PG_CERT")]
21
wrap_pg_cert: Option<PathBuf>,
22
+
/// path to a local fjall database directory (alternative to postgres)
23
+
#[arg(long, env = "ALLEGEDLY_WRAP_FJALL", conflicts_with_all = ["wrap_pg", "wrap_pg_cert"])]
24
+
wrap_fjall: Option<PathBuf>,
25
+
/// compact the fjall db on startup
26
+
#[arg(
27
+
long,
28
+
env = "ALLEGEDLY_FJALL_COMPACT",
29
+
conflicts_with_all = ["wrap_pg", "wrap_pg_cert"]
30
+
)]
31
+
compact_fjall: bool,
32
/// wrapping server listen address
33
#[arg(short, long, env = "ALLEGEDLY_BIND")]
34
#[clap(default_value = "127.0.0.1:8000")]
···
80
wrap,
81
wrap_pg,
82
wrap_pg_cert,
83
+
wrap_fjall,
84
+
compact_fjall,
85
bind,
86
acme_domain,
87
acme_cache_path,
···
118
119
let mut tasks = JoinSet::new();
120
121
+
if let Some(fjall_path) = wrap_fjall {
122
+
let db = FjallDb::open(&fjall_path)?;
123
+
if compact_fjall {
124
+
log::info!("compacting fjall...");
125
+
db.compact()?; // blocking here is fine, we didn't start anything yet
126
+
}
127
128
+
log::debug!("getting the latest op from fjall...");
129
let latest = db
130
+
.get_latest()?
131
.expect("there to be at least one op in the db. did you backfill?");
132
+
log::info!("starting polling from {latest}...");
133
134
let (send_page, recv_page) = mpsc::channel(8);
135
···
138
let throttle = Duration::from_millis(upstream_throttle_ms);
139
140
tasks.spawn(poll_upstream(Some(latest), poll_url, throttle, send_page));
141
+
tasks.spawn(pages_to_fjall(db.clone(), recv_page));
142
+
143
+
tasks.spawn(serve_fjall(upstream, listen_conf, experimental_conf, db));
144
} else {
145
+
let wrap = wrap.ok_or(anyhow::anyhow!(
146
+
"--wrap is required unless using --wrap-fjall"
147
+
))?;
148
+
149
+
let db: Option<Db> = if sync {
150
+
let wrap_pg = wrap_pg.ok_or(anyhow::anyhow!(
151
+
"a wrapped reference postgres (--wrap-pg) or fjall db (--wrap-fjall) must be provided to sync"
152
+
))?;
153
+
let db = Db::new(wrap_pg.as_str(), wrap_pg_cert).await?;
154
155
+
log::debug!("getting the latest op from the db...");
156
+
let latest = db
157
+
.get_latest()
158
+
.await?
159
+
.expect("there to be at least one op in the db. did you backfill?");
160
+
log::debug!("starting polling from {latest}...");
161
+
162
+
let (send_page, recv_page) = mpsc::channel(8);
163
+
164
+
let mut poll_url = upstream.clone();
165
+
poll_url.set_path("/export");
166
+
let throttle = Duration::from_millis(upstream_throttle_ms);
167
+
168
+
tasks.spawn(poll_upstream(Some(latest), poll_url, throttle, send_page));
169
+
tasks.spawn(pages_to_pg(db.clone(), recv_page));
170
+
Some(db)
171
+
} else {
172
+
None
173
+
};
174
+
175
+
tasks.spawn(serve(upstream, wrap, listen_conf, experimental_conf, db));
176
+
}
177
178
while let Some(next) = tasks.join_next().await {
179
match next {
+404
src/doc.rs
+404
src/doc.rs
···
···
1
+
use serde::{Deserialize, Serialize};
2
+
use serde_json::Value;
3
+
use std::borrow::Cow;
4
+
use std::collections::BTreeMap;
5
+
6
+
pub type CowStr<'a> = Cow<'a, str>;
7
+
8
+
#[derive(Debug, Clone, Serialize, Deserialize)]
9
+
pub struct Service<'a> {
10
+
pub r#type: CowStr<'a>,
11
+
pub endpoint: CowStr<'a>,
12
+
}
13
+
14
+
#[derive(Debug, Clone, Serialize, Deserialize)]
15
+
#[serde(rename_all = "camelCase")]
16
+
pub struct DocumentData<'a> {
17
+
pub did: CowStr<'a>,
18
+
pub rotation_keys: Vec<CowStr<'a>>,
19
+
pub verification_methods: BTreeMap<CowStr<'a>, CowStr<'a>>,
20
+
pub also_known_as: Vec<CowStr<'a>>,
21
+
pub services: BTreeMap<CowStr<'a>, Service<'a>>,
22
+
}
23
+
24
+
#[derive(Debug, Clone, Serialize)]
25
+
#[serde(rename_all = "camelCase")]
26
+
pub struct DidDocument<'a> {
27
+
#[serde(rename = "@context")]
28
+
pub context: Vec<CowStr<'a>>,
29
+
pub id: CowStr<'a>,
30
+
pub also_known_as: Vec<CowStr<'a>>,
31
+
pub verification_method: Vec<VerificationMethod<'a>>,
32
+
pub service: Vec<DocService<'a>>,
33
+
}
34
+
35
+
#[derive(Debug, Clone, Serialize)]
36
+
#[serde(rename_all = "camelCase")]
37
+
pub struct VerificationMethod<'a> {
38
+
pub id: CowStr<'a>,
39
+
pub r#type: CowStr<'a>,
40
+
pub controller: CowStr<'a>,
41
+
pub public_key_multibase: CowStr<'a>,
42
+
}
43
+
44
+
#[derive(Debug, Clone, Serialize)]
45
+
#[serde(rename_all = "camelCase")]
46
+
pub struct DocService<'a> {
47
+
pub id: CowStr<'a>,
48
+
pub r#type: CowStr<'a>,
49
+
pub service_endpoint: CowStr<'a>,
50
+
}
51
+
52
+
const P256_PREFIX: &str = "zDn";
53
+
const SECP256K1_PREFIX: &str = "zQ3";
54
+
55
+
fn key_context(multibase: &str) -> Option<&'static str> {
56
+
if multibase.starts_with(P256_PREFIX) {
57
+
Some("https://w3id.org/security/suites/ecdsa-2019/v1")
58
+
} else if multibase.starts_with(SECP256K1_PREFIX) {
59
+
Some("https://w3id.org/security/suites/secp256k1-2019/v1")
60
+
} else {
61
+
None
62
+
}
63
+
}
64
+
65
+
pub fn format_did_doc<'a>(data: &'a DocumentData<'a>) -> DidDocument<'a> {
66
+
let mut context = vec![
67
+
"https://www.w3.org/ns/did/v1".into(),
68
+
"https://w3id.org/security/multikey/v1".into(),
69
+
];
70
+
71
+
let verification_method = data
72
+
.verification_methods
73
+
.iter()
74
+
.map(|(keyid, did_key)| {
75
+
let multibase: CowStr = did_key.strip_prefix("did:key:").unwrap_or(did_key).into();
76
+
77
+
if let Some(ctx) = key_context(&multibase) {
78
+
if !context.iter().any(|c| c == ctx) {
79
+
context.push(ctx.into());
80
+
}
81
+
}
82
+
VerificationMethod {
83
+
id: format!("{}#{keyid}", data.did).into(),
84
+
r#type: "Multikey".into(),
85
+
controller: data.did.clone(),
86
+
public_key_multibase: multibase,
87
+
}
88
+
})
89
+
.collect();
90
+
91
+
let service = data
92
+
.services
93
+
.iter()
94
+
.map(|(service_id, svc)| DocService {
95
+
id: format!("#{service_id}").into(),
96
+
r#type: svc.r#type.clone(),
97
+
service_endpoint: svc.endpoint.clone(),
98
+
})
99
+
.collect();
100
+
101
+
DidDocument {
102
+
context,
103
+
id: data.did.clone(),
104
+
also_known_as: data.also_known_as.clone(),
105
+
verification_method,
106
+
service,
107
+
}
108
+
}
109
+
110
+
fn ensure_atproto_prefix(s: &str) -> CowStr<'_> {
111
+
if s.starts_with("at://") {
112
+
return s.into();
113
+
}
114
+
let stripped = s
115
+
.strip_prefix("http://")
116
+
.or_else(|| s.strip_prefix("https://"))
117
+
.unwrap_or(s);
118
+
format!("at://{stripped}").into()
119
+
}
120
+
121
+
fn ensure_http_prefix(s: &str) -> CowStr<'_> {
122
+
if s.starts_with("http://") || s.starts_with("https://") {
123
+
return s.into();
124
+
}
125
+
format!("https://{s}").into()
126
+
}
127
+
128
+
/// extract DocumentData from a single operation json blob.
129
+
/// returns None for tombstones.
130
+
pub fn op_to_doc_data<'a>(did: &'a str, op: &'a Value) -> Option<DocumentData<'a>> {
131
+
// TODO: this shouldnt just short circuit to None, we should provide better information about whats missing in an error
132
+
let obj = op.as_object()?;
133
+
let op_type = obj.get("type")?.as_str()?;
134
+
135
+
match op_type {
136
+
"plc_tombstone" => None,
137
+
"create" => {
138
+
let signing_key = obj.get("signingKey")?.as_str()?;
139
+
let recovery_key = obj.get("recoveryKey")?.as_str()?;
140
+
let handle = obj.get("handle")?.as_str()?;
141
+
let service = obj.get("service")?.as_str()?;
142
+
143
+
let mut verification_methods = BTreeMap::new();
144
+
verification_methods.insert("atproto".into(), signing_key.into());
145
+
146
+
let mut services = BTreeMap::new();
147
+
services.insert(
148
+
"atproto_pds".into(),
149
+
Service {
150
+
r#type: "AtprotoPersonalDataServer".into(),
151
+
endpoint: ensure_http_prefix(service),
152
+
},
153
+
);
154
+
155
+
Some(DocumentData {
156
+
did: Cow::Borrowed(did),
157
+
rotation_keys: vec![Cow::Borrowed(recovery_key), Cow::Borrowed(signing_key)],
158
+
verification_methods,
159
+
also_known_as: vec![ensure_atproto_prefix(handle)],
160
+
services,
161
+
})
162
+
}
163
+
"plc_operation" => {
164
+
let rotation_keys = obj
165
+
.get("rotationKeys")?
166
+
.as_array()?
167
+
.iter()
168
+
.filter_map(|v| v.as_str().map(Cow::Borrowed))
169
+
.collect();
170
+
171
+
let verification_methods = obj
172
+
.get("verificationMethods")?
173
+
.as_object()?
174
+
.iter()
175
+
.filter_map(|(k, v)| Some((k.as_str().into(), v.as_str()?.into())))
176
+
.collect();
177
+
178
+
let also_known_as = obj
179
+
.get("alsoKnownAs")?
180
+
.as_array()?
181
+
.iter()
182
+
.filter_map(|v| v.as_str().map(Cow::Borrowed))
183
+
.collect();
184
+
185
+
let services = obj
186
+
.get("services")?
187
+
.as_object()?
188
+
.iter()
189
+
.filter_map(|(k, v)| {
190
+
let svc: Service = Service::deserialize(v).ok()?;
191
+
Some((k.as_str().into(), svc))
192
+
})
193
+
.collect();
194
+
195
+
Some(DocumentData {
196
+
did: did.into(),
197
+
rotation_keys,
198
+
verification_methods,
199
+
also_known_as,
200
+
services,
201
+
})
202
+
}
203
+
_ => None,
204
+
}
205
+
}
206
+
207
+
/// apply a sequence of operation JSON blobs and return the current document data.
208
+
/// returns None if the DID is tombstoned (last op is a tombstone).
209
+
pub fn apply_op_log<'a>(
210
+
did: &'a str,
211
+
ops: impl IntoIterator<Item = &'a Value>,
212
+
) -> Option<DocumentData<'a>> {
213
+
// TODO: we don't verify signature chain, we should do that...
214
+
ops.into_iter()
215
+
.last()
216
+
.and_then(|op| op_to_doc_data(did, op))
217
+
}
218
+
219
+
#[cfg(test)]
220
+
mod tests {
221
+
use super::*;
222
+
223
+
#[test]
224
+
fn normalize_legacy_create() {
225
+
let op = serde_json::json!({
226
+
"type": "create",
227
+
"signingKey": "did:key:zDnaeSigningKey",
228
+
"recoveryKey": "did:key:zQ3shRecoveryKey",
229
+
"handle": "alice.bsky.social",
230
+
"service": "pds.example.com",
231
+
"prev": null,
232
+
"sig": "abc"
233
+
});
234
+
235
+
let data = op_to_doc_data("did:plc:test", &op).unwrap();
236
+
assert_eq!(data.rotation_keys.len(), 2);
237
+
assert_eq!(data.rotation_keys[0], "did:key:zQ3shRecoveryKey");
238
+
assert_eq!(data.rotation_keys[1], "did:key:zDnaeSigningKey");
239
+
assert_eq!(
240
+
data.verification_methods.get("atproto").unwrap(),
241
+
"did:key:zDnaeSigningKey"
242
+
);
243
+
assert_eq!(data.also_known_as, vec!["at://alice.bsky.social"]);
244
+
let pds = data.services.get("atproto_pds").unwrap();
245
+
assert_eq!(pds.endpoint, "https://pds.example.com");
246
+
}
247
+
248
+
#[test]
249
+
fn format_doc_p256_context() {
250
+
let data = DocumentData {
251
+
did: "did:plc:test123".into(),
252
+
rotation_keys: vec!["did:key:zDnaeXYZ".into()],
253
+
verification_methods: {
254
+
let mut m = BTreeMap::new();
255
+
m.insert("atproto".into(), "did:key:zDnaeXYZ".into());
256
+
m
257
+
},
258
+
also_known_as: vec!["at://alice.test".into()],
259
+
services: {
260
+
let mut m = BTreeMap::new();
261
+
m.insert(
262
+
"atproto_pds".into(),
263
+
Service {
264
+
r#type: "AtprotoPersonalDataServer".into(),
265
+
endpoint: "https://pds.test".into(),
266
+
},
267
+
);
268
+
m
269
+
},
270
+
};
271
+
272
+
let doc = format_did_doc(&data);
273
+
assert_eq!(doc.context.len(), 3);
274
+
assert!(
275
+
doc.context
276
+
.iter()
277
+
.any(|c| c == "https://w3id.org/security/suites/ecdsa-2019/v1")
278
+
);
279
+
assert_eq!(doc.verification_method[0].public_key_multibase, "zDnaeXYZ");
280
+
assert_eq!(doc.verification_method[0].id, "did:plc:test123#atproto");
281
+
}
282
+
283
+
#[test]
284
+
fn tombstone_returns_none() {
285
+
let op = serde_json::json!({
286
+
"type": "plc_tombstone",
287
+
"prev": "bafyabc",
288
+
"sig": "xyz"
289
+
});
290
+
assert!(op_to_doc_data("did:plc:test", &op).is_none());
291
+
}
292
+
293
+
#[test]
294
+
fn apply_log_with_tombstone() {
295
+
let create = serde_json::json!({
296
+
"type": "plc_operation",
297
+
"rotationKeys": ["did:key:zQ3shKey1"],
298
+
"verificationMethods": {"atproto": "did:key:zDnaeKey1"},
299
+
"alsoKnownAs": ["at://alice.test"],
300
+
"services": {
301
+
"atproto_pds": {"type": "AtprotoPersonalDataServer", "service_endpoint": "https://pds.test"}
302
+
},
303
+
"prev": null,
304
+
"sig": "abc"
305
+
});
306
+
let tombstone = serde_json::json!({
307
+
"type": "plc_tombstone",
308
+
"prev": "bafyabc",
309
+
"sig": "xyz"
310
+
});
311
+
312
+
let ops = vec![create.clone()];
313
+
let result = apply_op_log("did:plc:test", &ops);
314
+
assert!(result.is_some());
315
+
316
+
let ops = vec![create, tombstone];
317
+
let result = apply_op_log("did:plc:test", &ops);
318
+
assert!(result.is_none());
319
+
}
320
+
321
+
fn load_fixture(name: &str) -> (String, Vec<Value>) {
322
+
let path = format!("tests/fixtures/{name}");
323
+
let data = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{path}: {e}"));
324
+
let entries: Vec<Value> = serde_json::from_str(&data).unwrap();
325
+
let did = entries[0]["did"].as_str().unwrap().to_string();
326
+
let ops: Vec<Value> = entries
327
+
.iter()
328
+
.filter(|e| !e["nullified"].as_bool().unwrap_or(false))
329
+
.map(|e| e["operation"].clone())
330
+
.collect();
331
+
(did, ops)
332
+
}
333
+
334
+
#[test]
335
+
fn interop_legacy_dholms() {
336
+
let (did, ops) = load_fixture("log_legacy_dholms.json");
337
+
assert_eq!(did, "did:plc:yk4dd2qkboz2yv6tpubpc6co");
338
+
339
+
let data = apply_op_log(&did, &ops).expect("should reconstruct");
340
+
assert_eq!(data.did, did);
341
+
assert_eq!(data.also_known_as, vec!["at://dholms.xyz"]);
342
+
assert_eq!(
343
+
data.services.get("atproto_pds").unwrap().endpoint,
344
+
"https://bsky.social"
345
+
);
346
+
assert_eq!(
347
+
data.verification_methods.get("atproto").unwrap(),
348
+
"did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
349
+
);
350
+
351
+
let doc = format_did_doc(&data);
352
+
assert_eq!(doc.id, did);
353
+
assert!(
354
+
doc.context
355
+
.iter()
356
+
.any(|c| c == "https://w3id.org/security/suites/secp256k1-2019/v1")
357
+
);
358
+
}
359
+
360
+
#[test]
361
+
fn interop_bskyapp() {
362
+
let (did, ops) = load_fixture("log_bskyapp.json");
363
+
assert_eq!(did, "did:plc:z72i7hdynmk6r22z27h6tvur");
364
+
365
+
let data = apply_op_log(&did, &ops).expect("should reconstruct");
366
+
println!("{:?}", data);
367
+
assert_eq!(data.also_known_as, vec!["at://bsky.app"]);
368
+
assert_eq!(
369
+
data.verification_methods.get("atproto").unwrap(),
370
+
"did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
371
+
);
372
+
assert_eq!(
373
+
data.services.get("atproto_pds").unwrap().endpoint,
374
+
"https://bsky.social"
375
+
);
376
+
}
377
+
378
+
#[test]
379
+
fn interop_tombstone() {
380
+
let path = "tests/fixtures/log_tombstone.json";
381
+
let data = std::fs::read_to_string(path).unwrap();
382
+
let entries: Vec<Value> = serde_json::from_str(&data).unwrap();
383
+
let did = entries[0]["did"].as_str().unwrap();
384
+
let ops: Vec<Value> = entries.iter().map(|e| e["operation"].clone()).collect();
385
+
386
+
assert_eq!(did, "did:plc:6adr3q2labdllanslzhqkqd3");
387
+
let result = apply_op_log(did, &ops);
388
+
assert!(result.is_none(), "tombstoned DID should return None");
389
+
}
390
+
391
+
#[test]
392
+
fn interop_nullification() {
393
+
let (did, ops) = load_fixture("log_nullification.json");
394
+
assert_eq!(did, "did:plc:2s2mvm52ttz6r4hocmrq7x27");
395
+
396
+
let data = apply_op_log(&did, &ops).expect("should reconstruct");
397
+
assert_eq!(data.did, did);
398
+
assert_eq!(data.rotation_keys.len(), 2);
399
+
assert_eq!(
400
+
data.rotation_keys[0],
401
+
"did:key:zQ3shwPdax6jKMbhtzbueGwSjc7RnjsmPcNB1vQUpbKUCN1t1"
402
+
);
403
+
}
404
+
}
+5
-1
src/lib.rs
+5
-1
src/lib.rs
···
1
use serde::{Deserialize, Serialize};
2
use tokio::sync::{mpsc, oneshot};
3
4
mod backfill;
5
mod cached_value;
6
mod client;
7
mod mirror;
8
mod plc_pg;
9
mod poll;
10
mod ratelimit;
···
15
pub use backfill::backfill;
16
pub use cached_value::{CachedValue, Fetcher};
17
pub use client::{CLIENT, UA};
18
-
pub use mirror::{ExperimentalConf, ListenConf, serve};
19
pub use plc_pg::{Db, backfill_to_pg, pages_to_pg};
20
pub use poll::{PageBoundaryState, get_page, poll_upstream};
21
pub use ratelimit::{CreatePlcOpLimiter, GovernorMiddleware, IpLimiters};
···
1
use serde::{Deserialize, Serialize};
2
+
3
use tokio::sync::{mpsc, oneshot};
4
5
mod backfill;
6
mod cached_value;
7
mod client;
8
+
pub mod doc;
9
mod mirror;
10
+
mod plc_fjall;
11
mod plc_pg;
12
mod poll;
13
mod ratelimit;
···
18
pub use backfill::backfill;
19
pub use cached_value::{CachedValue, Fetcher};
20
pub use client::{CLIENT, UA};
21
+
pub use mirror::{ExperimentalConf, ListenConf, serve, serve_fjall};
22
+
pub use plc_fjall::{FjallDb, backfill_to_fjall, pages_to_fjall};
23
pub use plc_pg::{Db, backfill_to_pg, pages_to_pg};
24
pub use poll::{PageBoundaryState, get_page, poll_upstream};
25
pub use ratelimit::{CreatePlcOpLimiter, GovernorMiddleware, IpLimiters};
+434
src/mirror/fjall.rs
+434
src/mirror/fjall.rs
···
···
1
+
use super::*;
2
+
use futures::StreamExt;
3
+
use poem::web::Query;
4
+
use serde::Deserialize;
5
+
6
+
#[derive(Clone)]
7
+
struct FjallState {
8
+
client: Client,
9
+
upstream: Url,
10
+
fjall: FjallDb,
11
+
sync_info: FjallSyncInfo,
12
+
experimental: ExperimentalConf,
13
+
}
14
+
15
+
#[derive(Clone)]
16
+
struct FjallSyncInfo {
17
+
latest_at: CachedValue<Dt, GetFjallLatestAt>,
18
+
upstream_status: CachedValue<PlcStatus, CheckUpstream>,
19
+
}
20
+
21
+
#[derive(Clone)]
22
+
struct GetFjallLatestAt(FjallDb);
23
+
impl Fetcher<Dt> for GetFjallLatestAt {
24
+
async fn fetch(&self) -> Result<Dt, Box<dyn std::error::Error>> {
25
+
let db = self.0.clone();
26
+
let now = tokio::task::spawn_blocking(move || db.get_latest())
27
+
.await??
28
+
.ok_or(anyhow::anyhow!(
29
+
"expected to find at least one thing in the db"
30
+
))?;
31
+
Ok(now)
32
+
}
33
+
}
34
+
35
+
#[derive(Clone)]
36
+
struct CheckUpstream(Url, Client);
37
+
impl Fetcher<PlcStatus> for CheckUpstream {
38
+
async fn fetch(&self) -> Result<PlcStatus, Box<dyn std::error::Error>> {
39
+
Ok(plc_status(&self.0, &self.1).await)
40
+
}
41
+
}
42
+
43
+
#[handler]
44
+
fn fjall_hello(
45
+
Data(FjallState {
46
+
upstream,
47
+
experimental: exp,
48
+
..
49
+
}): Data<&FjallState>,
50
+
req: &Request,
51
+
) -> String {
52
+
let post_info = match (exp.write_upstream, &exp.acme_domain, req.uri().host()) {
53
+
(false, _, _) => " - POST /* Always rejected. This is a mirror.".to_string(),
54
+
(_, None, _) => {
55
+
" - POST /:did Create a PLC op. Allegedly will forward it upstream.".to_string()
56
+
}
57
+
(_, Some(d), Some(f)) if f == d => {
58
+
" - POST /:did Create a PLC op. Allegedly will forward it upstream.".to_string()
59
+
}
60
+
(_, Some(d), _) => format!(
61
+
r#" - POST /* Rejected, but experimental upstream op forwarding is
62
+
available at `POST https://{d}/:did`!"#
63
+
),
64
+
};
65
+
66
+
format!(
67
+
r#"{}
68
+
69
+
This is a PLC[1] mirror running Allegedly in fjall mirror mode. The PLC API
70
+
is served from a the local database, which is mirrored from the upstream PLC
71
+
server.
72
+
73
+
74
+
Configured upstream:
75
+
76
+
{upstream}
77
+
78
+
79
+
Available APIs:
80
+
81
+
- GET /_health Health and version info
82
+
83
+
- GET /did:plc:{{did}} Resolve a DID document
84
+
- GET /did:plc:{{did}}/log Operation log
85
+
- GET /did:plc:{{did}}/log/audit Full audit log (including nullified ops)
86
+
- GET /did:plc:{{did}}/log/last Last operation
87
+
- GET /did:plc:{{did}}/data Parsed document data
88
+
89
+
{post_info}
90
+
91
+
92
+
Allegedly is a suite of open-source CLI tools for working with PLC logs,
93
+
from microcosm:
94
+
95
+
https://tangled.org/@microcosm.blue/Allegedly
96
+
97
+
https://microcosm.blue
98
+
99
+
100
+
[1] https://web.plc.directory
101
+
[2] https://github.com/did-method-plc/did-method-plc
102
+
"#,
103
+
logo("mirror (fjall)")
104
+
)
105
+
}
106
+
107
+
#[handler]
108
+
async fn fjall_health(Data(FjallState { sync_info, .. }): Data<&FjallState>) -> impl IntoResponse {
109
+
let mut overall_status = StatusCode::OK;
110
+
111
+
let (ok, upstream_status) = sync_info
112
+
.upstream_status
113
+
.get()
114
+
.await
115
+
.expect("plc_status infallible");
116
+
if !ok {
117
+
overall_status = StatusCode::BAD_GATEWAY;
118
+
}
119
+
let latest = sync_info.latest_at.get().await.ok();
120
+
121
+
(
122
+
overall_status,
123
+
Json(serde_json::json!({
124
+
"server": "allegedly (mirror/fjall)",
125
+
"version": env!("CARGO_PKG_VERSION"),
126
+
"upstream_plc": upstream_status,
127
+
"latest_at": latest,
128
+
})),
129
+
)
130
+
}
131
+
132
+
#[handler]
133
+
async fn fjall_resolve(req: &Request, Data(state): Data<&FjallState>) -> Result<Response> {
134
+
let path = req.uri().path();
135
+
let did_and_rest = path.strip_prefix("/").unwrap_or(path);
136
+
137
+
let (did, sub_path) = match did_and_rest.find('/') {
138
+
Some(i) => (&did_and_rest[..i], &did_and_rest[i..]),
139
+
None => (did_and_rest, ""),
140
+
};
141
+
142
+
if !did.starts_with("did:plc:") {
143
+
return Err(Error::from_string("invalid DID", StatusCode::BAD_REQUEST));
144
+
}
145
+
146
+
let did = did.to_string();
147
+
let db = state.fjall.clone();
148
+
let ops = tokio::task::spawn_blocking(move || {
149
+
let iter = db.ops_for_did(&did)?;
150
+
iter.collect::<anyhow::Result<Vec<_>>>()
151
+
})
152
+
.await
153
+
.map_err(|e| Error::from_string(e.to_string(), StatusCode::INTERNAL_SERVER_ERROR))?
154
+
.map_err(|e| Error::from_string(e.to_string(), StatusCode::INTERNAL_SERVER_ERROR))?;
155
+
156
+
if ops.is_empty() {
157
+
return Err(Error::from_string(
158
+
format!(
159
+
"DID not registered: {}",
160
+
did_and_rest.split('/').next().unwrap_or(did_and_rest)
161
+
),
162
+
StatusCode::NOT_FOUND,
163
+
));
164
+
}
165
+
166
+
let did_str = &ops[0].did;
167
+
168
+
match sub_path {
169
+
"" => {
170
+
let data = doc::apply_op_log(
171
+
did_str,
172
+
ops.iter()
173
+
.filter(|op| !op.nullified)
174
+
.map(|op| &op.operation),
175
+
);
176
+
let Some(data) = data else {
177
+
return Err(Error::from_string(
178
+
format!("DID not available: {did_str}"),
179
+
StatusCode::NOT_FOUND,
180
+
));
181
+
};
182
+
let doc = doc::format_did_doc(&data);
183
+
Ok(Response::builder()
184
+
.content_type("application/did+ld+json")
185
+
.body(serde_json::to_string(&doc).unwrap()))
186
+
}
187
+
"/log" => {
188
+
let log: Vec<&serde_json::Value> = ops
189
+
.iter()
190
+
.filter(|op| !op.nullified)
191
+
.map(|op| &op.operation)
192
+
.collect();
193
+
Ok(Response::builder()
194
+
.content_type("application/json")
195
+
.body(serde_json::to_string(&log).unwrap()))
196
+
}
197
+
"/log/audit" => {
198
+
let audit: Vec<serde_json::Value> = ops
199
+
.iter()
200
+
.map(|op| {
201
+
serde_json::json!({
202
+
"did": op.did,
203
+
"operation": op.operation,
204
+
"cid": op.cid,
205
+
"nullified": op.nullified,
206
+
"createdAt": op.created_at.to_rfc3339(),
207
+
})
208
+
})
209
+
.collect();
210
+
Ok(Response::builder()
211
+
.content_type("application/json")
212
+
.body(serde_json::to_string(&audit).unwrap()))
213
+
}
214
+
"/log/last" => {
215
+
let last = ops
216
+
.iter()
217
+
.filter(|op| !op.nullified)
218
+
.last()
219
+
.map(|op| &op.operation);
220
+
let Some(last) = last else {
221
+
return Err(Error::from_string(
222
+
format!("DID not available: {did_str}"),
223
+
StatusCode::NOT_FOUND,
224
+
));
225
+
};
226
+
Ok(Response::builder()
227
+
.content_type("application/json")
228
+
.body(serde_json::to_string(&last).unwrap()))
229
+
}
230
+
"/data" => {
231
+
let data = doc::apply_op_log(
232
+
did_str,
233
+
ops.iter()
234
+
.filter(|op| !op.nullified)
235
+
.map(|op| &op.operation),
236
+
);
237
+
let Some(data) = data else {
238
+
return Err(Error::from_string(
239
+
format!("DID not available: {did_str}"),
240
+
StatusCode::NOT_FOUND,
241
+
));
242
+
};
243
+
Ok(Response::builder()
244
+
.content_type("application/json")
245
+
.body(serde_json::to_string(&data).unwrap()))
246
+
}
247
+
_ => Err(Error::from_string("not found", StatusCode::NOT_FOUND)),
248
+
}
249
+
}
250
+
251
+
#[derive(Deserialize)]
252
+
struct ExportQuery {
253
+
after: Option<String>,
254
+
#[allow(dead_code)] // we just cap at 1000 for now, matching reference impl
255
+
count: Option<usize>,
256
+
}
257
+
258
+
#[handler]
259
+
async fn fjall_export(
260
+
_req: &Request,
261
+
Query(query): Query<ExportQuery>,
262
+
Data(FjallState { fjall, .. }): Data<&FjallState>,
263
+
) -> Result<Body> {
264
+
let after = if let Some(a) = query.after {
265
+
Some(
266
+
chrono::DateTime::parse_from_rfc3339(&a)
267
+
.map_err(|e| Error::from_string(e.to_string(), StatusCode::BAD_REQUEST))?
268
+
.with_timezone(&chrono::Utc),
269
+
)
270
+
} else {
271
+
None
272
+
};
273
+
274
+
let limit = 1000;
275
+
let db = fjall.clone();
276
+
277
+
let ops = tokio::task::spawn_blocking(move || {
278
+
let iter = db.export_ops(after.unwrap_or(Dt::UNIX_EPOCH)..)?;
279
+
iter.take(limit).collect::<anyhow::Result<Vec<_>>>()
280
+
})
281
+
.await
282
+
.map_err(|e| Error::from_string(e.to_string(), StatusCode::INTERNAL_SERVER_ERROR))?
283
+
.map_err(|e| Error::from_string(e.to_string(), StatusCode::INTERNAL_SERVER_ERROR))?;
284
+
285
+
let stream = futures::stream::iter(ops).map(|op| {
286
+
let mut json = serde_json::to_string(&op).unwrap();
287
+
json.push('\n');
288
+
Ok::<_, std::io::Error>(json)
289
+
});
290
+
291
+
Ok(Body::from_bytes_stream(stream))
292
+
}
293
+
294
+
#[handler]
295
+
async fn fjall_nope(Data(FjallState { upstream, .. }): Data<&FjallState>) -> (StatusCode, String) {
296
+
(
297
+
StatusCode::BAD_REQUEST,
298
+
format!(
299
+
r#"{}
300
+
301
+
Sorry, this server does not accept POST requests.
302
+
303
+
You may wish to try sending that to our upstream: {upstream}.
304
+
305
+
If you operate this server, try running with `--experimental-write-upstream`.
306
+
"#,
307
+
logo("mirror (nope)")
308
+
),
309
+
)
310
+
}
311
+
312
+
pub async fn serve_fjall(
313
+
upstream: Url,
314
+
listen: ListenConf,
315
+
experimental: ExperimentalConf,
316
+
fjall: FjallDb,
317
+
) -> anyhow::Result<&'static str> {
318
+
log::info!("starting fjall mirror server...");
319
+
320
+
let client = Client::builder()
321
+
.user_agent(UA)
322
+
.timeout(Duration::from_secs(10))
323
+
.build()
324
+
.expect("reqwest client to build");
325
+
326
+
let sync_info = FjallSyncInfo {
327
+
latest_at: CachedValue::new(GetFjallLatestAt(fjall.clone()), Duration::from_secs(2)),
328
+
upstream_status: CachedValue::new(
329
+
CheckUpstream(upstream.clone(), client.clone()),
330
+
Duration::from_secs(6),
331
+
),
332
+
};
333
+
334
+
let state = FjallState {
335
+
client,
336
+
upstream,
337
+
fjall,
338
+
sync_info,
339
+
experimental: experimental.clone(),
340
+
};
341
+
342
+
let mut app = Route::new()
343
+
.at("/", get(fjall_hello))
344
+
.at("/favicon.ico", get(favicon))
345
+
.at("/_health", get(fjall_health))
346
+
.at("/export", get(fjall_export));
347
+
348
+
if experimental.write_upstream {
349
+
log::info!("enabling experimental write forwarding to upstream");
350
+
351
+
let ip_limiter = IpLimiters::new(Quota::per_hour(10.try_into().unwrap()));
352
+
let did_limiter = CreatePlcOpLimiter::new(Quota::per_hour(4.try_into().unwrap()));
353
+
354
+
let upstream_proxier = fjall_forward_create_op_upstream
355
+
.with(GovernorMiddleware::new(did_limiter))
356
+
.with(GovernorMiddleware::new(ip_limiter));
357
+
358
+
app = app.at("/did:plc:*", get(fjall_resolve).post(upstream_proxier));
359
+
} else {
360
+
app = app.at("/did:plc:*", get(fjall_resolve).post(fjall_nope));
361
+
}
362
+
363
+
let app = app
364
+
.with(AddData::new(state))
365
+
.with(Cors::new().allow_credentials(false))
366
+
.with(Compression::new())
367
+
.with(GovernorMiddleware::new(IpLimiters::new(Quota::per_minute(
368
+
3000.try_into().expect("ratelimit middleware to build"),
369
+
))))
370
+
.with(CatchPanic::new())
371
+
.with(Tracing);
372
+
373
+
bind_or_acme(app, listen).await
374
+
}
375
+
376
+
#[handler]
377
+
async fn fjall_forward_create_op_upstream(
378
+
Data(FjallState {
379
+
upstream,
380
+
client,
381
+
experimental,
382
+
..
383
+
}): Data<&FjallState>,
384
+
Path(did): Path<String>,
385
+
req: &Request,
386
+
body: Body,
387
+
) -> Result<Response> {
388
+
if let Some(expected_domain) = &experimental.acme_domain {
389
+
let Some(found_host) = req.uri().host() else {
390
+
return Ok(bad_create_op(&format!(
391
+
"missing `Host` header, expected {expected_domain:?} for experimental requests."
392
+
)));
393
+
};
394
+
if found_host != expected_domain {
395
+
return Ok(bad_create_op(&format!(
396
+
"experimental requests must be made to {expected_domain:?}, but this request's `Host` header was {found_host}"
397
+
)));
398
+
}
399
+
}
400
+
401
+
let mut headers: reqwest::header::HeaderMap = req.headers().clone();
402
+
log::trace!("original request headers: {headers:?}");
403
+
headers.insert("Host", upstream.host_str().unwrap().parse().unwrap());
404
+
let client_ua = headers
405
+
.get(USER_AGENT)
406
+
.map(|h| h.to_str().unwrap())
407
+
.unwrap_or("unknown");
408
+
headers.insert(
409
+
USER_AGENT,
410
+
format!("{UA} (forwarding from {client_ua:?})")
411
+
.parse()
412
+
.unwrap(),
413
+
);
414
+
log::trace!("adjusted request headers: {headers:?}");
415
+
416
+
let mut target = upstream.clone();
417
+
target.set_path(&did);
418
+
let upstream_res = client
419
+
.post(target)
420
+
.timeout(Duration::from_secs(15))
421
+
.headers(headers)
422
+
.body(reqwest::Body::wrap_stream(body.into_bytes_stream()))
423
+
.send()
424
+
.await
425
+
.map_err(|e| {
426
+
log::warn!("upstream write fail: {e}");
427
+
Error::from_string(
428
+
failed_to_reach_named("upstream PLC"),
429
+
StatusCode::BAD_GATEWAY,
430
+
)
431
+
})?;
432
+
433
+
Ok(proxy_response(upstream_res))
434
+
}
+201
src/mirror/mod.rs
+201
src/mirror/mod.rs
···
···
1
+
use crate::{
2
+
CachedValue, CreatePlcOpLimiter, Db, Dt, Fetcher, FjallDb, GovernorMiddleware, IpLimiters, UA,
3
+
doc, logo,
4
+
};
5
+
use futures::TryStreamExt;
6
+
use governor::Quota;
7
+
use poem::{
8
+
Body, Endpoint, EndpointExt, Error, IntoResponse, Request, Response, Result, Route, Server,
9
+
get, handler,
10
+
http::{StatusCode, header::USER_AGENT},
11
+
listener::{Listener, TcpListener, acme::AutoCert},
12
+
middleware::{AddData, CatchPanic, Compression, Cors, Tracing},
13
+
web::{Data, Json, Path},
14
+
};
15
+
use reqwest::{Client, Url};
16
+
use std::{net::SocketAddr, path::PathBuf, time::Duration};
17
+
18
+
pub mod fjall;
19
+
pub mod pg;
20
+
21
+
pub use fjall::serve_fjall;
22
+
pub use pg::serve;
23
+
24
+
#[derive(Debug)]
25
+
pub enum ListenConf {
26
+
Acme {
27
+
domains: Vec<String>,
28
+
cache_path: PathBuf,
29
+
directory_url: String,
30
+
ipv6: bool,
31
+
},
32
+
Bind(SocketAddr),
33
+
}
34
+
35
+
#[derive(Debug, Clone)]
36
+
pub struct ExperimentalConf {
37
+
pub acme_domain: Option<String>,
38
+
pub write_upstream: bool,
39
+
}
40
+
41
+
#[handler]
42
+
pub fn favicon() -> impl IntoResponse {
43
+
include_bytes!("../../favicon.ico").with_content_type("image/x-icon")
44
+
}
45
+
46
+
pub fn failed_to_reach_named(name: &str) -> String {
47
+
format!(
48
+
r#"{}
49
+
50
+
Failed to reach the {name} server. Sorry.
51
+
"#,
52
+
logo("mirror 502 :( ")
53
+
)
54
+
}
55
+
56
+
pub fn bad_create_op(reason: &str) -> Response {
57
+
Response::builder()
58
+
.status(StatusCode::BAD_REQUEST)
59
+
.body(format!(
60
+
r#"{}
61
+
62
+
NooOOOooooo: {reason}
63
+
"#,
64
+
logo("mirror 400 >:( ")
65
+
))
66
+
}
67
+
68
+
pub type PlcStatus = (bool, serde_json::Value);
69
+
70
+
pub async fn plc_status(url: &Url, client: &Client) -> PlcStatus {
71
+
use serde_json::json;
72
+
73
+
let mut url = url.clone();
74
+
url.set_path("/_health");
75
+
76
+
let Ok(response) = client.get(url).timeout(Duration::from_secs(3)).send().await else {
77
+
return (false, json!({"error": "cannot reach plc server"}));
78
+
};
79
+
80
+
let status = response.status();
81
+
82
+
let Ok(text) = response.text().await else {
83
+
return (false, json!({"error": "failed to read response body"}));
84
+
};
85
+
86
+
let body = match serde_json::from_str(&text) {
87
+
Ok(json) => json,
88
+
Err(_) => serde_json::Value::String(text.to_string()),
89
+
};
90
+
91
+
if status.is_success() {
92
+
(true, body)
93
+
} else {
94
+
(
95
+
false,
96
+
json!({
97
+
"error": "non-ok status",
98
+
"status": status.as_str(),
99
+
"status_code": status.as_u16(),
100
+
"response": body,
101
+
}),
102
+
)
103
+
}
104
+
}
105
+
106
+
pub fn proxy_response(res: reqwest::Response) -> Response {
107
+
let http_res: poem::http::Response<reqwest::Body> = res.into();
108
+
let (parts, reqw_body) = http_res.into_parts();
109
+
110
+
let parts = poem::ResponseParts {
111
+
status: parts.status,
112
+
version: parts.version,
113
+
headers: parts.headers,
114
+
extensions: parts.extensions,
115
+
};
116
+
117
+
let body = http_body_util::BodyDataStream::new(reqw_body)
118
+
.map_err(|e| std::io::Error::other(Box::new(e)));
119
+
120
+
Response::from_parts(parts, poem::Body::from_bytes_stream(body))
121
+
}
122
+
123
+
async fn run<A, L>(app: A, listener: L) -> std::io::Result<()>
124
+
where
125
+
A: Endpoint + 'static,
126
+
L: Listener + 'static,
127
+
{
128
+
Server::new(listener)
129
+
.name("allegedly (mirror)")
130
+
.run(app)
131
+
.await
132
+
}
133
+
134
+
/// kick off a tiny little server on a tokio task to tell people to use 443
135
+
async fn run_insecure_notice(ipv6: bool) -> Result<(), std::io::Error> {
136
+
#[handler]
137
+
fn oop_plz_be_secure() -> (StatusCode, String) {
138
+
(
139
+
StatusCode::BAD_REQUEST,
140
+
format!(
141
+
r#"{}
142
+
143
+
You probably want to change your request to use HTTPS instead of HTTP.
144
+
"#,
145
+
logo("mirror (tls on 443 please)")
146
+
),
147
+
)
148
+
}
149
+
150
+
let app = Route::new()
151
+
.at("/favicon.ico", get(favicon))
152
+
.nest("/", get(oop_plz_be_secure))
153
+
.with(Tracing);
154
+
Server::new(TcpListener::bind(if ipv6 {
155
+
"[::]:80"
156
+
} else {
157
+
"0.0.0.0:80"
158
+
}))
159
+
.name("allegedly (mirror:80 helper)")
160
+
.run(app)
161
+
.await
162
+
}
163
+
164
+
pub async fn bind_or_acme<A>(app: A, listen: ListenConf) -> anyhow::Result<&'static str>
165
+
where
166
+
A: Endpoint + 'static,
167
+
{
168
+
match listen {
169
+
ListenConf::Acme {
170
+
domains,
171
+
cache_path,
172
+
directory_url,
173
+
ipv6,
174
+
} => {
175
+
rustls::crypto::aws_lc_rs::default_provider()
176
+
.install_default()
177
+
.expect("crypto provider to be installable");
178
+
179
+
let mut auto_cert = AutoCert::builder()
180
+
.directory_url(directory_url)
181
+
.cache_path(cache_path);
182
+
for domain in domains {
183
+
auto_cert = auto_cert.domain(domain);
184
+
}
185
+
let auto_cert = auto_cert.build().expect("acme config to build");
186
+
187
+
log::trace!("auto_cert: {auto_cert:?}");
188
+
189
+
let notice_task = tokio::task::spawn(run_insecure_notice(ipv6));
190
+
let listener = TcpListener::bind(if ipv6 { "[::]:443" } else { "0.0.0.0:443" });
191
+
let app_res = run(app, listener.acme(auto_cert)).await;
192
+
log::warn!("server task ended, aborting insecure server task...");
193
+
notice_task.abort();
194
+
app_res?;
195
+
notice_task.await??;
196
+
}
197
+
ListenConf::Bind(addr) => run(app, TcpListener::bind(addr)).await?,
198
+
}
199
+
200
+
Ok("server (uh oh?)")
201
+
}
+334
src/mirror/pg.rs
+334
src/mirror/pg.rs
···
···
1
+
use super::*;
2
+
3
+
#[derive(Clone)]
4
+
struct State {
5
+
client: Client,
6
+
plc: Url,
7
+
upstream: Url,
8
+
sync_info: Option<SyncInfo>,
9
+
experimental: ExperimentalConf,
10
+
}
11
+
12
+
/// server info that only applies in mirror (synchronizing) mode
13
+
#[derive(Clone)]
14
+
struct SyncInfo {
15
+
latest_at: CachedValue<Dt, GetLatestAt>,
16
+
upstream_status: CachedValue<PlcStatus, CheckUpstream>,
17
+
}
18
+
19
+
#[derive(Clone)]
20
+
struct GetLatestAt(Db);
21
+
impl Fetcher<Dt> for GetLatestAt {
22
+
async fn fetch(&self) -> Result<Dt, Box<dyn std::error::Error>> {
23
+
let now = self.0.get_latest().await?.ok_or(anyhow::anyhow!(
24
+
"expected to find at least one thing in the db"
25
+
))?;
26
+
Ok(now)
27
+
}
28
+
}
29
+
30
+
#[derive(Clone)]
31
+
struct CheckUpstream(Url, Client);
32
+
impl Fetcher<PlcStatus> for CheckUpstream {
33
+
async fn fetch(&self) -> Result<PlcStatus, Box<dyn std::error::Error>> {
34
+
Ok(plc_status(&self.0, &self.1).await)
35
+
}
36
+
}
37
+
38
+
#[handler]
39
+
fn hello(
40
+
Data(State {
41
+
sync_info,
42
+
upstream,
43
+
experimental: exp,
44
+
..
45
+
}): Data<&State>,
46
+
req: &Request,
47
+
) -> String {
48
+
let pre_info = if sync_info.is_some() {
49
+
format!(
50
+
r#"
51
+
This is a PLC[1] mirror running Allegedly in mirror mode. Mirror mode wraps and
52
+
synchronizes a local PLC reference server instance[2] (why?[3]).
53
+
54
+
55
+
Configured upstream:
56
+
57
+
{upstream}
58
+
59
+
"#
60
+
)
61
+
} else {
62
+
format!(
63
+
r#"
64
+
This is a PLC[1] mirror running Allegedly in wrap mode. Wrap mode reverse-
65
+
proxies requests to a PLC server and can terminate TLS, like NGINX or Caddy.
66
+
67
+
68
+
Configured upstream (only used if experimental op forwarding is enabled):
69
+
70
+
{upstream}
71
+
72
+
"#
73
+
)
74
+
};
75
+
76
+
let post_info = match (exp.write_upstream, &exp.acme_domain, req.uri().host()) {
77
+
(false, _, _) => " - POST /* Always rejected. This is a mirror.".to_string(),
78
+
(_, None, _) => {
79
+
" - POST /:did Create a PLC op. Allegedly will forward it upstream.".to_string()
80
+
}
81
+
(_, Some(d), Some(f)) if f == d => {
82
+
" - POST /:did Create a PLC op. Allegedly will forward it upstream.".to_string()
83
+
}
84
+
(_, Some(d), _) => format!(
85
+
r#" - POST /* Rejected, but experimental upstream op forwarding is
86
+
available at `POST https://{d}/:did`!"#
87
+
),
88
+
};
89
+
90
+
format!(
91
+
r#"{}
92
+
{pre_info}
93
+
94
+
Available APIs:
95
+
96
+
- GET /_health Health and version info
97
+
98
+
- GET /* Proxies to wrapped server; see PLC API docs:
99
+
https://web.plc.directory/api/redoc
100
+
101
+
tip: try `GET /{{did}}` to resolve an identity
102
+
103
+
{post_info}
104
+
105
+
106
+
Allegedly is a suite of open-source CLI tools from for working with PLC logs,
107
+
from microcosm:
108
+
109
+
https://tangled.org/@microcosm.blue/Allegedly
110
+
111
+
https://microcosm.blue
112
+
113
+
114
+
[1] https://web.plc.directory
115
+
[2] https://github.com/did-method-plc/did-method-plc
116
+
[3] https://updates.microcosm.blue/3lz7nwvh4zc2u
117
+
"#,
118
+
logo("mirror")
119
+
)
120
+
}
121
+
122
+
#[handler]
123
+
async fn health(
124
+
Data(State {
125
+
plc,
126
+
client,
127
+
sync_info,
128
+
..
129
+
}): Data<&State>,
130
+
) -> impl IntoResponse {
131
+
let mut overall_status = StatusCode::OK;
132
+
let (ok, wrapped_status) = plc_status(plc, client).await;
133
+
if !ok {
134
+
overall_status = StatusCode::BAD_GATEWAY;
135
+
}
136
+
137
+
if let Some(SyncInfo {
138
+
latest_at,
139
+
upstream_status,
140
+
}) = sync_info
141
+
{
142
+
let (ok, upstream_status) = upstream_status.get().await.expect("plc_status infallible");
143
+
if !ok {
144
+
overall_status = StatusCode::BAD_GATEWAY;
145
+
}
146
+
let latest = latest_at.get().await.ok();
147
+
(
148
+
overall_status,
149
+
Json(serde_json::json!({
150
+
"server": "allegedly (mirror)",
151
+
"version": env!("CARGO_PKG_VERSION"),
152
+
"wrapped_plc": wrapped_status,
153
+
"upstream_plc": upstream_status,
154
+
"latest_at": latest,
155
+
})),
156
+
)
157
+
} else {
158
+
(
159
+
overall_status,
160
+
Json(serde_json::json!({
161
+
"server": "allegedly (mirror)",
162
+
"version": env!("CARGO_PKG_VERSION"),
163
+
"wrapped_plc": wrapped_status,
164
+
})),
165
+
)
166
+
}
167
+
}
168
+
169
+
#[handler]
170
+
async fn proxy(req: &Request, Data(state): Data<&State>) -> Result<Response> {
171
+
let mut target = state.plc.clone();
172
+
target.set_path(req.uri().path());
173
+
target.set_query(req.uri().query());
174
+
let wrapped_res = state
175
+
.client
176
+
.get(target)
177
+
.timeout(Duration::from_secs(3))
178
+
.headers(req.headers().clone())
179
+
.send()
180
+
.await
181
+
.map_err(|e| {
182
+
log::error!("upstream req fail: {e}");
183
+
Error::from_string(
184
+
failed_to_reach_named("wrapped reference PLC"),
185
+
StatusCode::BAD_GATEWAY,
186
+
)
187
+
})?;
188
+
189
+
Ok(proxy_response(wrapped_res))
190
+
}
191
+
192
+
#[handler]
193
+
async fn forward_create_op_upstream(
194
+
Data(State {
195
+
upstream,
196
+
client,
197
+
experimental,
198
+
..
199
+
}): Data<&State>,
200
+
Path(did): Path<String>,
201
+
req: &Request,
202
+
body: Body,
203
+
) -> Result<Response> {
204
+
if let Some(expected_domain) = &experimental.acme_domain {
205
+
let Some(found_host) = req.uri().host() else {
206
+
return Ok(bad_create_op(&format!(
207
+
"missing `Host` header, expected {expected_domain:?} for experimental requests."
208
+
)));
209
+
};
210
+
if found_host != expected_domain {
211
+
return Ok(bad_create_op(&format!(
212
+
"experimental requests must be made to {expected_domain:?}, but this request's `Host` header was {found_host}"
213
+
)));
214
+
}
215
+
}
216
+
217
+
let mut headers: reqwest::header::HeaderMap = req.headers().clone();
218
+
log::trace!("original request headers: {headers:?}");
219
+
headers.insert("Host", upstream.host_str().unwrap().parse().unwrap());
220
+
let client_ua = headers
221
+
.get(USER_AGENT)
222
+
.map(|h| h.to_str().unwrap())
223
+
.unwrap_or("unknown");
224
+
headers.insert(
225
+
USER_AGENT,
226
+
format!("{UA} (forwarding from {client_ua:?})")
227
+
.parse()
228
+
.unwrap(),
229
+
);
230
+
log::trace!("adjusted request headers: {headers:?}");
231
+
232
+
let mut target = upstream.clone();
233
+
target.set_path(&did);
234
+
let upstream_res = client
235
+
.post(target)
236
+
.timeout(Duration::from_secs(15))
237
+
.headers(headers)
238
+
.body(reqwest::Body::wrap_stream(body.into_bytes_stream()))
239
+
.send()
240
+
.await
241
+
.map_err(|e| {
242
+
log::warn!("upstream write fail: {e}");
243
+
Error::from_string(
244
+
failed_to_reach_named("upstream PLC"),
245
+
StatusCode::BAD_GATEWAY,
246
+
)
247
+
})?;
248
+
249
+
Ok(proxy_response(upstream_res))
250
+
}
251
+
252
+
#[handler]
253
+
async fn nope(Data(State { upstream, .. }): Data<&State>) -> (StatusCode, String) {
254
+
(
255
+
StatusCode::BAD_REQUEST,
256
+
format!(
257
+
r#"{}
258
+
259
+
Sorry, this server does not accept POST requests.
260
+
261
+
You may wish to try sending that to our upstream: {upstream}.
262
+
263
+
If you operate this server, try running with `--experimental-write-upstream`.
264
+
"#,
265
+
logo("mirror (nope)")
266
+
),
267
+
)
268
+
}
269
+
270
+
pub async fn serve(
271
+
upstream: Url,
272
+
plc: Url,
273
+
listen: ListenConf,
274
+
experimental: ExperimentalConf,
275
+
db: Option<Db>,
276
+
) -> anyhow::Result<&'static str> {
277
+
log::info!("starting server...");
278
+
279
+
let client = Client::builder()
280
+
.user_agent(UA)
281
+
.timeout(Duration::from_secs(10))
282
+
.build()
283
+
.expect("reqwest client to build");
284
+
285
+
// when `db` is None, we're running in wrap mode. no db access, no upstream sync
286
+
let sync_info = db.map(|db| SyncInfo {
287
+
latest_at: CachedValue::new(GetLatestAt(db), Duration::from_secs(2)),
288
+
upstream_status: CachedValue::new(
289
+
CheckUpstream(upstream.clone(), client.clone()),
290
+
Duration::from_secs(6),
291
+
),
292
+
});
293
+
294
+
let state = State {
295
+
client,
296
+
plc,
297
+
upstream: upstream.clone(),
298
+
sync_info,
299
+
experimental: experimental.clone(),
300
+
};
301
+
302
+
let mut app = Route::new()
303
+
.at("/", get(hello))
304
+
.at("/favicon.ico", get(favicon))
305
+
.at("/_health", get(health))
306
+
.at("/export", get(proxy));
307
+
308
+
if experimental.write_upstream {
309
+
log::info!("enabling experimental write forwarding to upstream");
310
+
311
+
let ip_limiter = IpLimiters::new(Quota::per_hour(10.try_into().unwrap()));
312
+
let did_limiter = CreatePlcOpLimiter::new(Quota::per_hour(4.try_into().unwrap()));
313
+
314
+
let upstream_proxier = forward_create_op_upstream
315
+
.with(GovernorMiddleware::new(did_limiter))
316
+
.with(GovernorMiddleware::new(ip_limiter));
317
+
318
+
app = app.at("/did:plc:*", get(proxy).post(upstream_proxier));
319
+
} else {
320
+
app = app.at("/did:plc:*", get(proxy).post(nope));
321
+
}
322
+
323
+
let app = app
324
+
.with(AddData::new(state))
325
+
.with(Cors::new().allow_credentials(false))
326
+
.with(Compression::new())
327
+
.with(GovernorMiddleware::new(IpLimiters::new(Quota::per_minute(
328
+
3000.try_into().expect("ratelimit middleware to build"),
329
+
))))
330
+
.with(CatchPanic::new())
331
+
.with(Tracing);
332
+
333
+
bind_or_acme(app, listen).await
334
+
}
-524
src/mirror.rs
-524
src/mirror.rs
···
1
-
use crate::{
2
-
CachedValue, CreatePlcOpLimiter, Db, Dt, Fetcher, GovernorMiddleware, IpLimiters, UA, logo,
3
-
};
4
-
use futures::TryStreamExt;
5
-
use governor::Quota;
6
-
use poem::{
7
-
Body, Endpoint, EndpointExt, Error, IntoResponse, Request, Response, Result, Route, Server,
8
-
get, handler,
9
-
http::{StatusCode, header::USER_AGENT},
10
-
listener::{Listener, TcpListener, acme::AutoCert},
11
-
middleware::{AddData, CatchPanic, Compression, Cors, Tracing},
12
-
web::{Data, Json, Path},
13
-
};
14
-
use reqwest::{Client, Url};
15
-
use std::{net::SocketAddr, path::PathBuf, time::Duration};
16
-
17
-
#[derive(Clone)]
18
-
struct State {
19
-
client: Client,
20
-
plc: Url,
21
-
upstream: Url,
22
-
sync_info: Option<SyncInfo>,
23
-
experimental: ExperimentalConf,
24
-
}
25
-
26
-
/// server info that only applies in mirror (synchronizing) mode
27
-
#[derive(Clone)]
28
-
struct SyncInfo {
29
-
latest_at: CachedValue<Dt, GetLatestAt>,
30
-
upstream_status: CachedValue<PlcStatus, CheckUpstream>,
31
-
}
32
-
33
-
#[handler]
34
-
fn hello(
35
-
Data(State {
36
-
sync_info,
37
-
upstream,
38
-
experimental: exp,
39
-
..
40
-
}): Data<&State>,
41
-
req: &Request,
42
-
) -> String {
43
-
// let mode = if sync_info.is_some() { "mirror" } else { "wrap" };
44
-
let pre_info = if sync_info.is_some() {
45
-
format!(
46
-
r#"
47
-
This is a PLC[1] mirror running Allegedly in mirror mode. Mirror mode wraps and
48
-
synchronizes a local PLC reference server instance[2] (why?[3]).
49
-
50
-
51
-
Configured upstream:
52
-
53
-
{upstream}
54
-
55
-
"#
56
-
)
57
-
} else {
58
-
format!(
59
-
r#"
60
-
This is a PLC[1] mirror running Allegedly in wrap mode. Wrap mode reverse-
61
-
proxies requests to a PLC server and can terminate TLS, like NGINX or Caddy.
62
-
63
-
64
-
Configured upstream (only used if experimental op forwarding is enabled):
65
-
66
-
{upstream}
67
-
68
-
"#
69
-
)
70
-
};
71
-
72
-
let post_info = match (exp.write_upstream, &exp.acme_domain, req.uri().host()) {
73
-
(false, _, _) => " - POST /* Always rejected. This is a mirror.".to_string(),
74
-
(_, None, _) => {
75
-
" - POST /:did Create a PLC op. Allegedly will forward it upstream.".to_string()
76
-
}
77
-
(_, Some(d), Some(f)) if f == d => {
78
-
" - POST /:did Create a PLC op. Allegedly will forward it upstream.".to_string()
79
-
}
80
-
(_, Some(d), _) => format!(
81
-
r#" - POST /* Rejected, but experimental upstream op forwarding is
82
-
available at `POST https://{d}/:did`!"#
83
-
),
84
-
};
85
-
86
-
format!(
87
-
r#"{}
88
-
{pre_info}
89
-
90
-
Available APIs:
91
-
92
-
- GET /_health Health and version info
93
-
94
-
- GET /* Proxies to wrapped server; see PLC API docs:
95
-
https://web.plc.directory/api/redoc
96
-
97
-
tip: try `GET /{{did}}` to resolve an identity
98
-
99
-
{post_info}
100
-
101
-
102
-
Allegedly is a suite of open-source CLI tools from for working with PLC logs,
103
-
from microcosm:
104
-
105
-
https://tangled.org/@microcosm.blue/Allegedly
106
-
107
-
https://microcosm.blue
108
-
109
-
110
-
[1] https://web.plc.directory
111
-
[2] https://github.com/did-method-plc/did-method-plc
112
-
[3] https://updates.microcosm.blue/3lz7nwvh4zc2u
113
-
"#,
114
-
logo("mirror")
115
-
)
116
-
}
117
-
118
-
#[handler]
119
-
fn favicon() -> impl IntoResponse {
120
-
include_bytes!("../favicon.ico").with_content_type("image/x-icon")
121
-
}
122
-
123
-
fn failed_to_reach_named(name: &str) -> String {
124
-
format!(
125
-
r#"{}
126
-
127
-
Failed to reach the {name} server. Sorry.
128
-
"#,
129
-
logo("mirror 502 :( ")
130
-
)
131
-
}
132
-
133
-
fn bad_create_op(reason: &str) -> Response {
134
-
Response::builder()
135
-
.status(StatusCode::BAD_REQUEST)
136
-
.body(format!(
137
-
r#"{}
138
-
139
-
NooOOOooooo: {reason}
140
-
"#,
141
-
logo("mirror 400 >:( ")
142
-
))
143
-
}
144
-
145
-
type PlcStatus = (bool, serde_json::Value);
146
-
147
-
async fn plc_status(url: &Url, client: &Client) -> PlcStatus {
148
-
use serde_json::json;
149
-
150
-
let mut url = url.clone();
151
-
url.set_path("/_health");
152
-
153
-
let Ok(response) = client.get(url).timeout(Duration::from_secs(3)).send().await else {
154
-
return (false, json!({"error": "cannot reach plc server"}));
155
-
};
156
-
157
-
let status = response.status();
158
-
159
-
let Ok(text) = response.text().await else {
160
-
return (false, json!({"error": "failed to read response body"}));
161
-
};
162
-
163
-
let body = match serde_json::from_str(&text) {
164
-
Ok(json) => json,
165
-
Err(_) => serde_json::Value::String(text.to_string()),
166
-
};
167
-
168
-
if status.is_success() {
169
-
(true, body)
170
-
} else {
171
-
(
172
-
false,
173
-
json!({
174
-
"error": "non-ok status",
175
-
"status": status.as_str(),
176
-
"status_code": status.as_u16(),
177
-
"response": body,
178
-
}),
179
-
)
180
-
}
181
-
}
182
-
183
-
#[derive(Clone)]
184
-
struct GetLatestAt(Db);
185
-
impl Fetcher<Dt> for GetLatestAt {
186
-
async fn fetch(&self) -> Result<Dt, Box<dyn std::error::Error>> {
187
-
let now = self.0.get_latest().await?.ok_or(anyhow::anyhow!(
188
-
"expected to find at least one thing in the db"
189
-
))?;
190
-
Ok(now)
191
-
}
192
-
}
193
-
194
-
#[derive(Clone)]
195
-
struct CheckUpstream(Url, Client);
196
-
impl Fetcher<PlcStatus> for CheckUpstream {
197
-
async fn fetch(&self) -> Result<PlcStatus, Box<dyn std::error::Error>> {
198
-
Ok(plc_status(&self.0, &self.1).await)
199
-
}
200
-
}
201
-
202
-
#[handler]
203
-
async fn health(
204
-
Data(State {
205
-
plc,
206
-
client,
207
-
sync_info,
208
-
..
209
-
}): Data<&State>,
210
-
) -> impl IntoResponse {
211
-
let mut overall_status = StatusCode::OK;
212
-
let (ok, wrapped_status) = plc_status(plc, client).await;
213
-
if !ok {
214
-
overall_status = StatusCode::BAD_GATEWAY;
215
-
}
216
-
if let Some(SyncInfo {
217
-
latest_at,
218
-
upstream_status,
219
-
}) = sync_info
220
-
{
221
-
// mirror mode
222
-
let (ok, upstream_status) = upstream_status.get().await.expect("plc_status infallible");
223
-
if !ok {
224
-
overall_status = StatusCode::BAD_GATEWAY;
225
-
}
226
-
let latest = latest_at.get().await.ok();
227
-
(
228
-
overall_status,
229
-
Json(serde_json::json!({
230
-
"server": "allegedly (mirror)",
231
-
"version": env!("CARGO_PKG_VERSION"),
232
-
"wrapped_plc": wrapped_status,
233
-
"upstream_plc": upstream_status,
234
-
"latest_at": latest,
235
-
})),
236
-
)
237
-
} else {
238
-
// wrap mode
239
-
(
240
-
overall_status,
241
-
Json(serde_json::json!({
242
-
"server": "allegedly (mirror)",
243
-
"version": env!("CARGO_PKG_VERSION"),
244
-
"wrapped_plc": wrapped_status,
245
-
})),
246
-
)
247
-
}
248
-
}
249
-
250
-
fn proxy_response(res: reqwest::Response) -> Response {
251
-
let http_res: poem::http::Response<reqwest::Body> = res.into();
252
-
let (parts, reqw_body) = http_res.into_parts();
253
-
254
-
let parts = poem::ResponseParts {
255
-
status: parts.status,
256
-
version: parts.version,
257
-
headers: parts.headers,
258
-
extensions: parts.extensions,
259
-
};
260
-
261
-
let body = http_body_util::BodyDataStream::new(reqw_body)
262
-
.map_err(|e| std::io::Error::other(Box::new(e)));
263
-
264
-
Response::from_parts(parts, poem::Body::from_bytes_stream(body))
265
-
}
266
-
267
-
#[handler]
268
-
async fn proxy(req: &Request, Data(state): Data<&State>) -> Result<Response> {
269
-
let mut target = state.plc.clone();
270
-
target.set_path(req.uri().path());
271
-
target.set_query(req.uri().query());
272
-
let wrapped_res = state
273
-
.client
274
-
.get(target)
275
-
.timeout(Duration::from_secs(3)) // should be low latency to wrapped server
276
-
.headers(req.headers().clone())
277
-
.send()
278
-
.await
279
-
.map_err(|e| {
280
-
log::error!("upstream req fail: {e}");
281
-
Error::from_string(
282
-
failed_to_reach_named("wrapped reference PLC"),
283
-
StatusCode::BAD_GATEWAY,
284
-
)
285
-
})?;
286
-
287
-
Ok(proxy_response(wrapped_res))
288
-
}
289
-
290
-
#[handler]
291
-
async fn forward_create_op_upstream(
292
-
Data(State {
293
-
upstream,
294
-
client,
295
-
experimental,
296
-
..
297
-
}): Data<&State>,
298
-
Path(did): Path<String>,
299
-
req: &Request,
300
-
body: Body,
301
-
) -> Result<Response> {
302
-
if let Some(expected_domain) = &experimental.acme_domain {
303
-
let Some(found_host) = req.uri().host() else {
304
-
return Ok(bad_create_op(&format!(
305
-
"missing `Host` header, expected {expected_domain:?} for experimental requests."
306
-
)));
307
-
};
308
-
if found_host != expected_domain {
309
-
return Ok(bad_create_op(&format!(
310
-
"experimental requests must be made to {expected_domain:?}, but this request's `Host` header was {found_host}"
311
-
)));
312
-
}
313
-
}
314
-
315
-
// adjust proxied headers
316
-
let mut headers: reqwest::header::HeaderMap = req.headers().clone();
317
-
log::trace!("original request headers: {headers:?}");
318
-
headers.insert("Host", upstream.host_str().unwrap().parse().unwrap());
319
-
let client_ua = headers
320
-
.get(USER_AGENT)
321
-
.map(|h| h.to_str().unwrap())
322
-
.unwrap_or("unknown");
323
-
headers.insert(
324
-
USER_AGENT,
325
-
format!("{UA} (forwarding from {client_ua:?})")
326
-
.parse()
327
-
.unwrap(),
328
-
);
329
-
log::trace!("adjusted request headers: {headers:?}");
330
-
331
-
let mut target = upstream.clone();
332
-
target.set_path(&did);
333
-
let upstream_res = client
334
-
.post(target)
335
-
.timeout(Duration::from_secs(15)) // be a little generous
336
-
.headers(headers)
337
-
.body(reqwest::Body::wrap_stream(body.into_bytes_stream()))
338
-
.send()
339
-
.await
340
-
.map_err(|e| {
341
-
log::warn!("upstream write fail: {e}");
342
-
Error::from_string(
343
-
failed_to_reach_named("upstream PLC"),
344
-
StatusCode::BAD_GATEWAY,
345
-
)
346
-
})?;
347
-
348
-
Ok(proxy_response(upstream_res))
349
-
}
350
-
351
-
#[handler]
352
-
async fn nope(Data(State { upstream, .. }): Data<&State>) -> (StatusCode, String) {
353
-
(
354
-
StatusCode::BAD_REQUEST,
355
-
format!(
356
-
r#"{}
357
-
358
-
Sorry, this server does not accept POST requests.
359
-
360
-
You may wish to try sending that to our upstream: {upstream}.
361
-
362
-
If you operate this server, try running with `--experimental-write-upstream`.
363
-
"#,
364
-
logo("mirror (nope)")
365
-
),
366
-
)
367
-
}
368
-
369
-
#[derive(Debug)]
370
-
pub enum ListenConf {
371
-
Acme {
372
-
domains: Vec<String>,
373
-
cache_path: PathBuf,
374
-
directory_url: String,
375
-
ipv6: bool,
376
-
},
377
-
Bind(SocketAddr),
378
-
}
379
-
380
-
#[derive(Debug, Clone)]
381
-
pub struct ExperimentalConf {
382
-
pub acme_domain: Option<String>,
383
-
pub write_upstream: bool,
384
-
}
385
-
386
-
pub async fn serve(
387
-
upstream: Url,
388
-
plc: Url,
389
-
listen: ListenConf,
390
-
experimental: ExperimentalConf,
391
-
db: Option<Db>,
392
-
) -> anyhow::Result<&'static str> {
393
-
log::info!("starting server...");
394
-
395
-
// not using crate CLIENT: don't want the retries etc
396
-
let client = Client::builder()
397
-
.user_agent(UA)
398
-
.timeout(Duration::from_secs(10)) // fallback
399
-
.build()
400
-
.expect("reqwest client to build");
401
-
402
-
// when `db` is None, we're running in wrap mode. no db access, no upstream sync
403
-
let sync_info = db.map(|db| SyncInfo {
404
-
latest_at: CachedValue::new(GetLatestAt(db), Duration::from_secs(2)),
405
-
upstream_status: CachedValue::new(
406
-
CheckUpstream(upstream.clone(), client.clone()),
407
-
Duration::from_secs(6),
408
-
),
409
-
});
410
-
411
-
let state = State {
412
-
client,
413
-
plc,
414
-
upstream: upstream.clone(),
415
-
sync_info,
416
-
experimental: experimental.clone(),
417
-
};
418
-
419
-
let mut app = Route::new()
420
-
.at("/", get(hello))
421
-
.at("/favicon.ico", get(favicon))
422
-
.at("/_health", get(health))
423
-
.at("/export", get(proxy));
424
-
425
-
if experimental.write_upstream {
426
-
log::info!("enabling experimental write forwarding to upstream");
427
-
428
-
let ip_limiter = IpLimiters::new(Quota::per_hour(10.try_into().unwrap()));
429
-
let did_limiter = CreatePlcOpLimiter::new(Quota::per_hour(4.try_into().unwrap()));
430
-
431
-
let upstream_proxier = forward_create_op_upstream
432
-
.with(GovernorMiddleware::new(did_limiter))
433
-
.with(GovernorMiddleware::new(ip_limiter));
434
-
435
-
app = app.at("/did:plc:*", get(proxy).post(upstream_proxier));
436
-
} else {
437
-
app = app.at("/did:plc:*", get(proxy).post(nope));
438
-
}
439
-
440
-
let app = app
441
-
.with(AddData::new(state))
442
-
.with(Cors::new().allow_credentials(false))
443
-
.with(Compression::new())
444
-
.with(GovernorMiddleware::new(IpLimiters::new(Quota::per_minute(
445
-
3000.try_into().expect("ratelimit middleware to build"),
446
-
))))
447
-
.with(CatchPanic::new())
448
-
.with(Tracing);
449
-
450
-
match listen {
451
-
ListenConf::Acme {
452
-
domains,
453
-
cache_path,
454
-
directory_url,
455
-
ipv6,
456
-
} => {
457
-
rustls::crypto::aws_lc_rs::default_provider()
458
-
.install_default()
459
-
.expect("crypto provider to be installable");
460
-
461
-
let mut auto_cert = AutoCert::builder()
462
-
.directory_url(directory_url)
463
-
.cache_path(cache_path);
464
-
for domain in domains {
465
-
auto_cert = auto_cert.domain(domain);
466
-
}
467
-
let auto_cert = auto_cert.build().expect("acme config to build");
468
-
469
-
log::trace!("auto_cert: {auto_cert:?}");
470
-
471
-
let notice_task = tokio::task::spawn(run_insecure_notice(ipv6));
472
-
let listener = TcpListener::bind(if ipv6 { "[::]:443" } else { "0.0.0.0:443" });
473
-
let app_res = run(app, listener.acme(auto_cert)).await;
474
-
log::warn!("server task ended, aborting insecure server task...");
475
-
notice_task.abort();
476
-
app_res?;
477
-
notice_task.await??;
478
-
}
479
-
ListenConf::Bind(addr) => run(app, TcpListener::bind(addr)).await?,
480
-
}
481
-
482
-
Ok("server (uh oh?)")
483
-
}
484
-
485
-
async fn run<A, L>(app: A, listener: L) -> std::io::Result<()>
486
-
where
487
-
A: Endpoint + 'static,
488
-
L: Listener + 'static,
489
-
{
490
-
Server::new(listener)
491
-
.name("allegedly (mirror)")
492
-
.run(app)
493
-
.await
494
-
}
495
-
496
-
/// kick off a tiny little server on a tokio task to tell people to use 443
497
-
async fn run_insecure_notice(ipv6: bool) -> Result<(), std::io::Error> {
498
-
#[handler]
499
-
fn oop_plz_be_secure() -> (StatusCode, String) {
500
-
(
501
-
StatusCode::BAD_REQUEST,
502
-
format!(
503
-
r#"{}
504
-
505
-
You probably want to change your request to use HTTPS instead of HTTP.
506
-
"#,
507
-
logo("mirror (tls on 443 please)")
508
-
),
509
-
)
510
-
}
511
-
512
-
let app = Route::new()
513
-
.at("/favicon.ico", get(favicon))
514
-
.nest("/", get(oop_plz_be_secure))
515
-
.with(Tracing);
516
-
Server::new(TcpListener::bind(if ipv6 {
517
-
"[::]:80"
518
-
} else {
519
-
"0.0.0.0:80"
520
-
}))
521
-
.name("allegedly (mirror:80 helper)")
522
-
.run(app)
523
-
.await
524
-
}
···
+1362
src/plc_fjall.rs
+1362
src/plc_fjall.rs
···
···
1
+
use crate::{BundleSource, Week};
2
+
use crate::{Dt, ExportPage, Op as CommonOp, PageBoundaryState};
3
+
use anyhow::Context;
4
+
use data_encoding::{BASE32_NOPAD, BASE64URL_NOPAD};
5
+
use fjall::{
6
+
Database, Keyspace, KeyspaceCreateOptions, OwnedWriteBatch, PersistMode,
7
+
config::BlockSizePolicy,
8
+
};
9
+
use futures::Future;
10
+
use serde::{Deserialize, Serialize};
11
+
use std::collections::BTreeMap;
12
+
use std::fmt;
13
+
use std::path::Path;
14
+
use std::sync::Arc;
15
+
use std::time::Instant;
16
+
use tokio::io::{AsyncRead, AsyncWriteExt};
17
+
use tokio::sync::{mpsc, oneshot};
18
+
19
+
const SEP: u8 = 0;
20
+
21
+
type IpldCid = cid::CidGeneric<64>;
22
+
23
+
// 24 bytes -> 15 bytes
24
+
fn encode_did(buf: &mut Vec<u8>, did: &str) -> anyhow::Result<usize> {
25
+
let input = did.trim_start_matches("did:plc:").to_uppercase();
26
+
let len = BASE32_NOPAD
27
+
.decode_len(input.len())
28
+
.map_err(|_| anyhow::anyhow!("failed to calculate decode len for {did}"))?;
29
+
30
+
let start = buf.len();
31
+
buf.resize(start + len, 0);
32
+
33
+
BASE32_NOPAD
34
+
.decode_mut(input.as_bytes(), &mut buf[start..])
35
+
.map_err(|_| anyhow::anyhow!("failed to encode did {did}"))
36
+
}
37
+
38
+
// 59 bytes -> 36 bytes
39
+
fn decode_cid_str(s: &str) -> anyhow::Result<Vec<u8>> {
40
+
let cid = IpldCid::try_from(s)?;
41
+
let mut buf = Vec::new();
42
+
cid.write_bytes(&mut buf)
43
+
.map_err(|e| anyhow::anyhow!("failed to encode cid {s}: {e}"))?;
44
+
Ok(buf)
45
+
}
46
+
47
+
fn decode_cid(bytes: &[u8]) -> anyhow::Result<String> {
48
+
IpldCid::try_from(bytes)
49
+
.map_err(|e| anyhow::anyhow!("failed to decode cid: {e}"))
50
+
.map(|cid| cid.to_string())
51
+
}
52
+
53
+
fn decode_did(bytes: &[u8]) -> String {
54
+
let decoded = BASE32_NOPAD.encode(bytes).to_lowercase();
55
+
format!("did:plc:{decoded}")
56
+
}
57
+
58
+
fn op_key(created_at: &Dt, cid_suffix: &[u8]) -> Vec<u8> {
59
+
let micros = created_at.timestamp_micros() as u64;
60
+
let mut key = Vec::with_capacity(8 + 1 + cid_suffix.len());
61
+
key.extend_from_slice(µs.to_be_bytes());
62
+
key.push(SEP);
63
+
key.extend_from_slice(cid_suffix);
64
+
key
65
+
}
66
+
67
+
fn by_did_prefix(did: &str) -> anyhow::Result<Vec<u8>> {
68
+
let mut p = Vec::with_capacity(BASE32_NOPAD.decode_len(did.len())? + 1);
69
+
encode_did(&mut p, did)?;
70
+
p.push(SEP);
71
+
Ok(p)
72
+
}
73
+
74
+
fn by_did_key(did: &str, created_at: &Dt, cid_suffix: &[u8]) -> anyhow::Result<Vec<u8>> {
75
+
let mut key = by_did_prefix(did)?;
76
+
let micros = created_at.timestamp_micros() as u64;
77
+
key.extend_from_slice(µs.to_be_bytes());
78
+
key.push(SEP);
79
+
key.extend_from_slice(cid_suffix);
80
+
Ok(key)
81
+
}
82
+
83
+
fn decode_timestamp(key: &[u8]) -> anyhow::Result<Dt> {
84
+
let micros = u64::from_be_bytes(
85
+
key.try_into()
86
+
.map_err(|e| anyhow::anyhow!("invalid timestamp key {key:?}: {e}"))?,
87
+
);
88
+
Dt::from_timestamp_micros(micros as i64)
89
+
.ok_or_else(|| anyhow::anyhow!("invalid timestamp {micros}"))
90
+
}
91
+
92
+
/// base64url-encoded ECDSA signature → raw bytes
93
+
#[derive(Debug, Clone, Serialize, Deserialize)]
94
+
struct Signature(#[serde(with = "serde_bytes")] Vec<u8>);
95
+
96
+
impl Signature {
97
+
fn from_base64url(s: &str) -> anyhow::Result<Self> {
98
+
BASE64URL_NOPAD
99
+
.decode(s.as_bytes())
100
+
.map(Self)
101
+
.map_err(|e| anyhow::anyhow!("invalid base64url sig {s}: {e}"))
102
+
}
103
+
}
104
+
105
+
impl fmt::Display for Signature {
106
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107
+
f.write_str(&BASE64URL_NOPAD.encode(&self.0))
108
+
}
109
+
}
110
+
111
+
/// did:key:z... → raw multicodec public key bytes
112
+
#[derive(Debug, Clone, Serialize, Deserialize)]
113
+
struct DidKey(#[serde(with = "serde_bytes")] Vec<u8>);
114
+
115
+
impl DidKey {
116
+
fn from_did_key(s: &str) -> anyhow::Result<Self> {
117
+
let multibase_str = s
118
+
.strip_prefix("did:key:")
119
+
.ok_or_else(|| anyhow::anyhow!("missing did:key: prefix in {s}"))?;
120
+
let (_base, bytes) = multibase::decode(multibase_str)
121
+
.map_err(|e| anyhow::anyhow!("invalid multibase in did:key {s}: {e}"))?;
122
+
Ok(Self(bytes))
123
+
}
124
+
}
125
+
126
+
impl fmt::Display for DidKey {
127
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128
+
write!(
129
+
f,
130
+
"did:key:{}",
131
+
multibase::encode(multibase::Base::Base58Btc, &self.0)
132
+
)
133
+
}
134
+
}
135
+
136
+
/// CID string → binary CID bytes
137
+
#[derive(Debug, Clone, Serialize, Deserialize)]
138
+
struct PlcCid(#[serde(with = "serde_bytes")] Vec<u8>);
139
+
140
+
impl PlcCid {
141
+
fn from_cid_str(s: &str) -> anyhow::Result<Self> {
142
+
let cid = IpldCid::try_from(s)?;
143
+
let mut buf = Vec::new();
144
+
cid.write_bytes(&mut buf)
145
+
.map_err(|e| anyhow::anyhow!("failed to encode cid {s}: {e}"))?;
146
+
Ok(Self(buf))
147
+
}
148
+
}
149
+
150
+
impl fmt::Display for PlcCid {
151
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152
+
let cid = IpldCid::try_from(self.0.as_slice()).map_err(|_| fmt::Error)?;
153
+
write!(f, "{cid}")
154
+
}
155
+
}
156
+
157
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
158
+
enum Aka {
159
+
Bluesky(String),
160
+
Atproto(String),
161
+
Other(String),
162
+
}
163
+
164
+
impl Aka {
165
+
fn from_str(s: &str) -> Self {
166
+
if let Some(stripped) = s.strip_prefix("at://") {
167
+
if let Some(handle) = stripped.strip_suffix(".bsky.social") {
168
+
Self::Bluesky(handle.to_string())
169
+
} else {
170
+
Self::Atproto(stripped.to_string())
171
+
}
172
+
} else {
173
+
Self::Other(s.to_string())
174
+
}
175
+
}
176
+
}
177
+
178
+
impl fmt::Display for Aka {
179
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180
+
match self {
181
+
Self::Bluesky(h) => write!(f, "at://{h}.bsky.social"),
182
+
Self::Atproto(h) => write!(f, "at://{h}"),
183
+
Self::Other(s) => f.write_str(s),
184
+
}
185
+
}
186
+
}
187
+
188
+
#[derive(Debug, Clone, Serialize, Deserialize)]
189
+
#[serde(rename_all = "snake_case")]
190
+
enum OpType {
191
+
PlcOperation,
192
+
Create,
193
+
PlcTombstone,
194
+
Other(String),
195
+
}
196
+
197
+
impl OpType {
198
+
fn from_str(s: &str) -> Self {
199
+
match s {
200
+
"plc_operation" => Self::PlcOperation,
201
+
"create" => Self::Create,
202
+
"plc_tombstone" => Self::PlcTombstone,
203
+
other => Self::Other(other.to_string()),
204
+
}
205
+
}
206
+
207
+
fn as_str(&self) -> &str {
208
+
match self {
209
+
Self::PlcOperation => "plc_operation",
210
+
Self::Create => "create",
211
+
Self::PlcTombstone => "plc_tombstone",
212
+
Self::Other(s) => s,
213
+
}
214
+
}
215
+
}
216
+
217
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218
+
enum StoredOpField {
219
+
Type,
220
+
Sig,
221
+
Prev,
222
+
RotationKeys,
223
+
VerificationMethods,
224
+
AlsoKnownAs,
225
+
Services,
226
+
SigningKey,
227
+
RecoveryKey,
228
+
Handle,
229
+
Service,
230
+
}
231
+
232
+
impl StoredOpField {
233
+
fn as_str(&self) -> &'static str {
234
+
match self {
235
+
Self::Type => "type",
236
+
Self::Sig => "sig",
237
+
Self::Prev => "prev",
238
+
Self::RotationKeys => "rotationKeys",
239
+
Self::VerificationMethods => "verificationMethods",
240
+
Self::AlsoKnownAs => "alsoKnownAs",
241
+
Self::Services => "services",
242
+
Self::SigningKey => "signingKey",
243
+
Self::RecoveryKey => "recoveryKey",
244
+
Self::Handle => "handle",
245
+
Self::Service => "service",
246
+
}
247
+
}
248
+
}
249
+
250
+
impl AsRef<str> for StoredOpField {
251
+
fn as_ref(&self) -> &str {
252
+
self.as_str()
253
+
}
254
+
}
255
+
256
+
impl std::ops::Deref for StoredOpField {
257
+
type Target = str;
258
+
fn deref(&self) -> &Self::Target {
259
+
self.as_str()
260
+
}
261
+
}
262
+
263
+
impl fmt::Display for StoredOpField {
264
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265
+
f.write_str(self.as_str())
266
+
}
267
+
}
268
+
269
+
#[derive(Debug, thiserror::Error)]
270
+
enum StoredOpError {
271
+
#[error("operation is not an object")]
272
+
NotAnObject,
273
+
#[error("missing required field: {0}")]
274
+
MissingField(StoredOpField),
275
+
#[error("invalid field {0}: {1}")]
276
+
InvalidField(StoredOpField, #[source] anyhow::Error),
277
+
#[error("type mismatch for field {0}: expected {1}")]
278
+
TypeMismatch(StoredOpField, &'static str),
279
+
}
280
+
281
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
282
+
enum VerificationMethodKey {
283
+
Atproto,
284
+
Other(String),
285
+
}
286
+
287
+
impl VerificationMethodKey {
288
+
fn from_str(s: &str) -> Self {
289
+
match s {
290
+
"atproto" => Self::Atproto,
291
+
_ => Self::Other(s.to_string()),
292
+
}
293
+
}
294
+
295
+
fn as_str(&self) -> &str {
296
+
match self {
297
+
Self::Atproto => "atproto",
298
+
Self::Other(s) => s,
299
+
}
300
+
}
301
+
}
302
+
303
+
impl fmt::Display for VerificationMethodKey {
304
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305
+
f.write_str(self.as_str())
306
+
}
307
+
}
308
+
309
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
310
+
enum ServiceKey {
311
+
AtprotoPds,
312
+
Other(String),
313
+
}
314
+
315
+
impl ServiceKey {
316
+
fn from_str(s: &str) -> Self {
317
+
match s {
318
+
"atproto_pds" => Self::AtprotoPds,
319
+
_ => Self::Other(s.to_string()),
320
+
}
321
+
}
322
+
323
+
fn as_str(&self) -> &str {
324
+
match self {
325
+
Self::AtprotoPds => "atproto_pds",
326
+
Self::Other(s) => s,
327
+
}
328
+
}
329
+
}
330
+
331
+
impl fmt::Display for ServiceKey {
332
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333
+
f.write_str(self.as_str())
334
+
}
335
+
}
336
+
337
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
338
+
enum ServiceType {
339
+
AtprotoPersonalDataServer,
340
+
Other(String),
341
+
}
342
+
343
+
impl ServiceType {
344
+
fn from_str(s: &str) -> Self {
345
+
match s {
346
+
"AtprotoPersonalDataServer" => Self::AtprotoPersonalDataServer,
347
+
_ => Self::Other(s.to_string()),
348
+
}
349
+
}
350
+
351
+
fn as_str(&self) -> &str {
352
+
match self {
353
+
Self::AtprotoPersonalDataServer => "AtprotoPersonalDataServer",
354
+
Self::Other(s) => s,
355
+
}
356
+
}
357
+
}
358
+
359
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
360
+
enum ServiceEndpoint {
361
+
BlueskyPds(String),
362
+
Other(String),
363
+
}
364
+
365
+
impl ServiceEndpoint {
366
+
fn from_str(s: &str) -> Self {
367
+
if let Some(host) = s
368
+
.strip_prefix("https://")
369
+
.and_then(|h| h.strip_suffix(".host.bsky.network"))
370
+
{
371
+
Self::BlueskyPds(host.to_string())
372
+
} else {
373
+
Self::Other(s.to_string())
374
+
}
375
+
}
376
+
377
+
fn as_string(&self) -> String {
378
+
match self {
379
+
Self::BlueskyPds(h) => format!("https://{h}.host.bsky.network"),
380
+
Self::Other(s) => s.clone(),
381
+
}
382
+
}
383
+
}
384
+
385
+
#[derive(Debug, Clone, Serialize, Deserialize)]
386
+
struct StoredService {
387
+
r#type: ServiceType,
388
+
endpoint: ServiceEndpoint,
389
+
}
390
+
391
+
#[derive(Debug, Clone, Serialize, Deserialize)]
392
+
struct StoredOp {
393
+
op_type: OpType,
394
+
sig: Signature,
395
+
prev: Option<PlcCid>,
396
+
397
+
rotation_keys: Option<Vec<DidKey>>,
398
+
verification_methods: Option<BTreeMap<VerificationMethodKey, DidKey>>,
399
+
also_known_as: Option<Vec<Aka>>,
400
+
services: Option<BTreeMap<ServiceKey, StoredService>>,
401
+
402
+
// legacy create fields
403
+
signing_key: Option<DidKey>,
404
+
recovery_key: Option<DidKey>,
405
+
handle: Option<String>,
406
+
service: Option<String>,
407
+
408
+
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
409
+
unknown: BTreeMap<String, serde_json::Value>,
410
+
}
411
+
412
+
impl StoredOp {
413
+
fn from_json_value(v: serde_json::Value) -> (Option<Self>, Vec<StoredOpError>) {
414
+
let serde_json::Value::Object(mut obj) = v else {
415
+
return (None, vec![StoredOpError::NotAnObject]);
416
+
};
417
+
418
+
let mut errors = Vec::new();
419
+
let mut unknown = BTreeMap::new();
420
+
421
+
let op_type = match obj.remove(&*StoredOpField::Type) {
422
+
Some(serde_json::Value::String(s)) => OpType::from_str(&s),
423
+
Some(v) => {
424
+
errors.push(StoredOpError::TypeMismatch(StoredOpField::Type, "string"));
425
+
unknown.insert(StoredOpField::Type.to_string(), v);
426
+
OpType::Other(String::new())
427
+
}
428
+
Option::None => {
429
+
errors.push(StoredOpError::MissingField(StoredOpField::Type));
430
+
OpType::Other(String::new())
431
+
}
432
+
};
433
+
434
+
let sig = match obj.remove(&*StoredOpField::Sig) {
435
+
Some(serde_json::Value::String(s)) => match Signature::from_base64url(&s) {
436
+
Ok(sig) => sig,
437
+
Err(e) => {
438
+
errors.push(StoredOpError::InvalidField(StoredOpField::Sig, e));
439
+
unknown.insert(StoredOpField::Sig.to_string(), serde_json::Value::String(s));
440
+
Signature(Vec::new())
441
+
}
442
+
},
443
+
Some(v) => {
444
+
errors.push(StoredOpError::TypeMismatch(StoredOpField::Sig, "string"));
445
+
unknown.insert(StoredOpField::Sig.to_string(), v);
446
+
Signature(Vec::new())
447
+
}
448
+
Option::None => {
449
+
errors.push(StoredOpError::MissingField(StoredOpField::Sig));
450
+
Signature(Vec::new())
451
+
}
452
+
};
453
+
454
+
let prev = match obj.remove(&*StoredOpField::Prev) {
455
+
Some(serde_json::Value::Null) | Option::None => Option::None,
456
+
Some(serde_json::Value::String(s)) => match PlcCid::from_cid_str(&s) {
457
+
Ok(p) => Some(p),
458
+
Err(e) => {
459
+
errors.push(StoredOpError::InvalidField(StoredOpField::Prev, e));
460
+
unknown.insert(
461
+
StoredOpField::Prev.to_string(),
462
+
serde_json::Value::String(s),
463
+
);
464
+
Option::None
465
+
}
466
+
},
467
+
Some(v) => {
468
+
errors.push(StoredOpError::TypeMismatch(StoredOpField::Prev, "string"));
469
+
unknown.insert(StoredOpField::Prev.to_string(), v);
470
+
Option::None
471
+
}
472
+
};
473
+
474
+
let rotation_keys = match obj.remove(&*StoredOpField::RotationKeys) {
475
+
Some(serde_json::Value::Array(arr)) => {
476
+
let mut keys = Vec::with_capacity(arr.len());
477
+
let mut failed = false;
478
+
for v in &arr {
479
+
match v {
480
+
serde_json::Value::String(s) => match DidKey::from_did_key(s) {
481
+
Ok(k) => keys.push(k),
482
+
Err(e) => {
483
+
errors.push(StoredOpError::InvalidField(
484
+
StoredOpField::RotationKeys,
485
+
e,
486
+
));
487
+
failed = true;
488
+
break;
489
+
}
490
+
},
491
+
_ => {
492
+
errors.push(StoredOpError::TypeMismatch(
493
+
StoredOpField::RotationKeys,
494
+
"string inside array",
495
+
));
496
+
failed = true;
497
+
break;
498
+
}
499
+
}
500
+
}
501
+
if failed {
502
+
unknown.insert(
503
+
StoredOpField::RotationKeys.to_string(),
504
+
serde_json::Value::Array(arr),
505
+
);
506
+
Option::None
507
+
} else {
508
+
Some(keys)
509
+
}
510
+
}
511
+
Some(v) => {
512
+
errors.push(StoredOpError::TypeMismatch(
513
+
StoredOpField::RotationKeys,
514
+
"array",
515
+
));
516
+
unknown.insert(StoredOpField::RotationKeys.to_string(), v);
517
+
Option::None
518
+
}
519
+
Option::None => Option::None,
520
+
};
521
+
522
+
let verification_methods = match obj.remove(&*StoredOpField::VerificationMethods) {
523
+
Some(serde_json::Value::Object(map)) => {
524
+
let mut methods = BTreeMap::new();
525
+
let mut failed = false;
526
+
for (k, v) in &map {
527
+
match v {
528
+
serde_json::Value::String(s) => match DidKey::from_did_key(s) {
529
+
Ok(key) => {
530
+
methods.insert(VerificationMethodKey::from_str(k), key);
531
+
}
532
+
Err(e) => {
533
+
errors.push(StoredOpError::InvalidField(
534
+
StoredOpField::VerificationMethods,
535
+
e,
536
+
));
537
+
failed = true;
538
+
break;
539
+
}
540
+
},
541
+
_ => {
542
+
errors.push(StoredOpError::TypeMismatch(
543
+
StoredOpField::VerificationMethods,
544
+
"string value in object",
545
+
));
546
+
failed = true;
547
+
break;
548
+
}
549
+
}
550
+
}
551
+
if failed {
552
+
unknown.insert(
553
+
StoredOpField::VerificationMethods.to_string(),
554
+
serde_json::Value::Object(map),
555
+
);
556
+
Option::None
557
+
} else {
558
+
Some(methods)
559
+
}
560
+
}
561
+
Some(v) => {
562
+
errors.push(StoredOpError::TypeMismatch(
563
+
StoredOpField::VerificationMethods,
564
+
"object",
565
+
));
566
+
unknown.insert(StoredOpField::VerificationMethods.to_string(), v);
567
+
Option::None
568
+
}
569
+
Option::None => Option::None,
570
+
};
571
+
572
+
let also_known_as = match obj.remove(&*StoredOpField::AlsoKnownAs) {
573
+
Some(serde_json::Value::Array(arr)) => {
574
+
let mut akas = Vec::with_capacity(arr.len());
575
+
let mut failed = false;
576
+
for v in &arr {
577
+
match v {
578
+
serde_json::Value::String(s) => akas.push(Aka::from_str(s)),
579
+
_ => {
580
+
errors.push(StoredOpError::TypeMismatch(
581
+
StoredOpField::AlsoKnownAs,
582
+
"string inside array",
583
+
));
584
+
failed = true;
585
+
break;
586
+
}
587
+
}
588
+
}
589
+
if failed {
590
+
unknown.insert(
591
+
StoredOpField::AlsoKnownAs.to_string(),
592
+
serde_json::Value::Array(arr),
593
+
);
594
+
Option::None
595
+
} else {
596
+
Some(akas)
597
+
}
598
+
}
599
+
Some(v) => {
600
+
errors.push(StoredOpError::TypeMismatch(
601
+
StoredOpField::AlsoKnownAs,
602
+
"array",
603
+
));
604
+
unknown.insert(StoredOpField::AlsoKnownAs.to_string(), v);
605
+
Option::None
606
+
}
607
+
Option::None => Option::None,
608
+
};
609
+
610
+
let services = match obj.remove(&*StoredOpField::Services) {
611
+
Some(serde_json::Value::Object(map)) => {
612
+
let mut svcs = BTreeMap::new();
613
+
let mut failed = false;
614
+
for (k, v) in &map {
615
+
if let (Some(r#type), Some(endpoint)) = (
616
+
v.get("type").and_then(|t| t.as_str()),
617
+
v.get("endpoint").and_then(|e| e.as_str()),
618
+
) {
619
+
let svc = StoredService {
620
+
r#type: ServiceType::from_str(r#type),
621
+
endpoint: ServiceEndpoint::from_str(endpoint),
622
+
};
623
+
svcs.insert(ServiceKey::from_str(k), svc);
624
+
} else {
625
+
errors.push(StoredOpError::TypeMismatch(
626
+
StoredOpField::Services,
627
+
"missing or invalid type/endpoint in service object",
628
+
));
629
+
failed = true;
630
+
break;
631
+
}
632
+
}
633
+
if failed {
634
+
unknown.insert(
635
+
StoredOpField::Services.to_string(),
636
+
serde_json::Value::Object(map),
637
+
);
638
+
Option::None
639
+
} else {
640
+
Some(svcs)
641
+
}
642
+
}
643
+
Some(v) => {
644
+
errors.push(StoredOpError::TypeMismatch(
645
+
StoredOpField::Services,
646
+
"object",
647
+
));
648
+
unknown.insert(StoredOpField::Services.to_string(), v);
649
+
Option::None
650
+
}
651
+
Option::None => Option::None,
652
+
};
653
+
654
+
let signing_key = match obj.remove(&*StoredOpField::SigningKey) {
655
+
Some(serde_json::Value::String(s)) => match DidKey::from_did_key(&s) {
656
+
Ok(key) => Some(key),
657
+
Err(e) => {
658
+
errors.push(StoredOpError::InvalidField(StoredOpField::SigningKey, e));
659
+
unknown.insert(
660
+
StoredOpField::SigningKey.to_string(),
661
+
serde_json::Value::String(s),
662
+
);
663
+
Option::None
664
+
}
665
+
},
666
+
Some(v) => {
667
+
errors.push(StoredOpError::TypeMismatch(
668
+
StoredOpField::SigningKey,
669
+
"string",
670
+
));
671
+
unknown.insert(StoredOpField::SigningKey.to_string(), v);
672
+
Option::None
673
+
}
674
+
Option::None => Option::None,
675
+
};
676
+
677
+
let recovery_key = match obj.remove(&*StoredOpField::RecoveryKey) {
678
+
Some(serde_json::Value::String(s)) => match DidKey::from_did_key(&s) {
679
+
Ok(key) => Some(key),
680
+
Err(e) => {
681
+
errors.push(StoredOpError::InvalidField(StoredOpField::RecoveryKey, e));
682
+
unknown.insert(
683
+
StoredOpField::RecoveryKey.to_string(),
684
+
serde_json::Value::String(s),
685
+
);
686
+
Option::None
687
+
}
688
+
},
689
+
Some(v) => {
690
+
errors.push(StoredOpError::TypeMismatch(
691
+
StoredOpField::RecoveryKey,
692
+
"string",
693
+
));
694
+
unknown.insert(StoredOpField::RecoveryKey.to_string(), v);
695
+
Option::None
696
+
}
697
+
Option::None => Option::None,
698
+
};
699
+
700
+
let handle = match obj.remove(&*StoredOpField::Handle) {
701
+
Some(serde_json::Value::String(s)) => Some(s),
702
+
Some(v) => {
703
+
errors.push(StoredOpError::TypeMismatch(StoredOpField::Handle, "string"));
704
+
unknown.insert(StoredOpField::Handle.to_string(), v);
705
+
Option::None
706
+
}
707
+
Option::None => Option::None,
708
+
};
709
+
710
+
let service = match obj.remove(&*StoredOpField::Service) {
711
+
Some(serde_json::Value::String(s)) => Some(s),
712
+
Some(v) => {
713
+
errors.push(StoredOpError::TypeMismatch(
714
+
StoredOpField::Service,
715
+
"string",
716
+
));
717
+
unknown.insert(StoredOpField::Service.to_string(), v);
718
+
Option::None
719
+
}
720
+
Option::None => Option::None,
721
+
};
722
+
723
+
for (k, v) in obj {
724
+
unknown.insert(k, v);
725
+
}
726
+
727
+
(
728
+
Some(Self {
729
+
op_type,
730
+
sig,
731
+
prev,
732
+
rotation_keys,
733
+
verification_methods,
734
+
also_known_as,
735
+
services,
736
+
signing_key,
737
+
recovery_key,
738
+
handle,
739
+
service,
740
+
unknown,
741
+
}),
742
+
errors,
743
+
)
744
+
}
745
+
746
+
fn to_json_value(&self) -> serde_json::Value {
747
+
let mut map = serde_json::Map::new();
748
+
749
+
map.insert((*StoredOpField::Type).into(), self.op_type.as_str().into());
750
+
map.insert((*StoredOpField::Sig).into(), self.sig.to_string().into());
751
+
map.insert(
752
+
(*StoredOpField::Prev).into(),
753
+
self.prev
754
+
.as_ref()
755
+
.map(|c| serde_json::Value::String(c.to_string()))
756
+
.unwrap_or(serde_json::Value::Null),
757
+
);
758
+
759
+
if let Some(keys) = &self.rotation_keys {
760
+
map.insert(
761
+
(*StoredOpField::RotationKeys).into(),
762
+
keys.iter()
763
+
.map(|k| serde_json::Value::String(k.to_string()))
764
+
.collect::<Vec<_>>()
765
+
.into(),
766
+
);
767
+
}
768
+
769
+
if let Some(methods) = &self.verification_methods {
770
+
let obj: serde_json::Map<String, serde_json::Value> = methods
771
+
.iter()
772
+
.map(|(k, v)| {
773
+
(
774
+
k.as_str().to_string(),
775
+
serde_json::Value::String(v.to_string()),
776
+
)
777
+
})
778
+
.collect();
779
+
map.insert((*StoredOpField::VerificationMethods).into(), obj.into());
780
+
}
781
+
782
+
if let Some(aka) = &self.also_known_as {
783
+
map.insert(
784
+
(*StoredOpField::AlsoKnownAs).into(),
785
+
aka.iter()
786
+
.map(|h| serde_json::Value::String(h.to_string()))
787
+
.collect::<Vec<_>>()
788
+
.into(),
789
+
);
790
+
}
791
+
792
+
if let Some(services) = &self.services {
793
+
let obj: serde_json::Map<String, serde_json::Value> = services
794
+
.iter()
795
+
.map(|(k, svc)| {
796
+
(
797
+
k.as_str().to_string(),
798
+
serde_json::json!({
799
+
"type": svc.r#type.as_str(),
800
+
"endpoint": svc.endpoint.as_string(),
801
+
}),
802
+
)
803
+
})
804
+
.collect();
805
+
map.insert((*StoredOpField::Services).into(), obj.into());
806
+
}
807
+
808
+
// legacy create fields
809
+
if let Some(key) = &self.signing_key {
810
+
map.insert((*StoredOpField::SigningKey).into(), key.to_string().into());
811
+
}
812
+
if let Some(key) = &self.recovery_key {
813
+
map.insert((*StoredOpField::RecoveryKey).into(), key.to_string().into());
814
+
}
815
+
if let Some(handle) = &self.handle {
816
+
map.insert((*StoredOpField::Handle).into(), handle.clone().into());
817
+
}
818
+
if let Some(service) = &self.service {
819
+
map.insert((*StoredOpField::Service).into(), service.clone().into());
820
+
}
821
+
822
+
for (k, v) in &self.unknown {
823
+
map.insert(k.clone(), v.clone());
824
+
}
825
+
826
+
serde_json::Value::Object(map)
827
+
}
828
+
}
829
+
830
+
// this is basically Op, but without the cid and created_at fields
831
+
// since we have them in the key already
832
+
#[derive(Debug, Deserialize, Serialize)]
833
+
#[serde(rename_all = "camelCase")]
834
+
struct DbOp {
835
+
#[serde(with = "serde_bytes")]
836
+
pub did: Vec<u8>,
837
+
#[serde(with = "serde_bytes")]
838
+
pub cid_prefix: Vec<u8>,
839
+
pub nullified: bool,
840
+
pub operation: StoredOp,
841
+
}
842
+
843
+
// we have our own Op struct for fjall since we dont want to have to convert Value back to RawValue
844
+
#[derive(Debug, Serialize)]
845
+
pub struct Op {
846
+
pub did: String,
847
+
pub cid: String,
848
+
pub created_at: Dt,
849
+
pub nullified: bool,
850
+
pub operation: serde_json::Value,
851
+
}
852
+
853
+
#[derive(Clone)]
854
+
pub struct FjallDb {
855
+
inner: Arc<FjallInner>,
856
+
}
857
+
858
+
struct FjallInner {
859
+
db: Database,
860
+
ops: Keyspace,
861
+
by_did: Keyspace,
862
+
}
863
+
864
+
impl FjallDb {
865
+
pub fn open(path: impl AsRef<Path>) -> fjall::Result<Self> {
866
+
const fn kb(kb: u32) -> u32 {
867
+
kb * 1_024
868
+
}
869
+
const fn mb(mb: u32) -> u64 {
870
+
kb(mb) as u64 * 1_024
871
+
}
872
+
873
+
let db = Database::builder(path)
874
+
// 32mb is too low we can afford more
875
+
// this should be configurable though!
876
+
.cache_size(mb(256))
877
+
.open()?;
878
+
let opts = KeyspaceCreateOptions::default;
879
+
let ops = db.keyspace("ops", || {
880
+
opts()
881
+
// this is mainly for when backfilling
882
+
.max_memtable_size(mb(192))
883
+
// this wont compress terribly well since its a bunch of CIDs and signatures and did:keys
884
+
// and we want to keep reads fast since we'll be reading a lot...
885
+
.data_block_size_policy(BlockSizePolicy::new([kb(4), kb(8), kb(32)]))
886
+
// this has no downsides, since the only point reads that might miss we do is on by_did
887
+
.expect_point_read_hits(true)
888
+
})?;
889
+
let by_did = db.keyspace("by_did", || {
890
+
opts()
891
+
.max_memtable_size(mb(64))
892
+
// this isn't gonna compress well anyway, since its just keys (did + timestamp + cid)
893
+
// and dids dont have many operations in the first place, so we can use small blocks
894
+
.data_block_size_policy(BlockSizePolicy::all(kb(2)))
895
+
})?;
896
+
Ok(Self {
897
+
inner: Arc::new(FjallInner { db, ops, by_did }),
898
+
})
899
+
}
900
+
901
+
pub fn clear(&self) -> fjall::Result<()> {
902
+
self.inner.ops.clear()?;
903
+
self.inner.by_did.clear()?;
904
+
Ok(())
905
+
}
906
+
907
+
pub fn persist(&self) -> fjall::Result<()> {
908
+
self.inner.db.persist(PersistMode::SyncAll)
909
+
}
910
+
911
+
pub fn compact(&self) -> fjall::Result<()> {
912
+
self.inner.ops.major_compact()?;
913
+
self.inner.by_did.major_compact()?;
914
+
Ok(())
915
+
}
916
+
917
+
pub fn get_latest(&self) -> anyhow::Result<Option<Dt>> {
918
+
let Some(guard) = self.inner.ops.last_key_value() else {
919
+
return Ok(None);
920
+
};
921
+
let key = guard
922
+
.key()
923
+
.map_err(|e| anyhow::anyhow!("fjall key error: {e}"))?;
924
+
925
+
key.get(..8)
926
+
.ok_or_else(|| anyhow::anyhow!("invalid timestamp key {key:?}"))
927
+
.map(decode_timestamp)
928
+
.flatten()
929
+
.map(Some)
930
+
}
931
+
932
+
pub fn insert_op(&self, batch: &mut OwnedWriteBatch, op: &CommonOp) -> anyhow::Result<usize> {
933
+
let cid_bytes = decode_cid_str(&op.cid)?;
934
+
let cid_prefix = cid_bytes
935
+
.get(..30)
936
+
.ok_or_else(|| anyhow::anyhow!("invalid cid length (prefix): {}", op.cid))?
937
+
.to_vec();
938
+
let cid_suffix = cid_bytes
939
+
.get(30..)
940
+
.ok_or_else(|| anyhow::anyhow!("invalid cid length (suffix): {}", op.cid))?;
941
+
942
+
let pk = by_did_key(&op.did, &op.created_at, cid_suffix)?;
943
+
if self.inner.by_did.get(&pk)?.is_some() {
944
+
return Ok(0);
945
+
}
946
+
let ts_key = op_key(&op.created_at, cid_suffix);
947
+
948
+
let mut encoded_did = Vec::with_capacity(15);
949
+
encode_did(&mut encoded_did, &op.did)?;
950
+
951
+
let json_val: serde_json::Value = serde_json::from_str(op.operation.get())?;
952
+
let (stored, mut errors) = StoredOp::from_json_value(json_val);
953
+
954
+
let Some(operation) = stored else {
955
+
return Err(errors.remove(0)).context("fatal operation parse error");
956
+
};
957
+
958
+
for e in &errors {
959
+
log::warn!("failed to parse operation {} {}: {}", op.did, op.cid, e);
960
+
}
961
+
if !errors.is_empty() {
962
+
// if parse failed but not fatal, we just dont store it
963
+
return Ok(0);
964
+
}
965
+
966
+
let db_op = DbOp {
967
+
did: encoded_did,
968
+
cid_prefix,
969
+
nullified: op.nullified,
970
+
operation,
971
+
};
972
+
let value = rmp_serde::to_vec(&db_op)?;
973
+
batch.insert(&self.inner.ops, &ts_key, &value);
974
+
batch.insert(&self.inner.by_did, &pk, &[]);
975
+
Ok(1)
976
+
}
977
+
978
+
pub fn ops_for_did(
979
+
&self,
980
+
did: &str,
981
+
) -> anyhow::Result<impl Iterator<Item = anyhow::Result<Op>> + '_> {
982
+
let prefix = by_did_prefix(did)?;
983
+
984
+
Ok(self.inner.by_did.prefix(&prefix).map(move |guard| {
985
+
let (by_did_key, _) = guard
986
+
.into_inner()
987
+
.map_err(|e| anyhow::anyhow!("fjall read error: {e}"))?;
988
+
989
+
let key_rest = by_did_key
990
+
.get(prefix.len()..)
991
+
.ok_or_else(|| anyhow::anyhow!("invalid by_did key {by_did_key:?}"))?;
992
+
993
+
let ts_bytes = key_rest
994
+
.get(..8)
995
+
.ok_or_else(|| anyhow::anyhow!("invalid length: {key_rest:?}"))?;
996
+
let cid_suffix = key_rest
997
+
.get(9..)
998
+
.ok_or_else(|| anyhow::anyhow!("invalid length: {key_rest:?}"))?;
999
+
1000
+
let op_key = [ts_bytes, &[SEP][..], cid_suffix].concat();
1001
+
let ts = decode_timestamp(ts_bytes)?;
1002
+
1003
+
let value = self
1004
+
.inner
1005
+
.ops
1006
+
.get(&op_key)?
1007
+
.ok_or_else(|| anyhow::anyhow!("op not found: {op_key:?}"))?;
1008
+
1009
+
let op: DbOp = rmp_serde::from_slice(&value)?;
1010
+
let mut full_cid_bytes = op.cid_prefix.clone();
1011
+
full_cid_bytes.extend_from_slice(cid_suffix);
1012
+
1013
+
let cid = decode_cid(&full_cid_bytes)?;
1014
+
let did = decode_did(&op.did);
1015
+
1016
+
Ok(Op {
1017
+
did,
1018
+
cid,
1019
+
created_at: ts,
1020
+
nullified: op.nullified,
1021
+
operation: op.operation.to_json_value(),
1022
+
})
1023
+
}))
1024
+
}
1025
+
1026
+
pub fn export_ops(
1027
+
&self,
1028
+
range: impl std::ops::RangeBounds<Dt>,
1029
+
) -> anyhow::Result<impl Iterator<Item = anyhow::Result<Op>> + '_> {
1030
+
use std::ops::Bound;
1031
+
let map_bound = |b: Bound<&Dt>| -> Bound<[u8; 8]> {
1032
+
match b {
1033
+
Bound::Included(dt) => Bound::Included(dt.timestamp_micros().to_be_bytes()),
1034
+
Bound::Excluded(dt) => Bound::Excluded(dt.timestamp_micros().to_be_bytes()),
1035
+
Bound::Unbounded => Bound::Unbounded,
1036
+
}
1037
+
};
1038
+
let range = (map_bound(range.start_bound()), map_bound(range.end_bound()));
1039
+
1040
+
let iter = self.inner.ops.range(range);
1041
+
1042
+
Ok(iter.map(|item| {
1043
+
let (key, value) = item
1044
+
.into_inner()
1045
+
.map_err(|e| anyhow::anyhow!("fjall read error: {e}"))?;
1046
+
let db_op: DbOp = rmp_serde::from_slice(&value)?;
1047
+
let created_at = decode_timestamp(
1048
+
key.get(..8)
1049
+
.ok_or_else(|| anyhow::anyhow!("invalid op key {key:?}"))?,
1050
+
)?;
1051
+
let cid_suffix = key
1052
+
.get(9..)
1053
+
.ok_or_else(|| anyhow::anyhow!("invalid op key {key:?}"))?;
1054
+
1055
+
let mut full_cid_bytes = db_op.cid_prefix.clone();
1056
+
full_cid_bytes.extend_from_slice(cid_suffix);
1057
+
1058
+
let cid = decode_cid(&full_cid_bytes)?;
1059
+
let did = decode_did(&db_op.did);
1060
+
1061
+
Ok(Op {
1062
+
did,
1063
+
cid,
1064
+
created_at,
1065
+
nullified: db_op.nullified,
1066
+
operation: db_op.operation.to_json_value(),
1067
+
})
1068
+
}))
1069
+
}
1070
+
1071
+
pub fn export_ops_week(
1072
+
&self,
1073
+
week: Week,
1074
+
) -> anyhow::Result<impl Iterator<Item = anyhow::Result<Op>> + '_> {
1075
+
let after: Dt = week.into();
1076
+
let before: Dt = week.next().into();
1077
+
1078
+
self.export_ops(after..before)
1079
+
}
1080
+
}
1081
+
1082
+
impl BundleSource for FjallDb {
1083
+
fn reader_for(
1084
+
&self,
1085
+
week: Week,
1086
+
) -> impl Future<Output = anyhow::Result<impl AsyncRead + Send>> + Send {
1087
+
let db = self.clone();
1088
+
1089
+
async move {
1090
+
let (mut tx, rx) = tokio::io::duplex(1024 * 1024 * 64);
1091
+
1092
+
tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
1093
+
let iter = db.export_ops_week(week)?;
1094
+
1095
+
let rt = tokio::runtime::Handle::current();
1096
+
1097
+
for op_res in iter {
1098
+
let op = op_res?;
1099
+
let operation_str = serde_json::to_string(&op.operation)?;
1100
+
let common_op = crate::Op {
1101
+
did: op.did,
1102
+
cid: op.cid,
1103
+
created_at: op.created_at,
1104
+
nullified: op.nullified,
1105
+
operation: serde_json::value::RawValue::from_string(operation_str)?,
1106
+
};
1107
+
1108
+
let mut json_bytes = serde_json::to_vec(&common_op)?;
1109
+
json_bytes.push(b'\n');
1110
+
1111
+
if rt.block_on(tx.write_all(&json_bytes)).is_err() {
1112
+
break;
1113
+
}
1114
+
}
1115
+
1116
+
Ok(())
1117
+
});
1118
+
1119
+
Ok(rx)
1120
+
}
1121
+
}
1122
+
}
1123
+
1124
+
pub async fn backfill_to_fjall(
1125
+
db: FjallDb,
1126
+
reset: bool,
1127
+
mut pages: mpsc::Receiver<ExportPage>,
1128
+
notify_last_at: Option<oneshot::Sender<Option<Dt>>>,
1129
+
) -> anyhow::Result<&'static str> {
1130
+
let t0 = Instant::now();
1131
+
1132
+
if reset {
1133
+
let db = db.clone();
1134
+
tokio::task::spawn_blocking(move || db.clear()).await??;
1135
+
log::warn!("fjall reset: cleared all data");
1136
+
}
1137
+
1138
+
let mut last_at = None;
1139
+
let mut ops_inserted: usize = 0;
1140
+
1141
+
while let Some(page) = pages.recv().await {
1142
+
let should_track = notify_last_at.is_some();
1143
+
if should_track {
1144
+
if let Some(s) = PageBoundaryState::new(&page) {
1145
+
last_at = last_at.filter(|&l| l >= s.last_at).or(Some(s.last_at));
1146
+
}
1147
+
}
1148
+
1149
+
let db = db.clone();
1150
+
let count = tokio::task::spawn_blocking(move || -> anyhow::Result<usize> {
1151
+
let mut batch = db.inner.db.batch();
1152
+
let mut count: usize = 0;
1153
+
for op in &page.ops {
1154
+
count += db.insert_op(&mut batch, op)?;
1155
+
}
1156
+
batch.commit()?;
1157
+
Ok(count)
1158
+
})
1159
+
.await??;
1160
+
ops_inserted += count;
1161
+
}
1162
+
log::debug!("finished receiving bulk pages");
1163
+
1164
+
if let Some(notify) = notify_last_at {
1165
+
log::trace!("notifying last_at: {last_at:?}");
1166
+
if notify.send(last_at).is_err() {
1167
+
log::error!("receiver for last_at dropped, can't notify");
1168
+
};
1169
+
}
1170
+
1171
+
let db = db.clone();
1172
+
tokio::task::spawn_blocking(move || db.persist()).await??;
1173
+
1174
+
log::info!(
1175
+
"backfill_to_fjall: inserted {ops_inserted} ops in {:?}",
1176
+
t0.elapsed()
1177
+
);
1178
+
Ok("backfill_to_fjall")
1179
+
}
1180
+
1181
+
pub async fn pages_to_fjall(
1182
+
db: FjallDb,
1183
+
mut pages: mpsc::Receiver<ExportPage>,
1184
+
) -> anyhow::Result<&'static str> {
1185
+
log::info!("starting pages_to_fjall writer...");
1186
+
1187
+
let t0 = Instant::now();
1188
+
let mut ops_inserted: usize = 0;
1189
+
1190
+
while let Some(page) = pages.recv().await {
1191
+
log::trace!("writing page with {} ops", page.ops.len());
1192
+
let db = db.clone();
1193
+
let count = tokio::task::spawn_blocking(move || -> anyhow::Result<usize> {
1194
+
let mut batch = db.inner.db.batch();
1195
+
let mut count: usize = 0;
1196
+
for op in &page.ops {
1197
+
count += db.insert_op(&mut batch, op)?;
1198
+
}
1199
+
batch.commit()?;
1200
+
Ok(count)
1201
+
})
1202
+
.await??;
1203
+
ops_inserted += count;
1204
+
}
1205
+
1206
+
log::info!(
1207
+
"no more pages. inserted {ops_inserted} ops in {:?}",
1208
+
t0.elapsed()
1209
+
);
1210
+
Ok("pages_to_fjall")
1211
+
}
1212
+
1213
+
#[cfg(test)]
1214
+
mod tests {
1215
+
use super::*;
1216
+
1217
+
#[test]
1218
+
fn signature_roundtrip() {
1219
+
let original = "9NuYV7AqwHVTc0YuWzNV3CJafsSZWH7qCxHRUIP2xWlB-YexXC1OaYAnUayiCXLVzRQ8WBXIqF-SvZdNalwcjA";
1220
+
let sig = Signature::from_base64url(original).unwrap();
1221
+
assert_eq!(sig.0.len(), 64);
1222
+
assert_eq!(sig.to_string(), original);
1223
+
}
1224
+
1225
+
#[test]
1226
+
fn did_key_roundtrip() {
1227
+
let original = "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg";
1228
+
let key = DidKey::from_did_key(original).unwrap();
1229
+
assert_eq!(key.to_string(), original);
1230
+
}
1231
+
1232
+
#[test]
1233
+
fn plc_cid_roundtrip() {
1234
+
let original = "bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm";
1235
+
let cid = PlcCid::from_cid_str(original).unwrap();
1236
+
assert_eq!(cid.to_string(), original);
1237
+
}
1238
+
1239
+
#[test]
1240
+
fn bsky_handle_roundtrip() {
1241
+
let h = Aka::from_str("at://alice.bsky.social");
1242
+
assert_eq!(h, Aka::Bluesky("alice".to_string()));
1243
+
assert_eq!(h.to_string(), "at://alice.bsky.social");
1244
+
}
1245
+
1246
+
#[test]
1247
+
fn atproto_handle_roundtrip() {
1248
+
let h = Aka::from_str("at://alice.example.com");
1249
+
assert_eq!(h, Aka::Atproto("alice.example.com".to_string()));
1250
+
assert_eq!(h.to_string(), "at://alice.example.com");
1251
+
}
1252
+
1253
+
#[test]
1254
+
fn other_handle_roundtrip() {
1255
+
let h = Aka::from_str("https://something.else");
1256
+
assert_eq!(h, Aka::Other("https://something.else".to_string()));
1257
+
assert_eq!(h.to_string(), "https://something.else");
1258
+
}
1259
+
1260
+
#[test]
1261
+
fn verification_method_key_roundtrip() {
1262
+
let k1 = VerificationMethodKey::from_str("atproto");
1263
+
assert_eq!(k1, VerificationMethodKey::Atproto);
1264
+
assert_eq!(k1.to_string(), "atproto");
1265
+
1266
+
let k2 = VerificationMethodKey::from_str("other_key");
1267
+
assert_eq!(k2, VerificationMethodKey::Other("other_key".to_string()));
1268
+
assert_eq!(k2.to_string(), "other_key");
1269
+
}
1270
+
1271
+
#[test]
1272
+
fn service_key_roundtrip() {
1273
+
let k1 = ServiceKey::from_str("atproto_pds");
1274
+
assert_eq!(k1, ServiceKey::AtprotoPds);
1275
+
assert_eq!(k1.to_string(), "atproto_pds");
1276
+
1277
+
let k2 = ServiceKey::from_str("other_svc");
1278
+
assert_eq!(k2, ServiceKey::Other("other_svc".to_string()));
1279
+
assert_eq!(k2.to_string(), "other_svc");
1280
+
}
1281
+
1282
+
#[test]
1283
+
fn service_type_roundtrip() {
1284
+
let t1 = ServiceType::from_str("AtprotoPersonalDataServer");
1285
+
assert_eq!(t1, ServiceType::AtprotoPersonalDataServer);
1286
+
assert_eq!(t1.as_str(), "AtprotoPersonalDataServer");
1287
+
1288
+
let t2 = ServiceType::from_str("OtherType");
1289
+
assert_eq!(t2, ServiceType::Other("OtherType".to_string()));
1290
+
assert_eq!(t2.as_str(), "OtherType");
1291
+
}
1292
+
1293
+
#[test]
1294
+
fn service_endpoint_roundtrip() {
1295
+
let e1 = ServiceEndpoint::from_str("https://example.host.bsky.network");
1296
+
assert_eq!(e1, ServiceEndpoint::BlueskyPds("example".to_string()));
1297
+
assert_eq!(e1.as_string(), "https://example.host.bsky.network");
1298
+
1299
+
let e2 = ServiceEndpoint::from_str("https://other.endpoint.com");
1300
+
assert_eq!(
1301
+
e2,
1302
+
ServiceEndpoint::Other("https://other.endpoint.com".to_string())
1303
+
);
1304
+
assert_eq!(e2.as_string(), "https://other.endpoint.com");
1305
+
}
1306
+
1307
+
#[test]
1308
+
fn op_type_roundtrip() {
1309
+
assert_eq!(OpType::from_str("plc_operation").as_str(), "plc_operation");
1310
+
assert_eq!(OpType::from_str("create").as_str(), "create");
1311
+
assert_eq!(OpType::from_str("plc_tombstone").as_str(), "plc_tombstone");
1312
+
assert_eq!(OpType::from_str("weird_thing").as_str(), "weird_thing");
1313
+
}
1314
+
1315
+
#[test]
1316
+
fn stored_op_fixture_roundtrip() {
1317
+
let fixtures = [
1318
+
"tests/fixtures/log_bskyapp.json",
1319
+
"tests/fixtures/log_legacy_dholms.json",
1320
+
"tests/fixtures/log_nullification.json",
1321
+
"tests/fixtures/log_tombstone.json",
1322
+
];
1323
+
1324
+
let mut total_json_size = 0;
1325
+
let mut total_packed_size = 0;
1326
+
1327
+
for path in fixtures {
1328
+
let data = std::fs::read_to_string(path).unwrap();
1329
+
let entries: Vec<serde_json::Value> = serde_json::from_str(&data).unwrap();
1330
+
1331
+
for entry in &entries {
1332
+
let op = &entry["operation"];
1333
+
let (stored, errors) = StoredOp::from_json_value(op.clone());
1334
+
if !errors.is_empty() {
1335
+
let mut msg = format!("failed to parse op in {path}:\n");
1336
+
for e in errors {
1337
+
msg.push_str(&format!(" - {e:?}\n"));
1338
+
}
1339
+
msg.push_str(&format!("op: {op}\n"));
1340
+
panic!("{msg}");
1341
+
}
1342
+
1343
+
// msgpack verification
1344
+
let packed = rmp_serde::to_vec(&stored).unwrap();
1345
+
let unpacked: StoredOp = rmp_serde::from_slice(&packed).unwrap();
1346
+
1347
+
let reconstructed = unpacked.to_json_value();
1348
+
assert_eq!(*op, reconstructed, "roundtrip mismatch in {path}");
1349
+
1350
+
total_json_size += serde_json::to_vec(op).unwrap().len();
1351
+
total_packed_size += packed.len();
1352
+
}
1353
+
}
1354
+
1355
+
println!(
1356
+
"json size: {} bytes, msgpack size: {} bytes, saved: {} bytes",
1357
+
total_json_size,
1358
+
total_packed_size,
1359
+
total_json_size as isize - total_packed_size as isize
1360
+
);
1361
+
}
1362
+
}
+42
-20
src/poll.rs
+42
-20
src/poll.rs
···
139
///
140
/// Extracts the final op so it can be used to fetch the following page
141
pub async fn get_page(url: Url) -> Result<(ExportPage, Option<LastOp>), GetPageError> {
142
log::trace!("Getting page: {url}");
143
144
-
let ops: Vec<Op> = CLIENT
145
-
.get(url)
146
-
.send()
147
-
.await?
148
-
.error_for_status()?
149
-
.text()
150
-
.await?
151
-
.trim()
152
-
.split('\n')
153
-
.filter_map(|s| {
154
-
serde_json::from_str::<Op>(s)
155
-
.inspect_err(|e| {
156
-
if !s.is_empty() {
157
-
log::warn!("failed to parse op: {e} ({s})")
158
-
}
159
-
})
160
-
.ok()
161
-
})
162
-
.collect();
163
164
let last_op = ops.last().map(Into::into);
165
···
214
.append_pair("after", &pl.created_at.to_rfc3339());
215
};
216
217
-
let (mut page, next_last) = get_page(url).await?;
218
if let Some(ref mut state) = boundary_state {
219
state.apply_to_next(&mut page);
220
} else {
···
139
///
140
/// Extracts the final op so it can be used to fetch the following page
141
pub async fn get_page(url: Url) -> Result<(ExportPage, Option<LastOp>), GetPageError> {
142
+
use futures::TryStreamExt;
143
+
use tokio::io::{AsyncBufReadExt, BufReader};
144
+
use tokio_util::compat::FuturesAsyncReadCompatExt;
145
+
146
log::trace!("Getting page: {url}");
147
148
+
let res = CLIENT.get(url).send().await?.error_for_status()?;
149
+
let stream = Box::pin(
150
+
res.bytes_stream()
151
+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
152
+
.into_async_read()
153
+
.compat(),
154
+
);
155
+
156
+
let mut lines = BufReader::new(stream).lines();
157
+
let mut ops = Vec::new();
158
+
159
+
loop {
160
+
match lines.next_line().await {
161
+
Ok(Some(line)) => {
162
+
let line = line.trim();
163
+
if line.is_empty() {
164
+
continue;
165
+
}
166
+
match serde_json::from_str::<Op>(line) {
167
+
Ok(op) => ops.push(op),
168
+
Err(e) => log::warn!("failed to parse op: {e} ({line})"),
169
+
}
170
+
}
171
+
Ok(None) => break,
172
+
Err(e) => {
173
+
log::warn!("transport error mid-page: {}; returning partial page", e);
174
+
break;
175
+
}
176
+
}
177
+
}
178
179
let last_op = ops.last().map(Into::into);
180
···
229
.append_pair("after", &pl.created_at.to_rfc3339());
230
};
231
232
+
let (mut page, next_last) = match get_page(url).await {
233
+
Ok(res) => res,
234
+
Err(e) => {
235
+
log::warn!("error polling upstream: {e}");
236
+
continue;
237
+
}
238
+
};
239
+
240
if let Some(ref mut state) = boundary_state {
241
state.apply_to_next(&mut page);
242
} else {
+61
-27
src/weekly.rs
+61
-27
src/weekly.rs
···
101
let file = File::open(path)
102
.await
103
.inspect_err(|e| log::error!("failed to open file: {e}"))?;
104
-
Ok(file)
105
}
106
}
107
···
112
use futures::TryStreamExt;
113
let HttpSource(base) = self;
114
let url = base.join(&format!("{}.jsonl.gz", week.0))?;
115
-
Ok(CLIENT
116
.get(url)
117
.send()
118
.await?
···
120
.bytes_stream()
121
.map_err(futures::io::Error::other)
122
.into_async_read()
123
-
.compat())
124
}
125
}
126
···
197
dest: mpsc::Sender<ExportPage>,
198
) -> anyhow::Result<()> {
199
use futures::TryStreamExt;
200
-
let reader = source
201
-
.reader_for(week)
202
-
.await
203
-
.inspect_err(|e| log::error!("week_to_pages reader failed: {e}"))?;
204
-
let decoder = GzipDecoder::new(BufReader::new(reader));
205
-
let mut chunks = pin!(LinesStream::new(BufReader::new(decoder).lines()).try_chunks(10000));
206
207
-
while let Some(chunk) = chunks
208
-
.try_next()
209
-
.await
210
-
.inspect_err(|e| log::error!("failed to get next chunk: {e}"))?
211
-
{
212
-
let ops: Vec<Op> = chunk
213
-
.into_iter()
214
-
.filter_map(|s| {
215
-
serde_json::from_str::<Op>(&s)
216
-
.inspect_err(|e| log::warn!("failed to parse op: {e} ({s})"))
217
-
.ok()
218
-
})
219
-
.collect();
220
-
let page = ExportPage { ops };
221
-
dest.send(page)
222
-
.await
223
-
.inspect_err(|e| log::error!("failed to send page: {e}"))?;
224
}
225
-
Ok(())
226
}
···
101
let file = File::open(path)
102
.await
103
.inspect_err(|e| log::error!("failed to open file: {e}"))?;
104
+
let decoder = GzipDecoder::new(BufReader::new(file));
105
+
Ok(decoder)
106
}
107
}
108
···
113
use futures::TryStreamExt;
114
let HttpSource(base) = self;
115
let url = base.join(&format!("{}.jsonl.gz", week.0))?;
116
+
let stream = CLIENT
117
.get(url)
118
.send()
119
.await?
···
121
.bytes_stream()
122
.map_err(futures::io::Error::other)
123
.into_async_read()
124
+
.compat();
125
+
let decoder = GzipDecoder::new(BufReader::new(stream));
126
+
Ok(decoder)
127
}
128
}
129
···
200
dest: mpsc::Sender<ExportPage>,
201
) -> anyhow::Result<()> {
202
use futures::TryStreamExt;
203
+
let mut retry_backoff = std::time::Duration::from_secs(2);
204
205
+
loop {
206
+
let reader = match source.reader_for(week).await {
207
+
Ok(r) => r,
208
+
Err(e) => {
209
+
log::warn!(
210
+
"week_to_pages reader_for failed {e}, retrying in {}s",
211
+
retry_backoff.as_secs()
212
+
);
213
+
tokio::time::sleep(retry_backoff).await;
214
+
retry_backoff = (retry_backoff * 2).min(std::time::Duration::from_secs(300));
215
+
continue;
216
+
}
217
+
};
218
+
219
+
let mut chunks = pin!(LinesStream::new(BufReader::new(reader).lines()).try_chunks(10000));
220
+
let mut success = true;
221
+
222
+
while let Some(chunk) = match chunks.as_mut().try_next().await {
223
+
Ok(Some(c)) => Some(c),
224
+
Ok(None) => None,
225
+
Err(e) => {
226
+
log::warn!(
227
+
"failed to get next chunk: {e}, retrying week in {}s",
228
+
retry_backoff.as_secs()
229
+
);
230
+
tokio::time::sleep(retry_backoff).await;
231
+
retry_backoff = (retry_backoff * 2).min(std::time::Duration::from_secs(300));
232
+
success = false;
233
+
None
234
+
}
235
+
} {
236
+
let ops: Vec<Op> = chunk
237
+
.into_iter()
238
+
.filter_map(|s| {
239
+
serde_json::from_str::<Op>(&s)
240
+
.inspect_err(|e| log::warn!("failed to parse op: {e} ({s})"))
241
+
.ok()
242
+
})
243
+
.collect();
244
+
245
+
if ops.is_empty() {
246
+
continue;
247
+
}
248
+
249
+
let page = ExportPage { ops };
250
+
if let Err(e) = dest.send(page).await {
251
+
log::error!("failed to send page (receiver closed): {e}");
252
+
return Err(e.into());
253
+
}
254
+
}
255
+
256
+
if success {
257
+
return Ok(());
258
+
}
259
}
260
}
+56
tests/fixtures/log_bskyapp.json
+56
tests/fixtures/log_bskyapp.json
···
···
1
+
[
2
+
{
3
+
"did": "did:plc:z72i7hdynmk6r22z27h6tvur",
4
+
"operation": {
5
+
"sig": "9NuYV7AqwHVTc0YuWzNV3CJafsSZWH7qCxHRUIP2xWlB-YexXC1OaYAnUayiCXLVzRQ8WBXIqF-SvZdNalwcjA",
6
+
"prev": null,
7
+
"type": "plc_operation",
8
+
"services": {
9
+
"atproto_pds": {
10
+
"type": "AtprotoPersonalDataServer",
11
+
"endpoint": "https://bsky.social"
12
+
}
13
+
},
14
+
"alsoKnownAs": [
15
+
"at://bluesky-team.bsky.social"
16
+
],
17
+
"rotationKeys": [
18
+
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
19
+
"did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"
20
+
],
21
+
"verificationMethods": {
22
+
"atproto": "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
23
+
}
24
+
},
25
+
"cid": "bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm",
26
+
"nullified": false,
27
+
"createdAt": "2023-04-12T04:53:57.057Z"
28
+
},
29
+
{
30
+
"did": "did:plc:z72i7hdynmk6r22z27h6tvur",
31
+
"operation": {
32
+
"sig": "1mEWzRtFOgeRXH-YCSPTxb990JOXxa__n8Qw6BOKl7Ndm6OFFmwYKiiMqMCpAbxpnGjF5abfIsKc7u3a77Cbnw",
33
+
"prev": "bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm",
34
+
"type": "plc_operation",
35
+
"services": {
36
+
"atproto_pds": {
37
+
"type": "AtprotoPersonalDataServer",
38
+
"endpoint": "https://bsky.social"
39
+
}
40
+
},
41
+
"alsoKnownAs": [
42
+
"at://bsky.app"
43
+
],
44
+
"rotationKeys": [
45
+
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
46
+
"did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"
47
+
],
48
+
"verificationMethods": {
49
+
"atproto": "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
50
+
}
51
+
},
52
+
"cid": "bafyreihmuvr3frdvd6vmdhucih277prdcfcezf67lasg5oekxoimnunjoq",
53
+
"nullified": false,
54
+
"createdAt": "2023-04-12T17:26:46.468Z"
55
+
}
56
+
]
+125
tests/fixtures/log_legacy_dholms.json
+125
tests/fixtures/log_legacy_dholms.json
···
···
1
+
[
2
+
{
3
+
"did": "did:plc:yk4dd2qkboz2yv6tpubpc6co",
4
+
"operation": {
5
+
"sig": "7QTzqO1BcL3eDzP4P_YBxMmv5U4brHzAItkM9w5o8gZA7ElZkrVYEwsfQCfk5EoWLk58Z1y6fyNP9x1pthJnlw",
6
+
"prev": null,
7
+
"type": "create",
8
+
"handle": "dan.bsky.social",
9
+
"service": "https://bsky.social",
10
+
"signingKey": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ",
11
+
"recoveryKey": "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg"
12
+
},
13
+
"cid": "bafyreigcxay6ucqlwowfpu35alyxqtv3c4vsj7gmdtmnidsnqs6nblyarq",
14
+
"nullified": false,
15
+
"createdAt": "2022-11-17T01:07:13.996Z"
16
+
},
17
+
{
18
+
"did": "did:plc:yk4dd2qkboz2yv6tpubpc6co",
19
+
"operation": {
20
+
"sig": "n-VWsPZY4xkFN8wlg-kJBU_yzWTNd2oBnbjkjxXu3HdjbBLaEB7K39JHIPn_DZVALKRjts6bUicjSEecZy8eIw",
21
+
"prev": "bafyreigcxay6ucqlwowfpu35alyxqtv3c4vsj7gmdtmnidsnqs6nblyarq",
22
+
"type": "plc_operation",
23
+
"services": {
24
+
"atproto_pds": {
25
+
"type": "AtprotoPersonalDataServer",
26
+
"endpoint": "https://bsky.social"
27
+
}
28
+
},
29
+
"alsoKnownAs": [
30
+
"at://dholms.xyz"
31
+
],
32
+
"rotationKeys": [
33
+
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
34
+
"did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ"
35
+
],
36
+
"verificationMethods": {
37
+
"atproto": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ"
38
+
}
39
+
},
40
+
"cid": "bafyreiho5sanautvnw3det66jcwic4vkeabc35y7iou3ygwj2l3xqcxdau",
41
+
"nullified": false,
42
+
"createdAt": "2023-03-06T18:47:09.501Z"
43
+
},
44
+
{
45
+
"did": "did:plc:yk4dd2qkboz2yv6tpubpc6co",
46
+
"operation": {
47
+
"sig": "HWgrfQXxUN3mhR5TR-nrwGJwVr9RDbyDn6eCmqBg32x2zIjhe98YxOtFOLI9jQkBlTTzqzUOwJh1KZd4O2pDOw",
48
+
"prev": "bafyreiho5sanautvnw3det66jcwic4vkeabc35y7iou3ygwj2l3xqcxdau",
49
+
"type": "plc_operation",
50
+
"services": {
51
+
"atproto_pds": {
52
+
"type": "AtprotoPersonalDataServer",
53
+
"endpoint": "https://bsky.social"
54
+
}
55
+
},
56
+
"alsoKnownAs": [
57
+
"at://dholms.bsky.social"
58
+
],
59
+
"rotationKeys": [
60
+
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
61
+
"did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ"
62
+
],
63
+
"verificationMethods": {
64
+
"atproto": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ"
65
+
}
66
+
},
67
+
"cid": "bafyreic3am2nmgykxtwsxwigzn6faibxv5ef5kalcv7li3eatcqldcqrku",
68
+
"nullified": false,
69
+
"createdAt": "2023-03-06T19:50:49.987Z"
70
+
},
71
+
{
72
+
"did": "did:plc:yk4dd2qkboz2yv6tpubpc6co",
73
+
"operation": {
74
+
"sig": "9Fy2iHCSK5mtgLNCkS9CyI0r7lu6H1SVgusaD1jQdsMUySUU6apde0z7SobpYZKp4sThk4hxOWtO-bXhu1cNjg",
75
+
"prev": "bafyreic3am2nmgykxtwsxwigzn6faibxv5ef5kalcv7li3eatcqldcqrku",
76
+
"type": "plc_operation",
77
+
"services": {
78
+
"atproto_pds": {
79
+
"type": "AtprotoPersonalDataServer",
80
+
"endpoint": "https://bsky.social"
81
+
}
82
+
},
83
+
"alsoKnownAs": [
84
+
"at://dholms.xyz"
85
+
],
86
+
"rotationKeys": [
87
+
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
88
+
"did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ"
89
+
],
90
+
"verificationMethods": {
91
+
"atproto": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ"
92
+
}
93
+
},
94
+
"cid": "bafyreicwybxr6h6vkxpoarismso3liozdzswshmzcvl4tyckdazn5lxjte",
95
+
"nullified": false,
96
+
"createdAt": "2023-03-06T19:51:09.950Z"
97
+
},
98
+
{
99
+
"did": "did:plc:yk4dd2qkboz2yv6tpubpc6co",
100
+
"operation": {
101
+
"sig": "lBXd8rHZ84hCuQysGdi_5A9C8yPHTHasPibO4DZiuZVrehs2hiBcjAL0srLSTsF1kvsHTw1ddai-QwH0Wd_drQ",
102
+
"prev": "bafyreicwybxr6h6vkxpoarismso3liozdzswshmzcvl4tyckdazn5lxjte",
103
+
"type": "plc_operation",
104
+
"services": {
105
+
"atproto_pds": {
106
+
"type": "AtprotoPersonalDataServer",
107
+
"endpoint": "https://bsky.social"
108
+
}
109
+
},
110
+
"alsoKnownAs": [
111
+
"at://dholms.xyz"
112
+
],
113
+
"rotationKeys": [
114
+
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
115
+
"did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"
116
+
],
117
+
"verificationMethods": {
118
+
"atproto": "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
119
+
}
120
+
},
121
+
"cid": "bafyreidfrpuegbqd5r56shka4duythb7phb6d7i3bck2dkeb5fjppwd7gi",
122
+
"nullified": false,
123
+
"createdAt": "2023-03-09T23:18:31.709Z"
124
+
}
125
+
]
+59
tests/fixtures/log_nullification.json
+59
tests/fixtures/log_nullification.json
···
···
1
+
[
2
+
{
3
+
"did": "did:plc:2s2mvm52ttz6r4hocmrq7x27",
4
+
"operation": {
5
+
"type": "plc_operation",
6
+
"rotationKeys": [
7
+
"did:key:zQ3shwPdax6jKMbhtzbueGwSjc7RnjsmPcNB1vQUpbKUCN1t1",
8
+
"did:key:zQ3shmMcsFLvSZQy2pSZFFKZNmQSNmyuSepPXMHpgTYrontu2"
9
+
],
10
+
"verificationMethods": {},
11
+
"alsoKnownAs": [
12
+
"at://foo"
13
+
],
14
+
"services": {},
15
+
"prev": null,
16
+
"sig": "1pTjQD5-LA4gN6tz1t7-28Tzct9NI-oYMN8QVUQkB7493j-vKeZnZO0YqNNBCXsORatmfKUXwteC9zW82mi_yw"
17
+
},
18
+
"cid": "bafyreiguwtflhou46pupb3qtemh56x6o3fn3y7ym2q3jgoms35tdu2d5ga",
19
+
"nullified": false,
20
+
"createdAt": "1970-01-01T00:00:00.000Z"
21
+
},
22
+
{
23
+
"did": "did:plc:2s2mvm52ttz6r4hocmrq7x27",
24
+
"operation": {
25
+
"prev": "bafyreiguwtflhou46pupb3qtemh56x6o3fn3y7ym2q3jgoms35tdu2d5ga",
26
+
"type": "plc_operation",
27
+
"services": {},
28
+
"alsoKnownAs": [
29
+
"at://foo"
30
+
],
31
+
"rotationKeys": [],
32
+
"verificationMethods": {},
33
+
"sig": "XfbRKnfzmJyRXUrajAISgdiJWNdNFMFgtDnlKvj-bIZhpfqGd39xiylJVXXu5Usw2cc2Js4EeSPh2xfhUQNxSA"
34
+
},
35
+
"cid": "bafyreicbxgoelatj24iyqnevm7v4f22utxb75p756ztyu5v7cjxlwti3oa",
36
+
"nullified": true,
37
+
"createdAt": "1970-01-01T00:00:01.000Z"
38
+
},
39
+
{
40
+
"did": "did:plc:2s2mvm52ttz6r4hocmrq7x27",
41
+
"operation": {
42
+
"prev": "bafyreiguwtflhou46pupb3qtemh56x6o3fn3y7ym2q3jgoms35tdu2d5ga",
43
+
"type": "plc_operation",
44
+
"services": {},
45
+
"alsoKnownAs": [
46
+
"at://foo"
47
+
],
48
+
"rotationKeys": [
49
+
"did:key:zQ3shwPdax6jKMbhtzbueGwSjc7RnjsmPcNB1vQUpbKUCN1t1",
50
+
"did:key:zQ3shmMcsFLvSZQy2pSZFFKZNmQSNmyuSepPXMHpgTYrontu2"
51
+
],
52
+
"verificationMethods": {},
53
+
"sig": "zCcbDgFbv5b0aGggLepLcARzNzH0sG6uuvq2GacyPsAsjDsLMZn6KmOxa50iWZEMRknQHsMYyMTS99eeTPu0fQ"
54
+
},
55
+
"cid": "bafyreidef3g2vvlshwzf7vxjyxg3lraahujvhtkpo7y3g4br52ft3lzwem",
56
+
"nullified": false,
57
+
"createdAt": "1970-01-01T00:00:02.000Z"
58
+
}
59
+
]
+33
tests/fixtures/log_tombstone.json
+33
tests/fixtures/log_tombstone.json
···
···
1
+
[
2
+
{
3
+
"did": "did:plc:6adr3q2labdllanslzhqkqd3",
4
+
"operation": {
5
+
"sig": "ZznbxHinpBI3NgEYWzXUXLA65s2U1ezJooreZlscHYQRfd4EMlijqhpikGMabs-81Tsy4Mt9Iscpmk7Uz13aAg",
6
+
"prev": null,
7
+
"type": "plc_operation",
8
+
"services": {},
9
+
"alsoKnownAs": [
10
+
"at://op0"
11
+
],
12
+
"rotationKeys": [
13
+
"did:key:zQ3shmWf4f6ZwzNyjUDYw4oFQjgHWZoYZDjJYtz75YfYYrphB",
14
+
"did:key:zQ3shr2PwdoF6kbuYYymyZq6YbGWShiTXAkdMioCPdSdPL9NV"
15
+
],
16
+
"verificationMethods": {}
17
+
},
18
+
"cid": "bafyreihqa4o4gsyai22ydms6j4cua6yr6tuc7trq2temvnycadk2daniru",
19
+
"nullified": false,
20
+
"createdAt": "2025-07-25T15:50:58.440Z"
21
+
},
22
+
{
23
+
"did": "did:plc:6adr3q2labdllanslzhqkqd3",
24
+
"operation": {
25
+
"sig": "iKFOLyEujXdHWujAAnwFhKfb9kkIcpMDGOV15eNWqlxLM2GtdQD3lVWCshTKWi0dF7InXGseVFDexS0kg0KmEQ",
26
+
"prev": "bafyreihqa4o4gsyai22ydms6j4cua6yr6tuc7trq2temvnycadk2daniru",
27
+
"type": "plc_tombstone"
28
+
},
29
+
"cid": "bafyreifafe44xylxagcom47xb3lrya5pysu3b7zfjmmmitiuimhmgmerze",
30
+
"nullified": false,
31
+
"createdAt": "2025-07-25T15:50:58.837Z"
32
+
}
33
+
]
+112
tests/fjall_mirror_test.rs
+112
tests/fjall_mirror_test.rs
···
···
1
+
use allegedly::{
2
+
ExperimentalConf, FjallDb, ListenConf, backfill_to_fjall, poll_upstream, serve_fjall,
3
+
};
4
+
use reqwest::Url;
5
+
use std::time::Duration;
6
+
use tokio::sync::mpsc;
7
+
8
+
#[tokio::test]
9
+
async fn test_fjall_mirror_mode() -> anyhow::Result<()> {
10
+
let _ = tracing_subscriber::fmt::try_init();
11
+
12
+
// setup
13
+
let temp_dir = tempfile::tempdir()?;
14
+
let db_path = temp_dir.path().join("fjall.db");
15
+
let db = FjallDb::open(&db_path)?;
16
+
17
+
// backfill (limited to 1 page)
18
+
let (backfill_tx, backfill_rx) = mpsc::channel(1);
19
+
let (upstream_tx, mut upstream_rx) = mpsc::channel(1);
20
+
21
+
// spawn upstream poller
22
+
let upstream_url: Url = "https://plc.directory/export".parse()?;
23
+
tokio::spawn(async move {
24
+
// poll fresh data so our data matches the upstream
25
+
let start_at = chrono::Utc::now() - chrono::Duration::try_minutes(5).unwrap();
26
+
let _ = poll_upstream(
27
+
Some(start_at),
28
+
upstream_url,
29
+
Duration::from_millis(100),
30
+
upstream_tx,
31
+
)
32
+
.await;
33
+
});
34
+
35
+
// bridge: take 1 page from upstream and forward to backfill
36
+
println!("waiting for page from upstream...");
37
+
let page = upstream_rx
38
+
.recv()
39
+
.await
40
+
.expect("to receive page from upstream");
41
+
println!("received page with {} ops", page.ops.len());
42
+
let sample_did = page.ops.last().unwrap().did.clone();
43
+
44
+
backfill_tx.send(page).await?;
45
+
drop(backfill_tx); // close backfill input
46
+
47
+
backfill_to_fjall(db.clone(), false, backfill_rx, None).await?;
48
+
49
+
// get free port
50
+
let listener = std::net::TcpListener::bind("127.0.0.1:17548")?;
51
+
let port = listener.local_addr()?.port();
52
+
drop(listener);
53
+
54
+
let listen_conf = ListenConf::Bind(([127, 0, 0, 1], port).into());
55
+
let exp_conf = ExperimentalConf {
56
+
acme_domain: None,
57
+
write_upstream: false,
58
+
};
59
+
60
+
let db_for_server = db.clone();
61
+
let server_handle = tokio::spawn(async move {
62
+
let upstream: Url = "https://plc.directory".parse().unwrap();
63
+
serve_fjall(upstream, listen_conf, exp_conf, db_for_server).await
64
+
});
65
+
66
+
// wait for server to be ready (retry loop)
67
+
let client = reqwest::Client::new();
68
+
let base_url = format!("http://127.0.0.1:{}", port);
69
+
let mut ready = false;
70
+
for _ in 0..50 {
71
+
if client
72
+
.get(format!("{}/_health", base_url))
73
+
.send()
74
+
.await
75
+
.is_ok()
76
+
{
77
+
ready = true;
78
+
break;
79
+
}
80
+
tokio::time::sleep(Duration::from_millis(100)).await;
81
+
}
82
+
assert!(ready, "server failed to start");
83
+
84
+
// verify health
85
+
let resp = client.get(format!("{}/_health", base_url)).send().await?;
86
+
assert!(resp.status().is_success());
87
+
let json: serde_json::Value = resp.json().await?;
88
+
assert_eq!(json["server"], "allegedly (mirror/fjall)");
89
+
90
+
// verify did resolution against upstream
91
+
let upstream_resp = client
92
+
.get(format!("https://plc.directory/{}", sample_did))
93
+
.send()
94
+
.await?;
95
+
assert!(upstream_resp.status().is_success());
96
+
let upstream_doc: serde_json::Value = upstream_resp.json().await?;
97
+
98
+
let resp = client
99
+
.get(format!("{}/{}", base_url, sample_did))
100
+
.send()
101
+
.await?;
102
+
assert!(resp.status().is_success());
103
+
let doc: serde_json::Value = resp.json().await?;
104
+
assert_eq!(
105
+
doc, upstream_doc,
106
+
"local doc != upstream doc.\nlocal: {:#?}\nupstream: {:#?}",
107
+
doc, upstream_doc
108
+
);
109
+
110
+
server_handle.abort();
111
+
Ok(())
112
+
}
History
1 round
0 comments
ptr.pet
submitted
#0
18 commits
expand
collapse
fjall mirror mode
don't exit the program on reqwest / decode errors, try again instead
fix inclusivity in fjall export ops
add fjall to --no-bulk
increase memtable size as test
refactor apply_op_log to take IntoIterator
fjall: use msgpack for storing DbOps, and store the decoded did bytes
fjall: store serde_json::Value in db instead of storing RawValue
log latest op in mirror mode
fjall: add option to major compact on startup
fjall: parse operation to store types more efficiently
fjall: dont use padded base64url to parse the signatures
fjall: dont propagate parsing error unless its fatal
tune fjall db config, add option to backfill from a local fjall db
fjall: store more types as bsky-optimized variants if possible
fjall: only use 6 bytes of CID in keys
fjall: add missing errors for when op parsing, fallback to inserting into unknown in places where we werent
update from-fjall backfill to use the weekly code by implementing BundleSource
no conflicts, ready to merge