├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src ├── actions.rs ├── actions ├── append_handle.rs ├── create_account.rs ├── domain.rs ├── factory.rs ├── inputs.rs ├── migrate.rs └── prepare.rs ├── bin └── tandem.rs ├── crypto.rs ├── lib.rs ├── plc.rs ├── resolve.rs └── xrpc.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 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.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "android-tzdata" 22 | version = "0.1.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 25 | 26 | [[package]] 27 | name = "android_system_properties" 28 | version = "0.1.5" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 31 | dependencies = [ 32 | "libc", 33 | ] 34 | 35 | [[package]] 36 | name = "anyhow" 37 | version = "1.0.93" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" 40 | 41 | [[package]] 42 | name = "async-trait" 43 | version = "0.1.83" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" 46 | dependencies = [ 47 | "proc-macro2", 48 | "quote", 49 | "syn 2.0.89", 50 | ] 51 | 52 | [[package]] 53 | name = "atomic-waker" 54 | version = "1.1.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 57 | 58 | [[package]] 59 | name = "autocfg" 60 | version = "1.4.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 63 | 64 | [[package]] 65 | name = "backtrace" 66 | version = "0.3.74" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 69 | dependencies = [ 70 | "addr2line", 71 | "cfg-if", 72 | "libc", 73 | "miniz_oxide", 74 | "object", 75 | "rustc-demangle", 76 | "windows-targets 0.52.6", 77 | ] 78 | 79 | [[package]] 80 | name = "base-x" 81 | version = "0.2.11" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" 84 | 85 | [[package]] 86 | name = "base16ct" 87 | version = "0.2.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 90 | 91 | [[package]] 92 | name = "base64" 93 | version = "0.22.1" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 96 | 97 | [[package]] 98 | name = "base64ct" 99 | version = "1.6.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 102 | 103 | [[package]] 104 | name = "bitflags" 105 | version = "2.6.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 108 | 109 | [[package]] 110 | name = "block-buffer" 111 | version = "0.10.4" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 114 | dependencies = [ 115 | "generic-array", 116 | ] 117 | 118 | [[package]] 119 | name = "bumpalo" 120 | version = "3.16.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 123 | 124 | [[package]] 125 | name = "byteorder" 126 | version = "1.5.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 129 | 130 | [[package]] 131 | name = "bytes" 132 | version = "1.8.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" 135 | 136 | [[package]] 137 | name = "cbor4ii" 138 | version = "0.2.14" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4" 141 | dependencies = [ 142 | "serde", 143 | ] 144 | 145 | [[package]] 146 | name = "cc" 147 | version = "1.2.1" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" 150 | dependencies = [ 151 | "shlex", 152 | ] 153 | 154 | [[package]] 155 | name = "cfg-if" 156 | version = "1.0.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 159 | 160 | [[package]] 161 | name = "cfg_aliases" 162 | version = "0.2.1" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 165 | 166 | [[package]] 167 | name = "chrono" 168 | version = "0.4.38" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 171 | dependencies = [ 172 | "android-tzdata", 173 | "iana-time-zone", 174 | "js-sys", 175 | "num-traits", 176 | "serde", 177 | "wasm-bindgen", 178 | "windows-targets 0.52.6", 179 | ] 180 | 181 | [[package]] 182 | name = "cid" 183 | version = "0.11.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a" 186 | dependencies = [ 187 | "core2", 188 | "multibase", 189 | "multihash", 190 | "serde", 191 | "serde_bytes", 192 | "unsigned-varint", 193 | ] 194 | 195 | [[package]] 196 | name = "console" 197 | version = "0.15.8" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 200 | dependencies = [ 201 | "encode_unicode", 202 | "lazy_static", 203 | "libc", 204 | "unicode-width", 205 | "windows-sys 0.52.0", 206 | ] 207 | 208 | [[package]] 209 | name = "const-oid" 210 | version = "0.9.6" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 213 | 214 | [[package]] 215 | name = "core-foundation" 216 | version = "0.9.4" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 219 | dependencies = [ 220 | "core-foundation-sys", 221 | "libc", 222 | ] 223 | 224 | [[package]] 225 | name = "core-foundation-sys" 226 | version = "0.8.7" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 229 | 230 | [[package]] 231 | name = "core2" 232 | version = "0.4.0" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" 235 | dependencies = [ 236 | "memchr", 237 | ] 238 | 239 | [[package]] 240 | name = "cpufeatures" 241 | version = "0.2.15" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" 244 | dependencies = [ 245 | "libc", 246 | ] 247 | 248 | [[package]] 249 | name = "crypto-bigint" 250 | version = "0.5.5" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" 253 | dependencies = [ 254 | "generic-array", 255 | "rand_core", 256 | "subtle", 257 | "zeroize", 258 | ] 259 | 260 | [[package]] 261 | name = "crypto-common" 262 | version = "0.1.6" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 265 | dependencies = [ 266 | "generic-array", 267 | "typenum", 268 | ] 269 | 270 | [[package]] 271 | name = "data-encoding" 272 | version = "2.6.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" 275 | 276 | [[package]] 277 | name = "data-encoding-macro" 278 | version = "0.1.15" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" 281 | dependencies = [ 282 | "data-encoding", 283 | "data-encoding-macro-internal", 284 | ] 285 | 286 | [[package]] 287 | name = "data-encoding-macro-internal" 288 | version = "0.1.13" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" 291 | dependencies = [ 292 | "data-encoding", 293 | "syn 1.0.109", 294 | ] 295 | 296 | [[package]] 297 | name = "der" 298 | version = "0.7.9" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" 301 | dependencies = [ 302 | "const-oid", 303 | "pem-rfc7468", 304 | "zeroize", 305 | ] 306 | 307 | [[package]] 308 | name = "dialoguer" 309 | version = "0.11.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" 312 | dependencies = [ 313 | "console", 314 | "shell-words", 315 | "thiserror 1.0.69", 316 | "zeroize", 317 | ] 318 | 319 | [[package]] 320 | name = "digest" 321 | version = "0.10.7" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 324 | dependencies = [ 325 | "block-buffer", 326 | "const-oid", 327 | "crypto-common", 328 | "subtle", 329 | ] 330 | 331 | [[package]] 332 | name = "displaydoc" 333 | version = "0.2.5" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 336 | dependencies = [ 337 | "proc-macro2", 338 | "quote", 339 | "syn 2.0.89", 340 | ] 341 | 342 | [[package]] 343 | name = "ecdsa" 344 | version = "0.16.9" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" 347 | dependencies = [ 348 | "der", 349 | "digest", 350 | "elliptic-curve", 351 | "rfc6979", 352 | "signature", 353 | "spki", 354 | ] 355 | 356 | [[package]] 357 | name = "either" 358 | version = "1.13.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 361 | 362 | [[package]] 363 | name = "elliptic-curve" 364 | version = "0.13.8" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 367 | dependencies = [ 368 | "base16ct", 369 | "base64ct", 370 | "crypto-bigint", 371 | "digest", 372 | "ff", 373 | "generic-array", 374 | "group", 375 | "hkdf", 376 | "pem-rfc7468", 377 | "pkcs8", 378 | "rand_core", 379 | "sec1", 380 | "serde_json", 381 | "serdect", 382 | "subtle", 383 | "zeroize", 384 | ] 385 | 386 | [[package]] 387 | name = "encode_unicode" 388 | version = "0.3.6" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 391 | 392 | [[package]] 393 | name = "encoding_rs" 394 | version = "0.8.35" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 397 | dependencies = [ 398 | "cfg-if", 399 | ] 400 | 401 | [[package]] 402 | name = "enum-as-inner" 403 | version = "0.6.1" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 406 | dependencies = [ 407 | "heck", 408 | "proc-macro2", 409 | "quote", 410 | "syn 2.0.89", 411 | ] 412 | 413 | [[package]] 414 | name = "equivalent" 415 | version = "1.0.1" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 418 | 419 | [[package]] 420 | name = "errno" 421 | version = "0.3.9" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 424 | dependencies = [ 425 | "libc", 426 | "windows-sys 0.52.0", 427 | ] 428 | 429 | [[package]] 430 | name = "fastrand" 431 | version = "2.2.0" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" 434 | 435 | [[package]] 436 | name = "ff" 437 | version = "0.13.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" 440 | dependencies = [ 441 | "rand_core", 442 | "subtle", 443 | ] 444 | 445 | [[package]] 446 | name = "fnv" 447 | version = "1.0.7" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 450 | 451 | [[package]] 452 | name = "foreign-types" 453 | version = "0.3.2" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 456 | dependencies = [ 457 | "foreign-types-shared", 458 | ] 459 | 460 | [[package]] 461 | name = "foreign-types-shared" 462 | version = "0.1.1" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 465 | 466 | [[package]] 467 | name = "form_urlencoded" 468 | version = "1.2.1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 471 | dependencies = [ 472 | "percent-encoding", 473 | ] 474 | 475 | [[package]] 476 | name = "futures" 477 | version = "0.3.31" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 480 | dependencies = [ 481 | "futures-channel", 482 | "futures-core", 483 | "futures-executor", 484 | "futures-io", 485 | "futures-sink", 486 | "futures-task", 487 | "futures-util", 488 | ] 489 | 490 | [[package]] 491 | name = "futures-channel" 492 | version = "0.3.31" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 495 | dependencies = [ 496 | "futures-core", 497 | "futures-sink", 498 | ] 499 | 500 | [[package]] 501 | name = "futures-core" 502 | version = "0.3.31" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 505 | 506 | [[package]] 507 | name = "futures-executor" 508 | version = "0.3.31" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 511 | dependencies = [ 512 | "futures-core", 513 | "futures-task", 514 | "futures-util", 515 | ] 516 | 517 | [[package]] 518 | name = "futures-io" 519 | version = "0.3.31" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 522 | 523 | [[package]] 524 | name = "futures-macro" 525 | version = "0.3.31" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 528 | dependencies = [ 529 | "proc-macro2", 530 | "quote", 531 | "syn 2.0.89", 532 | ] 533 | 534 | [[package]] 535 | name = "futures-sink" 536 | version = "0.3.31" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 539 | 540 | [[package]] 541 | name = "futures-task" 542 | version = "0.3.31" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 545 | 546 | [[package]] 547 | name = "futures-util" 548 | version = "0.3.31" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 551 | dependencies = [ 552 | "futures-channel", 553 | "futures-core", 554 | "futures-io", 555 | "futures-macro", 556 | "futures-sink", 557 | "futures-task", 558 | "memchr", 559 | "pin-project-lite", 560 | "pin-utils", 561 | "slab", 562 | ] 563 | 564 | [[package]] 565 | name = "generic-array" 566 | version = "0.14.7" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 569 | dependencies = [ 570 | "typenum", 571 | "version_check", 572 | "zeroize", 573 | ] 574 | 575 | [[package]] 576 | name = "getrandom" 577 | version = "0.2.15" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 580 | dependencies = [ 581 | "cfg-if", 582 | "js-sys", 583 | "libc", 584 | "wasi", 585 | "wasm-bindgen", 586 | ] 587 | 588 | [[package]] 589 | name = "gimli" 590 | version = "0.31.1" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 593 | 594 | [[package]] 595 | name = "group" 596 | version = "0.13.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 599 | dependencies = [ 600 | "ff", 601 | "rand_core", 602 | "subtle", 603 | ] 604 | 605 | [[package]] 606 | name = "h2" 607 | version = "0.4.7" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" 610 | dependencies = [ 611 | "atomic-waker", 612 | "bytes", 613 | "fnv", 614 | "futures-core", 615 | "futures-sink", 616 | "http", 617 | "indexmap", 618 | "slab", 619 | "tokio", 620 | "tokio-util", 621 | "tracing", 622 | ] 623 | 624 | [[package]] 625 | name = "hashbrown" 626 | version = "0.15.1" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" 629 | 630 | [[package]] 631 | name = "heck" 632 | version = "0.5.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 635 | 636 | [[package]] 637 | name = "hermit-abi" 638 | version = "0.3.9" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 641 | 642 | [[package]] 643 | name = "hickory-proto" 644 | version = "0.24.1" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" 647 | dependencies = [ 648 | "async-trait", 649 | "cfg-if", 650 | "data-encoding", 651 | "enum-as-inner", 652 | "futures-channel", 653 | "futures-io", 654 | "futures-util", 655 | "idna 0.4.0", 656 | "ipnet", 657 | "once_cell", 658 | "rand", 659 | "thiserror 1.0.69", 660 | "tinyvec", 661 | "tokio", 662 | "tracing", 663 | "url", 664 | ] 665 | 666 | [[package]] 667 | name = "hickory-resolver" 668 | version = "0.24.1" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" 671 | dependencies = [ 672 | "cfg-if", 673 | "futures-util", 674 | "hickory-proto", 675 | "ipconfig", 676 | "lru-cache", 677 | "once_cell", 678 | "parking_lot", 679 | "rand", 680 | "resolv-conf", 681 | "smallvec", 682 | "thiserror 1.0.69", 683 | "tokio", 684 | "tracing", 685 | ] 686 | 687 | [[package]] 688 | name = "hkdf" 689 | version = "0.12.4" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 692 | dependencies = [ 693 | "hmac", 694 | ] 695 | 696 | [[package]] 697 | name = "hmac" 698 | version = "0.12.1" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 701 | dependencies = [ 702 | "digest", 703 | ] 704 | 705 | [[package]] 706 | name = "hostname" 707 | version = "0.3.1" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 710 | dependencies = [ 711 | "libc", 712 | "match_cfg", 713 | "winapi", 714 | ] 715 | 716 | [[package]] 717 | name = "http" 718 | version = "1.1.0" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 721 | dependencies = [ 722 | "bytes", 723 | "fnv", 724 | "itoa", 725 | ] 726 | 727 | [[package]] 728 | name = "http-body" 729 | version = "1.0.1" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 732 | dependencies = [ 733 | "bytes", 734 | "http", 735 | ] 736 | 737 | [[package]] 738 | name = "http-body-util" 739 | version = "0.1.2" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 742 | dependencies = [ 743 | "bytes", 744 | "futures-util", 745 | "http", 746 | "http-body", 747 | "pin-project-lite", 748 | ] 749 | 750 | [[package]] 751 | name = "httparse" 752 | version = "1.9.5" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" 755 | 756 | [[package]] 757 | name = "hyper" 758 | version = "1.5.1" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" 761 | dependencies = [ 762 | "bytes", 763 | "futures-channel", 764 | "futures-util", 765 | "h2", 766 | "http", 767 | "http-body", 768 | "httparse", 769 | "itoa", 770 | "pin-project-lite", 771 | "smallvec", 772 | "tokio", 773 | "want", 774 | ] 775 | 776 | [[package]] 777 | name = "hyper-rustls" 778 | version = "0.27.3" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" 781 | dependencies = [ 782 | "futures-util", 783 | "http", 784 | "hyper", 785 | "hyper-util", 786 | "rustls", 787 | "rustls-pki-types", 788 | "tokio", 789 | "tokio-rustls", 790 | "tower-service", 791 | "webpki-roots", 792 | ] 793 | 794 | [[package]] 795 | name = "hyper-tls" 796 | version = "0.6.0" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 799 | dependencies = [ 800 | "bytes", 801 | "http-body-util", 802 | "hyper", 803 | "hyper-util", 804 | "native-tls", 805 | "tokio", 806 | "tokio-native-tls", 807 | "tower-service", 808 | ] 809 | 810 | [[package]] 811 | name = "hyper-util" 812 | version = "0.1.10" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 815 | dependencies = [ 816 | "bytes", 817 | "futures-channel", 818 | "futures-util", 819 | "http", 820 | "http-body", 821 | "hyper", 822 | "pin-project-lite", 823 | "socket2", 824 | "tokio", 825 | "tower-service", 826 | "tracing", 827 | ] 828 | 829 | [[package]] 830 | name = "iana-time-zone" 831 | version = "0.1.61" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 834 | dependencies = [ 835 | "android_system_properties", 836 | "core-foundation-sys", 837 | "iana-time-zone-haiku", 838 | "js-sys", 839 | "wasm-bindgen", 840 | "windows-core", 841 | ] 842 | 843 | [[package]] 844 | name = "iana-time-zone-haiku" 845 | version = "0.1.2" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 848 | dependencies = [ 849 | "cc", 850 | ] 851 | 852 | [[package]] 853 | name = "icu_collections" 854 | version = "1.5.0" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 857 | dependencies = [ 858 | "displaydoc", 859 | "yoke", 860 | "zerofrom", 861 | "zerovec", 862 | ] 863 | 864 | [[package]] 865 | name = "icu_locid" 866 | version = "1.5.0" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 869 | dependencies = [ 870 | "displaydoc", 871 | "litemap", 872 | "tinystr", 873 | "writeable", 874 | "zerovec", 875 | ] 876 | 877 | [[package]] 878 | name = "icu_locid_transform" 879 | version = "1.5.0" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 882 | dependencies = [ 883 | "displaydoc", 884 | "icu_locid", 885 | "icu_locid_transform_data", 886 | "icu_provider", 887 | "tinystr", 888 | "zerovec", 889 | ] 890 | 891 | [[package]] 892 | name = "icu_locid_transform_data" 893 | version = "1.5.0" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 896 | 897 | [[package]] 898 | name = "icu_normalizer" 899 | version = "1.5.0" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 902 | dependencies = [ 903 | "displaydoc", 904 | "icu_collections", 905 | "icu_normalizer_data", 906 | "icu_properties", 907 | "icu_provider", 908 | "smallvec", 909 | "utf16_iter", 910 | "utf8_iter", 911 | "write16", 912 | "zerovec", 913 | ] 914 | 915 | [[package]] 916 | name = "icu_normalizer_data" 917 | version = "1.5.0" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 920 | 921 | [[package]] 922 | name = "icu_properties" 923 | version = "1.5.1" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 926 | dependencies = [ 927 | "displaydoc", 928 | "icu_collections", 929 | "icu_locid_transform", 930 | "icu_properties_data", 931 | "icu_provider", 932 | "tinystr", 933 | "zerovec", 934 | ] 935 | 936 | [[package]] 937 | name = "icu_properties_data" 938 | version = "1.5.0" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 941 | 942 | [[package]] 943 | name = "icu_provider" 944 | version = "1.5.0" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 947 | dependencies = [ 948 | "displaydoc", 949 | "icu_locid", 950 | "icu_provider_macros", 951 | "stable_deref_trait", 952 | "tinystr", 953 | "writeable", 954 | "yoke", 955 | "zerofrom", 956 | "zerovec", 957 | ] 958 | 959 | [[package]] 960 | name = "icu_provider_macros" 961 | version = "1.5.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 964 | dependencies = [ 965 | "proc-macro2", 966 | "quote", 967 | "syn 2.0.89", 968 | ] 969 | 970 | [[package]] 971 | name = "idna" 972 | version = "0.4.0" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" 975 | dependencies = [ 976 | "unicode-bidi", 977 | "unicode-normalization", 978 | ] 979 | 980 | [[package]] 981 | name = "idna" 982 | version = "1.0.3" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 985 | dependencies = [ 986 | "idna_adapter", 987 | "smallvec", 988 | "utf8_iter", 989 | ] 990 | 991 | [[package]] 992 | name = "idna_adapter" 993 | version = "1.2.0" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 996 | dependencies = [ 997 | "icu_normalizer", 998 | "icu_properties", 999 | ] 1000 | 1001 | [[package]] 1002 | name = "indexmap" 1003 | version = "2.6.0" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 1006 | dependencies = [ 1007 | "equivalent", 1008 | "hashbrown", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "ipconfig" 1013 | version = "0.3.2" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" 1016 | dependencies = [ 1017 | "socket2", 1018 | "widestring", 1019 | "windows-sys 0.48.0", 1020 | "winreg", 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "ipld-core" 1025 | version = "0.4.1" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "b4ede82a79e134f179f4b29b5fdb1eb92bd1b38c4dfea394c539051150a21b9b" 1028 | dependencies = [ 1029 | "cid", 1030 | "serde", 1031 | "serde_bytes", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "ipnet" 1036 | version = "2.10.1" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" 1039 | 1040 | [[package]] 1041 | name = "itertools" 1042 | version = "0.13.0" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 1045 | dependencies = [ 1046 | "either", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "itoa" 1051 | version = "1.0.13" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" 1054 | 1055 | [[package]] 1056 | name = "js-sys" 1057 | version = "0.3.72" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 1060 | dependencies = [ 1061 | "wasm-bindgen", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "json-patch" 1066 | version = "3.0.1" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" 1069 | dependencies = [ 1070 | "jsonptr", 1071 | "serde", 1072 | "serde_json", 1073 | "thiserror 1.0.69", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "jsonptr" 1078 | version = "0.6.3" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" 1081 | dependencies = [ 1082 | "serde", 1083 | "serde_json", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "k256" 1088 | version = "0.13.4" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" 1091 | dependencies = [ 1092 | "cfg-if", 1093 | "ecdsa", 1094 | "elliptic-curve", 1095 | "once_cell", 1096 | "sha2", 1097 | "signature", 1098 | ] 1099 | 1100 | [[package]] 1101 | name = "lazy_static" 1102 | version = "1.5.0" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1105 | 1106 | [[package]] 1107 | name = "libc" 1108 | version = "0.2.164" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" 1111 | 1112 | [[package]] 1113 | name = "linked-hash-map" 1114 | version = "0.5.6" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 1117 | 1118 | [[package]] 1119 | name = "linux-raw-sys" 1120 | version = "0.4.14" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 1123 | 1124 | [[package]] 1125 | name = "litemap" 1126 | version = "0.7.3" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" 1129 | 1130 | [[package]] 1131 | name = "lock_api" 1132 | version = "0.4.12" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1135 | dependencies = [ 1136 | "autocfg", 1137 | "scopeguard", 1138 | ] 1139 | 1140 | [[package]] 1141 | name = "log" 1142 | version = "0.4.22" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 1145 | 1146 | [[package]] 1147 | name = "lru-cache" 1148 | version = "0.1.2" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 1151 | dependencies = [ 1152 | "linked-hash-map", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "match_cfg" 1157 | version = "0.1.0" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 1160 | 1161 | [[package]] 1162 | name = "memchr" 1163 | version = "2.7.4" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1166 | 1167 | [[package]] 1168 | name = "mime" 1169 | version = "0.3.17" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1172 | 1173 | [[package]] 1174 | name = "miniz_oxide" 1175 | version = "0.8.0" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 1178 | dependencies = [ 1179 | "adler2", 1180 | ] 1181 | 1182 | [[package]] 1183 | name = "mio" 1184 | version = "1.0.2" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 1187 | dependencies = [ 1188 | "hermit-abi", 1189 | "libc", 1190 | "wasi", 1191 | "windows-sys 0.52.0", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "multibase" 1196 | version = "0.9.1" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" 1199 | dependencies = [ 1200 | "base-x", 1201 | "data-encoding", 1202 | "data-encoding-macro", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "multihash" 1207 | version = "0.19.2" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "cc41f430805af9d1cf4adae4ed2149c759b877b01d909a1f40256188d09345d2" 1210 | dependencies = [ 1211 | "core2", 1212 | "serde", 1213 | "unsigned-varint", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "native-tls" 1218 | version = "0.2.12" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" 1221 | dependencies = [ 1222 | "libc", 1223 | "log", 1224 | "openssl", 1225 | "openssl-probe", 1226 | "openssl-sys", 1227 | "schannel", 1228 | "security-framework", 1229 | "security-framework-sys", 1230 | "tempfile", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "num-traits" 1235 | version = "0.2.19" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1238 | dependencies = [ 1239 | "autocfg", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "object" 1244 | version = "0.36.5" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 1247 | dependencies = [ 1248 | "memchr", 1249 | ] 1250 | 1251 | [[package]] 1252 | name = "once_cell" 1253 | version = "1.20.2" 1254 | source = "registry+https://github.com/rust-lang/crates.io-index" 1255 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 1256 | 1257 | [[package]] 1258 | name = "openssl" 1259 | version = "0.10.68" 1260 | source = "registry+https://github.com/rust-lang/crates.io-index" 1261 | checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" 1262 | dependencies = [ 1263 | "bitflags", 1264 | "cfg-if", 1265 | "foreign-types", 1266 | "libc", 1267 | "once_cell", 1268 | "openssl-macros", 1269 | "openssl-sys", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "openssl-macros" 1274 | version = "0.1.1" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1277 | dependencies = [ 1278 | "proc-macro2", 1279 | "quote", 1280 | "syn 2.0.89", 1281 | ] 1282 | 1283 | [[package]] 1284 | name = "openssl-probe" 1285 | version = "0.1.5" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1288 | 1289 | [[package]] 1290 | name = "openssl-sys" 1291 | version = "0.9.104" 1292 | source = "registry+https://github.com/rust-lang/crates.io-index" 1293 | checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" 1294 | dependencies = [ 1295 | "cc", 1296 | "libc", 1297 | "pkg-config", 1298 | "vcpkg", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "p256" 1303 | version = "0.13.2" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" 1306 | dependencies = [ 1307 | "ecdsa", 1308 | "elliptic-curve", 1309 | "primeorder", 1310 | "sha2", 1311 | ] 1312 | 1313 | [[package]] 1314 | name = "parking_lot" 1315 | version = "0.12.3" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1318 | dependencies = [ 1319 | "lock_api", 1320 | "parking_lot_core", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "parking_lot_core" 1325 | version = "0.9.10" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1328 | dependencies = [ 1329 | "cfg-if", 1330 | "libc", 1331 | "redox_syscall", 1332 | "smallvec", 1333 | "windows-targets 0.52.6", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "pem-rfc7468" 1338 | version = "0.7.0" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 1341 | dependencies = [ 1342 | "base64ct", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "percent-encoding" 1347 | version = "2.3.1" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1350 | 1351 | [[package]] 1352 | name = "petname" 1353 | version = "2.0.2" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "9cd31dcfdbbd7431a807ef4df6edd6473228e94d5c805e8cf671227a21bad068" 1356 | dependencies = [ 1357 | "anyhow", 1358 | "itertools", 1359 | "proc-macro2", 1360 | "quote", 1361 | "rand", 1362 | ] 1363 | 1364 | [[package]] 1365 | name = "pin-project-lite" 1366 | version = "0.2.15" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 1369 | 1370 | [[package]] 1371 | name = "pin-utils" 1372 | version = "0.1.0" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1375 | 1376 | [[package]] 1377 | name = "pkcs8" 1378 | version = "0.10.2" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 1381 | dependencies = [ 1382 | "der", 1383 | "spki", 1384 | ] 1385 | 1386 | [[package]] 1387 | name = "pkg-config" 1388 | version = "0.3.31" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 1391 | 1392 | [[package]] 1393 | name = "ppv-lite86" 1394 | version = "0.2.20" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1397 | dependencies = [ 1398 | "zerocopy", 1399 | ] 1400 | 1401 | [[package]] 1402 | name = "primeorder" 1403 | version = "0.13.6" 1404 | source = "registry+https://github.com/rust-lang/crates.io-index" 1405 | checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" 1406 | dependencies = [ 1407 | "elliptic-curve", 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "proc-macro2" 1412 | version = "1.0.91" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "307e3004becf10f5a6e0d59d20f3cd28231b0e0827a96cd3e0ce6d14bc1e4bb3" 1415 | dependencies = [ 1416 | "unicode-ident", 1417 | ] 1418 | 1419 | [[package]] 1420 | name = "quick-error" 1421 | version = "1.2.3" 1422 | source = "registry+https://github.com/rust-lang/crates.io-index" 1423 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1424 | 1425 | [[package]] 1426 | name = "quinn" 1427 | version = "0.11.6" 1428 | source = "registry+https://github.com/rust-lang/crates.io-index" 1429 | checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" 1430 | dependencies = [ 1431 | "bytes", 1432 | "pin-project-lite", 1433 | "quinn-proto", 1434 | "quinn-udp", 1435 | "rustc-hash", 1436 | "rustls", 1437 | "socket2", 1438 | "thiserror 2.0.3", 1439 | "tokio", 1440 | "tracing", 1441 | ] 1442 | 1443 | [[package]] 1444 | name = "quinn-proto" 1445 | version = "0.11.9" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" 1448 | dependencies = [ 1449 | "bytes", 1450 | "getrandom", 1451 | "rand", 1452 | "ring", 1453 | "rustc-hash", 1454 | "rustls", 1455 | "rustls-pki-types", 1456 | "slab", 1457 | "thiserror 2.0.3", 1458 | "tinyvec", 1459 | "tracing", 1460 | "web-time", 1461 | ] 1462 | 1463 | [[package]] 1464 | name = "quinn-udp" 1465 | version = "0.5.7" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" 1468 | dependencies = [ 1469 | "cfg_aliases", 1470 | "libc", 1471 | "once_cell", 1472 | "socket2", 1473 | "tracing", 1474 | "windows-sys 0.59.0", 1475 | ] 1476 | 1477 | [[package]] 1478 | name = "quote" 1479 | version = "1.0.37" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1482 | dependencies = [ 1483 | "proc-macro2", 1484 | ] 1485 | 1486 | [[package]] 1487 | name = "rand" 1488 | version = "0.8.5" 1489 | source = "registry+https://github.com/rust-lang/crates.io-index" 1490 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1491 | dependencies = [ 1492 | "libc", 1493 | "rand_chacha", 1494 | "rand_core", 1495 | ] 1496 | 1497 | [[package]] 1498 | name = "rand_chacha" 1499 | version = "0.3.1" 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" 1501 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1502 | dependencies = [ 1503 | "ppv-lite86", 1504 | "rand_core", 1505 | ] 1506 | 1507 | [[package]] 1508 | name = "rand_core" 1509 | version = "0.6.4" 1510 | source = "registry+https://github.com/rust-lang/crates.io-index" 1511 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1512 | dependencies = [ 1513 | "getrandom", 1514 | ] 1515 | 1516 | [[package]] 1517 | name = "redox_syscall" 1518 | version = "0.5.7" 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" 1520 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 1521 | dependencies = [ 1522 | "bitflags", 1523 | ] 1524 | 1525 | [[package]] 1526 | name = "reqwest" 1527 | version = "0.12.9" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" 1530 | dependencies = [ 1531 | "base64", 1532 | "bytes", 1533 | "encoding_rs", 1534 | "futures-core", 1535 | "futures-util", 1536 | "h2", 1537 | "hickory-resolver", 1538 | "http", 1539 | "http-body", 1540 | "http-body-util", 1541 | "hyper", 1542 | "hyper-rustls", 1543 | "hyper-tls", 1544 | "hyper-util", 1545 | "ipnet", 1546 | "js-sys", 1547 | "log", 1548 | "mime", 1549 | "native-tls", 1550 | "once_cell", 1551 | "percent-encoding", 1552 | "pin-project-lite", 1553 | "quinn", 1554 | "rustls", 1555 | "rustls-pemfile", 1556 | "rustls-pki-types", 1557 | "serde", 1558 | "serde_json", 1559 | "serde_urlencoded", 1560 | "sync_wrapper", 1561 | "system-configuration", 1562 | "tokio", 1563 | "tokio-native-tls", 1564 | "tokio-rustls", 1565 | "tower-service", 1566 | "url", 1567 | "wasm-bindgen", 1568 | "wasm-bindgen-futures", 1569 | "web-sys", 1570 | "webpki-roots", 1571 | "windows-registry", 1572 | ] 1573 | 1574 | [[package]] 1575 | name = "resolv-conf" 1576 | version = "0.7.0" 1577 | source = "registry+https://github.com/rust-lang/crates.io-index" 1578 | checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" 1579 | dependencies = [ 1580 | "hostname", 1581 | "quick-error", 1582 | ] 1583 | 1584 | [[package]] 1585 | name = "rfc6979" 1586 | version = "0.4.0" 1587 | source = "registry+https://github.com/rust-lang/crates.io-index" 1588 | checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" 1589 | dependencies = [ 1590 | "hmac", 1591 | "subtle", 1592 | ] 1593 | 1594 | [[package]] 1595 | name = "ring" 1596 | version = "0.17.8" 1597 | source = "registry+https://github.com/rust-lang/crates.io-index" 1598 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 1599 | dependencies = [ 1600 | "cc", 1601 | "cfg-if", 1602 | "getrandom", 1603 | "libc", 1604 | "spin", 1605 | "untrusted", 1606 | "windows-sys 0.52.0", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "rustc-demangle" 1611 | version = "0.1.24" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1614 | 1615 | [[package]] 1616 | name = "rustc-hash" 1617 | version = "2.0.0" 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" 1619 | checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" 1620 | 1621 | [[package]] 1622 | name = "rustix" 1623 | version = "0.38.41" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" 1626 | dependencies = [ 1627 | "bitflags", 1628 | "errno", 1629 | "libc", 1630 | "linux-raw-sys", 1631 | "windows-sys 0.52.0", 1632 | ] 1633 | 1634 | [[package]] 1635 | name = "rustls" 1636 | version = "0.23.17" 1637 | source = "registry+https://github.com/rust-lang/crates.io-index" 1638 | checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" 1639 | dependencies = [ 1640 | "once_cell", 1641 | "ring", 1642 | "rustls-pki-types", 1643 | "rustls-webpki", 1644 | "subtle", 1645 | "zeroize", 1646 | ] 1647 | 1648 | [[package]] 1649 | name = "rustls-pemfile" 1650 | version = "2.2.0" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1653 | dependencies = [ 1654 | "rustls-pki-types", 1655 | ] 1656 | 1657 | [[package]] 1658 | name = "rustls-pki-types" 1659 | version = "1.10.0" 1660 | source = "registry+https://github.com/rust-lang/crates.io-index" 1661 | checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" 1662 | dependencies = [ 1663 | "web-time", 1664 | ] 1665 | 1666 | [[package]] 1667 | name = "rustls-webpki" 1668 | version = "0.102.8" 1669 | source = "registry+https://github.com/rust-lang/crates.io-index" 1670 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 1671 | dependencies = [ 1672 | "ring", 1673 | "rustls-pki-types", 1674 | "untrusted", 1675 | ] 1676 | 1677 | [[package]] 1678 | name = "ryu" 1679 | version = "1.0.18" 1680 | source = "registry+https://github.com/rust-lang/crates.io-index" 1681 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1682 | 1683 | [[package]] 1684 | name = "schannel" 1685 | version = "0.1.27" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1688 | dependencies = [ 1689 | "windows-sys 0.59.0", 1690 | ] 1691 | 1692 | [[package]] 1693 | name = "scopeguard" 1694 | version = "1.2.0" 1695 | source = "registry+https://github.com/rust-lang/crates.io-index" 1696 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1697 | 1698 | [[package]] 1699 | name = "sec1" 1700 | version = "0.7.3" 1701 | source = "registry+https://github.com/rust-lang/crates.io-index" 1702 | checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" 1703 | dependencies = [ 1704 | "base16ct", 1705 | "der", 1706 | "generic-array", 1707 | "pkcs8", 1708 | "serdect", 1709 | "subtle", 1710 | "zeroize", 1711 | ] 1712 | 1713 | [[package]] 1714 | name = "security-framework" 1715 | version = "2.11.1" 1716 | source = "registry+https://github.com/rust-lang/crates.io-index" 1717 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1718 | dependencies = [ 1719 | "bitflags", 1720 | "core-foundation", 1721 | "core-foundation-sys", 1722 | "libc", 1723 | "security-framework-sys", 1724 | ] 1725 | 1726 | [[package]] 1727 | name = "security-framework-sys" 1728 | version = "2.12.1" 1729 | source = "registry+https://github.com/rust-lang/crates.io-index" 1730 | checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" 1731 | dependencies = [ 1732 | "core-foundation-sys", 1733 | "libc", 1734 | ] 1735 | 1736 | [[package]] 1737 | name = "serde" 1738 | version = "1.0.215" 1739 | source = "registry+https://github.com/rust-lang/crates.io-index" 1740 | checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" 1741 | dependencies = [ 1742 | "serde_derive", 1743 | ] 1744 | 1745 | [[package]] 1746 | name = "serde_bytes" 1747 | version = "0.11.15" 1748 | source = "registry+https://github.com/rust-lang/crates.io-index" 1749 | checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" 1750 | dependencies = [ 1751 | "serde", 1752 | ] 1753 | 1754 | [[package]] 1755 | name = "serde_derive" 1756 | version = "1.0.215" 1757 | source = "registry+https://github.com/rust-lang/crates.io-index" 1758 | checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" 1759 | dependencies = [ 1760 | "proc-macro2", 1761 | "quote", 1762 | "syn 2.0.89", 1763 | ] 1764 | 1765 | [[package]] 1766 | name = "serde_ipld_dagcbor" 1767 | version = "0.6.1" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "ded35fbe4ab8fdec1f1d14b4daff2206b1eada4d6e708cb451d464d2d965f493" 1770 | dependencies = [ 1771 | "cbor4ii", 1772 | "ipld-core", 1773 | "scopeguard", 1774 | "serde", 1775 | ] 1776 | 1777 | [[package]] 1778 | name = "serde_json" 1779 | version = "1.0.133" 1780 | source = "registry+https://github.com/rust-lang/crates.io-index" 1781 | checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" 1782 | dependencies = [ 1783 | "itoa", 1784 | "memchr", 1785 | "ryu", 1786 | "serde", 1787 | ] 1788 | 1789 | [[package]] 1790 | name = "serde_urlencoded" 1791 | version = "0.7.1" 1792 | source = "registry+https://github.com/rust-lang/crates.io-index" 1793 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1794 | dependencies = [ 1795 | "form_urlencoded", 1796 | "itoa", 1797 | "ryu", 1798 | "serde", 1799 | ] 1800 | 1801 | [[package]] 1802 | name = "serdect" 1803 | version = "0.2.0" 1804 | source = "registry+https://github.com/rust-lang/crates.io-index" 1805 | checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" 1806 | dependencies = [ 1807 | "base16ct", 1808 | "serde", 1809 | ] 1810 | 1811 | [[package]] 1812 | name = "sha2" 1813 | version = "0.10.8" 1814 | source = "registry+https://github.com/rust-lang/crates.io-index" 1815 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1816 | dependencies = [ 1817 | "cfg-if", 1818 | "cpufeatures", 1819 | "digest", 1820 | ] 1821 | 1822 | [[package]] 1823 | name = "shell-words" 1824 | version = "1.1.0" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 1827 | 1828 | [[package]] 1829 | name = "shlex" 1830 | version = "1.3.0" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1833 | 1834 | [[package]] 1835 | name = "signature" 1836 | version = "2.2.0" 1837 | source = "registry+https://github.com/rust-lang/crates.io-index" 1838 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 1839 | dependencies = [ 1840 | "digest", 1841 | "rand_core", 1842 | ] 1843 | 1844 | [[package]] 1845 | name = "slab" 1846 | version = "0.4.9" 1847 | source = "registry+https://github.com/rust-lang/crates.io-index" 1848 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1849 | dependencies = [ 1850 | "autocfg", 1851 | ] 1852 | 1853 | [[package]] 1854 | name = "smallvec" 1855 | version = "1.13.2" 1856 | source = "registry+https://github.com/rust-lang/crates.io-index" 1857 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1858 | 1859 | [[package]] 1860 | name = "socket2" 1861 | version = "0.5.7" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1864 | dependencies = [ 1865 | "libc", 1866 | "windows-sys 0.52.0", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "spin" 1871 | version = "0.9.8" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1874 | 1875 | [[package]] 1876 | name = "spki" 1877 | version = "0.7.3" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 1880 | dependencies = [ 1881 | "base64ct", 1882 | "der", 1883 | ] 1884 | 1885 | [[package]] 1886 | name = "stable_deref_trait" 1887 | version = "1.2.0" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1890 | 1891 | [[package]] 1892 | name = "subtle" 1893 | version = "2.6.1" 1894 | source = "registry+https://github.com/rust-lang/crates.io-index" 1895 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1896 | 1897 | [[package]] 1898 | name = "syn" 1899 | version = "1.0.109" 1900 | source = "registry+https://github.com/rust-lang/crates.io-index" 1901 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1902 | dependencies = [ 1903 | "proc-macro2", 1904 | "quote", 1905 | "unicode-ident", 1906 | ] 1907 | 1908 | [[package]] 1909 | name = "syn" 1910 | version = "2.0.89" 1911 | source = "registry+https://github.com/rust-lang/crates.io-index" 1912 | checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" 1913 | dependencies = [ 1914 | "proc-macro2", 1915 | "quote", 1916 | "unicode-ident", 1917 | ] 1918 | 1919 | [[package]] 1920 | name = "sync_wrapper" 1921 | version = "1.0.2" 1922 | source = "registry+https://github.com/rust-lang/crates.io-index" 1923 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1924 | dependencies = [ 1925 | "futures-core", 1926 | ] 1927 | 1928 | [[package]] 1929 | name = "synstructure" 1930 | version = "0.13.1" 1931 | source = "registry+https://github.com/rust-lang/crates.io-index" 1932 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1933 | dependencies = [ 1934 | "proc-macro2", 1935 | "quote", 1936 | "syn 2.0.89", 1937 | ] 1938 | 1939 | [[package]] 1940 | name = "system-configuration" 1941 | version = "0.6.1" 1942 | source = "registry+https://github.com/rust-lang/crates.io-index" 1943 | checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 1944 | dependencies = [ 1945 | "bitflags", 1946 | "core-foundation", 1947 | "system-configuration-sys", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "system-configuration-sys" 1952 | version = "0.6.0" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1955 | dependencies = [ 1956 | "core-foundation-sys", 1957 | "libc", 1958 | ] 1959 | 1960 | [[package]] 1961 | name = "tandem" 1962 | version = "0.1.0" 1963 | dependencies = [ 1964 | "anyhow", 1965 | "async-trait", 1966 | "base64", 1967 | "chrono", 1968 | "dialoguer", 1969 | "ecdsa", 1970 | "elliptic-curve", 1971 | "futures", 1972 | "hickory-resolver", 1973 | "json-patch", 1974 | "k256", 1975 | "multibase", 1976 | "p256", 1977 | "petname", 1978 | "rand", 1979 | "reqwest", 1980 | "sec1", 1981 | "serde", 1982 | "serde_ipld_dagcbor", 1983 | "serde_json", 1984 | "tokio", 1985 | ] 1986 | 1987 | [[package]] 1988 | name = "tempfile" 1989 | version = "3.14.0" 1990 | source = "registry+https://github.com/rust-lang/crates.io-index" 1991 | checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" 1992 | dependencies = [ 1993 | "cfg-if", 1994 | "fastrand", 1995 | "once_cell", 1996 | "rustix", 1997 | "windows-sys 0.59.0", 1998 | ] 1999 | 2000 | [[package]] 2001 | name = "thiserror" 2002 | version = "1.0.69" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 2005 | dependencies = [ 2006 | "thiserror-impl 1.0.69", 2007 | ] 2008 | 2009 | [[package]] 2010 | name = "thiserror" 2011 | version = "2.0.3" 2012 | source = "registry+https://github.com/rust-lang/crates.io-index" 2013 | checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" 2014 | dependencies = [ 2015 | "thiserror-impl 2.0.3", 2016 | ] 2017 | 2018 | [[package]] 2019 | name = "thiserror-impl" 2020 | version = "1.0.69" 2021 | source = "registry+https://github.com/rust-lang/crates.io-index" 2022 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 2023 | dependencies = [ 2024 | "proc-macro2", 2025 | "quote", 2026 | "syn 2.0.89", 2027 | ] 2028 | 2029 | [[package]] 2030 | name = "thiserror-impl" 2031 | version = "2.0.3" 2032 | source = "registry+https://github.com/rust-lang/crates.io-index" 2033 | checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" 2034 | dependencies = [ 2035 | "proc-macro2", 2036 | "quote", 2037 | "syn 2.0.89", 2038 | ] 2039 | 2040 | [[package]] 2041 | name = "tinystr" 2042 | version = "0.7.6" 2043 | source = "registry+https://github.com/rust-lang/crates.io-index" 2044 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 2045 | dependencies = [ 2046 | "displaydoc", 2047 | "zerovec", 2048 | ] 2049 | 2050 | [[package]] 2051 | name = "tinyvec" 2052 | version = "1.8.0" 2053 | source = "registry+https://github.com/rust-lang/crates.io-index" 2054 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 2055 | dependencies = [ 2056 | "tinyvec_macros", 2057 | ] 2058 | 2059 | [[package]] 2060 | name = "tinyvec_macros" 2061 | version = "0.1.1" 2062 | source = "registry+https://github.com/rust-lang/crates.io-index" 2063 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2064 | 2065 | [[package]] 2066 | name = "tokio" 2067 | version = "1.41.1" 2068 | source = "registry+https://github.com/rust-lang/crates.io-index" 2069 | checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" 2070 | dependencies = [ 2071 | "backtrace", 2072 | "bytes", 2073 | "libc", 2074 | "mio", 2075 | "pin-project-lite", 2076 | "socket2", 2077 | "tokio-macros", 2078 | "windows-sys 0.52.0", 2079 | ] 2080 | 2081 | [[package]] 2082 | name = "tokio-macros" 2083 | version = "2.4.0" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 2086 | dependencies = [ 2087 | "proc-macro2", 2088 | "quote", 2089 | "syn 2.0.89", 2090 | ] 2091 | 2092 | [[package]] 2093 | name = "tokio-native-tls" 2094 | version = "0.3.1" 2095 | source = "registry+https://github.com/rust-lang/crates.io-index" 2096 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 2097 | dependencies = [ 2098 | "native-tls", 2099 | "tokio", 2100 | ] 2101 | 2102 | [[package]] 2103 | name = "tokio-rustls" 2104 | version = "0.26.0" 2105 | source = "registry+https://github.com/rust-lang/crates.io-index" 2106 | checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" 2107 | dependencies = [ 2108 | "rustls", 2109 | "rustls-pki-types", 2110 | "tokio", 2111 | ] 2112 | 2113 | [[package]] 2114 | name = "tokio-util" 2115 | version = "0.7.12" 2116 | source = "registry+https://github.com/rust-lang/crates.io-index" 2117 | checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" 2118 | dependencies = [ 2119 | "bytes", 2120 | "futures-core", 2121 | "futures-sink", 2122 | "pin-project-lite", 2123 | "tokio", 2124 | ] 2125 | 2126 | [[package]] 2127 | name = "tower-service" 2128 | version = "0.3.3" 2129 | source = "registry+https://github.com/rust-lang/crates.io-index" 2130 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 2131 | 2132 | [[package]] 2133 | name = "tracing" 2134 | version = "0.1.40" 2135 | source = "registry+https://github.com/rust-lang/crates.io-index" 2136 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2137 | dependencies = [ 2138 | "pin-project-lite", 2139 | "tracing-attributes", 2140 | "tracing-core", 2141 | ] 2142 | 2143 | [[package]] 2144 | name = "tracing-attributes" 2145 | version = "0.1.27" 2146 | source = "registry+https://github.com/rust-lang/crates.io-index" 2147 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 2148 | dependencies = [ 2149 | "proc-macro2", 2150 | "quote", 2151 | "syn 2.0.89", 2152 | ] 2153 | 2154 | [[package]] 2155 | name = "tracing-core" 2156 | version = "0.1.32" 2157 | source = "registry+https://github.com/rust-lang/crates.io-index" 2158 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2159 | dependencies = [ 2160 | "once_cell", 2161 | ] 2162 | 2163 | [[package]] 2164 | name = "try-lock" 2165 | version = "0.2.5" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2168 | 2169 | [[package]] 2170 | name = "typenum" 2171 | version = "1.17.0" 2172 | source = "registry+https://github.com/rust-lang/crates.io-index" 2173 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2174 | 2175 | [[package]] 2176 | name = "unicode-bidi" 2177 | version = "0.3.17" 2178 | source = "registry+https://github.com/rust-lang/crates.io-index" 2179 | checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" 2180 | 2181 | [[package]] 2182 | name = "unicode-ident" 2183 | version = "1.0.14" 2184 | source = "registry+https://github.com/rust-lang/crates.io-index" 2185 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 2186 | 2187 | [[package]] 2188 | name = "unicode-normalization" 2189 | version = "0.1.24" 2190 | source = "registry+https://github.com/rust-lang/crates.io-index" 2191 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 2192 | dependencies = [ 2193 | "tinyvec", 2194 | ] 2195 | 2196 | [[package]] 2197 | name = "unicode-width" 2198 | version = "0.1.14" 2199 | source = "registry+https://github.com/rust-lang/crates.io-index" 2200 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 2201 | 2202 | [[package]] 2203 | name = "unsigned-varint" 2204 | version = "0.8.0" 2205 | source = "registry+https://github.com/rust-lang/crates.io-index" 2206 | checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 2207 | 2208 | [[package]] 2209 | name = "untrusted" 2210 | version = "0.9.0" 2211 | source = "registry+https://github.com/rust-lang/crates.io-index" 2212 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2213 | 2214 | [[package]] 2215 | name = "url" 2216 | version = "2.5.3" 2217 | source = "registry+https://github.com/rust-lang/crates.io-index" 2218 | checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" 2219 | dependencies = [ 2220 | "form_urlencoded", 2221 | "idna 1.0.3", 2222 | "percent-encoding", 2223 | ] 2224 | 2225 | [[package]] 2226 | name = "utf16_iter" 2227 | version = "1.0.5" 2228 | source = "registry+https://github.com/rust-lang/crates.io-index" 2229 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 2230 | 2231 | [[package]] 2232 | name = "utf8_iter" 2233 | version = "1.0.4" 2234 | source = "registry+https://github.com/rust-lang/crates.io-index" 2235 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2236 | 2237 | [[package]] 2238 | name = "vcpkg" 2239 | version = "0.2.15" 2240 | source = "registry+https://github.com/rust-lang/crates.io-index" 2241 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2242 | 2243 | [[package]] 2244 | name = "version_check" 2245 | version = "0.9.5" 2246 | source = "registry+https://github.com/rust-lang/crates.io-index" 2247 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2248 | 2249 | [[package]] 2250 | name = "want" 2251 | version = "0.3.1" 2252 | source = "registry+https://github.com/rust-lang/crates.io-index" 2253 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2254 | dependencies = [ 2255 | "try-lock", 2256 | ] 2257 | 2258 | [[package]] 2259 | name = "wasi" 2260 | version = "0.11.0+wasi-snapshot-preview1" 2261 | source = "registry+https://github.com/rust-lang/crates.io-index" 2262 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2263 | 2264 | [[package]] 2265 | name = "wasm-bindgen" 2266 | version = "0.2.95" 2267 | source = "registry+https://github.com/rust-lang/crates.io-index" 2268 | checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" 2269 | dependencies = [ 2270 | "cfg-if", 2271 | "once_cell", 2272 | "wasm-bindgen-macro", 2273 | ] 2274 | 2275 | [[package]] 2276 | name = "wasm-bindgen-backend" 2277 | version = "0.2.95" 2278 | source = "registry+https://github.com/rust-lang/crates.io-index" 2279 | checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" 2280 | dependencies = [ 2281 | "bumpalo", 2282 | "log", 2283 | "once_cell", 2284 | "proc-macro2", 2285 | "quote", 2286 | "syn 2.0.89", 2287 | "wasm-bindgen-shared", 2288 | ] 2289 | 2290 | [[package]] 2291 | name = "wasm-bindgen-futures" 2292 | version = "0.4.45" 2293 | source = "registry+https://github.com/rust-lang/crates.io-index" 2294 | checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" 2295 | dependencies = [ 2296 | "cfg-if", 2297 | "js-sys", 2298 | "wasm-bindgen", 2299 | "web-sys", 2300 | ] 2301 | 2302 | [[package]] 2303 | name = "wasm-bindgen-macro" 2304 | version = "0.2.95" 2305 | source = "registry+https://github.com/rust-lang/crates.io-index" 2306 | checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" 2307 | dependencies = [ 2308 | "quote", 2309 | "wasm-bindgen-macro-support", 2310 | ] 2311 | 2312 | [[package]] 2313 | name = "wasm-bindgen-macro-support" 2314 | version = "0.2.95" 2315 | source = "registry+https://github.com/rust-lang/crates.io-index" 2316 | checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" 2317 | dependencies = [ 2318 | "proc-macro2", 2319 | "quote", 2320 | "syn 2.0.89", 2321 | "wasm-bindgen-backend", 2322 | "wasm-bindgen-shared", 2323 | ] 2324 | 2325 | [[package]] 2326 | name = "wasm-bindgen-shared" 2327 | version = "0.2.95" 2328 | source = "registry+https://github.com/rust-lang/crates.io-index" 2329 | checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 2330 | 2331 | [[package]] 2332 | name = "web-sys" 2333 | version = "0.3.72" 2334 | source = "registry+https://github.com/rust-lang/crates.io-index" 2335 | checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" 2336 | dependencies = [ 2337 | "js-sys", 2338 | "wasm-bindgen", 2339 | ] 2340 | 2341 | [[package]] 2342 | name = "web-time" 2343 | version = "1.1.0" 2344 | source = "registry+https://github.com/rust-lang/crates.io-index" 2345 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2346 | dependencies = [ 2347 | "js-sys", 2348 | "wasm-bindgen", 2349 | ] 2350 | 2351 | [[package]] 2352 | name = "webpki-roots" 2353 | version = "0.26.6" 2354 | source = "registry+https://github.com/rust-lang/crates.io-index" 2355 | checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" 2356 | dependencies = [ 2357 | "rustls-pki-types", 2358 | ] 2359 | 2360 | [[package]] 2361 | name = "widestring" 2362 | version = "1.1.0" 2363 | source = "registry+https://github.com/rust-lang/crates.io-index" 2364 | checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" 2365 | 2366 | [[package]] 2367 | name = "winapi" 2368 | version = "0.3.9" 2369 | source = "registry+https://github.com/rust-lang/crates.io-index" 2370 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2371 | dependencies = [ 2372 | "winapi-i686-pc-windows-gnu", 2373 | "winapi-x86_64-pc-windows-gnu", 2374 | ] 2375 | 2376 | [[package]] 2377 | name = "winapi-i686-pc-windows-gnu" 2378 | version = "0.4.0" 2379 | source = "registry+https://github.com/rust-lang/crates.io-index" 2380 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2381 | 2382 | [[package]] 2383 | name = "winapi-x86_64-pc-windows-gnu" 2384 | version = "0.4.0" 2385 | source = "registry+https://github.com/rust-lang/crates.io-index" 2386 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2387 | 2388 | [[package]] 2389 | name = "windows-core" 2390 | version = "0.52.0" 2391 | source = "registry+https://github.com/rust-lang/crates.io-index" 2392 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2393 | dependencies = [ 2394 | "windows-targets 0.52.6", 2395 | ] 2396 | 2397 | [[package]] 2398 | name = "windows-registry" 2399 | version = "0.2.0" 2400 | source = "registry+https://github.com/rust-lang/crates.io-index" 2401 | checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" 2402 | dependencies = [ 2403 | "windows-result", 2404 | "windows-strings", 2405 | "windows-targets 0.52.6", 2406 | ] 2407 | 2408 | [[package]] 2409 | name = "windows-result" 2410 | version = "0.2.0" 2411 | source = "registry+https://github.com/rust-lang/crates.io-index" 2412 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 2413 | dependencies = [ 2414 | "windows-targets 0.52.6", 2415 | ] 2416 | 2417 | [[package]] 2418 | name = "windows-strings" 2419 | version = "0.1.0" 2420 | source = "registry+https://github.com/rust-lang/crates.io-index" 2421 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 2422 | dependencies = [ 2423 | "windows-result", 2424 | "windows-targets 0.52.6", 2425 | ] 2426 | 2427 | [[package]] 2428 | name = "windows-sys" 2429 | version = "0.48.0" 2430 | source = "registry+https://github.com/rust-lang/crates.io-index" 2431 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2432 | dependencies = [ 2433 | "windows-targets 0.48.5", 2434 | ] 2435 | 2436 | [[package]] 2437 | name = "windows-sys" 2438 | version = "0.52.0" 2439 | source = "registry+https://github.com/rust-lang/crates.io-index" 2440 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2441 | dependencies = [ 2442 | "windows-targets 0.52.6", 2443 | ] 2444 | 2445 | [[package]] 2446 | name = "windows-sys" 2447 | version = "0.59.0" 2448 | source = "registry+https://github.com/rust-lang/crates.io-index" 2449 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2450 | dependencies = [ 2451 | "windows-targets 0.52.6", 2452 | ] 2453 | 2454 | [[package]] 2455 | name = "windows-targets" 2456 | version = "0.48.5" 2457 | source = "registry+https://github.com/rust-lang/crates.io-index" 2458 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2459 | dependencies = [ 2460 | "windows_aarch64_gnullvm 0.48.5", 2461 | "windows_aarch64_msvc 0.48.5", 2462 | "windows_i686_gnu 0.48.5", 2463 | "windows_i686_msvc 0.48.5", 2464 | "windows_x86_64_gnu 0.48.5", 2465 | "windows_x86_64_gnullvm 0.48.5", 2466 | "windows_x86_64_msvc 0.48.5", 2467 | ] 2468 | 2469 | [[package]] 2470 | name = "windows-targets" 2471 | version = "0.52.6" 2472 | source = "registry+https://github.com/rust-lang/crates.io-index" 2473 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2474 | dependencies = [ 2475 | "windows_aarch64_gnullvm 0.52.6", 2476 | "windows_aarch64_msvc 0.52.6", 2477 | "windows_i686_gnu 0.52.6", 2478 | "windows_i686_gnullvm", 2479 | "windows_i686_msvc 0.52.6", 2480 | "windows_x86_64_gnu 0.52.6", 2481 | "windows_x86_64_gnullvm 0.52.6", 2482 | "windows_x86_64_msvc 0.52.6", 2483 | ] 2484 | 2485 | [[package]] 2486 | name = "windows_aarch64_gnullvm" 2487 | version = "0.48.5" 2488 | source = "registry+https://github.com/rust-lang/crates.io-index" 2489 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2490 | 2491 | [[package]] 2492 | name = "windows_aarch64_gnullvm" 2493 | version = "0.52.6" 2494 | source = "registry+https://github.com/rust-lang/crates.io-index" 2495 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2496 | 2497 | [[package]] 2498 | name = "windows_aarch64_msvc" 2499 | version = "0.48.5" 2500 | source = "registry+https://github.com/rust-lang/crates.io-index" 2501 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2502 | 2503 | [[package]] 2504 | name = "windows_aarch64_msvc" 2505 | version = "0.52.6" 2506 | source = "registry+https://github.com/rust-lang/crates.io-index" 2507 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2508 | 2509 | [[package]] 2510 | name = "windows_i686_gnu" 2511 | version = "0.48.5" 2512 | source = "registry+https://github.com/rust-lang/crates.io-index" 2513 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2514 | 2515 | [[package]] 2516 | name = "windows_i686_gnu" 2517 | version = "0.52.6" 2518 | source = "registry+https://github.com/rust-lang/crates.io-index" 2519 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2520 | 2521 | [[package]] 2522 | name = "windows_i686_gnullvm" 2523 | version = "0.52.6" 2524 | source = "registry+https://github.com/rust-lang/crates.io-index" 2525 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2526 | 2527 | [[package]] 2528 | name = "windows_i686_msvc" 2529 | version = "0.48.5" 2530 | source = "registry+https://github.com/rust-lang/crates.io-index" 2531 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2532 | 2533 | [[package]] 2534 | name = "windows_i686_msvc" 2535 | version = "0.52.6" 2536 | source = "registry+https://github.com/rust-lang/crates.io-index" 2537 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2538 | 2539 | [[package]] 2540 | name = "windows_x86_64_gnu" 2541 | version = "0.48.5" 2542 | source = "registry+https://github.com/rust-lang/crates.io-index" 2543 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2544 | 2545 | [[package]] 2546 | name = "windows_x86_64_gnu" 2547 | version = "0.52.6" 2548 | source = "registry+https://github.com/rust-lang/crates.io-index" 2549 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2550 | 2551 | [[package]] 2552 | name = "windows_x86_64_gnullvm" 2553 | version = "0.48.5" 2554 | source = "registry+https://github.com/rust-lang/crates.io-index" 2555 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2556 | 2557 | [[package]] 2558 | name = "windows_x86_64_gnullvm" 2559 | version = "0.52.6" 2560 | source = "registry+https://github.com/rust-lang/crates.io-index" 2561 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2562 | 2563 | [[package]] 2564 | name = "windows_x86_64_msvc" 2565 | version = "0.48.5" 2566 | source = "registry+https://github.com/rust-lang/crates.io-index" 2567 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2568 | 2569 | [[package]] 2570 | name = "windows_x86_64_msvc" 2571 | version = "0.52.6" 2572 | source = "registry+https://github.com/rust-lang/crates.io-index" 2573 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2574 | 2575 | [[package]] 2576 | name = "winreg" 2577 | version = "0.50.0" 2578 | source = "registry+https://github.com/rust-lang/crates.io-index" 2579 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 2580 | dependencies = [ 2581 | "cfg-if", 2582 | "windows-sys 0.48.0", 2583 | ] 2584 | 2585 | [[package]] 2586 | name = "write16" 2587 | version = "1.0.0" 2588 | source = "registry+https://github.com/rust-lang/crates.io-index" 2589 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 2590 | 2591 | [[package]] 2592 | name = "writeable" 2593 | version = "0.5.5" 2594 | source = "registry+https://github.com/rust-lang/crates.io-index" 2595 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2596 | 2597 | [[package]] 2598 | name = "yoke" 2599 | version = "0.7.4" 2600 | source = "registry+https://github.com/rust-lang/crates.io-index" 2601 | checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" 2602 | dependencies = [ 2603 | "serde", 2604 | "stable_deref_trait", 2605 | "yoke-derive", 2606 | "zerofrom", 2607 | ] 2608 | 2609 | [[package]] 2610 | name = "yoke-derive" 2611 | version = "0.7.4" 2612 | source = "registry+https://github.com/rust-lang/crates.io-index" 2613 | checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" 2614 | dependencies = [ 2615 | "proc-macro2", 2616 | "quote", 2617 | "syn 2.0.89", 2618 | "synstructure", 2619 | ] 2620 | 2621 | [[package]] 2622 | name = "zerocopy" 2623 | version = "0.7.35" 2624 | source = "registry+https://github.com/rust-lang/crates.io-index" 2625 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2626 | dependencies = [ 2627 | "byteorder", 2628 | "zerocopy-derive", 2629 | ] 2630 | 2631 | [[package]] 2632 | name = "zerocopy-derive" 2633 | version = "0.7.35" 2634 | source = "registry+https://github.com/rust-lang/crates.io-index" 2635 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2636 | dependencies = [ 2637 | "proc-macro2", 2638 | "quote", 2639 | "syn 2.0.89", 2640 | ] 2641 | 2642 | [[package]] 2643 | name = "zerofrom" 2644 | version = "0.1.4" 2645 | source = "registry+https://github.com/rust-lang/crates.io-index" 2646 | checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" 2647 | dependencies = [ 2648 | "zerofrom-derive", 2649 | ] 2650 | 2651 | [[package]] 2652 | name = "zerofrom-derive" 2653 | version = "0.1.4" 2654 | source = "registry+https://github.com/rust-lang/crates.io-index" 2655 | checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" 2656 | dependencies = [ 2657 | "proc-macro2", 2658 | "quote", 2659 | "syn 2.0.89", 2660 | "synstructure", 2661 | ] 2662 | 2663 | [[package]] 2664 | name = "zeroize" 2665 | version = "1.8.1" 2666 | source = "registry+https://github.com/rust-lang/crates.io-index" 2667 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2668 | 2669 | [[package]] 2670 | name = "zerovec" 2671 | version = "0.10.4" 2672 | source = "registry+https://github.com/rust-lang/crates.io-index" 2673 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2674 | dependencies = [ 2675 | "yoke", 2676 | "zerofrom", 2677 | "zerovec-derive", 2678 | ] 2679 | 2680 | [[package]] 2681 | name = "zerovec-derive" 2682 | version = "0.10.3" 2683 | source = "registry+https://github.com/rust-lang/crates.io-index" 2684 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2685 | dependencies = [ 2686 | "proc-macro2", 2687 | "quote", 2688 | "syn 2.0.89", 2689 | ] 2690 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "tandem" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [[bin]] 8 | name = "tandem" 9 | 10 | [dependencies] 11 | anyhow = "1.0.93" 12 | async-trait = "0.1.83" 13 | base64 = "0.22.1" 14 | chrono = { version = "0.4.38", features = ["serde"] } 15 | dialoguer = { version = "0.11.0", default-features = false, features = ["password"] } 16 | ecdsa = { version = "0.16.9", features = ["std", "signing", "verifying", "pem", "pkcs8", "der"] } 17 | elliptic-curve = { version = "0.13.8", features = ["std", "ecdh", "digest", "pem"] } 18 | futures = "0.3.31" 19 | hickory-resolver = "0.24.1" 20 | json-patch = "3.0.1" 21 | k256 = { version = "0.13.4", features = ["pem", "ecdsa", "jwk"] } 22 | multibase = "0.9.1" 23 | p256 = { version = "0.13.2", features = ["ecdsa", "jwk"] } 24 | petname = { version = "2.0.2", default-features = false, features = ["default-rng", "default-words"] } 25 | rand = "0.8.5" 26 | reqwest = { version = "0.12.9", features = ["json", "hickory-dns", "rustls-tls"] } 27 | sec1 = { version = "0.7.3", features = ["pem"] } 28 | serde = { version = "1.0.215", features = ["alloc", "derive"] } 29 | serde_ipld_dagcbor = "0.6.1" 30 | serde_json = { version = "1.0.133", features = ["alloc"] } 31 | tokio = { version = "1.41.1", default-features = false, features = ["macros", "rt", "rt-multi-thread"] } 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tandem 2 | 3 | This is a proof of concept that is in active development. Do not use it yet. 4 | 5 | ## Demo 6 | 7 | ![tandem_demo](https://github.com/user-attachments/assets/c1f0a98f-dc65-45e1-9309-c087fab28290) 8 | -------------------------------------------------------------------------------- /src/actions.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod append_handle; 2 | pub(crate) mod create_account; 3 | pub mod domain; 4 | pub mod factory; 5 | pub(crate) mod inputs; 6 | pub(crate) mod migrate; 7 | pub(crate) mod prepare; 8 | 9 | pub use domain::{TandemAction, Theme}; 10 | pub use factory::{get_action, SUPPORTED_ACTIONS}; 11 | 12 | pub(crate) use append_handle::ActionAppendHandle; 13 | pub(crate) use create_account::ActionCreateAccount; 14 | pub(crate) use inputs::{get_did_plc_input, get_handle_input, get_jwk_input}; 15 | pub(crate) use migrate::ActionMigrate; 16 | pub(crate) use prepare::ActionPrepare; 17 | -------------------------------------------------------------------------------- /src/actions/append_handle.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use async_trait::async_trait; 3 | use json_patch::{patch, Patch}; 4 | use serde_json::{from_value, json}; 5 | 6 | use crate::{ 7 | actions::{get_did_plc_input, get_handle_input, get_jwk_input, TandemAction, Theme}, 8 | crypto::sign_operation, 9 | plc::{did_plc_last_operation, submit_operation}, 10 | }; 11 | 12 | pub(crate) struct ActionAppendHandle<'a> { 13 | theme: &'a Theme<'a>, 14 | http_client: reqwest::Client, 15 | plc: String, 16 | } 17 | 18 | impl<'a> ActionAppendHandle<'a> { 19 | pub(crate) fn new(theme: &'a Theme<'_>, http_client: &reqwest::Client, plc: &str) -> Self { 20 | Self { 21 | theme, 22 | http_client: http_client.clone(), 23 | plc: plc.to_string(), 24 | } 25 | } 26 | } 27 | 28 | #[async_trait] 29 | impl TandemAction for ActionAppendHandle<'_> { 30 | async fn run(&self) -> Result<()> { 31 | println!( 32 | "{}", 33 | self.theme 34 | .white_dim 35 | .apply_to("The 'Append Handle' action appends an additional handle to the 'alsoKnownAs' field in your DID-PLC document. This action requires your tandem private key.") 36 | ); 37 | 38 | let did = get_did_plc_input(self.theme.colorful_theme, "What is your DID?")?; 39 | let jwk = get_jwk_input(self.theme.colorful_theme)?; 40 | let new_handle = get_handle_input( 41 | self.theme.colorful_theme, 42 | "What is the new handle being added?", 43 | )?; 44 | 45 | let did_key = crate::crypto::jwk_to_did_key(&jwk)?; 46 | println!("{}", self.theme.green.apply_to("✔ Derived DID key")); 47 | println!("{}", self.theme.white_dim.apply_to(&did_key)); 48 | 49 | let (last_commit, last_operation) = 50 | did_plc_last_operation(&self.http_client, &self.plc, &did).await?; 51 | println!( 52 | "{}", 53 | self.theme.green.apply_to("✔ Retreived last operation") 54 | ); 55 | println!("{}", self.theme.white_dim.apply_to(&last_commit)); 56 | println!( 57 | "{}", 58 | self.theme.white_dim.apply_to( 59 | serde_json::to_string_pretty(&last_operation) 60 | .context("failed to serialize DID document")? 61 | ) 62 | ); 63 | 64 | let operation_patch: Patch = from_value(json!([ 65 | { "op": "add", "path": "/alsoKnownAs/-", "value": format!("at://{}", new_handle) }, 66 | { "op": "remove", "path": "/sig" }, 67 | { "op": "replace", "path": "/prev", "value": last_commit }, 68 | ])) 69 | .context("failed to create patch to append handle")?; 70 | 71 | let mut operation = last_operation.clone(); 72 | 73 | patch(&mut operation, &operation_patch)?; 74 | println!( 75 | "{}", 76 | self.theme 77 | .green 78 | .apply_to("✔ Prepared operation for signing") 79 | ); 80 | println!( 81 | "{}", 82 | self.theme.white_dim.apply_to( 83 | serde_json::to_string_pretty(&operation) 84 | .context("failed to serialize DID document")? 85 | ) 86 | ); 87 | 88 | let signed_operation = sign_operation(&jwk, &operation)?; 89 | println!("{}", self.theme.green.apply_to("✔ Signed operation")); 90 | println!( 91 | "{}", 92 | self.theme.white_dim.apply_to( 93 | serde_json::to_string_pretty(&signed_operation) 94 | .context("failed to serialize DID document")? 95 | ) 96 | ); 97 | 98 | submit_operation(&self.http_client, &self.plc, &did, &signed_operation).await?; 99 | println!("{}", self.theme.green.apply_to("✔ Operation submitted")); 100 | 101 | Ok(()) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/actions/create_account.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Context, Result}; 2 | use async_trait::async_trait; 3 | use dialoguer::{Confirm, Input, Password, Select}; 4 | 5 | use crate::{ 6 | actions::{get_handle_input, get_jwk_input, TandemAction, Theme}, 7 | xrpc::{create_account, describe_server}, 8 | }; 9 | 10 | pub(crate) struct ActionCreateAccount<'a> { 11 | theme: &'a Theme<'a>, 12 | http_client: reqwest::Client, 13 | } 14 | 15 | impl<'a> ActionCreateAccount<'a> { 16 | pub(crate) fn new(theme: &'a Theme<'_>, http_client: &reqwest::Client) -> Self { 17 | Self { 18 | theme, 19 | http_client: http_client.clone(), 20 | } 21 | } 22 | } 23 | 24 | #[async_trait] 25 | impl TandemAction for ActionCreateAccount<'_> { 26 | async fn run(&self) -> Result<()> { 27 | println!( 28 | "{}", 29 | self.theme 30 | .white_dim 31 | .apply_to("The 'Create Account' creates an account on a PDS.") 32 | ); 33 | 34 | let pds_hostname = get_handle_input( 35 | self.theme.colorful_theme, 36 | "What is the hostname of the PDS?", 37 | )?; 38 | 39 | let (_pds_did, invite_required, available_domains) = 40 | describe_server(&self.http_client, &pds_hostname) 41 | .await 42 | .context("Unable to describe server.")?; 43 | println!( 44 | "{}", 45 | self.theme.green.apply_to("✔ Retrieved PDS information") 46 | ); 47 | 48 | if available_domains.is_empty() { 49 | println!( 50 | "{}", 51 | self.theme.red_bold.apply_to( 52 | "This PDS does not have any available domains. It is probably misconfigured." 53 | ) 54 | ); 55 | return Ok(()); 56 | } 57 | 58 | let invite_code = if invite_required { 59 | println!( 60 | "{}", 61 | self.theme 62 | .yellow_bold 63 | .apply_to("This PDS requires an invite code.") 64 | ); 65 | 66 | let invite_code = Input::::with_theme(self.theme.colorful_theme) 67 | .with_prompt("Invite Code") 68 | .interact()?; 69 | Some(invite_code) 70 | } else { 71 | None 72 | }; 73 | 74 | let random_handle = 75 | petname::petname(2, "-").ok_or_else(|| anyhow!("Failed to generate random handle"))?; 76 | 77 | let suggested_handle = format!("{}{}", random_handle, available_domains[0]); 78 | 79 | let handle = Input::::with_theme(self.theme.colorful_theme) 80 | .with_prompt("Handle") 81 | .default(suggested_handle) 82 | .interact()?; 83 | 84 | let email = Input::::with_theme(self.theme.colorful_theme) 85 | .with_prompt("Email") 86 | .interact()?; 87 | 88 | let password = Password::with_theme(self.theme.colorful_theme) 89 | .with_prompt("Password") 90 | .interact()?; 91 | 92 | let existing_did = { 93 | if Confirm::with_theme(self.theme.colorful_theme) 94 | .with_prompt("Are you creating an account for an existing DID?") 95 | .default(false) 96 | .show_default(true) 97 | .wait_for_newline(true) 98 | .interact()? 99 | { 100 | let did = Input::::with_theme(self.theme.colorful_theme) 101 | .with_prompt("Existing DID") 102 | .interact()?; 103 | Some(did) 104 | } else { 105 | None 106 | } 107 | }; 108 | 109 | let key_types = &["provided jwk", "generate p256", "generate k256"]; 110 | 111 | let key_type = Select::with_theme(self.theme.colorful_theme) 112 | .with_prompt("Select key type") 113 | .default(0) 114 | .items(&key_types[..]) 115 | .interact()?; 116 | 117 | let recovery_key = if key_type == 0 { 118 | let jwk = get_jwk_input(self.theme.colorful_theme)?; 119 | let did_key = crate::crypto::jwk_to_did_key(&jwk)?; 120 | 121 | println!("{}", self.theme.green.apply_to("✔ Derived DID key")); 122 | println!("{}", self.theme.white_dim.apply_to(&did_key)); 123 | did_key 124 | } else { 125 | let (secret_jwk, encoded_public_key) = if key_type == 1 { 126 | crate::crypto::p256::gen_key() 127 | } else if key_type == 2 { 128 | crate::crypto::k256::gen_key() 129 | } else { 130 | Err(anyhow!("Invalid key type")) 131 | }?; 132 | println!( 133 | "{}", 134 | self.theme 135 | .yellow_bold 136 | .apply_to("Important! Securely store the following private key."), 137 | ); 138 | println!("{}", self.theme.red_bold.apply_to(&secret_jwk)); 139 | encoded_public_key 140 | }; 141 | let recovery_key = format!("did:key:{}", recovery_key); 142 | 143 | let (new_did, new_handle, _new_access_jwt) = create_account( 144 | &self.http_client, 145 | &pds_hostname, 146 | &handle, 147 | &password, 148 | &email, 149 | &recovery_key, 150 | invite_code, 151 | existing_did, 152 | ) 153 | .await?; 154 | 155 | println!( 156 | "{}", 157 | self.theme 158 | .green 159 | .apply_to(format!("✔ Account created: {} ({})", new_did, new_handle)) 160 | ); 161 | 162 | Ok(()) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/actions/domain.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_trait::async_trait; 3 | use dialoguer::{console::Style, theme::ColorfulTheme}; 4 | 5 | pub struct Theme<'a> { 6 | pub red_bold: Style, 7 | pub yellow_bold: Style, 8 | pub green: Style, 9 | pub white_dim: Style, 10 | pub colorful_theme: &'a ColorfulTheme, 11 | } 12 | 13 | #[async_trait] 14 | pub trait TandemAction: Sync + Send { 15 | async fn run(&self) -> Result<()>; 16 | } 17 | -------------------------------------------------------------------------------- /src/actions/factory.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | ActionAppendHandle, ActionCreateAccount, ActionMigrate, ActionPrepare, TandemAction, Theme, 3 | }; 4 | use anyhow::{anyhow, Result}; 5 | 6 | pub const SUPPORTED_ACTIONS: &[&str; 4] = &[ 7 | "Upgrade Account", 8 | "Create Account", 9 | "Migrate Account", 10 | "Append Handle", 11 | ]; 12 | 13 | pub fn get_action<'a>( 14 | selected_operation: usize, 15 | theme: &'a Theme<'a>, 16 | http_client: &reqwest::Client, 17 | plc: &str, 18 | ) -> Result> { 19 | match selected_operation { 20 | 0 => Ok(Box::new(ActionPrepare::new(theme, http_client, plc)) as Box), 21 | 1 => Ok(Box::new(ActionCreateAccount::new(theme, http_client)) as Box), 22 | 2 => Ok(Box::new(ActionMigrate::new(theme, http_client, plc)) as Box), 23 | 3 => { 24 | Ok(Box::new(ActionAppendHandle::new(theme, http_client, plc)) as Box) 25 | } 26 | _ => Err(anyhow!("Unsupported operation")), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/actions/inputs.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Context, Result}; 2 | use dialoguer::{theme::ColorfulTheme, Input}; 3 | use elliptic_curve::JwkEcKey; 4 | use std::str::FromStr; 5 | 6 | pub(crate) fn get_jwk_input(theme: &ColorfulTheme) -> Result { 7 | let secret_jwk = Input::::with_theme(theme) 8 | .with_prompt("JWK") 9 | .interact()?; 10 | 11 | JwkEcKey::from_str(&secret_jwk).context("failed to parse JWK") 12 | } 13 | 14 | pub(crate) fn get_handle_input(theme: &ColorfulTheme, prompt: &str) -> Result { 15 | let handle = Input::::with_theme(theme) 16 | .with_prompt(prompt) 17 | .interact()?; 18 | 19 | is_valid_handle(&handle).ok_or(anyhow!("invalid handle")) 20 | } 21 | 22 | pub(crate) fn get_did_plc_input(theme: &ColorfulTheme, prompt: &str) -> Result { 23 | let handle = Input::::with_theme(theme) 24 | .with_prompt(prompt) 25 | .interact()?; 26 | 27 | is_valid_did_plc(&handle).ok_or(anyhow!("invalid DID-PLC")) 28 | } 29 | 30 | fn is_valid_hostname(hostname: &str) -> bool { 31 | fn is_valid_char(byte: u8) -> bool { 32 | byte.is_ascii_lowercase() 33 | || byte.is_ascii_uppercase() 34 | || byte.is_ascii_digit() 35 | || byte == b'-' 36 | || byte == b'.' 37 | } 38 | !(hostname.ends_with(".localhost") 39 | || hostname.ends_with(".internal") 40 | || hostname.ends_with(".arpa") 41 | || hostname.ends_with(".local") 42 | || hostname.bytes().any(|byte| !is_valid_char(byte)) 43 | || hostname.split('.').any(|label| { 44 | label.is_empty() || label.len() > 63 || label.starts_with('-') || label.ends_with('-') 45 | }) 46 | || hostname.is_empty() 47 | || hostname.len() > 253) 48 | } 49 | 50 | fn is_valid_handle(handle: &str) -> Option { 51 | let trimmed = { 52 | if let Some(value) = handle.strip_prefix("at://") { 53 | value 54 | } else if let Some(value) = handle.strip_prefix('@') { 55 | value 56 | } else { 57 | handle 58 | } 59 | }; 60 | let trimmed = trimmed.to_lowercase(); 61 | if is_valid_hostname(&trimmed) && trimmed.chars().any(|c| c == '.') { 62 | Some(trimmed.to_string()) 63 | } else { 64 | None 65 | } 66 | } 67 | 68 | fn is_valid_did_plc(input: &str) -> Option { 69 | let trimmed = { 70 | if let Some(value) = input.strip_prefix("at://") { 71 | value 72 | } else { 73 | input 74 | } 75 | }; 76 | if trimmed.starts_with("did:plc:") { 77 | Some(trimmed.to_string()) 78 | } else { 79 | None 80 | } 81 | } 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | use super::*; 86 | 87 | #[test] 88 | fn valid_hostnames() { 89 | for hostname in &[ 90 | "VaLiD-HoStNaMe", 91 | "50-name", 92 | "235235", 93 | "example.com", 94 | "VaLid.HoStNaMe", 95 | "123.456", 96 | ] { 97 | assert!( 98 | is_valid_hostname(hostname), 99 | "{} is not valid hostname", 100 | hostname 101 | ); 102 | } 103 | } 104 | 105 | #[test] 106 | fn invalid_hostnames() { 107 | for hostname in &[ 108 | "-invalid-name", 109 | "also-invalid-", 110 | "asdf@fasd", 111 | "@asdfl", 112 | "asd f@", 113 | ".invalid", 114 | "invalid.name.", 115 | "foo.label-is-way-to-longgggggggggggggggggggggggggggggggggggggggggggg.org", 116 | "invalid.-starting.char", 117 | "invalid.ending-.char", 118 | "empty..label", 119 | ] { 120 | assert!( 121 | !is_valid_hostname(hostname), 122 | "{} should not be valid hostname", 123 | hostname 124 | ); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/actions/migrate.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use async_trait::async_trait; 3 | 4 | use crate::actions::{TandemAction, Theme}; 5 | 6 | pub(crate) struct ActionMigrate<'a> { 7 | theme: &'a Theme<'a>, 8 | #[allow(dead_code)] 9 | http_client: reqwest::Client, 10 | #[allow(dead_code)] 11 | plc: String, 12 | } 13 | 14 | impl<'a> ActionMigrate<'a> { 15 | pub(crate) fn new(theme: &'a Theme<'_>, http_client: &reqwest::Client, plc: &str) -> Self { 16 | Self { 17 | theme, 18 | http_client: http_client.clone(), 19 | plc: plc.to_string(), 20 | } 21 | } 22 | } 23 | 24 | #[async_trait] 25 | impl TandemAction for ActionMigrate<'_> { 26 | async fn run(&self) -> Result<()> { 27 | println!( 28 | "{}", 29 | self.theme 30 | .white_dim 31 | .apply_to("The 'Migrate' action performs a migration of your DID-PLC identity to a different PDS.") 32 | ); 33 | 34 | // TODO: User input DID 35 | // TODO: User input jwk 36 | // TODO: User input destination_pds 37 | // TODO: User input destination_password 38 | // TODO: Create session with destination_pds 39 | // TODO: Get the PLC credentails from the destination PDS /xrpc/com.atproto.identity.getRecommendedDidCredentials 40 | // TODO: Get last PLC operation for DID 41 | // TODO: Create operation with updated PDS keys and service endpoints 42 | // TODO: Sign operation with jwk 43 | // TODO: Submit operation to PLC 44 | Ok(()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/actions/prepare.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use async_trait::async_trait; 3 | use dialoguer::{Input, Password, Select}; 4 | use json_patch::{patch, Patch}; 5 | use serde_json::{from_value, json}; 6 | 7 | use crate::{ 8 | actions::{get_handle_input, TandemAction, Theme}, 9 | plc::did_plc_data, 10 | resolve::resolve_handle, 11 | xrpc::PdsClient, 12 | }; 13 | 14 | pub(crate) struct ActionPrepare<'a> { 15 | theme: &'a Theme<'a>, 16 | http_client: reqwest::Client, 17 | plc: String, 18 | } 19 | 20 | impl<'a> ActionPrepare<'a> { 21 | pub(crate) fn new(theme: &'a Theme<'_>, http_client: &reqwest::Client, plc: &str) -> Self { 22 | Self { 23 | theme, 24 | http_client: http_client.clone(), 25 | plc: plc.to_string(), 26 | } 27 | } 28 | } 29 | 30 | #[async_trait] 31 | impl TandemAction for ActionPrepare<'_> { 32 | async fn run(&self) -> Result<()> { 33 | println!( 34 | "{}", 35 | self.theme 36 | .white_dim 37 | .apply_to("The 'Install Tandem Key' action will generate a rotation key and update your DID-PLC document with it.") 38 | ); 39 | 40 | let handle = get_handle_input(self.theme.colorful_theme, "What is your handle?")?; 41 | 42 | println!( 43 | "{}", 44 | self.theme 45 | .yellow_bold 46 | .apply_to("Your password is required to authenticate with your PDS. This cannot be an application password or OAuth token.") 47 | ); 48 | 49 | let password = Password::with_theme(self.theme.colorful_theme) 50 | .with_prompt("What is your password?") 51 | .interact()?; 52 | 53 | let key_types = &["p256", "k256"]; 54 | 55 | let key_type = Select::with_theme(self.theme.colorful_theme) 56 | .with_prompt("Select key type") 57 | .default(0) 58 | .items(&key_types[..]) 59 | .interact()?; 60 | 61 | let key_positions = &["first", "last"]; 62 | 63 | let key_position = Select::with_theme(self.theme.colorful_theme) 64 | .with_prompt("Select key position") 65 | .default(0) 66 | .items(&key_positions[..]) 67 | .interact()?; 68 | 69 | let resolved_handle = resolve_handle(&self.http_client, &self.plc, &handle) 70 | .await 71 | .context("failed to resolve handle")?; 72 | 73 | println!( 74 | "{}", 75 | self.theme.green.apply_to(format!( 76 | "✔ Resolved {} ({}) known as {}", 77 | resolved_handle.did, 78 | resolved_handle.pds, 79 | resolved_handle.handles.join(" ") 80 | )), 81 | ); 82 | 83 | let pds_client = PdsClient::from_credentials( 84 | &self.http_client, 85 | &resolved_handle.pds, 86 | &resolved_handle.did, 87 | &password, 88 | ) 89 | .await 90 | .context("failed to authenticate against PDS")?; 91 | 92 | let (secret_pem, encoded_public_key) = if key_type == 0 { 93 | crate::crypto::p256::gen_key()? 94 | } else { 95 | crate::crypto::k256::gen_key()? 96 | }; 97 | 98 | println!( 99 | "{}", 100 | self.theme 101 | .yellow_bold 102 | .apply_to("Important! Securely store the following private key."), 103 | ); 104 | println!("{}", self.theme.red_bold.apply_to(&secret_pem)); 105 | 106 | let mut did_doc_data = did_plc_data(&self.http_client, &self.plc, &resolved_handle.did) 107 | .await 108 | .context("failed to get DID document")?; 109 | 110 | let key_path = if key_position == 0 { 111 | "/rotationKeys/0".to_string() 112 | } else { 113 | "/rotationKeys/-".to_string() 114 | }; 115 | 116 | let did_doc_data_patch: Patch = from_value(json!([ 117 | { "op": "add", "path": key_path, "value": format!("did:key:{}", encoded_public_key) } 118 | ])) 119 | .context("failed to create patch")?; 120 | 121 | patch(&mut did_doc_data, &did_doc_data_patch).context("failed to apply patch")?; 122 | 123 | println!("{}", self.theme.green.apply_to("✔ Created patch document")); 124 | println!( 125 | "{}", 126 | self.theme.white_dim.apply_to( 127 | serde_json::to_string_pretty(&did_doc_data) 128 | .context("failed to serialize DID document")? 129 | ) 130 | ); 131 | 132 | pds_client 133 | .request_plc_op_sig() 134 | .await 135 | .context("failed to request PLC signing operation")?; 136 | 137 | println!( 138 | "{}", 139 | self.theme.yellow_bold.apply_to( 140 | "Important! Check your email for a confirmation code. Enter it below to continue." 141 | ) 142 | ); 143 | 144 | let token = Input::::with_theme(self.theme.colorful_theme) 145 | .with_prompt("Confirmation code") 146 | .interact() 147 | .context("failed to get confirmation code")?; 148 | 149 | let plc_operation = pds_client 150 | .sign_plc_op(&did_doc_data, &token) 151 | .await 152 | .context("failed to request PLC signing operation")?; 153 | 154 | println!( 155 | "{}", 156 | self.theme.green.apply_to("✔ Acquired signed PLC operation") 157 | ); 158 | println!( 159 | "{}", 160 | self.theme.white_dim.apply_to( 161 | serde_json::to_string_pretty(&plc_operation) 162 | .context("failed to serialize PLC operation")? 163 | ) 164 | ); 165 | 166 | pds_client 167 | .submit_plc_op(&plc_operation) 168 | .await 169 | .context("failed to submit PLC operation")?; 170 | 171 | println!( 172 | "{}", 173 | self.theme 174 | .green 175 | .apply_to("✔ Submitted signed PLC operation") 176 | ); 177 | 178 | Ok(()) 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/bin/tandem.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use dialoguer::{console::Style, theme::ColorfulTheme, Confirm, Input, Select}; 3 | use std::{env, process::ExitCode}; 4 | use tandem::actions::{get_action, Theme, SUPPORTED_ACTIONS}; 5 | 6 | #[tokio::main] 7 | async fn main() -> ExitCode { 8 | if let Err(err) = real_main().await { 9 | let red_bold = Style::new().red().bold(); 10 | println!("{}: {:?}", red_bold.apply_to("Error"), err); 11 | return ExitCode::FAILURE; 12 | } 13 | 14 | let green_bold = Style::new().green().bold(); 15 | println!("{}", green_bold.apply_to("Success")); 16 | ExitCode::SUCCESS 17 | } 18 | 19 | async fn real_main() -> Result<()> { 20 | let args: Vec = env::args().skip(1).collect(); 21 | 22 | let display_help = args.iter().any(|arg| arg == "--help"); 23 | 24 | if display_help { 25 | println!("Usage: tandem [options]"); 26 | println!("Options:"); 27 | println!("\t--help\t\t\tDisplays this message."); 28 | println!("\t--ca-certificate=FILE\tAllows one or more CA certificate to be used for HTTPS connections."); 29 | return Ok(()); 30 | } 31 | 32 | let colorful_theme = ColorfulTheme { 33 | values_style: Style::new().white().bold(), 34 | ..ColorfulTheme::default() 35 | }; 36 | let theme = Theme { 37 | colorful_theme: &colorful_theme, 38 | red_bold: Style::new().red().bold(), 39 | yellow_bold: Style::new().yellow().bold(), 40 | green: Style::new().green(), 41 | white_dim: Style::new().white().dim(), 42 | }; 43 | 44 | println!("{} This tool will perform potentially dangerous operations on your behalf. Do not proceed unless you know what you are doing.", theme.red_bold.apply_to("Warning!")); 45 | 46 | if !Confirm::with_theme(theme.colorful_theme) 47 | .with_prompt("Do you want to proceed?") 48 | .default(true) 49 | .show_default(true) 50 | .wait_for_newline(true) 51 | .interact()? 52 | { 53 | return Ok(()); 54 | } 55 | 56 | let mut client_builder = reqwest::Client::builder(); 57 | 58 | for arg in &args { 59 | if let Some(ca_certificate) = arg.strip_prefix("--ca-certificate=") { 60 | let cert_data = std::fs::read(ca_certificate) 61 | .with_context(|| format!("failed to read CA certificate: {}", ca_certificate))?; 62 | let cert = reqwest::Certificate::from_pem(&cert_data) 63 | .with_context(|| format!("failed to parse CA certificate: {}", ca_certificate))?; 64 | client_builder = client_builder.add_root_certificate(cert); 65 | } 66 | } 67 | 68 | let http_client = client_builder 69 | .build() 70 | .context("failed to create HTTP client")?; 71 | 72 | let plc = Input::::with_theme(theme.colorful_theme) 73 | .with_prompt("PLC Directory") 74 | .default("plc.pyroclastic.cloud".parse().unwrap()) 75 | .interact()?; 76 | 77 | let selected_operation = Select::with_theme(theme.colorful_theme) 78 | .with_prompt("Supported Operations") 79 | .default(0) 80 | .items(&SUPPORTED_ACTIONS[..]) 81 | .interact()?; 82 | 83 | let action = get_action(selected_operation, &theme, &http_client, &plc)?; 84 | 85 | action.run().await 86 | } 87 | -------------------------------------------------------------------------------- /src/crypto.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Result}; 2 | use elliptic_curve::JwkEcKey; 3 | use serde_json::json; 4 | 5 | #[allow(dead_code)] 6 | pub(crate) fn validate(multibase_key: &str, signature: &[u8], content: &str) -> Result<()> { 7 | let (_, decoded_multibase_key) = multibase::decode(multibase_key)?; 8 | match &decoded_multibase_key[..2] { 9 | // secp256k1 10 | [0xe7, 0x01] => { 11 | let signature = ecdsa::Signature::from_slice(signature)?; 12 | let verifying_key = 13 | ::k256::ecdsa::VerifyingKey::from_sec1_bytes(&decoded_multibase_key[2..])?; 14 | ecdsa::signature::Verifier::verify(&verifying_key, content.as_bytes(), &signature)?; 15 | Ok(()) 16 | } 17 | // p256 18 | [0x80, 0x24] => { 19 | let signature = ecdsa::Signature::from_slice(signature)?; 20 | let verifying_key = 21 | ::p256::ecdsa::VerifyingKey::from_sec1_bytes(&decoded_multibase_key[2..])?; 22 | ecdsa::signature::Verifier::verify(&verifying_key, content.as_bytes(), &signature)?; 23 | Ok(()) 24 | } 25 | _ => Err(anyhow!( 26 | "invalid multibase: {:?}", 27 | &decoded_multibase_key[..2] 28 | )), 29 | } 30 | } 31 | 32 | pub fn sign_operation(jwk: &JwkEcKey, operation: &serde_json::Value) -> Result { 33 | let serialized_operation = serde_ipld_dagcbor::to_vec(operation)?; 34 | 35 | let signature_str = match jwk.crv() { 36 | "P-256" => p256::sign_operation(jwk, &serialized_operation), 37 | "secp256k1" => k256::sign_operation(jwk, &serialized_operation), 38 | _ => Err(anyhow!("unsupported curve")), 39 | }?; 40 | 41 | let mut signed_operation = operation 42 | .as_object() 43 | .expect("operation is an object") 44 | .clone(); 45 | 46 | signed_operation.insert( 47 | "sig".to_string(), 48 | serde_json::Value::String(signature_str.to_string()), 49 | ); 50 | 51 | Ok(json!(signed_operation)) 52 | } 53 | pub(crate) fn jwk_to_did_key(jwk: &JwkEcKey) -> Result { 54 | match jwk.crv() { 55 | "P-256" => p256::jwk_to_did_key(jwk), 56 | "secp256k1" => k256::jwk_to_did_key(jwk), 57 | _ => Err(anyhow!("unsupported curve")), 58 | } 59 | } 60 | 61 | pub(crate) mod p256 { 62 | 63 | use anyhow::Result; 64 | use base64::{engine::general_purpose, Engine as _}; 65 | use elliptic_curve::{sec1::ToEncodedPoint, JwkEcKey}; 66 | use p256::{ 67 | ecdsa::{signature::Signer, Signature, SigningKey}, 68 | SecretKey, 69 | }; 70 | 71 | pub(crate) fn gen_key() -> Result<(String, String)> { 72 | let secret_key: SecretKey = SecretKey::random(&mut rand::thread_rng()); 73 | 74 | let secret_jwk = secret_key.to_jwk_string().to_string(); 75 | 76 | let public_key = secret_key.public_key(); 77 | let encoded_point = public_key.to_encoded_point(true); 78 | 79 | let full = [&[0x80, 0x24], encoded_point.as_bytes()].concat(); 80 | 81 | let encoded_public_key = multibase::encode(multibase::Base::Base58Btc, full); 82 | 83 | Ok((secret_jwk, encoded_public_key)) 84 | } 85 | 86 | pub(crate) fn jwk_to_did_key(jwk: &JwkEcKey) -> Result { 87 | let secret_key: SecretKey = jwk.try_into()?; 88 | let public_key = secret_key.public_key(); 89 | let encoded_point = public_key.to_encoded_point(true); 90 | 91 | let full = [&[0x80, 0x24], encoded_point.as_bytes()].concat(); 92 | 93 | let encoded_public_key = multibase::encode(multibase::Base::Base58Btc, full); 94 | 95 | Ok(encoded_public_key) 96 | } 97 | 98 | pub(crate) fn sign_operation(jwk: &JwkEcKey, payload: &[u8]) -> Result { 99 | let secret_key: SecretKey = jwk.try_into()?; 100 | let signing_key: SigningKey = secret_key.into(); 101 | let signature: Signature = signing_key.try_sign(payload)?; 102 | Ok(general_purpose::URL_SAFE_NO_PAD.encode(signature.to_bytes())) 103 | } 104 | } 105 | 106 | pub(crate) mod k256 { 107 | 108 | use anyhow::Result; 109 | use base64::{engine::general_purpose, Engine as _}; 110 | use elliptic_curve::{sec1::ToEncodedPoint, JwkEcKey}; 111 | use k256::{ 112 | ecdsa::{signature::Signer, Signature, SigningKey}, 113 | SecretKey, 114 | }; 115 | 116 | pub(crate) fn gen_key() -> Result<(String, String)> { 117 | let secret_key: k256::SecretKey = k256::SecretKey::random(&mut rand::thread_rng()); 118 | 119 | let secret_jwk = secret_key.to_jwk_string().to_string(); 120 | 121 | let public_key = secret_key.public_key(); 122 | let encoded_point = public_key.to_encoded_point(true); 123 | 124 | let full = [&[0xe7, 0x01], encoded_point.as_bytes()].concat(); 125 | 126 | let encoded_public_key = multibase::encode(multibase::Base::Base58Btc, full); 127 | 128 | Ok((secret_jwk, encoded_public_key)) 129 | } 130 | 131 | pub(crate) fn jwk_to_did_key(jwk: &JwkEcKey) -> Result { 132 | let secret_key: SecretKey = jwk.try_into()?; 133 | let public_key = secret_key.public_key(); 134 | let encoded_point = public_key.to_encoded_point(true); 135 | 136 | let full = [&[0xe7, 0x01], encoded_point.as_bytes()].concat(); 137 | 138 | let encoded_public_key = multibase::encode(multibase::Base::Base58Btc, full); 139 | 140 | Ok(encoded_public_key) 141 | } 142 | 143 | pub(crate) fn sign_operation(jwk: &JwkEcKey, payload: &[u8]) -> Result { 144 | let secret_key: SecretKey = jwk.try_into()?; 145 | let signing_key: SigningKey = secret_key.into(); 146 | let signature: Signature = signing_key.try_sign(payload)?; 147 | Ok(general_purpose::URL_SAFE_NO_PAD.encode(signature.to_bytes())) 148 | } 149 | } 150 | 151 | #[cfg(test)] 152 | mod tests { 153 | 154 | use anyhow::Result; 155 | use chrono::Utc; 156 | use ecdsa::signature::Signer; 157 | 158 | #[tokio::test] 159 | async fn test_validate_p256() -> Result<()> { 160 | let (secret_pem, encoded_public_key) = super::p256::gen_key()?; 161 | 162 | let secret_key: p256::SecretKey = elliptic_curve::SecretKey::from_sec1_pem(&secret_pem)?; 163 | 164 | let now = Utc::now(); 165 | let content = format!("hello world {}", now); 166 | 167 | let signing_key: p256::ecdsa::SigningKey = p256::ecdsa::SigningKey::from(secret_key); 168 | 169 | let signature: p256::ecdsa::Signature = signing_key.try_sign(content.as_bytes())?; 170 | 171 | super::validate(&encoded_public_key, &signature.to_bytes(), &content)?; 172 | 173 | Ok(()) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod actions; 2 | pub(crate) mod crypto; 3 | pub(crate) mod plc; 4 | pub(crate) mod resolve; 5 | pub(crate) mod xrpc; 6 | -------------------------------------------------------------------------------- /src/plc.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Context, Result}; 2 | use chrono::{DateTime, Utc}; 3 | use serde::Deserialize; 4 | 5 | #[derive(Clone, Deserialize)] 6 | #[serde(rename_all = "camelCase")] 7 | struct PlcService { 8 | #[serde(rename = "type")] 9 | service_type: String, 10 | 11 | service_endpoint: String, 12 | } 13 | 14 | #[derive(Clone, Deserialize)] 15 | #[serde(rename_all = "camelCase")] 16 | struct ResolveDid { 17 | also_known_as: Vec, 18 | service: Vec, 19 | } 20 | 21 | #[derive(Clone, Deserialize)] 22 | struct AuditEntry { 23 | operation: serde_json::Value, 24 | cid: String, 25 | #[serde(rename = "createdAt")] 26 | created_at: DateTime, 27 | } 28 | 29 | pub(crate) async fn plc_query( 30 | http_client: &reqwest::Client, 31 | plc_hostname: &str, 32 | did: &str, 33 | ) -> Result<(Vec, Vec)> { 34 | let url = format!("https://{}/{}", plc_hostname, did); 35 | 36 | let resolved_did: ResolveDid = http_client.get(url).send().await?.json().await?; 37 | 38 | let handles = resolved_did 39 | .also_known_as 40 | .iter() 41 | .map(|value| { 42 | if let Some(handle) = value.strip_prefix("at://") { 43 | handle.to_string() 44 | } else { 45 | value.to_string() 46 | } 47 | }) 48 | .collect::>(); 49 | 50 | let pds = resolved_did 51 | .service 52 | .iter() 53 | .filter_map(|value| { 54 | if value.service_type == "AtprotoPersonalDataServer" { 55 | Some(value.service_endpoint.clone()) 56 | } else { 57 | None 58 | } 59 | }) 60 | .collect::>(); 61 | 62 | Ok((pds, handles)) 63 | } 64 | 65 | pub(crate) async fn did_plc_data( 66 | http_client: &reqwest::Client, 67 | plc_hostname: &str, 68 | did: &str, 69 | ) -> Result { 70 | let url = format!("https://{}/{}/data", plc_hostname, did); 71 | 72 | http_client 73 | .get(url) 74 | .send() 75 | .await 76 | .context("unable to get DID document")? 77 | .json() 78 | .await 79 | .context("unable to deserialize DID document") 80 | } 81 | 82 | pub(crate) async fn did_plc_last_operation( 83 | http_client: &reqwest::Client, 84 | plc_hostname: &str, 85 | did: &str, 86 | ) -> Result<(String, serde_json::Value)> { 87 | let url = format!("https://{}/{}/log/audit", plc_hostname, did); 88 | 89 | println!("url: {}", url); 90 | 91 | let mut operations: Vec = http_client 92 | .get(url) 93 | .send() 94 | .await 95 | .context("unable to get DID audit log")? 96 | .json() 97 | .await 98 | .context("unable to deserialize DID audit log")?; 99 | 100 | operations.sort_by_key(|entry| entry.created_at); 101 | 102 | let selected = operations 103 | .last() 104 | .cloned() 105 | .ok_or_else(|| anyhow!("no operations found"))?; 106 | 107 | Ok((selected.cid, selected.operation)) 108 | } 109 | 110 | pub(crate) async fn submit_operation( 111 | http_client: &reqwest::Client, 112 | plc_hostname: &str, 113 | did: &str, 114 | operation: &serde_json::Value, 115 | ) -> Result<()> { 116 | let url = format!("https://{}/{}", plc_hostname, did); 117 | 118 | http_client 119 | .post(url) 120 | .json(operation) 121 | .send() 122 | .await 123 | .context("unable to submit operation") 124 | .and_then(|response| { 125 | let status = response.status(); 126 | if status.is_success() { 127 | Ok(()) 128 | } else { 129 | Err(anyhow!("response {}", status)) 130 | } 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /src/resolve.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Result}; 2 | use hickory_resolver::{ 3 | config::{ResolverConfig, ResolverOpts}, 4 | AsyncResolver, 5 | }; 6 | use std::collections::HashSet; 7 | use std::time::Duration; 8 | 9 | use crate::plc::plc_query; 10 | 11 | pub async fn resolve_handle_dns(handle: &str) -> Result { 12 | let lookup_dns = format!("_atproto.{}", handle); 13 | let resolver = AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()); 14 | 15 | let lookup = resolver.txt_lookup(lookup_dns.clone()).await?; 16 | 17 | let dids = lookup 18 | .iter() 19 | .filter_map(|record| { 20 | record 21 | .to_string() 22 | .strip_prefix("did=") 23 | .map(|did| did.to_string()) 24 | }) 25 | .collect::>(); 26 | 27 | if dids.len() > 1 { 28 | return Err(anyhow!("Multiple records found for handle {}", handle)); 29 | } 30 | 31 | dids.iter() 32 | .next() 33 | .cloned() 34 | .ok_or(anyhow!("No records found for handle {}", handle)) 35 | } 36 | 37 | pub async fn resolve_handle_http(http_client: &reqwest::Client, handle: &str) -> Result { 38 | let lookup_url = format!("https://{}/.well-known/atproto-did", handle); 39 | 40 | http_client 41 | .get(lookup_url.clone()) 42 | .timeout(Duration::from_secs(10)) 43 | .send() 44 | .await? 45 | .text() 46 | .await 47 | .map_err(|err| err.into()) 48 | .and_then(|body| { 49 | if body.starts_with("did:") { 50 | Ok(body.to_string()) 51 | } else { 52 | Err(anyhow!("Invalid response from {}", lookup_url)) 53 | } 54 | }) 55 | } 56 | 57 | pub struct ResolvedHandle { 58 | pub did: String, 59 | pub pds: String, 60 | pub handles: Vec, 61 | } 62 | 63 | pub async fn resolve_handle( 64 | http_client: &reqwest::Client, 65 | plc_hostname: &str, 66 | subject: &str, 67 | ) -> Result { 68 | let mut resolved_dids: HashSet = HashSet::new(); 69 | let mut unresolved_dids: HashSet = HashSet::new(); 70 | let mut resolved_handles: HashSet = HashSet::new(); 71 | let mut unresolved_handles: HashSet = HashSet::new(); 72 | 73 | let mut found_pds: HashSet = HashSet::new(); 74 | let mut found_handles: HashSet = HashSet::new(); 75 | let mut found_dids: HashSet = HashSet::new(); 76 | 77 | if subject.starts_with("did:") { 78 | unresolved_dids.insert(subject.to_string()); 79 | } else { 80 | unresolved_handles.insert(subject.to_string()); 81 | } 82 | 83 | let mut iterations = 0; 84 | loop { 85 | iterations += 1; 86 | if iterations > 10 { 87 | return Err(anyhow!("resolve_handle exceeded max iteration depth")); 88 | } 89 | 90 | let next_did = unresolved_dids.difference(&resolved_dids).next().cloned(); 91 | let next_handle = &unresolved_handles 92 | .difference(&resolved_handles) 93 | .next() 94 | .cloned(); 95 | if next_did.is_none() && next_handle.is_none() { 96 | break; 97 | } 98 | 99 | if let Some(next_did) = next_did { 100 | resolved_dids.insert(next_did.to_string()); 101 | let query_res = plc_query(http_client, plc_hostname, &next_did).await; 102 | if let Ok((pds, handles)) = query_res { 103 | found_pds.extend(pds.clone()); 104 | found_handles.extend(handles.clone()); 105 | unresolved_handles.extend(handles); 106 | } 107 | } 108 | 109 | if let Some(next_handle) = next_handle { 110 | resolved_handles.insert(next_handle.to_string()); 111 | let http_resolve = resolve_handle_http(http_client, next_handle).await; 112 | if let Ok(resolved_did) = http_resolve { 113 | unresolved_dids.insert(resolved_did.clone()); 114 | found_dids.insert(resolved_did); 115 | } 116 | 117 | let dns_resolve = resolve_handle_dns(next_handle).await; 118 | if let Ok(resolved_did) = dns_resolve { 119 | unresolved_dids.insert(resolved_did.clone()); 120 | found_dids.insert(resolved_did); 121 | } 122 | } 123 | } 124 | 125 | if found_dids.len() > 1 { 126 | return Err(anyhow!("Multiple DIDs found for subject {}", subject)); 127 | } 128 | if found_handles.is_empty() { 129 | return Err(anyhow!("No handles found for subject {}", subject)); 130 | } 131 | if found_pds.len() > 1 { 132 | return Err(anyhow!("Multiple PDSs found for subject {}", subject)); 133 | } 134 | 135 | let found_did = found_dids 136 | .iter() 137 | .next() 138 | .cloned() 139 | .ok_or(anyhow!("No DIDs found for subject {}", subject))?; 140 | let found_pds = found_pds 141 | .iter() 142 | .next() 143 | .cloned() 144 | .ok_or(anyhow!("No PDSs found for subject {}", subject))?; 145 | 146 | Ok(ResolvedHandle { 147 | did: found_did, 148 | pds: found_pds, 149 | handles: found_handles.iter().cloned().collect(), 150 | }) 151 | } 152 | -------------------------------------------------------------------------------- /src/xrpc.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use anyhow::{anyhow, Result}; 4 | use json_patch::merge; 5 | use serde_json::json; 6 | 7 | pub struct PdsClient { 8 | pub http_client: reqwest::Client, 9 | pub pds: String, 10 | pub access_jwt: String, 11 | } 12 | 13 | #[derive(serde::Deserialize)] 14 | enum WrappedCredentialResponse { 15 | #[serde(untagged)] 16 | CredentialResponse { 17 | #[serde(rename = "accessJwt")] 18 | access_jwt: String, 19 | _handle: String, 20 | _did: String, 21 | }, 22 | 23 | #[serde(untagged)] 24 | Other { 25 | #[serde(flatten)] 26 | extra: HashMap, 27 | }, 28 | } 29 | 30 | #[derive(serde::Deserialize)] 31 | enum WrappedDescribeServerResponse { 32 | #[serde(untagged)] 33 | DescribeServerResponse { 34 | did: String, 35 | #[serde(rename = "inviteCodeRequired")] 36 | invite_code_required: bool, 37 | #[serde(rename = "availableUserDomains")] 38 | available_user_domains: Vec, 39 | }, 40 | 41 | #[serde(untagged)] 42 | Other { 43 | #[serde(flatten)] 44 | extra: HashMap, 45 | }, 46 | } 47 | 48 | #[derive(serde::Deserialize)] 49 | enum WrappedCreateAccountResponse { 50 | #[serde(untagged)] 51 | CreateAccountResponse { 52 | did: String, 53 | handle: String, 54 | #[serde(rename = "accessJwt")] 55 | access_jwt: String, 56 | }, 57 | 58 | #[serde(untagged)] 59 | Other { 60 | #[serde(flatten)] 61 | extra: HashMap, 62 | }, 63 | } 64 | 65 | #[derive(serde::Deserialize, serde::Serialize)] 66 | pub struct WrappedOperation { 67 | pub operation: serde_json::Value, 68 | } 69 | 70 | impl PdsClient { 71 | pub async fn from_credentials( 72 | http_client: &reqwest::Client, 73 | pds: &str, 74 | did: &str, 75 | password: &str, 76 | ) -> Result { 77 | let create_session_uri = format!("{}/xrpc/com.atproto.server.createSession", pds); 78 | 79 | let mut body = HashMap::new(); 80 | body.insert("identifier", did); 81 | body.insert("password", password); 82 | 83 | let response: WrappedCredentialResponse = http_client 84 | .post(create_session_uri) 85 | .json(&body) 86 | .send() 87 | .await? 88 | .json() 89 | .await?; 90 | 91 | let access_jwt = match response { 92 | WrappedCredentialResponse::CredentialResponse { access_jwt, .. } => Ok(access_jwt), 93 | WrappedCredentialResponse::Other { extra } => { 94 | Err(anyhow!("Unexpected response from PDS: {:?}", extra)) 95 | } 96 | }?; 97 | 98 | Ok(Self { 99 | http_client: http_client.clone(), 100 | pds: pds.to_string(), 101 | access_jwt, 102 | }) 103 | } 104 | 105 | pub async fn request_plc_op_sig(&self) -> Result<()> { 106 | let request_plc_op_sig_uri = format!( 107 | "{}/xrpc/com.atproto.identity.requestPlcOperationSignature", 108 | self.pds 109 | ); 110 | 111 | self.http_client 112 | .post(request_plc_op_sig_uri) 113 | .header("Authorization", format!("Bearer {}", self.access_jwt)) 114 | .send() 115 | .await 116 | .map(|_| ()) 117 | .map_err(|err| err.into()) 118 | } 119 | 120 | pub async fn sign_plc_op( 121 | &self, 122 | did_doc: &serde_json::Value, 123 | token: &str, 124 | ) -> Result { 125 | let request_plc_op_sig_uri = 126 | format!("{}/xrpc/com.atproto.identity.signPlcOperation", self.pds); 127 | 128 | let token_patch = json!({ 129 | "token": token, 130 | }); 131 | let mut request_body = did_doc.clone(); 132 | merge(&mut request_body, &token_patch); 133 | 134 | let wrapped_operation: WrappedOperation = self 135 | .http_client 136 | .post(request_plc_op_sig_uri) 137 | .header("Authorization", format!("Bearer {}", self.access_jwt)) 138 | .json(&request_body) 139 | .send() 140 | .await? 141 | .json() 142 | .await?; 143 | Ok(wrapped_operation.operation) 144 | } 145 | 146 | pub async fn submit_plc_op(&self, operation: &serde_json::Value) -> Result<()> { 147 | let submit_plc_op_uri = 148 | format!("{}/xrpc/com.atproto.identity.submitPlcOperation", self.pds); 149 | 150 | let wrapped_operation = WrappedOperation { 151 | operation: operation.clone(), 152 | }; 153 | 154 | self.http_client 155 | .post(submit_plc_op_uri) 156 | .header("Authorization", format!("Bearer {}", self.access_jwt)) 157 | .json(&wrapped_operation) 158 | .send() 159 | .await 160 | .map(|_| ()) 161 | .map_err(|err| err.into()) 162 | } 163 | } 164 | 165 | pub(crate) async fn describe_server( 166 | http_client: &reqwest::Client, 167 | pds_hostname: &str, 168 | ) -> Result<(String, bool, Vec)> { 169 | let uri = format!( 170 | "https://{}/xrpc/com.atproto.server.describeServer", 171 | pds_hostname 172 | ); 173 | let wrapped_response: WrappedDescribeServerResponse = 174 | http_client.get(uri).send().await?.json().await?; 175 | 176 | match wrapped_response { 177 | WrappedDescribeServerResponse::DescribeServerResponse { 178 | did, 179 | invite_code_required, 180 | available_user_domains, 181 | } => Ok((did, invite_code_required, available_user_domains)), 182 | WrappedDescribeServerResponse::Other { extra } => { 183 | println!("Unexpected response from PDS: {:?}", extra); 184 | Err(anyhow!("Unexpected response from PDS")) 185 | } 186 | } 187 | } 188 | 189 | #[derive(serde::Serialize)] 190 | struct CreateAccountRequest { 191 | handle: String, 192 | email: String, 193 | #[serde(skip_serializing_if = "Option::is_none")] 194 | did: Option, 195 | #[serde(rename = "inviteCode", skip_serializing_if = "Option::is_none")] 196 | invite_code: Option, 197 | password: String, 198 | #[serde(rename = "recoveryKey")] 199 | recovery_key: String, 200 | } 201 | 202 | // TODO: Use a request object here. 203 | #[allow(clippy::too_many_arguments)] 204 | pub(crate) async fn create_account( 205 | http_client: &reqwest::Client, 206 | pds_hostname: &str, 207 | handle: &str, 208 | password: &str, 209 | email: &str, 210 | recovery_key: &str, 211 | invite_code: Option, 212 | did: Option, 213 | ) -> Result<(String, String, String)> { 214 | let uri = format!( 215 | "https://{}/xrpc/com.atproto.server.createAccount", 216 | pds_hostname 217 | ); 218 | 219 | let payload = CreateAccountRequest { 220 | handle: handle.to_string(), 221 | email: email.to_string(), 222 | did, 223 | invite_code, 224 | password: password.to_string(), 225 | recovery_key: recovery_key.to_string(), 226 | }; 227 | 228 | let wrapped_response: WrappedCreateAccountResponse = http_client 229 | .post(uri) 230 | .json(&payload) 231 | .send() 232 | .await? 233 | .json() 234 | .await?; 235 | 236 | match wrapped_response { 237 | WrappedCreateAccountResponse::CreateAccountResponse { 238 | did, 239 | handle, 240 | access_jwt, 241 | } => Ok((did, handle, access_jwt)), 242 | WrappedCreateAccountResponse::Other { extra } => { 243 | println!("Unexpected response from PDS: {:?}", extra); 244 | Err(anyhow!("Unexpected response from PDS")) 245 | } 246 | } 247 | } 248 | --------------------------------------------------------------------------------