Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

basic token verification works

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