alpha
Login
or
Join now
microcosm.blue
/
microcosm-rs
Star
0
Fork
3
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm
Star
0
Fork
3
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
basic token verification works
author
phil
date
9 months ago
(Sep 3, 2025, 3:00 PM -0400)
commit
11279d97
11279d97ac2f5945a282c6d2bbff6343e76875d5
parent
cf9f6ca8
cf9f6ca8ac8a32140fef58570cc8d1a66b425b86
+561
-33
8 changed files
Expand all
Collapse all
Unified
Split
Cargo.lock
Cargo.toml
pocket
Cargo.toml
src
lib.rs
main.rs
server.rs
token.rs
quasar
src
storage.rs
+243
-33
Cargo.lock
Reviewed
···
192
192
"nom",
193
193
"num-traits",
194
194
"rusticata-macros",
195
195
-
"thiserror 2.0.12",
195
195
+
"thiserror 2.0.16",
196
196
"time",
197
197
]
198
198
···
371
371
]
372
372
373
373
[[package]]
374
374
+
name = "atrium-crypto"
375
375
+
version = "0.1.2"
376
376
+
source = "registry+https://github.com/rust-lang/crates.io-index"
377
377
+
checksum = "73a3da430c71dd9006d61072c20771f264e5c498420a49c32305ceab8bd71955"
378
378
+
dependencies = [
379
379
+
"ecdsa",
380
380
+
"k256",
381
381
+
"multibase",
382
382
+
"p256",
383
383
+
"thiserror 1.0.69",
384
384
+
]
385
385
+
386
386
+
[[package]]
374
387
name = "atrium-identity"
375
388
version = "0.1.5"
376
389
source = "registry+https://github.com/rust-lang/crates.io-index"
···
628
641
"axum",
629
642
"handlebars",
630
643
"serde",
631
631
-
"thiserror 2.0.12",
644
644
+
"thiserror 2.0.16",
632
645
]
633
646
634
647
[[package]]
···
774
787
]
775
788
776
789
[[package]]
790
790
+
name = "bitcoin-io"
791
791
+
version = "0.1.3"
792
792
+
source = "registry+https://github.com/rust-lang/crates.io-index"
793
793
+
checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
794
794
+
795
795
+
[[package]]
796
796
+
name = "bitcoin_hashes"
797
797
+
version = "0.14.0"
798
798
+
source = "registry+https://github.com/rust-lang/crates.io-index"
799
799
+
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
800
800
+
dependencies = [
801
801
+
"bitcoin-io",
802
802
+
"hex-conservative",
803
803
+
]
804
804
+
805
805
+
[[package]]
777
806
name = "bitflags"
778
807
version = "2.9.0"
779
808
source = "registry+https://github.com/rust-lang/crates.io-index"
···
907
936
]
908
937
909
938
[[package]]
939
939
+
name = "ciborium"
940
940
+
version = "0.2.2"
941
941
+
source = "registry+https://github.com/rust-lang/crates.io-index"
942
942
+
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
943
943
+
dependencies = [
944
944
+
"ciborium-io",
945
945
+
"ciborium-ll",
946
946
+
"serde",
947
947
+
]
948
948
+
949
949
+
[[package]]
950
950
+
name = "ciborium-io"
951
951
+
version = "0.2.2"
952
952
+
source = "registry+https://github.com/rust-lang/crates.io-index"
953
953
+
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
954
954
+
955
955
+
[[package]]
956
956
+
name = "ciborium-ll"
957
957
+
version = "0.2.2"
958
958
+
source = "registry+https://github.com/rust-lang/crates.io-index"
959
959
+
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
960
960
+
dependencies = [
961
961
+
"ciborium-io",
962
962
+
"half",
963
963
+
]
964
964
+
965
965
+
[[package]]
910
966
name = "cid"
911
967
version = "0.11.1"
912
968
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1172
1228
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
1173
1229
1174
1230
[[package]]
1231
1231
+
name = "crunchy"
1232
1232
+
version = "0.2.4"
1233
1233
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1234
1234
+
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
1235
1235
+
1236
1236
+
[[package]]
1175
1237
name = "crypto-bigint"
1176
1238
version = "0.5.5"
1177
1239
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1514
1576
"slog-bunyan",
1515
1577
"slog-json",
1516
1578
"slog-term",
1517
1517
-
"thiserror 2.0.12",
1579
1579
+
"thiserror 2.0.16",
1518
1580
"tokio",
1519
1581
"tokio-rustls 0.25.0",
1520
1582
"toml",
···
1684
1746
]
1685
1747
1686
1748
[[package]]
1749
1749
+
name = "fallible-iterator"
1750
1750
+
version = "0.3.0"
1751
1751
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1752
1752
+
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
1753
1753
+
1754
1754
+
[[package]]
1755
1755
+
name = "fallible-streaming-iterator"
1756
1756
+
version = "0.1.9"
1757
1757
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1758
1758
+
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
1759
1759
+
1760
1760
+
[[package]]
1687
1761
name = "fastrand"
1688
1762
version = "2.3.0"
1689
1763
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1814
1888
"mixtrics",
1815
1889
"pin-project",
1816
1890
"serde",
1817
1817
-
"thiserror 2.0.12",
1891
1891
+
"thiserror 2.0.16",
1818
1892
"tokio",
1819
1893
"tracing",
1820
1894
]
···
1834
1908
"parking_lot",
1835
1909
"pin-project",
1836
1910
"serde",
1837
1837
-
"thiserror 2.0.12",
1911
1911
+
"thiserror 2.0.16",
1838
1912
"tokio",
1839
1913
"twox-hash",
1840
1914
]
···
1867
1941
"parking_lot",
1868
1942
"pin-project",
1869
1943
"serde",
1870
1870
-
"thiserror 2.0.12",
1944
1944
+
"thiserror 2.0.16",
1871
1945
"tokio",
1872
1946
"tracing",
1873
1947
]
···
1899
1973
"pin-project",
1900
1974
"rand 0.9.1",
1901
1975
"serde",
1902
1902
-
"thiserror 2.0.12",
1976
1976
+
"thiserror 2.0.16",
1903
1977
"tokio",
1904
1978
"tracing",
1905
1979
"twox-hash",
···
2121
2195
]
2122
2196
2123
2197
[[package]]
2198
2198
+
name = "half"
2199
2199
+
version = "2.6.0"
2200
2200
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2201
2201
+
checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9"
2202
2202
+
dependencies = [
2203
2203
+
"cfg-if",
2204
2204
+
"crunchy",
2205
2205
+
]
2206
2206
+
2207
2207
+
[[package]]
2124
2208
name = "handlebars"
2125
2209
version = "6.3.2"
2126
2210
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2133
2217
"pest_derive",
2134
2218
"serde",
2135
2219
"serde_json",
2136
2136
-
"thiserror 2.0.12",
2220
2220
+
"thiserror 2.0.16",
2137
2221
"walkdir",
2138
2222
]
2139
2223
···
2170
2254
]
2171
2255
2172
2256
[[package]]
2257
2257
+
name = "hashlink"
2258
2258
+
version = "0.10.0"
2259
2259
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2260
2260
+
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
2261
2261
+
dependencies = [
2262
2262
+
"hashbrown 0.15.2",
2263
2263
+
]
2264
2264
+
2265
2265
+
[[package]]
2173
2266
name = "headers"
2174
2267
version = "0.4.0"
2175
2268
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2223
2316
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
2224
2317
2225
2318
[[package]]
2319
2319
+
name = "hex-conservative"
2320
2320
+
version = "0.2.1"
2321
2321
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2322
2322
+
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
2323
2323
+
dependencies = [
2324
2324
+
"arrayvec",
2325
2325
+
]
2326
2326
+
2327
2327
+
[[package]]
2226
2328
name = "hickory-proto"
2227
2329
version = "0.25.2"
2228
2330
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2240
2342
"once_cell",
2241
2343
"rand 0.9.1",
2242
2344
"ring",
2243
2243
-
"thiserror 2.0.12",
2345
2345
+
"thiserror 2.0.16",
2244
2346
"tinyvec",
2245
2347
"tokio",
2246
2348
"tracing",
···
2263
2365
"rand 0.9.1",
2264
2366
"resolv-conf",
2265
2367
"smallvec",
2266
2266
-
"thiserror 2.0.12",
2368
2368
+
"thiserror 2.0.16",
2267
2369
"tokio",
2268
2370
"tracing",
2269
2371
]
···
2757
2859
"metrics",
2758
2860
"serde",
2759
2861
"serde_json",
2760
2760
-
"thiserror 2.0.12",
2862
2862
+
"thiserror 2.0.16",
2761
2863
"tokio",
2762
2864
"tokio-tungstenite 0.26.2",
2763
2865
"url",
···
2860
2962
]
2861
2963
2862
2964
[[package]]
2965
2965
+
name = "jwt-compact"
2966
2966
+
version = "0.9.0-beta.1"
2967
2967
+
source = "git+https://github.com/fatfingers23/jwt-compact.git#aed088b8ff5ad44ef2785c453f6a4b7916728b1c"
2968
2968
+
dependencies = [
2969
2969
+
"anyhow",
2970
2970
+
"base64ct",
2971
2971
+
"chrono",
2972
2972
+
"ciborium",
2973
2973
+
"hmac",
2974
2974
+
"lazy_static",
2975
2975
+
"rand_core 0.6.4",
2976
2976
+
"secp256k1",
2977
2977
+
"serde",
2978
2978
+
"serde_json",
2979
2979
+
"sha2",
2980
2980
+
"smallvec",
2981
2981
+
"subtle",
2982
2982
+
"zeroize",
2983
2983
+
]
2984
2984
+
2985
2985
+
[[package]]
2986
2986
+
name = "k256"
2987
2987
+
version = "0.13.4"
2988
2988
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2989
2989
+
checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
2990
2990
+
dependencies = [
2991
2991
+
"cfg-if",
2992
2992
+
"ecdsa",
2993
2993
+
"elliptic-curve",
2994
2994
+
"sha2",
2995
2995
+
]
2996
2996
+
2997
2997
+
[[package]]
2863
2998
name = "langtag"
2864
2999
version = "0.3.4"
2865
3000
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2952
3087
]
2953
3088
2954
3089
[[package]]
3090
3090
+
name = "libsqlite3-sys"
3091
3091
+
version = "0.35.0"
3092
3092
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3093
3093
+
checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f"
3094
3094
+
dependencies = [
3095
3095
+
"pkg-config",
3096
3096
+
"vcpkg",
3097
3097
+
]
3098
3098
+
3099
3099
+
[[package]]
2955
3100
name = "libz-sys"
2956
3101
version = "1.1.22"
2957
3102
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2969
3114
"anyhow",
2970
3115
"fluent-uri",
2971
3116
"nom",
2972
2972
-
"thiserror 2.0.12",
3117
3117
+
"thiserror 2.0.16",
2973
3118
"tinyjson",
2974
3119
]
2975
3120
···
3235
3380
"metrics",
3236
3381
"metrics-util 0.20.0",
3237
3382
"quanta",
3238
3238
-
"thiserror 2.0.12",
3383
3383
+
"thiserror 2.0.16",
3239
3384
"tokio",
3240
3385
"tracing",
3241
3386
]
···
3784
3929
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
3785
3930
dependencies = [
3786
3931
"memchr",
3787
3787
-
"thiserror 2.0.12",
3932
3932
+
"thiserror 2.0.16",
3788
3933
"ucd-trie",
3789
3934
]
3790
3935
···
3881
4026
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
3882
4027
3883
4028
[[package]]
4029
4029
+
name = "pocket"
4030
4030
+
version = "0.1.0"
4031
4031
+
dependencies = [
4032
4032
+
"atrium-crypto",
4033
4033
+
"clap",
4034
4034
+
"jwt-compact",
4035
4035
+
"log",
4036
4036
+
"poem",
4037
4037
+
"poem-openapi",
4038
4038
+
"reqwest",
4039
4039
+
"rusqlite",
4040
4040
+
"serde",
4041
4041
+
"serde_json",
4042
4042
+
"thiserror 2.0.16",
4043
4043
+
"tokio",
4044
4044
+
"tracing-subscriber",
4045
4045
+
]
4046
4046
+
4047
4047
+
[[package]]
3884
4048
name = "poem"
3885
4049
version = "3.1.12"
3886
4050
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3918
4082
"smallvec",
3919
4083
"sync_wrapper",
3920
4084
"tempfile",
3921
3921
-
"thiserror 2.0.12",
4085
4085
+
"thiserror 2.0.16",
3922
4086
"tokio",
3923
4087
"tokio-rustls 0.26.2",
3924
4088
"tokio-stream",
···
3962
4126
"serde_json",
3963
4127
"serde_urlencoded",
3964
4128
"serde_yaml",
3965
3965
-
"thiserror 2.0.12",
4129
4129
+
"thiserror 2.0.16",
3966
4130
"tokio",
3967
4131
]
3968
4132
···
3981
4145
"quote",
3982
4146
"regex",
3983
4147
"syn 2.0.103",
3984
3984
-
"thiserror 2.0.12",
4148
4148
+
"thiserror 2.0.16",
3985
4149
]
3986
4150
3987
4151
[[package]]
···
4130
4294
"rustc-hash 2.1.1",
4131
4295
"rustls 0.23.31",
4132
4296
"socket2 0.5.9",
4133
4133
-
"thiserror 2.0.12",
4297
4297
+
"thiserror 2.0.16",
4134
4298
"tokio",
4135
4299
"tracing",
4136
4300
"web-time",
···
4151
4315
"rustls 0.23.31",
4152
4316
"rustls-pki-types",
4153
4317
"slab",
4154
4154
-
"thiserror 2.0.12",
4318
4318
+
"thiserror 2.0.16",
4155
4319
"tinyvec",
4156
4320
"tracing",
4157
4321
"web-time",
···
4505
4669
]
4506
4670
4507
4671
[[package]]
4672
4672
+
name = "rusqlite"
4673
4673
+
version = "0.37.0"
4674
4674
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4675
4675
+
checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f"
4676
4676
+
dependencies = [
4677
4677
+
"bitflags",
4678
4678
+
"fallible-iterator",
4679
4679
+
"fallible-streaming-iterator",
4680
4680
+
"hashlink",
4681
4681
+
"libsqlite3-sys",
4682
4682
+
"smallvec",
4683
4683
+
]
4684
4684
+
4685
4685
+
[[package]]
4508
4686
name = "rustc-demangle"
4509
4687
version = "0.1.24"
4510
4688
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4733
4911
]
4734
4912
4735
4913
[[package]]
4914
4914
+
name = "secp256k1"
4915
4915
+
version = "0.30.0"
4916
4916
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4917
4917
+
checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252"
4918
4918
+
dependencies = [
4919
4919
+
"bitcoin_hashes",
4920
4920
+
"rand 0.8.5",
4921
4921
+
"secp256k1-sys",
4922
4922
+
]
4923
4923
+
4924
4924
+
[[package]]
4925
4925
+
name = "secp256k1-sys"
4926
4926
+
version = "0.10.1"
4927
4927
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4928
4928
+
checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9"
4929
4929
+
dependencies = [
4930
4930
+
"cc",
4931
4931
+
]
4932
4932
+
4933
4933
+
[[package]]
4736
4934
name = "security-framework"
4737
4935
version = "2.11.1"
4738
4936
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4865
5063
"percent-encoding",
4866
5064
"ryu",
4867
5065
"serde",
4868
4868
-
"thiserror 2.0.12",
5066
5066
+
"thiserror 2.0.16",
4869
5067
]
4870
5068
4871
5069
[[package]]
···
5008
5206
dependencies = [
5009
5207
"num-bigint",
5010
5208
"num-traits",
5011
5011
-
"thiserror 2.0.12",
5209
5209
+
"thiserror 2.0.16",
5012
5210
"time",
5013
5211
]
5014
5212
···
5050
5248
"rustls 0.23.31",
5051
5249
"serde",
5052
5250
"serde_json",
5053
5053
-
"thiserror 2.0.12",
5251
5251
+
"thiserror 2.0.16",
5054
5252
"time",
5055
5253
"tokio",
5056
5254
"tokio-util",
···
5161
5359
"serde",
5162
5360
"serde_json",
5163
5361
"serde_qs",
5164
5164
-
"thiserror 2.0.12",
5362
5362
+
"thiserror 2.0.16",
5165
5363
"tinyjson",
5166
5364
"tokio",
5167
5365
"tokio-tungstenite 0.27.0",
···
5327
5525
5328
5526
[[package]]
5329
5527
name = "thiserror"
5330
5330
-
version = "2.0.12"
5528
5528
+
version = "2.0.16"
5331
5529
source = "registry+https://github.com/rust-lang/crates.io-index"
5332
5332
-
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
5530
5530
+
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
5333
5531
dependencies = [
5334
5334
-
"thiserror-impl 2.0.12",
5532
5532
+
"thiserror-impl 2.0.16",
5335
5533
]
5336
5534
5337
5535
[[package]]
···
5347
5545
5348
5546
[[package]]
5349
5547
name = "thiserror-impl"
5350
5350
-
version = "2.0.12"
5548
5548
+
version = "2.0.16"
5351
5549
source = "registry+https://github.com/rust-lang/crates.io-index"
5352
5352
-
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
5550
5550
+
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
5353
5551
dependencies = [
5354
5552
"proc-macro2",
5355
5553
"quote",
···
5742
5940
"native-tls",
5743
5941
"rand 0.9.1",
5744
5942
"sha1",
5745
5745
-
"thiserror 2.0.12",
5943
5943
+
"thiserror 2.0.16",
5746
5944
"url",
5747
5945
"utf-8",
5748
5946
]
···
5760
5958
"log",
5761
5959
"rand 0.9.1",
5762
5960
"sha1",
5763
5763
-
"thiserror 2.0.12",
5961
5961
+
"thiserror 2.0.16",
5764
5962
"utf-8",
5765
5963
]
5766
5964
···
5813
6011
"serde_qs",
5814
6012
"sha2",
5815
6013
"tempfile",
5816
5816
-
"thiserror 2.0.12",
6014
6014
+
"thiserror 2.0.16",
5817
6015
"tikv-jemallocator",
5818
6016
"tokio",
5819
6017
"tokio-util",
···
6147
6345
"reqwest",
6148
6346
"serde",
6149
6347
"serde_json",
6150
6150
-
"thiserror 2.0.12",
6348
6348
+
"thiserror 2.0.16",
6151
6349
"tokio",
6152
6350
"tokio-util",
6153
6351
"url",
···
6540
6738
"nom",
6541
6739
"oid-registry",
6542
6740
"rusticata-macros",
6543
6543
-
"thiserror 2.0.12",
6741
6741
+
"thiserror 2.0.16",
6544
6742
"time",
6545
6743
]
6546
6744
···
6651
6849
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
6652
6850
dependencies = [
6653
6851
"serde",
6852
6852
+
"zeroize_derive",
6853
6853
+
]
6854
6854
+
6855
6855
+
[[package]]
6856
6856
+
name = "zeroize_derive"
6857
6857
+
version = "1.4.2"
6858
6858
+
source = "registry+https://github.com/rust-lang/crates.io-index"
6859
6859
+
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
6860
6860
+
dependencies = [
6861
6861
+
"proc-macro2",
6862
6862
+
"quote",
6863
6863
+
"syn 2.0.103",
6654
6864
]
6655
6865
6656
6866
[[package]]
+1
Cargo.toml
Reviewed
···
10
10
"who-am-i",
11
11
"slingshot",
12
12
"quasar",
13
13
+
"pocket",
13
14
]
+19
pocket/Cargo.toml
Reviewed
···
1
1
+
[package]
2
2
+
name = "pocket"
3
3
+
version = "0.1.0"
4
4
+
edition = "2024"
5
5
+
6
6
+
[dependencies]
7
7
+
atrium-crypto = "0.1.2"
8
8
+
clap = { version = "4.5.41", features = ["derive"] }
9
9
+
jwt-compact = { git = "https://github.com/fatfingers23/jwt-compact.git", features = ["es256k"] }
10
10
+
log = "0.4.27"
11
11
+
poem = { version = "3.1.12", features = ["acme", "static-files"] }
12
12
+
poem-openapi = { version = "5.1.16", features = ["scalar"] }
13
13
+
reqwest = { version = "0.12.22", features = ["json"] }
14
14
+
rusqlite = "0.37.0"
15
15
+
serde = { version = "1.0.219", features = ["derive"] }
16
16
+
serde_json = { version = "1.0.141" }
17
17
+
thiserror = "2.0.16"
18
18
+
tokio = { version = "1.47.0", features = ["full"] }
19
19
+
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
+5
pocket/src/lib.rs
Reviewed
···
1
1
+
mod server;
2
2
+
mod token;
3
3
+
4
4
+
pub use server::serve;
5
5
+
pub use token::verify;
+9
pocket/src/main.rs
Reviewed
···
1
1
+
use pocket::serve;
2
2
+
3
3
+
#[tokio::main]
4
4
+
async fn main() {
5
5
+
tracing_subscriber::fmt::init();
6
6
+
println!("Hello, world!");
7
7
+
serve("mac.cinnebar-tet.ts.net").await
8
8
+
}
9
9
+
+208
pocket/src/server.rs
Reviewed
···
1
1
+
use poem::{
2
2
+
endpoint::make_sync,
3
3
+
Endpoint,
4
4
+
Route,
5
5
+
Server,
6
6
+
EndpointExt,
7
7
+
http::{Method, HeaderMap},
8
8
+
middleware::{CatchPanic, Cors, Tracing},
9
9
+
listener::TcpListener,
10
10
+
};
11
11
+
use poem_openapi::{
12
12
+
ContactObject,
13
13
+
ExternalDocumentObject,
14
14
+
OpenApi,
15
15
+
OpenApiService,
16
16
+
Tags,
17
17
+
Object,
18
18
+
ApiResponse,
19
19
+
types::Example,
20
20
+
auth::Bearer,
21
21
+
payload::Json,
22
22
+
SecurityScheme,
23
23
+
};
24
24
+
use crate::verify;
25
25
+
use serde::Serialize;
26
26
+
use serde_json::{Value, json};
27
27
+
28
28
+
29
29
+
#[derive(Debug, SecurityScheme)]
30
30
+
#[oai(ty = "bearer")]
31
31
+
struct BlahAuth(Bearer);
32
32
+
33
33
+
34
34
+
#[derive(Tags)]
35
35
+
enum ApiTags {
36
36
+
/// Bluesky-compatible APIs.
37
37
+
#[oai(rename = "app.bsky.* queries")]
38
38
+
AppBsky,
39
39
+
}
40
40
+
41
41
+
#[derive(Object)]
42
42
+
#[oai(example = true)]
43
43
+
struct XrpcErrorResponseObject {
44
44
+
/// Should correspond an error `name` in the lexicon errors array
45
45
+
error: String,
46
46
+
/// Human-readable description and possibly additonal context
47
47
+
message: String,
48
48
+
}
49
49
+
impl Example for XrpcErrorResponseObject {
50
50
+
fn example() -> Self {
51
51
+
Self {
52
52
+
error: "PreferencesNotFound".to_string(),
53
53
+
message: "No preferences were found for this user".to_string(),
54
54
+
}
55
55
+
}
56
56
+
}
57
57
+
type XrpcError = Json<XrpcErrorResponseObject>;
58
58
+
fn xrpc_error(error: impl AsRef<str>, message: impl AsRef<str>) -> XrpcError {
59
59
+
Json(XrpcErrorResponseObject {
60
60
+
error: error.as_ref().to_string(),
61
61
+
message: message.as_ref().to_string(),
62
62
+
})
63
63
+
}
64
64
+
65
65
+
#[derive(Object)]
66
66
+
#[oai(example = true)]
67
67
+
struct GetBskyPrefsResponseObject {
68
68
+
/// at-uri for this record
69
69
+
preferences: Value,
70
70
+
}
71
71
+
impl Example for GetBskyPrefsResponseObject {
72
72
+
fn example() -> Self {
73
73
+
Self {
74
74
+
preferences: json!({
75
75
+
"hello": "world",
76
76
+
}),
77
77
+
}
78
78
+
}
79
79
+
}
80
80
+
81
81
+
#[derive(ApiResponse)]
82
82
+
enum GetBskyPrefsResponse {
83
83
+
/// Record found
84
84
+
#[oai(status = 200)]
85
85
+
Ok(Json<GetBskyPrefsResponseObject>),
86
86
+
/// Bad request or no preferences to return
87
87
+
#[oai(status = 400)]
88
88
+
BadRequest(XrpcError),
89
89
+
// /// Server errors
90
90
+
// #[oai(status = 500)]
91
91
+
// ServerError(XrpcError),
92
92
+
}
93
93
+
94
94
+
struct Xrpc {
95
95
+
domain: String,
96
96
+
}
97
97
+
98
98
+
#[OpenApi]
99
99
+
impl Xrpc {
100
100
+
/// app.bsky.actor.getPreferences
101
101
+
///
102
102
+
/// get stored bluesky prefs
103
103
+
#[oai(
104
104
+
path = "/app.bsky.actor.getPreferences",
105
105
+
method = "get",
106
106
+
tag = "ApiTags::AppBsky"
107
107
+
)]
108
108
+
async fn app_bsky_get_prefs(
109
109
+
&self,
110
110
+
BlahAuth(auth): BlahAuth,
111
111
+
m: &HeaderMap,
112
112
+
) -> GetBskyPrefsResponse {
113
113
+
log::warn!("hm: {m:?}");
114
114
+
match verify(
115
115
+
&format!("did:web:{}#bsky_appview", self.domain),
116
116
+
"app.bsky.actor.getPreferences",
117
117
+
&auth.token,
118
118
+
).await {
119
119
+
Ok(did) => log::info!("wooo! {did}"),
120
120
+
Err(err) => return GetBskyPrefsResponse::BadRequest(xrpc_error("booo", err)),
121
121
+
};
122
122
+
log::warn!("got bearer: {:?}", auth.token);
123
123
+
GetBskyPrefsResponse::Ok(Json(GetBskyPrefsResponseObject::example()))
124
124
+
}
125
125
+
126
126
+
/// app.bsky.actor.putPreferences
127
127
+
///
128
128
+
/// store bluesky prefs
129
129
+
#[oai(
130
130
+
path = "/app.bsky.actor.putPreferences",
131
131
+
method = "post",
132
132
+
tag = "ApiTags::AppBsky"
133
133
+
)]
134
134
+
async fn app_bsky_put_prefs(
135
135
+
&self,
136
136
+
Json(prefs): Json<Value>,
137
137
+
) -> () {
138
138
+
log::warn!("received prefs: {prefs:?}");
139
139
+
()
140
140
+
}
141
141
+
}
142
142
+
143
143
+
#[derive(Debug, Clone, Serialize)]
144
144
+
#[serde(rename_all = "camelCase")]
145
145
+
struct AppViewService {
146
146
+
id: String,
147
147
+
r#type: String,
148
148
+
service_endpoint: String,
149
149
+
}
150
150
+
#[derive(Debug, Clone, Serialize)]
151
151
+
struct AppViewDoc {
152
152
+
id: String,
153
153
+
service: [AppViewService; 1],
154
154
+
}
155
155
+
/// Serve a did document for did:web for this to be an xrpc appview
156
156
+
fn get_did_doc(domain: &str) -> impl Endpoint + use<> {
157
157
+
let doc = poem::web::Json(AppViewDoc {
158
158
+
id: format!("did:web:{domain}"),
159
159
+
service: [AppViewService {
160
160
+
id: "#bsky_appview".to_string(),
161
161
+
r#type: "PocketBlueskyPreferences".to_string(),
162
162
+
service_endpoint: format!("https://{domain}"),
163
163
+
}],
164
164
+
});
165
165
+
make_sync(move |_| doc.clone())
166
166
+
}
167
167
+
168
168
+
pub async fn serve(
169
169
+
domain: &str,
170
170
+
) -> () {
171
171
+
let api_service = OpenApiService::new(
172
172
+
Xrpc { domain: domain.to_string() },
173
173
+
"Pocket",
174
174
+
env!("CARGO_PKG_VERSION"),
175
175
+
)
176
176
+
.server(domain)
177
177
+
.url_prefix("/xrpc")
178
178
+
.contact(
179
179
+
ContactObject::new()
180
180
+
.name("@microcosm.blue")
181
181
+
.url("https://bsky.app/profile/microcosm.blue"),
182
182
+
)
183
183
+
// .description(include_str!("../api-description.md"))
184
184
+
.external_document(ExternalDocumentObject::new(
185
185
+
"https://microcosm.blue/pocket",
186
186
+
));
187
187
+
188
188
+
let app = Route::new()
189
189
+
.at("/.well-known/did.json", get_did_doc(&domain))
190
190
+
.nest("/xrpc/", api_service)
191
191
+
// .at("/", StaticFileEndpoint::new("./static/index.html"))
192
192
+
// .nest("/openapi", api_service.spec_endpoint())
193
193
+
.with(
194
194
+
Cors::new()
195
195
+
.allow_method(Method::GET)
196
196
+
.allow_method(Method::POST)
197
197
+
)
198
198
+
.with(CatchPanic::new())
199
199
+
.with(Tracing);
200
200
+
201
201
+
let listener = TcpListener::bind("127.0.0.1:3000");
202
202
+
Server::new(listener)
203
203
+
.name("pocket")
204
204
+
.run(app)
205
205
+
.await
206
206
+
.unwrap();
207
207
+
208
208
+
}
+72
pocket/src/token.rs
Reviewed
···
1
1
+
use jwt_compact::{Claims, UntrustedToken};
2
2
+
use atrium_crypto::did::parse_multikey;
3
3
+
use atrium_crypto::verify::Verifier;
4
4
+
use std::collections::HashMap;
5
5
+
use serde::Deserialize;
6
6
+
7
7
+
#[derive(Debug, Deserialize)]
8
8
+
struct MiniDoc {
9
9
+
signing_key: String,
10
10
+
}
11
11
+
12
12
+
pub async fn verify(
13
13
+
expected_aud: &str,
14
14
+
expected_lxm: &str,
15
15
+
token: &str,
16
16
+
) -> Result<String, &'static str> {
17
17
+
let untrusted = UntrustedToken::new(token).unwrap();
18
18
+
19
19
+
let claims: Claims<HashMap<String, String>> = untrusted.deserialize_claims_unchecked().unwrap();
20
20
+
21
21
+
let Some(did) = claims.custom.get("iss") else {
22
22
+
return Err("jwt must include the user's did in `iss`");
23
23
+
};
24
24
+
25
25
+
if !did.starts_with("did:") {
26
26
+
return Err("iss should be a did");
27
27
+
}
28
28
+
if did.contains("#") {
29
29
+
return Err("iss should be a user did without a service identifier");
30
30
+
}
31
31
+
32
32
+
println!("Claims: {claims:#?}");
33
33
+
println!("did: {did:#?}");
34
34
+
35
35
+
let endpoint = "https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc";
36
36
+
let doc: MiniDoc = reqwest::get(format!("{endpoint}?identifier={did}"))
37
37
+
.await
38
38
+
.unwrap()
39
39
+
.error_for_status()
40
40
+
.unwrap()
41
41
+
.json()
42
42
+
.await
43
43
+
.unwrap();
44
44
+
45
45
+
log::info!("got minidoc response: {doc:?}");
46
46
+
47
47
+
let (alg, public_key) = parse_multikey(&doc.signing_key).unwrap();
48
48
+
log::info!("parsed key: {public_key:?}");
49
49
+
50
50
+
Verifier::default().verify(
51
51
+
alg,
52
52
+
&public_key,
53
53
+
&untrusted.signed_data,
54
54
+
untrusted.signature_bytes(),
55
55
+
).unwrap();
56
56
+
// if this passes, then our claims were trustworthy after all(??)
57
57
+
58
58
+
let Some(aud) = claims.custom.get("aud") else {
59
59
+
return Err("missing aud");
60
60
+
};
61
61
+
if aud != expected_aud {
62
62
+
return Err("wrong aud");
63
63
+
}
64
64
+
let Some(lxm) = claims.custom.get("lxm") else {
65
65
+
return Err("missing lxm");
66
66
+
};
67
67
+
if lxm != expected_lxm {
68
68
+
return Err("wrong lxm");
69
69
+
}
70
70
+
71
71
+
Ok(did.to_string())
72
72
+
}
+4
quasar/src/storage.rs
Reviewed
···
1
1
+
2
2
+
trait Storage {
3
3
+
4
4
+
}