Microservice to bring 2FA to self hosted PDSes

Compare changes

Choose any two refs to compare.

+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 22 ] 23 23 24 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 25 name = "adler2" 35 26 version = "2.0.1" 36 27 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 50 41 51 42 [[package]] 52 43 name = "aho-corasick" 53 - version = "1.1.3" 44 + version = "1.1.4" 54 45 source = "registry+https://github.com/rust-lang/crates.io-index" 55 - checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 46 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 56 47 dependencies = [ 57 48 "memchr", 58 49 ] ··· 80 71 81 72 [[package]] 82 73 name = "anyhow" 83 - version = "1.0.99" 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" 84 81 source = "registry+https://github.com/rust-lang/crates.io-index" 85 - checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 82 + checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" 83 + dependencies = [ 84 + "object", 85 + ] 86 86 87 87 [[package]] 88 88 name = "async-compression" 89 - version = "0.4.27" 89 + version = "0.4.36" 90 90 source = "registry+https://github.com/rust-lang/crates.io-index" 91 - checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" 91 + checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" 92 92 dependencies = [ 93 - "flate2", 93 + "compression-codecs", 94 + "compression-core", 94 95 "futures-core", 95 - "memchr", 96 96 "pin-project-lite", 97 97 "tokio", 98 - "zstd", 99 - "zstd-safe", 100 98 ] 101 99 102 100 [[package]] ··· 107 105 dependencies = [ 108 106 "proc-macro2", 109 107 "quote", 110 - "syn 2.0.105", 108 + "syn 2.0.112", 111 109 ] 112 110 113 111 [[package]] ··· 120 118 ] 121 119 122 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]] 123 130 name = "atomic-waker" 124 131 version = "1.1.2" 125 132 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 133 140 134 141 [[package]] 135 142 name = "aws-lc-rs" 136 - version = "1.13.3" 143 + version = "1.15.2" 137 144 source = "registry+https://github.com/rust-lang/crates.io-index" 138 - checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" 145 + checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" 139 146 dependencies = [ 140 147 "aws-lc-sys", 141 148 "untrusted 0.7.1", ··· 144 151 145 152 [[package]] 146 153 name = "aws-lc-sys" 147 - version = "0.30.0" 154 + version = "0.35.0" 148 155 source = "registry+https://github.com/rust-lang/crates.io-index" 149 - checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" 156 + checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" 150 157 dependencies = [ 151 - "bindgen", 152 158 "cc", 153 159 "cmake", 154 160 "dunce", ··· 157 163 158 164 [[package]] 159 165 name = "axum" 160 - version = "0.8.4" 166 + version = "0.8.8" 161 167 source = "registry+https://github.com/rust-lang/crates.io-index" 162 - checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" 168 + checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" 163 169 dependencies = [ 164 170 "axum-core", 165 171 "axum-macros", ··· 177 183 "mime", 178 184 "percent-encoding", 179 185 "pin-project-lite", 180 - "rustversion", 181 - "serde", 186 + "serde_core", 182 187 "serde_json", 183 188 "serde_path_to_error", 184 189 "serde_urlencoded", ··· 192 197 193 198 [[package]] 194 199 name = "axum-core" 195 - version = "0.5.2" 200 + version = "0.5.6" 196 201 source = "registry+https://github.com/rust-lang/crates.io-index" 197 - checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" 202 + checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" 198 203 dependencies = [ 199 204 "bytes", 200 205 "futures-core", ··· 203 208 "http-body-util", 204 209 "mime", 205 210 "pin-project-lite", 206 - "rustversion", 207 211 "sync_wrapper", 208 212 "tower-layer", 209 213 "tower-service", ··· 218 222 dependencies = [ 219 223 "proc-macro2", 220 224 "quote", 221 - "syn 2.0.105", 225 + "syn 2.0.112", 222 226 ] 223 227 224 228 [[package]] ··· 230 234 "axum", 231 235 "handlebars", 232 236 "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", 237 + "thiserror 2.0.17", 249 238 ] 250 239 251 240 [[package]] ··· 278 267 279 268 [[package]] 280 269 name = "base64ct" 281 - version = "1.8.0" 270 + version = "1.8.1" 282 271 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 - ] 272 + checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" 307 273 308 274 [[package]] 309 275 name = "bitflags" 310 - version = "2.9.1" 276 + version = "2.10.0" 311 277 source = "registry+https://github.com/rust-lang/crates.io-index" 312 - checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 278 + checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 313 279 dependencies = [ 314 - "serde", 280 + "serde_core", 315 281 ] 316 282 317 283 [[package]] ··· 345 311 "proc-macro2", 346 312 "quote", 347 313 "rustversion", 348 - "syn 2.0.105", 314 + "syn 2.0.112", 349 315 ] 350 316 351 317 [[package]] ··· 359 325 360 326 [[package]] 361 327 name = "bstr" 362 - version = "1.12.0" 328 + version = "1.12.1" 363 329 source = "registry+https://github.com/rust-lang/crates.io-index" 364 - checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" 330 + checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" 365 331 dependencies = [ 366 332 "memchr", 367 333 "serde", ··· 393 359 394 360 [[package]] 395 361 name = "bumpalo" 396 - version = "3.19.0" 362 + version = "3.19.1" 397 363 source = "registry+https://github.com/rust-lang/crates.io-index" 398 - checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 364 + checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 399 365 400 366 [[package]] 401 367 name = "byteorder" ··· 405 371 406 372 [[package]] 407 373 name = "bytes" 408 - version = "1.10.1" 374 + version = "1.11.0" 409 375 source = "registry+https://github.com/rust-lang/crates.io-index" 410 - checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 376 + checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" 411 377 dependencies = [ 412 378 "serde", 413 379 ] ··· 423 389 424 390 [[package]] 425 391 name = "cc" 426 - version = "1.2.32" 392 + version = "1.2.51" 427 393 source = "registry+https://github.com/rust-lang/crates.io-index" 428 - checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" 394 + checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" 429 395 dependencies = [ 396 + "find-msvc-tools", 430 397 "jobserver", 431 398 "libc", 432 399 "shlex", ··· 442 409 ] 443 410 444 411 [[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 412 name = "cfg-if" 455 - version = "1.0.1" 413 + version = "1.0.4" 456 414 source = "registry+https://github.com/rust-lang/crates.io-index" 457 - checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 415 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 458 416 459 417 [[package]] 460 418 name = "cfg_aliases" ··· 473 431 "num-traits", 474 432 "serde", 475 433 "wasm-bindgen", 476 - "windows-link 0.2.1", 434 + "windows-link", 477 435 ] 478 436 479 437 [[package]] ··· 538 496 ] 539 497 540 498 [[package]] 541 - name = "clang-sys" 542 - version = "1.8.1" 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" 543 510 source = "registry+https://github.com/rust-lang/crates.io-index" 544 - checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 511 + checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" 545 512 dependencies = [ 546 - "glob", 547 - "libc", 548 - "libloading", 513 + "thiserror 2.0.17", 549 514 ] 550 515 551 516 [[package]] 552 - name = "cmake" 553 - version = "0.1.54" 517 + name = "compression-codecs" 518 + version = "0.4.35" 554 519 source = "registry+https://github.com/rust-lang/crates.io-index" 555 - checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" 520 + checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" 556 521 dependencies = [ 557 - "cc", 522 + "compression-core", 523 + "flate2", 524 + "memchr", 525 + "zstd", 526 + "zstd-safe", 558 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" 559 534 560 535 [[package]] 561 536 name = "concurrent-queue" ··· 579 554 checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 580 555 581 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]] 582 567 name = "core-foundation" 583 568 version = "0.9.4" 584 569 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 614 599 615 600 [[package]] 616 601 name = "crc" 617 - version = "3.3.0" 602 + version = "3.4.0" 618 603 source = "registry+https://github.com/rust-lang/crates.io-index" 619 - checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" 604 + checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" 620 605 dependencies = [ 621 606 "crc-catalog", 622 607 ] ··· 635 620 dependencies = [ 636 621 "cfg-if", 637 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" 638 629 639 630 [[package]] 640 631 name = "crossbeam-queue" ··· 671 662 672 663 [[package]] 673 664 name = "crypto-common" 674 - version = "0.1.6" 665 + version = "0.1.7" 675 666 source = "registry+https://github.com/rust-lang/crates.io-index" 676 - checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 667 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 677 668 dependencies = [ 678 669 "generic-array", 679 670 "typenum", ··· 710 701 "proc-macro2", 711 702 "quote", 712 703 "strsim", 713 - "syn 2.0.105", 704 + "syn 2.0.112", 714 705 ] 715 706 716 707 [[package]] ··· 724 715 "proc-macro2", 725 716 "quote", 726 717 "strsim", 727 - "syn 2.0.105", 718 + "syn 2.0.112", 728 719 ] 729 720 730 721 [[package]] ··· 735 726 dependencies = [ 736 727 "darling_core 0.20.11", 737 728 "quote", 738 - "syn 2.0.105", 729 + "syn 2.0.112", 739 730 ] 740 731 741 732 [[package]] ··· 746 737 dependencies = [ 747 738 "darling_core 0.21.3", 748 739 "quote", 749 - "syn 2.0.105", 740 + "syn 2.0.112", 750 741 ] 751 742 752 743 [[package]] ··· 786 777 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 787 778 dependencies = [ 788 779 "data-encoding", 789 - "syn 2.0.105", 780 + "syn 2.0.112", 790 781 ] 791 782 792 783 [[package]] ··· 828 819 "darling 0.20.11", 829 820 "proc-macro2", 830 821 "quote", 831 - "syn 2.0.105", 822 + "syn 2.0.112", 832 823 ] 833 824 834 825 [[package]] ··· 838 829 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" 839 830 dependencies = [ 840 831 "derive_builder_core", 841 - "syn 2.0.105", 832 + "syn 2.0.112", 842 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" 843 861 844 862 [[package]] 845 863 name = "digest" ··· 861 879 dependencies = [ 862 880 "proc-macro2", 863 881 "quote", 864 - "syn 2.0.105", 882 + "syn 2.0.112", 865 883 ] 866 884 867 885 [[package]] ··· 942 960 checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" 943 961 944 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]] 945 975 name = "encoding_rs" 946 976 version = "0.8.35" 947 977 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 958 988 959 989 [[package]] 960 990 name = "errno" 961 - version = "0.3.13" 991 + version = "0.3.14" 962 992 source = "registry+https://github.com/rust-lang/crates.io-index" 963 - checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 993 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 964 994 dependencies = [ 965 995 "libc", 966 996 "windows-sys 0.59.0", ··· 1005 1035 ] 1006 1036 1007 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]] 1008 1044 name = "flate2" 1009 1045 version = "1.1.5" 1010 1046 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1022 1058 dependencies = [ 1023 1059 "futures-core", 1024 1060 "futures-sink", 1025 - "spin", 1061 + "spin 0.9.8", 1026 1062 ] 1027 1063 1028 1064 [[package]] ··· 1038 1074 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1039 1075 1040 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]] 1041 1083 name = "foreign-types" 1042 1084 version = "0.3.2" 1043 1085 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1054 1096 1055 1097 [[package]] 1056 1098 name = "form_urlencoded" 1057 - version = "1.2.1" 1099 + version = "1.2.2" 1058 1100 source = "registry+https://github.com/rust-lang/crates.io-index" 1059 - checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1101 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 1060 1102 dependencies = [ 1061 1103 "percent-encoding", 1062 1104 ] ··· 1078 1120 checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 1079 1121 1080 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]] 1081 1136 name = "futures-channel" 1082 1137 version = "0.3.31" 1083 1138 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1122 1177 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1123 1178 1124 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]] 1125 1193 name = "futures-macro" 1126 1194 version = "0.3.31" 1127 1195 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1129 1197 dependencies = [ 1130 1198 "proc-macro2", 1131 1199 "quote", 1132 - "syn 2.0.105", 1200 + "syn 2.0.112", 1133 1201 ] 1134 1202 1135 1203 [[package]] ··· 1168 1236 ] 1169 1237 1170 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]] 1171 1254 name = "generic-array" 1172 1255 version = "0.14.7" 1173 1256 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1206 1289 ] 1207 1290 1208 1291 [[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 1292 name = "globset" 1222 - version = "0.4.16" 1293 + version = "0.4.18" 1223 1294 source = "registry+https://github.com/rust-lang/crates.io-index" 1224 - checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 1295 + checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" 1225 1296 dependencies = [ 1226 1297 "aho-corasick", 1227 1298 "bstr", 1228 1299 "log", 1229 - "regex-automata 0.4.13", 1230 - "regex-syntax 0.8.5", 1300 + "regex-automata", 1301 + "regex-syntax", 1231 1302 ] 1232 1303 1233 1304 [[package]] 1234 1305 name = "governor" 1235 - version = "0.10.1" 1306 + version = "0.10.4" 1236 1307 source = "registry+https://github.com/rust-lang/crates.io-index" 1237 - checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19" 1308 + checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" 1238 1309 dependencies = [ 1239 1310 "cfg-if", 1240 1311 "dashmap", ··· 1242 1313 "futures-timer", 1243 1314 "futures-util", 1244 1315 "getrandom 0.3.4", 1245 - "hashbrown 0.15.5", 1316 + "hashbrown 0.16.1", 1246 1317 "nonzero_ext", 1247 1318 "parking_lot", 1248 1319 "portable-atomic", ··· 1276 1347 "futures-core", 1277 1348 "futures-sink", 1278 1349 "http", 1279 - "indexmap 2.10.0", 1350 + "indexmap 2.12.1", 1280 1351 "slab", 1281 1352 "tokio", 1282 1353 "tokio-util", ··· 1285 1356 1286 1357 [[package]] 1287 1358 name = "half" 1288 - version = "2.6.0" 1359 + version = "2.7.1" 1289 1360 source = "registry+https://github.com/rust-lang/crates.io-index" 1290 - checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 1361 + checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" 1291 1362 dependencies = [ 1292 1363 "cfg-if", 1293 1364 "crunchy", 1365 + "zerocopy", 1294 1366 ] 1295 1367 1296 1368 [[package]] 1297 1369 name = "handlebars" 1298 - version = "6.3.2" 1370 + version = "6.4.0" 1299 1371 source = "registry+https://github.com/rust-lang/crates.io-index" 1300 - checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" 1372 + checksum = "9b3f9296c208515b87bd915a2f5d1163d4b3f863ba83337d7713cf478055948e" 1301 1373 dependencies = [ 1302 1374 "derive_builder", 1303 1375 "log", ··· 1307 1379 "rust-embed", 1308 1380 "serde", 1309 1381 "serde_json", 1310 - "thiserror 2.0.14", 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", 1311 1392 ] 1312 1393 1313 1394 [[package]] ··· 1334 1415 dependencies = [ 1335 1416 "allocator-api2", 1336 1417 "equivalent", 1337 - "foldhash", 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", 1338 1430 ] 1339 1431 1340 1432 [[package]] ··· 1347 1439 ] 1348 1440 1349 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]] 1350 1456 name = "heck" 1351 1457 version = "0.4.1" 1352 1458 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1390 1496 1391 1497 [[package]] 1392 1498 name = "home" 1393 - version = "0.5.11" 1499 + version = "0.5.12" 1394 1500 source = "registry+https://github.com/rust-lang/crates.io-index" 1395 - checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 1501 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 1396 1502 dependencies = [ 1397 - "windows-sys 0.59.0", 1503 + "windows-sys 0.61.2", 1398 1504 ] 1399 1505 1400 1506 [[package]] ··· 1408 1514 1409 1515 [[package]] 1410 1516 name = "http" 1411 - version = "1.3.1" 1517 + version = "1.4.0" 1412 1518 source = "registry+https://github.com/rust-lang/crates.io-index" 1413 - checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 1519 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 1414 1520 dependencies = [ 1415 1521 "bytes", 1416 - "fnv", 1417 1522 "itoa", 1418 1523 ] 1419 1524 ··· 1454 1559 1455 1560 [[package]] 1456 1561 name = "hyper" 1457 - version = "1.6.0" 1562 + version = "1.8.1" 1458 1563 source = "registry+https://github.com/rust-lang/crates.io-index" 1459 - checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 1564 + checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" 1460 1565 dependencies = [ 1566 + "atomic-waker", 1461 1567 "bytes", 1462 1568 "futures-channel", 1463 - "futures-util", 1569 + "futures-core", 1464 1570 "h2", 1465 1571 "http", 1466 1572 "http-body", ··· 1468 1574 "httpdate", 1469 1575 "itoa", 1470 1576 "pin-project-lite", 1577 + "pin-utils", 1471 1578 "smallvec", 1472 1579 "tokio", 1473 1580 "want", ··· 1487 1594 "tokio", 1488 1595 "tokio-rustls", 1489 1596 "tower-service", 1490 - "webpki-roots 1.0.2", 1597 + "webpki-roots 1.0.5", 1491 1598 ] 1492 1599 1493 1600 [[package]] ··· 1505 1612 1506 1613 [[package]] 1507 1614 name = "hyper-util" 1508 - version = "0.1.16" 1615 + version = "0.1.19" 1509 1616 source = "registry+https://github.com/rust-lang/crates.io-index" 1510 - checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 1617 + checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" 1511 1618 dependencies = [ 1512 1619 "base64", 1513 1620 "bytes", ··· 1531 1638 1532 1639 [[package]] 1533 1640 name = "iana-time-zone" 1534 - version = "0.1.63" 1641 + version = "0.1.64" 1535 1642 source = "registry+https://github.com/rust-lang/crates.io-index" 1536 - checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 1643 + checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" 1537 1644 dependencies = [ 1538 1645 "android_system_properties", 1539 1646 "core-foundation-sys", ··· 1555 1662 1556 1663 [[package]] 1557 1664 name = "icu_collections" 1558 - version = "2.0.0" 1665 + version = "2.1.1" 1559 1666 source = "registry+https://github.com/rust-lang/crates.io-index" 1560 - checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 1667 + checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" 1561 1668 dependencies = [ 1562 1669 "displaydoc", 1563 1670 "potential_utf", ··· 1568 1675 1569 1676 [[package]] 1570 1677 name = "icu_locale_core" 1571 - version = "2.0.0" 1678 + version = "2.1.1" 1572 1679 source = "registry+https://github.com/rust-lang/crates.io-index" 1573 - checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 1680 + checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" 1574 1681 dependencies = [ 1575 1682 "displaydoc", 1576 1683 "litemap", ··· 1581 1688 1582 1689 [[package]] 1583 1690 name = "icu_normalizer" 1584 - version = "2.0.0" 1691 + version = "2.1.1" 1585 1692 source = "registry+https://github.com/rust-lang/crates.io-index" 1586 - checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 1693 + checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" 1587 1694 dependencies = [ 1588 - "displaydoc", 1589 1695 "icu_collections", 1590 1696 "icu_normalizer_data", 1591 1697 "icu_properties", ··· 1596 1702 1597 1703 [[package]] 1598 1704 name = "icu_normalizer_data" 1599 - version = "2.0.0" 1705 + version = "2.1.1" 1600 1706 source = "registry+https://github.com/rust-lang/crates.io-index" 1601 - checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 1707 + checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" 1602 1708 1603 1709 [[package]] 1604 1710 name = "icu_properties" 1605 - version = "2.0.1" 1711 + version = "2.1.2" 1606 1712 source = "registry+https://github.com/rust-lang/crates.io-index" 1607 - checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 1713 + checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" 1608 1714 dependencies = [ 1609 - "displaydoc", 1610 1715 "icu_collections", 1611 1716 "icu_locale_core", 1612 1717 "icu_properties_data", 1613 1718 "icu_provider", 1614 - "potential_utf", 1615 1719 "zerotrie", 1616 1720 "zerovec", 1617 1721 ] 1618 1722 1619 1723 [[package]] 1620 1724 name = "icu_properties_data" 1621 - version = "2.0.1" 1725 + version = "2.1.2" 1622 1726 source = "registry+https://github.com/rust-lang/crates.io-index" 1623 - checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 1727 + checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" 1624 1728 1625 1729 [[package]] 1626 1730 name = "icu_provider" 1627 - version = "2.0.0" 1731 + version = "2.1.1" 1628 1732 source = "registry+https://github.com/rust-lang/crates.io-index" 1629 - checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 1733 + checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" 1630 1734 dependencies = [ 1631 1735 "displaydoc", 1632 1736 "icu_locale_core", 1633 - "stable_deref_trait", 1634 - "tinystr", 1635 1737 "writeable", 1636 1738 "yoke", 1637 1739 "zerofrom", ··· 1647 1749 1648 1750 [[package]] 1649 1751 name = "idna" 1650 - version = "1.0.3" 1752 + version = "1.1.0" 1651 1753 source = "registry+https://github.com/rust-lang/crates.io-index" 1652 - checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 1754 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 1653 1755 dependencies = [ 1654 1756 "idna_adapter", 1655 1757 "smallvec", ··· 1679 1781 1680 1782 [[package]] 1681 1783 name = "indexmap" 1682 - version = "2.10.0" 1784 + version = "2.12.1" 1683 1785 source = "registry+https://github.com/rust-lang/crates.io-index" 1684 - checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 1786 + checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 1685 1787 dependencies = [ 1686 1788 "equivalent", 1687 - "hashbrown 0.15.5", 1789 + "hashbrown 0.16.1", 1688 1790 "serde", 1791 + "serde_core", 1689 1792 ] 1690 1793 1691 1794 [[package]] ··· 1716 1819 ] 1717 1820 1718 1821 [[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 1822 name = "ipld-core" 1731 1823 version = "0.4.2" 1732 1824 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1745 1837 1746 1838 [[package]] 1747 1839 name = "iri-string" 1748 - version = "0.7.9" 1840 + version = "0.7.10" 1749 1841 source = "registry+https://github.com/rust-lang/crates.io-index" 1750 - checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" 1842 + checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" 1751 1843 dependencies = [ 1752 1844 "memchr", 1753 1845 "serde", 1754 1846 ] 1755 1847 1756 1848 [[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 1849 name = "itoa" 1767 - version = "1.0.15" 1850 + version = "1.0.17" 1768 1851 source = "registry+https://github.com/rust-lang/crates.io-index" 1769 - checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1852 + checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 1770 1853 1771 1854 [[package]] 1772 1855 name = "jacquard-api" 1773 - version = "0.9.2" 1856 + version = "0.9.5" 1774 1857 source = "registry+https://github.com/rust-lang/crates.io-index" 1775 - checksum = "bbbfd6e2b10fa1731f4d4e40c8f791956b0d4f804fb3efef891afec903f20597" 1858 + checksum = "4979fb1848c1dd7ac8fd12745bc71f56f6da61374407d5f9b06005467a954e5a" 1776 1859 dependencies = [ 1777 1860 "bon", 1778 1861 "bytes", ··· 1782 1865 "miette", 1783 1866 "rustversion", 1784 1867 "serde", 1868 + "serde_bytes", 1785 1869 "serde_ipld_dagcbor", 1786 - "thiserror 2.0.14", 1870 + "thiserror 2.0.17", 1787 1871 "unicode-segmentation", 1788 1872 ] 1789 1873 1790 1874 [[package]] 1791 1875 name = "jacquard-common" 1792 - version = "0.9.2" 1876 + version = "0.9.5" 1793 1877 source = "registry+https://github.com/rust-lang/crates.io-index" 1794 - checksum = "df86cb117d9f1c2b0251ba67c3f0e3f963fd22abc6cf8de0e02a7fc846c288ca" 1878 + checksum = "1751921e0bdae5e0077afade6161545e9ef7698306c868f800916e99ecbcaae9" 1795 1879 dependencies = [ 1796 1880 "base64", 1797 1881 "bon", ··· 1809 1893 "multihash", 1810 1894 "ouroboros", 1811 1895 "p256", 1896 + "postcard", 1812 1897 "rand 0.9.2", 1813 1898 "regex", 1814 1899 "regex-lite", 1815 1900 "reqwest", 1816 1901 "serde", 1902 + "serde_bytes", 1817 1903 "serde_html_form", 1818 1904 "serde_ipld_dagcbor", 1819 1905 "serde_json", 1820 1906 "signature", 1821 1907 "smol_str", 1822 - "thiserror 2.0.14", 1908 + "thiserror 2.0.17", 1823 1909 "tokio", 1824 1910 "tokio-util", 1825 1911 "trait-variant", ··· 1828 1914 1829 1915 [[package]] 1830 1916 name = "jacquard-derive" 1831 - version = "0.9.2" 1917 + version = "0.9.5" 1832 1918 source = "registry+https://github.com/rust-lang/crates.io-index" 1833 - checksum = "42ca61a69dc7aa8fb2d7163416514ff7df5d79f2e8b22e269f4610afa85572fe" 1919 + checksum = "9c8d73dfee07943fdab93569ed1c28b06c6921ed891c08b415c4a323ff67e593" 1834 1920 dependencies = [ 1835 1921 "heck 0.5.0", 1836 1922 "jacquard-lexicon", 1837 1923 "proc-macro2", 1838 1924 "quote", 1839 - "syn 2.0.105", 1925 + "syn 2.0.112", 1840 1926 ] 1841 1927 1842 1928 [[package]] 1843 1929 name = "jacquard-identity" 1844 - version = "0.9.2" 1930 + version = "0.9.5" 1845 1931 source = "registry+https://github.com/rust-lang/crates.io-index" 1846 - checksum = "1ef714cacebfca486558a9f8e205daf466bfba0466c4d0c450fd6d0252400a53" 1932 + checksum = "e7aaefa819fa4213cf59f180dba932f018a7cd0599582fd38474ee2a38c16cf2" 1847 1933 dependencies = [ 1848 1934 "bon", 1849 1935 "bytes", ··· 1852 1938 "jacquard-common", 1853 1939 "jacquard-lexicon", 1854 1940 "miette", 1941 + "n0-future", 1855 1942 "percent-encoding", 1856 1943 "reqwest", 1857 1944 "serde", 1858 1945 "serde_html_form", 1859 1946 "serde_json", 1860 - "thiserror 2.0.14", 1947 + "thiserror 2.0.17", 1861 1948 "tokio", 1862 1949 "trait-variant", 1863 1950 "url", ··· 1866 1953 1867 1954 [[package]] 1868 1955 name = "jacquard-lexicon" 1869 - version = "0.9.2" 1956 + version = "0.9.5" 1870 1957 source = "registry+https://github.com/rust-lang/crates.io-index" 1871 - checksum = "de87f2c938faea1b1f1b32d5b9e0c870e7b5bb5efbf96e3692ae2d8f6b2beb7a" 1958 + checksum = "8411aff546569b0a1e0ef669bed2380cec1c00d48f02f3fcd57a71545321b3d8" 1872 1959 dependencies = [ 1873 1960 "cid", 1874 1961 "dashmap", ··· 1886 1973 "serde_repr", 1887 1974 "serde_with", 1888 1975 "sha2", 1889 - "syn 2.0.105", 1890 - "thiserror 2.0.14", 1976 + "syn 2.0.112", 1977 + "thiserror 2.0.17", 1891 1978 "unicode-segmentation", 1892 1979 ] 1893 1980 1894 1981 [[package]] 1895 1982 name = "jobserver" 1896 - version = "0.1.33" 1983 + version = "0.1.34" 1897 1984 source = "registry+https://github.com/rust-lang/crates.io-index" 1898 - checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 1985 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 1899 1986 dependencies = [ 1900 1987 "getrandom 0.3.4", 1901 1988 "libc", ··· 1914 2001 "regex", 1915 2002 "serde", 1916 2003 "serde_json", 1917 - "thiserror 2.0.14", 2004 + "thiserror 2.0.17", 1918 2005 "time", 1919 2006 ] 1920 2007 1921 2008 [[package]] 1922 2009 name = "js-sys" 1923 - version = "0.3.77" 2010 + version = "0.3.83" 1924 2011 source = "registry+https://github.com/rust-lang/crates.io-index" 1925 - checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 2012 + checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 1926 2013 dependencies = [ 1927 2014 "once_cell", 1928 2015 "wasm-bindgen", ··· 1979 2066 source = "registry+https://github.com/rust-lang/crates.io-index" 1980 2067 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1981 2068 dependencies = [ 1982 - "spin", 2069 + "spin 0.9.8", 1983 2070 ] 1984 2071 1985 2072 [[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 2073 name = "lettre" 1993 - version = "0.11.18" 2074 + version = "0.11.19" 1994 2075 source = "registry+https://github.com/rust-lang/crates.io-index" 1995 - checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56" 2076 + checksum = "9e13e10e8818f8b2a60f52cb127041d388b89f3a96a62be9ceaffa22262fef7f" 1996 2077 dependencies = [ 1997 2078 "async-trait", 1998 2079 "base64", ··· 2013 2094 "tokio", 2014 2095 "tokio-rustls", 2015 2096 "url", 2016 - "webpki-roots 1.0.2", 2097 + "webpki-roots 1.0.5", 2017 2098 ] 2018 2099 2019 2100 [[package]] 2020 2101 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" 2102 + version = "0.2.178" 2028 2103 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 - ] 2104 + checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 2034 2105 2035 2106 [[package]] 2036 2107 name = "libm" ··· 2040 2111 2041 2112 [[package]] 2042 2113 name = "libredox" 2043 - version = "0.1.9" 2114 + version = "0.1.12" 2044 2115 source = "registry+https://github.com/rust-lang/crates.io-index" 2045 - checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" 2116 + checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" 2046 2117 dependencies = [ 2047 2118 "bitflags", 2048 2119 "libc", 2049 - "redox_syscall", 2120 + "redox_syscall 0.7.0", 2050 2121 ] 2051 2122 2052 2123 [[package]] ··· 2059 2130 "pkg-config", 2060 2131 "vcpkg", 2061 2132 ] 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 2133 2069 2134 [[package]] 2070 2135 name = "litemap" 2071 - version = "0.8.0" 2136 + version = "0.8.1" 2072 2137 source = "registry+https://github.com/rust-lang/crates.io-index" 2073 - checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 2138 + checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" 2074 2139 2075 2140 [[package]] 2076 2141 name = "lock_api" 2077 - version = "0.4.13" 2142 + version = "0.4.14" 2078 2143 source = "registry+https://github.com/rust-lang/crates.io-index" 2079 - checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 2144 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 2080 2145 dependencies = [ 2081 - "autocfg", 2082 2146 "scopeguard", 2083 2147 ] 2084 2148 2085 2149 [[package]] 2086 2150 name = "log" 2087 - version = "0.4.27" 2151 + version = "0.4.29" 2088 2152 source = "registry+https://github.com/rust-lang/crates.io-index" 2089 - checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 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 + ] 2090 2167 2091 2168 [[package]] 2092 2169 name = "lru-slab" ··· 2107 2184 2108 2185 [[package]] 2109 2186 name = "matchers" 2110 - version = "0.1.0" 2187 + version = "0.2.0" 2111 2188 source = "registry+https://github.com/rust-lang/crates.io-index" 2112 - checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 2189 + checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 2113 2190 dependencies = [ 2114 - "regex-automata 0.1.10", 2191 + "regex-automata", 2115 2192 ] 2116 2193 2117 2194 [[package]] ··· 2132 2209 2133 2210 [[package]] 2134 2211 name = "memchr" 2135 - version = "2.7.5" 2212 + version = "2.7.6" 2136 2213 source = "registry+https://github.com/rust-lang/crates.io-index" 2137 - checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 2214 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 2138 2215 2139 2216 [[package]] 2140 2217 name = "miette" ··· 2155 2232 dependencies = [ 2156 2233 "proc-macro2", 2157 2234 "quote", 2158 - "syn 2.0.105", 2235 + "syn 2.0.112", 2159 2236 ] 2160 2237 2161 2238 [[package]] ··· 2182 2259 2183 2260 [[package]] 2184 2261 name = "mio" 2185 - version = "1.0.4" 2262 + version = "1.1.1" 2186 2263 source = "registry+https://github.com/rust-lang/crates.io-index" 2187 - checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 2264 + checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" 2188 2265 dependencies = [ 2189 2266 "libc", 2190 2267 "wasi", 2191 - "windows-sys 0.59.0", 2268 + "windows-sys 0.61.2", 2192 2269 ] 2193 2270 2194 2271 [[package]] ··· 2215 2292 ] 2216 2293 2217 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]] 2218 2316 name = "nom" 2219 2317 version = "7.1.3" 2220 2318 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2247 2345 2248 2346 [[package]] 2249 2347 name = "nu-ansi-term" 2250 - version = "0.46.0" 2348 + version = "0.50.3" 2251 2349 source = "registry+https://github.com/rust-lang/crates.io-index" 2252 - checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 2350 + checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 2253 2351 dependencies = [ 2254 - "overload", 2255 - "winapi", 2352 + "windows-sys 0.59.0", 2256 2353 ] 2257 2354 2258 2355 [[package]] 2259 2356 name = "num-bigint-dig" 2260 - version = "0.8.4" 2357 + version = "0.8.6" 2261 2358 source = "registry+https://github.com/rust-lang/crates.io-index" 2262 - checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" 2359 + checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" 2263 2360 dependencies = [ 2264 - "byteorder", 2265 2361 "lazy_static", 2266 2362 "libm", 2267 2363 "num-integer", ··· 2325 2421 2326 2422 [[package]] 2327 2423 name = "object" 2328 - version = "0.36.7" 2424 + version = "0.32.2" 2329 2425 source = "registry+https://github.com/rust-lang/crates.io-index" 2330 - checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 2426 + checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 2331 2427 dependencies = [ 2332 2428 "memchr", 2333 2429 ] ··· 2361 2457 dependencies = [ 2362 2458 "proc-macro2", 2363 2459 "quote", 2364 - "syn 2.0.105", 2460 + "syn 2.0.112", 2365 2461 ] 2366 2462 2367 2463 [[package]] ··· 2397 2493 "proc-macro2", 2398 2494 "proc-macro2-diagnostics", 2399 2495 "quote", 2400 - "syn 2.0.105", 2496 + "syn 2.0.112", 2401 2497 ] 2402 2498 2403 2499 [[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 2500 name = "p256" 2411 2501 version = "0.13.2" 2412 2502 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2426 2516 2427 2517 [[package]] 2428 2518 name = "parking_lot" 2429 - version = "0.12.4" 2519 + version = "0.12.5" 2430 2520 source = "registry+https://github.com/rust-lang/crates.io-index" 2431 - checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 2521 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 2432 2522 dependencies = [ 2433 2523 "lock_api", 2434 2524 "parking_lot_core", ··· 2436 2526 2437 2527 [[package]] 2438 2528 name = "parking_lot_core" 2439 - version = "0.9.11" 2529 + version = "0.9.12" 2440 2530 source = "registry+https://github.com/rust-lang/crates.io-index" 2441 - checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 2531 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 2442 2532 dependencies = [ 2443 2533 "cfg-if", 2444 2534 "libc", 2445 - "redox_syscall", 2535 + "redox_syscall 0.5.18", 2446 2536 "smallvec", 2447 - "windows-targets 0.52.6", 2537 + "windows-link", 2448 2538 ] 2449 2539 2450 2540 [[package]] ··· 2477 2567 "axum", 2478 2568 "axum-template", 2479 2569 "chrono", 2570 + "dashmap", 2480 2571 "dotenvy", 2481 2572 "handlebars", 2482 2573 "hex", ··· 2498 2589 "sha2", 2499 2590 "sqlx", 2500 2591 "tokio", 2592 + "tower", 2501 2593 "tower-http", 2502 2594 "tower_governor", 2503 2595 "tracing", ··· 2516 2608 2517 2609 [[package]] 2518 2610 name = "percent-encoding" 2519 - version = "2.3.1" 2611 + version = "2.3.2" 2520 2612 source = "registry+https://github.com/rust-lang/crates.io-index" 2521 - checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 2613 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2522 2614 2523 2615 [[package]] 2524 2616 name = "pest" 2525 - version = "2.8.1" 2617 + version = "2.8.4" 2526 2618 source = "registry+https://github.com/rust-lang/crates.io-index" 2527 - checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" 2619 + checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" 2528 2620 dependencies = [ 2529 2621 "memchr", 2530 - "thiserror 2.0.14", 2531 2622 "ucd-trie", 2532 2623 ] 2533 2624 2534 2625 [[package]] 2535 2626 name = "pest_derive" 2536 - version = "2.8.1" 2627 + version = "2.8.4" 2537 2628 source = "registry+https://github.com/rust-lang/crates.io-index" 2538 - checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" 2629 + checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" 2539 2630 dependencies = [ 2540 2631 "pest", 2541 2632 "pest_generator", ··· 2543 2634 2544 2635 [[package]] 2545 2636 name = "pest_generator" 2546 - version = "2.8.1" 2637 + version = "2.8.4" 2547 2638 source = "registry+https://github.com/rust-lang/crates.io-index" 2548 - checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" 2639 + checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" 2549 2640 dependencies = [ 2550 2641 "pest", 2551 2642 "pest_meta", 2552 2643 "proc-macro2", 2553 2644 "quote", 2554 - "syn 2.0.105", 2645 + "syn 2.0.112", 2555 2646 ] 2556 2647 2557 2648 [[package]] 2558 2649 name = "pest_meta" 2559 - version = "2.8.1" 2650 + version = "2.8.4" 2560 2651 source = "registry+https://github.com/rust-lang/crates.io-index" 2561 - checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" 2652 + checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" 2562 2653 dependencies = [ 2563 2654 "pest", 2564 2655 "sha2", ··· 2581 2672 dependencies = [ 2582 2673 "proc-macro2", 2583 2674 "quote", 2584 - "syn 2.0.105", 2675 + "syn 2.0.112", 2585 2676 ] 2586 2677 2587 2678 [[package]] ··· 2625 2716 2626 2717 [[package]] 2627 2718 name = "portable-atomic" 2628 - version = "1.11.1" 2719 + version = "1.13.0" 2629 2720 source = "registry+https://github.com/rust-lang/crates.io-index" 2630 - checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 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 + ] 2631 2735 2632 2736 [[package]] 2633 2737 name = "potential_utf" 2634 - version = "0.1.2" 2738 + version = "0.1.4" 2635 2739 source = "registry+https://github.com/rust-lang/crates.io-index" 2636 - checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 2740 + checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" 2637 2741 dependencies = [ 2638 2742 "zerovec", 2639 2743 ] ··· 2655 2759 2656 2760 [[package]] 2657 2761 name = "prettyplease" 2658 - version = "0.2.35" 2762 + version = "0.2.37" 2659 2763 source = "registry+https://github.com/rust-lang/crates.io-index" 2660 - checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" 2764 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2661 2765 dependencies = [ 2662 2766 "proc-macro2", 2663 - "syn 2.0.105", 2767 + "syn 2.0.112", 2664 2768 ] 2665 2769 2666 2770 [[package]] ··· 2698 2802 2699 2803 [[package]] 2700 2804 name = "proc-macro2" 2701 - version = "1.0.97" 2805 + version = "1.0.104" 2702 2806 source = "registry+https://github.com/rust-lang/crates.io-index" 2703 - checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" 2807 + checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" 2704 2808 dependencies = [ 2705 2809 "unicode-ident", 2706 2810 ] ··· 2713 2817 dependencies = [ 2714 2818 "proc-macro2", 2715 2819 "quote", 2716 - "syn 2.0.105", 2820 + "syn 2.0.112", 2717 2821 "version_check", 2718 2822 "yansi", 2719 2823 ] 2720 2824 2721 2825 [[package]] 2722 2826 name = "psm" 2723 - version = "0.1.26" 2827 + version = "0.1.28" 2724 2828 source = "registry+https://github.com/rust-lang/crates.io-index" 2725 - checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" 2829 + checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" 2726 2830 dependencies = [ 2831 + "ar_archive_writer", 2727 2832 "cc", 2728 2833 ] 2729 2834 ··· 2753 2858 "pin-project-lite", 2754 2859 "quinn-proto", 2755 2860 "quinn-udp", 2756 - "rustc-hash 2.1.1", 2861 + "rustc-hash", 2757 2862 "rustls", 2758 2863 "socket2", 2759 - "thiserror 2.0.14", 2864 + "thiserror 2.0.17", 2760 2865 "tokio", 2761 2866 "tracing", 2762 2867 "web-time", ··· 2773 2878 "lru-slab", 2774 2879 "rand 0.9.2", 2775 2880 "ring", 2776 - "rustc-hash 2.1.1", 2881 + "rustc-hash", 2777 2882 "rustls", 2778 2883 "rustls-pki-types", 2779 2884 "slab", 2780 - "thiserror 2.0.14", 2885 + "thiserror 2.0.17", 2781 2886 "tinyvec", 2782 2887 "tracing", 2783 2888 "web-time", ··· 2799 2904 2800 2905 [[package]] 2801 2906 name = "quote" 2802 - version = "1.0.40" 2907 + version = "1.0.42" 2803 2908 source = "registry+https://github.com/rust-lang/crates.io-index" 2804 - checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 2909 + checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 2805 2910 dependencies = [ 2806 2911 "proc-macro2", 2807 2912 ] ··· 2885 2990 2886 2991 [[package]] 2887 2992 name = "raw-cpuid" 2888 - version = "11.5.0" 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" 2889 3003 source = "registry+https://github.com/rust-lang/crates.io-index" 2890 - checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" 3004 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 2891 3005 dependencies = [ 2892 3006 "bitflags", 2893 3007 ] 2894 3008 2895 3009 [[package]] 2896 3010 name = "redox_syscall" 2897 - version = "0.5.17" 3011 + version = "0.7.0" 2898 3012 source = "registry+https://github.com/rust-lang/crates.io-index" 2899 - checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" 3013 + checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" 2900 3014 dependencies = [ 2901 3015 "bitflags", 2902 3016 ] ··· 2918 3032 dependencies = [ 2919 3033 "proc-macro2", 2920 3034 "quote", 2921 - "syn 2.0.105", 3035 + "syn 2.0.112", 2922 3036 ] 2923 3037 2924 3038 [[package]] ··· 2929 3043 dependencies = [ 2930 3044 "aho-corasick", 2931 3045 "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", 3046 + "regex-automata", 3047 + "regex-syntax", 2943 3048 ] 2944 3049 2945 3050 [[package]] ··· 2950 3055 dependencies = [ 2951 3056 "aho-corasick", 2952 3057 "memchr", 2953 - "regex-syntax 0.8.5", 3058 + "regex-syntax", 2954 3059 ] 2955 3060 2956 3061 [[package]] ··· 2961 3066 2962 3067 [[package]] 2963 3068 name = "regex-syntax" 2964 - version = "0.6.29" 3069 + version = "0.8.8" 2965 3070 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" 3071 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 2973 3072 2974 3073 [[package]] 2975 3074 name = "reqwest" 2976 - version = "0.12.24" 3075 + version = "0.12.28" 2977 3076 source = "registry+https://github.com/rust-lang/crates.io-index" 2978 - checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" 3077 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 2979 3078 dependencies = [ 2980 - "async-compression", 2981 3079 "base64", 2982 3080 "bytes", 2983 3081 "encoding_rs", 2984 3082 "futures-core", 2985 - "futures-util", 2986 3083 "h2", 2987 3084 "http", 2988 3085 "http-body", ··· 3004 3101 "sync_wrapper", 3005 3102 "tokio", 3006 3103 "tokio-rustls", 3007 - "tokio-util", 3008 3104 "tower", 3009 3105 "tower-http", 3010 3106 "tower-service", 3011 3107 "url", 3012 3108 "wasm-bindgen", 3013 3109 "wasm-bindgen-futures", 3014 - "wasm-streams", 3015 3110 "web-sys", 3016 - "webpki-roots 1.0.2", 3111 + "webpki-roots 1.0.5", 3017 3112 ] 3018 3113 3019 3114 [[package]] ··· 3042 3137 3043 3138 [[package]] 3044 3139 name = "rsa" 3045 - version = "0.9.8" 3140 + version = "0.9.9" 3046 3141 source = "registry+https://github.com/rust-lang/crates.io-index" 3047 - checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" 3142 + checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" 3048 3143 dependencies = [ 3049 3144 "const-oid", 3050 3145 "digest", ··· 3062 3157 3063 3158 [[package]] 3064 3159 name = "rust-embed" 3065 - version = "8.7.2" 3160 + version = "8.9.0" 3066 3161 source = "registry+https://github.com/rust-lang/crates.io-index" 3067 - checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" 3162 + checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" 3068 3163 dependencies = [ 3069 3164 "rust-embed-impl", 3070 3165 "rust-embed-utils", ··· 3073 3168 3074 3169 [[package]] 3075 3170 name = "rust-embed-impl" 3076 - version = "8.7.2" 3171 + version = "8.9.0" 3077 3172 source = "registry+https://github.com/rust-lang/crates.io-index" 3078 - checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" 3173 + checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" 3079 3174 dependencies = [ 3080 3175 "proc-macro2", 3081 3176 "quote", 3082 3177 "rust-embed-utils", 3083 - "syn 2.0.105", 3178 + "syn 2.0.112", 3084 3179 "walkdir", 3085 3180 ] 3086 3181 3087 3182 [[package]] 3088 3183 name = "rust-embed-utils" 3089 - version = "8.7.2" 3184 + version = "8.9.0" 3090 3185 source = "registry+https://github.com/rust-lang/crates.io-index" 3091 - checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" 3186 + checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" 3092 3187 dependencies = [ 3093 3188 "globset", 3094 3189 "sha2", ··· 3096 3191 ] 3097 3192 3098 3193 [[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 3194 name = "rustc-hash" 3112 3195 version = "2.1.1" 3113 3196 source = "registry+https://github.com/rust-lang/crates.io-index" 3114 3197 checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3115 3198 3116 3199 [[package]] 3117 - name = "rustix" 3118 - version = "0.38.44" 3200 + name = "rustc_version" 3201 + version = "0.4.1" 3119 3202 source = "registry+https://github.com/rust-lang/crates.io-index" 3120 - checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 3203 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 3121 3204 dependencies = [ 3122 - "bitflags", 3123 - "errno", 3124 - "libc", 3125 - "linux-raw-sys", 3126 - "windows-sys 0.59.0", 3205 + "semver", 3127 3206 ] 3128 3207 3129 3208 [[package]] 3130 3209 name = "rustls" 3131 - version = "0.23.31" 3210 + version = "0.23.35" 3132 3211 source = "registry+https://github.com/rust-lang/crates.io-index" 3133 - checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 3212 + checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" 3134 3213 dependencies = [ 3135 3214 "aws-lc-rs", 3136 3215 "log", ··· 3144 3223 3145 3224 [[package]] 3146 3225 name = "rustls-pki-types" 3147 - version = "1.12.0" 3226 + version = "1.13.2" 3148 3227 source = "registry+https://github.com/rust-lang/crates.io-index" 3149 - checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 3228 + checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" 3150 3229 dependencies = [ 3151 3230 "web-time", 3152 3231 "zeroize", ··· 3154 3233 3155 3234 [[package]] 3156 3235 name = "rustls-webpki" 3157 - version = "0.103.4" 3236 + version = "0.103.8" 3158 3237 source = "registry+https://github.com/rust-lang/crates.io-index" 3159 - checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 3238 + checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" 3160 3239 dependencies = [ 3161 3240 "aws-lc-rs", 3162 3241 "ring", ··· 3172 3251 3173 3252 [[package]] 3174 3253 name = "ryu" 3175 - version = "1.0.20" 3254 + version = "1.0.22" 3176 3255 source = "registry+https://github.com/rust-lang/crates.io-index" 3177 - checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 3256 + checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" 3178 3257 3179 3258 [[package]] 3180 3259 name = "salsa20" ··· 3208 3287 3209 3288 [[package]] 3210 3289 name = "schemars" 3211 - version = "1.1.0" 3290 + version = "1.2.0" 3212 3291 source = "registry+https://github.com/rust-lang/crates.io-index" 3213 - checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" 3292 + checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" 3214 3293 dependencies = [ 3215 3294 "dyn-clone", 3216 3295 "ref-cast", ··· 3219 3298 ] 3220 3299 3221 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]] 3222 3307 name = "scopeguard" 3223 3308 version = "1.2.0" 3224 3309 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3269 3354 ] 3270 3355 3271 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]] 3272 3369 name = "serde" 3273 3370 version = "1.0.228" 3274 3371 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3305 3402 dependencies = [ 3306 3403 "proc-macro2", 3307 3404 "quote", 3308 - "syn 2.0.105", 3405 + "syn 2.0.112", 3309 3406 ] 3310 3407 3311 3408 [[package]] ··· 3315 3412 checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3316 3413 dependencies = [ 3317 3414 "form_urlencoded", 3318 - "indexmap 2.10.0", 3415 + "indexmap 2.12.1", 3319 3416 "itoa", 3320 3417 "ryu", 3321 3418 "serde_core", ··· 3335 3432 3336 3433 [[package]] 3337 3434 name = "serde_json" 3338 - version = "1.0.145" 3435 + version = "1.0.148" 3339 3436 source = "registry+https://github.com/rust-lang/crates.io-index" 3340 - checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 3437 + checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" 3341 3438 dependencies = [ 3342 - "indexmap 2.10.0", 3439 + "indexmap 2.12.1", 3343 3440 "itoa", 3344 3441 "memchr", 3345 - "ryu", 3346 3442 "serde", 3347 3443 "serde_core", 3444 + "zmij", 3348 3445 ] 3349 3446 3350 3447 [[package]] 3351 3448 name = "serde_path_to_error" 3352 - version = "0.1.17" 3449 + version = "0.1.20" 3353 3450 source = "registry+https://github.com/rust-lang/crates.io-index" 3354 - checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 3451 + checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 3355 3452 dependencies = [ 3356 3453 "itoa", 3357 3454 "serde", 3455 + "serde_core", 3358 3456 ] 3359 3457 3360 3458 [[package]] ··· 3365 3463 dependencies = [ 3366 3464 "proc-macro2", 3367 3465 "quote", 3368 - "syn 2.0.105", 3466 + "syn 2.0.112", 3369 3467 ] 3370 3468 3371 3469 [[package]] ··· 3382 3480 3383 3481 [[package]] 3384 3482 name = "serde_with" 3385 - version = "3.16.0" 3483 + version = "3.16.1" 3386 3484 source = "registry+https://github.com/rust-lang/crates.io-index" 3387 - checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" 3485 + checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" 3388 3486 dependencies = [ 3389 3487 "base64", 3390 3488 "chrono", 3391 3489 "hex", 3392 3490 "indexmap 1.9.3", 3393 - "indexmap 2.10.0", 3491 + "indexmap 2.12.1", 3394 3492 "schemars 0.9.0", 3395 - "schemars 1.1.0", 3493 + "schemars 1.2.0", 3396 3494 "serde_core", 3397 3495 "serde_json", 3398 3496 "serde_with_macros", ··· 3401 3499 3402 3500 [[package]] 3403 3501 name = "serde_with_macros" 3404 - version = "3.16.0" 3502 + version = "3.16.1" 3405 3503 source = "registry+https://github.com/rust-lang/crates.io-index" 3406 - checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" 3504 + checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" 3407 3505 dependencies = [ 3408 3506 "darling 0.21.3", 3409 3507 "proc-macro2", 3410 3508 "quote", 3411 - "syn 2.0.105", 3509 + "syn 2.0.112", 3412 3510 ] 3413 3511 3414 3512 [[package]] ··· 3450 3548 3451 3549 [[package]] 3452 3550 name = "signal-hook-registry" 3453 - version = "1.4.6" 3551 + version = "1.4.8" 3454 3552 source = "registry+https://github.com/rust-lang/crates.io-index" 3455 - checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 3553 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 3456 3554 dependencies = [ 3555 + "errno", 3457 3556 "libc", 3458 3557 ] 3459 3558 ··· 3469 3568 3470 3569 [[package]] 3471 3570 name = "simd-adler32" 3472 - version = "0.3.7" 3571 + version = "0.3.8" 3473 3572 source = "registry+https://github.com/rust-lang/crates.io-index" 3474 - checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 3573 + checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" 3475 3574 3476 3575 [[package]] 3477 3576 name = "slab" ··· 3500 3599 3501 3600 [[package]] 3502 3601 name = "socket2" 3503 - version = "0.6.0" 3602 + version = "0.6.1" 3504 3603 source = "registry+https://github.com/rust-lang/crates.io-index" 3505 - checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 3604 + checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 3506 3605 dependencies = [ 3507 3606 "libc", 3508 - "windows-sys 0.59.0", 3607 + "windows-sys 0.60.2", 3509 3608 ] 3510 3609 3511 3610 [[package]] ··· 3518 3617 ] 3519 3618 3520 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]] 3521 3626 name = "spinning_top" 3522 3627 version = "0.3.0" 3523 3628 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3568 3673 "futures-util", 3569 3674 "hashbrown 0.15.5", 3570 3675 "hashlink", 3571 - "indexmap 2.10.0", 3676 + "indexmap 2.12.1", 3572 3677 "log", 3573 3678 "memchr", 3574 3679 "once_cell", ··· 3578 3683 "serde_json", 3579 3684 "sha2", 3580 3685 "smallvec", 3581 - "thiserror 2.0.14", 3686 + "thiserror 2.0.17", 3582 3687 "tokio", 3583 3688 "tokio-stream", 3584 3689 "tracing", ··· 3596 3701 "quote", 3597 3702 "sqlx-core", 3598 3703 "sqlx-macros-core", 3599 - "syn 2.0.105", 3704 + "syn 2.0.112", 3600 3705 ] 3601 3706 3602 3707 [[package]] ··· 3619 3724 "sqlx-mysql", 3620 3725 "sqlx-postgres", 3621 3726 "sqlx-sqlite", 3622 - "syn 2.0.105", 3727 + "syn 2.0.112", 3623 3728 "tokio", 3624 3729 "url", 3625 3730 ] ··· 3662 3767 "smallvec", 3663 3768 "sqlx-core", 3664 3769 "stringprep", 3665 - "thiserror 2.0.14", 3770 + "thiserror 2.0.17", 3666 3771 "tracing", 3667 3772 "whoami", 3668 3773 ] ··· 3700 3805 "smallvec", 3701 3806 "sqlx-core", 3702 3807 "stringprep", 3703 - "thiserror 2.0.14", 3808 + "thiserror 2.0.17", 3704 3809 "tracing", 3705 3810 "whoami", 3706 3811 ] ··· 3725 3830 "serde", 3726 3831 "serde_urlencoded", 3727 3832 "sqlx-core", 3728 - "thiserror 2.0.14", 3833 + "thiserror 2.0.17", 3729 3834 "tracing", 3730 3835 "url", 3731 3836 ] 3732 3837 3733 3838 [[package]] 3734 3839 name = "stable_deref_trait" 3735 - version = "1.2.0" 3840 + version = "1.2.1" 3736 3841 source = "registry+https://github.com/rust-lang/crates.io-index" 3737 - checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 3842 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 3738 3843 3739 3844 [[package]] 3740 3845 name = "stacker" 3741 - version = "0.1.21" 3846 + version = "0.1.22" 3742 3847 source = "registry+https://github.com/rust-lang/crates.io-index" 3743 - checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" 3848 + checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" 3744 3849 dependencies = [ 3745 3850 "cc", 3746 3851 "cfg-if", ··· 3765 3870 "quote", 3766 3871 "serde", 3767 3872 "sha2", 3768 - "syn 2.0.105", 3873 + "syn 2.0.112", 3769 3874 "thiserror 1.0.69", 3770 3875 ] 3771 3876 ··· 3811 3916 3812 3917 [[package]] 3813 3918 name = "syn" 3814 - version = "2.0.105" 3919 + version = "2.0.112" 3815 3920 source = "registry+https://github.com/rust-lang/crates.io-index" 3816 - checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" 3921 + checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" 3817 3922 dependencies = [ 3818 3923 "proc-macro2", 3819 3924 "quote", ··· 3837 3942 dependencies = [ 3838 3943 "proc-macro2", 3839 3944 "quote", 3840 - "syn 2.0.105", 3945 + "syn 2.0.112", 3841 3946 ] 3842 3947 3843 3948 [[package]] ··· 3872 3977 3873 3978 [[package]] 3874 3979 name = "thiserror" 3875 - version = "2.0.14" 3980 + version = "2.0.17" 3876 3981 source = "registry+https://github.com/rust-lang/crates.io-index" 3877 - checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" 3982 + checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 3878 3983 dependencies = [ 3879 - "thiserror-impl 2.0.14", 3984 + "thiserror-impl 2.0.17", 3880 3985 ] 3881 3986 3882 3987 [[package]] ··· 3887 3992 dependencies = [ 3888 3993 "proc-macro2", 3889 3994 "quote", 3890 - "syn 2.0.105", 3995 + "syn 2.0.112", 3891 3996 ] 3892 3997 3893 3998 [[package]] 3894 3999 name = "thiserror-impl" 3895 - version = "2.0.14" 4000 + version = "2.0.17" 3896 4001 source = "registry+https://github.com/rust-lang/crates.io-index" 3897 - checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" 4002 + checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 3898 4003 dependencies = [ 3899 4004 "proc-macro2", 3900 4005 "quote", 3901 - "syn 2.0.105", 4006 + "syn 2.0.112", 3902 4007 ] 3903 4008 3904 4009 [[package]] ··· 3943 4048 3944 4049 [[package]] 3945 4050 name = "tinystr" 3946 - version = "0.8.1" 4051 + version = "0.8.2" 3947 4052 source = "registry+https://github.com/rust-lang/crates.io-index" 3948 - checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 4053 + checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" 3949 4054 dependencies = [ 3950 4055 "displaydoc", 3951 4056 "zerovec", ··· 3953 4058 3954 4059 [[package]] 3955 4060 name = "tinyvec" 3956 - version = "1.9.0" 4061 + version = "1.10.0" 3957 4062 source = "registry+https://github.com/rust-lang/crates.io-index" 3958 - checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 4063 + checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 3959 4064 dependencies = [ 3960 4065 "tinyvec_macros", 3961 4066 ] ··· 3968 4073 3969 4074 [[package]] 3970 4075 name = "tokio" 3971 - version = "1.47.1" 4076 + version = "1.48.0" 3972 4077 source = "registry+https://github.com/rust-lang/crates.io-index" 3973 - checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" 4078 + checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 3974 4079 dependencies = [ 3975 - "backtrace", 3976 4080 "bytes", 3977 - "io-uring", 3978 4081 "libc", 3979 4082 "mio", 3980 4083 "pin-project-lite", 3981 4084 "signal-hook-registry", 3982 - "slab", 3983 4085 "socket2", 3984 4086 "tokio-macros", 3985 - "windows-sys 0.59.0", 4087 + "windows-sys 0.61.2", 3986 4088 ] 3987 4089 3988 4090 [[package]] 3989 4091 name = "tokio-macros" 3990 - version = "2.5.0" 4092 + version = "2.6.0" 3991 4093 source = "registry+https://github.com/rust-lang/crates.io-index" 3992 - checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 4094 + checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 3993 4095 dependencies = [ 3994 4096 "proc-macro2", 3995 4097 "quote", 3996 - "syn 2.0.105", 4098 + "syn 2.0.112", 3997 4099 ] 3998 4100 3999 4101 [[package]] 4000 4102 name = "tokio-rustls" 4001 - version = "0.26.2" 4103 + version = "0.26.4" 4002 4104 source = "registry+https://github.com/rust-lang/crates.io-index" 4003 - checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 4105 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 4004 4106 dependencies = [ 4005 4107 "rustls", 4006 4108 "tokio", ··· 4026 4128 "bytes", 4027 4129 "futures-core", 4028 4130 "futures-sink", 4131 + "futures-util", 4029 4132 "pin-project-lite", 4030 4133 "tokio", 4031 4134 ] 4032 4135 4033 4136 [[package]] 4034 4137 name = "tonic" 4035 - version = "0.14.1" 4138 + version = "0.14.2" 4036 4139 source = "registry+https://github.com/rust-lang/crates.io-index" 4037 - checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" 4140 + checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" 4038 4141 dependencies = [ 4039 4142 "async-trait", 4040 4143 "axum", ··· 4067 4170 dependencies = [ 4068 4171 "futures-core", 4069 4172 "futures-util", 4070 - "indexmap 2.10.0", 4173 + "indexmap 2.12.1", 4071 4174 "pin-project-lite", 4072 4175 "slab", 4073 4176 "sync_wrapper", ··· 4080 4183 4081 4184 [[package]] 4082 4185 name = "tower-http" 4083 - version = "0.6.6" 4186 + version = "0.6.8" 4084 4187 source = "registry+https://github.com/rust-lang/crates.io-index" 4085 - checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 4188 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 4086 4189 dependencies = [ 4087 4190 "async-compression", 4088 4191 "bitflags", ··· 4091 4194 "futures-util", 4092 4195 "http", 4093 4196 "http-body", 4197 + "http-body-util", 4094 4198 "iri-string", 4095 4199 "pin-project-lite", 4096 4200 "tokio", ··· 4123 4227 "governor", 4124 4228 "http", 4125 4229 "pin-project", 4126 - "thiserror 2.0.14", 4230 + "thiserror 2.0.17", 4127 4231 "tonic", 4128 4232 "tower", 4129 4233 "tracing", ··· 4131 4235 4132 4236 [[package]] 4133 4237 name = "tracing" 4134 - version = "0.1.41" 4238 + version = "0.1.44" 4135 4239 source = "registry+https://github.com/rust-lang/crates.io-index" 4136 - checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 4240 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 4137 4241 dependencies = [ 4138 4242 "log", 4139 4243 "pin-project-lite", ··· 4143 4247 4144 4248 [[package]] 4145 4249 name = "tracing-attributes" 4146 - version = "0.1.30" 4250 + version = "0.1.31" 4147 4251 source = "registry+https://github.com/rust-lang/crates.io-index" 4148 - checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" 4252 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 4149 4253 dependencies = [ 4150 4254 "proc-macro2", 4151 4255 "quote", 4152 - "syn 2.0.105", 4256 + "syn 2.0.112", 4153 4257 ] 4154 4258 4155 4259 [[package]] 4156 4260 name = "tracing-core" 4157 - version = "0.1.34" 4261 + version = "0.1.36" 4158 4262 source = "registry+https://github.com/rust-lang/crates.io-index" 4159 - checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 4263 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 4160 4264 dependencies = [ 4161 4265 "once_cell", 4162 4266 "valuable", ··· 4175 4279 4176 4280 [[package]] 4177 4281 name = "tracing-subscriber" 4178 - version = "0.3.19" 4282 + version = "0.3.22" 4179 4283 source = "registry+https://github.com/rust-lang/crates.io-index" 4180 - checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 4284 + checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 4181 4285 dependencies = [ 4182 4286 "matchers", 4183 4287 "nu-ansi-term", 4184 4288 "once_cell", 4185 - "regex", 4289 + "regex-automata", 4186 4290 "sharded-slab", 4187 4291 "smallvec", 4188 4292 "thread_local", ··· 4199 4303 dependencies = [ 4200 4304 "proc-macro2", 4201 4305 "quote", 4202 - "syn 2.0.105", 4306 + "syn 2.0.112", 4203 4307 ] 4204 4308 4205 4309 [[package]] ··· 4210 4314 4211 4315 [[package]] 4212 4316 name = "typenum" 4213 - version = "1.18.0" 4317 + version = "1.19.0" 4214 4318 source = "registry+https://github.com/rust-lang/crates.io-index" 4215 - checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 4319 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 4216 4320 4217 4321 [[package]] 4218 4322 name = "ucd-trie" ··· 4228 4332 4229 4333 [[package]] 4230 4334 name = "unicode-ident" 4231 - version = "1.0.18" 4335 + version = "1.0.22" 4232 4336 source = "registry+https://github.com/rust-lang/crates.io-index" 4233 - checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 4337 + checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 4234 4338 4235 4339 [[package]] 4236 4340 name = "unicode-normalization" 4237 - version = "0.1.24" 4341 + version = "0.1.25" 4238 4342 source = "registry+https://github.com/rust-lang/crates.io-index" 4239 - checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 4343 + checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" 4240 4344 dependencies = [ 4241 4345 "tinyvec", 4242 4346 ] 4243 4347 4244 4348 [[package]] 4245 4349 name = "unicode-properties" 4246 - version = "0.1.3" 4350 + version = "0.1.4" 4247 4351 source = "registry+https://github.com/rust-lang/crates.io-index" 4248 - checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 4352 + checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" 4249 4353 4250 4354 [[package]] 4251 4355 name = "unicode-segmentation" ··· 4260 4364 checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 4261 4365 4262 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]] 4263 4373 name = "unsigned-varint" 4264 4374 version = "0.8.0" 4265 4375 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4279 4389 4280 4390 [[package]] 4281 4391 name = "url" 4282 - version = "2.5.4" 4392 + version = "2.5.7" 4283 4393 source = "registry+https://github.com/rust-lang/crates.io-index" 4284 - checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 4394 + checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 4285 4395 dependencies = [ 4286 4396 "form_urlencoded", 4287 4397 "idna", ··· 4367 4477 4368 4478 [[package]] 4369 4479 name = "wasm-bindgen" 4370 - version = "0.2.100" 4480 + version = "0.2.106" 4371 4481 source = "registry+https://github.com/rust-lang/crates.io-index" 4372 - checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 4482 + checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 4373 4483 dependencies = [ 4374 4484 "cfg-if", 4375 4485 "once_cell", 4376 4486 "rustversion", 4377 4487 "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 4488 "wasm-bindgen-shared", 4392 4489 ] 4393 4490 4394 4491 [[package]] 4395 4492 name = "wasm-bindgen-futures" 4396 - version = "0.4.50" 4493 + version = "0.4.56" 4397 4494 source = "registry+https://github.com/rust-lang/crates.io-index" 4398 - checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 4495 + checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" 4399 4496 dependencies = [ 4400 4497 "cfg-if", 4401 4498 "js-sys", ··· 4406 4503 4407 4504 [[package]] 4408 4505 name = "wasm-bindgen-macro" 4409 - version = "0.2.100" 4506 + version = "0.2.106" 4410 4507 source = "registry+https://github.com/rust-lang/crates.io-index" 4411 - checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 4508 + checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 4412 4509 dependencies = [ 4413 4510 "quote", 4414 4511 "wasm-bindgen-macro-support", ··· 4416 4513 4417 4514 [[package]] 4418 4515 name = "wasm-bindgen-macro-support" 4419 - version = "0.2.100" 4516 + version = "0.2.106" 4420 4517 source = "registry+https://github.com/rust-lang/crates.io-index" 4421 - checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 4518 + checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 4422 4519 dependencies = [ 4520 + "bumpalo", 4423 4521 "proc-macro2", 4424 4522 "quote", 4425 - "syn 2.0.105", 4426 - "wasm-bindgen-backend", 4523 + "syn 2.0.112", 4427 4524 "wasm-bindgen-shared", 4428 4525 ] 4429 4526 4430 4527 [[package]] 4431 4528 name = "wasm-bindgen-shared" 4432 - version = "0.2.100" 4529 + version = "0.2.106" 4433 4530 source = "registry+https://github.com/rust-lang/crates.io-index" 4434 - checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 4531 + checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 4435 4532 dependencies = [ 4436 4533 "unicode-ident", 4437 4534 ] 4438 4535 4439 4536 [[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 4537 name = "web-sys" 4454 - version = "0.3.77" 4538 + version = "0.3.83" 4455 4539 source = "registry+https://github.com/rust-lang/crates.io-index" 4456 - checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 4540 + checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" 4457 4541 dependencies = [ 4458 4542 "js-sys", 4459 4543 "wasm-bindgen", ··· 4475 4559 source = "registry+https://github.com/rust-lang/crates.io-index" 4476 4560 checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 4477 4561 dependencies = [ 4478 - "webpki-roots 1.0.2", 4562 + "webpki-roots 1.0.5", 4479 4563 ] 4480 4564 4481 4565 [[package]] 4482 4566 name = "webpki-roots" 4483 - version = "1.0.2" 4567 + version = "1.0.5" 4484 4568 source = "registry+https://github.com/rust-lang/crates.io-index" 4485 - checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" 4569 + checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" 4486 4570 dependencies = [ 4487 4571 "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 4572 ] 4501 4573 4502 4574 [[package]] ··· 4527 4599 4528 4600 [[package]] 4529 4601 name = "winapi-util" 4530 - version = "0.1.9" 4602 + version = "0.1.11" 4531 4603 source = "registry+https://github.com/rust-lang/crates.io-index" 4532 - checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 4604 + checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 4533 4605 dependencies = [ 4534 - "windows-sys 0.59.0", 4606 + "windows-sys 0.48.0", 4535 4607 ] 4536 4608 4537 4609 [[package]] ··· 4542 4614 4543 4615 [[package]] 4544 4616 name = "windows-core" 4545 - version = "0.61.2" 4617 + version = "0.62.2" 4546 4618 source = "registry+https://github.com/rust-lang/crates.io-index" 4547 - checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 4619 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 4548 4620 dependencies = [ 4549 4621 "windows-implement", 4550 4622 "windows-interface", 4551 - "windows-link 0.1.3", 4623 + "windows-link", 4552 4624 "windows-result", 4553 4625 "windows-strings", 4554 4626 ] 4555 4627 4556 4628 [[package]] 4557 4629 name = "windows-implement" 4558 - version = "0.60.0" 4630 + version = "0.60.2" 4559 4631 source = "registry+https://github.com/rust-lang/crates.io-index" 4560 - checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 4632 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 4561 4633 dependencies = [ 4562 4634 "proc-macro2", 4563 4635 "quote", 4564 - "syn 2.0.105", 4636 + "syn 2.0.112", 4565 4637 ] 4566 4638 4567 4639 [[package]] 4568 4640 name = "windows-interface" 4569 - version = "0.59.1" 4641 + version = "0.59.3" 4570 4642 source = "registry+https://github.com/rust-lang/crates.io-index" 4571 - checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 4643 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 4572 4644 dependencies = [ 4573 4645 "proc-macro2", 4574 4646 "quote", 4575 - "syn 2.0.105", 4647 + "syn 2.0.112", 4576 4648 ] 4577 4649 4578 4650 [[package]] 4579 4651 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 4652 version = "0.2.1" 4587 4653 source = "registry+https://github.com/rust-lang/crates.io-index" 4588 4654 checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 4589 4655 4590 4656 [[package]] 4591 4657 name = "windows-registry" 4592 - version = "0.5.3" 4658 + version = "0.6.1" 4593 4659 source = "registry+https://github.com/rust-lang/crates.io-index" 4594 - checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" 4660 + checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 4595 4661 dependencies = [ 4596 - "windows-link 0.1.3", 4662 + "windows-link", 4597 4663 "windows-result", 4598 4664 "windows-strings", 4599 4665 ] 4600 4666 4601 4667 [[package]] 4602 4668 name = "windows-result" 4603 - version = "0.3.4" 4669 + version = "0.4.1" 4604 4670 source = "registry+https://github.com/rust-lang/crates.io-index" 4605 - checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 4671 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 4606 4672 dependencies = [ 4607 - "windows-link 0.1.3", 4673 + "windows-link", 4608 4674 ] 4609 4675 4610 4676 [[package]] 4611 4677 name = "windows-strings" 4612 - version = "0.4.2" 4678 + version = "0.5.1" 4613 4679 source = "registry+https://github.com/rust-lang/crates.io-index" 4614 - checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 4680 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 4615 4681 dependencies = [ 4616 - "windows-link 0.1.3", 4682 + "windows-link", 4617 4683 ] 4618 4684 4619 4685 [[package]] ··· 4644 4710 ] 4645 4711 4646 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]] 4647 4731 name = "windows-targets" 4648 4732 version = "0.48.5" 4649 4733 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4667 4751 "windows_aarch64_gnullvm 0.52.6", 4668 4752 "windows_aarch64_msvc 0.52.6", 4669 4753 "windows_i686_gnu 0.52.6", 4670 - "windows_i686_gnullvm", 4754 + "windows_i686_gnullvm 0.52.6", 4671 4755 "windows_i686_msvc 0.52.6", 4672 4756 "windows_x86_64_gnu 0.52.6", 4673 4757 "windows_x86_64_gnullvm 0.52.6", ··· 4675 4759 ] 4676 4760 4677 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]] 4678 4779 name = "windows_aarch64_gnullvm" 4679 4780 version = "0.48.5" 4680 4781 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4687 4788 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 4688 4789 4689 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]] 4690 4797 name = "windows_aarch64_msvc" 4691 4798 version = "0.48.5" 4692 4799 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4699 4806 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 4700 4807 4701 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]] 4702 4815 name = "windows_i686_gnu" 4703 4816 version = "0.48.5" 4704 4817 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4711 4824 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 4712 4825 4713 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]] 4714 4833 name = "windows_i686_gnullvm" 4715 4834 version = "0.52.6" 4716 4835 source = "registry+https://github.com/rust-lang/crates.io-index" 4717 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" 4718 4843 4719 4844 [[package]] 4720 4845 name = "windows_i686_msvc" ··· 4729 4854 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 4730 4855 4731 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]] 4732 4863 name = "windows_x86_64_gnu" 4733 4864 version = "0.48.5" 4734 4865 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4741 4872 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 4742 4873 4743 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]] 4744 4881 name = "windows_x86_64_gnullvm" 4745 4882 version = "0.48.5" 4746 4883 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4753 4890 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 4754 4891 4755 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]] 4756 4899 name = "windows_x86_64_msvc" 4757 4900 version = "0.48.5" 4758 4901 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4765 4908 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 4766 4909 4767 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]] 4768 4917 name = "wit-bindgen" 4769 4918 version = "0.46.0" 4770 4919 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4772 4921 4773 4922 [[package]] 4774 4923 name = "writeable" 4775 - version = "0.6.1" 4924 + version = "0.6.2" 4776 4925 source = "registry+https://github.com/rust-lang/crates.io-index" 4777 - checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 4926 + checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" 4778 4927 4779 4928 [[package]] 4780 4929 name = "yansi" ··· 4784 4933 4785 4934 [[package]] 4786 4935 name = "yoke" 4787 - version = "0.8.0" 4936 + version = "0.8.1" 4788 4937 source = "registry+https://github.com/rust-lang/crates.io-index" 4789 - checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 4938 + checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" 4790 4939 dependencies = [ 4791 - "serde", 4792 4940 "stable_deref_trait", 4793 4941 "yoke-derive", 4794 4942 "zerofrom", ··· 4796 4944 4797 4945 [[package]] 4798 4946 name = "yoke-derive" 4799 - version = "0.8.0" 4947 + version = "0.8.1" 4800 4948 source = "registry+https://github.com/rust-lang/crates.io-index" 4801 - checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 4949 + checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" 4802 4950 dependencies = [ 4803 4951 "proc-macro2", 4804 4952 "quote", 4805 - "syn 2.0.105", 4953 + "syn 2.0.112", 4806 4954 "synstructure", 4807 4955 ] 4808 4956 4809 4957 [[package]] 4810 4958 name = "zerocopy" 4811 - version = "0.8.26" 4959 + version = "0.8.31" 4812 4960 source = "registry+https://github.com/rust-lang/crates.io-index" 4813 - checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" 4961 + checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" 4814 4962 dependencies = [ 4815 4963 "zerocopy-derive", 4816 4964 ] 4817 4965 4818 4966 [[package]] 4819 4967 name = "zerocopy-derive" 4820 - version = "0.8.26" 4968 + version = "0.8.31" 4821 4969 source = "registry+https://github.com/rust-lang/crates.io-index" 4822 - checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" 4970 + checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" 4823 4971 dependencies = [ 4824 4972 "proc-macro2", 4825 4973 "quote", 4826 - "syn 2.0.105", 4974 + "syn 2.0.112", 4827 4975 ] 4828 4976 4829 4977 [[package]] ··· 4843 4991 dependencies = [ 4844 4992 "proc-macro2", 4845 4993 "quote", 4846 - "syn 2.0.105", 4994 + "syn 2.0.112", 4847 4995 "synstructure", 4848 4996 ] 4849 4997 4850 4998 [[package]] 4851 4999 name = "zeroize" 4852 - version = "1.8.1" 5000 + version = "1.8.2" 4853 5001 source = "registry+https://github.com/rust-lang/crates.io-index" 4854 - checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 5002 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 4855 5003 dependencies = [ 4856 5004 "zeroize_derive", 4857 5005 ] 4858 5006 4859 5007 [[package]] 4860 5008 name = "zeroize_derive" 4861 - version = "1.4.2" 5009 + version = "1.4.3" 4862 5010 source = "registry+https://github.com/rust-lang/crates.io-index" 4863 - checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 5011 + checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" 4864 5012 dependencies = [ 4865 5013 "proc-macro2", 4866 5014 "quote", 4867 - "syn 2.0.105", 5015 + "syn 2.0.112", 4868 5016 ] 4869 5017 4870 5018 [[package]] 4871 5019 name = "zerotrie" 4872 - version = "0.2.2" 5020 + version = "0.2.3" 4873 5021 source = "registry+https://github.com/rust-lang/crates.io-index" 4874 - checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 5022 + checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" 4875 5023 dependencies = [ 4876 5024 "displaydoc", 4877 5025 "yoke", ··· 4880 5028 4881 5029 [[package]] 4882 5030 name = "zerovec" 4883 - version = "0.11.4" 5031 + version = "0.11.5" 4884 5032 source = "registry+https://github.com/rust-lang/crates.io-index" 4885 - checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 5033 + checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" 4886 5034 dependencies = [ 4887 5035 "yoke", 4888 5036 "zerofrom", ··· 4891 5039 4892 5040 [[package]] 4893 5041 name = "zerovec-derive" 4894 - version = "0.11.1" 5042 + version = "0.11.2" 4895 5043 source = "registry+https://github.com/rust-lang/crates.io-index" 4896 - checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 5044 + checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" 4897 5045 dependencies = [ 4898 5046 "proc-macro2", 4899 5047 "quote", 4900 - "syn 2.0.105", 5048 + "syn 2.0.112", 4901 5049 ] 4902 5050 4903 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]] 4904 5058 name = "zstd" 4905 5059 version = "0.13.3" 4906 5060 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4920 5074 4921 5075 [[package]] 4922 5076 name = "zstd-sys" 4923 - version = "2.0.15+zstd.1.5.7" 5077 + version = "2.0.16+zstd.1.5.7" 4924 5078 source = "registry+https://github.com/rust-lang/crates.io-index" 4925 - checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" 5079 + checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" 4926 5080 dependencies = [ 4927 5081 "cc", 4928 5082 "pkg-config",
+11 -9
Cargo.toml
··· 5 5 license = "MIT" 6 6 7 7 [dependencies] 8 - axum = { version = "0.8.4", features = ["macros", "json"] } 9 - tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "signal"] } 8 + axum = { version = "0.8.8", features = ["macros", "json"] } 9 + tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "signal"] } 10 10 sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "sqlite", "migrate", "chrono"] } 11 11 dotenvy = "0.15.7" 12 12 serde = { version = "1.0", features = ["derive"] } 13 13 serde_json = "1.0" 14 14 tracing = "0.1" 15 15 tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } 16 - hyper-util = { version = "0.1.16", features = ["client", "client-legacy"] } 16 + hyper-util = { version = "0.1.19", features = ["client", "client-legacy"] } 17 17 tower-http = { version = "0.6", features = ["cors", "compression-zstd"] } 18 18 tower_governor = { version = "0.8.0", features = ["axum", "tracing"] } 19 19 hex = "0.4" 20 20 jwt-compact = { version = "0.8.0", features = ["es256k"] } 21 21 scrypt = "0.11" 22 22 #Leaveing these two cause I think it is needed by the email crate for ssl 23 - aws-lc-rs = "1.13.0" 23 + aws-lc-rs = "1.15.2" 24 24 rustls = { version = "0.23", default-features = false, features = ["tls12", "std", "logging", "aws_lc_rs"] } 25 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" 26 + handlebars = { version = "6.4.0", features = ["rust-embed"] } 27 + rust-embed = "8.9.0" 28 28 axum-template = { version = "3.0.0", features = ["handlebars"] } 29 29 rand = "0.9.2" 30 - anyhow = "1.0.99" 30 + anyhow = "1.0.100" 31 31 chrono = { version = "0.4.42", features = ["default", "serde"] } 32 32 sha2 = "0.10" 33 - jacquard-common = "0.9.2" 34 - jacquard-identity = "0.9.2" 33 + jacquard-common = "0.9.5" 34 + jacquard-identity = "0.9.5" 35 35 multibase = "0.9.2" 36 36 reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } 37 37 urlencoding = "2.1" 38 38 html-escape = "0.2.13" 39 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 + }
+4 -1
src/main.rs
··· 37 37 use tracing::log; 38 38 use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 39 39 40 + mod auth; 40 41 mod gate; 41 42 pub mod helpers; 42 43 mod middleware; ··· 153 154 mailer: AsyncSmtpTransport<Tokio1Executor>, 154 155 template_engine: Engine<Handlebars<'static>>, 155 156 resolver: Arc<PublicResolver>, 157 + handle_cache: auth::HandleCache, 156 158 app_config: AppConfig, 157 159 } 158 160 ··· 278 280 mailer, 279 281 template_engine: Engine::from(hbs), 280 282 resolver: Arc::new(resolver), 283 + handle_cache: auth::HandleCache::new(), 281 284 app_config: AppConfig::new(), 282 285 }; 283 286 ··· 387 390 .layer(cors) 388 391 .with_state(state); 389 392 390 - let host = env::var("GATEKEEPER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); 393 + let host = env::var("GATEKEEPER_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()); 391 394 let port: u16 = env::var("GATEKEEPER_PORT") 392 395 .ok() 393 396 .and_then(|s| s.parse().ok())