Scalable and distributed custom feed generator, ott - on that topic

Prepare to setup xrpcs from smokesignals hello-world

+750 -26
+402 -24
crates/Cargo.lock
··· 9 9 checksum = "ffd5e23cd33dd73c030d1c424967eb2fbdc917d89e0bdcf1162edc4cf504f756" 10 10 dependencies = [ 11 11 "anyhow", 12 - "thiserror", 12 + "thiserror 2.0.17", 13 13 ] 14 14 15 15 [[package]] ··· 49 49 checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 50 50 dependencies = [ 51 51 "libc", 52 + ] 53 + 54 + [[package]] 55 + name = "anstream" 56 + version = "0.6.21" 57 + source = "registry+https://github.com/rust-lang/crates.io-index" 58 + checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 59 + dependencies = [ 60 + "anstyle", 61 + "anstyle-parse", 62 + "anstyle-query", 63 + "anstyle-wincon", 64 + "colorchoice", 65 + "is_terminal_polyfill", 66 + "utf8parse", 67 + ] 68 + 69 + [[package]] 70 + name = "anstyle" 71 + version = "1.0.13" 72 + source = "registry+https://github.com/rust-lang/crates.io-index" 73 + checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 74 + 75 + [[package]] 76 + name = "anstyle-parse" 77 + version = "0.2.7" 78 + source = "registry+https://github.com/rust-lang/crates.io-index" 79 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 80 + dependencies = [ 81 + "utf8parse", 82 + ] 83 + 84 + [[package]] 85 + name = "anstyle-query" 86 + version = "1.1.4" 87 + source = "registry+https://github.com/rust-lang/crates.io-index" 88 + checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 89 + dependencies = [ 90 + "windows-sys 0.60.2", 91 + ] 92 + 93 + [[package]] 94 + name = "anstyle-wincon" 95 + version = "3.0.10" 96 + source = "registry+https://github.com/rust-lang/crates.io-index" 97 + checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 98 + dependencies = [ 99 + "anstyle", 100 + "once_cell_polyfill", 101 + "windows-sys 0.60.2", 52 102 ] 53 103 54 104 [[package]] ··· 202 252 "serde", 203 253 "serde_ipld_dagcbor", 204 254 "serde_json", 205 - "thiserror", 255 + "thiserror 2.0.17", 206 256 "tokio", 207 257 "tracing", 208 258 "urlencoding", 209 259 ] 210 260 211 261 [[package]] 262 + name = "atproto-oauth" 263 + version = "0.13.0" 264 + source = "registry+https://github.com/rust-lang/crates.io-index" 265 + checksum = "3ea205901c33d074a1b498591d0511bcd788b6772ec0ca6e09a92c4327ddbdff" 266 + dependencies = [ 267 + "anyhow", 268 + "async-trait", 269 + "atproto-identity", 270 + "base64", 271 + "chrono", 272 + "ecdsa", 273 + "elliptic-curve", 274 + "k256", 275 + "lru", 276 + "multibase", 277 + "p256", 278 + "p384", 279 + "rand 0.8.5", 280 + "reqwest", 281 + "reqwest-chain", 282 + "reqwest-middleware", 283 + "serde", 284 + "serde_ipld_dagcbor", 285 + "serde_json", 286 + "sha2", 287 + "thiserror 2.0.17", 288 + "tokio", 289 + "tracing", 290 + "ulid", 291 + ] 292 + 293 + [[package]] 294 + name = "atproto-record" 295 + version = "0.13.0" 296 + source = "registry+https://github.com/rust-lang/crates.io-index" 297 + checksum = "0550f74423ca745132dc07ba1cb01f2f08243a7bf7497f5c3f2185a774c92ca2" 298 + dependencies = [ 299 + "anyhow", 300 + "atproto-identity", 301 + "base64", 302 + "chrono", 303 + "serde", 304 + "serde_ipld_dagcbor", 305 + "serde_json", 306 + "thiserror 2.0.17", 307 + ] 308 + 309 + [[package]] 310 + name = "atproto-xrpcs" 311 + version = "0.13.0" 312 + source = "registry+https://github.com/rust-lang/crates.io-index" 313 + checksum = "d2bd3c09bc3e7bbc04fd7271d25184c607644adc4c365633468184edb94094ac" 314 + dependencies = [ 315 + "anyhow", 316 + "async-trait", 317 + "atproto-identity", 318 + "atproto-oauth", 319 + "atproto-record", 320 + "axum", 321 + "base64", 322 + "chrono", 323 + "elliptic-curve", 324 + "hickory-resolver", 325 + "http", 326 + "rand 0.8.5", 327 + "reqwest", 328 + "reqwest-chain", 329 + "reqwest-middleware", 330 + "serde", 331 + "serde_json", 332 + "thiserror 2.0.17", 333 + "tokio", 334 + "tracing", 335 + ] 336 + 337 + [[package]] 212 338 name = "autocfg" 213 339 version = "1.5.0" 214 340 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 236 362 "dunce", 237 363 "fs_extra", 238 364 "libloading", 365 + ] 366 + 367 + [[package]] 368 + name = "axum" 369 + version = "0.8.6" 370 + source = "registry+https://github.com/rust-lang/crates.io-index" 371 + checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" 372 + dependencies = [ 373 + "axum-core", 374 + "axum-macros", 375 + "bytes", 376 + "form_urlencoded", 377 + "futures-util", 378 + "http", 379 + "http-body", 380 + "http-body-util", 381 + "hyper", 382 + "hyper-util", 383 + "itoa", 384 + "matchit", 385 + "memchr", 386 + "mime", 387 + "percent-encoding", 388 + "pin-project-lite", 389 + "serde_core", 390 + "serde_json", 391 + "serde_path_to_error", 392 + "serde_urlencoded", 393 + "sync_wrapper", 394 + "tokio", 395 + "tower", 396 + "tower-layer", 397 + "tower-service", 398 + "tracing", 399 + ] 400 + 401 + [[package]] 402 + name = "axum-core" 403 + version = "0.5.5" 404 + source = "registry+https://github.com/rust-lang/crates.io-index" 405 + checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" 406 + dependencies = [ 407 + "bytes", 408 + "futures-core", 409 + "http", 410 + "http-body", 411 + "http-body-util", 412 + "mime", 413 + "pin-project-lite", 414 + "sync_wrapper", 415 + "tower-layer", 416 + "tower-service", 417 + "tracing", 418 + ] 419 + 420 + [[package]] 421 + name = "axum-macros" 422 + version = "0.5.0" 423 + source = "registry+https://github.com/rust-lang/crates.io-index" 424 + checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" 425 + dependencies = [ 426 + "proc-macro2", 427 + "quote", 428 + "syn 2.0.106", 239 429 ] 240 430 241 431 [[package]] ··· 428 618 "iana-time-zone", 429 619 "js-sys", 430 620 "num-traits", 621 + "serde", 431 622 "wasm-bindgen", 432 623 "windows-link 0.2.1", 433 624 ] ··· 458 649 ] 459 650 460 651 [[package]] 652 + name = "clap" 653 + version = "4.5.48" 654 + source = "registry+https://github.com/rust-lang/crates.io-index" 655 + checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" 656 + dependencies = [ 657 + "clap_builder", 658 + "clap_derive", 659 + ] 660 + 661 + [[package]] 662 + name = "clap_builder" 663 + version = "4.5.48" 664 + source = "registry+https://github.com/rust-lang/crates.io-index" 665 + checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" 666 + dependencies = [ 667 + "anstream", 668 + "anstyle", 669 + "clap_lex", 670 + "strsim", 671 + ] 672 + 673 + [[package]] 674 + name = "clap_derive" 675 + version = "4.5.47" 676 + source = "registry+https://github.com/rust-lang/crates.io-index" 677 + checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" 678 + dependencies = [ 679 + "heck", 680 + "proc-macro2", 681 + "quote", 682 + "syn 2.0.106", 683 + ] 684 + 685 + [[package]] 686 + name = "clap_lex" 687 + version = "0.7.5" 688 + source = "registry+https://github.com/rust-lang/crates.io-index" 689 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 690 + 691 + [[package]] 461 692 name = "cmake" 462 693 version = "0.1.54" 463 694 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 467 698 ] 468 699 469 700 [[package]] 701 + name = "colorchoice" 702 + version = "1.0.4" 703 + source = "registry+https://github.com/rust-lang/crates.io-index" 704 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 705 + 706 + [[package]] 470 707 name = "concurrent-queue" 471 708 version = "2.5.0" 472 709 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 799 1036 "digest", 800 1037 "elliptic-curve", 801 1038 "rfc6979", 1039 + "serdect", 802 1040 "signature", 803 1041 "spki", 804 1042 ] ··· 1023 1261 "semver", 1024 1262 "serde", 1025 1263 "siphasher", 1026 - "thiserror", 1264 + "thiserror 2.0.17", 1027 1265 "tokio", 1028 1266 "toml", 1029 1267 "tracing", ··· 1042 1280 "lz4_flex", 1043 1281 "serde", 1044 1282 "snap", 1045 - "thiserror", 1283 + "thiserror 2.0.17", 1046 1284 "zstd", 1047 1285 ] 1048 1286 ··· 1069 1307 "semver", 1070 1308 "serde", 1071 1309 "serde_yaml", 1072 - "thiserror", 1310 + "thiserror 2.0.17", 1073 1311 "toml", 1074 1312 "tracing", 1075 1313 ] ··· 1094 1332 "pin-project", 1095 1333 "rustls-pemfile", 1096 1334 "socket2 0.5.10", 1097 - "thiserror", 1335 + "thiserror 2.0.17", 1098 1336 "tokio", 1099 1337 "tracing", 1100 1338 "wasm-bindgen-futures", ··· 1119 1357 "flv-util", 1120 1358 "once_cell", 1121 1359 "semver", 1122 - "thiserror", 1360 + "thiserror 2.0.17", 1123 1361 "tokio-util", 1124 1362 "tracing", 1125 1363 ] ··· 1149 1387 "fluvio-stream-model", 1150 1388 "paste", 1151 1389 "static_assertions", 1152 - "thiserror", 1390 + "thiserror 2.0.17", 1153 1391 "tracing", 1154 1392 ] 1155 1393 ··· 1162 1400 "eyre", 1163 1401 "fluvio-protocol", 1164 1402 "fluvio-smartmodule-derive", 1165 - "thiserror", 1403 + "thiserror 2.0.17", 1166 1404 "tracing", 1167 1405 ] 1168 1406 ··· 1197 1435 "once_cell", 1198 1436 "pin-project", 1199 1437 "semver", 1200 - "thiserror", 1438 + "thiserror 2.0.17", 1201 1439 "tokio", 1202 1440 "tokio-util", 1203 1441 "tracing", ··· 1269 1507 "event-listener", 1270 1508 "schemars", 1271 1509 "serde", 1272 - "thiserror", 1510 + "thiserror 2.0.17", 1273 1511 "toml", 1274 1512 "tracing", 1275 1513 ] ··· 1613 1851 "once_cell", 1614 1852 "rand 0.9.2", 1615 1853 "ring", 1616 - "thiserror", 1854 + "thiserror 2.0.17", 1617 1855 "tinyvec", 1618 1856 "tokio", 1619 1857 "tracing", ··· 1636 1874 "rand 0.9.2", 1637 1875 "resolv-conf", 1638 1876 "smallvec", 1639 - "thiserror", 1877 + "thiserror 2.0.17", 1640 1878 "tokio", 1641 1879 "tracing", 1642 1880 ] ··· 1709 1947 checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 1710 1948 1711 1949 [[package]] 1950 + name = "httpdate" 1951 + version = "1.0.3" 1952 + source = "registry+https://github.com/rust-lang/crates.io-index" 1953 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 1954 + 1955 + [[package]] 1712 1956 name = "humantime" 1713 1957 version = "2.3.0" 1714 1958 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1738 1982 "http", 1739 1983 "http-body", 1740 1984 "httparse", 1985 + "httpdate", 1741 1986 "itoa", 1742 1987 "pin-project-lite", 1743 1988 "pin-utils", ··· 2021 2266 ] 2022 2267 2023 2268 [[package]] 2269 + name = "is_terminal_polyfill" 2270 + version = "1.70.1" 2271 + source = "registry+https://github.com/rust-lang/crates.io-index" 2272 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 2273 + 2274 + [[package]] 2024 2275 name = "itertools" 2025 2276 version = "0.13.0" 2026 2277 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2232 2483 ] 2233 2484 2234 2485 [[package]] 2486 + name = "matchit" 2487 + version = "0.8.4" 2488 + source = "registry+https://github.com/rust-lang/crates.io-index" 2489 + checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 2490 + 2491 + [[package]] 2235 2492 name = "md-5" 2236 2493 version = "0.10.6" 2237 2494 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2252 2509 version = "0.3.17" 2253 2510 source = "registry+https://github.com/rust-lang/crates.io-index" 2254 2511 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 2512 + 2513 + [[package]] 2514 + name = "mime_guess" 2515 + version = "2.0.5" 2516 + source = "registry+https://github.com/rust-lang/crates.io-index" 2517 + checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" 2518 + dependencies = [ 2519 + "mime", 2520 + "unicase", 2521 + ] 2255 2522 2256 2523 [[package]] 2257 2524 name = "minimal-lexical" ··· 2436 2703 ] 2437 2704 2438 2705 [[package]] 2706 + name = "once_cell_polyfill" 2707 + version = "1.70.1" 2708 + source = "registry+https://github.com/rust-lang/crates.io-index" 2709 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 2710 + 2711 + [[package]] 2439 2712 name = "openssl" 2440 2713 version = "0.10.73" 2441 2714 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2543 2816 name = "ott-xrpc" 2544 2817 version = "0.1.0" 2545 2818 dependencies = [ 2819 + "anyhow", 2820 + "async-trait", 2546 2821 "atproto-identity", 2822 + "atproto-xrpcs", 2823 + "axum", 2824 + "clap", 2825 + "http", 2826 + "reqwest", 2827 + "serde", 2828 + "serde_json", 2829 + "tokio", 2547 2830 ] 2548 2831 2549 2832 [[package]] ··· 2555 2838 "ecdsa", 2556 2839 "elliptic-curve", 2557 2840 "primeorder", 2841 + "serdect", 2558 2842 "sha2", 2559 2843 ] 2560 2844 ··· 2567 2851 "ecdsa", 2568 2852 "elliptic-curve", 2569 2853 "primeorder", 2854 + "serdect", 2570 2855 "sha2", 2571 2856 ] 2572 2857 ··· 2789 3074 checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" 2790 3075 dependencies = [ 2791 3076 "elliptic-curve", 3077 + "serdect", 2792 3078 ] 2793 3079 2794 3080 [[package]] ··· 2823 3109 "rustc-hash", 2824 3110 "rustls", 2825 3111 "socket2 0.6.0", 2826 - "thiserror", 3112 + "thiserror 2.0.17", 2827 3113 "tokio", 2828 3114 "tracing", 2829 3115 "web-time", ··· 2844 3130 "rustls", 2845 3131 "rustls-pki-types", 2846 3132 "slab", 2847 - "thiserror", 3133 + "thiserror 2.0.17", 2848 3134 "tinyvec", 2849 3135 "tracing", 2850 3136 "web-time", ··· 2964 3250 dependencies = [ 2965 3251 "getrandom 0.2.16", 2966 3252 "libredox", 2967 - "thiserror", 3253 + "thiserror 2.0.17", 2968 3254 ] 2969 3255 2970 3256 [[package]] ··· 3032 3318 "bytes", 3033 3319 "encoding_rs", 3034 3320 "futures-core", 3321 + "futures-util", 3035 3322 "h2", 3036 3323 "http", 3037 3324 "http-body", ··· 3043 3330 "js-sys", 3044 3331 "log", 3045 3332 "mime", 3333 + "mime_guess", 3046 3334 "native-tls", 3047 3335 "percent-encoding", 3048 3336 "pin-project-lite", ··· 3067 3355 ] 3068 3356 3069 3357 [[package]] 3358 + name = "reqwest-chain" 3359 + version = "1.0.0" 3360 + source = "registry+https://github.com/rust-lang/crates.io-index" 3361 + checksum = "da5c014fb79a8227db44a0433d748107750d2550b7fca55c59a3d7ee7d2ee2b2" 3362 + dependencies = [ 3363 + "anyhow", 3364 + "async-trait", 3365 + "http", 3366 + "reqwest-middleware", 3367 + ] 3368 + 3369 + [[package]] 3370 + name = "reqwest-middleware" 3371 + version = "0.4.2" 3372 + source = "registry+https://github.com/rust-lang/crates.io-index" 3373 + checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" 3374 + dependencies = [ 3375 + "anyhow", 3376 + "async-trait", 3377 + "http", 3378 + "reqwest", 3379 + "serde", 3380 + "thiserror 1.0.69", 3381 + "tower-service", 3382 + ] 3383 + 3384 + [[package]] 3070 3385 name = "resolv-conf" 3071 3386 version = "0.7.5" 3072 3387 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3409 3724 ] 3410 3725 3411 3726 [[package]] 3727 + name = "serde_path_to_error" 3728 + version = "0.1.20" 3729 + source = "registry+https://github.com/rust-lang/crates.io-index" 3730 + checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 3731 + dependencies = [ 3732 + "itoa", 3733 + "serde", 3734 + "serde_core", 3735 + ] 3736 + 3737 + [[package]] 3412 3738 name = "serde_spanned" 3413 3739 version = "0.6.9" 3414 3740 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3622 3948 "serde_json", 3623 3949 "sha2", 3624 3950 "smallvec", 3625 - "thiserror", 3951 + "thiserror 2.0.17", 3626 3952 "tokio", 3627 3953 "tokio-stream", 3628 3954 "tracing", ··· 3705 4031 "smallvec", 3706 4032 "sqlx-core", 3707 4033 "stringprep", 3708 - "thiserror", 4034 + "thiserror 2.0.17", 3709 4035 "tracing", 3710 4036 "whoami", 3711 4037 ] ··· 3742 4068 "smallvec", 3743 4069 "sqlx-core", 3744 4070 "stringprep", 3745 - "thiserror", 4071 + "thiserror 2.0.17", 3746 4072 "tracing", 3747 4073 "whoami", 3748 4074 ] ··· 3766 4092 "serde", 3767 4093 "serde_urlencoded", 3768 4094 "sqlx-core", 3769 - "thiserror", 4095 + "thiserror 2.0.17", 3770 4096 "tracing", 3771 4097 "url", 3772 4098 ] ··· 3890 4216 3891 4217 [[package]] 3892 4218 name = "thiserror" 4219 + version = "1.0.69" 4220 + source = "registry+https://github.com/rust-lang/crates.io-index" 4221 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 4222 + dependencies = [ 4223 + "thiserror-impl 1.0.69", 4224 + ] 4225 + 4226 + [[package]] 4227 + name = "thiserror" 3893 4228 version = "2.0.17" 3894 4229 source = "registry+https://github.com/rust-lang/crates.io-index" 3895 4230 checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 3896 4231 dependencies = [ 3897 - "thiserror-impl", 4232 + "thiserror-impl 2.0.17", 4233 + ] 4234 + 4235 + [[package]] 4236 + name = "thiserror-impl" 4237 + version = "1.0.69" 4238 + source = "registry+https://github.com/rust-lang/crates.io-index" 4239 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 4240 + dependencies = [ 4241 + "proc-macro2", 4242 + "quote", 4243 + "syn 2.0.106", 3898 4244 ] 3899 4245 3900 4246 [[package]] ··· 4103 4449 "tokio", 4104 4450 "tower-layer", 4105 4451 "tower-service", 4452 + "tracing", 4106 4453 ] 4107 4454 4108 4455 [[package]] ··· 4216 4563 checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 4217 4564 4218 4565 [[package]] 4566 + name = "ulid" 4567 + version = "1.2.1" 4568 + source = "registry+https://github.com/rust-lang/crates.io-index" 4569 + checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" 4570 + dependencies = [ 4571 + "rand 0.9.2", 4572 + "web-time", 4573 + ] 4574 + 4575 + [[package]] 4576 + name = "unicase" 4577 + version = "2.8.1" 4578 + source = "registry+https://github.com/rust-lang/crates.io-index" 4579 + checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" 4580 + 4581 + [[package]] 4219 4582 name = "unicode-bidi" 4220 4583 version = "0.3.18" 4221 4584 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4283 4646 version = "1.0.4" 4284 4647 source = "registry+https://github.com/rust-lang/crates.io-index" 4285 4648 checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 4649 + 4650 + [[package]] 4651 + name = "utf8parse" 4652 + version = "0.2.2" 4653 + source = "registry+https://github.com/rust-lang/crates.io-index" 4654 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 4286 4655 4287 4656 [[package]] 4288 4657 name = "uuid" ··· 4474 4843 4475 4844 [[package]] 4476 4845 name = "widestring" 4477 - version = "1.2.0" 4846 + version = "1.2.1" 4478 4847 source = "registry+https://github.com/rust-lang/crates.io-index" 4479 - checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" 4848 + checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" 4480 4849 4481 4850 [[package]] 4482 4851 name = "winapi" ··· 4619 4988 checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 4620 4989 dependencies = [ 4621 4990 "windows-targets 0.52.6", 4991 + ] 4992 + 4993 + [[package]] 4994 + name = "windows-sys" 4995 + version = "0.60.2" 4996 + source = "registry+https://github.com/rust-lang/crates.io-index" 4997 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 4998 + dependencies = [ 4999 + "windows-targets 0.53.5", 4622 5000 ] 4623 5001 4624 5002 [[package]] ··· 4860 5238 "pharos", 4861 5239 "rustc_version", 4862 5240 "send_wrapper", 4863 - "thiserror", 5241 + "thiserror 2.0.17", 4864 5242 "wasm-bindgen", 4865 5243 "wasm-bindgen-futures", 4866 5244 "web-sys",
+17
crates/ott-xrpc/Cargo.toml
··· 3 3 version = "0.1.0" 4 4 edition = "2024" 5 5 6 + [[bin]] 7 + name = "atproto-xrpcs-helloworld" 8 + path = "src/main.rs" 9 + test = false 10 + bench = false 11 + doc = true 12 + 6 13 [dependencies] 14 + anyhow = "1.0.100" 15 + async-trait = "0.1.89" 7 16 atproto-identity = "0.13.0" 17 + atproto-xrpcs = "0.13.0" 18 + axum = "0.8.6" 19 + clap = { version = "4.5.48", features = ["derive", "env"] } 20 + http = "1.3.1" 21 + reqwest = "0.12.23" 22 + serde = { version = "1.0.228", features = ["derive"] } 23 + serde_json = "1.0.145" 24 + tokio = { version = "1.47.1", features = ["full"] }
+1
crates/ott-xrpc/src/lib.rs
··· 1 + pub mod webcontext;
+154 -2
crates/ott-xrpc/src/main.rs
··· 1 - fn main() { 2 - println!("Hello, world!"); 1 + use anyhow::Result; 2 + use atproto_identity::{ 3 + config::{default_env, optional_env, require_env, version, DnsNameservers}, 4 + key::{identify_key, to_public}, 5 + }; 6 + use atproto_xrpcs::authorization::ResolvingAuthorization; 7 + use axum::{ 8 + extract::{Query, State}, 9 + response::{Html, IntoResponse, Response}, 10 + routing::get, 11 + Json, Router, 12 + }; 13 + use clap::Parser; 14 + use http::{HeaderMap, StatusCode}; 15 + use ott_xrpc::webcontext::{ContextConfig, ServiceDID, ServiceDocument, WebContext}; 16 + use serde::Deserialize; 17 + use serde_json::json; 18 + 19 + /// AT Protocol XRPC Hello World Service 20 + #[derive(Parser)] 21 + #[command( 22 + name = "atproto-xrpcs-helloworld", 23 + version, 24 + about = "AT Protocol XRPC Hello World demonstration service", 25 + long_about = " 26 + A demonstration XRPC service implementation showcasing the AT Protocol ecosystem. 27 + This service provides a simple \"Hello, World!\" endpoint that supports both 28 + authenticated and unauthenticated requests. 29 + 30 + FEATURES: 31 + - AT Protocol identity resolution and DID document management 32 + - XRPC service endpoint with optional authentication 33 + - DID:web identity publishing via .well-known endpoints 34 + - JWT-based request authentication using AT Protocol standards 35 + 36 + ENVIRONMENT VARIABLES: 37 + SERVICE_KEY Private key for service identity (required) 38 + EXTERNAL_BASE External hostname for service endpoints (required) 39 + PORT HTTP server port (default: 8080) 40 + PLC_HOSTNAME PLC directory hostname (default: plc.directory) 41 + USER_AGENT HTTP User-Agent header (auto-generated) 42 + DNS_NAMESERVERS Custom DNS nameservers (optional) 43 + CERTIFICATE_BUNDLES Additional CA certificates (optional) 44 + 45 + ENDPOINTS: 46 + GET / HTML index page 47 + GET /.well-known/did.json DID document (DID:web) 48 + GET /.well-known/atproto-did AT Protocol DID identifier 49 + GET /xrpc/.../Hello Hello World XRPC endpoint 50 + " 51 + )] 52 + struct Args {} 53 + 54 + #[tokio::main] 55 + async fn main() -> Result<()> { 56 + let _args = Args::parse(); 57 + 58 + let plc_hostname = default_env("PLC_HOSTNAME", "plc.directory"); 59 + 60 + let external_base = require_env("EXTERNAL_BASE")?; 61 + let port = default_env("PORT", "8080"); 62 + let service_did = format!("did:web:{}", external_base); 63 + let dns_nameservers: DnsNameservers = optional_env("DNS_NAMESERVERS").try_into()?; 64 + 65 + let private_service_key = require_env("SERVICE_KEY")?; 66 + let private_service_key_data = identify_key(&private_service_key)?; 67 + let public_service_key_data = to_public(&private_service_key_data)?; 68 + let public_service_key = public_service_key_data.to_string(); 69 + let default_user_agent = format!( 70 + "atproto-identity-rs ({}; +https://tangled.sh/@smokesignal.events/atproto-identity-rs)", 71 + version()? 72 + ); 73 + let user_agent = default_env("USER_AGENT", &default_user_agent); 74 + 75 + let config = ContextConfig { 76 + public_service_key, 77 + private_service_key_data, 78 + service_did, 79 + plc_hostname, 80 + external_base, 81 + dns_nameservers, 82 + user_agent, 83 + }; 84 + let web_context = WebContext::new(config).unwrap(); 85 + 86 + let router = Router::new() 87 + .route("/", get(handle_index)) 88 + .route("/.well-known/did.json", get(handle_wellknown_did_web)) 89 + .route( 90 + "/.well-known/atproto-did", 91 + get(handle_wellknown_atproto_did), 92 + ) 93 + .route( 94 + "/xrpc/garden.lexicon.ngerakines.helloworld.Hello", 95 + get(handle_xrpc_hello_world), 96 + ) 97 + .with_state(web_context); 98 + 99 + let bind_address = format!("0.0.0.0:{}", port); 100 + let listener = tokio::net::TcpListener::bind(&bind_address).await?; 101 + 102 + // Start the web server in the background 103 + let server_handle = tokio::spawn(async move { 104 + if let Err(e) = axum::serve(listener, router).await { 105 + eprintln!("Server error: {}", e); 106 + } 107 + }); 108 + 109 + println!( 110 + "XRPC Hello World service started on http://0.0.0.0:{}", 111 + port 112 + ); 113 + 114 + // Keep the server running 115 + server_handle.await.unwrap(); 116 + 117 + Ok(()) 118 + } 119 + 120 + async fn handle_index() -> Html<&'static str> { 121 + Html("<html><body><h1>Hello, World!</h1></body></html>") 122 + } 123 + 124 + // /.well-known/did.json 125 + async fn handle_wellknown_did_web( 126 + service_document: State<ServiceDocument>, 127 + ) -> Json<serde_json::Value> { 128 + Json(service_document.0 .0) 129 + } 130 + 131 + // /.well-known/atproto-did 132 + async fn handle_wellknown_atproto_did(service_did: State<ServiceDID>) -> Response { 133 + (StatusCode::OK, service_did.0 .0).into_response() 134 + } 135 + 136 + #[derive(Deserialize)] 137 + struct HelloParameters { 138 + subject: Option<String>, 139 + } 140 + 141 + // /xrpc/garden.lexicon.ngerakines.helloworld.Hello 142 + async fn handle_xrpc_hello_world( 143 + parameters: Query<HelloParameters>, 144 + headers: HeaderMap, 145 + authorization: Option<ResolvingAuthorization>, 146 + ) -> Json<serde_json::Value> { 147 + println!("headers {headers:?}"); 148 + let subject = parameters.subject.as_deref().unwrap_or("World"); 149 + let message = if authorization.is_none() { 150 + format!("Hello, {subject}!") 151 + } else { 152 + format!("Hello, authenticated {subject}!") 153 + }; 154 + Json(json!({ "message": message })) 3 155 }
+176
crates/ott-xrpc/src/webcontext.rs
··· 1 + use anyhow::Result; 2 + use async_trait::async_trait; 3 + use atproto_identity::config::{optional_env, CertificateBundles, DnsNameservers}; 4 + use atproto_identity::resolve::{HickoryDnsResolver, SharedIdentityResolver}; 5 + use atproto_identity::{ 6 + key::{KeyData, KeyProvider}, 7 + resolve::InnerIdentityResolver, 8 + storage_lru::LruDidDocumentStorage, 9 + }; 10 + use serde_json::json; 11 + use std::ops::Deref; 12 + use std::{collections::HashMap, num::NonZeroUsize, sync::Arc}; 13 + 14 + use atproto_identity::{resolve::IdentityResolver, storage::DidDocumentStorage}; 15 + use axum::extract::FromRef; 16 + 17 + #[derive(Clone)] 18 + pub struct SimpleKeyProvider { 19 + keys: HashMap<String, KeyData>, 20 + } 21 + 22 + impl Default for SimpleKeyProvider { 23 + fn default() -> Self { 24 + Self::new() 25 + } 26 + } 27 + 28 + impl SimpleKeyProvider { 29 + pub fn new() -> Self { 30 + Self { 31 + keys: HashMap::new(), 32 + } 33 + } 34 + } 35 + 36 + #[async_trait] 37 + impl KeyProvider for SimpleKeyProvider { 38 + async fn get_private_key_by_id(&self, key_id: &str) -> anyhow::Result<Option<KeyData>> { 39 + Ok(self.keys.get(key_id).cloned()) 40 + } 41 + } 42 + 43 + #[derive(Clone)] 44 + pub struct ServiceDocument(pub serde_json::Value); 45 + 46 + #[derive(Clone)] 47 + pub struct ServiceDID(pub String); 48 + 49 + pub struct InnerWebContext { 50 + pub http_client: reqwest::Client, 51 + pub document_storage: Arc<dyn DidDocumentStorage>, 52 + pub key_provider: Arc<dyn KeyProvider>, 53 + pub service_document: ServiceDocument, 54 + pub service_did: ServiceDID, 55 + pub identity_resolver: Arc<dyn IdentityResolver>, 56 + } 57 + 58 + #[derive(Clone, FromRef)] 59 + pub struct WebContext(pub Arc<InnerWebContext>); 60 + 61 + pub struct ContextConfig { 62 + pub public_service_key: String, 63 + pub private_service_key_data: KeyData, 64 + pub service_did: String, 65 + pub plc_hostname: String, 66 + pub external_base: String, 67 + pub dns_nameservers: DnsNameservers, 68 + pub user_agent: String, 69 + } 70 + 71 + impl WebContext { 72 + pub fn new(config: ContextConfig) -> Result<Self> { 73 + let signing_key_storage = HashMap::from_iter(vec![( 74 + config.public_service_key.clone(), 75 + config.private_service_key_data.clone(), 76 + )]); 77 + 78 + let certificate_bundles: CertificateBundles = 79 + optional_env("CERTIFICATE_BUNDLES").try_into()?; 80 + 81 + let mut client_builder = reqwest::Client::builder(); 82 + for ca_certificate in certificate_bundles.as_ref() { 83 + let cert = std::fs::read(ca_certificate)?; 84 + let cert = reqwest::Certificate::from_pem(&cert)?; 85 + client_builder = client_builder.add_root_certificate(cert); 86 + } 87 + 88 + client_builder = client_builder.user_agent(config.user_agent); 89 + let http_client = client_builder.build()?; 90 + 91 + let dns_resolver = HickoryDnsResolver::create_resolver(config.dns_nameservers.as_ref()); 92 + 93 + let service_did = config.service_did.clone(); 94 + let external_base = config.external_base; 95 + let service_document = ServiceDocument(json!({ 96 + "@context": vec!["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1"], 97 + "id": service_did, 98 + "verificationMethod":[{ 99 + "id": format!("{service_did}#atproto"), 100 + "type":"Multikey", 101 + "controller": service_did, 102 + "publicKeyMultibase": config.public_service_key 103 + }], 104 + "service":[{ 105 + "id":"#helloworld", 106 + "type":"HelloWorldService", 107 + "serviceEndpoint":format!("https://{external_base}") 108 + }] 109 + } 110 + )); 111 + 112 + let service_did = ServiceDID(config.service_did); 113 + 114 + let identity_resolver = Arc::new(SharedIdentityResolver(Arc::new(InnerIdentityResolver { 115 + dns_resolver: Arc::new(dns_resolver), 116 + http_client: http_client.clone(), 117 + plc_hostname: config.plc_hostname, 118 + }))); 119 + 120 + let web_context = Self(Arc::new(InnerWebContext { 121 + http_client: http_client.clone(), 122 + document_storage: Arc::new(LruDidDocumentStorage::new(NonZeroUsize::new(255).unwrap())), 123 + key_provider: Arc::new(SimpleKeyProvider { 124 + keys: signing_key_storage, 125 + }), 126 + service_document, 127 + service_did, 128 + identity_resolver, 129 + })); 130 + Ok(web_context) 131 + } 132 + } 133 + 134 + impl Deref for WebContext { 135 + type Target = InnerWebContext; 136 + 137 + fn deref(&self) -> &Self::Target { 138 + &self.0 139 + } 140 + } 141 + 142 + impl FromRef<WebContext> for reqwest::Client { 143 + fn from_ref(context: &WebContext) -> Self { 144 + context.0.http_client.clone() 145 + } 146 + } 147 + 148 + impl FromRef<WebContext> for ServiceDocument { 149 + fn from_ref(context: &WebContext) -> Self { 150 + context.0.service_document.clone() 151 + } 152 + } 153 + 154 + impl FromRef<WebContext> for ServiceDID { 155 + fn from_ref(context: &WebContext) -> Self { 156 + context.0.service_did.clone() 157 + } 158 + } 159 + 160 + impl FromRef<WebContext> for Arc<dyn DidDocumentStorage> { 161 + fn from_ref(context: &WebContext) -> Self { 162 + context.0.document_storage.clone() 163 + } 164 + } 165 + 166 + impl FromRef<WebContext> for Arc<dyn KeyProvider> { 167 + fn from_ref(context: &WebContext) -> Self { 168 + context.0.key_provider.clone() 169 + } 170 + } 171 + 172 + impl FromRef<WebContext> for Arc<dyn IdentityResolver> { 173 + fn from_ref(context: &WebContext) -> Self { 174 + context.0.identity_resolver.clone() 175 + } 176 + }