Microservice to bring 2FA to self hosted PDSes

Adding an auth module with all the scopes, handles, and did checking goodies that can be used as middleware layers in axum inspired from my attribute macros elsewhere.

example

```
// Multi-community endpoint
.route("/xrpc/community.shared.moderation.report",
post(report).layer(from_fn_with_state(
with_rules(AuthRules::All(vec![
AuthRules::HandleEndsWithAny(vec![
".blacksky.team".into(),
".bsky.team".into(),
".mod.social".into(),
]),
AuthRules::ScopeEquals("atproto".into()),
]), &state),
auth_middleware
)))
```

authored by Clinton Bowen and committed by tangled.org a49ee039 2e39f1e7

+279
AUTH.md
···
··· 1 + # PDS Gatekeeper Authentication Middleware 2 + 3 + This document describes the authentication middleware system for pds-gatekeeper, which provides flexible authorization rules based on DIDs, handles, and OAuth scopes. 4 + 5 + ## Overview 6 + 7 + The auth middleware validates incoming requests by: 8 + 9 + 1. **Extracting** the DID and scopes from a JWT Bearer token 10 + 2. **Resolving** the DID to a handle using jacquard-identity 11 + 3. **Validating** against configured authorization rules 12 + 4. **Returning** appropriate HTTP errors (401/403) on failure 13 + 14 + ## Quick Start 15 + 16 + ```rust 17 + use axum::middleware::from_fn_with_state; 18 + use crate::auth::{auth_middleware, handle_ends_with, scope_equals, with_rules, AuthRules}; 19 + 20 + let app = Router::new() 21 + // Simple: require handle from specific domain 22 + .route("/xrpc/community.blacksky.feed.get", 23 + get(handler).layer(from_fn_with_state( 24 + handle_ends_with(".blacksky.team", &state), 25 + auth_middleware 26 + ))) 27 + 28 + // Simple: require specific OAuth scope 29 + .route("/xrpc/com.atproto.repo.createRecord", 30 + post(handler).layer(from_fn_with_state( 31 + scope_equals("repo:app.bsky.feed.post", &state), 32 + auth_middleware 33 + ))) 34 + 35 + .with_state(state); 36 + ``` 37 + 38 + ## ATProto OAuth Scopes Reference 39 + 40 + | Scope | Description | 41 + |-------|-------------| 42 + | `atproto` | Base scope, required for all OAuth clients | 43 + | `transition:generic` | Full repository access (equivalent to app passwords) | 44 + | `repo:<collection>` | Access to specific collection (e.g., `repo:app.bsky.feed.post`) | 45 + | `identity:handle` | Permits handle changes | 46 + | `identity:*` | Full DID document control | 47 + | `account:email` | Read email addresses | 48 + | `account:repo?action=manage` | Import repository data | 49 + | `blob:*/*` | Upload any blob type | 50 + | `blob?accept=image/*` | Upload only images | 51 + 52 + See [Marvin's Guide to OAuth Scopes](https://marvins-guide.leaflet.pub/3mbfvey7sok26) for complete details. 53 + 54 + ## Helper Functions 55 + 56 + ### Identity Helpers 57 + 58 + | Function | Description | 59 + |----------|-------------| 60 + | `handle_ends_with(suffix, state)` | Handle must end with suffix | 61 + | `handle_ends_with_any(suffixes, state)` | Handle must end with any suffix (OR) | 62 + | `did_equals(did, state)` | DID must match exactly | 63 + | `did_equals_any(dids, state)` | DID must match any value (OR) | 64 + 65 + ### Scope Helpers 66 + 67 + | Function | Description | 68 + |----------|-------------| 69 + | `scope_equals(scope, state)` | Must have specific scope | 70 + | `scope_any(scopes, state)` | Must have any of the scopes (OR) | 71 + | `scope_all(scopes, state)` | Must have all scopes (AND) | 72 + 73 + ### Combined Helpers (Identity + Scope) 74 + 75 + | Function | Description | 76 + |----------|-------------| 77 + | `handle_ends_with_and_scope(suffix, scope, state)` | Handle suffix AND scope | 78 + | `handle_ends_with_and_scopes(suffix, scopes, state)` | Handle suffix AND all scopes | 79 + | `did_with_scope(did, scope, state)` | DID match AND scope | 80 + | `did_with_scopes(did, scopes, state)` | DID match AND all scopes | 81 + 82 + ### Custom Rules 83 + 84 + For complex authorization logic, use `with_rules()`: 85 + 86 + ```rust 87 + with_rules(AuthRules::Any(vec![ 88 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 89 + AuthRules::All(vec![ 90 + AuthRules::HandleEndsWith(".mod.team".into()), 91 + AuthRules::ScopeEquals("account:email".into()), 92 + ]), 93 + ]), &state) 94 + ``` 95 + 96 + ## Realistic PDS Endpoint Examples 97 + 98 + ### Admin Endpoints 99 + 100 + Based on `com.atproto.admin.*` endpoints from the ATProto PDS: 101 + 102 + ```rust 103 + // com.atproto.admin.deleteAccount 104 + // Admin-only: specific DID with full access scope 105 + .route("/xrpc/com.atproto.admin.deleteAccount", 106 + post(delete_account).layer(from_fn_with_state( 107 + did_with_scope("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "transition:generic", &state), 108 + auth_middleware 109 + ))) 110 + 111 + // com.atproto.admin.getAccountInfo 112 + // Either admin DID OR (moderator handle + account scope) 113 + .route("/xrpc/com.atproto.admin.getAccountInfo", 114 + get(get_account_info).layer(from_fn_with_state( 115 + with_rules(AuthRules::Any(vec![ 116 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 117 + AuthRules::All(vec![ 118 + AuthRules::HandleEndsWith(".mod.team".into()), 119 + AuthRules::ScopeEquals("account:email".into()), 120 + ]), 121 + ]), &state), 122 + auth_middleware 123 + ))) 124 + 125 + // com.atproto.admin.updateAccountEmail 126 + // Admin DID with account management scope 127 + .route("/xrpc/com.atproto.admin.updateAccountEmail", 128 + post(update_email).layer(from_fn_with_state( 129 + did_with_scopes( 130 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 131 + ["account:email", "account:repo?action=manage"], 132 + &state 133 + ), 134 + auth_middleware 135 + ))) 136 + 137 + // com.atproto.admin.updateAccountHandle 138 + // Admin with identity control 139 + .route("/xrpc/com.atproto.admin.updateAccountHandle", 140 + post(update_handle).layer(from_fn_with_state( 141 + did_with_scope("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "identity:*", &state), 142 + auth_middleware 143 + ))) 144 + ``` 145 + 146 + ### Repository Endpoints 147 + 148 + ```rust 149 + // com.atproto.repo.createRecord 150 + // Scoped write access to specific collection 151 + .route("/xrpc/com.atproto.repo.createRecord", 152 + post(create_record).layer(from_fn_with_state( 153 + scope_equals("repo:app.bsky.feed.post", &state), 154 + auth_middleware 155 + ))) 156 + 157 + // com.atproto.repo.putRecord 158 + // Either specific collection scope OR full access 159 + .route("/xrpc/com.atproto.repo.putRecord", 160 + post(put_record).layer(from_fn_with_state( 161 + scope_any(["repo:app.bsky.feed.post", "transition:generic"], &state), 162 + auth_middleware 163 + ))) 164 + 165 + // com.atproto.repo.uploadBlob 166 + // Blob upload with media type restriction (scope-based) 167 + .route("/xrpc/com.atproto.repo.uploadBlob", 168 + post(upload_blob).layer(from_fn_with_state( 169 + scope_any(["blob:*/*", "blob?accept=image/*", "transition:generic"], &state), 170 + auth_middleware 171 + ))) 172 + ``` 173 + 174 + ### Community/Custom Endpoints 175 + 176 + ```rust 177 + // Community feed generator - restricted to team members with full access 178 + .route("/xrpc/community.blacksky.feed.generator", 179 + post(generator).layer(from_fn_with_state( 180 + handle_ends_with_and_scope(".blacksky.team", "transition:generic", &state), 181 + auth_middleware 182 + ))) 183 + 184 + // Multi-community endpoint 185 + .route("/xrpc/community.shared.moderation.report", 186 + post(report).layer(from_fn_with_state( 187 + with_rules(AuthRules::All(vec![ 188 + AuthRules::HandleEndsWithAny(vec![ 189 + ".blacksky.team".into(), 190 + ".bsky.team".into(), 191 + ".mod.social".into(), 192 + ]), 193 + AuthRules::ScopeEquals("atproto".into()), 194 + ]), &state), 195 + auth_middleware 196 + ))) 197 + 198 + // VIP access - specific DIDs only 199 + .route("/xrpc/community.blacksky.vip.access", 200 + get(vip_handler).layer(from_fn_with_state( 201 + did_equals_any([ 202 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 203 + "did:plc:abc123def456ghi789jklmno", 204 + "did:plc:xyz987uvw654rst321qponml", 205 + ], &state), 206 + auth_middleware 207 + ))) 208 + ``` 209 + 210 + ## Building Complex Authorization Rules 211 + 212 + The `AuthRules` enum supports arbitrary nesting: 213 + 214 + ```rust 215 + // Complex: Admin OR (Team member with write scope) OR (Moderator with read-only) 216 + let rules = AuthRules::Any(vec![ 217 + // Admin bypass 218 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 219 + 220 + // Team member with write access 221 + AuthRules::All(vec![ 222 + AuthRules::HandleEndsWith(".blacksky.team".into()), 223 + AuthRules::ScopeEquals("transition:generic".into()), 224 + ]), 225 + 226 + // Moderator with limited scope 227 + AuthRules::All(vec![ 228 + AuthRules::HandleEndsWith(".mod.team".into()), 229 + AuthRules::ScopeEqualsAny(vec![ 230 + "account:email".into(), 231 + "atproto".into(), 232 + ]), 233 + ]), 234 + ]); 235 + ``` 236 + 237 + ## Error Responses 238 + 239 + | Status | Error Code | Description | 240 + |--------|------------|-------------| 241 + | `401` | `AuthRequired` | No Authorization header provided | 242 + | `401` | `InvalidToken` | JWT validation failed (expired, invalid signature, malformed) | 243 + | `403` | `AccessDenied` | Valid authentication but authorization rules rejected | 244 + | `500` | `ResolutionError` | Failed to resolve DID to handle | 245 + 246 + Response format: 247 + ```json 248 + { 249 + "error": "AccessDenied", 250 + "message": "Access denied by authorization rules" 251 + } 252 + ``` 253 + 254 + ## JWT Token Format 255 + 256 + The middleware expects JWT tokens with these claims: 257 + 258 + ```json 259 + { 260 + "sub": "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 261 + "scope": "atproto transition:generic repo:app.bsky.feed.post", 262 + "iat": 1704067200, 263 + "exp": 1704153600 264 + } 265 + ``` 266 + 267 + - `sub` (required): The user's DID 268 + - `scope` (optional): Space-separated OAuth scopes per [RFC 6749](https://tools.ietf.org/html/rfc6749) 269 + 270 + ## Handle Resolution 271 + 272 + DIDs are resolved to handles using the jacquard-identity `PublicResolver`: 273 + 274 + 1. Check the `HandleCache` for a cached result 275 + 2. If miss, resolve the DID document via PLC directory 276 + 3. Extract handle from `alsoKnownAs` field (format: `at://handle.example.com`) 277 + 4. Cache the result (1 hour TTL default) 278 + 279 + This allows rules like `HandleEndsWith(".blacksky.team")` to work even though the JWT only contains the DID.
+759 -605
Cargo.lock
··· 22 ] 23 24 [[package]] 25 - name = "addr2line" 26 - version = "0.24.2" 27 - source = "registry+https://github.com/rust-lang/crates.io-index" 28 - checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 29 - dependencies = [ 30 - "gimli", 31 - ] 32 - 33 - [[package]] 34 name = "adler2" 35 version = "2.0.1" 36 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 50 51 [[package]] 52 name = "aho-corasick" 53 - version = "1.1.3" 54 source = "registry+https://github.com/rust-lang/crates.io-index" 55 - checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 56 dependencies = [ 57 "memchr", 58 ] ··· 80 81 [[package]] 82 name = "anyhow" 83 - version = "1.0.99" 84 source = "registry+https://github.com/rust-lang/crates.io-index" 85 - checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 86 87 [[package]] 88 name = "async-compression" 89 - version = "0.4.27" 90 source = "registry+https://github.com/rust-lang/crates.io-index" 91 - checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" 92 dependencies = [ 93 - "flate2", 94 "futures-core", 95 - "memchr", 96 "pin-project-lite", 97 "tokio", 98 - "zstd", 99 - "zstd-safe", 100 ] 101 102 [[package]] ··· 107 dependencies = [ 108 "proc-macro2", 109 "quote", 110 - "syn 2.0.105", 111 ] 112 113 [[package]] ··· 120 ] 121 122 [[package]] 123 name = "atomic-waker" 124 version = "1.1.2" 125 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 133 134 [[package]] 135 name = "aws-lc-rs" 136 - version = "1.13.3" 137 source = "registry+https://github.com/rust-lang/crates.io-index" 138 - checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" 139 dependencies = [ 140 "aws-lc-sys", 141 "untrusted 0.7.1", ··· 144 145 [[package]] 146 name = "aws-lc-sys" 147 - version = "0.30.0" 148 source = "registry+https://github.com/rust-lang/crates.io-index" 149 - checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" 150 dependencies = [ 151 - "bindgen", 152 "cc", 153 "cmake", 154 "dunce", ··· 157 158 [[package]] 159 name = "axum" 160 - version = "0.8.4" 161 source = "registry+https://github.com/rust-lang/crates.io-index" 162 - checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" 163 dependencies = [ 164 "axum-core", 165 "axum-macros", ··· 177 "mime", 178 "percent-encoding", 179 "pin-project-lite", 180 - "rustversion", 181 - "serde", 182 "serde_json", 183 "serde_path_to_error", 184 "serde_urlencoded", ··· 192 193 [[package]] 194 name = "axum-core" 195 - version = "0.5.2" 196 source = "registry+https://github.com/rust-lang/crates.io-index" 197 - checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" 198 dependencies = [ 199 "bytes", 200 "futures-core", ··· 203 "http-body-util", 204 "mime", 205 "pin-project-lite", 206 - "rustversion", 207 "sync_wrapper", 208 "tower-layer", 209 "tower-service", ··· 218 dependencies = [ 219 "proc-macro2", 220 "quote", 221 - "syn 2.0.105", 222 ] 223 224 [[package]] ··· 230 "axum", 231 "handlebars", 232 "serde", 233 - "thiserror 2.0.14", 234 - ] 235 - 236 - [[package]] 237 - name = "backtrace" 238 - version = "0.3.75" 239 - source = "registry+https://github.com/rust-lang/crates.io-index" 240 - checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 241 - dependencies = [ 242 - "addr2line", 243 - "cfg-if", 244 - "libc", 245 - "miniz_oxide", 246 - "object", 247 - "rustc-demangle", 248 - "windows-targets 0.52.6", 249 ] 250 251 [[package]] ··· 278 279 [[package]] 280 name = "base64ct" 281 - version = "1.8.0" 282 source = "registry+https://github.com/rust-lang/crates.io-index" 283 - checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" 284 - 285 - [[package]] 286 - name = "bindgen" 287 - version = "0.69.5" 288 - source = "registry+https://github.com/rust-lang/crates.io-index" 289 - checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" 290 - dependencies = [ 291 - "bitflags", 292 - "cexpr", 293 - "clang-sys", 294 - "itertools", 295 - "lazy_static", 296 - "lazycell", 297 - "log", 298 - "prettyplease", 299 - "proc-macro2", 300 - "quote", 301 - "regex", 302 - "rustc-hash 1.1.0", 303 - "shlex", 304 - "syn 2.0.105", 305 - "which", 306 - ] 307 308 [[package]] 309 name = "bitflags" 310 - version = "2.9.1" 311 source = "registry+https://github.com/rust-lang/crates.io-index" 312 - checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 313 dependencies = [ 314 - "serde", 315 ] 316 317 [[package]] ··· 345 "proc-macro2", 346 "quote", 347 "rustversion", 348 - "syn 2.0.105", 349 ] 350 351 [[package]] ··· 359 360 [[package]] 361 name = "bstr" 362 - version = "1.12.0" 363 source = "registry+https://github.com/rust-lang/crates.io-index" 364 - checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" 365 dependencies = [ 366 "memchr", 367 "serde", ··· 393 394 [[package]] 395 name = "bumpalo" 396 - version = "3.19.0" 397 source = "registry+https://github.com/rust-lang/crates.io-index" 398 - checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 399 400 [[package]] 401 name = "byteorder" ··· 405 406 [[package]] 407 name = "bytes" 408 - version = "1.10.1" 409 source = "registry+https://github.com/rust-lang/crates.io-index" 410 - checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 411 dependencies = [ 412 "serde", 413 ] ··· 423 424 [[package]] 425 name = "cc" 426 - version = "1.2.32" 427 source = "registry+https://github.com/rust-lang/crates.io-index" 428 - checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" 429 dependencies = [ 430 "jobserver", 431 "libc", 432 "shlex", ··· 442 ] 443 444 [[package]] 445 - name = "cexpr" 446 - version = "0.6.0" 447 - source = "registry+https://github.com/rust-lang/crates.io-index" 448 - checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 449 - dependencies = [ 450 - "nom 7.1.3", 451 - ] 452 - 453 - [[package]] 454 name = "cfg-if" 455 - version = "1.0.1" 456 source = "registry+https://github.com/rust-lang/crates.io-index" 457 - checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 458 459 [[package]] 460 name = "cfg_aliases" ··· 473 "num-traits", 474 "serde", 475 "wasm-bindgen", 476 - "windows-link 0.2.1", 477 ] 478 479 [[package]] ··· 538 ] 539 540 [[package]] 541 - name = "clang-sys" 542 - version = "1.8.1" 543 source = "registry+https://github.com/rust-lang/crates.io-index" 544 - checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 545 dependencies = [ 546 - "glob", 547 - "libc", 548 - "libloading", 549 ] 550 551 [[package]] 552 - name = "cmake" 553 - version = "0.1.54" 554 source = "registry+https://github.com/rust-lang/crates.io-index" 555 - checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" 556 dependencies = [ 557 - "cc", 558 ] 559 560 [[package]] 561 name = "concurrent-queue" ··· 579 checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 580 581 [[package]] 582 name = "core-foundation" 583 version = "0.9.4" 584 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 614 615 [[package]] 616 name = "crc" 617 - version = "3.3.0" 618 source = "registry+https://github.com/rust-lang/crates.io-index" 619 - checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" 620 dependencies = [ 621 "crc-catalog", 622 ] ··· 635 dependencies = [ 636 "cfg-if", 637 ] 638 639 [[package]] 640 name = "crossbeam-queue" ··· 671 672 [[package]] 673 name = "crypto-common" 674 - version = "0.1.6" 675 source = "registry+https://github.com/rust-lang/crates.io-index" 676 - checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 677 dependencies = [ 678 "generic-array", 679 "typenum", ··· 710 "proc-macro2", 711 "quote", 712 "strsim", 713 - "syn 2.0.105", 714 ] 715 716 [[package]] ··· 724 "proc-macro2", 725 "quote", 726 "strsim", 727 - "syn 2.0.105", 728 ] 729 730 [[package]] ··· 735 dependencies = [ 736 "darling_core 0.20.11", 737 "quote", 738 - "syn 2.0.105", 739 ] 740 741 [[package]] ··· 746 dependencies = [ 747 "darling_core 0.21.3", 748 "quote", 749 - "syn 2.0.105", 750 ] 751 752 [[package]] ··· 786 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 787 dependencies = [ 788 "data-encoding", 789 - "syn 2.0.105", 790 ] 791 792 [[package]] ··· 828 "darling 0.20.11", 829 "proc-macro2", 830 "quote", 831 - "syn 2.0.105", 832 ] 833 834 [[package]] ··· 838 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" 839 dependencies = [ 840 "derive_builder_core", 841 - "syn 2.0.105", 842 ] 843 844 [[package]] 845 name = "digest" ··· 861 dependencies = [ 862 "proc-macro2", 863 "quote", 864 - "syn 2.0.105", 865 ] 866 867 [[package]] ··· 942 checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" 943 944 [[package]] 945 name = "encoding_rs" 946 version = "0.8.35" 947 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 958 959 [[package]] 960 name = "errno" 961 - version = "0.3.13" 962 source = "registry+https://github.com/rust-lang/crates.io-index" 963 - checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 964 dependencies = [ 965 "libc", 966 "windows-sys 0.59.0", ··· 1005 ] 1006 1007 [[package]] 1008 name = "flate2" 1009 version = "1.1.5" 1010 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1022 dependencies = [ 1023 "futures-core", 1024 "futures-sink", 1025 - "spin", 1026 ] 1027 1028 [[package]] ··· 1038 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1039 1040 [[package]] 1041 name = "foreign-types" 1042 version = "0.3.2" 1043 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1054 1055 [[package]] 1056 name = "form_urlencoded" 1057 - version = "1.2.1" 1058 source = "registry+https://github.com/rust-lang/crates.io-index" 1059 - checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1060 dependencies = [ 1061 "percent-encoding", 1062 ] ··· 1078 checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 1079 1080 [[package]] 1081 name = "futures-channel" 1082 version = "0.3.31" 1083 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1122 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1123 1124 [[package]] 1125 name = "futures-macro" 1126 version = "0.3.31" 1127 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1129 dependencies = [ 1130 "proc-macro2", 1131 "quote", 1132 - "syn 2.0.105", 1133 ] 1134 1135 [[package]] ··· 1168 ] 1169 1170 [[package]] 1171 name = "generic-array" 1172 version = "0.14.7" 1173 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1206 ] 1207 1208 [[package]] 1209 - name = "gimli" 1210 - version = "0.31.1" 1211 - source = "registry+https://github.com/rust-lang/crates.io-index" 1212 - checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 1213 - 1214 - [[package]] 1215 - name = "glob" 1216 - version = "0.3.3" 1217 - source = "registry+https://github.com/rust-lang/crates.io-index" 1218 - checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" 1219 - 1220 - [[package]] 1221 name = "globset" 1222 - version = "0.4.16" 1223 source = "registry+https://github.com/rust-lang/crates.io-index" 1224 - checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 1225 dependencies = [ 1226 "aho-corasick", 1227 "bstr", 1228 "log", 1229 - "regex-automata 0.4.13", 1230 - "regex-syntax 0.8.5", 1231 ] 1232 1233 [[package]] 1234 name = "governor" 1235 - version = "0.10.1" 1236 source = "registry+https://github.com/rust-lang/crates.io-index" 1237 - checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19" 1238 dependencies = [ 1239 "cfg-if", 1240 "dashmap", ··· 1242 "futures-timer", 1243 "futures-util", 1244 "getrandom 0.3.4", 1245 - "hashbrown 0.15.5", 1246 "nonzero_ext", 1247 "parking_lot", 1248 "portable-atomic", ··· 1276 "futures-core", 1277 "futures-sink", 1278 "http", 1279 - "indexmap 2.10.0", 1280 "slab", 1281 "tokio", 1282 "tokio-util", ··· 1285 1286 [[package]] 1287 name = "half" 1288 - version = "2.6.0" 1289 source = "registry+https://github.com/rust-lang/crates.io-index" 1290 - checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 1291 dependencies = [ 1292 "cfg-if", 1293 "crunchy", 1294 ] 1295 1296 [[package]] 1297 name = "handlebars" 1298 - version = "6.3.2" 1299 source = "registry+https://github.com/rust-lang/crates.io-index" 1300 - checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" 1301 dependencies = [ 1302 "derive_builder", 1303 "log", ··· 1307 "rust-embed", 1308 "serde", 1309 "serde_json", 1310 - "thiserror 2.0.14", 1311 ] 1312 1313 [[package]] ··· 1334 dependencies = [ 1335 "allocator-api2", 1336 "equivalent", 1337 - "foldhash", 1338 ] 1339 1340 [[package]] ··· 1347 ] 1348 1349 [[package]] 1350 name = "heck" 1351 version = "0.4.1" 1352 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1390 1391 [[package]] 1392 name = "home" 1393 - version = "0.5.11" 1394 source = "registry+https://github.com/rust-lang/crates.io-index" 1395 - checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 1396 dependencies = [ 1397 - "windows-sys 0.59.0", 1398 ] 1399 1400 [[package]] ··· 1408 1409 [[package]] 1410 name = "http" 1411 - version = "1.3.1" 1412 source = "registry+https://github.com/rust-lang/crates.io-index" 1413 - checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 1414 dependencies = [ 1415 "bytes", 1416 - "fnv", 1417 "itoa", 1418 ] 1419 ··· 1454 1455 [[package]] 1456 name = "hyper" 1457 - version = "1.6.0" 1458 source = "registry+https://github.com/rust-lang/crates.io-index" 1459 - checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 1460 dependencies = [ 1461 "bytes", 1462 "futures-channel", 1463 - "futures-util", 1464 "h2", 1465 "http", 1466 "http-body", ··· 1468 "httpdate", 1469 "itoa", 1470 "pin-project-lite", 1471 "smallvec", 1472 "tokio", 1473 "want", ··· 1487 "tokio", 1488 "tokio-rustls", 1489 "tower-service", 1490 - "webpki-roots 1.0.2", 1491 ] 1492 1493 [[package]] ··· 1505 1506 [[package]] 1507 name = "hyper-util" 1508 - version = "0.1.16" 1509 source = "registry+https://github.com/rust-lang/crates.io-index" 1510 - checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 1511 dependencies = [ 1512 "base64", 1513 "bytes", ··· 1531 1532 [[package]] 1533 name = "iana-time-zone" 1534 - version = "0.1.63" 1535 source = "registry+https://github.com/rust-lang/crates.io-index" 1536 - checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 1537 dependencies = [ 1538 "android_system_properties", 1539 "core-foundation-sys", ··· 1555 1556 [[package]] 1557 name = "icu_collections" 1558 - version = "2.0.0" 1559 source = "registry+https://github.com/rust-lang/crates.io-index" 1560 - checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 1561 dependencies = [ 1562 "displaydoc", 1563 "potential_utf", ··· 1568 1569 [[package]] 1570 name = "icu_locale_core" 1571 - version = "2.0.0" 1572 source = "registry+https://github.com/rust-lang/crates.io-index" 1573 - checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 1574 dependencies = [ 1575 "displaydoc", 1576 "litemap", ··· 1581 1582 [[package]] 1583 name = "icu_normalizer" 1584 - version = "2.0.0" 1585 source = "registry+https://github.com/rust-lang/crates.io-index" 1586 - checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 1587 dependencies = [ 1588 - "displaydoc", 1589 "icu_collections", 1590 "icu_normalizer_data", 1591 "icu_properties", ··· 1596 1597 [[package]] 1598 name = "icu_normalizer_data" 1599 - version = "2.0.0" 1600 source = "registry+https://github.com/rust-lang/crates.io-index" 1601 - checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 1602 1603 [[package]] 1604 name = "icu_properties" 1605 - version = "2.0.1" 1606 source = "registry+https://github.com/rust-lang/crates.io-index" 1607 - checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 1608 dependencies = [ 1609 - "displaydoc", 1610 "icu_collections", 1611 "icu_locale_core", 1612 "icu_properties_data", 1613 "icu_provider", 1614 - "potential_utf", 1615 "zerotrie", 1616 "zerovec", 1617 ] 1618 1619 [[package]] 1620 name = "icu_properties_data" 1621 - version = "2.0.1" 1622 source = "registry+https://github.com/rust-lang/crates.io-index" 1623 - checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 1624 1625 [[package]] 1626 name = "icu_provider" 1627 - version = "2.0.0" 1628 source = "registry+https://github.com/rust-lang/crates.io-index" 1629 - checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 1630 dependencies = [ 1631 "displaydoc", 1632 "icu_locale_core", 1633 - "stable_deref_trait", 1634 - "tinystr", 1635 "writeable", 1636 "yoke", 1637 "zerofrom", ··· 1647 1648 [[package]] 1649 name = "idna" 1650 - version = "1.0.3" 1651 source = "registry+https://github.com/rust-lang/crates.io-index" 1652 - checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 1653 dependencies = [ 1654 "idna_adapter", 1655 "smallvec", ··· 1679 1680 [[package]] 1681 name = "indexmap" 1682 - version = "2.10.0" 1683 source = "registry+https://github.com/rust-lang/crates.io-index" 1684 - checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 1685 dependencies = [ 1686 "equivalent", 1687 - "hashbrown 0.15.5", 1688 "serde", 1689 ] 1690 1691 [[package]] ··· 1716 ] 1717 1718 [[package]] 1719 - name = "io-uring" 1720 - version = "0.7.9" 1721 - source = "registry+https://github.com/rust-lang/crates.io-index" 1722 - checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" 1723 - dependencies = [ 1724 - "bitflags", 1725 - "cfg-if", 1726 - "libc", 1727 - ] 1728 - 1729 - [[package]] 1730 name = "ipld-core" 1731 version = "0.4.2" 1732 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1745 1746 [[package]] 1747 name = "iri-string" 1748 - version = "0.7.9" 1749 source = "registry+https://github.com/rust-lang/crates.io-index" 1750 - checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" 1751 dependencies = [ 1752 "memchr", 1753 "serde", 1754 ] 1755 1756 [[package]] 1757 - name = "itertools" 1758 - version = "0.12.1" 1759 - source = "registry+https://github.com/rust-lang/crates.io-index" 1760 - checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 1761 - dependencies = [ 1762 - "either", 1763 - ] 1764 - 1765 - [[package]] 1766 name = "itoa" 1767 - version = "1.0.15" 1768 source = "registry+https://github.com/rust-lang/crates.io-index" 1769 - checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1770 1771 [[package]] 1772 name = "jacquard-api" 1773 - version = "0.9.2" 1774 source = "registry+https://github.com/rust-lang/crates.io-index" 1775 - checksum = "bbbfd6e2b10fa1731f4d4e40c8f791956b0d4f804fb3efef891afec903f20597" 1776 dependencies = [ 1777 "bon", 1778 "bytes", ··· 1782 "miette", 1783 "rustversion", 1784 "serde", 1785 "serde_ipld_dagcbor", 1786 - "thiserror 2.0.14", 1787 "unicode-segmentation", 1788 ] 1789 1790 [[package]] 1791 name = "jacquard-common" 1792 - version = "0.9.2" 1793 source = "registry+https://github.com/rust-lang/crates.io-index" 1794 - checksum = "df86cb117d9f1c2b0251ba67c3f0e3f963fd22abc6cf8de0e02a7fc846c288ca" 1795 dependencies = [ 1796 "base64", 1797 "bon", ··· 1809 "multihash", 1810 "ouroboros", 1811 "p256", 1812 "rand 0.9.2", 1813 "regex", 1814 "regex-lite", 1815 "reqwest", 1816 "serde", 1817 "serde_html_form", 1818 "serde_ipld_dagcbor", 1819 "serde_json", 1820 "signature", 1821 "smol_str", 1822 - "thiserror 2.0.14", 1823 "tokio", 1824 "tokio-util", 1825 "trait-variant", ··· 1828 1829 [[package]] 1830 name = "jacquard-derive" 1831 - version = "0.9.2" 1832 source = "registry+https://github.com/rust-lang/crates.io-index" 1833 - checksum = "42ca61a69dc7aa8fb2d7163416514ff7df5d79f2e8b22e269f4610afa85572fe" 1834 dependencies = [ 1835 "heck 0.5.0", 1836 "jacquard-lexicon", 1837 "proc-macro2", 1838 "quote", 1839 - "syn 2.0.105", 1840 ] 1841 1842 [[package]] 1843 name = "jacquard-identity" 1844 - version = "0.9.2" 1845 source = "registry+https://github.com/rust-lang/crates.io-index" 1846 - checksum = "1ef714cacebfca486558a9f8e205daf466bfba0466c4d0c450fd6d0252400a53" 1847 dependencies = [ 1848 "bon", 1849 "bytes", ··· 1852 "jacquard-common", 1853 "jacquard-lexicon", 1854 "miette", 1855 "percent-encoding", 1856 "reqwest", 1857 "serde", 1858 "serde_html_form", 1859 "serde_json", 1860 - "thiserror 2.0.14", 1861 "tokio", 1862 "trait-variant", 1863 "url", ··· 1866 1867 [[package]] 1868 name = "jacquard-lexicon" 1869 - version = "0.9.2" 1870 source = "registry+https://github.com/rust-lang/crates.io-index" 1871 - checksum = "de87f2c938faea1b1f1b32d5b9e0c870e7b5bb5efbf96e3692ae2d8f6b2beb7a" 1872 dependencies = [ 1873 "cid", 1874 "dashmap", ··· 1886 "serde_repr", 1887 "serde_with", 1888 "sha2", 1889 - "syn 2.0.105", 1890 - "thiserror 2.0.14", 1891 "unicode-segmentation", 1892 ] 1893 1894 [[package]] 1895 name = "jobserver" 1896 - version = "0.1.33" 1897 source = "registry+https://github.com/rust-lang/crates.io-index" 1898 - checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 1899 dependencies = [ 1900 "getrandom 0.3.4", 1901 "libc", ··· 1914 "regex", 1915 "serde", 1916 "serde_json", 1917 - "thiserror 2.0.14", 1918 "time", 1919 ] 1920 1921 [[package]] 1922 name = "js-sys" 1923 - version = "0.3.77" 1924 source = "registry+https://github.com/rust-lang/crates.io-index" 1925 - checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 1926 dependencies = [ 1927 "once_cell", 1928 "wasm-bindgen", ··· 1979 source = "registry+https://github.com/rust-lang/crates.io-index" 1980 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1981 dependencies = [ 1982 - "spin", 1983 ] 1984 1985 [[package]] 1986 - name = "lazycell" 1987 - version = "1.3.0" 1988 - source = "registry+https://github.com/rust-lang/crates.io-index" 1989 - checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 1990 - 1991 - [[package]] 1992 name = "lettre" 1993 - version = "0.11.18" 1994 source = "registry+https://github.com/rust-lang/crates.io-index" 1995 - checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56" 1996 dependencies = [ 1997 "async-trait", 1998 "base64", ··· 2013 "tokio", 2014 "tokio-rustls", 2015 "url", 2016 - "webpki-roots 1.0.2", 2017 ] 2018 2019 [[package]] 2020 name = "libc" 2021 - version = "0.2.175" 2022 - source = "registry+https://github.com/rust-lang/crates.io-index" 2023 - checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 2024 - 2025 - [[package]] 2026 - name = "libloading" 2027 - version = "0.8.8" 2028 source = "registry+https://github.com/rust-lang/crates.io-index" 2029 - checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" 2030 - dependencies = [ 2031 - "cfg-if", 2032 - "windows-targets 0.52.6", 2033 - ] 2034 2035 [[package]] 2036 name = "libm" ··· 2040 2041 [[package]] 2042 name = "libredox" 2043 - version = "0.1.9" 2044 source = "registry+https://github.com/rust-lang/crates.io-index" 2045 - checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" 2046 dependencies = [ 2047 "bitflags", 2048 "libc", 2049 - "redox_syscall", 2050 ] 2051 2052 [[package]] ··· 2059 "pkg-config", 2060 "vcpkg", 2061 ] 2062 - 2063 - [[package]] 2064 - name = "linux-raw-sys" 2065 - version = "0.4.15" 2066 - source = "registry+https://github.com/rust-lang/crates.io-index" 2067 - checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 2068 2069 [[package]] 2070 name = "litemap" 2071 - version = "0.8.0" 2072 source = "registry+https://github.com/rust-lang/crates.io-index" 2073 - checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 2074 2075 [[package]] 2076 name = "lock_api" 2077 - version = "0.4.13" 2078 source = "registry+https://github.com/rust-lang/crates.io-index" 2079 - checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 2080 dependencies = [ 2081 - "autocfg", 2082 "scopeguard", 2083 ] 2084 2085 [[package]] 2086 name = "log" 2087 - version = "0.4.27" 2088 source = "registry+https://github.com/rust-lang/crates.io-index" 2089 - checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 2090 2091 [[package]] 2092 name = "lru-slab" ··· 2107 2108 [[package]] 2109 name = "matchers" 2110 - version = "0.1.0" 2111 source = "registry+https://github.com/rust-lang/crates.io-index" 2112 - checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 2113 dependencies = [ 2114 - "regex-automata 0.1.10", 2115 ] 2116 2117 [[package]] ··· 2132 2133 [[package]] 2134 name = "memchr" 2135 - version = "2.7.5" 2136 source = "registry+https://github.com/rust-lang/crates.io-index" 2137 - checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 2138 2139 [[package]] 2140 name = "miette" ··· 2155 dependencies = [ 2156 "proc-macro2", 2157 "quote", 2158 - "syn 2.0.105", 2159 ] 2160 2161 [[package]] ··· 2182 2183 [[package]] 2184 name = "mio" 2185 - version = "1.0.4" 2186 source = "registry+https://github.com/rust-lang/crates.io-index" 2187 - checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 2188 dependencies = [ 2189 "libc", 2190 "wasi", 2191 - "windows-sys 0.59.0", 2192 ] 2193 2194 [[package]] ··· 2215 ] 2216 2217 [[package]] 2218 name = "nom" 2219 version = "7.1.3" 2220 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2247 2248 [[package]] 2249 name = "nu-ansi-term" 2250 - version = "0.46.0" 2251 source = "registry+https://github.com/rust-lang/crates.io-index" 2252 - checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 2253 dependencies = [ 2254 - "overload", 2255 - "winapi", 2256 ] 2257 2258 [[package]] 2259 name = "num-bigint-dig" 2260 - version = "0.8.4" 2261 source = "registry+https://github.com/rust-lang/crates.io-index" 2262 - checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" 2263 dependencies = [ 2264 - "byteorder", 2265 "lazy_static", 2266 "libm", 2267 "num-integer", ··· 2325 2326 [[package]] 2327 name = "object" 2328 - version = "0.36.7" 2329 source = "registry+https://github.com/rust-lang/crates.io-index" 2330 - checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 2331 dependencies = [ 2332 "memchr", 2333 ] ··· 2361 dependencies = [ 2362 "proc-macro2", 2363 "quote", 2364 - "syn 2.0.105", 2365 ] 2366 2367 [[package]] ··· 2397 "proc-macro2", 2398 "proc-macro2-diagnostics", 2399 "quote", 2400 - "syn 2.0.105", 2401 ] 2402 2403 [[package]] 2404 - name = "overload" 2405 - version = "0.1.1" 2406 - source = "registry+https://github.com/rust-lang/crates.io-index" 2407 - checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 2408 - 2409 - [[package]] 2410 name = "p256" 2411 version = "0.13.2" 2412 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2426 2427 [[package]] 2428 name = "parking_lot" 2429 - version = "0.12.4" 2430 source = "registry+https://github.com/rust-lang/crates.io-index" 2431 - checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 2432 dependencies = [ 2433 "lock_api", 2434 "parking_lot_core", ··· 2436 2437 [[package]] 2438 name = "parking_lot_core" 2439 - version = "0.9.11" 2440 source = "registry+https://github.com/rust-lang/crates.io-index" 2441 - checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 2442 dependencies = [ 2443 "cfg-if", 2444 "libc", 2445 - "redox_syscall", 2446 "smallvec", 2447 - "windows-targets 0.52.6", 2448 ] 2449 2450 [[package]] ··· 2477 "axum", 2478 "axum-template", 2479 "chrono", 2480 "dotenvy", 2481 "handlebars", 2482 "hex", ··· 2498 "sha2", 2499 "sqlx", 2500 "tokio", 2501 "tower-http", 2502 "tower_governor", 2503 "tracing", ··· 2516 2517 [[package]] 2518 name = "percent-encoding" 2519 - version = "2.3.1" 2520 source = "registry+https://github.com/rust-lang/crates.io-index" 2521 - checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 2522 2523 [[package]] 2524 name = "pest" 2525 - version = "2.8.1" 2526 source = "registry+https://github.com/rust-lang/crates.io-index" 2527 - checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" 2528 dependencies = [ 2529 "memchr", 2530 - "thiserror 2.0.14", 2531 "ucd-trie", 2532 ] 2533 2534 [[package]] 2535 name = "pest_derive" 2536 - version = "2.8.1" 2537 source = "registry+https://github.com/rust-lang/crates.io-index" 2538 - checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" 2539 dependencies = [ 2540 "pest", 2541 "pest_generator", ··· 2543 2544 [[package]] 2545 name = "pest_generator" 2546 - version = "2.8.1" 2547 source = "registry+https://github.com/rust-lang/crates.io-index" 2548 - checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" 2549 dependencies = [ 2550 "pest", 2551 "pest_meta", 2552 "proc-macro2", 2553 "quote", 2554 - "syn 2.0.105", 2555 ] 2556 2557 [[package]] 2558 name = "pest_meta" 2559 - version = "2.8.1" 2560 source = "registry+https://github.com/rust-lang/crates.io-index" 2561 - checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" 2562 dependencies = [ 2563 "pest", 2564 "sha2", ··· 2581 dependencies = [ 2582 "proc-macro2", 2583 "quote", 2584 - "syn 2.0.105", 2585 ] 2586 2587 [[package]] ··· 2625 2626 [[package]] 2627 name = "portable-atomic" 2628 - version = "1.11.1" 2629 source = "registry+https://github.com/rust-lang/crates.io-index" 2630 - checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 2631 2632 [[package]] 2633 name = "potential_utf" 2634 - version = "0.1.2" 2635 source = "registry+https://github.com/rust-lang/crates.io-index" 2636 - checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 2637 dependencies = [ 2638 "zerovec", 2639 ] ··· 2655 2656 [[package]] 2657 name = "prettyplease" 2658 - version = "0.2.35" 2659 source = "registry+https://github.com/rust-lang/crates.io-index" 2660 - checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" 2661 dependencies = [ 2662 "proc-macro2", 2663 - "syn 2.0.105", 2664 ] 2665 2666 [[package]] ··· 2698 2699 [[package]] 2700 name = "proc-macro2" 2701 - version = "1.0.97" 2702 source = "registry+https://github.com/rust-lang/crates.io-index" 2703 - checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" 2704 dependencies = [ 2705 "unicode-ident", 2706 ] ··· 2713 dependencies = [ 2714 "proc-macro2", 2715 "quote", 2716 - "syn 2.0.105", 2717 "version_check", 2718 "yansi", 2719 ] 2720 2721 [[package]] 2722 name = "psm" 2723 - version = "0.1.26" 2724 source = "registry+https://github.com/rust-lang/crates.io-index" 2725 - checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" 2726 dependencies = [ 2727 "cc", 2728 ] 2729 ··· 2753 "pin-project-lite", 2754 "quinn-proto", 2755 "quinn-udp", 2756 - "rustc-hash 2.1.1", 2757 "rustls", 2758 "socket2", 2759 - "thiserror 2.0.14", 2760 "tokio", 2761 "tracing", 2762 "web-time", ··· 2773 "lru-slab", 2774 "rand 0.9.2", 2775 "ring", 2776 - "rustc-hash 2.1.1", 2777 "rustls", 2778 "rustls-pki-types", 2779 "slab", 2780 - "thiserror 2.0.14", 2781 "tinyvec", 2782 "tracing", 2783 "web-time", ··· 2799 2800 [[package]] 2801 name = "quote" 2802 - version = "1.0.40" 2803 source = "registry+https://github.com/rust-lang/crates.io-index" 2804 - checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 2805 dependencies = [ 2806 "proc-macro2", 2807 ] ··· 2885 2886 [[package]] 2887 name = "raw-cpuid" 2888 - version = "11.5.0" 2889 source = "registry+https://github.com/rust-lang/crates.io-index" 2890 - checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" 2891 dependencies = [ 2892 "bitflags", 2893 ] 2894 2895 [[package]] 2896 name = "redox_syscall" 2897 - version = "0.5.17" 2898 source = "registry+https://github.com/rust-lang/crates.io-index" 2899 - checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" 2900 dependencies = [ 2901 "bitflags", 2902 ] ··· 2918 dependencies = [ 2919 "proc-macro2", 2920 "quote", 2921 - "syn 2.0.105", 2922 ] 2923 2924 [[package]] ··· 2929 dependencies = [ 2930 "aho-corasick", 2931 "memchr", 2932 - "regex-automata 0.4.13", 2933 - "regex-syntax 0.8.5", 2934 - ] 2935 - 2936 - [[package]] 2937 - name = "regex-automata" 2938 - version = "0.1.10" 2939 - source = "registry+https://github.com/rust-lang/crates.io-index" 2940 - checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 2941 - dependencies = [ 2942 - "regex-syntax 0.6.29", 2943 ] 2944 2945 [[package]] ··· 2950 dependencies = [ 2951 "aho-corasick", 2952 "memchr", 2953 - "regex-syntax 0.8.5", 2954 ] 2955 2956 [[package]] ··· 2961 2962 [[package]] 2963 name = "regex-syntax" 2964 - version = "0.6.29" 2965 source = "registry+https://github.com/rust-lang/crates.io-index" 2966 - checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 2967 - 2968 - [[package]] 2969 - name = "regex-syntax" 2970 - version = "0.8.5" 2971 - source = "registry+https://github.com/rust-lang/crates.io-index" 2972 - checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 2973 2974 [[package]] 2975 name = "reqwest" 2976 - version = "0.12.24" 2977 source = "registry+https://github.com/rust-lang/crates.io-index" 2978 - checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" 2979 dependencies = [ 2980 - "async-compression", 2981 "base64", 2982 "bytes", 2983 "encoding_rs", 2984 "futures-core", 2985 - "futures-util", 2986 "h2", 2987 "http", 2988 "http-body", ··· 3004 "sync_wrapper", 3005 "tokio", 3006 "tokio-rustls", 3007 - "tokio-util", 3008 "tower", 3009 "tower-http", 3010 "tower-service", 3011 "url", 3012 "wasm-bindgen", 3013 "wasm-bindgen-futures", 3014 - "wasm-streams", 3015 "web-sys", 3016 - "webpki-roots 1.0.2", 3017 ] 3018 3019 [[package]] ··· 3042 3043 [[package]] 3044 name = "rsa" 3045 - version = "0.9.8" 3046 source = "registry+https://github.com/rust-lang/crates.io-index" 3047 - checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" 3048 dependencies = [ 3049 "const-oid", 3050 "digest", ··· 3062 3063 [[package]] 3064 name = "rust-embed" 3065 - version = "8.7.2" 3066 source = "registry+https://github.com/rust-lang/crates.io-index" 3067 - checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" 3068 dependencies = [ 3069 "rust-embed-impl", 3070 "rust-embed-utils", ··· 3073 3074 [[package]] 3075 name = "rust-embed-impl" 3076 - version = "8.7.2" 3077 source = "registry+https://github.com/rust-lang/crates.io-index" 3078 - checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" 3079 dependencies = [ 3080 "proc-macro2", 3081 "quote", 3082 "rust-embed-utils", 3083 - "syn 2.0.105", 3084 "walkdir", 3085 ] 3086 3087 [[package]] 3088 name = "rust-embed-utils" 3089 - version = "8.7.2" 3090 source = "registry+https://github.com/rust-lang/crates.io-index" 3091 - checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" 3092 dependencies = [ 3093 "globset", 3094 "sha2", ··· 3096 ] 3097 3098 [[package]] 3099 - name = "rustc-demangle" 3100 - version = "0.1.26" 3101 - source = "registry+https://github.com/rust-lang/crates.io-index" 3102 - checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 3103 - 3104 - [[package]] 3105 - name = "rustc-hash" 3106 - version = "1.1.0" 3107 - source = "registry+https://github.com/rust-lang/crates.io-index" 3108 - checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 3109 - 3110 - [[package]] 3111 name = "rustc-hash" 3112 version = "2.1.1" 3113 source = "registry+https://github.com/rust-lang/crates.io-index" 3114 checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3115 3116 [[package]] 3117 - name = "rustix" 3118 - version = "0.38.44" 3119 source = "registry+https://github.com/rust-lang/crates.io-index" 3120 - checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 3121 dependencies = [ 3122 - "bitflags", 3123 - "errno", 3124 - "libc", 3125 - "linux-raw-sys", 3126 - "windows-sys 0.59.0", 3127 ] 3128 3129 [[package]] 3130 name = "rustls" 3131 - version = "0.23.31" 3132 source = "registry+https://github.com/rust-lang/crates.io-index" 3133 - checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 3134 dependencies = [ 3135 "aws-lc-rs", 3136 "log", ··· 3144 3145 [[package]] 3146 name = "rustls-pki-types" 3147 - version = "1.12.0" 3148 source = "registry+https://github.com/rust-lang/crates.io-index" 3149 - checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 3150 dependencies = [ 3151 "web-time", 3152 "zeroize", ··· 3154 3155 [[package]] 3156 name = "rustls-webpki" 3157 - version = "0.103.4" 3158 source = "registry+https://github.com/rust-lang/crates.io-index" 3159 - checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 3160 dependencies = [ 3161 "aws-lc-rs", 3162 "ring", ··· 3172 3173 [[package]] 3174 name = "ryu" 3175 - version = "1.0.20" 3176 source = "registry+https://github.com/rust-lang/crates.io-index" 3177 - checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 3178 3179 [[package]] 3180 name = "salsa20" ··· 3208 3209 [[package]] 3210 name = "schemars" 3211 - version = "1.1.0" 3212 source = "registry+https://github.com/rust-lang/crates.io-index" 3213 - checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" 3214 dependencies = [ 3215 "dyn-clone", 3216 "ref-cast", ··· 3219 ] 3220 3221 [[package]] 3222 name = "scopeguard" 3223 version = "1.2.0" 3224 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3269 ] 3270 3271 [[package]] 3272 name = "serde" 3273 version = "1.0.228" 3274 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3305 dependencies = [ 3306 "proc-macro2", 3307 "quote", 3308 - "syn 2.0.105", 3309 ] 3310 3311 [[package]] ··· 3315 checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3316 dependencies = [ 3317 "form_urlencoded", 3318 - "indexmap 2.10.0", 3319 "itoa", 3320 "ryu", 3321 "serde_core", ··· 3335 3336 [[package]] 3337 name = "serde_json" 3338 - version = "1.0.145" 3339 source = "registry+https://github.com/rust-lang/crates.io-index" 3340 - checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 3341 dependencies = [ 3342 - "indexmap 2.10.0", 3343 "itoa", 3344 "memchr", 3345 - "ryu", 3346 "serde", 3347 "serde_core", 3348 ] 3349 3350 [[package]] 3351 name = "serde_path_to_error" 3352 - version = "0.1.17" 3353 source = "registry+https://github.com/rust-lang/crates.io-index" 3354 - checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 3355 dependencies = [ 3356 "itoa", 3357 "serde", 3358 ] 3359 3360 [[package]] ··· 3365 dependencies = [ 3366 "proc-macro2", 3367 "quote", 3368 - "syn 2.0.105", 3369 ] 3370 3371 [[package]] ··· 3382 3383 [[package]] 3384 name = "serde_with" 3385 - version = "3.16.0" 3386 source = "registry+https://github.com/rust-lang/crates.io-index" 3387 - checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" 3388 dependencies = [ 3389 "base64", 3390 "chrono", 3391 "hex", 3392 "indexmap 1.9.3", 3393 - "indexmap 2.10.0", 3394 "schemars 0.9.0", 3395 - "schemars 1.1.0", 3396 "serde_core", 3397 "serde_json", 3398 "serde_with_macros", ··· 3401 3402 [[package]] 3403 name = "serde_with_macros" 3404 - version = "3.16.0" 3405 source = "registry+https://github.com/rust-lang/crates.io-index" 3406 - checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" 3407 dependencies = [ 3408 "darling 0.21.3", 3409 "proc-macro2", 3410 "quote", 3411 - "syn 2.0.105", 3412 ] 3413 3414 [[package]] ··· 3450 3451 [[package]] 3452 name = "signal-hook-registry" 3453 - version = "1.4.6" 3454 source = "registry+https://github.com/rust-lang/crates.io-index" 3455 - checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 3456 dependencies = [ 3457 "libc", 3458 ] 3459 ··· 3469 3470 [[package]] 3471 name = "simd-adler32" 3472 - version = "0.3.7" 3473 source = "registry+https://github.com/rust-lang/crates.io-index" 3474 - checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 3475 3476 [[package]] 3477 name = "slab" ··· 3500 3501 [[package]] 3502 name = "socket2" 3503 - version = "0.6.0" 3504 source = "registry+https://github.com/rust-lang/crates.io-index" 3505 - checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 3506 dependencies = [ 3507 "libc", 3508 - "windows-sys 0.59.0", 3509 ] 3510 3511 [[package]] ··· 3518 ] 3519 3520 [[package]] 3521 name = "spinning_top" 3522 version = "0.3.0" 3523 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3568 "futures-util", 3569 "hashbrown 0.15.5", 3570 "hashlink", 3571 - "indexmap 2.10.0", 3572 "log", 3573 "memchr", 3574 "once_cell", ··· 3578 "serde_json", 3579 "sha2", 3580 "smallvec", 3581 - "thiserror 2.0.14", 3582 "tokio", 3583 "tokio-stream", 3584 "tracing", ··· 3596 "quote", 3597 "sqlx-core", 3598 "sqlx-macros-core", 3599 - "syn 2.0.105", 3600 ] 3601 3602 [[package]] ··· 3619 "sqlx-mysql", 3620 "sqlx-postgres", 3621 "sqlx-sqlite", 3622 - "syn 2.0.105", 3623 "tokio", 3624 "url", 3625 ] ··· 3662 "smallvec", 3663 "sqlx-core", 3664 "stringprep", 3665 - "thiserror 2.0.14", 3666 "tracing", 3667 "whoami", 3668 ] ··· 3700 "smallvec", 3701 "sqlx-core", 3702 "stringprep", 3703 - "thiserror 2.0.14", 3704 "tracing", 3705 "whoami", 3706 ] ··· 3725 "serde", 3726 "serde_urlencoded", 3727 "sqlx-core", 3728 - "thiserror 2.0.14", 3729 "tracing", 3730 "url", 3731 ] 3732 3733 [[package]] 3734 name = "stable_deref_trait" 3735 - version = "1.2.0" 3736 source = "registry+https://github.com/rust-lang/crates.io-index" 3737 - checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 3738 3739 [[package]] 3740 name = "stacker" 3741 - version = "0.1.21" 3742 source = "registry+https://github.com/rust-lang/crates.io-index" 3743 - checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" 3744 dependencies = [ 3745 "cc", 3746 "cfg-if", ··· 3765 "quote", 3766 "serde", 3767 "sha2", 3768 - "syn 2.0.105", 3769 "thiserror 1.0.69", 3770 ] 3771 ··· 3811 3812 [[package]] 3813 name = "syn" 3814 - version = "2.0.105" 3815 source = "registry+https://github.com/rust-lang/crates.io-index" 3816 - checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" 3817 dependencies = [ 3818 "proc-macro2", 3819 "quote", ··· 3837 dependencies = [ 3838 "proc-macro2", 3839 "quote", 3840 - "syn 2.0.105", 3841 ] 3842 3843 [[package]] ··· 3872 3873 [[package]] 3874 name = "thiserror" 3875 - version = "2.0.14" 3876 source = "registry+https://github.com/rust-lang/crates.io-index" 3877 - checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" 3878 dependencies = [ 3879 - "thiserror-impl 2.0.14", 3880 ] 3881 3882 [[package]] ··· 3887 dependencies = [ 3888 "proc-macro2", 3889 "quote", 3890 - "syn 2.0.105", 3891 ] 3892 3893 [[package]] 3894 name = "thiserror-impl" 3895 - version = "2.0.14" 3896 source = "registry+https://github.com/rust-lang/crates.io-index" 3897 - checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" 3898 dependencies = [ 3899 "proc-macro2", 3900 "quote", 3901 - "syn 2.0.105", 3902 ] 3903 3904 [[package]] ··· 3943 3944 [[package]] 3945 name = "tinystr" 3946 - version = "0.8.1" 3947 source = "registry+https://github.com/rust-lang/crates.io-index" 3948 - checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 3949 dependencies = [ 3950 "displaydoc", 3951 "zerovec", ··· 3953 3954 [[package]] 3955 name = "tinyvec" 3956 - version = "1.9.0" 3957 source = "registry+https://github.com/rust-lang/crates.io-index" 3958 - checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 3959 dependencies = [ 3960 "tinyvec_macros", 3961 ] ··· 3968 3969 [[package]] 3970 name = "tokio" 3971 - version = "1.47.1" 3972 source = "registry+https://github.com/rust-lang/crates.io-index" 3973 - checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" 3974 dependencies = [ 3975 - "backtrace", 3976 "bytes", 3977 - "io-uring", 3978 "libc", 3979 "mio", 3980 "pin-project-lite", 3981 "signal-hook-registry", 3982 - "slab", 3983 "socket2", 3984 "tokio-macros", 3985 - "windows-sys 0.59.0", 3986 ] 3987 3988 [[package]] 3989 name = "tokio-macros" 3990 - version = "2.5.0" 3991 source = "registry+https://github.com/rust-lang/crates.io-index" 3992 - checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 3993 dependencies = [ 3994 "proc-macro2", 3995 "quote", 3996 - "syn 2.0.105", 3997 ] 3998 3999 [[package]] 4000 name = "tokio-rustls" 4001 - version = "0.26.2" 4002 source = "registry+https://github.com/rust-lang/crates.io-index" 4003 - checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 4004 dependencies = [ 4005 "rustls", 4006 "tokio", ··· 4026 "bytes", 4027 "futures-core", 4028 "futures-sink", 4029 "pin-project-lite", 4030 "tokio", 4031 ] 4032 4033 [[package]] 4034 name = "tonic" 4035 - version = "0.14.1" 4036 source = "registry+https://github.com/rust-lang/crates.io-index" 4037 - checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" 4038 dependencies = [ 4039 "async-trait", 4040 "axum", ··· 4067 dependencies = [ 4068 "futures-core", 4069 "futures-util", 4070 - "indexmap 2.10.0", 4071 "pin-project-lite", 4072 "slab", 4073 "sync_wrapper", ··· 4080 4081 [[package]] 4082 name = "tower-http" 4083 - version = "0.6.6" 4084 source = "registry+https://github.com/rust-lang/crates.io-index" 4085 - checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 4086 dependencies = [ 4087 "async-compression", 4088 "bitflags", ··· 4091 "futures-util", 4092 "http", 4093 "http-body", 4094 "iri-string", 4095 "pin-project-lite", 4096 "tokio", ··· 4123 "governor", 4124 "http", 4125 "pin-project", 4126 - "thiserror 2.0.14", 4127 "tonic", 4128 "tower", 4129 "tracing", ··· 4131 4132 [[package]] 4133 name = "tracing" 4134 - version = "0.1.41" 4135 source = "registry+https://github.com/rust-lang/crates.io-index" 4136 - checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 4137 dependencies = [ 4138 "log", 4139 "pin-project-lite", ··· 4143 4144 [[package]] 4145 name = "tracing-attributes" 4146 - version = "0.1.30" 4147 source = "registry+https://github.com/rust-lang/crates.io-index" 4148 - checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" 4149 dependencies = [ 4150 "proc-macro2", 4151 "quote", 4152 - "syn 2.0.105", 4153 ] 4154 4155 [[package]] 4156 name = "tracing-core" 4157 - version = "0.1.34" 4158 source = "registry+https://github.com/rust-lang/crates.io-index" 4159 - checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 4160 dependencies = [ 4161 "once_cell", 4162 "valuable", ··· 4175 4176 [[package]] 4177 name = "tracing-subscriber" 4178 - version = "0.3.19" 4179 source = "registry+https://github.com/rust-lang/crates.io-index" 4180 - checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 4181 dependencies = [ 4182 "matchers", 4183 "nu-ansi-term", 4184 "once_cell", 4185 - "regex", 4186 "sharded-slab", 4187 "smallvec", 4188 "thread_local", ··· 4199 dependencies = [ 4200 "proc-macro2", 4201 "quote", 4202 - "syn 2.0.105", 4203 ] 4204 4205 [[package]] ··· 4210 4211 [[package]] 4212 name = "typenum" 4213 - version = "1.18.0" 4214 source = "registry+https://github.com/rust-lang/crates.io-index" 4215 - checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 4216 4217 [[package]] 4218 name = "ucd-trie" ··· 4228 4229 [[package]] 4230 name = "unicode-ident" 4231 - version = "1.0.18" 4232 source = "registry+https://github.com/rust-lang/crates.io-index" 4233 - checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 4234 4235 [[package]] 4236 name = "unicode-normalization" 4237 - version = "0.1.24" 4238 source = "registry+https://github.com/rust-lang/crates.io-index" 4239 - checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 4240 dependencies = [ 4241 "tinyvec", 4242 ] 4243 4244 [[package]] 4245 name = "unicode-properties" 4246 - version = "0.1.3" 4247 source = "registry+https://github.com/rust-lang/crates.io-index" 4248 - checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 4249 4250 [[package]] 4251 name = "unicode-segmentation" ··· 4260 checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 4261 4262 [[package]] 4263 name = "unsigned-varint" 4264 version = "0.8.0" 4265 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4279 4280 [[package]] 4281 name = "url" 4282 - version = "2.5.4" 4283 source = "registry+https://github.com/rust-lang/crates.io-index" 4284 - checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 4285 dependencies = [ 4286 "form_urlencoded", 4287 "idna", ··· 4367 4368 [[package]] 4369 name = "wasm-bindgen" 4370 - version = "0.2.100" 4371 source = "registry+https://github.com/rust-lang/crates.io-index" 4372 - checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 4373 dependencies = [ 4374 "cfg-if", 4375 "once_cell", 4376 "rustversion", 4377 "wasm-bindgen-macro", 4378 - ] 4379 - 4380 - [[package]] 4381 - name = "wasm-bindgen-backend" 4382 - version = "0.2.100" 4383 - source = "registry+https://github.com/rust-lang/crates.io-index" 4384 - checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 4385 - dependencies = [ 4386 - "bumpalo", 4387 - "log", 4388 - "proc-macro2", 4389 - "quote", 4390 - "syn 2.0.105", 4391 "wasm-bindgen-shared", 4392 ] 4393 4394 [[package]] 4395 name = "wasm-bindgen-futures" 4396 - version = "0.4.50" 4397 source = "registry+https://github.com/rust-lang/crates.io-index" 4398 - checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 4399 dependencies = [ 4400 "cfg-if", 4401 "js-sys", ··· 4406 4407 [[package]] 4408 name = "wasm-bindgen-macro" 4409 - version = "0.2.100" 4410 source = "registry+https://github.com/rust-lang/crates.io-index" 4411 - checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 4412 dependencies = [ 4413 "quote", 4414 "wasm-bindgen-macro-support", ··· 4416 4417 [[package]] 4418 name = "wasm-bindgen-macro-support" 4419 - version = "0.2.100" 4420 source = "registry+https://github.com/rust-lang/crates.io-index" 4421 - checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 4422 dependencies = [ 4423 "proc-macro2", 4424 "quote", 4425 - "syn 2.0.105", 4426 - "wasm-bindgen-backend", 4427 "wasm-bindgen-shared", 4428 ] 4429 4430 [[package]] 4431 name = "wasm-bindgen-shared" 4432 - version = "0.2.100" 4433 source = "registry+https://github.com/rust-lang/crates.io-index" 4434 - checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 4435 dependencies = [ 4436 "unicode-ident", 4437 ] 4438 4439 [[package]] 4440 - name = "wasm-streams" 4441 - version = "0.4.2" 4442 - source = "registry+https://github.com/rust-lang/crates.io-index" 4443 - checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 4444 - dependencies = [ 4445 - "futures-util", 4446 - "js-sys", 4447 - "wasm-bindgen", 4448 - "wasm-bindgen-futures", 4449 - "web-sys", 4450 - ] 4451 - 4452 - [[package]] 4453 name = "web-sys" 4454 - version = "0.3.77" 4455 source = "registry+https://github.com/rust-lang/crates.io-index" 4456 - checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 4457 dependencies = [ 4458 "js-sys", 4459 "wasm-bindgen", ··· 4475 source = "registry+https://github.com/rust-lang/crates.io-index" 4476 checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 4477 dependencies = [ 4478 - "webpki-roots 1.0.2", 4479 ] 4480 4481 [[package]] 4482 name = "webpki-roots" 4483 - version = "1.0.2" 4484 source = "registry+https://github.com/rust-lang/crates.io-index" 4485 - checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" 4486 dependencies = [ 4487 "rustls-pki-types", 4488 - ] 4489 - 4490 - [[package]] 4491 - name = "which" 4492 - version = "4.4.2" 4493 - source = "registry+https://github.com/rust-lang/crates.io-index" 4494 - checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 4495 - dependencies = [ 4496 - "either", 4497 - "home", 4498 - "once_cell", 4499 - "rustix", 4500 ] 4501 4502 [[package]] ··· 4527 4528 [[package]] 4529 name = "winapi-util" 4530 - version = "0.1.9" 4531 source = "registry+https://github.com/rust-lang/crates.io-index" 4532 - checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 4533 dependencies = [ 4534 - "windows-sys 0.59.0", 4535 ] 4536 4537 [[package]] ··· 4542 4543 [[package]] 4544 name = "windows-core" 4545 - version = "0.61.2" 4546 source = "registry+https://github.com/rust-lang/crates.io-index" 4547 - checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 4548 dependencies = [ 4549 "windows-implement", 4550 "windows-interface", 4551 - "windows-link 0.1.3", 4552 "windows-result", 4553 "windows-strings", 4554 ] 4555 4556 [[package]] 4557 name = "windows-implement" 4558 - version = "0.60.0" 4559 source = "registry+https://github.com/rust-lang/crates.io-index" 4560 - checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 4561 dependencies = [ 4562 "proc-macro2", 4563 "quote", 4564 - "syn 2.0.105", 4565 ] 4566 4567 [[package]] 4568 name = "windows-interface" 4569 - version = "0.59.1" 4570 source = "registry+https://github.com/rust-lang/crates.io-index" 4571 - checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 4572 dependencies = [ 4573 "proc-macro2", 4574 "quote", 4575 - "syn 2.0.105", 4576 ] 4577 4578 [[package]] 4579 name = "windows-link" 4580 - version = "0.1.3" 4581 - source = "registry+https://github.com/rust-lang/crates.io-index" 4582 - checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 4583 - 4584 - [[package]] 4585 - name = "windows-link" 4586 version = "0.2.1" 4587 source = "registry+https://github.com/rust-lang/crates.io-index" 4588 checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 4589 4590 [[package]] 4591 name = "windows-registry" 4592 - version = "0.5.3" 4593 source = "registry+https://github.com/rust-lang/crates.io-index" 4594 - checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" 4595 dependencies = [ 4596 - "windows-link 0.1.3", 4597 "windows-result", 4598 "windows-strings", 4599 ] 4600 4601 [[package]] 4602 name = "windows-result" 4603 - version = "0.3.4" 4604 source = "registry+https://github.com/rust-lang/crates.io-index" 4605 - checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 4606 dependencies = [ 4607 - "windows-link 0.1.3", 4608 ] 4609 4610 [[package]] 4611 name = "windows-strings" 4612 - version = "0.4.2" 4613 source = "registry+https://github.com/rust-lang/crates.io-index" 4614 - checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 4615 dependencies = [ 4616 - "windows-link 0.1.3", 4617 ] 4618 4619 [[package]] ··· 4644 ] 4645 4646 [[package]] 4647 name = "windows-targets" 4648 version = "0.48.5" 4649 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4667 "windows_aarch64_gnullvm 0.52.6", 4668 "windows_aarch64_msvc 0.52.6", 4669 "windows_i686_gnu 0.52.6", 4670 - "windows_i686_gnullvm", 4671 "windows_i686_msvc 0.52.6", 4672 "windows_x86_64_gnu 0.52.6", 4673 "windows_x86_64_gnullvm 0.52.6", ··· 4675 ] 4676 4677 [[package]] 4678 name = "windows_aarch64_gnullvm" 4679 version = "0.48.5" 4680 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4687 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 4688 4689 [[package]] 4690 name = "windows_aarch64_msvc" 4691 version = "0.48.5" 4692 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4699 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 4700 4701 [[package]] 4702 name = "windows_i686_gnu" 4703 version = "0.48.5" 4704 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4711 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 4712 4713 [[package]] 4714 name = "windows_i686_gnullvm" 4715 version = "0.52.6" 4716 source = "registry+https://github.com/rust-lang/crates.io-index" 4717 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 4718 4719 [[package]] 4720 name = "windows_i686_msvc" ··· 4729 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 4730 4731 [[package]] 4732 name = "windows_x86_64_gnu" 4733 version = "0.48.5" 4734 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4741 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 4742 4743 [[package]] 4744 name = "windows_x86_64_gnullvm" 4745 version = "0.48.5" 4746 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4753 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 4754 4755 [[package]] 4756 name = "windows_x86_64_msvc" 4757 version = "0.48.5" 4758 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4765 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 4766 4767 [[package]] 4768 name = "wit-bindgen" 4769 version = "0.46.0" 4770 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4772 4773 [[package]] 4774 name = "writeable" 4775 - version = "0.6.1" 4776 source = "registry+https://github.com/rust-lang/crates.io-index" 4777 - checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 4778 4779 [[package]] 4780 name = "yansi" ··· 4784 4785 [[package]] 4786 name = "yoke" 4787 - version = "0.8.0" 4788 source = "registry+https://github.com/rust-lang/crates.io-index" 4789 - checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 4790 dependencies = [ 4791 - "serde", 4792 "stable_deref_trait", 4793 "yoke-derive", 4794 "zerofrom", ··· 4796 4797 [[package]] 4798 name = "yoke-derive" 4799 - version = "0.8.0" 4800 source = "registry+https://github.com/rust-lang/crates.io-index" 4801 - checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 4802 dependencies = [ 4803 "proc-macro2", 4804 "quote", 4805 - "syn 2.0.105", 4806 "synstructure", 4807 ] 4808 4809 [[package]] 4810 name = "zerocopy" 4811 - version = "0.8.26" 4812 source = "registry+https://github.com/rust-lang/crates.io-index" 4813 - checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" 4814 dependencies = [ 4815 "zerocopy-derive", 4816 ] 4817 4818 [[package]] 4819 name = "zerocopy-derive" 4820 - version = "0.8.26" 4821 source = "registry+https://github.com/rust-lang/crates.io-index" 4822 - checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" 4823 dependencies = [ 4824 "proc-macro2", 4825 "quote", 4826 - "syn 2.0.105", 4827 ] 4828 4829 [[package]] ··· 4843 dependencies = [ 4844 "proc-macro2", 4845 "quote", 4846 - "syn 2.0.105", 4847 "synstructure", 4848 ] 4849 4850 [[package]] 4851 name = "zeroize" 4852 - version = "1.8.1" 4853 source = "registry+https://github.com/rust-lang/crates.io-index" 4854 - checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 4855 dependencies = [ 4856 "zeroize_derive", 4857 ] 4858 4859 [[package]] 4860 name = "zeroize_derive" 4861 - version = "1.4.2" 4862 source = "registry+https://github.com/rust-lang/crates.io-index" 4863 - checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 4864 dependencies = [ 4865 "proc-macro2", 4866 "quote", 4867 - "syn 2.0.105", 4868 ] 4869 4870 [[package]] 4871 name = "zerotrie" 4872 - version = "0.2.2" 4873 source = "registry+https://github.com/rust-lang/crates.io-index" 4874 - checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 4875 dependencies = [ 4876 "displaydoc", 4877 "yoke", ··· 4880 4881 [[package]] 4882 name = "zerovec" 4883 - version = "0.11.4" 4884 source = "registry+https://github.com/rust-lang/crates.io-index" 4885 - checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 4886 dependencies = [ 4887 "yoke", 4888 "zerofrom", ··· 4891 4892 [[package]] 4893 name = "zerovec-derive" 4894 - version = "0.11.1" 4895 source = "registry+https://github.com/rust-lang/crates.io-index" 4896 - checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 4897 dependencies = [ 4898 "proc-macro2", 4899 "quote", 4900 - "syn 2.0.105", 4901 ] 4902 4903 [[package]] 4904 name = "zstd" 4905 version = "0.13.3" 4906 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4920 4921 [[package]] 4922 name = "zstd-sys" 4923 - version = "2.0.15+zstd.1.5.7" 4924 source = "registry+https://github.com/rust-lang/crates.io-index" 4925 - checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" 4926 dependencies = [ 4927 "cc", 4928 "pkg-config",
··· 22 ] 23 24 [[package]] 25 name = "adler2" 26 version = "2.0.1" 27 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 41 42 [[package]] 43 name = "aho-corasick" 44 + version = "1.1.4" 45 source = "registry+https://github.com/rust-lang/crates.io-index" 46 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 47 dependencies = [ 48 "memchr", 49 ] ··· 71 72 [[package]] 73 name = "anyhow" 74 + version = "1.0.100" 75 + source = "registry+https://github.com/rust-lang/crates.io-index" 76 + checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 77 + 78 + [[package]] 79 + name = "ar_archive_writer" 80 + version = "0.2.0" 81 source = "registry+https://github.com/rust-lang/crates.io-index" 82 + checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" 83 + dependencies = [ 84 + "object", 85 + ] 86 87 [[package]] 88 name = "async-compression" 89 + version = "0.4.36" 90 source = "registry+https://github.com/rust-lang/crates.io-index" 91 + checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" 92 dependencies = [ 93 + "compression-codecs", 94 + "compression-core", 95 "futures-core", 96 "pin-project-lite", 97 "tokio", 98 ] 99 100 [[package]] ··· 105 dependencies = [ 106 "proc-macro2", 107 "quote", 108 + "syn 2.0.112", 109 ] 110 111 [[package]] ··· 118 ] 119 120 [[package]] 121 + name = "atomic-polyfill" 122 + version = "1.0.3" 123 + source = "registry+https://github.com/rust-lang/crates.io-index" 124 + checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 125 + dependencies = [ 126 + "critical-section", 127 + ] 128 + 129 + [[package]] 130 name = "atomic-waker" 131 version = "1.1.2" 132 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 140 141 [[package]] 142 name = "aws-lc-rs" 143 + version = "1.15.2" 144 source = "registry+https://github.com/rust-lang/crates.io-index" 145 + checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" 146 dependencies = [ 147 "aws-lc-sys", 148 "untrusted 0.7.1", ··· 151 152 [[package]] 153 name = "aws-lc-sys" 154 + version = "0.35.0" 155 source = "registry+https://github.com/rust-lang/crates.io-index" 156 + checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" 157 dependencies = [ 158 "cc", 159 "cmake", 160 "dunce", ··· 163 164 [[package]] 165 name = "axum" 166 + version = "0.8.8" 167 source = "registry+https://github.com/rust-lang/crates.io-index" 168 + checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" 169 dependencies = [ 170 "axum-core", 171 "axum-macros", ··· 183 "mime", 184 "percent-encoding", 185 "pin-project-lite", 186 + "serde_core", 187 "serde_json", 188 "serde_path_to_error", 189 "serde_urlencoded", ··· 197 198 [[package]] 199 name = "axum-core" 200 + version = "0.5.6" 201 source = "registry+https://github.com/rust-lang/crates.io-index" 202 + checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" 203 dependencies = [ 204 "bytes", 205 "futures-core", ··· 208 "http-body-util", 209 "mime", 210 "pin-project-lite", 211 "sync_wrapper", 212 "tower-layer", 213 "tower-service", ··· 222 dependencies = [ 223 "proc-macro2", 224 "quote", 225 + "syn 2.0.112", 226 ] 227 228 [[package]] ··· 234 "axum", 235 "handlebars", 236 "serde", 237 + "thiserror 2.0.17", 238 ] 239 240 [[package]] ··· 267 268 [[package]] 269 name = "base64ct" 270 + version = "1.8.1" 271 source = "registry+https://github.com/rust-lang/crates.io-index" 272 + checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" 273 274 [[package]] 275 name = "bitflags" 276 + version = "2.10.0" 277 source = "registry+https://github.com/rust-lang/crates.io-index" 278 + checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 279 dependencies = [ 280 + "serde_core", 281 ] 282 283 [[package]] ··· 311 "proc-macro2", 312 "quote", 313 "rustversion", 314 + "syn 2.0.112", 315 ] 316 317 [[package]] ··· 325 326 [[package]] 327 name = "bstr" 328 + version = "1.12.1" 329 source = "registry+https://github.com/rust-lang/crates.io-index" 330 + checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" 331 dependencies = [ 332 "memchr", 333 "serde", ··· 359 360 [[package]] 361 name = "bumpalo" 362 + version = "3.19.1" 363 source = "registry+https://github.com/rust-lang/crates.io-index" 364 + checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 365 366 [[package]] 367 name = "byteorder" ··· 371 372 [[package]] 373 name = "bytes" 374 + version = "1.11.0" 375 source = "registry+https://github.com/rust-lang/crates.io-index" 376 + checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" 377 dependencies = [ 378 "serde", 379 ] ··· 389 390 [[package]] 391 name = "cc" 392 + version = "1.2.51" 393 source = "registry+https://github.com/rust-lang/crates.io-index" 394 + checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" 395 dependencies = [ 396 + "find-msvc-tools", 397 "jobserver", 398 "libc", 399 "shlex", ··· 409 ] 410 411 [[package]] 412 name = "cfg-if" 413 + version = "1.0.4" 414 source = "registry+https://github.com/rust-lang/crates.io-index" 415 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 416 417 [[package]] 418 name = "cfg_aliases" ··· 431 "num-traits", 432 "serde", 433 "wasm-bindgen", 434 + "windows-link", 435 ] 436 437 [[package]] ··· 496 ] 497 498 [[package]] 499 + name = "cmake" 500 + version = "0.1.57" 501 + source = "registry+https://github.com/rust-lang/crates.io-index" 502 + checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" 503 + dependencies = [ 504 + "cc", 505 + ] 506 + 507 + [[package]] 508 + name = "cobs" 509 + version = "0.3.0" 510 source = "registry+https://github.com/rust-lang/crates.io-index" 511 + checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" 512 dependencies = [ 513 + "thiserror 2.0.17", 514 ] 515 516 [[package]] 517 + name = "compression-codecs" 518 + version = "0.4.35" 519 source = "registry+https://github.com/rust-lang/crates.io-index" 520 + checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" 521 dependencies = [ 522 + "compression-core", 523 + "flate2", 524 + "memchr", 525 + "zstd", 526 + "zstd-safe", 527 ] 528 + 529 + [[package]] 530 + name = "compression-core" 531 + version = "0.4.31" 532 + source = "registry+https://github.com/rust-lang/crates.io-index" 533 + checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" 534 535 [[package]] 536 name = "concurrent-queue" ··· 554 checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 555 556 [[package]] 557 + name = "cordyceps" 558 + version = "0.3.4" 559 + source = "registry+https://github.com/rust-lang/crates.io-index" 560 + checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" 561 + dependencies = [ 562 + "loom", 563 + "tracing", 564 + ] 565 + 566 + [[package]] 567 name = "core-foundation" 568 version = "0.9.4" 569 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 599 600 [[package]] 601 name = "crc" 602 + version = "3.4.0" 603 source = "registry+https://github.com/rust-lang/crates.io-index" 604 + checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" 605 dependencies = [ 606 "crc-catalog", 607 ] ··· 620 dependencies = [ 621 "cfg-if", 622 ] 623 + 624 + [[package]] 625 + name = "critical-section" 626 + version = "1.2.0" 627 + source = "registry+https://github.com/rust-lang/crates.io-index" 628 + checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 629 630 [[package]] 631 name = "crossbeam-queue" ··· 662 663 [[package]] 664 name = "crypto-common" 665 + version = "0.1.7" 666 source = "registry+https://github.com/rust-lang/crates.io-index" 667 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 668 dependencies = [ 669 "generic-array", 670 "typenum", ··· 701 "proc-macro2", 702 "quote", 703 "strsim", 704 + "syn 2.0.112", 705 ] 706 707 [[package]] ··· 715 "proc-macro2", 716 "quote", 717 "strsim", 718 + "syn 2.0.112", 719 ] 720 721 [[package]] ··· 726 dependencies = [ 727 "darling_core 0.20.11", 728 "quote", 729 + "syn 2.0.112", 730 ] 731 732 [[package]] ··· 737 dependencies = [ 738 "darling_core 0.21.3", 739 "quote", 740 + "syn 2.0.112", 741 ] 742 743 [[package]] ··· 777 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 778 dependencies = [ 779 "data-encoding", 780 + "syn 2.0.112", 781 ] 782 783 [[package]] ··· 819 "darling 0.20.11", 820 "proc-macro2", 821 "quote", 822 + "syn 2.0.112", 823 ] 824 825 [[package]] ··· 829 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" 830 dependencies = [ 831 "derive_builder_core", 832 + "syn 2.0.112", 833 ] 834 + 835 + [[package]] 836 + name = "derive_more" 837 + version = "1.0.0" 838 + source = "registry+https://github.com/rust-lang/crates.io-index" 839 + checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 840 + dependencies = [ 841 + "derive_more-impl", 842 + ] 843 + 844 + [[package]] 845 + name = "derive_more-impl" 846 + version = "1.0.0" 847 + source = "registry+https://github.com/rust-lang/crates.io-index" 848 + checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 849 + dependencies = [ 850 + "proc-macro2", 851 + "quote", 852 + "syn 2.0.112", 853 + "unicode-xid", 854 + ] 855 + 856 + [[package]] 857 + name = "diatomic-waker" 858 + version = "0.2.3" 859 + source = "registry+https://github.com/rust-lang/crates.io-index" 860 + checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" 861 862 [[package]] 863 name = "digest" ··· 879 dependencies = [ 880 "proc-macro2", 881 "quote", 882 + "syn 2.0.112", 883 ] 884 885 [[package]] ··· 960 checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" 961 962 [[package]] 963 + name = "embedded-io" 964 + version = "0.4.0" 965 + source = "registry+https://github.com/rust-lang/crates.io-index" 966 + checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" 967 + 968 + [[package]] 969 + name = "embedded-io" 970 + version = "0.6.1" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 973 + 974 + [[package]] 975 name = "encoding_rs" 976 version = "0.8.35" 977 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 988 989 [[package]] 990 name = "errno" 991 + version = "0.3.14" 992 source = "registry+https://github.com/rust-lang/crates.io-index" 993 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 994 dependencies = [ 995 "libc", 996 "windows-sys 0.59.0", ··· 1035 ] 1036 1037 [[package]] 1038 + name = "find-msvc-tools" 1039 + version = "0.1.6" 1040 + source = "registry+https://github.com/rust-lang/crates.io-index" 1041 + checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" 1042 + 1043 + [[package]] 1044 name = "flate2" 1045 version = "1.1.5" 1046 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1058 dependencies = [ 1059 "futures-core", 1060 "futures-sink", 1061 + "spin 0.9.8", 1062 ] 1063 1064 [[package]] ··· 1074 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1075 1076 [[package]] 1077 + name = "foldhash" 1078 + version = "0.2.0" 1079 + source = "registry+https://github.com/rust-lang/crates.io-index" 1080 + checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" 1081 + 1082 + [[package]] 1083 name = "foreign-types" 1084 version = "0.3.2" 1085 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1096 1097 [[package]] 1098 name = "form_urlencoded" 1099 + version = "1.2.2" 1100 source = "registry+https://github.com/rust-lang/crates.io-index" 1101 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 1102 dependencies = [ 1103 "percent-encoding", 1104 ] ··· 1120 checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 1121 1122 [[package]] 1123 + name = "futures-buffered" 1124 + version = "0.2.12" 1125 + source = "registry+https://github.com/rust-lang/crates.io-index" 1126 + checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" 1127 + dependencies = [ 1128 + "cordyceps", 1129 + "diatomic-waker", 1130 + "futures-core", 1131 + "pin-project-lite", 1132 + "spin 0.10.0", 1133 + ] 1134 + 1135 + [[package]] 1136 name = "futures-channel" 1137 version = "0.3.31" 1138 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1177 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1178 1179 [[package]] 1180 + name = "futures-lite" 1181 + version = "2.6.1" 1182 + source = "registry+https://github.com/rust-lang/crates.io-index" 1183 + checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" 1184 + dependencies = [ 1185 + "fastrand", 1186 + "futures-core", 1187 + "futures-io", 1188 + "parking", 1189 + "pin-project-lite", 1190 + ] 1191 + 1192 + [[package]] 1193 name = "futures-macro" 1194 version = "0.3.31" 1195 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1197 dependencies = [ 1198 "proc-macro2", 1199 "quote", 1200 + "syn 2.0.112", 1201 ] 1202 1203 [[package]] ··· 1236 ] 1237 1238 [[package]] 1239 + name = "generator" 1240 + version = "0.8.8" 1241 + source = "registry+https://github.com/rust-lang/crates.io-index" 1242 + checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" 1243 + dependencies = [ 1244 + "cc", 1245 + "cfg-if", 1246 + "libc", 1247 + "log", 1248 + "rustversion", 1249 + "windows-link", 1250 + "windows-result", 1251 + ] 1252 + 1253 + [[package]] 1254 name = "generic-array" 1255 version = "0.14.7" 1256 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1289 ] 1290 1291 [[package]] 1292 name = "globset" 1293 + version = "0.4.18" 1294 source = "registry+https://github.com/rust-lang/crates.io-index" 1295 + checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" 1296 dependencies = [ 1297 "aho-corasick", 1298 "bstr", 1299 "log", 1300 + "regex-automata", 1301 + "regex-syntax", 1302 ] 1303 1304 [[package]] 1305 name = "governor" 1306 + version = "0.10.4" 1307 source = "registry+https://github.com/rust-lang/crates.io-index" 1308 + checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" 1309 dependencies = [ 1310 "cfg-if", 1311 "dashmap", ··· 1313 "futures-timer", 1314 "futures-util", 1315 "getrandom 0.3.4", 1316 + "hashbrown 0.16.1", 1317 "nonzero_ext", 1318 "parking_lot", 1319 "portable-atomic", ··· 1347 "futures-core", 1348 "futures-sink", 1349 "http", 1350 + "indexmap 2.12.1", 1351 "slab", 1352 "tokio", 1353 "tokio-util", ··· 1356 1357 [[package]] 1358 name = "half" 1359 + version = "2.7.1" 1360 source = "registry+https://github.com/rust-lang/crates.io-index" 1361 + checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" 1362 dependencies = [ 1363 "cfg-if", 1364 "crunchy", 1365 + "zerocopy", 1366 ] 1367 1368 [[package]] 1369 name = "handlebars" 1370 + version = "6.4.0" 1371 source = "registry+https://github.com/rust-lang/crates.io-index" 1372 + checksum = "9b3f9296c208515b87bd915a2f5d1163d4b3f863ba83337d7713cf478055948e" 1373 dependencies = [ 1374 "derive_builder", 1375 "log", ··· 1379 "rust-embed", 1380 "serde", 1381 "serde_json", 1382 + "thiserror 2.0.17", 1383 + ] 1384 + 1385 + [[package]] 1386 + name = "hash32" 1387 + version = "0.2.1" 1388 + source = "registry+https://github.com/rust-lang/crates.io-index" 1389 + checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 1390 + dependencies = [ 1391 + "byteorder", 1392 ] 1393 1394 [[package]] ··· 1415 dependencies = [ 1416 "allocator-api2", 1417 "equivalent", 1418 + "foldhash 0.1.5", 1419 + ] 1420 + 1421 + [[package]] 1422 + name = "hashbrown" 1423 + version = "0.16.1" 1424 + source = "registry+https://github.com/rust-lang/crates.io-index" 1425 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 1426 + dependencies = [ 1427 + "allocator-api2", 1428 + "equivalent", 1429 + "foldhash 0.2.0", 1430 ] 1431 1432 [[package]] ··· 1439 ] 1440 1441 [[package]] 1442 + name = "heapless" 1443 + version = "0.7.17" 1444 + source = "registry+https://github.com/rust-lang/crates.io-index" 1445 + checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" 1446 + dependencies = [ 1447 + "atomic-polyfill", 1448 + "hash32", 1449 + "rustc_version", 1450 + "serde", 1451 + "spin 0.9.8", 1452 + "stable_deref_trait", 1453 + ] 1454 + 1455 + [[package]] 1456 name = "heck" 1457 version = "0.4.1" 1458 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1496 1497 [[package]] 1498 name = "home" 1499 + version = "0.5.12" 1500 source = "registry+https://github.com/rust-lang/crates.io-index" 1501 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 1502 dependencies = [ 1503 + "windows-sys 0.61.2", 1504 ] 1505 1506 [[package]] ··· 1514 1515 [[package]] 1516 name = "http" 1517 + version = "1.4.0" 1518 source = "registry+https://github.com/rust-lang/crates.io-index" 1519 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 1520 dependencies = [ 1521 "bytes", 1522 "itoa", 1523 ] 1524 ··· 1559 1560 [[package]] 1561 name = "hyper" 1562 + version = "1.8.1" 1563 source = "registry+https://github.com/rust-lang/crates.io-index" 1564 + checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" 1565 dependencies = [ 1566 + "atomic-waker", 1567 "bytes", 1568 "futures-channel", 1569 + "futures-core", 1570 "h2", 1571 "http", 1572 "http-body", ··· 1574 "httpdate", 1575 "itoa", 1576 "pin-project-lite", 1577 + "pin-utils", 1578 "smallvec", 1579 "tokio", 1580 "want", ··· 1594 "tokio", 1595 "tokio-rustls", 1596 "tower-service", 1597 + "webpki-roots 1.0.5", 1598 ] 1599 1600 [[package]] ··· 1612 1613 [[package]] 1614 name = "hyper-util" 1615 + version = "0.1.19" 1616 source = "registry+https://github.com/rust-lang/crates.io-index" 1617 + checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" 1618 dependencies = [ 1619 "base64", 1620 "bytes", ··· 1638 1639 [[package]] 1640 name = "iana-time-zone" 1641 + version = "0.1.64" 1642 source = "registry+https://github.com/rust-lang/crates.io-index" 1643 + checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" 1644 dependencies = [ 1645 "android_system_properties", 1646 "core-foundation-sys", ··· 1662 1663 [[package]] 1664 name = "icu_collections" 1665 + version = "2.1.1" 1666 source = "registry+https://github.com/rust-lang/crates.io-index" 1667 + checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" 1668 dependencies = [ 1669 "displaydoc", 1670 "potential_utf", ··· 1675 1676 [[package]] 1677 name = "icu_locale_core" 1678 + version = "2.1.1" 1679 source = "registry+https://github.com/rust-lang/crates.io-index" 1680 + checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" 1681 dependencies = [ 1682 "displaydoc", 1683 "litemap", ··· 1688 1689 [[package]] 1690 name = "icu_normalizer" 1691 + version = "2.1.1" 1692 source = "registry+https://github.com/rust-lang/crates.io-index" 1693 + checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" 1694 dependencies = [ 1695 "icu_collections", 1696 "icu_normalizer_data", 1697 "icu_properties", ··· 1702 1703 [[package]] 1704 name = "icu_normalizer_data" 1705 + version = "2.1.1" 1706 source = "registry+https://github.com/rust-lang/crates.io-index" 1707 + checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" 1708 1709 [[package]] 1710 name = "icu_properties" 1711 + version = "2.1.2" 1712 source = "registry+https://github.com/rust-lang/crates.io-index" 1713 + checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" 1714 dependencies = [ 1715 "icu_collections", 1716 "icu_locale_core", 1717 "icu_properties_data", 1718 "icu_provider", 1719 "zerotrie", 1720 "zerovec", 1721 ] 1722 1723 [[package]] 1724 name = "icu_properties_data" 1725 + version = "2.1.2" 1726 source = "registry+https://github.com/rust-lang/crates.io-index" 1727 + checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" 1728 1729 [[package]] 1730 name = "icu_provider" 1731 + version = "2.1.1" 1732 source = "registry+https://github.com/rust-lang/crates.io-index" 1733 + checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" 1734 dependencies = [ 1735 "displaydoc", 1736 "icu_locale_core", 1737 "writeable", 1738 "yoke", 1739 "zerofrom", ··· 1749 1750 [[package]] 1751 name = "idna" 1752 + version = "1.1.0" 1753 source = "registry+https://github.com/rust-lang/crates.io-index" 1754 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 1755 dependencies = [ 1756 "idna_adapter", 1757 "smallvec", ··· 1781 1782 [[package]] 1783 name = "indexmap" 1784 + version = "2.12.1" 1785 source = "registry+https://github.com/rust-lang/crates.io-index" 1786 + checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 1787 dependencies = [ 1788 "equivalent", 1789 + "hashbrown 0.16.1", 1790 "serde", 1791 + "serde_core", 1792 ] 1793 1794 [[package]] ··· 1819 ] 1820 1821 [[package]] 1822 name = "ipld-core" 1823 version = "0.4.2" 1824 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1837 1838 [[package]] 1839 name = "iri-string" 1840 + version = "0.7.10" 1841 source = "registry+https://github.com/rust-lang/crates.io-index" 1842 + checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" 1843 dependencies = [ 1844 "memchr", 1845 "serde", 1846 ] 1847 1848 [[package]] 1849 name = "itoa" 1850 + version = "1.0.17" 1851 source = "registry+https://github.com/rust-lang/crates.io-index" 1852 + checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 1853 1854 [[package]] 1855 name = "jacquard-api" 1856 + version = "0.9.5" 1857 source = "registry+https://github.com/rust-lang/crates.io-index" 1858 + checksum = "4979fb1848c1dd7ac8fd12745bc71f56f6da61374407d5f9b06005467a954e5a" 1859 dependencies = [ 1860 "bon", 1861 "bytes", ··· 1865 "miette", 1866 "rustversion", 1867 "serde", 1868 + "serde_bytes", 1869 "serde_ipld_dagcbor", 1870 + "thiserror 2.0.17", 1871 "unicode-segmentation", 1872 ] 1873 1874 [[package]] 1875 name = "jacquard-common" 1876 + version = "0.9.5" 1877 source = "registry+https://github.com/rust-lang/crates.io-index" 1878 + checksum = "1751921e0bdae5e0077afade6161545e9ef7698306c868f800916e99ecbcaae9" 1879 dependencies = [ 1880 "base64", 1881 "bon", ··· 1893 "multihash", 1894 "ouroboros", 1895 "p256", 1896 + "postcard", 1897 "rand 0.9.2", 1898 "regex", 1899 "regex-lite", 1900 "reqwest", 1901 "serde", 1902 + "serde_bytes", 1903 "serde_html_form", 1904 "serde_ipld_dagcbor", 1905 "serde_json", 1906 "signature", 1907 "smol_str", 1908 + "thiserror 2.0.17", 1909 "tokio", 1910 "tokio-util", 1911 "trait-variant", ··· 1914 1915 [[package]] 1916 name = "jacquard-derive" 1917 + version = "0.9.5" 1918 source = "registry+https://github.com/rust-lang/crates.io-index" 1919 + checksum = "9c8d73dfee07943fdab93569ed1c28b06c6921ed891c08b415c4a323ff67e593" 1920 dependencies = [ 1921 "heck 0.5.0", 1922 "jacquard-lexicon", 1923 "proc-macro2", 1924 "quote", 1925 + "syn 2.0.112", 1926 ] 1927 1928 [[package]] 1929 name = "jacquard-identity" 1930 + version = "0.9.5" 1931 source = "registry+https://github.com/rust-lang/crates.io-index" 1932 + checksum = "e7aaefa819fa4213cf59f180dba932f018a7cd0599582fd38474ee2a38c16cf2" 1933 dependencies = [ 1934 "bon", 1935 "bytes", ··· 1938 "jacquard-common", 1939 "jacquard-lexicon", 1940 "miette", 1941 + "n0-future", 1942 "percent-encoding", 1943 "reqwest", 1944 "serde", 1945 "serde_html_form", 1946 "serde_json", 1947 + "thiserror 2.0.17", 1948 "tokio", 1949 "trait-variant", 1950 "url", ··· 1953 1954 [[package]] 1955 name = "jacquard-lexicon" 1956 + version = "0.9.5" 1957 source = "registry+https://github.com/rust-lang/crates.io-index" 1958 + checksum = "8411aff546569b0a1e0ef669bed2380cec1c00d48f02f3fcd57a71545321b3d8" 1959 dependencies = [ 1960 "cid", 1961 "dashmap", ··· 1973 "serde_repr", 1974 "serde_with", 1975 "sha2", 1976 + "syn 2.0.112", 1977 + "thiserror 2.0.17", 1978 "unicode-segmentation", 1979 ] 1980 1981 [[package]] 1982 name = "jobserver" 1983 + version = "0.1.34" 1984 source = "registry+https://github.com/rust-lang/crates.io-index" 1985 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 1986 dependencies = [ 1987 "getrandom 0.3.4", 1988 "libc", ··· 2001 "regex", 2002 "serde", 2003 "serde_json", 2004 + "thiserror 2.0.17", 2005 "time", 2006 ] 2007 2008 [[package]] 2009 name = "js-sys" 2010 + version = "0.3.83" 2011 source = "registry+https://github.com/rust-lang/crates.io-index" 2012 + checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 2013 dependencies = [ 2014 "once_cell", 2015 "wasm-bindgen", ··· 2066 source = "registry+https://github.com/rust-lang/crates.io-index" 2067 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2068 dependencies = [ 2069 + "spin 0.9.8", 2070 ] 2071 2072 [[package]] 2073 name = "lettre" 2074 + version = "0.11.19" 2075 source = "registry+https://github.com/rust-lang/crates.io-index" 2076 + checksum = "9e13e10e8818f8b2a60f52cb127041d388b89f3a96a62be9ceaffa22262fef7f" 2077 dependencies = [ 2078 "async-trait", 2079 "base64", ··· 2094 "tokio", 2095 "tokio-rustls", 2096 "url", 2097 + "webpki-roots 1.0.5", 2098 ] 2099 2100 [[package]] 2101 name = "libc" 2102 + version = "0.2.178" 2103 source = "registry+https://github.com/rust-lang/crates.io-index" 2104 + checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 2105 2106 [[package]] 2107 name = "libm" ··· 2111 2112 [[package]] 2113 name = "libredox" 2114 + version = "0.1.12" 2115 source = "registry+https://github.com/rust-lang/crates.io-index" 2116 + checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" 2117 dependencies = [ 2118 "bitflags", 2119 "libc", 2120 + "redox_syscall 0.7.0", 2121 ] 2122 2123 [[package]] ··· 2130 "pkg-config", 2131 "vcpkg", 2132 ] 2133 2134 [[package]] 2135 name = "litemap" 2136 + version = "0.8.1" 2137 source = "registry+https://github.com/rust-lang/crates.io-index" 2138 + checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" 2139 2140 [[package]] 2141 name = "lock_api" 2142 + version = "0.4.14" 2143 source = "registry+https://github.com/rust-lang/crates.io-index" 2144 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 2145 dependencies = [ 2146 "scopeguard", 2147 ] 2148 2149 [[package]] 2150 name = "log" 2151 + version = "0.4.29" 2152 source = "registry+https://github.com/rust-lang/crates.io-index" 2153 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 2154 + 2155 + [[package]] 2156 + name = "loom" 2157 + version = "0.7.2" 2158 + source = "registry+https://github.com/rust-lang/crates.io-index" 2159 + checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" 2160 + dependencies = [ 2161 + "cfg-if", 2162 + "generator", 2163 + "scoped-tls", 2164 + "tracing", 2165 + "tracing-subscriber", 2166 + ] 2167 2168 [[package]] 2169 name = "lru-slab" ··· 2184 2185 [[package]] 2186 name = "matchers" 2187 + version = "0.2.0" 2188 source = "registry+https://github.com/rust-lang/crates.io-index" 2189 + checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 2190 dependencies = [ 2191 + "regex-automata", 2192 ] 2193 2194 [[package]] ··· 2209 2210 [[package]] 2211 name = "memchr" 2212 + version = "2.7.6" 2213 source = "registry+https://github.com/rust-lang/crates.io-index" 2214 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 2215 2216 [[package]] 2217 name = "miette" ··· 2232 dependencies = [ 2233 "proc-macro2", 2234 "quote", 2235 + "syn 2.0.112", 2236 ] 2237 2238 [[package]] ··· 2259 2260 [[package]] 2261 name = "mio" 2262 + version = "1.1.1" 2263 source = "registry+https://github.com/rust-lang/crates.io-index" 2264 + checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" 2265 dependencies = [ 2266 "libc", 2267 "wasi", 2268 + "windows-sys 0.61.2", 2269 ] 2270 2271 [[package]] ··· 2292 ] 2293 2294 [[package]] 2295 + name = "n0-future" 2296 + version = "0.1.3" 2297 + source = "registry+https://github.com/rust-lang/crates.io-index" 2298 + checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" 2299 + dependencies = [ 2300 + "cfg_aliases", 2301 + "derive_more", 2302 + "futures-buffered", 2303 + "futures-lite", 2304 + "futures-util", 2305 + "js-sys", 2306 + "pin-project", 2307 + "send_wrapper", 2308 + "tokio", 2309 + "tokio-util", 2310 + "wasm-bindgen", 2311 + "wasm-bindgen-futures", 2312 + "web-time", 2313 + ] 2314 + 2315 + [[package]] 2316 name = "nom" 2317 version = "7.1.3" 2318 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2345 2346 [[package]] 2347 name = "nu-ansi-term" 2348 + version = "0.50.3" 2349 source = "registry+https://github.com/rust-lang/crates.io-index" 2350 + checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 2351 dependencies = [ 2352 + "windows-sys 0.59.0", 2353 ] 2354 2355 [[package]] 2356 name = "num-bigint-dig" 2357 + version = "0.8.6" 2358 source = "registry+https://github.com/rust-lang/crates.io-index" 2359 + checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" 2360 dependencies = [ 2361 "lazy_static", 2362 "libm", 2363 "num-integer", ··· 2421 2422 [[package]] 2423 name = "object" 2424 + version = "0.32.2" 2425 source = "registry+https://github.com/rust-lang/crates.io-index" 2426 + checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 2427 dependencies = [ 2428 "memchr", 2429 ] ··· 2457 dependencies = [ 2458 "proc-macro2", 2459 "quote", 2460 + "syn 2.0.112", 2461 ] 2462 2463 [[package]] ··· 2493 "proc-macro2", 2494 "proc-macro2-diagnostics", 2495 "quote", 2496 + "syn 2.0.112", 2497 ] 2498 2499 [[package]] 2500 name = "p256" 2501 version = "0.13.2" 2502 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2516 2517 [[package]] 2518 name = "parking_lot" 2519 + version = "0.12.5" 2520 source = "registry+https://github.com/rust-lang/crates.io-index" 2521 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 2522 dependencies = [ 2523 "lock_api", 2524 "parking_lot_core", ··· 2526 2527 [[package]] 2528 name = "parking_lot_core" 2529 + version = "0.9.12" 2530 source = "registry+https://github.com/rust-lang/crates.io-index" 2531 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 2532 dependencies = [ 2533 "cfg-if", 2534 "libc", 2535 + "redox_syscall 0.5.18", 2536 "smallvec", 2537 + "windows-link", 2538 ] 2539 2540 [[package]] ··· 2567 "axum", 2568 "axum-template", 2569 "chrono", 2570 + "dashmap", 2571 "dotenvy", 2572 "handlebars", 2573 "hex", ··· 2589 "sha2", 2590 "sqlx", 2591 "tokio", 2592 + "tower", 2593 "tower-http", 2594 "tower_governor", 2595 "tracing", ··· 2608 2609 [[package]] 2610 name = "percent-encoding" 2611 + version = "2.3.2" 2612 source = "registry+https://github.com/rust-lang/crates.io-index" 2613 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2614 2615 [[package]] 2616 name = "pest" 2617 + version = "2.8.4" 2618 source = "registry+https://github.com/rust-lang/crates.io-index" 2619 + checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" 2620 dependencies = [ 2621 "memchr", 2622 "ucd-trie", 2623 ] 2624 2625 [[package]] 2626 name = "pest_derive" 2627 + version = "2.8.4" 2628 source = "registry+https://github.com/rust-lang/crates.io-index" 2629 + checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" 2630 dependencies = [ 2631 "pest", 2632 "pest_generator", ··· 2634 2635 [[package]] 2636 name = "pest_generator" 2637 + version = "2.8.4" 2638 source = "registry+https://github.com/rust-lang/crates.io-index" 2639 + checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" 2640 dependencies = [ 2641 "pest", 2642 "pest_meta", 2643 "proc-macro2", 2644 "quote", 2645 + "syn 2.0.112", 2646 ] 2647 2648 [[package]] 2649 name = "pest_meta" 2650 + version = "2.8.4" 2651 source = "registry+https://github.com/rust-lang/crates.io-index" 2652 + checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" 2653 dependencies = [ 2654 "pest", 2655 "sha2", ··· 2672 dependencies = [ 2673 "proc-macro2", 2674 "quote", 2675 + "syn 2.0.112", 2676 ] 2677 2678 [[package]] ··· 2716 2717 [[package]] 2718 name = "portable-atomic" 2719 + version = "1.13.0" 2720 source = "registry+https://github.com/rust-lang/crates.io-index" 2721 + checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" 2722 + 2723 + [[package]] 2724 + name = "postcard" 2725 + version = "1.1.3" 2726 + source = "registry+https://github.com/rust-lang/crates.io-index" 2727 + checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" 2728 + dependencies = [ 2729 + "cobs", 2730 + "embedded-io 0.4.0", 2731 + "embedded-io 0.6.1", 2732 + "heapless", 2733 + "serde", 2734 + ] 2735 2736 [[package]] 2737 name = "potential_utf" 2738 + version = "0.1.4" 2739 source = "registry+https://github.com/rust-lang/crates.io-index" 2740 + checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" 2741 dependencies = [ 2742 "zerovec", 2743 ] ··· 2759 2760 [[package]] 2761 name = "prettyplease" 2762 + version = "0.2.37" 2763 source = "registry+https://github.com/rust-lang/crates.io-index" 2764 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2765 dependencies = [ 2766 "proc-macro2", 2767 + "syn 2.0.112", 2768 ] 2769 2770 [[package]] ··· 2802 2803 [[package]] 2804 name = "proc-macro2" 2805 + version = "1.0.104" 2806 source = "registry+https://github.com/rust-lang/crates.io-index" 2807 + checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" 2808 dependencies = [ 2809 "unicode-ident", 2810 ] ··· 2817 dependencies = [ 2818 "proc-macro2", 2819 "quote", 2820 + "syn 2.0.112", 2821 "version_check", 2822 "yansi", 2823 ] 2824 2825 [[package]] 2826 name = "psm" 2827 + version = "0.1.28" 2828 source = "registry+https://github.com/rust-lang/crates.io-index" 2829 + checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" 2830 dependencies = [ 2831 + "ar_archive_writer", 2832 "cc", 2833 ] 2834 ··· 2858 "pin-project-lite", 2859 "quinn-proto", 2860 "quinn-udp", 2861 + "rustc-hash", 2862 "rustls", 2863 "socket2", 2864 + "thiserror 2.0.17", 2865 "tokio", 2866 "tracing", 2867 "web-time", ··· 2878 "lru-slab", 2879 "rand 0.9.2", 2880 "ring", 2881 + "rustc-hash", 2882 "rustls", 2883 "rustls-pki-types", 2884 "slab", 2885 + "thiserror 2.0.17", 2886 "tinyvec", 2887 "tracing", 2888 "web-time", ··· 2904 2905 [[package]] 2906 name = "quote" 2907 + version = "1.0.42" 2908 source = "registry+https://github.com/rust-lang/crates.io-index" 2909 + checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 2910 dependencies = [ 2911 "proc-macro2", 2912 ] ··· 2990 2991 [[package]] 2992 name = "raw-cpuid" 2993 + version = "11.6.0" 2994 + source = "registry+https://github.com/rust-lang/crates.io-index" 2995 + checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" 2996 + dependencies = [ 2997 + "bitflags", 2998 + ] 2999 + 3000 + [[package]] 3001 + name = "redox_syscall" 3002 + version = "0.5.18" 3003 source = "registry+https://github.com/rust-lang/crates.io-index" 3004 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 3005 dependencies = [ 3006 "bitflags", 3007 ] 3008 3009 [[package]] 3010 name = "redox_syscall" 3011 + version = "0.7.0" 3012 source = "registry+https://github.com/rust-lang/crates.io-index" 3013 + checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" 3014 dependencies = [ 3015 "bitflags", 3016 ] ··· 3032 dependencies = [ 3033 "proc-macro2", 3034 "quote", 3035 + "syn 2.0.112", 3036 ] 3037 3038 [[package]] ··· 3043 dependencies = [ 3044 "aho-corasick", 3045 "memchr", 3046 + "regex-automata", 3047 + "regex-syntax", 3048 ] 3049 3050 [[package]] ··· 3055 dependencies = [ 3056 "aho-corasick", 3057 "memchr", 3058 + "regex-syntax", 3059 ] 3060 3061 [[package]] ··· 3066 3067 [[package]] 3068 name = "regex-syntax" 3069 + version = "0.8.8" 3070 source = "registry+https://github.com/rust-lang/crates.io-index" 3071 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 3072 3073 [[package]] 3074 name = "reqwest" 3075 + version = "0.12.28" 3076 source = "registry+https://github.com/rust-lang/crates.io-index" 3077 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 3078 dependencies = [ 3079 "base64", 3080 "bytes", 3081 "encoding_rs", 3082 "futures-core", 3083 "h2", 3084 "http", 3085 "http-body", ··· 3101 "sync_wrapper", 3102 "tokio", 3103 "tokio-rustls", 3104 "tower", 3105 "tower-http", 3106 "tower-service", 3107 "url", 3108 "wasm-bindgen", 3109 "wasm-bindgen-futures", 3110 "web-sys", 3111 + "webpki-roots 1.0.5", 3112 ] 3113 3114 [[package]] ··· 3137 3138 [[package]] 3139 name = "rsa" 3140 + version = "0.9.9" 3141 source = "registry+https://github.com/rust-lang/crates.io-index" 3142 + checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" 3143 dependencies = [ 3144 "const-oid", 3145 "digest", ··· 3157 3158 [[package]] 3159 name = "rust-embed" 3160 + version = "8.9.0" 3161 source = "registry+https://github.com/rust-lang/crates.io-index" 3162 + checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" 3163 dependencies = [ 3164 "rust-embed-impl", 3165 "rust-embed-utils", ··· 3168 3169 [[package]] 3170 name = "rust-embed-impl" 3171 + version = "8.9.0" 3172 source = "registry+https://github.com/rust-lang/crates.io-index" 3173 + checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" 3174 dependencies = [ 3175 "proc-macro2", 3176 "quote", 3177 "rust-embed-utils", 3178 + "syn 2.0.112", 3179 "walkdir", 3180 ] 3181 3182 [[package]] 3183 name = "rust-embed-utils" 3184 + version = "8.9.0" 3185 source = "registry+https://github.com/rust-lang/crates.io-index" 3186 + checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" 3187 dependencies = [ 3188 "globset", 3189 "sha2", ··· 3191 ] 3192 3193 [[package]] 3194 name = "rustc-hash" 3195 version = "2.1.1" 3196 source = "registry+https://github.com/rust-lang/crates.io-index" 3197 checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3198 3199 [[package]] 3200 + name = "rustc_version" 3201 + version = "0.4.1" 3202 source = "registry+https://github.com/rust-lang/crates.io-index" 3203 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 3204 dependencies = [ 3205 + "semver", 3206 ] 3207 3208 [[package]] 3209 name = "rustls" 3210 + version = "0.23.35" 3211 source = "registry+https://github.com/rust-lang/crates.io-index" 3212 + checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" 3213 dependencies = [ 3214 "aws-lc-rs", 3215 "log", ··· 3223 3224 [[package]] 3225 name = "rustls-pki-types" 3226 + version = "1.13.2" 3227 source = "registry+https://github.com/rust-lang/crates.io-index" 3228 + checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" 3229 dependencies = [ 3230 "web-time", 3231 "zeroize", ··· 3233 3234 [[package]] 3235 name = "rustls-webpki" 3236 + version = "0.103.8" 3237 source = "registry+https://github.com/rust-lang/crates.io-index" 3238 + checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" 3239 dependencies = [ 3240 "aws-lc-rs", 3241 "ring", ··· 3251 3252 [[package]] 3253 name = "ryu" 3254 + version = "1.0.22" 3255 source = "registry+https://github.com/rust-lang/crates.io-index" 3256 + checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" 3257 3258 [[package]] 3259 name = "salsa20" ··· 3287 3288 [[package]] 3289 name = "schemars" 3290 + version = "1.2.0" 3291 source = "registry+https://github.com/rust-lang/crates.io-index" 3292 + checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" 3293 dependencies = [ 3294 "dyn-clone", 3295 "ref-cast", ··· 3298 ] 3299 3300 [[package]] 3301 + name = "scoped-tls" 3302 + version = "1.0.1" 3303 + source = "registry+https://github.com/rust-lang/crates.io-index" 3304 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 3305 + 3306 + [[package]] 3307 name = "scopeguard" 3308 version = "1.2.0" 3309 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3354 ] 3355 3356 [[package]] 3357 + name = "semver" 3358 + version = "1.0.27" 3359 + source = "registry+https://github.com/rust-lang/crates.io-index" 3360 + checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 3361 + 3362 + [[package]] 3363 + name = "send_wrapper" 3364 + version = "0.6.0" 3365 + source = "registry+https://github.com/rust-lang/crates.io-index" 3366 + checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" 3367 + 3368 + [[package]] 3369 name = "serde" 3370 version = "1.0.228" 3371 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3402 dependencies = [ 3403 "proc-macro2", 3404 "quote", 3405 + "syn 2.0.112", 3406 ] 3407 3408 [[package]] ··· 3412 checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3413 dependencies = [ 3414 "form_urlencoded", 3415 + "indexmap 2.12.1", 3416 "itoa", 3417 "ryu", 3418 "serde_core", ··· 3432 3433 [[package]] 3434 name = "serde_json" 3435 + version = "1.0.148" 3436 source = "registry+https://github.com/rust-lang/crates.io-index" 3437 + checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" 3438 dependencies = [ 3439 + "indexmap 2.12.1", 3440 "itoa", 3441 "memchr", 3442 "serde", 3443 "serde_core", 3444 + "zmij", 3445 ] 3446 3447 [[package]] 3448 name = "serde_path_to_error" 3449 + version = "0.1.20" 3450 source = "registry+https://github.com/rust-lang/crates.io-index" 3451 + checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 3452 dependencies = [ 3453 "itoa", 3454 "serde", 3455 + "serde_core", 3456 ] 3457 3458 [[package]] ··· 3463 dependencies = [ 3464 "proc-macro2", 3465 "quote", 3466 + "syn 2.0.112", 3467 ] 3468 3469 [[package]] ··· 3480 3481 [[package]] 3482 name = "serde_with" 3483 + version = "3.16.1" 3484 source = "registry+https://github.com/rust-lang/crates.io-index" 3485 + checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" 3486 dependencies = [ 3487 "base64", 3488 "chrono", 3489 "hex", 3490 "indexmap 1.9.3", 3491 + "indexmap 2.12.1", 3492 "schemars 0.9.0", 3493 + "schemars 1.2.0", 3494 "serde_core", 3495 "serde_json", 3496 "serde_with_macros", ··· 3499 3500 [[package]] 3501 name = "serde_with_macros" 3502 + version = "3.16.1" 3503 source = "registry+https://github.com/rust-lang/crates.io-index" 3504 + checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" 3505 dependencies = [ 3506 "darling 0.21.3", 3507 "proc-macro2", 3508 "quote", 3509 + "syn 2.0.112", 3510 ] 3511 3512 [[package]] ··· 3548 3549 [[package]] 3550 name = "signal-hook-registry" 3551 + version = "1.4.8" 3552 source = "registry+https://github.com/rust-lang/crates.io-index" 3553 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 3554 dependencies = [ 3555 + "errno", 3556 "libc", 3557 ] 3558 ··· 3568 3569 [[package]] 3570 name = "simd-adler32" 3571 + version = "0.3.8" 3572 source = "registry+https://github.com/rust-lang/crates.io-index" 3573 + checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" 3574 3575 [[package]] 3576 name = "slab" ··· 3599 3600 [[package]] 3601 name = "socket2" 3602 + version = "0.6.1" 3603 source = "registry+https://github.com/rust-lang/crates.io-index" 3604 + checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 3605 dependencies = [ 3606 "libc", 3607 + "windows-sys 0.60.2", 3608 ] 3609 3610 [[package]] ··· 3617 ] 3618 3619 [[package]] 3620 + name = "spin" 3621 + version = "0.10.0" 3622 + source = "registry+https://github.com/rust-lang/crates.io-index" 3623 + checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" 3624 + 3625 + [[package]] 3626 name = "spinning_top" 3627 version = "0.3.0" 3628 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3673 "futures-util", 3674 "hashbrown 0.15.5", 3675 "hashlink", 3676 + "indexmap 2.12.1", 3677 "log", 3678 "memchr", 3679 "once_cell", ··· 3683 "serde_json", 3684 "sha2", 3685 "smallvec", 3686 + "thiserror 2.0.17", 3687 "tokio", 3688 "tokio-stream", 3689 "tracing", ··· 3701 "quote", 3702 "sqlx-core", 3703 "sqlx-macros-core", 3704 + "syn 2.0.112", 3705 ] 3706 3707 [[package]] ··· 3724 "sqlx-mysql", 3725 "sqlx-postgres", 3726 "sqlx-sqlite", 3727 + "syn 2.0.112", 3728 "tokio", 3729 "url", 3730 ] ··· 3767 "smallvec", 3768 "sqlx-core", 3769 "stringprep", 3770 + "thiserror 2.0.17", 3771 "tracing", 3772 "whoami", 3773 ] ··· 3805 "smallvec", 3806 "sqlx-core", 3807 "stringprep", 3808 + "thiserror 2.0.17", 3809 "tracing", 3810 "whoami", 3811 ] ··· 3830 "serde", 3831 "serde_urlencoded", 3832 "sqlx-core", 3833 + "thiserror 2.0.17", 3834 "tracing", 3835 "url", 3836 ] 3837 3838 [[package]] 3839 name = "stable_deref_trait" 3840 + version = "1.2.1" 3841 source = "registry+https://github.com/rust-lang/crates.io-index" 3842 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 3843 3844 [[package]] 3845 name = "stacker" 3846 + version = "0.1.22" 3847 source = "registry+https://github.com/rust-lang/crates.io-index" 3848 + checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" 3849 dependencies = [ 3850 "cc", 3851 "cfg-if", ··· 3870 "quote", 3871 "serde", 3872 "sha2", 3873 + "syn 2.0.112", 3874 "thiserror 1.0.69", 3875 ] 3876 ··· 3916 3917 [[package]] 3918 name = "syn" 3919 + version = "2.0.112" 3920 source = "registry+https://github.com/rust-lang/crates.io-index" 3921 + checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" 3922 dependencies = [ 3923 "proc-macro2", 3924 "quote", ··· 3942 dependencies = [ 3943 "proc-macro2", 3944 "quote", 3945 + "syn 2.0.112", 3946 ] 3947 3948 [[package]] ··· 3977 3978 [[package]] 3979 name = "thiserror" 3980 + version = "2.0.17" 3981 source = "registry+https://github.com/rust-lang/crates.io-index" 3982 + checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 3983 dependencies = [ 3984 + "thiserror-impl 2.0.17", 3985 ] 3986 3987 [[package]] ··· 3992 dependencies = [ 3993 "proc-macro2", 3994 "quote", 3995 + "syn 2.0.112", 3996 ] 3997 3998 [[package]] 3999 name = "thiserror-impl" 4000 + version = "2.0.17" 4001 source = "registry+https://github.com/rust-lang/crates.io-index" 4002 + checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 4003 dependencies = [ 4004 "proc-macro2", 4005 "quote", 4006 + "syn 2.0.112", 4007 ] 4008 4009 [[package]] ··· 4048 4049 [[package]] 4050 name = "tinystr" 4051 + version = "0.8.2" 4052 source = "registry+https://github.com/rust-lang/crates.io-index" 4053 + checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" 4054 dependencies = [ 4055 "displaydoc", 4056 "zerovec", ··· 4058 4059 [[package]] 4060 name = "tinyvec" 4061 + version = "1.10.0" 4062 source = "registry+https://github.com/rust-lang/crates.io-index" 4063 + checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 4064 dependencies = [ 4065 "tinyvec_macros", 4066 ] ··· 4073 4074 [[package]] 4075 name = "tokio" 4076 + version = "1.48.0" 4077 source = "registry+https://github.com/rust-lang/crates.io-index" 4078 + checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 4079 dependencies = [ 4080 "bytes", 4081 "libc", 4082 "mio", 4083 "pin-project-lite", 4084 "signal-hook-registry", 4085 "socket2", 4086 "tokio-macros", 4087 + "windows-sys 0.61.2", 4088 ] 4089 4090 [[package]] 4091 name = "tokio-macros" 4092 + version = "2.6.0" 4093 source = "registry+https://github.com/rust-lang/crates.io-index" 4094 + checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 4095 dependencies = [ 4096 "proc-macro2", 4097 "quote", 4098 + "syn 2.0.112", 4099 ] 4100 4101 [[package]] 4102 name = "tokio-rustls" 4103 + version = "0.26.4" 4104 source = "registry+https://github.com/rust-lang/crates.io-index" 4105 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 4106 dependencies = [ 4107 "rustls", 4108 "tokio", ··· 4128 "bytes", 4129 "futures-core", 4130 "futures-sink", 4131 + "futures-util", 4132 "pin-project-lite", 4133 "tokio", 4134 ] 4135 4136 [[package]] 4137 name = "tonic" 4138 + version = "0.14.2" 4139 source = "registry+https://github.com/rust-lang/crates.io-index" 4140 + checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" 4141 dependencies = [ 4142 "async-trait", 4143 "axum", ··· 4170 dependencies = [ 4171 "futures-core", 4172 "futures-util", 4173 + "indexmap 2.12.1", 4174 "pin-project-lite", 4175 "slab", 4176 "sync_wrapper", ··· 4183 4184 [[package]] 4185 name = "tower-http" 4186 + version = "0.6.8" 4187 source = "registry+https://github.com/rust-lang/crates.io-index" 4188 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 4189 dependencies = [ 4190 "async-compression", 4191 "bitflags", ··· 4194 "futures-util", 4195 "http", 4196 "http-body", 4197 + "http-body-util", 4198 "iri-string", 4199 "pin-project-lite", 4200 "tokio", ··· 4227 "governor", 4228 "http", 4229 "pin-project", 4230 + "thiserror 2.0.17", 4231 "tonic", 4232 "tower", 4233 "tracing", ··· 4235 4236 [[package]] 4237 name = "tracing" 4238 + version = "0.1.44" 4239 source = "registry+https://github.com/rust-lang/crates.io-index" 4240 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 4241 dependencies = [ 4242 "log", 4243 "pin-project-lite", ··· 4247 4248 [[package]] 4249 name = "tracing-attributes" 4250 + version = "0.1.31" 4251 source = "registry+https://github.com/rust-lang/crates.io-index" 4252 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 4253 dependencies = [ 4254 "proc-macro2", 4255 "quote", 4256 + "syn 2.0.112", 4257 ] 4258 4259 [[package]] 4260 name = "tracing-core" 4261 + version = "0.1.36" 4262 source = "registry+https://github.com/rust-lang/crates.io-index" 4263 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 4264 dependencies = [ 4265 "once_cell", 4266 "valuable", ··· 4279 4280 [[package]] 4281 name = "tracing-subscriber" 4282 + version = "0.3.22" 4283 source = "registry+https://github.com/rust-lang/crates.io-index" 4284 + checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 4285 dependencies = [ 4286 "matchers", 4287 "nu-ansi-term", 4288 "once_cell", 4289 + "regex-automata", 4290 "sharded-slab", 4291 "smallvec", 4292 "thread_local", ··· 4303 dependencies = [ 4304 "proc-macro2", 4305 "quote", 4306 + "syn 2.0.112", 4307 ] 4308 4309 [[package]] ··· 4314 4315 [[package]] 4316 name = "typenum" 4317 + version = "1.19.0" 4318 source = "registry+https://github.com/rust-lang/crates.io-index" 4319 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 4320 4321 [[package]] 4322 name = "ucd-trie" ··· 4332 4333 [[package]] 4334 name = "unicode-ident" 4335 + version = "1.0.22" 4336 source = "registry+https://github.com/rust-lang/crates.io-index" 4337 + checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 4338 4339 [[package]] 4340 name = "unicode-normalization" 4341 + version = "0.1.25" 4342 source = "registry+https://github.com/rust-lang/crates.io-index" 4343 + checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" 4344 dependencies = [ 4345 "tinyvec", 4346 ] 4347 4348 [[package]] 4349 name = "unicode-properties" 4350 + version = "0.1.4" 4351 source = "registry+https://github.com/rust-lang/crates.io-index" 4352 + checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" 4353 4354 [[package]] 4355 name = "unicode-segmentation" ··· 4364 checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 4365 4366 [[package]] 4367 + name = "unicode-xid" 4368 + version = "0.2.6" 4369 + source = "registry+https://github.com/rust-lang/crates.io-index" 4370 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 4371 + 4372 + [[package]] 4373 name = "unsigned-varint" 4374 version = "0.8.0" 4375 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4389 4390 [[package]] 4391 name = "url" 4392 + version = "2.5.7" 4393 source = "registry+https://github.com/rust-lang/crates.io-index" 4394 + checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 4395 dependencies = [ 4396 "form_urlencoded", 4397 "idna", ··· 4477 4478 [[package]] 4479 name = "wasm-bindgen" 4480 + version = "0.2.106" 4481 source = "registry+https://github.com/rust-lang/crates.io-index" 4482 + checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 4483 dependencies = [ 4484 "cfg-if", 4485 "once_cell", 4486 "rustversion", 4487 "wasm-bindgen-macro", 4488 "wasm-bindgen-shared", 4489 ] 4490 4491 [[package]] 4492 name = "wasm-bindgen-futures" 4493 + version = "0.4.56" 4494 source = "registry+https://github.com/rust-lang/crates.io-index" 4495 + checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" 4496 dependencies = [ 4497 "cfg-if", 4498 "js-sys", ··· 4503 4504 [[package]] 4505 name = "wasm-bindgen-macro" 4506 + version = "0.2.106" 4507 source = "registry+https://github.com/rust-lang/crates.io-index" 4508 + checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 4509 dependencies = [ 4510 "quote", 4511 "wasm-bindgen-macro-support", ··· 4513 4514 [[package]] 4515 name = "wasm-bindgen-macro-support" 4516 + version = "0.2.106" 4517 source = "registry+https://github.com/rust-lang/crates.io-index" 4518 + checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 4519 dependencies = [ 4520 + "bumpalo", 4521 "proc-macro2", 4522 "quote", 4523 + "syn 2.0.112", 4524 "wasm-bindgen-shared", 4525 ] 4526 4527 [[package]] 4528 name = "wasm-bindgen-shared" 4529 + version = "0.2.106" 4530 source = "registry+https://github.com/rust-lang/crates.io-index" 4531 + checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 4532 dependencies = [ 4533 "unicode-ident", 4534 ] 4535 4536 [[package]] 4537 name = "web-sys" 4538 + version = "0.3.83" 4539 source = "registry+https://github.com/rust-lang/crates.io-index" 4540 + checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" 4541 dependencies = [ 4542 "js-sys", 4543 "wasm-bindgen", ··· 4559 source = "registry+https://github.com/rust-lang/crates.io-index" 4560 checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 4561 dependencies = [ 4562 + "webpki-roots 1.0.5", 4563 ] 4564 4565 [[package]] 4566 name = "webpki-roots" 4567 + version = "1.0.5" 4568 source = "registry+https://github.com/rust-lang/crates.io-index" 4569 + checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" 4570 dependencies = [ 4571 "rustls-pki-types", 4572 ] 4573 4574 [[package]] ··· 4599 4600 [[package]] 4601 name = "winapi-util" 4602 + version = "0.1.11" 4603 source = "registry+https://github.com/rust-lang/crates.io-index" 4604 + checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 4605 dependencies = [ 4606 + "windows-sys 0.48.0", 4607 ] 4608 4609 [[package]] ··· 4614 4615 [[package]] 4616 name = "windows-core" 4617 + version = "0.62.2" 4618 source = "registry+https://github.com/rust-lang/crates.io-index" 4619 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 4620 dependencies = [ 4621 "windows-implement", 4622 "windows-interface", 4623 + "windows-link", 4624 "windows-result", 4625 "windows-strings", 4626 ] 4627 4628 [[package]] 4629 name = "windows-implement" 4630 + version = "0.60.2" 4631 source = "registry+https://github.com/rust-lang/crates.io-index" 4632 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 4633 dependencies = [ 4634 "proc-macro2", 4635 "quote", 4636 + "syn 2.0.112", 4637 ] 4638 4639 [[package]] 4640 name = "windows-interface" 4641 + version = "0.59.3" 4642 source = "registry+https://github.com/rust-lang/crates.io-index" 4643 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 4644 dependencies = [ 4645 "proc-macro2", 4646 "quote", 4647 + "syn 2.0.112", 4648 ] 4649 4650 [[package]] 4651 name = "windows-link" 4652 version = "0.2.1" 4653 source = "registry+https://github.com/rust-lang/crates.io-index" 4654 checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 4655 4656 [[package]] 4657 name = "windows-registry" 4658 + version = "0.6.1" 4659 source = "registry+https://github.com/rust-lang/crates.io-index" 4660 + checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 4661 dependencies = [ 4662 + "windows-link", 4663 "windows-result", 4664 "windows-strings", 4665 ] 4666 4667 [[package]] 4668 name = "windows-result" 4669 + version = "0.4.1" 4670 source = "registry+https://github.com/rust-lang/crates.io-index" 4671 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 4672 dependencies = [ 4673 + "windows-link", 4674 ] 4675 4676 [[package]] 4677 name = "windows-strings" 4678 + version = "0.5.1" 4679 source = "registry+https://github.com/rust-lang/crates.io-index" 4680 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 4681 dependencies = [ 4682 + "windows-link", 4683 ] 4684 4685 [[package]] ··· 4710 ] 4711 4712 [[package]] 4713 + name = "windows-sys" 4714 + version = "0.60.2" 4715 + source = "registry+https://github.com/rust-lang/crates.io-index" 4716 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 4717 + dependencies = [ 4718 + "windows-targets 0.53.5", 4719 + ] 4720 + 4721 + [[package]] 4722 + name = "windows-sys" 4723 + version = "0.61.2" 4724 + source = "registry+https://github.com/rust-lang/crates.io-index" 4725 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 4726 + dependencies = [ 4727 + "windows-link", 4728 + ] 4729 + 4730 + [[package]] 4731 name = "windows-targets" 4732 version = "0.48.5" 4733 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4751 "windows_aarch64_gnullvm 0.52.6", 4752 "windows_aarch64_msvc 0.52.6", 4753 "windows_i686_gnu 0.52.6", 4754 + "windows_i686_gnullvm 0.52.6", 4755 "windows_i686_msvc 0.52.6", 4756 "windows_x86_64_gnu 0.52.6", 4757 "windows_x86_64_gnullvm 0.52.6", ··· 4759 ] 4760 4761 [[package]] 4762 + name = "windows-targets" 4763 + version = "0.53.5" 4764 + source = "registry+https://github.com/rust-lang/crates.io-index" 4765 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 4766 + dependencies = [ 4767 + "windows-link", 4768 + "windows_aarch64_gnullvm 0.53.1", 4769 + "windows_aarch64_msvc 0.53.1", 4770 + "windows_i686_gnu 0.53.1", 4771 + "windows_i686_gnullvm 0.53.1", 4772 + "windows_i686_msvc 0.53.1", 4773 + "windows_x86_64_gnu 0.53.1", 4774 + "windows_x86_64_gnullvm 0.53.1", 4775 + "windows_x86_64_msvc 0.53.1", 4776 + ] 4777 + 4778 + [[package]] 4779 name = "windows_aarch64_gnullvm" 4780 version = "0.48.5" 4781 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4788 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 4789 4790 [[package]] 4791 + name = "windows_aarch64_gnullvm" 4792 + version = "0.53.1" 4793 + source = "registry+https://github.com/rust-lang/crates.io-index" 4794 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 4795 + 4796 + [[package]] 4797 name = "windows_aarch64_msvc" 4798 version = "0.48.5" 4799 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4806 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 4807 4808 [[package]] 4809 + name = "windows_aarch64_msvc" 4810 + version = "0.53.1" 4811 + source = "registry+https://github.com/rust-lang/crates.io-index" 4812 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 4813 + 4814 + [[package]] 4815 name = "windows_i686_gnu" 4816 version = "0.48.5" 4817 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4824 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 4825 4826 [[package]] 4827 + name = "windows_i686_gnu" 4828 + version = "0.53.1" 4829 + source = "registry+https://github.com/rust-lang/crates.io-index" 4830 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 4831 + 4832 + [[package]] 4833 name = "windows_i686_gnullvm" 4834 version = "0.52.6" 4835 source = "registry+https://github.com/rust-lang/crates.io-index" 4836 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 4837 + 4838 + [[package]] 4839 + name = "windows_i686_gnullvm" 4840 + version = "0.53.1" 4841 + source = "registry+https://github.com/rust-lang/crates.io-index" 4842 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 4843 4844 [[package]] 4845 name = "windows_i686_msvc" ··· 4854 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 4855 4856 [[package]] 4857 + name = "windows_i686_msvc" 4858 + version = "0.53.1" 4859 + source = "registry+https://github.com/rust-lang/crates.io-index" 4860 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 4861 + 4862 + [[package]] 4863 name = "windows_x86_64_gnu" 4864 version = "0.48.5" 4865 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4872 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 4873 4874 [[package]] 4875 + name = "windows_x86_64_gnu" 4876 + version = "0.53.1" 4877 + source = "registry+https://github.com/rust-lang/crates.io-index" 4878 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 4879 + 4880 + [[package]] 4881 name = "windows_x86_64_gnullvm" 4882 version = "0.48.5" 4883 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4890 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 4891 4892 [[package]] 4893 + name = "windows_x86_64_gnullvm" 4894 + version = "0.53.1" 4895 + source = "registry+https://github.com/rust-lang/crates.io-index" 4896 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 4897 + 4898 + [[package]] 4899 name = "windows_x86_64_msvc" 4900 version = "0.48.5" 4901 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4908 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 4909 4910 [[package]] 4911 + name = "windows_x86_64_msvc" 4912 + version = "0.53.1" 4913 + source = "registry+https://github.com/rust-lang/crates.io-index" 4914 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 4915 + 4916 + [[package]] 4917 name = "wit-bindgen" 4918 version = "0.46.0" 4919 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4921 4922 [[package]] 4923 name = "writeable" 4924 + version = "0.6.2" 4925 source = "registry+https://github.com/rust-lang/crates.io-index" 4926 + checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" 4927 4928 [[package]] 4929 name = "yansi" ··· 4933 4934 [[package]] 4935 name = "yoke" 4936 + version = "0.8.1" 4937 source = "registry+https://github.com/rust-lang/crates.io-index" 4938 + checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" 4939 dependencies = [ 4940 "stable_deref_trait", 4941 "yoke-derive", 4942 "zerofrom", ··· 4944 4945 [[package]] 4946 name = "yoke-derive" 4947 + version = "0.8.1" 4948 source = "registry+https://github.com/rust-lang/crates.io-index" 4949 + checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" 4950 dependencies = [ 4951 "proc-macro2", 4952 "quote", 4953 + "syn 2.0.112", 4954 "synstructure", 4955 ] 4956 4957 [[package]] 4958 name = "zerocopy" 4959 + version = "0.8.31" 4960 source = "registry+https://github.com/rust-lang/crates.io-index" 4961 + checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" 4962 dependencies = [ 4963 "zerocopy-derive", 4964 ] 4965 4966 [[package]] 4967 name = "zerocopy-derive" 4968 + version = "0.8.31" 4969 source = "registry+https://github.com/rust-lang/crates.io-index" 4970 + checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" 4971 dependencies = [ 4972 "proc-macro2", 4973 "quote", 4974 + "syn 2.0.112", 4975 ] 4976 4977 [[package]] ··· 4991 dependencies = [ 4992 "proc-macro2", 4993 "quote", 4994 + "syn 2.0.112", 4995 "synstructure", 4996 ] 4997 4998 [[package]] 4999 name = "zeroize" 5000 + version = "1.8.2" 5001 source = "registry+https://github.com/rust-lang/crates.io-index" 5002 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 5003 dependencies = [ 5004 "zeroize_derive", 5005 ] 5006 5007 [[package]] 5008 name = "zeroize_derive" 5009 + version = "1.4.3" 5010 source = "registry+https://github.com/rust-lang/crates.io-index" 5011 + checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" 5012 dependencies = [ 5013 "proc-macro2", 5014 "quote", 5015 + "syn 2.0.112", 5016 ] 5017 5018 [[package]] 5019 name = "zerotrie" 5020 + version = "0.2.3" 5021 source = "registry+https://github.com/rust-lang/crates.io-index" 5022 + checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" 5023 dependencies = [ 5024 "displaydoc", 5025 "yoke", ··· 5028 5029 [[package]] 5030 name = "zerovec" 5031 + version = "0.11.5" 5032 source = "registry+https://github.com/rust-lang/crates.io-index" 5033 + checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" 5034 dependencies = [ 5035 "yoke", 5036 "zerofrom", ··· 5039 5040 [[package]] 5041 name = "zerovec-derive" 5042 + version = "0.11.2" 5043 source = "registry+https://github.com/rust-lang/crates.io-index" 5044 + checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" 5045 dependencies = [ 5046 "proc-macro2", 5047 "quote", 5048 + "syn 2.0.112", 5049 ] 5050 5051 [[package]] 5052 + name = "zmij" 5053 + version = "1.0.8" 5054 + source = "registry+https://github.com/rust-lang/crates.io-index" 5055 + checksum = "317f17ff091ac4515f17cc7a190d2769a8c9a96d227de5d64b500b01cda8f2cd" 5056 + 5057 + [[package]] 5058 name = "zstd" 5059 version = "0.13.3" 5060 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5074 5075 [[package]] 5076 name = "zstd-sys" 5077 + version = "2.0.16+zstd.1.5.7" 5078 source = "registry+https://github.com/rust-lang/crates.io-index" 5079 + checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" 5080 dependencies = [ 5081 "cc", 5082 "pkg-config",
+11 -9
Cargo.toml
··· 5 license = "MIT" 6 7 [dependencies] 8 - axum = { version = "0.8.4", features = ["macros", "json"] } 9 - tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "signal"] } 10 sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "sqlite", "migrate", "chrono"] } 11 dotenvy = "0.15.7" 12 serde = { version = "1.0", features = ["derive"] } 13 serde_json = "1.0" 14 tracing = "0.1" 15 tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } 16 - hyper-util = { version = "0.1.16", features = ["client", "client-legacy"] } 17 tower-http = { version = "0.6", features = ["cors", "compression-zstd"] } 18 tower_governor = { version = "0.8.0", features = ["axum", "tracing"] } 19 hex = "0.4" 20 jwt-compact = { version = "0.8.0", features = ["es256k"] } 21 scrypt = "0.11" 22 #Leaveing these two cause I think it is needed by the email crate for ssl 23 - aws-lc-rs = "1.13.0" 24 rustls = { version = "0.23", default-features = false, features = ["tls12", "std", "logging", "aws_lc_rs"] } 25 lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "tokio1", "tokio1-rustls"] } 26 - handlebars = { version = "6.3.2", features = ["rust-embed"] } 27 - rust-embed = "8.7.2" 28 axum-template = { version = "3.0.0", features = ["handlebars"] } 29 rand = "0.9.2" 30 - anyhow = "1.0.99" 31 chrono = { version = "0.4.42", features = ["default", "serde"] } 32 sha2 = "0.10" 33 - jacquard-common = "0.9.2" 34 - jacquard-identity = "0.9.2" 35 multibase = "0.9.2" 36 reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } 37 urlencoding = "2.1" 38 html-escape = "0.2.13" 39 josekit = "0.10.3"
··· 5 license = "MIT" 6 7 [dependencies] 8 + axum = { version = "0.8.8", features = ["macros", "json"] } 9 + tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "signal"] } 10 sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "sqlite", "migrate", "chrono"] } 11 dotenvy = "0.15.7" 12 serde = { version = "1.0", features = ["derive"] } 13 serde_json = "1.0" 14 tracing = "0.1" 15 tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } 16 + hyper-util = { version = "0.1.19", features = ["client", "client-legacy"] } 17 tower-http = { version = "0.6", features = ["cors", "compression-zstd"] } 18 tower_governor = { version = "0.8.0", features = ["axum", "tracing"] } 19 hex = "0.4" 20 jwt-compact = { version = "0.8.0", features = ["es256k"] } 21 scrypt = "0.11" 22 #Leaveing these two cause I think it is needed by the email crate for ssl 23 + aws-lc-rs = "1.15.2" 24 rustls = { version = "0.23", default-features = false, features = ["tls12", "std", "logging", "aws_lc_rs"] } 25 lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "tokio1", "tokio1-rustls"] } 26 + handlebars = { version = "6.4.0", features = ["rust-embed"] } 27 + rust-embed = "8.9.0" 28 axum-template = { version = "3.0.0", features = ["handlebars"] } 29 rand = "0.9.2" 30 + anyhow = "1.0.100" 31 chrono = { version = "0.4.42", features = ["default", "serde"] } 32 sha2 = "0.10" 33 + jacquard-common = "0.9.5" 34 + jacquard-identity = "0.9.5" 35 multibase = "0.9.2" 36 reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } 37 urlencoding = "2.1" 38 html-escape = "0.2.13" 39 josekit = "0.10.3" 40 + dashmap = "6.1" 41 + tower = "0.5"
+101
src/auth/cache.rs
···
··· 1 + use dashmap::DashMap; 2 + use std::sync::Arc; 3 + use std::time::{Duration, Instant}; 4 + 5 + #[derive(Clone, Debug)] 6 + struct CachedHandle { 7 + handle: String, 8 + cached_at: Instant, 9 + } 10 + 11 + /// A thread-safe cache for DID-to-handle resolutions with TTL expiration. 12 + #[derive(Clone)] 13 + pub struct HandleCache { 14 + cache: Arc<DashMap<String, CachedHandle>>, 15 + ttl: Duration, 16 + } 17 + 18 + impl Default for HandleCache { 19 + fn default() -> Self { 20 + Self::new() 21 + } 22 + } 23 + 24 + impl HandleCache { 25 + /// Creates a new HandleCache with a default TTL of 1 hour. 26 + pub fn new() -> Self { 27 + Self::with_ttl(Duration::from_secs(3600)) 28 + } 29 + 30 + /// Creates a new HandleCache with a custom TTL. 31 + pub fn with_ttl(ttl: Duration) -> Self { 32 + Self { 33 + cache: Arc::new(DashMap::new()), 34 + ttl, 35 + } 36 + } 37 + 38 + /// Gets a cached handle for the given DID, if it exists and hasn't expired. 39 + pub fn get(&self, did: &str) -> Option<String> { 40 + let entry = self.cache.get(did)?; 41 + if entry.cached_at.elapsed() > self.ttl { 42 + drop(entry); 43 + self.cache.remove(did); 44 + return None; 45 + } 46 + Some(entry.handle.clone()) 47 + } 48 + 49 + /// Inserts a DID-to-handle mapping into the cache. 50 + pub fn insert(&self, did: String, handle: String) { 51 + self.cache.insert( 52 + did, 53 + CachedHandle { 54 + handle, 55 + cached_at: Instant::now(), 56 + }, 57 + ); 58 + } 59 + 60 + /// Removes expired entries from the cache. 61 + /// Call this periodically to prevent memory growth. 62 + pub fn cleanup(&self) { 63 + self.cache.retain(|_, v| v.cached_at.elapsed() <= self.ttl); 64 + } 65 + 66 + /// Returns the number of entries in the cache. 67 + pub fn len(&self) -> usize { 68 + self.cache.len() 69 + } 70 + 71 + /// Returns true if the cache is empty. 72 + pub fn is_empty(&self) -> bool { 73 + self.cache.is_empty() 74 + } 75 + } 76 + 77 + #[cfg(test)] 78 + mod tests { 79 + use super::*; 80 + 81 + #[test] 82 + fn test_cache_insert_and_get() { 83 + let cache = HandleCache::new(); 84 + cache.insert("did:plc:test".into(), "test.handle.com".into()); 85 + assert_eq!(cache.get("did:plc:test"), Some("test.handle.com".into())); 86 + } 87 + 88 + #[test] 89 + fn test_cache_miss() { 90 + let cache = HandleCache::new(); 91 + assert_eq!(cache.get("did:plc:nonexistent"), None); 92 + } 93 + 94 + #[test] 95 + fn test_cache_expiration() { 96 + let cache = HandleCache::with_ttl(Duration::from_millis(1)); 97 + cache.insert("did:plc:test".into(), "test.handle.com".into()); 98 + std::thread::sleep(Duration::from_millis(10)); 99 + assert_eq!(cache.get("did:plc:test"), None); 100 + } 101 + }
+456
src/auth/middleware.rs
···
··· 1 + use super::{AuthRules, HandleCache, SessionData}; 2 + use crate::helpers::json_error_response; 3 + use crate::AppState; 4 + use axum::extract::{Request, State}; 5 + use axum::http::{HeaderMap, StatusCode}; 6 + use axum::middleware::Next; 7 + use axum::response::{IntoResponse, Response}; 8 + use jacquard_identity::resolver::IdentityResolver; 9 + use jacquard_identity::PublicResolver; 10 + use jwt_compact::alg::{Hs256, Hs256Key}; 11 + use jwt_compact::{AlgorithmExt, Claims, Token, UntrustedToken, ValidationError}; 12 + use serde::{Deserialize, Serialize}; 13 + use std::env; 14 + use std::sync::Arc; 15 + use tracing::log; 16 + 17 + #[derive(Clone, Copy, Debug, PartialEq, Eq)] 18 + pub enum AuthScheme { 19 + Bearer, 20 + DPoP, 21 + } 22 + 23 + #[derive(Serialize, Deserialize)] 24 + pub struct TokenClaims { 25 + pub sub: String, 26 + /// OAuth scopes as space-separated string (per OAuth 2.0 spec) 27 + #[serde(default)] 28 + pub scope: Option<String>, 29 + } 30 + 31 + /// State passed to the auth middleware containing both AppState and auth rules. 32 + #[derive(Clone)] 33 + pub struct AuthMiddlewareState { 34 + pub app_state: AppState, 35 + pub rules: AuthRules, 36 + } 37 + 38 + /// Core middleware function that validates authentication and applies auth rules. 39 + /// 40 + /// Use this with `axum::middleware::from_fn_with_state`: 41 + /// ```ignore 42 + /// use axum::middleware::from_fn_with_state; 43 + /// 44 + /// let mw_state = AuthMiddlewareState { 45 + /// app_state: state.clone(), 46 + /// rules: AuthRules::HandleEndsWith(".blacksky.team".into()), 47 + /// }; 48 + /// 49 + /// .route("/protected", get(handler).layer(from_fn_with_state(mw_state, auth_middleware))) 50 + /// ``` 51 + pub async fn auth_middleware( 52 + State(mw_state): State<AuthMiddlewareState>, 53 + req: Request, 54 + next: Next, 55 + ) -> Response { 56 + let AuthMiddlewareState { app_state, rules } = mw_state; 57 + 58 + // 1. Extract DID and scopes from JWT (Bearer token) 59 + let extracted = match extract_auth_from_request(req.headers()) { 60 + Ok(Some(auth)) => auth, 61 + Ok(None) => { 62 + return json_error_response(StatusCode::UNAUTHORIZED, "AuthRequired", "Authentication required") 63 + .unwrap_or_else(|_| StatusCode::UNAUTHORIZED.into_response()); 64 + } 65 + Err(e) => { 66 + log::error!("Token extraction error: {}", e); 67 + return json_error_response(StatusCode::UNAUTHORIZED, "InvalidToken", &e) 68 + .unwrap_or_else(|_| StatusCode::UNAUTHORIZED.into_response()); 69 + } 70 + }; 71 + 72 + // 2. Resolve DID to handle (check cache first) 73 + let handle = match resolve_did_to_handle(&app_state.resolver, &app_state.handle_cache, &extracted.did).await { 74 + Ok(handle) => handle, 75 + Err(e) => { 76 + log::error!("Failed to resolve DID {} to handle: {}", extracted.did, e); 77 + return json_error_response( 78 + StatusCode::INTERNAL_SERVER_ERROR, 79 + "ResolutionError", 80 + "Failed to resolve identity", 81 + ) 82 + .unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR.into_response()); 83 + } 84 + }; 85 + 86 + // 3. Build session data and validate rules 87 + let session = SessionData { 88 + did: extracted.did, 89 + handle, 90 + scopes: extracted.scopes, 91 + }; 92 + 93 + if !rules.validate(&session) { 94 + return json_error_response(StatusCode::FORBIDDEN, "AccessDenied", "Access denied by authorization rules") 95 + .unwrap_or_else(|_| StatusCode::FORBIDDEN.into_response()); 96 + } 97 + 98 + // 4. Pass through on success 99 + next.run(req).await 100 + } 101 + 102 + /// Extracted authentication data from JWT 103 + struct ExtractedAuth { 104 + did: String, 105 + scopes: Vec<String>, 106 + } 107 + 108 + /// Extracts the DID and scopes from the Authorization header (Bearer JWT). 109 + fn extract_auth_from_request(headers: &HeaderMap) -> Result<Option<ExtractedAuth>, String> { 110 + let auth = extract_auth(headers)?; 111 + 112 + match auth { 113 + None => Ok(None), 114 + Some((scheme, token_str)) => { 115 + match scheme { 116 + AuthScheme::Bearer => { 117 + let token = UntrustedToken::new(&token_str) 118 + .map_err(|_| "Invalid token format".to_string())?; 119 + 120 + let _claims: Claims<TokenClaims> = token 121 + .deserialize_claims_unchecked() 122 + .map_err(|_| "Failed to parse token claims".to_string())?; 123 + 124 + let key = Hs256Key::new( 125 + env::var("PDS_JWT_SECRET") 126 + .map_err(|_| "PDS_JWT_SECRET not configured".to_string())?, 127 + ); 128 + 129 + let validated: Token<TokenClaims> = Hs256 130 + .validator(&key) 131 + .validate(&token) 132 + .map_err(|e: ValidationError| format!("Token validation failed: {:?}", e))?; 133 + 134 + let custom = &validated.claims().custom; 135 + 136 + // Parse scopes from space-separated string (OAuth 2.0 spec) 137 + let scopes: Vec<String> = custom.scope 138 + .as_ref() 139 + .map(|s| s.split_whitespace().map(|s| s.to_string()).collect()) 140 + .unwrap_or_default(); 141 + 142 + Ok(Some(ExtractedAuth { 143 + did: custom.sub.clone(), 144 + scopes, 145 + })) 146 + } 147 + AuthScheme::DPoP => { 148 + // DPoP tokens are not validated here; pass through without auth data 149 + Ok(None) 150 + } 151 + } 152 + } 153 + } 154 + } 155 + 156 + /// Extracts the authentication scheme and token from the Authorization header. 157 + fn extract_auth(headers: &HeaderMap) -> Result<Option<(AuthScheme, String)>, String> { 158 + match headers.get(axum::http::header::AUTHORIZATION) { 159 + None => Ok(None), 160 + Some(hv) => { 161 + let s = hv 162 + .to_str() 163 + .map_err(|_| "Authorization header is not valid UTF-8".to_string())?; 164 + 165 + let mut parts = s.splitn(2, ' '); 166 + match (parts.next(), parts.next()) { 167 + (Some("Bearer"), Some(tok)) if !tok.is_empty() => { 168 + Ok(Some((AuthScheme::Bearer, tok.to_string()))) 169 + } 170 + (Some("DPoP"), Some(tok)) if !tok.is_empty() => { 171 + Ok(Some((AuthScheme::DPoP, tok.to_string()))) 172 + } 173 + _ => Err( 174 + "Authorization header must be in format 'Bearer <token>' or 'DPoP <token>'" 175 + .to_string(), 176 + ), 177 + } 178 + } 179 + } 180 + } 181 + 182 + /// Resolves a DID to its handle using the PublicResolver, with caching. 183 + async fn resolve_did_to_handle( 184 + resolver: &Arc<PublicResolver>, 185 + cache: &HandleCache, 186 + did: &str, 187 + ) -> Result<String, String> { 188 + // Check cache first 189 + if let Some(handle) = cache.get(did) { 190 + return Ok(handle); 191 + } 192 + 193 + // Parse the DID 194 + let did_parsed = jacquard_common::types::did::Did::new(did) 195 + .map_err(|e| format!("Invalid DID: {:?}", e))?; 196 + 197 + // Resolve the DID document 198 + let did_doc_response = resolver 199 + .resolve_did_doc(&did_parsed) 200 + .await 201 + .map_err(|e| format!("DID resolution failed: {:?}", e))?; 202 + 203 + let doc = did_doc_response 204 + .parse() 205 + .map_err(|e| format!("Failed to parse DID document: {:?}", e))?; 206 + 207 + // Extract handle from alsoKnownAs field 208 + // Format is typically: ["at://handle.example.com"] 209 + let handle: String = doc 210 + .also_known_as 211 + .as_ref() 212 + .and_then(|aka| { 213 + aka.iter() 214 + .find(|uri| uri.starts_with("at://")) 215 + .map(|uri| uri.strip_prefix("at://").unwrap_or(uri.as_ref()).to_string()) 216 + }) 217 + .ok_or_else(|| "No ATProto handle found in DID document".to_string())?; 218 + 219 + // Cache the result 220 + cache.insert(did.to_string(), handle.clone()); 221 + 222 + Ok(handle) 223 + } 224 + 225 + // ============================================================================ 226 + // Helper Functions for Creating Middleware State 227 + // ============================================================================ 228 + 229 + /// Creates an `AuthMiddlewareState` for requiring the handle to end with a specific suffix. 230 + /// 231 + /// # Example 232 + /// ```ignore 233 + /// use axum::middleware::from_fn_with_state; 234 + /// use crate::auth::{auth_middleware, handle_ends_with}; 235 + /// 236 + /// .route("/protected", get(handler).layer( 237 + /// from_fn_with_state(handle_ends_with(".blacksky.team", &state), auth_middleware) 238 + /// )) 239 + /// ``` 240 + pub fn handle_ends_with(suffix: impl Into<String>, state: &AppState) -> AuthMiddlewareState { 241 + AuthMiddlewareState { 242 + app_state: state.clone(), 243 + rules: AuthRules::HandleEndsWith(suffix.into()), 244 + } 245 + } 246 + 247 + /// Creates an `AuthMiddlewareState` for requiring the handle to end with any of the specified suffixes. 248 + pub fn handle_ends_with_any<I, T>(suffixes: I, state: &AppState) -> AuthMiddlewareState 249 + where 250 + I: IntoIterator<Item = T>, 251 + T: Into<String>, 252 + { 253 + AuthMiddlewareState { 254 + app_state: state.clone(), 255 + rules: AuthRules::HandleEndsWithAny(suffixes.into_iter().map(|s| s.into()).collect()), 256 + } 257 + } 258 + 259 + /// Creates an `AuthMiddlewareState` for requiring the DID to equal a specific value. 260 + pub fn did_equals(did: impl Into<String>, state: &AppState) -> AuthMiddlewareState { 261 + AuthMiddlewareState { 262 + app_state: state.clone(), 263 + rules: AuthRules::DidEquals(did.into()), 264 + } 265 + } 266 + 267 + /// Creates an `AuthMiddlewareState` for requiring the DID to be one of the specified values. 268 + pub fn did_equals_any<I, T>(dids: I, state: &AppState) -> AuthMiddlewareState 269 + where 270 + I: IntoIterator<Item = T>, 271 + T: Into<String>, 272 + { 273 + AuthMiddlewareState { 274 + app_state: state.clone(), 275 + rules: AuthRules::DidEqualsAny(dids.into_iter().map(|d| d.into()).collect()), 276 + } 277 + } 278 + 279 + /// Creates an `AuthMiddlewareState` with custom auth rules. 280 + pub fn with_rules(rules: AuthRules, state: &AppState) -> AuthMiddlewareState { 281 + AuthMiddlewareState { 282 + app_state: state.clone(), 283 + rules, 284 + } 285 + } 286 + 287 + // ============================================================================ 288 + // Scope Helper Functions 289 + // ============================================================================ 290 + 291 + /// Creates an `AuthMiddlewareState` requiring a specific OAuth scope. 292 + /// 293 + /// # Example 294 + /// ```ignore 295 + /// .route("/xrpc/com.atproto.repo.createRecord", 296 + /// post(handler).layer(from_fn_with_state( 297 + /// scope_equals("repo:app.bsky.feed.post", &state), 298 + /// auth_middleware 299 + /// ))) 300 + /// ``` 301 + pub fn scope_equals(scope: impl Into<String>, state: &AppState) -> AuthMiddlewareState { 302 + AuthMiddlewareState { 303 + app_state: state.clone(), 304 + rules: AuthRules::ScopeEquals(scope.into()), 305 + } 306 + } 307 + 308 + /// Creates an `AuthMiddlewareState` requiring ANY of the specified scopes (OR logic). 309 + /// 310 + /// # Example 311 + /// ```ignore 312 + /// .route("/xrpc/com.atproto.repo.putRecord", 313 + /// post(handler).layer(from_fn_with_state( 314 + /// scope_any(["repo:app.bsky.feed.post", "transition:generic"], &state), 315 + /// auth_middleware 316 + /// ))) 317 + /// ``` 318 + pub fn scope_any<I, T>(scopes: I, state: &AppState) -> AuthMiddlewareState 319 + where 320 + I: IntoIterator<Item = T>, 321 + T: Into<String>, 322 + { 323 + AuthMiddlewareState { 324 + app_state: state.clone(), 325 + rules: AuthRules::ScopeEqualsAny(scopes.into_iter().map(|s| s.into()).collect()), 326 + } 327 + } 328 + 329 + /// Creates an `AuthMiddlewareState` requiring ALL of the specified scopes (AND logic). 330 + /// 331 + /// # Example 332 + /// ```ignore 333 + /// .route("/xrpc/com.atproto.admin.updateAccount", 334 + /// post(handler).layer(from_fn_with_state( 335 + /// scope_all(["account:email", "account:repo?action=manage"], &state), 336 + /// auth_middleware 337 + /// ))) 338 + /// ``` 339 + pub fn scope_all<I, T>(scopes: I, state: &AppState) -> AuthMiddlewareState 340 + where 341 + I: IntoIterator<Item = T>, 342 + T: Into<String>, 343 + { 344 + AuthMiddlewareState { 345 + app_state: state.clone(), 346 + rules: AuthRules::ScopeEqualsAll(scopes.into_iter().map(|s| s.into()).collect()), 347 + } 348 + } 349 + 350 + // ============================================================================ 351 + // Combined Rule Helpers (Identity + Scope) 352 + // ============================================================================ 353 + 354 + /// Creates an `AuthMiddlewareState` requiring handle to end with suffix AND have a specific scope. 355 + /// 356 + /// # Example 357 + /// ```ignore 358 + /// .route("/xrpc/community.blacksky.feed.generator", 359 + /// post(handler).layer(from_fn_with_state( 360 + /// handle_ends_with_and_scope(".blacksky.team", "transition:generic", &state), 361 + /// auth_middleware 362 + /// ))) 363 + /// ``` 364 + pub fn handle_ends_with_and_scope( 365 + suffix: impl Into<String>, 366 + scope: impl Into<String>, 367 + state: &AppState, 368 + ) -> AuthMiddlewareState { 369 + AuthMiddlewareState { 370 + app_state: state.clone(), 371 + rules: AuthRules::All(vec![ 372 + AuthRules::HandleEndsWith(suffix.into()), 373 + AuthRules::ScopeEquals(scope.into()), 374 + ]), 375 + } 376 + } 377 + 378 + /// Creates an `AuthMiddlewareState` requiring handle to end with suffix AND have ALL specified scopes. 379 + /// 380 + /// # Example 381 + /// ```ignore 382 + /// .route("/xrpc/community.blacksky.admin.manage", 383 + /// post(handler).layer(from_fn_with_state( 384 + /// handle_ends_with_and_scopes(".blacksky.team", ["transition:generic", "identity:*"], &state), 385 + /// auth_middleware 386 + /// ))) 387 + /// ``` 388 + pub fn handle_ends_with_and_scopes<I, T>( 389 + suffix: impl Into<String>, 390 + scopes: I, 391 + state: &AppState, 392 + ) -> AuthMiddlewareState 393 + where 394 + I: IntoIterator<Item = T>, 395 + T: Into<String>, 396 + { 397 + AuthMiddlewareState { 398 + app_state: state.clone(), 399 + rules: AuthRules::All(vec![ 400 + AuthRules::HandleEndsWith(suffix.into()), 401 + AuthRules::ScopeEqualsAll(scopes.into_iter().map(|s| s.into()).collect()), 402 + ]), 403 + } 404 + } 405 + 406 + /// Creates an `AuthMiddlewareState` requiring DID to equal value AND have a specific scope. 407 + /// 408 + /// # Example 409 + /// ```ignore 410 + /// .route("/xrpc/com.atproto.admin.deleteAccount", 411 + /// post(handler).layer(from_fn_with_state( 412 + /// did_with_scope("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "transition:generic", &state), 413 + /// auth_middleware 414 + /// ))) 415 + /// ``` 416 + pub fn did_with_scope( 417 + did: impl Into<String>, 418 + scope: impl Into<String>, 419 + state: &AppState, 420 + ) -> AuthMiddlewareState { 421 + AuthMiddlewareState { 422 + app_state: state.clone(), 423 + rules: AuthRules::All(vec![ 424 + AuthRules::DidEquals(did.into()), 425 + AuthRules::ScopeEquals(scope.into()), 426 + ]), 427 + } 428 + } 429 + 430 + /// Creates an `AuthMiddlewareState` requiring DID to equal value AND have ALL specified scopes. 431 + /// 432 + /// # Example 433 + /// ```ignore 434 + /// .route("/xrpc/com.atproto.admin.fullAccess", 435 + /// post(handler).layer(from_fn_with_state( 436 + /// did_with_scopes("did:plc:rnpkyqnmsw4ipey6eotbdnnf", ["transition:generic", "identity:*"], &state), 437 + /// auth_middleware 438 + /// ))) 439 + /// ``` 440 + pub fn did_with_scopes<I, T>( 441 + did: impl Into<String>, 442 + scopes: I, 443 + state: &AppState, 444 + ) -> AuthMiddlewareState 445 + where 446 + I: IntoIterator<Item = T>, 447 + T: Into<String>, 448 + { 449 + AuthMiddlewareState { 450 + app_state: state.clone(), 451 + rules: AuthRules::All(vec![ 452 + AuthRules::DidEquals(did.into()), 453 + AuthRules::ScopeEqualsAll(scopes.into_iter().map(|s| s.into()).collect()), 454 + ]), 455 + } 456 + }
+16
src/auth/mod.rs
···
··· 1 + mod cache; 2 + mod middleware; 3 + mod rules; 4 + 5 + pub use cache::HandleCache; 6 + pub use middleware::{ 7 + // Core middleware 8 + auth_middleware, with_rules, AuthMiddlewareState, 9 + // Identity helpers 10 + did_equals, did_equals_any, handle_ends_with, handle_ends_with_any, 11 + // Scope helpers 12 + scope_all, scope_any, scope_equals, 13 + // Combined helpers (identity + scope) 14 + did_with_scope, did_with_scopes, handle_ends_with_and_scope, handle_ends_with_and_scopes, 15 + }; 16 + pub use rules::{AuthRules, SessionData};
+694
src/auth/rules.rs
···
··· 1 + /// Authentication rules that can be validated against session data 2 + #[derive(Debug, Clone, PartialEq)] 3 + pub enum AuthRules { 4 + /// Handle must end with the specified suffix 5 + HandleEndsWith(String), 6 + /// Handle must end with any of the specified suffixes (OR logic) 7 + HandleEndsWithAny(Vec<String>), 8 + /// DID must exactly match the specified value 9 + DidEquals(String), 10 + /// DID must match any of the specified values (OR logic) 11 + DidEqualsAny(Vec<String>), 12 + /// Session must have the specified OAuth scope 13 + ScopeEquals(String), 14 + /// Session must have ANY of the specified scopes (OR logic) 15 + ScopeEqualsAny(Vec<String>), 16 + /// Session must have ALL of the specified scopes (AND logic) 17 + ScopeEqualsAll(Vec<String>), 18 + /// All nested rules must be satisfied (AND logic) 19 + All(Vec<AuthRules>), 20 + /// At least one nested rule must be satisfied (OR logic) 21 + Any(Vec<AuthRules>), 22 + } 23 + 24 + /// Session data used for authentication validation 25 + #[derive(Debug, Clone)] 26 + pub struct SessionData { 27 + /// The user's DID 28 + pub did: String, 29 + /// The user's handle 30 + pub handle: String, 31 + /// OAuth 2.0 scopes granted to this session 32 + pub scopes: Vec<String>, 33 + } 34 + 35 + impl AuthRules { 36 + /// Validates if the given session data meets the authentication requirements 37 + pub fn validate(&self, session_data: &SessionData) -> bool { 38 + match self { 39 + AuthRules::HandleEndsWith(suffix) => session_data.handle.ends_with(suffix), 40 + AuthRules::HandleEndsWithAny(suffixes) => { 41 + suffixes.iter().any(|s| session_data.handle.ends_with(s)) 42 + } 43 + AuthRules::DidEquals(did) => session_data.did == *did, 44 + AuthRules::DidEqualsAny(dids) => dids.iter().any(|d| session_data.did == *d), 45 + AuthRules::ScopeEquals(scope) => has_scope(&session_data.scopes, scope), 46 + AuthRules::ScopeEqualsAny(scopes) => has_any_scope(&session_data.scopes, scopes), 47 + AuthRules::ScopeEqualsAll(scopes) => has_all_scopes(&session_data.scopes, scopes), 48 + AuthRules::All(rules) => rules.iter().all(|r| r.validate(session_data)), 49 + AuthRules::Any(rules) => rules.iter().any(|r| r.validate(session_data)), 50 + } 51 + } 52 + } 53 + 54 + /// Checks if the session has a specific scope 55 + pub fn has_scope(scopes: &[String], required_scope: &str) -> bool { 56 + scopes.iter().any(|s| s == required_scope) 57 + } 58 + 59 + /// Checks if the session has ANY of the required scopes (OR logic) 60 + pub fn has_any_scope(scopes: &[String], required_scopes: &[String]) -> bool { 61 + required_scopes.iter().any(|req| has_scope(scopes, req)) 62 + } 63 + 64 + /// Checks if the session has ALL of the required scopes (AND logic) 65 + pub fn has_all_scopes(scopes: &[String], required_scopes: &[String]) -> bool { 66 + required_scopes.iter().all(|req| has_scope(scopes, req)) 67 + } 68 + 69 + #[cfg(test)] 70 + mod tests { 71 + use super::*; 72 + 73 + fn test_session(did: &str, handle: &str, scopes: Vec<&str>) -> SessionData { 74 + SessionData { 75 + did: did.to_string(), 76 + handle: handle.to_string(), 77 + scopes: scopes.into_iter().map(|s| s.to_string()).collect(), 78 + } 79 + } 80 + 81 + #[test] 82 + fn test_handle_ends_with() { 83 + let rules = AuthRules::HandleEndsWith(".blacksky.team".into()); 84 + 85 + let valid = test_session("did:plc:123", "alice.blacksky.team", vec!["atproto"]); 86 + assert!(rules.validate(&valid)); 87 + 88 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 89 + assert!(!rules.validate(&invalid)); 90 + } 91 + 92 + #[test] 93 + fn test_handle_ends_with_any() { 94 + let rules = AuthRules::HandleEndsWithAny(vec![".blacksky.team".into(), ".bsky.team".into()]); 95 + 96 + let valid1 = test_session("did:plc:123", "alice.blacksky.team", vec!["atproto"]); 97 + assert!(rules.validate(&valid1)); 98 + 99 + let valid2 = test_session("did:plc:123", "bob.bsky.team", vec!["atproto"]); 100 + assert!(rules.validate(&valid2)); 101 + 102 + let invalid = test_session("did:plc:123", "charlie.bsky.social", vec!["atproto"]); 103 + assert!(!rules.validate(&invalid)); 104 + } 105 + 106 + #[test] 107 + fn test_did_equals() { 108 + let rules = AuthRules::DidEquals("did:plc:alice".into()); 109 + 110 + let valid = test_session("did:plc:alice", "alice.bsky.social", vec!["atproto"]); 111 + assert!(rules.validate(&valid)); 112 + 113 + let invalid = test_session("did:plc:bob", "bob.bsky.social", vec!["atproto"]); 114 + assert!(!rules.validate(&invalid)); 115 + } 116 + 117 + #[test] 118 + fn test_any_combinator() { 119 + let rules = AuthRules::Any(vec![ 120 + AuthRules::DidEquals("did:plc:admin".into()), 121 + AuthRules::HandleEndsWith(".blacksky.team".into()), 122 + ]); 123 + 124 + // First condition met 125 + let valid1 = test_session("did:plc:admin", "admin.bsky.social", vec!["atproto"]); 126 + assert!(rules.validate(&valid1)); 127 + 128 + // Second condition met 129 + let valid2 = test_session("did:plc:user", "user.blacksky.team", vec!["atproto"]); 130 + assert!(rules.validate(&valid2)); 131 + 132 + // Neither condition met 133 + let invalid = test_session("did:plc:user", "user.bsky.social", vec!["atproto"]); 134 + assert!(!rules.validate(&invalid)); 135 + } 136 + 137 + #[test] 138 + fn test_all_combinator() { 139 + let rules = AuthRules::All(vec![ 140 + AuthRules::HandleEndsWith(".blacksky.team".into()), 141 + AuthRules::DidEqualsAny(vec!["did:plc:alice".into(), "did:plc:bob".into()]), 142 + ]); 143 + 144 + // Both conditions met 145 + let valid = test_session("did:plc:alice", "alice.blacksky.team", vec!["atproto"]); 146 + assert!(rules.validate(&valid)); 147 + 148 + // Handle wrong 149 + let invalid1 = test_session("did:plc:alice", "alice.bsky.social", vec!["atproto"]); 150 + assert!(!rules.validate(&invalid1)); 151 + 152 + // DID wrong 153 + let invalid2 = test_session("did:plc:charlie", "charlie.blacksky.team", vec!["atproto"]); 154 + assert!(!rules.validate(&invalid2)); 155 + } 156 + 157 + // ======================================================================== 158 + // Scope Tests 159 + // ======================================================================== 160 + 161 + #[test] 162 + fn test_scope_equals() { 163 + let rules = AuthRules::ScopeEquals("transition:generic".into()); 164 + 165 + let valid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "transition:generic"]); 166 + assert!(rules.validate(&valid)); 167 + 168 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 169 + assert!(!rules.validate(&invalid)); 170 + } 171 + 172 + #[test] 173 + fn test_scope_any() { 174 + let rules = AuthRules::ScopeEqualsAny(vec![ 175 + "transition:generic".into(), 176 + "repo:app.bsky.feed.post".into(), 177 + ]); 178 + 179 + // Has first scope 180 + let valid1 = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "transition:generic"]); 181 + assert!(rules.validate(&valid1)); 182 + 183 + // Has second scope 184 + let valid2 = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "repo:app.bsky.feed.post"]); 185 + assert!(rules.validate(&valid2)); 186 + 187 + // Has neither 188 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 189 + assert!(!rules.validate(&invalid)); 190 + } 191 + 192 + #[test] 193 + fn test_scope_all() { 194 + let rules = AuthRules::ScopeEqualsAll(vec![ 195 + "atproto".into(), 196 + "transition:generic".into(), 197 + ]); 198 + 199 + // Has both scopes 200 + let valid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto", "transition:generic"]); 201 + assert!(rules.validate(&valid)); 202 + 203 + // Missing one scope 204 + let invalid = test_session("did:plc:123", "alice.bsky.social", vec!["atproto"]); 205 + assert!(!rules.validate(&invalid)); 206 + } 207 + 208 + // ======================================================================== 209 + // Combined Rules Tests (Identity + Scope) 210 + // ======================================================================== 211 + 212 + #[test] 213 + fn test_handle_ends_with_and_scope() { 214 + let rules = AuthRules::All(vec![ 215 + AuthRules::HandleEndsWith(".blacksky.team".into()), 216 + AuthRules::ScopeEquals("transition:generic".into()), 217 + ]); 218 + 219 + // Both conditions met 220 + let valid = test_session( 221 + "did:plc:123", 222 + "alice.blacksky.team", 223 + vec!["atproto", "transition:generic"], 224 + ); 225 + assert!(rules.validate(&valid)); 226 + 227 + // Handle correct, scope wrong 228 + let invalid1 = test_session("did:plc:123", "alice.blacksky.team", vec!["atproto"]); 229 + assert!(!rules.validate(&invalid1)); 230 + 231 + // Scope correct, handle wrong 232 + let invalid2 = test_session( 233 + "did:plc:123", 234 + "alice.bsky.social", 235 + vec!["atproto", "transition:generic"], 236 + ); 237 + assert!(!rules.validate(&invalid2)); 238 + } 239 + 240 + #[test] 241 + fn test_did_with_scope() { 242 + let rules = AuthRules::All(vec![ 243 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 244 + AuthRules::ScopeEquals("transition:generic".into()), 245 + ]); 246 + 247 + // Both conditions met 248 + let valid = test_session( 249 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 250 + "admin.bsky.social", 251 + vec!["atproto", "transition:generic"], 252 + ); 253 + assert!(rules.validate(&valid)); 254 + 255 + // DID correct, scope wrong 256 + let invalid1 = test_session( 257 + "did:plc:rnpkyqnmsw4ipey6eotbdnnf", 258 + "admin.bsky.social", 259 + vec!["atproto"], 260 + ); 261 + assert!(!rules.validate(&invalid1)); 262 + 263 + // Scope correct, DID wrong 264 + let invalid2 = test_session( 265 + "did:plc:wrongdid", 266 + "admin.bsky.social", 267 + vec!["atproto", "transition:generic"], 268 + ); 269 + assert!(!rules.validate(&invalid2)); 270 + } 271 + 272 + #[test] 273 + fn test_complex_admin_or_moderator_rule() { 274 + // Admin DID OR (moderator handle + scope) 275 + let rules = AuthRules::Any(vec![ 276 + AuthRules::DidEquals("did:plc:rnpkyqnmsw4ipey6eotbdnnf".into()), 277 + AuthRules::All(vec![ 278 + AuthRules::HandleEndsWith(".mod.team".into()), 279 + AuthRules::ScopeEquals("account:email".into()), 280 + ]), 281 + ]); 282 + 283 + // Admin DID (doesn't need scope) 284 + let admin = test_session("did:plc:rnpkyqnmsw4ipey6eotbdnnf", "admin.bsky.social", vec!["atproto"]); 285 + assert!(rules.validate(&admin)); 286 + 287 + // Moderator with correct handle and scope 288 + let mod_valid = test_session( 289 + "did:plc:somemod", 290 + "alice.mod.team", 291 + vec!["atproto", "account:email"], 292 + ); 293 + assert!(rules.validate(&mod_valid)); 294 + 295 + // Moderator handle but missing scope 296 + let mod_no_scope = test_session("did:plc:somemod", "alice.mod.team", vec!["atproto"]); 297 + assert!(!rules.validate(&mod_no_scope)); 298 + 299 + // Has scope but not moderator handle 300 + let not_mod = test_session( 301 + "did:plc:someuser", 302 + "alice.bsky.social", 303 + vec!["atproto", "account:email"], 304 + ); 305 + assert!(!rules.validate(&not_mod)); 306 + } 307 + 308 + // ======================================================================== 309 + // Additional Coverage Tests 310 + // ======================================================================== 311 + 312 + #[test] 313 + fn test_did_equals_any() { 314 + let rules = AuthRules::DidEqualsAny(vec![ 315 + "did:plc:alice123456789012345678".into(), 316 + "did:plc:bob12345678901234567890".into(), 317 + "did:plc:charlie12345678901234567".into(), 318 + ]); 319 + 320 + // First DID matches 321 + let valid1 = test_session("did:plc:alice123456789012345678", "alice.bsky.social", vec!["atproto"]); 322 + assert!(rules.validate(&valid1)); 323 + 324 + // Second DID matches 325 + let valid2 = test_session("did:plc:bob12345678901234567890", "bob.bsky.social", vec!["atproto"]); 326 + assert!(rules.validate(&valid2)); 327 + 328 + // Third DID matches 329 + let valid3 = test_session("did:plc:charlie12345678901234567", "charlie.bsky.social", vec!["atproto"]); 330 + assert!(rules.validate(&valid3)); 331 + 332 + // DID not in list 333 + let invalid = test_session("did:plc:unknown1234567890123456", "unknown.bsky.social", vec!["atproto"]); 334 + assert!(!rules.validate(&invalid)); 335 + 336 + // Partial match should fail (prefix) 337 + let partial = test_session("did:plc:alice", "alice.bsky.social", vec!["atproto"]); 338 + assert!(!rules.validate(&partial)); 339 + } 340 + 341 + // ======================================================================== 342 + // Empty Combinator Edge Cases 343 + // ======================================================================== 344 + 345 + #[test] 346 + fn test_empty_any_returns_false() { 347 + // Any with empty rules should return false (no rule can be satisfied) 348 + let rules = AuthRules::Any(vec![]); 349 + let session = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["atproto"]); 350 + assert!(!rules.validate(&session)); 351 + } 352 + 353 + #[test] 354 + fn test_empty_all_returns_true() { 355 + // All with empty rules should return true (vacuous truth - all zero rules are satisfied) 356 + let rules = AuthRules::All(vec![]); 357 + let session = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["atproto"]); 358 + assert!(rules.validate(&session)); 359 + } 360 + 361 + // ======================================================================== 362 + // Handle Edge Cases 363 + // ======================================================================== 364 + 365 + #[test] 366 + fn test_handle_exact_suffix_match() { 367 + // Handle that IS exactly the suffix should still match 368 + let rules = AuthRules::HandleEndsWith(".blacksky.team".into()); 369 + 370 + // Handle is exactly the suffix 371 + let exact = test_session("did:plc:test12345678901234567", ".blacksky.team", vec!["atproto"]); 372 + assert!(rules.validate(&exact)); 373 + 374 + // Normal case - handle with prefix 375 + let normal = test_session("did:plc:test12345678901234567", "alice.blacksky.team", vec!["atproto"]); 376 + assert!(rules.validate(&normal)); 377 + 378 + // Empty handle should not match 379 + let empty = test_session("did:plc:test12345678901234567", "", vec!["atproto"]); 380 + assert!(!rules.validate(&empty)); 381 + } 382 + 383 + // ======================================================================== 384 + // Deeply Nested Combinators 385 + // ======================================================================== 386 + 387 + #[test] 388 + fn test_deeply_nested_rules() { 389 + // Complex nested rule: Any(All(Any(...), ...), All(...)) 390 + // Scenario: (Admin OR VIP) AND (has scope OR team member) 391 + // OR 392 + // Specific moderator DID 393 + let rules = AuthRules::Any(vec![ 394 + // Branch 1: Complex nested condition 395 + AuthRules::All(vec![ 396 + // Must be admin or VIP 397 + AuthRules::Any(vec![ 398 + AuthRules::DidEquals("did:plc:admin123456789012345".into()), 399 + AuthRules::HandleEndsWith(".vip.social".into()), 400 + ]), 401 + // AND must have scope or be team member 402 + AuthRules::Any(vec![ 403 + AuthRules::ScopeEquals("transition:generic".into()), 404 + AuthRules::HandleEndsWith(".team.internal".into()), 405 + ]), 406 + ]), 407 + // Branch 2: Specific moderator bypass 408 + AuthRules::DidEquals("did:plc:moderator12345678901".into()), 409 + ]); 410 + 411 + // Admin with required scope - should pass via Branch 1 412 + let admin_with_scope = test_session( 413 + "did:plc:admin123456789012345", 414 + "admin.bsky.social", 415 + vec!["atproto", "transition:generic"], 416 + ); 417 + assert!(rules.validate(&admin_with_scope)); 418 + 419 + // VIP with required scope - should pass via Branch 1 420 + let vip_with_scope = test_session( 421 + "did:plc:somevip1234567890123", 422 + "alice.vip.social", 423 + vec!["atproto", "transition:generic"], 424 + ); 425 + assert!(rules.validate(&vip_with_scope)); 426 + 427 + // Moderator bypass - should pass via Branch 2 428 + let moderator = test_session( 429 + "did:plc:moderator12345678901", 430 + "mod.bsky.social", 431 + vec!["atproto"], 432 + ); 433 + assert!(rules.validate(&moderator)); 434 + 435 + // Admin without scope and not team member - should fail 436 + let admin_no_scope = test_session( 437 + "did:plc:admin123456789012345", 438 + "admin.bsky.social", 439 + vec!["atproto"], 440 + ); 441 + assert!(!rules.validate(&admin_no_scope)); 442 + 443 + // Random user - should fail 444 + let random = test_session( 445 + "did:plc:random12345678901234", 446 + "random.bsky.social", 447 + vec!["atproto", "transition:generic"], 448 + ); 449 + assert!(!rules.validate(&random)); 450 + } 451 + 452 + // ======================================================================== 453 + // ATProto Scope Specification Tests 454 + // ======================================================================== 455 + 456 + #[test] 457 + fn test_scope_with_query_params() { 458 + // ATProto scopes can have query parameters 459 + // These are treated as literal strings (exact matching) 460 + 461 + // blob scope with accept parameter 462 + let blob_rules = AuthRules::ScopeEquals("blob?accept=image/*".into()); 463 + let has_blob = test_session( 464 + "did:plc:test12345678901234567", 465 + "test.bsky.social", 466 + vec!["atproto", "blob?accept=image/*"], 467 + ); 468 + assert!(blob_rules.validate(&has_blob)); 469 + 470 + // Different query param should not match 471 + let wrong_blob = test_session( 472 + "did:plc:test12345678901234567", 473 + "test.bsky.social", 474 + vec!["atproto", "blob?accept=video/*"], 475 + ); 476 + assert!(!blob_rules.validate(&wrong_blob)); 477 + 478 + // account:repo with action parameter 479 + let account_rules = AuthRules::ScopeEquals("account:repo?action=manage".into()); 480 + let has_account = test_session( 481 + "did:plc:test12345678901234567", 482 + "test.bsky.social", 483 + vec!["atproto", "account:repo?action=manage"], 484 + ); 485 + assert!(account_rules.validate(&has_account)); 486 + 487 + // Multiple query params 488 + let multi_param_rules = AuthRules::ScopeEquals("blob?accept=image/*&accept=video/*".into()); 489 + let has_multi = test_session( 490 + "did:plc:test12345678901234567", 491 + "test.bsky.social", 492 + vec!["atproto", "blob?accept=image/*&accept=video/*"], 493 + ); 494 + assert!(multi_param_rules.validate(&has_multi)); 495 + } 496 + 497 + #[test] 498 + fn test_scope_exact_matching_no_wildcards() { 499 + // ATProto spec: Wildcards do NOT work for collections 500 + // repo:app.bsky.feed.post should NOT match repo:app.bsky.feed.* 501 + // This test verifies our exact matching is correct 502 + 503 + let rules = AuthRules::ScopeEquals("repo:app.bsky.feed.post".into()); 504 + 505 + // Exact match works 506 + let exact = test_session( 507 + "did:plc:test12345678901234567", 508 + "test.bsky.social", 509 + vec!["atproto", "repo:app.bsky.feed.post"], 510 + ); 511 + assert!(rules.validate(&exact)); 512 + 513 + // Wildcard scope in JWT should NOT satisfy specific requirement 514 + // (user has repo:app.bsky.feed.* but endpoint requires repo:app.bsky.feed.post) 515 + let wildcard = test_session( 516 + "did:plc:test12345678901234567", 517 + "test.bsky.social", 518 + vec!["atproto", "repo:app.bsky.feed.*"], 519 + ); 520 + assert!(!rules.validate(&wildcard)); 521 + 522 + // Different collection should not match 523 + let different = test_session( 524 + "did:plc:test12345678901234567", 525 + "test.bsky.social", 526 + vec!["atproto", "repo:app.bsky.feed.like"], 527 + ); 528 + assert!(!rules.validate(&different)); 529 + 530 + // Prefix should not match 531 + let prefix = test_session( 532 + "did:plc:test12345678901234567", 533 + "test.bsky.social", 534 + vec!["atproto", "repo:app.bsky.feed"], 535 + ); 536 + assert!(!rules.validate(&prefix)); 537 + 538 + // repo:* should not match specific collection (exact matching) 539 + let full_wildcard = test_session( 540 + "did:plc:test12345678901234567", 541 + "test.bsky.social", 542 + vec!["atproto", "repo:*"], 543 + ); 544 + assert!(!rules.validate(&full_wildcard)); 545 + } 546 + 547 + #[test] 548 + fn test_scope_case_sensitivity() { 549 + // OAuth scopes are case-sensitive per RFC 6749 550 + 551 + let rules = AuthRules::ScopeEquals("atproto".into()); 552 + 553 + // Exact case matches 554 + let exact = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["atproto"]); 555 + assert!(rules.validate(&exact)); 556 + 557 + // UPPERCASE should NOT match 558 + let upper = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["ATPROTO"]); 559 + assert!(!rules.validate(&upper)); 560 + 561 + // Mixed case should NOT match 562 + let mixed = test_session("did:plc:test12345678901234567", "test.bsky.social", vec!["AtProto"]); 563 + assert!(!rules.validate(&mixed)); 564 + 565 + // Test with namespaced scope 566 + let ns_rules = AuthRules::ScopeEquals("transition:generic".into()); 567 + let ns_upper = test_session( 568 + "did:plc:test12345678901234567", 569 + "test.bsky.social", 570 + vec!["TRANSITION:GENERIC"], 571 + ); 572 + assert!(!ns_rules.validate(&ns_upper)); 573 + } 574 + 575 + #[test] 576 + fn test_scope_with_wildcards_exact_match() { 577 + // Wildcard scopes like blob:*/* and identity:* are stored as literal strings 578 + // The middleware does exact matching, so JWT must contain the exact string 579 + 580 + // blob:*/* - full blob wildcard 581 + let blob_rules = AuthRules::ScopeEquals("blob:*/*".into()); 582 + let has_blob_wildcard = test_session( 583 + "did:plc:test12345678901234567", 584 + "test.bsky.social", 585 + vec!["atproto", "blob:*/*"], 586 + ); 587 + assert!(blob_rules.validate(&has_blob_wildcard)); 588 + 589 + // Specific blob type should NOT match blob:*/* requirement 590 + let has_specific_blob = test_session( 591 + "did:plc:test12345678901234567", 592 + "test.bsky.social", 593 + vec!["atproto", "blob:image/png"], 594 + ); 595 + assert!(!blob_rules.validate(&has_specific_blob)); 596 + 597 + // identity:* - full identity wildcard 598 + let identity_rules = AuthRules::ScopeEquals("identity:*".into()); 599 + let has_identity_wildcard = test_session( 600 + "did:plc:test12345678901234567", 601 + "test.bsky.social", 602 + vec!["atproto", "identity:*"], 603 + ); 604 + assert!(identity_rules.validate(&has_identity_wildcard)); 605 + 606 + // Specific identity scope should NOT match identity:* requirement 607 + let has_specific_identity = test_session( 608 + "did:plc:test12345678901234567", 609 + "test.bsky.social", 610 + vec!["atproto", "identity:handle"], 611 + ); 612 + assert!(!identity_rules.validate(&has_specific_identity)); 613 + } 614 + 615 + // ======================================================================== 616 + // Scope Helper Function Tests 617 + // ======================================================================== 618 + 619 + #[test] 620 + fn test_has_scope_helper() { 621 + let scopes: Vec<String> = vec![ 622 + "atproto".to_string(), 623 + "transition:generic".to_string(), 624 + "repo:app.bsky.feed.post".to_string(), 625 + ]; 626 + 627 + // Present scopes 628 + assert!(has_scope(&scopes, "atproto")); 629 + assert!(has_scope(&scopes, "transition:generic")); 630 + assert!(has_scope(&scopes, "repo:app.bsky.feed.post")); 631 + 632 + // Absent scopes 633 + assert!(!has_scope(&scopes, "identity:*")); 634 + assert!(!has_scope(&scopes, "")); 635 + assert!(!has_scope(&scopes, "ATPROTO")); // Case sensitive 636 + 637 + // Empty scopes list 638 + let empty: Vec<String> = vec![]; 639 + assert!(!has_scope(&empty, "atproto")); 640 + } 641 + 642 + #[test] 643 + fn test_has_any_scope_helper() { 644 + let scopes: Vec<String> = vec![ 645 + "atproto".to_string(), 646 + "repo:app.bsky.feed.post".to_string(), 647 + ]; 648 + 649 + // Has one of the required scopes 650 + let required1 = vec!["transition:generic".to_string(), "atproto".to_string()]; 651 + assert!(has_any_scope(&scopes, &required1)); 652 + 653 + // Has none of the required scopes 654 + let required2 = vec!["transition:generic".to_string(), "identity:*".to_string()]; 655 + assert!(!has_any_scope(&scopes, &required2)); 656 + 657 + // Empty required list - should return false (no scope to match) 658 + let empty_required: Vec<String> = vec![]; 659 + assert!(!has_any_scope(&scopes, &empty_required)); 660 + 661 + // Empty scopes list 662 + let empty_scopes: Vec<String> = vec![]; 663 + assert!(!has_any_scope(&empty_scopes, &required1)); 664 + } 665 + 666 + #[test] 667 + fn test_has_all_scopes_helper() { 668 + let scopes: Vec<String> = vec![ 669 + "atproto".to_string(), 670 + "transition:generic".to_string(), 671 + "repo:app.bsky.feed.post".to_string(), 672 + ]; 673 + 674 + // Has all required scopes 675 + let required1 = vec!["atproto".to_string(), "transition:generic".to_string()]; 676 + assert!(has_all_scopes(&scopes, &required1)); 677 + 678 + // Missing one required scope 679 + let required2 = vec!["atproto".to_string(), "identity:*".to_string()]; 680 + assert!(!has_all_scopes(&scopes, &required2)); 681 + 682 + // Empty required list - should return true (vacuously all zero required are present) 683 + let empty_required: Vec<String> = vec![]; 684 + assert!(has_all_scopes(&scopes, &empty_required)); 685 + 686 + // Empty scopes list with requirements 687 + let empty_scopes: Vec<String> = vec![]; 688 + assert!(!has_all_scopes(&empty_scopes, &required1)); 689 + 690 + // Single scope requirement 691 + let single = vec!["atproto".to_string()]; 692 + assert!(has_all_scopes(&scopes, &single)); 693 + } 694 + }
+3
src/main.rs
··· 37 use tracing::log; 38 use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 39 40 mod gate; 41 pub mod helpers; 42 mod middleware; ··· 153 mailer: AsyncSmtpTransport<Tokio1Executor>, 154 template_engine: Engine<Handlebars<'static>>, 155 resolver: Arc<PublicResolver>, 156 app_config: AppConfig, 157 } 158 ··· 278 mailer, 279 template_engine: Engine::from(hbs), 280 resolver: Arc::new(resolver), 281 app_config: AppConfig::new(), 282 }; 283
··· 37 use tracing::log; 38 use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 39 40 + mod auth; 41 mod gate; 42 pub mod helpers; 43 mod middleware; ··· 154 mailer: AsyncSmtpTransport<Tokio1Executor>, 155 template_engine: Engine<Handlebars<'static>>, 156 resolver: Arc<PublicResolver>, 157 + handle_cache: auth::HandleCache, 158 app_config: AppConfig, 159 } 160 ··· 280 mailer, 281 template_engine: Engine::from(hbs), 282 resolver: Arc::new(resolver), 283 + handle_cache: auth::HandleCache::new(), 284 app_config: AppConfig::new(), 285 }; 286