A library for ATProtocol identities.

Initial implementation of atproto-identity Rust library

Complete AT Protocol identity management library with DID resolution, handle resolution, and identity document management across multiple DID methods (did:plc and did:web). Includes comprehensive CLI tool, structured error handling, and full test coverage.

Signed-off-by: Nick Gerakines <nick.gerakines@gmail.com>

Nick Gerakines 4f969af3

+8
.gitignore
··· 1 + /target 2 + 3 + 4 + # Added by cargo 5 + # 6 + # already existing elements were commented out 7 + 8 + #/target
+70
CLAUDE.md
··· 1 + # CLAUDE.md 2 + 3 + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 + 5 + ## Project Overview 6 + 7 + This is `atproto-identity`, a comprehensive Rust library for AT Protocol identity management. The project provides full functionality for DID resolution, handle resolution, and identity document management across multiple DID methods. 8 + 9 + ## Common Commands 10 + 11 + - **Build**: `cargo build` 12 + - **Run tests**: `cargo test` 13 + - **Run specific test**: `cargo test test_name` 14 + - **Check code**: `cargo check` 15 + - **Format code**: `cargo fmt` 16 + - **Lint**: `cargo clippy` 17 + - **Run CLI tool**: `cargo run --bin atproto-identity-resolve -- <handle_or_did>` 18 + - **Run CLI with DID document**: `cargo run --bin atproto-identity-resolve -- --did-document <handle_or_did>` 19 + 20 + ## Architecture 21 + 22 + A comprehensive Rust library with: 23 + - Modular architecture with 7 core modules (resolve, plc, web, model, validation, config, errors) 24 + - Complete CLI tool for identity resolution (`atproto-identity-resolve`) 25 + - Rust edition 2021 with modern async/await patterns 26 + - Comprehensive error handling with structured error types 27 + - Multiple external dependencies for HTTP, DNS, JSON, and cryptographic operations 28 + - Full test coverage with unit tests for all modules 29 + 30 + ## Error Handling 31 + 32 + All error strings must use this format: 33 + 34 + error-atproto-identity-<domain>-<number> <message>: <details> 35 + 36 + Example errors: 37 + 38 + * error-atproto-identity-resolve-1 Multiple DIDs resolved for method 39 + * error-atproto-identity-plc-1 HTTP request failed: https://google.com/ Not Found 40 + * error-did-web-1 Invalid DID format: missing 'did:web:' prefix 41 + 42 + Errors should be represented as enums using the `thiserror` library when possible using `src/errors.rs` as a reference and example. 43 + 44 + Avoid creating new errors with the `anyhow!(...)` macro. 45 + 46 + When a function call would return `anyhow::Error`, use the following pattern to log the error in addition to any code specific handling that must occur 47 + 48 + ``` 49 + If let Err(err) = result { 50 + tracing::error!(error = ?error, "Helpful contextual log line."); 51 + } 52 + ``` 53 + 54 + ### Logging 55 + 56 + Use tracing for structured logging. 57 + 58 + Async calls should be instrumented using the `.instrument()` that references the `use tracing::Instrument;` trait. 59 + 60 + ## Module Structure 61 + 62 + - **`src/lib.rs`**: Main library exports 63 + - **`src/resolve.rs`**: Core resolution logic for handles and DIDs, DNS/HTTP resolution 64 + - **`src/plc.rs`**: PLC directory client for did:plc resolution 65 + - **`src/web.rs`**: Web DID client for did:web resolution and URL conversion 66 + - **`src/model.rs`**: Data structures for DID documents and AT Protocol entities 67 + - **`src/validation.rs`**: Input validation for handles and DIDs 68 + - **`src/config.rs`**: Configuration management and environment variable handling 69 + - **`src/errors.rs`**: Structured error types following project conventions 70 + - **`src/bin/atproto-identity-resolve.rs`**: CLI tool for identity resolution
+2463
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "addr2line" 7 + version = "0.24.2" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 + dependencies = [ 11 + "gimli", 12 + ] 13 + 14 + [[package]] 15 + name = "adler2" 16 + version = "2.0.0" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 + 20 + [[package]] 21 + name = "aho-corasick" 22 + version = "1.1.3" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 + dependencies = [ 26 + "memchr", 27 + ] 28 + 29 + [[package]] 30 + name = "anyhow" 31 + version = "1.0.98" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 34 + 35 + [[package]] 36 + name = "async-trait" 37 + version = "0.1.88" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" 40 + dependencies = [ 41 + "proc-macro2", 42 + "quote", 43 + "syn", 44 + ] 45 + 46 + [[package]] 47 + name = "atomic-waker" 48 + version = "1.1.2" 49 + source = "registry+https://github.com/rust-lang/crates.io-index" 50 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 51 + 52 + [[package]] 53 + name = "atproto-identity" 54 + version = "0.1.0" 55 + dependencies = [ 56 + "anyhow", 57 + "futures-util", 58 + "hickory-resolver", 59 + "reqwest", 60 + "serde", 61 + "serde_json", 62 + "thiserror 2.0.12", 63 + "tokio", 64 + "tracing", 65 + ] 66 + 67 + [[package]] 68 + name = "autocfg" 69 + version = "1.4.0" 70 + source = "registry+https://github.com/rust-lang/crates.io-index" 71 + checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 72 + 73 + [[package]] 74 + name = "backtrace" 75 + version = "0.3.75" 76 + source = "registry+https://github.com/rust-lang/crates.io-index" 77 + checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 78 + dependencies = [ 79 + "addr2line", 80 + "cfg-if", 81 + "libc", 82 + "miniz_oxide", 83 + "object", 84 + "rustc-demangle", 85 + "windows-targets 0.52.6", 86 + ] 87 + 88 + [[package]] 89 + name = "base64" 90 + version = "0.22.1" 91 + source = "registry+https://github.com/rust-lang/crates.io-index" 92 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 93 + 94 + [[package]] 95 + name = "bitflags" 96 + version = "2.9.1" 97 + source = "registry+https://github.com/rust-lang/crates.io-index" 98 + checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 99 + 100 + [[package]] 101 + name = "bumpalo" 102 + version = "3.17.0" 103 + source = "registry+https://github.com/rust-lang/crates.io-index" 104 + checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 105 + 106 + [[package]] 107 + name = "bytes" 108 + version = "1.10.1" 109 + source = "registry+https://github.com/rust-lang/crates.io-index" 110 + checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 111 + 112 + [[package]] 113 + name = "cc" 114 + version = "1.2.24" 115 + source = "registry+https://github.com/rust-lang/crates.io-index" 116 + checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" 117 + dependencies = [ 118 + "shlex", 119 + ] 120 + 121 + [[package]] 122 + name = "cfg-if" 123 + version = "1.0.0" 124 + source = "registry+https://github.com/rust-lang/crates.io-index" 125 + checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 126 + 127 + [[package]] 128 + name = "cfg_aliases" 129 + version = "0.2.1" 130 + source = "registry+https://github.com/rust-lang/crates.io-index" 131 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 132 + 133 + [[package]] 134 + name = "core-foundation" 135 + version = "0.9.4" 136 + source = "registry+https://github.com/rust-lang/crates.io-index" 137 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 138 + dependencies = [ 139 + "core-foundation-sys", 140 + "libc", 141 + ] 142 + 143 + [[package]] 144 + name = "core-foundation-sys" 145 + version = "0.8.7" 146 + source = "registry+https://github.com/rust-lang/crates.io-index" 147 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 148 + 149 + [[package]] 150 + name = "critical-section" 151 + version = "1.2.0" 152 + source = "registry+https://github.com/rust-lang/crates.io-index" 153 + checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 154 + 155 + [[package]] 156 + name = "crossbeam-channel" 157 + version = "0.5.15" 158 + source = "registry+https://github.com/rust-lang/crates.io-index" 159 + checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" 160 + dependencies = [ 161 + "crossbeam-utils", 162 + ] 163 + 164 + [[package]] 165 + name = "crossbeam-epoch" 166 + version = "0.9.18" 167 + source = "registry+https://github.com/rust-lang/crates.io-index" 168 + checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 169 + dependencies = [ 170 + "crossbeam-utils", 171 + ] 172 + 173 + [[package]] 174 + name = "crossbeam-utils" 175 + version = "0.8.21" 176 + source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 178 + 179 + [[package]] 180 + name = "data-encoding" 181 + version = "2.9.0" 182 + source = "registry+https://github.com/rust-lang/crates.io-index" 183 + checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 184 + 185 + [[package]] 186 + name = "displaydoc" 187 + version = "0.2.5" 188 + source = "registry+https://github.com/rust-lang/crates.io-index" 189 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 190 + dependencies = [ 191 + "proc-macro2", 192 + "quote", 193 + "syn", 194 + ] 195 + 196 + [[package]] 197 + name = "encoding_rs" 198 + version = "0.8.35" 199 + source = "registry+https://github.com/rust-lang/crates.io-index" 200 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 201 + dependencies = [ 202 + "cfg-if", 203 + ] 204 + 205 + [[package]] 206 + name = "enum-as-inner" 207 + version = "0.6.1" 208 + source = "registry+https://github.com/rust-lang/crates.io-index" 209 + checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 210 + dependencies = [ 211 + "heck", 212 + "proc-macro2", 213 + "quote", 214 + "syn", 215 + ] 216 + 217 + [[package]] 218 + name = "equivalent" 219 + version = "1.0.2" 220 + source = "registry+https://github.com/rust-lang/crates.io-index" 221 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 222 + 223 + [[package]] 224 + name = "errno" 225 + version = "0.3.12" 226 + source = "registry+https://github.com/rust-lang/crates.io-index" 227 + checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" 228 + dependencies = [ 229 + "libc", 230 + "windows-sys 0.59.0", 231 + ] 232 + 233 + [[package]] 234 + name = "fastrand" 235 + version = "2.3.0" 236 + source = "registry+https://github.com/rust-lang/crates.io-index" 237 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 238 + 239 + [[package]] 240 + name = "fnv" 241 + version = "1.0.7" 242 + source = "registry+https://github.com/rust-lang/crates.io-index" 243 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 244 + 245 + [[package]] 246 + name = "foreign-types" 247 + version = "0.3.2" 248 + source = "registry+https://github.com/rust-lang/crates.io-index" 249 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 250 + dependencies = [ 251 + "foreign-types-shared", 252 + ] 253 + 254 + [[package]] 255 + name = "foreign-types-shared" 256 + version = "0.1.1" 257 + source = "registry+https://github.com/rust-lang/crates.io-index" 258 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 259 + 260 + [[package]] 261 + name = "form_urlencoded" 262 + version = "1.2.1" 263 + source = "registry+https://github.com/rust-lang/crates.io-index" 264 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 265 + dependencies = [ 266 + "percent-encoding", 267 + ] 268 + 269 + [[package]] 270 + name = "futures-channel" 271 + version = "0.3.31" 272 + source = "registry+https://github.com/rust-lang/crates.io-index" 273 + checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 274 + dependencies = [ 275 + "futures-core", 276 + ] 277 + 278 + [[package]] 279 + name = "futures-core" 280 + version = "0.3.31" 281 + source = "registry+https://github.com/rust-lang/crates.io-index" 282 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 283 + 284 + [[package]] 285 + name = "futures-io" 286 + version = "0.3.31" 287 + source = "registry+https://github.com/rust-lang/crates.io-index" 288 + checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 289 + 290 + [[package]] 291 + name = "futures-macro" 292 + version = "0.3.31" 293 + source = "registry+https://github.com/rust-lang/crates.io-index" 294 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 295 + dependencies = [ 296 + "proc-macro2", 297 + "quote", 298 + "syn", 299 + ] 300 + 301 + [[package]] 302 + name = "futures-sink" 303 + version = "0.3.31" 304 + source = "registry+https://github.com/rust-lang/crates.io-index" 305 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 306 + 307 + [[package]] 308 + name = "futures-task" 309 + version = "0.3.31" 310 + source = "registry+https://github.com/rust-lang/crates.io-index" 311 + checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 312 + 313 + [[package]] 314 + name = "futures-util" 315 + version = "0.3.31" 316 + source = "registry+https://github.com/rust-lang/crates.io-index" 317 + checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 318 + dependencies = [ 319 + "futures-core", 320 + "futures-macro", 321 + "futures-task", 322 + "pin-project-lite", 323 + "pin-utils", 324 + "slab", 325 + ] 326 + 327 + [[package]] 328 + name = "generator" 329 + version = "0.8.5" 330 + source = "registry+https://github.com/rust-lang/crates.io-index" 331 + checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" 332 + dependencies = [ 333 + "cc", 334 + "cfg-if", 335 + "libc", 336 + "log", 337 + "rustversion", 338 + "windows", 339 + ] 340 + 341 + [[package]] 342 + name = "getrandom" 343 + version = "0.2.16" 344 + source = "registry+https://github.com/rust-lang/crates.io-index" 345 + checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 346 + dependencies = [ 347 + "cfg-if", 348 + "js-sys", 349 + "libc", 350 + "wasi 0.11.0+wasi-snapshot-preview1", 351 + "wasm-bindgen", 352 + ] 353 + 354 + [[package]] 355 + name = "getrandom" 356 + version = "0.3.3" 357 + source = "registry+https://github.com/rust-lang/crates.io-index" 358 + checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 359 + dependencies = [ 360 + "cfg-if", 361 + "js-sys", 362 + "libc", 363 + "r-efi", 364 + "wasi 0.14.2+wasi-0.2.4", 365 + "wasm-bindgen", 366 + ] 367 + 368 + [[package]] 369 + name = "gimli" 370 + version = "0.31.1" 371 + source = "registry+https://github.com/rust-lang/crates.io-index" 372 + checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 373 + 374 + [[package]] 375 + name = "h2" 376 + version = "0.4.10" 377 + source = "registry+https://github.com/rust-lang/crates.io-index" 378 + checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" 379 + dependencies = [ 380 + "atomic-waker", 381 + "bytes", 382 + "fnv", 383 + "futures-core", 384 + "futures-sink", 385 + "http", 386 + "indexmap", 387 + "slab", 388 + "tokio", 389 + "tokio-util", 390 + "tracing", 391 + ] 392 + 393 + [[package]] 394 + name = "hashbrown" 395 + version = "0.15.3" 396 + source = "registry+https://github.com/rust-lang/crates.io-index" 397 + checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 398 + 399 + [[package]] 400 + name = "heck" 401 + version = "0.5.0" 402 + source = "registry+https://github.com/rust-lang/crates.io-index" 403 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 404 + 405 + [[package]] 406 + name = "hickory-proto" 407 + version = "0.25.2" 408 + source = "registry+https://github.com/rust-lang/crates.io-index" 409 + checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" 410 + dependencies = [ 411 + "async-trait", 412 + "cfg-if", 413 + "data-encoding", 414 + "enum-as-inner", 415 + "futures-channel", 416 + "futures-io", 417 + "futures-util", 418 + "idna", 419 + "ipnet", 420 + "once_cell", 421 + "rand", 422 + "ring", 423 + "thiserror 2.0.12", 424 + "tinyvec", 425 + "tokio", 426 + "tracing", 427 + "url", 428 + ] 429 + 430 + [[package]] 431 + name = "hickory-resolver" 432 + version = "0.25.2" 433 + source = "registry+https://github.com/rust-lang/crates.io-index" 434 + checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" 435 + dependencies = [ 436 + "cfg-if", 437 + "futures-util", 438 + "hickory-proto", 439 + "ipconfig", 440 + "moka", 441 + "once_cell", 442 + "parking_lot", 443 + "rand", 444 + "resolv-conf", 445 + "smallvec", 446 + "thiserror 2.0.12", 447 + "tokio", 448 + "tracing", 449 + ] 450 + 451 + [[package]] 452 + name = "http" 453 + version = "1.3.1" 454 + source = "registry+https://github.com/rust-lang/crates.io-index" 455 + checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 456 + dependencies = [ 457 + "bytes", 458 + "fnv", 459 + "itoa", 460 + ] 461 + 462 + [[package]] 463 + name = "http-body" 464 + version = "1.0.1" 465 + source = "registry+https://github.com/rust-lang/crates.io-index" 466 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 467 + dependencies = [ 468 + "bytes", 469 + "http", 470 + ] 471 + 472 + [[package]] 473 + name = "http-body-util" 474 + version = "0.1.3" 475 + source = "registry+https://github.com/rust-lang/crates.io-index" 476 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 477 + dependencies = [ 478 + "bytes", 479 + "futures-core", 480 + "http", 481 + "http-body", 482 + "pin-project-lite", 483 + ] 484 + 485 + [[package]] 486 + name = "httparse" 487 + version = "1.10.1" 488 + source = "registry+https://github.com/rust-lang/crates.io-index" 489 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 490 + 491 + [[package]] 492 + name = "hyper" 493 + version = "1.6.0" 494 + source = "registry+https://github.com/rust-lang/crates.io-index" 495 + checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 496 + dependencies = [ 497 + "bytes", 498 + "futures-channel", 499 + "futures-util", 500 + "h2", 501 + "http", 502 + "http-body", 503 + "httparse", 504 + "itoa", 505 + "pin-project-lite", 506 + "smallvec", 507 + "tokio", 508 + "want", 509 + ] 510 + 511 + [[package]] 512 + name = "hyper-rustls" 513 + version = "0.27.6" 514 + source = "registry+https://github.com/rust-lang/crates.io-index" 515 + checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" 516 + dependencies = [ 517 + "http", 518 + "hyper", 519 + "hyper-util", 520 + "rustls", 521 + "rustls-pki-types", 522 + "tokio", 523 + "tokio-rustls", 524 + "tower-service", 525 + "webpki-roots", 526 + ] 527 + 528 + [[package]] 529 + name = "hyper-tls" 530 + version = "0.6.0" 531 + source = "registry+https://github.com/rust-lang/crates.io-index" 532 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 533 + dependencies = [ 534 + "bytes", 535 + "http-body-util", 536 + "hyper", 537 + "hyper-util", 538 + "native-tls", 539 + "tokio", 540 + "tokio-native-tls", 541 + "tower-service", 542 + ] 543 + 544 + [[package]] 545 + name = "hyper-util" 546 + version = "0.1.13" 547 + source = "registry+https://github.com/rust-lang/crates.io-index" 548 + checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" 549 + dependencies = [ 550 + "base64", 551 + "bytes", 552 + "futures-channel", 553 + "futures-core", 554 + "futures-util", 555 + "http", 556 + "http-body", 557 + "hyper", 558 + "ipnet", 559 + "libc", 560 + "percent-encoding", 561 + "pin-project-lite", 562 + "socket2", 563 + "system-configuration", 564 + "tokio", 565 + "tower-service", 566 + "tracing", 567 + "windows-registry", 568 + ] 569 + 570 + [[package]] 571 + name = "icu_collections" 572 + version = "2.0.0" 573 + source = "registry+https://github.com/rust-lang/crates.io-index" 574 + checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 575 + dependencies = [ 576 + "displaydoc", 577 + "potential_utf", 578 + "yoke", 579 + "zerofrom", 580 + "zerovec", 581 + ] 582 + 583 + [[package]] 584 + name = "icu_locale_core" 585 + version = "2.0.0" 586 + source = "registry+https://github.com/rust-lang/crates.io-index" 587 + checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 588 + dependencies = [ 589 + "displaydoc", 590 + "litemap", 591 + "tinystr", 592 + "writeable", 593 + "zerovec", 594 + ] 595 + 596 + [[package]] 597 + name = "icu_normalizer" 598 + version = "2.0.0" 599 + source = "registry+https://github.com/rust-lang/crates.io-index" 600 + checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 601 + dependencies = [ 602 + "displaydoc", 603 + "icu_collections", 604 + "icu_normalizer_data", 605 + "icu_properties", 606 + "icu_provider", 607 + "smallvec", 608 + "zerovec", 609 + ] 610 + 611 + [[package]] 612 + name = "icu_normalizer_data" 613 + version = "2.0.0" 614 + source = "registry+https://github.com/rust-lang/crates.io-index" 615 + checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 616 + 617 + [[package]] 618 + name = "icu_properties" 619 + version = "2.0.1" 620 + source = "registry+https://github.com/rust-lang/crates.io-index" 621 + checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 622 + dependencies = [ 623 + "displaydoc", 624 + "icu_collections", 625 + "icu_locale_core", 626 + "icu_properties_data", 627 + "icu_provider", 628 + "potential_utf", 629 + "zerotrie", 630 + "zerovec", 631 + ] 632 + 633 + [[package]] 634 + name = "icu_properties_data" 635 + version = "2.0.1" 636 + source = "registry+https://github.com/rust-lang/crates.io-index" 637 + checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 638 + 639 + [[package]] 640 + name = "icu_provider" 641 + version = "2.0.0" 642 + source = "registry+https://github.com/rust-lang/crates.io-index" 643 + checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 644 + dependencies = [ 645 + "displaydoc", 646 + "icu_locale_core", 647 + "stable_deref_trait", 648 + "tinystr", 649 + "writeable", 650 + "yoke", 651 + "zerofrom", 652 + "zerotrie", 653 + "zerovec", 654 + ] 655 + 656 + [[package]] 657 + name = "idna" 658 + version = "1.0.3" 659 + source = "registry+https://github.com/rust-lang/crates.io-index" 660 + checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 661 + dependencies = [ 662 + "idna_adapter", 663 + "smallvec", 664 + "utf8_iter", 665 + ] 666 + 667 + [[package]] 668 + name = "idna_adapter" 669 + version = "1.2.1" 670 + source = "registry+https://github.com/rust-lang/crates.io-index" 671 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 672 + dependencies = [ 673 + "icu_normalizer", 674 + "icu_properties", 675 + ] 676 + 677 + [[package]] 678 + name = "indexmap" 679 + version = "2.9.0" 680 + source = "registry+https://github.com/rust-lang/crates.io-index" 681 + checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 682 + dependencies = [ 683 + "equivalent", 684 + "hashbrown", 685 + ] 686 + 687 + [[package]] 688 + name = "ipconfig" 689 + version = "0.3.2" 690 + source = "registry+https://github.com/rust-lang/crates.io-index" 691 + checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" 692 + dependencies = [ 693 + "socket2", 694 + "widestring", 695 + "windows-sys 0.48.0", 696 + "winreg", 697 + ] 698 + 699 + [[package]] 700 + name = "ipnet" 701 + version = "2.11.0" 702 + source = "registry+https://github.com/rust-lang/crates.io-index" 703 + checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 704 + 705 + [[package]] 706 + name = "iri-string" 707 + version = "0.7.8" 708 + source = "registry+https://github.com/rust-lang/crates.io-index" 709 + checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" 710 + dependencies = [ 711 + "memchr", 712 + "serde", 713 + ] 714 + 715 + [[package]] 716 + name = "itoa" 717 + version = "1.0.15" 718 + source = "registry+https://github.com/rust-lang/crates.io-index" 719 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 720 + 721 + [[package]] 722 + name = "js-sys" 723 + version = "0.3.77" 724 + source = "registry+https://github.com/rust-lang/crates.io-index" 725 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 726 + dependencies = [ 727 + "once_cell", 728 + "wasm-bindgen", 729 + ] 730 + 731 + [[package]] 732 + name = "lazy_static" 733 + version = "1.5.0" 734 + source = "registry+https://github.com/rust-lang/crates.io-index" 735 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 736 + 737 + [[package]] 738 + name = "libc" 739 + version = "0.2.172" 740 + source = "registry+https://github.com/rust-lang/crates.io-index" 741 + checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 742 + 743 + [[package]] 744 + name = "linux-raw-sys" 745 + version = "0.9.4" 746 + source = "registry+https://github.com/rust-lang/crates.io-index" 747 + checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 748 + 749 + [[package]] 750 + name = "litemap" 751 + version = "0.8.0" 752 + source = "registry+https://github.com/rust-lang/crates.io-index" 753 + checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 754 + 755 + [[package]] 756 + name = "lock_api" 757 + version = "0.4.13" 758 + source = "registry+https://github.com/rust-lang/crates.io-index" 759 + checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 760 + dependencies = [ 761 + "autocfg", 762 + "scopeguard", 763 + ] 764 + 765 + [[package]] 766 + name = "log" 767 + version = "0.4.27" 768 + source = "registry+https://github.com/rust-lang/crates.io-index" 769 + checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 770 + 771 + [[package]] 772 + name = "loom" 773 + version = "0.7.2" 774 + source = "registry+https://github.com/rust-lang/crates.io-index" 775 + checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" 776 + dependencies = [ 777 + "cfg-if", 778 + "generator", 779 + "scoped-tls", 780 + "tracing", 781 + "tracing-subscriber", 782 + ] 783 + 784 + [[package]] 785 + name = "lru-slab" 786 + version = "0.1.2" 787 + source = "registry+https://github.com/rust-lang/crates.io-index" 788 + checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 789 + 790 + [[package]] 791 + name = "matchers" 792 + version = "0.1.0" 793 + source = "registry+https://github.com/rust-lang/crates.io-index" 794 + checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 795 + dependencies = [ 796 + "regex-automata 0.1.10", 797 + ] 798 + 799 + [[package]] 800 + name = "memchr" 801 + version = "2.7.4" 802 + source = "registry+https://github.com/rust-lang/crates.io-index" 803 + checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 804 + 805 + [[package]] 806 + name = "mime" 807 + version = "0.3.17" 808 + source = "registry+https://github.com/rust-lang/crates.io-index" 809 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 810 + 811 + [[package]] 812 + name = "miniz_oxide" 813 + version = "0.8.8" 814 + source = "registry+https://github.com/rust-lang/crates.io-index" 815 + checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" 816 + dependencies = [ 817 + "adler2", 818 + ] 819 + 820 + [[package]] 821 + name = "mio" 822 + version = "1.0.4" 823 + source = "registry+https://github.com/rust-lang/crates.io-index" 824 + checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 825 + dependencies = [ 826 + "libc", 827 + "wasi 0.11.0+wasi-snapshot-preview1", 828 + "windows-sys 0.59.0", 829 + ] 830 + 831 + [[package]] 832 + name = "moka" 833 + version = "0.12.10" 834 + source = "registry+https://github.com/rust-lang/crates.io-index" 835 + checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" 836 + dependencies = [ 837 + "crossbeam-channel", 838 + "crossbeam-epoch", 839 + "crossbeam-utils", 840 + "loom", 841 + "parking_lot", 842 + "portable-atomic", 843 + "rustc_version", 844 + "smallvec", 845 + "tagptr", 846 + "thiserror 1.0.69", 847 + "uuid", 848 + ] 849 + 850 + [[package]] 851 + name = "native-tls" 852 + version = "0.2.14" 853 + source = "registry+https://github.com/rust-lang/crates.io-index" 854 + checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 855 + dependencies = [ 856 + "libc", 857 + "log", 858 + "openssl", 859 + "openssl-probe", 860 + "openssl-sys", 861 + "schannel", 862 + "security-framework", 863 + "security-framework-sys", 864 + "tempfile", 865 + ] 866 + 867 + [[package]] 868 + name = "nu-ansi-term" 869 + version = "0.46.0" 870 + source = "registry+https://github.com/rust-lang/crates.io-index" 871 + checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 872 + dependencies = [ 873 + "overload", 874 + "winapi", 875 + ] 876 + 877 + [[package]] 878 + name = "object" 879 + version = "0.36.7" 880 + source = "registry+https://github.com/rust-lang/crates.io-index" 881 + checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 882 + dependencies = [ 883 + "memchr", 884 + ] 885 + 886 + [[package]] 887 + name = "once_cell" 888 + version = "1.21.3" 889 + source = "registry+https://github.com/rust-lang/crates.io-index" 890 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 891 + dependencies = [ 892 + "critical-section", 893 + "portable-atomic", 894 + ] 895 + 896 + [[package]] 897 + name = "openssl" 898 + version = "0.10.73" 899 + source = "registry+https://github.com/rust-lang/crates.io-index" 900 + checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 901 + dependencies = [ 902 + "bitflags", 903 + "cfg-if", 904 + "foreign-types", 905 + "libc", 906 + "once_cell", 907 + "openssl-macros", 908 + "openssl-sys", 909 + ] 910 + 911 + [[package]] 912 + name = "openssl-macros" 913 + version = "0.1.1" 914 + source = "registry+https://github.com/rust-lang/crates.io-index" 915 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 916 + dependencies = [ 917 + "proc-macro2", 918 + "quote", 919 + "syn", 920 + ] 921 + 922 + [[package]] 923 + name = "openssl-probe" 924 + version = "0.1.6" 925 + source = "registry+https://github.com/rust-lang/crates.io-index" 926 + checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 927 + 928 + [[package]] 929 + name = "openssl-sys" 930 + version = "0.9.109" 931 + source = "registry+https://github.com/rust-lang/crates.io-index" 932 + checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" 933 + dependencies = [ 934 + "cc", 935 + "libc", 936 + "pkg-config", 937 + "vcpkg", 938 + ] 939 + 940 + [[package]] 941 + name = "overload" 942 + version = "0.1.1" 943 + source = "registry+https://github.com/rust-lang/crates.io-index" 944 + checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 945 + 946 + [[package]] 947 + name = "parking_lot" 948 + version = "0.12.4" 949 + source = "registry+https://github.com/rust-lang/crates.io-index" 950 + checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 951 + dependencies = [ 952 + "lock_api", 953 + "parking_lot_core", 954 + ] 955 + 956 + [[package]] 957 + name = "parking_lot_core" 958 + version = "0.9.11" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 961 + dependencies = [ 962 + "cfg-if", 963 + "libc", 964 + "redox_syscall", 965 + "smallvec", 966 + "windows-targets 0.52.6", 967 + ] 968 + 969 + [[package]] 970 + name = "percent-encoding" 971 + version = "2.3.1" 972 + source = "registry+https://github.com/rust-lang/crates.io-index" 973 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 974 + 975 + [[package]] 976 + name = "pin-project-lite" 977 + version = "0.2.16" 978 + source = "registry+https://github.com/rust-lang/crates.io-index" 979 + checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 980 + 981 + [[package]] 982 + name = "pin-utils" 983 + version = "0.1.0" 984 + source = "registry+https://github.com/rust-lang/crates.io-index" 985 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 986 + 987 + [[package]] 988 + name = "pkg-config" 989 + version = "0.3.32" 990 + source = "registry+https://github.com/rust-lang/crates.io-index" 991 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 992 + 993 + [[package]] 994 + name = "portable-atomic" 995 + version = "1.11.0" 996 + source = "registry+https://github.com/rust-lang/crates.io-index" 997 + checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 998 + 999 + [[package]] 1000 + name = "potential_utf" 1001 + version = "0.1.2" 1002 + source = "registry+https://github.com/rust-lang/crates.io-index" 1003 + checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 1004 + dependencies = [ 1005 + "zerovec", 1006 + ] 1007 + 1008 + [[package]] 1009 + name = "ppv-lite86" 1010 + version = "0.2.21" 1011 + source = "registry+https://github.com/rust-lang/crates.io-index" 1012 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1013 + dependencies = [ 1014 + "zerocopy", 1015 + ] 1016 + 1017 + [[package]] 1018 + name = "proc-macro2" 1019 + version = "1.0.95" 1020 + source = "registry+https://github.com/rust-lang/crates.io-index" 1021 + checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 1022 + dependencies = [ 1023 + "unicode-ident", 1024 + ] 1025 + 1026 + [[package]] 1027 + name = "quinn" 1028 + version = "0.11.8" 1029 + source = "registry+https://github.com/rust-lang/crates.io-index" 1030 + checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" 1031 + dependencies = [ 1032 + "bytes", 1033 + "cfg_aliases", 1034 + "pin-project-lite", 1035 + "quinn-proto", 1036 + "quinn-udp", 1037 + "rustc-hash", 1038 + "rustls", 1039 + "socket2", 1040 + "thiserror 2.0.12", 1041 + "tokio", 1042 + "tracing", 1043 + "web-time", 1044 + ] 1045 + 1046 + [[package]] 1047 + name = "quinn-proto" 1048 + version = "0.11.12" 1049 + source = "registry+https://github.com/rust-lang/crates.io-index" 1050 + checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" 1051 + dependencies = [ 1052 + "bytes", 1053 + "getrandom 0.3.3", 1054 + "lru-slab", 1055 + "rand", 1056 + "ring", 1057 + "rustc-hash", 1058 + "rustls", 1059 + "rustls-pki-types", 1060 + "slab", 1061 + "thiserror 2.0.12", 1062 + "tinyvec", 1063 + "tracing", 1064 + "web-time", 1065 + ] 1066 + 1067 + [[package]] 1068 + name = "quinn-udp" 1069 + version = "0.5.12" 1070 + source = "registry+https://github.com/rust-lang/crates.io-index" 1071 + checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" 1072 + dependencies = [ 1073 + "cfg_aliases", 1074 + "libc", 1075 + "once_cell", 1076 + "socket2", 1077 + "tracing", 1078 + "windows-sys 0.59.0", 1079 + ] 1080 + 1081 + [[package]] 1082 + name = "quote" 1083 + version = "1.0.40" 1084 + source = "registry+https://github.com/rust-lang/crates.io-index" 1085 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1086 + dependencies = [ 1087 + "proc-macro2", 1088 + ] 1089 + 1090 + [[package]] 1091 + name = "r-efi" 1092 + version = "5.2.0" 1093 + source = "registry+https://github.com/rust-lang/crates.io-index" 1094 + checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 1095 + 1096 + [[package]] 1097 + name = "rand" 1098 + version = "0.9.1" 1099 + source = "registry+https://github.com/rust-lang/crates.io-index" 1100 + checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" 1101 + dependencies = [ 1102 + "rand_chacha", 1103 + "rand_core", 1104 + ] 1105 + 1106 + [[package]] 1107 + name = "rand_chacha" 1108 + version = "0.9.0" 1109 + source = "registry+https://github.com/rust-lang/crates.io-index" 1110 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1111 + dependencies = [ 1112 + "ppv-lite86", 1113 + "rand_core", 1114 + ] 1115 + 1116 + [[package]] 1117 + name = "rand_core" 1118 + version = "0.9.3" 1119 + source = "registry+https://github.com/rust-lang/crates.io-index" 1120 + checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 1121 + dependencies = [ 1122 + "getrandom 0.3.3", 1123 + ] 1124 + 1125 + [[package]] 1126 + name = "redox_syscall" 1127 + version = "0.5.12" 1128 + source = "registry+https://github.com/rust-lang/crates.io-index" 1129 + checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" 1130 + dependencies = [ 1131 + "bitflags", 1132 + ] 1133 + 1134 + [[package]] 1135 + name = "regex" 1136 + version = "1.11.1" 1137 + source = "registry+https://github.com/rust-lang/crates.io-index" 1138 + checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1139 + dependencies = [ 1140 + "aho-corasick", 1141 + "memchr", 1142 + "regex-automata 0.4.9", 1143 + "regex-syntax 0.8.5", 1144 + ] 1145 + 1146 + [[package]] 1147 + name = "regex-automata" 1148 + version = "0.1.10" 1149 + source = "registry+https://github.com/rust-lang/crates.io-index" 1150 + checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1151 + dependencies = [ 1152 + "regex-syntax 0.6.29", 1153 + ] 1154 + 1155 + [[package]] 1156 + name = "regex-automata" 1157 + version = "0.4.9" 1158 + source = "registry+https://github.com/rust-lang/crates.io-index" 1159 + checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1160 + dependencies = [ 1161 + "aho-corasick", 1162 + "memchr", 1163 + "regex-syntax 0.8.5", 1164 + ] 1165 + 1166 + [[package]] 1167 + name = "regex-syntax" 1168 + version = "0.6.29" 1169 + source = "registry+https://github.com/rust-lang/crates.io-index" 1170 + checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1171 + 1172 + [[package]] 1173 + name = "regex-syntax" 1174 + version = "0.8.5" 1175 + source = "registry+https://github.com/rust-lang/crates.io-index" 1176 + checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1177 + 1178 + [[package]] 1179 + name = "reqwest" 1180 + version = "0.12.18" 1181 + source = "registry+https://github.com/rust-lang/crates.io-index" 1182 + checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" 1183 + dependencies = [ 1184 + "base64", 1185 + "bytes", 1186 + "encoding_rs", 1187 + "futures-core", 1188 + "h2", 1189 + "http", 1190 + "http-body", 1191 + "http-body-util", 1192 + "hyper", 1193 + "hyper-rustls", 1194 + "hyper-tls", 1195 + "hyper-util", 1196 + "ipnet", 1197 + "js-sys", 1198 + "log", 1199 + "mime", 1200 + "native-tls", 1201 + "once_cell", 1202 + "percent-encoding", 1203 + "pin-project-lite", 1204 + "quinn", 1205 + "rustls", 1206 + "rustls-pki-types", 1207 + "serde", 1208 + "serde_json", 1209 + "serde_urlencoded", 1210 + "sync_wrapper", 1211 + "tokio", 1212 + "tokio-native-tls", 1213 + "tokio-rustls", 1214 + "tower", 1215 + "tower-http", 1216 + "tower-service", 1217 + "url", 1218 + "wasm-bindgen", 1219 + "wasm-bindgen-futures", 1220 + "web-sys", 1221 + "webpki-roots", 1222 + ] 1223 + 1224 + [[package]] 1225 + name = "resolv-conf" 1226 + version = "0.7.4" 1227 + source = "registry+https://github.com/rust-lang/crates.io-index" 1228 + checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" 1229 + 1230 + [[package]] 1231 + name = "ring" 1232 + version = "0.17.14" 1233 + source = "registry+https://github.com/rust-lang/crates.io-index" 1234 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1235 + dependencies = [ 1236 + "cc", 1237 + "cfg-if", 1238 + "getrandom 0.2.16", 1239 + "libc", 1240 + "untrusted", 1241 + "windows-sys 0.52.0", 1242 + ] 1243 + 1244 + [[package]] 1245 + name = "rustc-demangle" 1246 + version = "0.1.24" 1247 + source = "registry+https://github.com/rust-lang/crates.io-index" 1248 + checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1249 + 1250 + [[package]] 1251 + name = "rustc-hash" 1252 + version = "2.1.1" 1253 + source = "registry+https://github.com/rust-lang/crates.io-index" 1254 + checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 1255 + 1256 + [[package]] 1257 + name = "rustc_version" 1258 + version = "0.4.1" 1259 + source = "registry+https://github.com/rust-lang/crates.io-index" 1260 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1261 + dependencies = [ 1262 + "semver", 1263 + ] 1264 + 1265 + [[package]] 1266 + name = "rustix" 1267 + version = "1.0.7" 1268 + source = "registry+https://github.com/rust-lang/crates.io-index" 1269 + checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" 1270 + dependencies = [ 1271 + "bitflags", 1272 + "errno", 1273 + "libc", 1274 + "linux-raw-sys", 1275 + "windows-sys 0.59.0", 1276 + ] 1277 + 1278 + [[package]] 1279 + name = "rustls" 1280 + version = "0.23.27" 1281 + source = "registry+https://github.com/rust-lang/crates.io-index" 1282 + checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" 1283 + dependencies = [ 1284 + "once_cell", 1285 + "ring", 1286 + "rustls-pki-types", 1287 + "rustls-webpki", 1288 + "subtle", 1289 + "zeroize", 1290 + ] 1291 + 1292 + [[package]] 1293 + name = "rustls-pki-types" 1294 + version = "1.12.0" 1295 + source = "registry+https://github.com/rust-lang/crates.io-index" 1296 + checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 1297 + dependencies = [ 1298 + "web-time", 1299 + "zeroize", 1300 + ] 1301 + 1302 + [[package]] 1303 + name = "rustls-webpki" 1304 + version = "0.103.3" 1305 + source = "registry+https://github.com/rust-lang/crates.io-index" 1306 + checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" 1307 + dependencies = [ 1308 + "ring", 1309 + "rustls-pki-types", 1310 + "untrusted", 1311 + ] 1312 + 1313 + [[package]] 1314 + name = "rustversion" 1315 + version = "1.0.21" 1316 + source = "registry+https://github.com/rust-lang/crates.io-index" 1317 + checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 1318 + 1319 + [[package]] 1320 + name = "ryu" 1321 + version = "1.0.20" 1322 + source = "registry+https://github.com/rust-lang/crates.io-index" 1323 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1324 + 1325 + [[package]] 1326 + name = "schannel" 1327 + version = "0.1.27" 1328 + source = "registry+https://github.com/rust-lang/crates.io-index" 1329 + checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1330 + dependencies = [ 1331 + "windows-sys 0.59.0", 1332 + ] 1333 + 1334 + [[package]] 1335 + name = "scoped-tls" 1336 + version = "1.0.1" 1337 + source = "registry+https://github.com/rust-lang/crates.io-index" 1338 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 1339 + 1340 + [[package]] 1341 + name = "scopeguard" 1342 + version = "1.2.0" 1343 + source = "registry+https://github.com/rust-lang/crates.io-index" 1344 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1345 + 1346 + [[package]] 1347 + name = "security-framework" 1348 + version = "2.11.1" 1349 + source = "registry+https://github.com/rust-lang/crates.io-index" 1350 + checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1351 + dependencies = [ 1352 + "bitflags", 1353 + "core-foundation", 1354 + "core-foundation-sys", 1355 + "libc", 1356 + "security-framework-sys", 1357 + ] 1358 + 1359 + [[package]] 1360 + name = "security-framework-sys" 1361 + version = "2.14.0" 1362 + source = "registry+https://github.com/rust-lang/crates.io-index" 1363 + checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1364 + dependencies = [ 1365 + "core-foundation-sys", 1366 + "libc", 1367 + ] 1368 + 1369 + [[package]] 1370 + name = "semver" 1371 + version = "1.0.26" 1372 + source = "registry+https://github.com/rust-lang/crates.io-index" 1373 + checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 1374 + 1375 + [[package]] 1376 + name = "serde" 1377 + version = "1.0.219" 1378 + source = "registry+https://github.com/rust-lang/crates.io-index" 1379 + checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1380 + dependencies = [ 1381 + "serde_derive", 1382 + ] 1383 + 1384 + [[package]] 1385 + name = "serde_derive" 1386 + version = "1.0.219" 1387 + source = "registry+https://github.com/rust-lang/crates.io-index" 1388 + checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1389 + dependencies = [ 1390 + "proc-macro2", 1391 + "quote", 1392 + "syn", 1393 + ] 1394 + 1395 + [[package]] 1396 + name = "serde_json" 1397 + version = "1.0.140" 1398 + source = "registry+https://github.com/rust-lang/crates.io-index" 1399 + checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1400 + dependencies = [ 1401 + "itoa", 1402 + "memchr", 1403 + "ryu", 1404 + "serde", 1405 + ] 1406 + 1407 + [[package]] 1408 + name = "serde_urlencoded" 1409 + version = "0.7.1" 1410 + source = "registry+https://github.com/rust-lang/crates.io-index" 1411 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1412 + dependencies = [ 1413 + "form_urlencoded", 1414 + "itoa", 1415 + "ryu", 1416 + "serde", 1417 + ] 1418 + 1419 + [[package]] 1420 + name = "sharded-slab" 1421 + version = "0.1.7" 1422 + source = "registry+https://github.com/rust-lang/crates.io-index" 1423 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1424 + dependencies = [ 1425 + "lazy_static", 1426 + ] 1427 + 1428 + [[package]] 1429 + name = "shlex" 1430 + version = "1.3.0" 1431 + source = "registry+https://github.com/rust-lang/crates.io-index" 1432 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1433 + 1434 + [[package]] 1435 + name = "slab" 1436 + version = "0.4.9" 1437 + source = "registry+https://github.com/rust-lang/crates.io-index" 1438 + checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1439 + dependencies = [ 1440 + "autocfg", 1441 + ] 1442 + 1443 + [[package]] 1444 + name = "smallvec" 1445 + version = "1.15.0" 1446 + source = "registry+https://github.com/rust-lang/crates.io-index" 1447 + checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 1448 + 1449 + [[package]] 1450 + name = "socket2" 1451 + version = "0.5.10" 1452 + source = "registry+https://github.com/rust-lang/crates.io-index" 1453 + checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" 1454 + dependencies = [ 1455 + "libc", 1456 + "windows-sys 0.52.0", 1457 + ] 1458 + 1459 + [[package]] 1460 + name = "stable_deref_trait" 1461 + version = "1.2.0" 1462 + source = "registry+https://github.com/rust-lang/crates.io-index" 1463 + checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1464 + 1465 + [[package]] 1466 + name = "subtle" 1467 + version = "2.6.1" 1468 + source = "registry+https://github.com/rust-lang/crates.io-index" 1469 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1470 + 1471 + [[package]] 1472 + name = "syn" 1473 + version = "2.0.101" 1474 + source = "registry+https://github.com/rust-lang/crates.io-index" 1475 + checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 1476 + dependencies = [ 1477 + "proc-macro2", 1478 + "quote", 1479 + "unicode-ident", 1480 + ] 1481 + 1482 + [[package]] 1483 + name = "sync_wrapper" 1484 + version = "1.0.2" 1485 + source = "registry+https://github.com/rust-lang/crates.io-index" 1486 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1487 + dependencies = [ 1488 + "futures-core", 1489 + ] 1490 + 1491 + [[package]] 1492 + name = "synstructure" 1493 + version = "0.13.2" 1494 + source = "registry+https://github.com/rust-lang/crates.io-index" 1495 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1496 + dependencies = [ 1497 + "proc-macro2", 1498 + "quote", 1499 + "syn", 1500 + ] 1501 + 1502 + [[package]] 1503 + name = "system-configuration" 1504 + version = "0.6.1" 1505 + source = "registry+https://github.com/rust-lang/crates.io-index" 1506 + checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 1507 + dependencies = [ 1508 + "bitflags", 1509 + "core-foundation", 1510 + "system-configuration-sys", 1511 + ] 1512 + 1513 + [[package]] 1514 + name = "system-configuration-sys" 1515 + version = "0.6.0" 1516 + source = "registry+https://github.com/rust-lang/crates.io-index" 1517 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1518 + dependencies = [ 1519 + "core-foundation-sys", 1520 + "libc", 1521 + ] 1522 + 1523 + [[package]] 1524 + name = "tagptr" 1525 + version = "0.2.0" 1526 + source = "registry+https://github.com/rust-lang/crates.io-index" 1527 + checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" 1528 + 1529 + [[package]] 1530 + name = "tempfile" 1531 + version = "3.20.0" 1532 + source = "registry+https://github.com/rust-lang/crates.io-index" 1533 + checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 1534 + dependencies = [ 1535 + "fastrand", 1536 + "getrandom 0.3.3", 1537 + "once_cell", 1538 + "rustix", 1539 + "windows-sys 0.59.0", 1540 + ] 1541 + 1542 + [[package]] 1543 + name = "thiserror" 1544 + version = "1.0.69" 1545 + source = "registry+https://github.com/rust-lang/crates.io-index" 1546 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1547 + dependencies = [ 1548 + "thiserror-impl 1.0.69", 1549 + ] 1550 + 1551 + [[package]] 1552 + name = "thiserror" 1553 + version = "2.0.12" 1554 + source = "registry+https://github.com/rust-lang/crates.io-index" 1555 + checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1556 + dependencies = [ 1557 + "thiserror-impl 2.0.12", 1558 + ] 1559 + 1560 + [[package]] 1561 + name = "thiserror-impl" 1562 + version = "1.0.69" 1563 + source = "registry+https://github.com/rust-lang/crates.io-index" 1564 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1565 + dependencies = [ 1566 + "proc-macro2", 1567 + "quote", 1568 + "syn", 1569 + ] 1570 + 1571 + [[package]] 1572 + name = "thiserror-impl" 1573 + version = "2.0.12" 1574 + source = "registry+https://github.com/rust-lang/crates.io-index" 1575 + checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1576 + dependencies = [ 1577 + "proc-macro2", 1578 + "quote", 1579 + "syn", 1580 + ] 1581 + 1582 + [[package]] 1583 + name = "thread_local" 1584 + version = "1.1.8" 1585 + source = "registry+https://github.com/rust-lang/crates.io-index" 1586 + checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1587 + dependencies = [ 1588 + "cfg-if", 1589 + "once_cell", 1590 + ] 1591 + 1592 + [[package]] 1593 + name = "tinystr" 1594 + version = "0.8.1" 1595 + source = "registry+https://github.com/rust-lang/crates.io-index" 1596 + checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 1597 + dependencies = [ 1598 + "displaydoc", 1599 + "zerovec", 1600 + ] 1601 + 1602 + [[package]] 1603 + name = "tinyvec" 1604 + version = "1.9.0" 1605 + source = "registry+https://github.com/rust-lang/crates.io-index" 1606 + checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 1607 + dependencies = [ 1608 + "tinyvec_macros", 1609 + ] 1610 + 1611 + [[package]] 1612 + name = "tinyvec_macros" 1613 + version = "0.1.1" 1614 + source = "registry+https://github.com/rust-lang/crates.io-index" 1615 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1616 + 1617 + [[package]] 1618 + name = "tokio" 1619 + version = "1.45.1" 1620 + source = "registry+https://github.com/rust-lang/crates.io-index" 1621 + checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" 1622 + dependencies = [ 1623 + "backtrace", 1624 + "bytes", 1625 + "libc", 1626 + "mio", 1627 + "pin-project-lite", 1628 + "socket2", 1629 + "tokio-macros", 1630 + "windows-sys 0.52.0", 1631 + ] 1632 + 1633 + [[package]] 1634 + name = "tokio-macros" 1635 + version = "2.5.0" 1636 + source = "registry+https://github.com/rust-lang/crates.io-index" 1637 + checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1638 + dependencies = [ 1639 + "proc-macro2", 1640 + "quote", 1641 + "syn", 1642 + ] 1643 + 1644 + [[package]] 1645 + name = "tokio-native-tls" 1646 + version = "0.3.1" 1647 + source = "registry+https://github.com/rust-lang/crates.io-index" 1648 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1649 + dependencies = [ 1650 + "native-tls", 1651 + "tokio", 1652 + ] 1653 + 1654 + [[package]] 1655 + name = "tokio-rustls" 1656 + version = "0.26.2" 1657 + source = "registry+https://github.com/rust-lang/crates.io-index" 1658 + checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 1659 + dependencies = [ 1660 + "rustls", 1661 + "tokio", 1662 + ] 1663 + 1664 + [[package]] 1665 + name = "tokio-util" 1666 + version = "0.7.15" 1667 + source = "registry+https://github.com/rust-lang/crates.io-index" 1668 + checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" 1669 + dependencies = [ 1670 + "bytes", 1671 + "futures-core", 1672 + "futures-sink", 1673 + "pin-project-lite", 1674 + "tokio", 1675 + ] 1676 + 1677 + [[package]] 1678 + name = "tower" 1679 + version = "0.5.2" 1680 + source = "registry+https://github.com/rust-lang/crates.io-index" 1681 + checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1682 + dependencies = [ 1683 + "futures-core", 1684 + "futures-util", 1685 + "pin-project-lite", 1686 + "sync_wrapper", 1687 + "tokio", 1688 + "tower-layer", 1689 + "tower-service", 1690 + ] 1691 + 1692 + [[package]] 1693 + name = "tower-http" 1694 + version = "0.6.4" 1695 + source = "registry+https://github.com/rust-lang/crates.io-index" 1696 + checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" 1697 + dependencies = [ 1698 + "bitflags", 1699 + "bytes", 1700 + "futures-util", 1701 + "http", 1702 + "http-body", 1703 + "iri-string", 1704 + "pin-project-lite", 1705 + "tower", 1706 + "tower-layer", 1707 + "tower-service", 1708 + ] 1709 + 1710 + [[package]] 1711 + name = "tower-layer" 1712 + version = "0.3.3" 1713 + source = "registry+https://github.com/rust-lang/crates.io-index" 1714 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1715 + 1716 + [[package]] 1717 + name = "tower-service" 1718 + version = "0.3.3" 1719 + source = "registry+https://github.com/rust-lang/crates.io-index" 1720 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1721 + 1722 + [[package]] 1723 + name = "tracing" 1724 + version = "0.1.41" 1725 + source = "registry+https://github.com/rust-lang/crates.io-index" 1726 + checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1727 + dependencies = [ 1728 + "pin-project-lite", 1729 + "tracing-attributes", 1730 + "tracing-core", 1731 + ] 1732 + 1733 + [[package]] 1734 + name = "tracing-attributes" 1735 + version = "0.1.28" 1736 + source = "registry+https://github.com/rust-lang/crates.io-index" 1737 + checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1738 + dependencies = [ 1739 + "proc-macro2", 1740 + "quote", 1741 + "syn", 1742 + ] 1743 + 1744 + [[package]] 1745 + name = "tracing-core" 1746 + version = "0.1.33" 1747 + source = "registry+https://github.com/rust-lang/crates.io-index" 1748 + checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1749 + dependencies = [ 1750 + "once_cell", 1751 + "valuable", 1752 + ] 1753 + 1754 + [[package]] 1755 + name = "tracing-log" 1756 + version = "0.2.0" 1757 + source = "registry+https://github.com/rust-lang/crates.io-index" 1758 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1759 + dependencies = [ 1760 + "log", 1761 + "once_cell", 1762 + "tracing-core", 1763 + ] 1764 + 1765 + [[package]] 1766 + name = "tracing-subscriber" 1767 + version = "0.3.19" 1768 + source = "registry+https://github.com/rust-lang/crates.io-index" 1769 + checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 1770 + dependencies = [ 1771 + "matchers", 1772 + "nu-ansi-term", 1773 + "once_cell", 1774 + "regex", 1775 + "sharded-slab", 1776 + "smallvec", 1777 + "thread_local", 1778 + "tracing", 1779 + "tracing-core", 1780 + "tracing-log", 1781 + ] 1782 + 1783 + [[package]] 1784 + name = "try-lock" 1785 + version = "0.2.5" 1786 + source = "registry+https://github.com/rust-lang/crates.io-index" 1787 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1788 + 1789 + [[package]] 1790 + name = "unicode-ident" 1791 + version = "1.0.18" 1792 + source = "registry+https://github.com/rust-lang/crates.io-index" 1793 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1794 + 1795 + [[package]] 1796 + name = "untrusted" 1797 + version = "0.9.0" 1798 + source = "registry+https://github.com/rust-lang/crates.io-index" 1799 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1800 + 1801 + [[package]] 1802 + name = "url" 1803 + version = "2.5.4" 1804 + source = "registry+https://github.com/rust-lang/crates.io-index" 1805 + checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1806 + dependencies = [ 1807 + "form_urlencoded", 1808 + "idna", 1809 + "percent-encoding", 1810 + ] 1811 + 1812 + [[package]] 1813 + name = "utf8_iter" 1814 + version = "1.0.4" 1815 + source = "registry+https://github.com/rust-lang/crates.io-index" 1816 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1817 + 1818 + [[package]] 1819 + name = "uuid" 1820 + version = "1.17.0" 1821 + source = "registry+https://github.com/rust-lang/crates.io-index" 1822 + checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" 1823 + dependencies = [ 1824 + "getrandom 0.3.3", 1825 + "js-sys", 1826 + "wasm-bindgen", 1827 + ] 1828 + 1829 + [[package]] 1830 + name = "valuable" 1831 + version = "0.1.1" 1832 + source = "registry+https://github.com/rust-lang/crates.io-index" 1833 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 1834 + 1835 + [[package]] 1836 + name = "vcpkg" 1837 + version = "0.2.15" 1838 + source = "registry+https://github.com/rust-lang/crates.io-index" 1839 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1840 + 1841 + [[package]] 1842 + name = "want" 1843 + version = "0.3.1" 1844 + source = "registry+https://github.com/rust-lang/crates.io-index" 1845 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1846 + dependencies = [ 1847 + "try-lock", 1848 + ] 1849 + 1850 + [[package]] 1851 + name = "wasi" 1852 + version = "0.11.0+wasi-snapshot-preview1" 1853 + source = "registry+https://github.com/rust-lang/crates.io-index" 1854 + checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1855 + 1856 + [[package]] 1857 + name = "wasi" 1858 + version = "0.14.2+wasi-0.2.4" 1859 + source = "registry+https://github.com/rust-lang/crates.io-index" 1860 + checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1861 + dependencies = [ 1862 + "wit-bindgen-rt", 1863 + ] 1864 + 1865 + [[package]] 1866 + name = "wasm-bindgen" 1867 + version = "0.2.100" 1868 + source = "registry+https://github.com/rust-lang/crates.io-index" 1869 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1870 + dependencies = [ 1871 + "cfg-if", 1872 + "once_cell", 1873 + "rustversion", 1874 + "wasm-bindgen-macro", 1875 + ] 1876 + 1877 + [[package]] 1878 + name = "wasm-bindgen-backend" 1879 + version = "0.2.100" 1880 + source = "registry+https://github.com/rust-lang/crates.io-index" 1881 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1882 + dependencies = [ 1883 + "bumpalo", 1884 + "log", 1885 + "proc-macro2", 1886 + "quote", 1887 + "syn", 1888 + "wasm-bindgen-shared", 1889 + ] 1890 + 1891 + [[package]] 1892 + name = "wasm-bindgen-futures" 1893 + version = "0.4.50" 1894 + source = "registry+https://github.com/rust-lang/crates.io-index" 1895 + checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1896 + dependencies = [ 1897 + "cfg-if", 1898 + "js-sys", 1899 + "once_cell", 1900 + "wasm-bindgen", 1901 + "web-sys", 1902 + ] 1903 + 1904 + [[package]] 1905 + name = "wasm-bindgen-macro" 1906 + version = "0.2.100" 1907 + source = "registry+https://github.com/rust-lang/crates.io-index" 1908 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1909 + dependencies = [ 1910 + "quote", 1911 + "wasm-bindgen-macro-support", 1912 + ] 1913 + 1914 + [[package]] 1915 + name = "wasm-bindgen-macro-support" 1916 + version = "0.2.100" 1917 + source = "registry+https://github.com/rust-lang/crates.io-index" 1918 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1919 + dependencies = [ 1920 + "proc-macro2", 1921 + "quote", 1922 + "syn", 1923 + "wasm-bindgen-backend", 1924 + "wasm-bindgen-shared", 1925 + ] 1926 + 1927 + [[package]] 1928 + name = "wasm-bindgen-shared" 1929 + version = "0.2.100" 1930 + source = "registry+https://github.com/rust-lang/crates.io-index" 1931 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1932 + dependencies = [ 1933 + "unicode-ident", 1934 + ] 1935 + 1936 + [[package]] 1937 + name = "web-sys" 1938 + version = "0.3.77" 1939 + source = "registry+https://github.com/rust-lang/crates.io-index" 1940 + checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1941 + dependencies = [ 1942 + "js-sys", 1943 + "wasm-bindgen", 1944 + ] 1945 + 1946 + [[package]] 1947 + name = "web-time" 1948 + version = "1.1.0" 1949 + source = "registry+https://github.com/rust-lang/crates.io-index" 1950 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1951 + dependencies = [ 1952 + "js-sys", 1953 + "wasm-bindgen", 1954 + ] 1955 + 1956 + [[package]] 1957 + name = "webpki-roots" 1958 + version = "1.0.0" 1959 + source = "registry+https://github.com/rust-lang/crates.io-index" 1960 + checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" 1961 + dependencies = [ 1962 + "rustls-pki-types", 1963 + ] 1964 + 1965 + [[package]] 1966 + name = "widestring" 1967 + version = "1.2.0" 1968 + source = "registry+https://github.com/rust-lang/crates.io-index" 1969 + checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" 1970 + 1971 + [[package]] 1972 + name = "winapi" 1973 + version = "0.3.9" 1974 + source = "registry+https://github.com/rust-lang/crates.io-index" 1975 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1976 + dependencies = [ 1977 + "winapi-i686-pc-windows-gnu", 1978 + "winapi-x86_64-pc-windows-gnu", 1979 + ] 1980 + 1981 + [[package]] 1982 + name = "winapi-i686-pc-windows-gnu" 1983 + version = "0.4.0" 1984 + source = "registry+https://github.com/rust-lang/crates.io-index" 1985 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1986 + 1987 + [[package]] 1988 + name = "winapi-x86_64-pc-windows-gnu" 1989 + version = "0.4.0" 1990 + source = "registry+https://github.com/rust-lang/crates.io-index" 1991 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1992 + 1993 + [[package]] 1994 + name = "windows" 1995 + version = "0.61.1" 1996 + source = "registry+https://github.com/rust-lang/crates.io-index" 1997 + checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" 1998 + dependencies = [ 1999 + "windows-collections", 2000 + "windows-core", 2001 + "windows-future", 2002 + "windows-link", 2003 + "windows-numerics", 2004 + ] 2005 + 2006 + [[package]] 2007 + name = "windows-collections" 2008 + version = "0.2.0" 2009 + source = "registry+https://github.com/rust-lang/crates.io-index" 2010 + checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" 2011 + dependencies = [ 2012 + "windows-core", 2013 + ] 2014 + 2015 + [[package]] 2016 + name = "windows-core" 2017 + version = "0.61.2" 2018 + source = "registry+https://github.com/rust-lang/crates.io-index" 2019 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 2020 + dependencies = [ 2021 + "windows-implement", 2022 + "windows-interface", 2023 + "windows-link", 2024 + "windows-result", 2025 + "windows-strings 0.4.2", 2026 + ] 2027 + 2028 + [[package]] 2029 + name = "windows-future" 2030 + version = "0.2.1" 2031 + source = "registry+https://github.com/rust-lang/crates.io-index" 2032 + checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" 2033 + dependencies = [ 2034 + "windows-core", 2035 + "windows-link", 2036 + "windows-threading", 2037 + ] 2038 + 2039 + [[package]] 2040 + name = "windows-implement" 2041 + version = "0.60.0" 2042 + source = "registry+https://github.com/rust-lang/crates.io-index" 2043 + checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 2044 + dependencies = [ 2045 + "proc-macro2", 2046 + "quote", 2047 + "syn", 2048 + ] 2049 + 2050 + [[package]] 2051 + name = "windows-interface" 2052 + version = "0.59.1" 2053 + source = "registry+https://github.com/rust-lang/crates.io-index" 2054 + checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 2055 + dependencies = [ 2056 + "proc-macro2", 2057 + "quote", 2058 + "syn", 2059 + ] 2060 + 2061 + [[package]] 2062 + name = "windows-link" 2063 + version = "0.1.1" 2064 + source = "registry+https://github.com/rust-lang/crates.io-index" 2065 + checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" 2066 + 2067 + [[package]] 2068 + name = "windows-numerics" 2069 + version = "0.2.0" 2070 + source = "registry+https://github.com/rust-lang/crates.io-index" 2071 + checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" 2072 + dependencies = [ 2073 + "windows-core", 2074 + "windows-link", 2075 + ] 2076 + 2077 + [[package]] 2078 + name = "windows-registry" 2079 + version = "0.4.0" 2080 + source = "registry+https://github.com/rust-lang/crates.io-index" 2081 + checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" 2082 + dependencies = [ 2083 + "windows-result", 2084 + "windows-strings 0.3.1", 2085 + "windows-targets 0.53.0", 2086 + ] 2087 + 2088 + [[package]] 2089 + name = "windows-result" 2090 + version = "0.3.4" 2091 + source = "registry+https://github.com/rust-lang/crates.io-index" 2092 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 2093 + dependencies = [ 2094 + "windows-link", 2095 + ] 2096 + 2097 + [[package]] 2098 + name = "windows-strings" 2099 + version = "0.3.1" 2100 + source = "registry+https://github.com/rust-lang/crates.io-index" 2101 + checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" 2102 + dependencies = [ 2103 + "windows-link", 2104 + ] 2105 + 2106 + [[package]] 2107 + name = "windows-strings" 2108 + version = "0.4.2" 2109 + source = "registry+https://github.com/rust-lang/crates.io-index" 2110 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 2111 + dependencies = [ 2112 + "windows-link", 2113 + ] 2114 + 2115 + [[package]] 2116 + name = "windows-sys" 2117 + version = "0.48.0" 2118 + source = "registry+https://github.com/rust-lang/crates.io-index" 2119 + checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2120 + dependencies = [ 2121 + "windows-targets 0.48.5", 2122 + ] 2123 + 2124 + [[package]] 2125 + name = "windows-sys" 2126 + version = "0.52.0" 2127 + source = "registry+https://github.com/rust-lang/crates.io-index" 2128 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2129 + dependencies = [ 2130 + "windows-targets 0.52.6", 2131 + ] 2132 + 2133 + [[package]] 2134 + name = "windows-sys" 2135 + version = "0.59.0" 2136 + source = "registry+https://github.com/rust-lang/crates.io-index" 2137 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2138 + dependencies = [ 2139 + "windows-targets 0.52.6", 2140 + ] 2141 + 2142 + [[package]] 2143 + name = "windows-targets" 2144 + version = "0.48.5" 2145 + source = "registry+https://github.com/rust-lang/crates.io-index" 2146 + checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2147 + dependencies = [ 2148 + "windows_aarch64_gnullvm 0.48.5", 2149 + "windows_aarch64_msvc 0.48.5", 2150 + "windows_i686_gnu 0.48.5", 2151 + "windows_i686_msvc 0.48.5", 2152 + "windows_x86_64_gnu 0.48.5", 2153 + "windows_x86_64_gnullvm 0.48.5", 2154 + "windows_x86_64_msvc 0.48.5", 2155 + ] 2156 + 2157 + [[package]] 2158 + name = "windows-targets" 2159 + version = "0.52.6" 2160 + source = "registry+https://github.com/rust-lang/crates.io-index" 2161 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2162 + dependencies = [ 2163 + "windows_aarch64_gnullvm 0.52.6", 2164 + "windows_aarch64_msvc 0.52.6", 2165 + "windows_i686_gnu 0.52.6", 2166 + "windows_i686_gnullvm 0.52.6", 2167 + "windows_i686_msvc 0.52.6", 2168 + "windows_x86_64_gnu 0.52.6", 2169 + "windows_x86_64_gnullvm 0.52.6", 2170 + "windows_x86_64_msvc 0.52.6", 2171 + ] 2172 + 2173 + [[package]] 2174 + name = "windows-targets" 2175 + version = "0.53.0" 2176 + source = "registry+https://github.com/rust-lang/crates.io-index" 2177 + checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" 2178 + dependencies = [ 2179 + "windows_aarch64_gnullvm 0.53.0", 2180 + "windows_aarch64_msvc 0.53.0", 2181 + "windows_i686_gnu 0.53.0", 2182 + "windows_i686_gnullvm 0.53.0", 2183 + "windows_i686_msvc 0.53.0", 2184 + "windows_x86_64_gnu 0.53.0", 2185 + "windows_x86_64_gnullvm 0.53.0", 2186 + "windows_x86_64_msvc 0.53.0", 2187 + ] 2188 + 2189 + [[package]] 2190 + name = "windows-threading" 2191 + version = "0.1.0" 2192 + source = "registry+https://github.com/rust-lang/crates.io-index" 2193 + checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" 2194 + dependencies = [ 2195 + "windows-link", 2196 + ] 2197 + 2198 + [[package]] 2199 + name = "windows_aarch64_gnullvm" 2200 + version = "0.48.5" 2201 + source = "registry+https://github.com/rust-lang/crates.io-index" 2202 + checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2203 + 2204 + [[package]] 2205 + name = "windows_aarch64_gnullvm" 2206 + version = "0.52.6" 2207 + source = "registry+https://github.com/rust-lang/crates.io-index" 2208 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2209 + 2210 + [[package]] 2211 + name = "windows_aarch64_gnullvm" 2212 + version = "0.53.0" 2213 + source = "registry+https://github.com/rust-lang/crates.io-index" 2214 + checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2215 + 2216 + [[package]] 2217 + name = "windows_aarch64_msvc" 2218 + version = "0.48.5" 2219 + source = "registry+https://github.com/rust-lang/crates.io-index" 2220 + checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2221 + 2222 + [[package]] 2223 + name = "windows_aarch64_msvc" 2224 + version = "0.52.6" 2225 + source = "registry+https://github.com/rust-lang/crates.io-index" 2226 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2227 + 2228 + [[package]] 2229 + name = "windows_aarch64_msvc" 2230 + version = "0.53.0" 2231 + source = "registry+https://github.com/rust-lang/crates.io-index" 2232 + checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2233 + 2234 + [[package]] 2235 + name = "windows_i686_gnu" 2236 + version = "0.48.5" 2237 + source = "registry+https://github.com/rust-lang/crates.io-index" 2238 + checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2239 + 2240 + [[package]] 2241 + name = "windows_i686_gnu" 2242 + version = "0.52.6" 2243 + source = "registry+https://github.com/rust-lang/crates.io-index" 2244 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2245 + 2246 + [[package]] 2247 + name = "windows_i686_gnu" 2248 + version = "0.53.0" 2249 + source = "registry+https://github.com/rust-lang/crates.io-index" 2250 + checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2251 + 2252 + [[package]] 2253 + name = "windows_i686_gnullvm" 2254 + version = "0.52.6" 2255 + source = "registry+https://github.com/rust-lang/crates.io-index" 2256 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2257 + 2258 + [[package]] 2259 + name = "windows_i686_gnullvm" 2260 + version = "0.53.0" 2261 + source = "registry+https://github.com/rust-lang/crates.io-index" 2262 + checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2263 + 2264 + [[package]] 2265 + name = "windows_i686_msvc" 2266 + version = "0.48.5" 2267 + source = "registry+https://github.com/rust-lang/crates.io-index" 2268 + checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2269 + 2270 + [[package]] 2271 + name = "windows_i686_msvc" 2272 + version = "0.52.6" 2273 + source = "registry+https://github.com/rust-lang/crates.io-index" 2274 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2275 + 2276 + [[package]] 2277 + name = "windows_i686_msvc" 2278 + version = "0.53.0" 2279 + source = "registry+https://github.com/rust-lang/crates.io-index" 2280 + checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2281 + 2282 + [[package]] 2283 + name = "windows_x86_64_gnu" 2284 + version = "0.48.5" 2285 + source = "registry+https://github.com/rust-lang/crates.io-index" 2286 + checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2287 + 2288 + [[package]] 2289 + name = "windows_x86_64_gnu" 2290 + version = "0.52.6" 2291 + source = "registry+https://github.com/rust-lang/crates.io-index" 2292 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2293 + 2294 + [[package]] 2295 + name = "windows_x86_64_gnu" 2296 + version = "0.53.0" 2297 + source = "registry+https://github.com/rust-lang/crates.io-index" 2298 + checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2299 + 2300 + [[package]] 2301 + name = "windows_x86_64_gnullvm" 2302 + version = "0.48.5" 2303 + source = "registry+https://github.com/rust-lang/crates.io-index" 2304 + checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2305 + 2306 + [[package]] 2307 + name = "windows_x86_64_gnullvm" 2308 + version = "0.52.6" 2309 + source = "registry+https://github.com/rust-lang/crates.io-index" 2310 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2311 + 2312 + [[package]] 2313 + name = "windows_x86_64_gnullvm" 2314 + version = "0.53.0" 2315 + source = "registry+https://github.com/rust-lang/crates.io-index" 2316 + checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 2317 + 2318 + [[package]] 2319 + name = "windows_x86_64_msvc" 2320 + version = "0.48.5" 2321 + source = "registry+https://github.com/rust-lang/crates.io-index" 2322 + checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2323 + 2324 + [[package]] 2325 + name = "windows_x86_64_msvc" 2326 + version = "0.52.6" 2327 + source = "registry+https://github.com/rust-lang/crates.io-index" 2328 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2329 + 2330 + [[package]] 2331 + name = "windows_x86_64_msvc" 2332 + version = "0.53.0" 2333 + source = "registry+https://github.com/rust-lang/crates.io-index" 2334 + checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2335 + 2336 + [[package]] 2337 + name = "winreg" 2338 + version = "0.50.0" 2339 + source = "registry+https://github.com/rust-lang/crates.io-index" 2340 + checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 2341 + dependencies = [ 2342 + "cfg-if", 2343 + "windows-sys 0.48.0", 2344 + ] 2345 + 2346 + [[package]] 2347 + name = "wit-bindgen-rt" 2348 + version = "0.39.0" 2349 + source = "registry+https://github.com/rust-lang/crates.io-index" 2350 + checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2351 + dependencies = [ 2352 + "bitflags", 2353 + ] 2354 + 2355 + [[package]] 2356 + name = "writeable" 2357 + version = "0.6.1" 2358 + source = "registry+https://github.com/rust-lang/crates.io-index" 2359 + checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2360 + 2361 + [[package]] 2362 + name = "yoke" 2363 + version = "0.8.0" 2364 + source = "registry+https://github.com/rust-lang/crates.io-index" 2365 + checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 2366 + dependencies = [ 2367 + "serde", 2368 + "stable_deref_trait", 2369 + "yoke-derive", 2370 + "zerofrom", 2371 + ] 2372 + 2373 + [[package]] 2374 + name = "yoke-derive" 2375 + version = "0.8.0" 2376 + source = "registry+https://github.com/rust-lang/crates.io-index" 2377 + checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 2378 + dependencies = [ 2379 + "proc-macro2", 2380 + "quote", 2381 + "syn", 2382 + "synstructure", 2383 + ] 2384 + 2385 + [[package]] 2386 + name = "zerocopy" 2387 + version = "0.8.25" 2388 + source = "registry+https://github.com/rust-lang/crates.io-index" 2389 + checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" 2390 + dependencies = [ 2391 + "zerocopy-derive", 2392 + ] 2393 + 2394 + [[package]] 2395 + name = "zerocopy-derive" 2396 + version = "0.8.25" 2397 + source = "registry+https://github.com/rust-lang/crates.io-index" 2398 + checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" 2399 + dependencies = [ 2400 + "proc-macro2", 2401 + "quote", 2402 + "syn", 2403 + ] 2404 + 2405 + [[package]] 2406 + name = "zerofrom" 2407 + version = "0.1.6" 2408 + source = "registry+https://github.com/rust-lang/crates.io-index" 2409 + checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2410 + dependencies = [ 2411 + "zerofrom-derive", 2412 + ] 2413 + 2414 + [[package]] 2415 + name = "zerofrom-derive" 2416 + version = "0.1.6" 2417 + source = "registry+https://github.com/rust-lang/crates.io-index" 2418 + checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2419 + dependencies = [ 2420 + "proc-macro2", 2421 + "quote", 2422 + "syn", 2423 + "synstructure", 2424 + ] 2425 + 2426 + [[package]] 2427 + name = "zeroize" 2428 + version = "1.8.1" 2429 + source = "registry+https://github.com/rust-lang/crates.io-index" 2430 + checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2431 + 2432 + [[package]] 2433 + name = "zerotrie" 2434 + version = "0.2.2" 2435 + source = "registry+https://github.com/rust-lang/crates.io-index" 2436 + checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 2437 + dependencies = [ 2438 + "displaydoc", 2439 + "yoke", 2440 + "zerofrom", 2441 + ] 2442 + 2443 + [[package]] 2444 + name = "zerovec" 2445 + version = "0.11.2" 2446 + source = "registry+https://github.com/rust-lang/crates.io-index" 2447 + checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" 2448 + dependencies = [ 2449 + "yoke", 2450 + "zerofrom", 2451 + "zerovec-derive", 2452 + ] 2453 + 2454 + [[package]] 2455 + name = "zerovec-derive" 2456 + version = "0.11.1" 2457 + source = "registry+https://github.com/rust-lang/crates.io-index" 2458 + checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 2459 + dependencies = [ 2460 + "proc-macro2", 2461 + "quote", 2462 + "syn", 2463 + ]
+26
Cargo.toml
··· 1 + [package] 2 + name = "atproto-identity" 3 + version = "0.1.0" 4 + edition = "2021" 5 + rust-version = "1.83" 6 + authors = ["Nick Gerakines <nick.gerakines@gmail.com>"] 7 + description = "An ATProtocol identity library" 8 + readme = "README.md" 9 + repository = "https://tangled.sh/@smokesignal.events/atproto-identity-rs" 10 + license = "MIT" 11 + 12 + [[bin]] 13 + name = "atproto-identity-resolve" 14 + test = false 15 + bench = false 16 + 17 + [dependencies] 18 + anyhow = "1.0" 19 + futures-util = "0.3" 20 + serde = { version = "1.0", features = ["derive"] } 21 + serde_json = "1.0" 22 + thiserror = "2.0" 23 + hickory-resolver = { version = "0.25" } 24 + reqwest = { version = "0.12", features = ["json", "rustls-tls"] } 25 + tracing = { version = "0.1", features = ["async-await"] } 26 + tokio = { version = "1.41", features = ["macros", "rt", "rt-multi-thread"] }
+170
README.md
··· 1 + # atproto-identity 2 + 3 + A Rust library for AT Protocol identity resolution and management. 4 + 5 + ## Overview 6 + 7 + `atproto-identity` provides comprehensive support for resolving and managing identities in the AT Protocol ecosystem. This library handles multiple DID (Decentralized Identifier) methods including `did:plc` and `did:web`, as well as AT Protocol handle resolution via both DNS and HTTP methods. 8 + 9 + This project was extracted from the open-sourced [Smokesignal](https://tangled.sh/@smokesignal.events/smokesignal) project and is designed to be a standalone, reusable library for AT Protocol identity operations. 10 + 11 + ## Features 12 + 13 + - **Handle Resolution**: Resolve AT Protocol handles to DIDs using DNS TXT records and HTTP well-known endpoints 14 + - **DID Document Retrieval**: Fetch and parse DID documents for `did:plc` and `did:web` identifiers 15 + - **Multiple Resolution Methods**: Supports both DNS and HTTP-based handle resolution with conflict detection 16 + - **Configurable DNS**: Custom DNS nameserver support with fallback to system defaults 17 + - **Structured Logging**: Built-in tracing support for debugging and monitoring 18 + - **Type Safety**: Comprehensive error handling with structured error types 19 + 20 + ## Supported DID Methods 21 + 22 + - **did-method-plc**: Public Ledger of Credentials DIDs via PLC directory 23 + - **did-method-web**: Web-based DIDs following the did:web specification with URL conversion utilities 24 + - **ATProtocol Handle Resolution**: AT Protocol handles (e.g., `ngerakines.me`) can be resolved to DIDs 25 + 26 + ## Installation 27 + 28 + Add this to your `Cargo.toml`: 29 + 30 + ```toml 31 + [dependencies] 32 + atproto-identity = "0.1.0" 33 + ``` 34 + 35 + ## Usage 36 + 37 + ### Basic Handle Resolution 38 + 39 + ```rust 40 + use atproto_identity::resolve::{resolve_subject, create_resolver}; 41 + 42 + #[tokio::main] 43 + async fn main() -> anyhow::Result<()> { 44 + let http_client = reqwest::Client::new(); 45 + let dns_resolver = create_resolver(&[]); 46 + 47 + let did = resolve_subject(&http_client, &dns_resolver, "ngerakines.me").await?; 48 + println!("Resolved DID: {}", did); 49 + 50 + Ok(()) 51 + } 52 + ``` 53 + 54 + ### DID Document Retrieval 55 + 56 + ```rust 57 + use atproto_identity::{plc, web}; 58 + 59 + #[tokio::main] 60 + async fn main() -> anyhow::Result<()> { 61 + let http_client = reqwest::Client::new(); 62 + 63 + // Query PLC DID document 64 + let plc_doc = plc::query(&http_client, "plc.directory", "did:plc:example123").await?; 65 + 66 + // Query Web DID document 67 + let web_doc = web::query(&http_client, "did:web:example.com").await?; 68 + 69 + // Convert Web DID to URL (for custom processing) 70 + let did_url = web::did_web_to_url("did:web:example.com")?; 71 + println!("DID document URL: {}", did_url); 72 + 73 + Ok(()) 74 + } 75 + ``` 76 + 77 + ### Web DID URL Conversion 78 + 79 + The `web` module provides utilities for converting DID identifiers to their HTTPS document URLs: 80 + 81 + ```rust 82 + use atproto_identity::web; 83 + 84 + fn main() -> anyhow::Result<()> { 85 + // Convert simple hostname DID 86 + let url = web::did_web_to_url("did:web:example.com")?; 87 + // Returns: "https://example.com/.well-known/did.json" 88 + 89 + // Convert DID with path components 90 + let url = web::did_web_to_url("did:web:example.com:path:subpath")?; 91 + // Returns: "https://example.com/path/subpath/did.json" 92 + 93 + Ok(()) 94 + } 95 + ``` 96 + 97 + ### Configuration 98 + 99 + The library supports various configuration options through environment variables: 100 + 101 + ```bash 102 + # Custom PLC directory hostname 103 + export PLC_HOSTNAME=plc.directory 104 + 105 + # Custom DNS nameservers (semicolon-separated) 106 + export DNS_NAMESERVERS=8.8.8.8;1.1.1.1 107 + 108 + # Custom CA certificate bundles (semicolon-separated paths) 109 + export CERTIFICATE_BUNDLES=/path/to/cert1.pem;/path/to/cert2.pem 110 + 111 + # Custom User-Agent string 112 + export USER_AGENT="my-app/1.0" 113 + ``` 114 + 115 + ## Command Line Tool 116 + 117 + The library includes a command-line tool for testing and resolution: 118 + 119 + ```bash 120 + # Install the binary 121 + cargo install --path . 122 + 123 + # Resolve a handle to DID 124 + atproto-identity-resolve ngerakines.me 125 + 126 + # Get full DID document 127 + atproto-identity-resolve --did-document ngerakines.me 128 + ``` 129 + 130 + ## Architecture 131 + 132 + The library is organized into several modules: 133 + 134 + - **resolve**: Core resolution logic for handles and DIDs 135 + - **plc**: PLC directory client for `did:plc` resolution 136 + - **web**: Web DID client for `did:web` resolution and URL conversion 137 + - **model**: Data structures for DID documents and AT Protocol entities 138 + - **validation**: Input validation for handles and DIDs 139 + - **config**: Configuration management and environment variable handling 140 + - **errors**: Structured error types following project conventions 141 + 142 + ## Error Handling 143 + 144 + All errors follow a structured format: 145 + 146 + ``` 147 + error-atproto-identity-<domain>-<number> <message>: <details> 148 + ``` 149 + 150 + Examples: 151 + - `error-atproto-identity-resolve-1 Multiple DIDs resolved for method` 152 + - `error-atproto-identity-plc-1 HTTP request failed: https://plc.directory/did:plc:example Not Found` 153 + - `error-did-web-1 Invalid DID format: missing 'did:web:' prefix` 154 + 155 + ## Contributing 156 + 157 + Contributions are welcome! Please ensure that: 158 + 159 + 1. All tests pass: `cargo test` 160 + 2. Code is properly formatted: `cargo fmt` 161 + 3. No linting issues: `cargo clippy` 162 + 4. New functionality includes appropriate tests 163 + 164 + ## License 165 + 166 + This project is licensed under the MIT License. See the LICENSE file for details. 167 + 168 + ## Acknowledgments 169 + 170 + This library was extracted from the [Smokesignal](https://tangled.sh/@smokesignal.events/smokesignal) project, an open-source event and RSVP management and discovery application.
+90
src/bin/atproto-identity-resolve.rs
··· 1 + use std::env; 2 + 3 + use anyhow::Result; 4 + use atproto_identity::{ 5 + config::{default_env, optional_env, version, CertificateBundles, DnsNameservers}, 6 + plc::query as plc_query, 7 + resolve::{create_resolver, parse_input, resolve_subject, InputType}, 8 + web::query as web_query, 9 + }; 10 + 11 + #[tokio::main] 12 + async fn main() -> Result<()> { 13 + let plc_hostname = default_env("PLC_HOSTNAME", "plc.directory"); 14 + let certificate_bundles: CertificateBundles = optional_env("CERTIFICATE_BUNDLES").try_into()?; 15 + let default_user_agent = format!( 16 + "atproto-identity-rs ({}; +https://tangled.sh/@smokesignal.events/atproto-identity-rs)", 17 + version()? 18 + ); 19 + let user_agent = default_env("USER_AGENT", &default_user_agent); 20 + let dns_nameservers: DnsNameservers = optional_env("DNS_NAMESERVERS").try_into()?; 21 + 22 + let mut client_builder = reqwest::Client::builder(); 23 + for ca_certificate in certificate_bundles.as_ref() { 24 + let cert = std::fs::read(ca_certificate)?; 25 + let cert = reqwest::Certificate::from_pem(&cert)?; 26 + client_builder = client_builder.add_root_certificate(cert); 27 + } 28 + 29 + client_builder = client_builder.user_agent(user_agent); 30 + let http_client = client_builder.build()?; 31 + 32 + let dns_resolver = create_resolver(dns_nameservers.as_ref()); 33 + 34 + let mut subjects = vec![]; 35 + let mut get_did_document = false; 36 + 37 + for subject in env::args().skip(1) { 38 + match subject.as_str() { 39 + "--did-document" => { 40 + get_did_document = true; 41 + } 42 + _ => { 43 + subjects.push(subject); 44 + } 45 + } 46 + } 47 + 48 + for subject in subjects { 49 + let resolved_did = resolve_subject(&http_client, &dns_resolver, &subject).await; 50 + let resolved_did = match resolved_did { 51 + Ok(value) => { 52 + println!("{value}"); 53 + value 54 + } 55 + Err(err) => { 56 + eprintln!("{err}"); 57 + continue; 58 + } 59 + }; 60 + 61 + if !get_did_document { 62 + continue; 63 + } 64 + 65 + let did_document = match parse_input(&resolved_did) { 66 + Ok(InputType::Plc(did)) => plc_query(&http_client, &plc_hostname, &did).await, 67 + Ok(InputType::Web(did)) => web_query(&http_client, &did).await, 68 + Ok(InputType::Handle(_)) => { 69 + eprintln!("error: subject resolved to handle"); 70 + continue; 71 + } 72 + Err(err) => { 73 + eprintln!("{err}"); 74 + continue; 75 + } 76 + }; 77 + 78 + match did_document { 79 + Ok(value) => { 80 + println!("{value:?}") 81 + } 82 + Err(err) => { 83 + eprintln!("{err}"); 84 + continue; 85 + } 86 + } 87 + } 88 + 89 + Ok(()) 90 + }
+95
src/config.rs
··· 1 + use crate::errors::ConfigError; 2 + use anyhow::Result; 3 + 4 + /// Certificate bundle paths for TLS verification. 5 + /// Contains a collection of file paths to certificate bundles. 6 + #[derive(Clone)] 7 + pub struct CertificateBundles(Vec<String>); 8 + 9 + /// DNS nameserver IP addresses for domain resolution. 10 + /// Contains a collection of IP addresses for custom DNS resolution. 11 + #[derive(Clone)] 12 + pub struct DnsNameservers(Vec<std::net::IpAddr>); 13 + 14 + /// Gets a required environment variable value. 15 + /// Returns an error if the variable is not set. 16 + pub fn require_env(name: &str) -> Result<String, ConfigError> { 17 + std::env::var(name).map_err(|_| ConfigError::MissingEnvironmentVariable { 18 + name: name.to_string(), 19 + }) 20 + } 21 + 22 + /// Gets an optional environment variable value. 23 + /// Returns empty string if the variable is not set. 24 + pub fn optional_env(name: &str) -> String { 25 + std::env::var(name).unwrap_or("".to_string()) 26 + } 27 + 28 + /// Gets an environment variable value with a fallback default. 29 + /// Returns the default value if the variable is not set. 30 + pub fn default_env(name: &str, default_value: &str) -> String { 31 + std::env::var(name).unwrap_or(default_value.to_string()) 32 + } 33 + 34 + /// Gets the application version from git hash or package version. 35 + /// Returns the git hash if available, otherwise falls back to cargo package version. 36 + pub fn version() -> Result<String, ConfigError> { 37 + option_env!("GIT_HASH") 38 + .or(option_env!("CARGO_PKG_VERSION")) 39 + .map(|val| val.to_string()) 40 + .ok_or(ConfigError::VersionNotAvailable) 41 + } 42 + 43 + impl TryFrom<String> for CertificateBundles { 44 + type Error = anyhow::Error; 45 + fn try_from(value: String) -> Result<Self, Self::Error> { 46 + Ok(Self( 47 + value 48 + .split(';') 49 + .filter_map(|s| { 50 + if s.is_empty() { 51 + None 52 + } else { 53 + Some(s.to_string()) 54 + } 55 + }) 56 + .collect::<Vec<String>>(), 57 + )) 58 + } 59 + } 60 + impl AsRef<Vec<String>> for CertificateBundles { 61 + fn as_ref(&self) -> &Vec<String> { 62 + &self.0 63 + } 64 + } 65 + 66 + impl TryFrom<String> for DnsNameservers { 67 + type Error = anyhow::Error; 68 + fn try_from(value: String) -> Result<Self, Self::Error> { 69 + // Allow empty value for default DNS configuration 70 + if value.is_empty() { 71 + return Ok(Self(Vec::new())); 72 + } 73 + 74 + let nameservers = value 75 + .split(';') 76 + .map(|s| s.trim()) 77 + .filter(|s| !s.is_empty()) 78 + .map(|s| { 79 + s.parse::<std::net::IpAddr>().map_err(|_| { 80 + anyhow::Error::from(ConfigError::InvalidNameserverIP { 81 + value: s.to_string(), 82 + }) 83 + }) 84 + }) 85 + .collect::<Result<Vec<std::net::IpAddr>, _>>()?; 86 + 87 + Ok(Self(nameservers)) 88 + } 89 + } 90 + 91 + impl AsRef<Vec<std::net::IpAddr>> for DnsNameservers { 92 + fn as_ref(&self) -> &Vec<std::net::IpAddr> { 93 + &self.0 94 + } 95 + }
+101
src/errors.rs
··· 1 + use thiserror::Error; 2 + 3 + /// Error types that can occur when working with Web DIDs 4 + #[derive(Debug, Error)] 5 + pub enum WebDIDError { 6 + /// Occurs when the DID is missing the 'did:web:' prefix 7 + #[error("error-did-web-1 Invalid DID format: missing 'did:web:' prefix")] 8 + InvalidDIDPrefix, 9 + 10 + /// Occurs when the DID is missing a hostname component 11 + #[error("error-did-web-2 Invalid DID format: missing hostname component")] 12 + MissingHostname, 13 + 14 + /// Occurs when the HTTP request to fetch the DID document fails 15 + #[error("error-did-web-3 HTTP request failed: {url} {error}")] 16 + HttpRequestFailed { 17 + /// The URL that was requested 18 + url: String, 19 + /// The underlying HTTP error 20 + error: reqwest::Error, 21 + }, 22 + 23 + /// Occurs when the DID document cannot be parsed from the HTTP response 24 + #[error("error-did-web-4 Failed to parse DID document: {url} {error}")] 25 + DocumentParseFailed { 26 + /// The URL that was requested 27 + url: String, 28 + /// The underlying parse error 29 + error: reqwest::Error, 30 + }, 31 + } 32 + 33 + /// Error types that can occur when working with configuration 34 + #[derive(Debug, Error)] 35 + pub enum ConfigError { 36 + /// Occurs when a required environment variable is not set 37 + #[error("error-atproto-identity-config-1 Required environment variable not found: {name}")] 38 + MissingEnvironmentVariable { name: String }, 39 + 40 + /// Occurs when parsing an IP address from nameserver configuration fails 41 + #[error("error-atproto-identity-config-2 Unable to parse nameserver IP: {value}")] 42 + InvalidNameserverIP { value: String }, 43 + 44 + /// Occurs when version information cannot be determined 45 + #[error("error-atproto-identity-config-3 Version information not available: GIT_HASH or CARGO_PKG_VERSION must be set")] 46 + VersionNotAvailable, 47 + } 48 + 49 + /// Error types that can occur when resolving AT Protocol identities 50 + #[derive(Debug, Error)] 51 + pub enum ResolveError { 52 + /// Occurs when multiple different DIDs are found via DNS TXT record lookup 53 + #[error("error-atproto-identity-resolve-1 Multiple DIDs resolved for method")] 54 + MultipleDIDsFound, 55 + 56 + /// Occurs when no DIDs are found via either DNS or HTTP resolution methods 57 + #[error("error-atproto-identity-resolve-2 No DIDs resolved for method")] 58 + NoDIDsFound, 59 + 60 + /// Occurs when DNS and HTTP resolution return different DIDs for the same handle 61 + #[error("error-atproto-identity-resolve-3 Conflicting DIDs found for method")] 62 + ConflictingDIDsFound, 63 + 64 + /// Occurs when DNS TXT record lookup fails 65 + #[error("error-atproto-identity-resolve-4 DNS resolution failed: {0:?}")] 66 + DNSResolutionFailed(hickory_resolver::ResolveError), 67 + 68 + /// Occurs when HTTP request to .well-known/atproto-did endpoint fails 69 + #[error("error-atproto-identity-resolve-5 HTTP resolution failed: {0:?}")] 70 + HTTPResolutionFailed(reqwest::Error), 71 + 72 + /// Occurs when HTTP response from .well-known/atproto-did doesn't start with "did:" 73 + #[error("error-atproto-identity-resolve-6 Invalid HTTP resolution response")] 74 + InvalidHTTPResolutionResponse, 75 + 76 + /// Occurs when input cannot be parsed as a valid handle or DID 77 + #[error("error-atproto-identity-resolve-8 Invalid input")] 78 + InvalidInput, 79 + } 80 + 81 + /// Error types that can occur when working with PLC DIDs 82 + #[derive(Debug, Error)] 83 + pub enum PLCDIDError { 84 + /// Occurs when the HTTP request to the PLC directory fails 85 + #[error("error-atproto-identity-plc-1 HTTP request failed: {url} {error}")] 86 + HttpRequestFailed { 87 + /// The URL that was requested 88 + url: String, 89 + /// The underlying HTTP error 90 + error: reqwest::Error, 91 + }, 92 + 93 + /// Occurs when the DID document cannot be parsed from the PLC directory response 94 + #[error("error-atproto-identity-plc-2 Failed to parse DID document: {url} {error}")] 95 + DocumentParseFailed { 96 + /// The URL that was requested 97 + url: String, 98 + /// The underlying parse error 99 + error: reqwest::Error, 100 + }, 101 + }
+7
src/lib.rs
··· 1 + pub mod config; 2 + pub mod errors; 3 + pub mod model; 4 + pub mod plc; 5 + pub mod resolve; 6 + pub mod validation; 7 + pub mod web;
+160
src/model.rs
··· 1 + use serde::{Deserialize, Serialize}; 2 + use serde_json::Value; 3 + use std::collections::HashMap; 4 + 5 + /// AT Protocol service configuration from a DID document. 6 + /// Represents services like Personal Data Servers (PDS). 7 + #[derive(Clone, Deserialize, Debug)] 8 + #[serde(rename_all = "camelCase")] 9 + pub struct Service { 10 + /// Unique identifier for the service. 11 + pub id: String, 12 + /// Service type (e.g., "AtprotoPersonalDataServer"). 13 + pub r#type: String, 14 + /// URL endpoint where the service can be reached. 15 + pub service_endpoint: String, 16 + 17 + /// Additional service properties not explicitly defined. 18 + #[serde(flatten)] 19 + pub extra: HashMap<String, Value>, 20 + } 21 + 22 + /// Cryptographic verification method from a DID document. 23 + /// Used to verify signatures and authenticate identity operations. 24 + #[derive(Clone, Deserialize, Debug)] 25 + #[serde(tag = "type")] 26 + pub enum VerificationMethod { 27 + /// Multikey verification method with multibase-encoded public key. 28 + Multikey { 29 + /// Unique identifier for this verification method. 30 + id: String, 31 + /// DID that controls this verification method. 32 + controller: String, 33 + 34 + /// Public key encoded in multibase format. 35 + #[serde(rename = "publicKeyMultibase")] 36 + public_key_multibase: String, 37 + 38 + /// Additional verification method properties. 39 + #[serde(flatten)] 40 + extra: HashMap<String, Value>, 41 + }, 42 + 43 + /// Other verification method types not explicitly supported. 44 + #[serde(untagged)] 45 + Other { 46 + /// All properties of the unsupported verification method. 47 + #[serde(flatten)] 48 + extra: HashMap<String, Value>, 49 + }, 50 + } 51 + 52 + /// Complete DID document containing identity information. 53 + /// Contains services, verification methods, and aliases for a DID. 54 + #[derive(Clone, Deserialize, Debug)] 55 + #[serde(rename_all = "camelCase")] 56 + pub struct Document { 57 + /// The DID identifier (e.g., "did:plc:abc123"). 58 + pub id: String, 59 + /// Alternative identifiers like handles and domains. 60 + pub also_known_as: Vec<String>, 61 + /// Available services for this identity. 62 + pub service: Vec<Service>, 63 + 64 + /// Cryptographic verification methods. 65 + #[serde(alias = "verificationMethod")] 66 + pub verification_method: Vec<VerificationMethod>, 67 + 68 + /// Additional document properties not explicitly defined. 69 + #[serde(flatten)] 70 + pub extra: HashMap<String, Value>, 71 + } 72 + 73 + impl Document { 74 + /// Extracts Personal Data Server endpoints from services. 75 + /// Returns URLs of all AtprotoPersonalDataServer services. 76 + pub fn pds_endpoints(&self) -> Vec<&str> { 77 + self.service 78 + .iter() 79 + .filter_map(|service| { 80 + if service.r#type == "AtprotoPersonalDataServer" { 81 + Some(service.service_endpoint.as_str()) 82 + } else { 83 + None 84 + } 85 + }) 86 + .collect() 87 + } 88 + 89 + /// Gets the primary handle from alsoKnownAs aliases. 90 + /// Returns the first alias with "at://" prefix stripped if present. 91 + pub fn handles(&self) -> Option<&str> { 92 + self.also_known_as.first().map(|handle| { 93 + if let Some(trimmed) = handle.strip_prefix("at://") { 94 + trimmed 95 + } else { 96 + handle.as_str() 97 + } 98 + }) 99 + } 100 + 101 + /// Extracts multibase public keys from verification methods. 102 + /// Returns public keys from Multikey verification methods only. 103 + pub fn did_keys(&self) -> Vec<&str> { 104 + self.verification_method 105 + .iter() 106 + .filter_map(|verification_method| match verification_method { 107 + VerificationMethod::Multikey { 108 + public_key_multibase, 109 + .. 110 + } => Some(public_key_multibase.as_str()), 111 + VerificationMethod::Other { extra: _ } => None, 112 + }) 113 + .collect() 114 + } 115 + } 116 + 117 + /// Resolved handle information linking DID to human-readable identifier. 118 + /// Contains the complete identity resolution result. 119 + #[derive(Clone, Deserialize, Serialize, Debug)] 120 + pub struct Handle { 121 + /// The resolved DID identifier. 122 + pub did: String, 123 + /// Human-readable handle (e.g., "alice.bsky.social"). 124 + pub handle: String, 125 + /// Personal Data Server URL hosting the identity. 126 + pub pds: String, 127 + /// Available cryptographic verification methods. 128 + pub verification_methods: Vec<String>, 129 + } 130 + 131 + #[cfg(test)] 132 + mod tests { 133 + use crate::model::Document; 134 + 135 + #[test] 136 + fn test_deserialize() { 137 + let document = serde_json::from_str::<Document>( 138 + r##"{"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/suites/secp256k1-2019/v1"],"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","alsoKnownAs":["at://ngerakines.me","at://nick.gerakines.net","at://nick.thegem.city","https://github.com/ngerakines","https://ngerakines.me/","dns:ngerakines.me"],"verificationMethod":[{"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2#atproto","type":"Multikey","controller":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","publicKeyMultibase":"zQ3shXvCK2RyPrSLYQjBEw5CExZkUhJH3n1K2Mb9sC7JbvRMF"}],"service":[{"id":"#atproto_pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.cauda.cloud"}]}"##, 139 + ); 140 + assert!(document.is_ok()); 141 + 142 + let document = document.unwrap(); 143 + assert_eq!(document.id, "did:plc:cbkjy5n7bk3ax2wplmtjofq2"); 144 + } 145 + 146 + #[test] 147 + fn test_deserialize_unsupported_verification_method() { 148 + let documents = vec![ 149 + r##"{"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/suites/secp256k1-2019/v1"],"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","alsoKnownAs":["at://ngerakines.me","at://nick.gerakines.net","at://nick.thegem.city","https://github.com/ngerakines","https://ngerakines.me/","dns:ngerakines.me"],"verificationMethod":[{"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2#atproto","type":"Ed25519VerificationKey2020","controller":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","publicKeyMultibase":"zQ3shXvCK2RyPrSLYQjBEw5CExZkUhJH3n1K2Mb9sC7JbvRMF"}],"service":[{"id":"#atproto_pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.cauda.cloud"}]}"##, 150 + r##"{"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/suites/secp256k1-2019/v1"],"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","alsoKnownAs":["at://ngerakines.me","at://nick.gerakines.net","at://nick.thegem.city","https://github.com/ngerakines","https://ngerakines.me/","dns:ngerakines.me"],"verificationMethod":[{"id": "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A","type": "JsonWebKey2020","controller": "did:example:123","publicKeyJwk": {"crv": "Ed25519","x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ","kty": "OKP","kid": "_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A"}}],"service":[{"id":"#atproto_pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.cauda.cloud"}]}"##, 151 + ]; 152 + for document in documents { 153 + let document = serde_json::from_str::<Document>(document); 154 + assert!(document.is_ok()); 155 + 156 + let document = document.unwrap(); 157 + assert_eq!(document.id, "did:plc:cbkjy5n7bk3ax2wplmtjofq2"); 158 + } 159 + } 160 + }
+27
src/plc.rs
··· 1 + use anyhow::Result; 2 + 3 + use super::errors::PLCDIDError; 4 + use super::model::Document; 5 + 6 + /// Queries a PLC directory for a DID document. 7 + /// Fetches the complete DID document from the specified PLC hostname. 8 + pub async fn query( 9 + http_client: &reqwest::Client, 10 + plc_hostname: &str, 11 + did: &str, 12 + ) -> Result<Document> { 13 + let url = format!("https://{}/{}", plc_hostname, did); 14 + 15 + http_client 16 + .get(&url) 17 + .send() 18 + .await 19 + .map_err(|error| PLCDIDError::HttpRequestFailed { 20 + url: url.clone(), 21 + error, 22 + })? 23 + .json::<Document>() 24 + .await 25 + .map_err(|error| PLCDIDError::DocumentParseFailed { url, error }) 26 + .map_err(Into::into) 27 + }
+169
src/resolve.rs
··· 1 + use anyhow::Result; 2 + use futures_util::future::join; 3 + use hickory_resolver::{ 4 + config::{NameServerConfigGroup, ResolverConfig}, 5 + name_server::TokioConnectionProvider, 6 + Resolver, TokioResolver, 7 + }; 8 + use std::collections::HashSet; 9 + use std::time::Duration; 10 + 11 + use crate::errors::ResolveError; 12 + use crate::validation::{is_valid_did_method_plc, is_valid_handle}; 13 + 14 + /// Type of input identifier for resolution. 15 + /// Distinguishes between handles and different DID methods. 16 + pub enum InputType { 17 + /// AT Protocol handle (e.g., "alice.bsky.social"). 18 + Handle(String), 19 + /// PLC DID identifier (e.g., "did:plc:abc123"). 20 + Plc(String), 21 + /// Web DID identifier (e.g., "did:web:example.com"). 22 + Web(String), 23 + } 24 + 25 + /// Resolves a handle to DID using DNS TXT records. 26 + /// Looks up _atproto.{handle} TXT record for DID value. 27 + pub async fn resolve_handle_dns( 28 + dns_resolver: &TokioResolver, 29 + lookup_dns: &str, 30 + ) -> Result<String, ResolveError> { 31 + let lookup = dns_resolver 32 + .txt_lookup(&format!("_atproto.{}", lookup_dns)) 33 + .await 34 + .map_err(ResolveError::DNSResolutionFailed)?; 35 + 36 + let dids = lookup 37 + .iter() 38 + .filter_map(|record| { 39 + record 40 + .to_string() 41 + .strip_prefix("did=") 42 + .map(|did| did.to_string()) 43 + }) 44 + .collect::<HashSet<String>>(); 45 + 46 + if dids.len() > 1 { 47 + return Err(ResolveError::MultipleDIDsFound); 48 + } 49 + 50 + dids.iter().next().cloned().ok_or(ResolveError::NoDIDsFound) 51 + } 52 + 53 + /// Resolves a handle to DID using HTTPS well-known endpoint. 54 + /// Fetches DID from https://{handle}/.well-known/atproto-did 55 + pub async fn resolve_handle_http( 56 + http_client: &reqwest::Client, 57 + handle: &str, 58 + ) -> Result<String, ResolveError> { 59 + let lookup_url = format!("https://{}/.well-known/atproto-did", handle); 60 + 61 + http_client 62 + .get(lookup_url.clone()) 63 + .timeout(Duration::from_secs(10)) 64 + .send() 65 + .await 66 + .map_err(ResolveError::HTTPResolutionFailed)? 67 + .text() 68 + .await 69 + .map_err(ResolveError::HTTPResolutionFailed) 70 + .and_then(|body| { 71 + if body.starts_with("did:") { 72 + Ok(body.trim().to_string()) 73 + } else { 74 + Err(ResolveError::InvalidHTTPResolutionResponse) 75 + } 76 + }) 77 + } 78 + 79 + /// Parses input string into appropriate identifier type. 80 + /// Handles prefixes like "at://", "@", and DID formats. 81 + pub fn parse_input(input: &str) -> Result<InputType, ResolveError> { 82 + let trimmed = { 83 + if let Some(value) = input.trim().strip_prefix("at://") { 84 + value.trim() 85 + } else if let Some(value) = input.trim().strip_prefix('@') { 86 + value.trim() 87 + } else { 88 + input.trim() 89 + } 90 + }; 91 + if trimmed.is_empty() { 92 + return Err(ResolveError::InvalidInput); 93 + } 94 + if trimmed.starts_with("did:web:") { 95 + Ok(InputType::Web(trimmed.to_string())) 96 + } else if trimmed.starts_with("did:plc:") && is_valid_did_method_plc(trimmed) { 97 + Ok(InputType::Plc(trimmed.to_string())) 98 + } else { 99 + is_valid_handle(trimmed) 100 + .map(InputType::Handle) 101 + .ok_or(ResolveError::InvalidInput) 102 + } 103 + } 104 + 105 + /// Resolves a handle to DID using both DNS and HTTP methods. 106 + /// Returns DID if both methods agree, or error if conflicting. 107 + pub async fn resolve_handle( 108 + http_client: &reqwest::Client, 109 + dns_resolver: &TokioResolver, 110 + handle: &str, 111 + ) -> Result<String, ResolveError> { 112 + let trimmed = { 113 + if let Some(value) = handle.trim().strip_prefix("at://") { 114 + value 115 + } else if let Some(value) = handle.trim().strip_prefix('@') { 116 + value 117 + } else { 118 + handle.trim() 119 + } 120 + }; 121 + 122 + let (dns_lookup, http_lookup) = join( 123 + resolve_handle_dns(dns_resolver, trimmed), 124 + resolve_handle_http(http_client, trimmed), 125 + ) 126 + .await; 127 + 128 + let results = vec![dns_lookup, http_lookup] 129 + .into_iter() 130 + .filter_map(|result| result.ok()) 131 + .collect::<Vec<String>>(); 132 + if results.is_empty() { 133 + return Err(ResolveError::NoDIDsFound); 134 + } 135 + 136 + let first = results[0].clone(); 137 + if results.iter().all(|result| result == &first) { 138 + return Ok(first); 139 + } 140 + Err(ResolveError::ConflictingDIDsFound) 141 + } 142 + 143 + /// Resolves any subject (handle or DID) to a canonical DID. 144 + /// Handles all supported identifier formats automatically. 145 + pub async fn resolve_subject( 146 + http_client: &reqwest::Client, 147 + dns_resolver: &TokioResolver, 148 + subject: &str, 149 + ) -> Result<String, ResolveError> { 150 + match parse_input(subject)? { 151 + InputType::Handle(handle) => resolve_handle(http_client, dns_resolver, &handle).await, 152 + InputType::Plc(did) | InputType::Web(did) => Ok(did), 153 + } 154 + } 155 + 156 + /// Creates a DNS resolver with custom or system nameservers. 157 + /// Uses custom nameservers if provided, otherwise system defaults. 158 + pub fn create_resolver(nameservers: &[std::net::IpAddr]) -> TokioResolver { 159 + // Initialize the DNS resolver with custom nameservers if configured 160 + if !nameservers.is_empty() { 161 + tracing::debug!("Using custom DNS nameservers: {:?}", nameservers); 162 + let nameserver_group = NameServerConfigGroup::from_ips_clear(nameservers, 53, true); 163 + let resolver_config = ResolverConfig::from_parts(None, vec![], nameserver_group); 164 + Resolver::builder_with_config(resolver_config, TokioConnectionProvider::default()).build() 165 + } else { 166 + tracing::debug!("Using system default DNS nameservers"); 167 + Resolver::builder_tokio().unwrap().build() 168 + } 169 + }
+84
src/validation.rs
··· 1 + /// Maximum length for a valid hostname as defined in RFC 1035 2 + const MAX_HOSTNAME_LENGTH: usize = 253; 3 + 4 + /// Maximum length for a DNS label (component between dots) as defined in RFC 1035 5 + const MAX_LABEL_LENGTH: usize = 63; 6 + 7 + /// List of reserved top-level domains that are not valid for AT Protocol handles 8 + const RESERVED_TLDS: [&str; 4] = [".localhost", ".internal", ".arpa", ".local"]; 9 + 10 + /// Validates if a string is a valid hostname according to RFC 1035. 11 + /// Checks length limits, reserved TLDs, and character validity. 12 + pub fn is_valid_hostname(hostname: &str) -> bool { 13 + // Empty hostnames are invalid 14 + if hostname.is_empty() || hostname.len() > MAX_HOSTNAME_LENGTH { 15 + return false; 16 + } 17 + 18 + // Check if hostname uses any reserved TLDs 19 + if RESERVED_TLDS.iter().any(|tld| hostname.ends_with(tld)) { 20 + return false; 21 + } 22 + 23 + // Ensure all characters are valid hostname characters 24 + if hostname.bytes().any(|byte| !is_valid_hostname_char(byte)) { 25 + return false; 26 + } 27 + 28 + // Validate each DNS label in the hostname 29 + if hostname.split('.').any(|label| !is_valid_dns_label(label)) { 30 + return false; 31 + } 32 + 33 + true 34 + } 35 + 36 + fn is_valid_hostname_char(byte: u8) -> bool { 37 + byte.is_ascii_lowercase() 38 + || byte.is_ascii_uppercase() 39 + || byte.is_ascii_digit() 40 + || byte == b'-' 41 + || byte == b'.' 42 + } 43 + 44 + fn is_valid_dns_label(label: &str) -> bool { 45 + !(label.is_empty() 46 + || label.len() > MAX_LABEL_LENGTH 47 + || label.starts_with('-') 48 + || label.ends_with('-')) 49 + } 50 + 51 + /// Validates and normalizes an AT Protocol handle. 52 + /// Returns the normalized handle if valid, None otherwise. 53 + pub fn is_valid_handle(handle: &str) -> Option<String> { 54 + // Strip optional prefixes to get the core handle 55 + let trimmed = strip_handle_prefixes(handle); 56 + 57 + // A valid handle must be a valid hostname with at least one period 58 + if is_valid_hostname(trimmed) && trimmed.contains('.') { 59 + Some(trimmed.to_string()) 60 + } else { 61 + None 62 + } 63 + } 64 + 65 + fn strip_handle_prefixes(handle: &str) -> &str { 66 + if let Some(value) = handle.strip_prefix("at://") { 67 + value 68 + } else if let Some(value) = handle.strip_prefix('@') { 69 + value 70 + } else { 71 + handle 72 + } 73 + } 74 + 75 + /// Validates if a string is a properly formatted PLC DID. 76 + /// Checks for correct prefix and 24-character identifier length. 77 + pub fn is_valid_did_method_plc(did: &str) -> bool { 78 + let did_value = match did.strip_prefix("did:plc:") { 79 + Some(value) => value, 80 + None => return false, 81 + }; 82 + 83 + did_value.len() == 24 84 + }
+107
src/web.rs
··· 1 + use anyhow::Result; 2 + 3 + use super::errors::WebDIDError; 4 + use super::model::Document; 5 + 6 + /// Converts a did:web DID to its corresponding HTTPS URL. 7 + /// Transforms DID format to the expected well-known document location. 8 + pub fn did_web_to_url(did: &str) -> Result<String, WebDIDError> { 9 + let parts = did 10 + .strip_prefix("did:web:") 11 + .ok_or(WebDIDError::InvalidDIDPrefix)? 12 + .split(':') 13 + .collect::<Vec<&str>>(); 14 + 15 + let hostname = parts.first().ok_or(WebDIDError::MissingHostname)?; 16 + if hostname.is_empty() { 17 + return Err(WebDIDError::MissingHostname); 18 + } 19 + let path_parts = &parts[1..]; 20 + 21 + let url = if path_parts.is_empty() { 22 + format!("https://{}/.well-known/did.json", hostname) 23 + } else { 24 + format!("https://{}/{}/did.json", hostname, path_parts.join("/")) 25 + }; 26 + 27 + Ok(url) 28 + } 29 + 30 + /// Queries a did:web DID document from its hosting location. 31 + /// Resolves the DID to HTTPS URL and fetches the JSON document. 32 + pub async fn query(http_client: &reqwest::Client, did: &str) -> Result<Document> { 33 + let url = did_web_to_url(did)?; 34 + 35 + http_client 36 + .get(&url) 37 + .send() 38 + .await 39 + .map_err(|error| WebDIDError::HttpRequestFailed { 40 + url: url.clone(), 41 + error, 42 + })? 43 + .json::<Document>() 44 + .await 45 + .map_err(|error| WebDIDError::DocumentParseFailed { url, error }) 46 + .map_err(Into::into) 47 + } 48 + 49 + /// Queries a DID document directly from a hostname's well-known location. 50 + /// Fetches from https://{hostname}/.well-known/did.json 51 + pub async fn query_hostname(http_client: &reqwest::Client, hostname: &str) -> Result<Document> { 52 + let url = format!("https://{}/.well-known/did.json", hostname); 53 + 54 + http_client 55 + .get(&url) 56 + .send() 57 + .await 58 + .map_err(|error| WebDIDError::HttpRequestFailed { 59 + url: url.clone(), 60 + error, 61 + })? 62 + .json::<Document>() 63 + .await 64 + .map_err(|error| WebDIDError::DocumentParseFailed { url, error }) 65 + .map_err(Into::into) 66 + } 67 + 68 + #[cfg(test)] 69 + mod tests { 70 + use super::*; 71 + 72 + #[test] 73 + fn test_did_web_to_url_simple_hostname() { 74 + let result = did_web_to_url("did:web:example.com"); 75 + assert_eq!(result.unwrap(), "https://example.com/.well-known/did.json"); 76 + } 77 + 78 + #[test] 79 + fn test_did_web_to_url_with_path() { 80 + let result = did_web_to_url("did:web:example.com:path"); 81 + assert_eq!(result.unwrap(), "https://example.com/path/did.json"); 82 + } 83 + 84 + #[test] 85 + fn test_did_web_to_url_with_nested_path() { 86 + let result = did_web_to_url("did:web:example.com:path:subpath"); 87 + assert_eq!(result.unwrap(), "https://example.com/path/subpath/did.json"); 88 + } 89 + 90 + #[test] 91 + fn test_did_web_to_url_invalid_prefix() { 92 + let result = did_web_to_url("did:plc:example.com"); 93 + assert!(matches!(result, Err(WebDIDError::InvalidDIDPrefix))); 94 + } 95 + 96 + #[test] 97 + fn test_did_web_to_url_missing_hostname() { 98 + let result = did_web_to_url("did:web:"); 99 + assert!(matches!(result, Err(WebDIDError::MissingHostname))); 100 + } 101 + 102 + #[test] 103 + fn test_did_web_to_url_no_prefix() { 104 + let result = did_web_to_url("example.com"); 105 + assert!(matches!(result, Err(WebDIDError::InvalidDIDPrefix))); 106 + } 107 + }