Skip to main content

max / makenotwork

sando: add the hardening_test gate for what fast-tests hides cargo_test builds the server with --features fast-tests, which relaxes AUTH_RATE_LIMIT_BURST 5 -> 20, SANDBOX_RATE_LIMIT_MS 30s -> 10ms, and argon2 from 46 MiB/t=2 to 8 MiB/t=1. The rate-limiting suite is then #[cfg_attr(feature = "fast-tests", ignore)]d on top, because a bucket refilling at 100/sec never depletes under parallel threads. Sando's only code gate was therefore skipping every test of the auth hardening it exists to protect, and a rate-limiter regression would reach a node unblocked. hardening_test re-runs that suite with no features, --test-threads=1, filtered to the rate_limit names. Verified against the current tree: the six tests pass on production constants in 1.5s. It fails closed when the filter matches zero tests, so renaming the suite cannot turn the gate into a green no-op. cargo_test_command now takes the feature list so both gates share one env setup and cannot drift apart on DATABASE_URL/CARGO_TARGET_DIR handling. Cost: a second compile of the server's lib + integration binary, since a different feature set shares no artifacts. Roughly 8 minutes on fw13, added to every build. That is the price of the coverage.
Author: Max Johnson <me@maxj.phd> · 2026-07-21 19:24 UTC
Commit: 57d872e66192f62f47e76894f9d5e6b719694ca5
Parent: d897b6e
7 files changed, +283 insertions, -163 deletions
@@ -98,6 +98,39 @@ curl -X POST http://127.0.0.1:7766/promote/a \
98 98 -d '{"version":"0.8.2"}'
99 99 ```
100 100
101 + ## Gates
102 +
103 + Build-time gates run on the host tier, once per build, against the worktree.
104 + Promote-time gates run against a tier's deployed nodes or its operator.
105 +
106 + | Kind | When | What it proves |
107 + |------|------|----------------|
108 + | `code_smoke` | build | Boots the fresh binary on a throwaway DB it migrates from scratch and seeds, then probes `/health`. Runs first: green here isolates a later red as an environment problem, not a code one. |
109 + | `cargo_test` | build | The server suite under `--features fast-tests`. |
110 + | `hardening_test` | build | What `cargo_test` structurally cannot reach (see below). |
111 + | `migration_dry_run` | build | Migrations apply cleanly to a restored production dump. |
112 + | `boot_smoke` | build | The staged artifact boots in minimal no-DB mode on the build host. |
113 + | `node_health` | post-deploy | Each deployed node's unit is active (and serves 2xx if `health_url` is set). Recorded at the end of a promote as the evidence the next promote checks. |
114 + | `burn_in` | promote | The tier has held its current version for N hours. Evaluated live against the clock. |
115 + | `manual_confirm` | promote | An operator signed off, at or after this version landed on the tier. |
116 +
117 + ### Why `hardening_test` exists
118 +
119 + `cargo_test` builds with `--features fast-tests`, which relaxes
120 + `AUTH_RATE_LIMIT_BURST` (5 to 20), `SANDBOX_RATE_LIMIT_MS` (30s to 10ms), and
121 + argon2 (46 MiB/t=2 down to 8 MiB/t=1) so the signup-heavy workflow suite
122 + finishes in reasonable time. On top of that, the rate-limiting tests are
123 + `#[cfg_attr(feature = "fast-tests", ignore)]`d, because a bucket refilling at
124 + 100/sec never depletes under parallel test threads.
125 +
126 + The net effect was that Sando's only code gate skipped every test of the auth
127 + hardening it exists to protect. `hardening_test` re-runs that suite with no
128 + features, single-threaded, against production constants. It costs a second
129 + compile of the server's test binary, since a different feature set is a
130 + different cfg and shares no artifacts. It also fails closed if its name filter
131 + matches zero tests, so renaming the suite cannot quietly turn the gate into a
132 + green no-op.
133 +
101 134 ## API
102 135
103 136 | Method | Path | Body | Purpose |
@@ -237,6 +237,14 @@ impl<'r> sqlx::Decode<'r, Sqlite> for GitSha {
237 237 #[serde(rename_all = "snake_case")]
238 238 pub enum GateKind {
239 239 CargoTest,
240 + /// The tests `cargo_test` structurally cannot run: `--features fast-tests`
241 + /// relaxes the auth/sandbox rate limits and argon2 cost so the signup-heavy
242 + /// workflow suite finishes, and the rate-limiting tests are
243 + /// `#[cfg_attr(feature = "fast-tests", ignore)]`d because a bucket that
244 + /// refills at 100/sec never depletes under parallel test threads. This gate
245 + /// runs that set WITHOUT the feature, against the production constants, so a
246 + /// rate-limiter regression cannot reach a node unblocked.
247 + HardeningTest,
240 248 MigrationDryRun,
241 249 /// Boots the freshly-built binary against a throwaway *empty* DB it
242 250 /// migrates from scratch, seeds the example catalog into, and probes
@@ -259,6 +267,7 @@ impl GateKind {
259 267 pub fn as_str(self) -> &'static str {
260 268 match self {
261 269 GateKind::CargoTest => "cargo_test",
270 + GateKind::HardeningTest => "hardening_test",
262 271 GateKind::MigrationDryRun => "migration_dry_run",
263 272 GateKind::CodeSmoke => "code_smoke",
264 273 GateKind::BootSmoke => "boot_smoke",
@@ -278,6 +287,7 @@ impl FromStr for GateKind {
278 287 fn from_str(s: &str) -> Result<Self, Self::Err> {
279 288 match s {
280 289 "cargo_test" => Ok(GateKind::CargoTest),
290 + "hardening_test" => Ok(GateKind::HardeningTest),
281 291 "migration_dry_run" => Ok(GateKind::MigrationDryRun),
282 292 "code_smoke" => Ok(GateKind::CodeSmoke),
283 293 "boot_smoke" => Ok(GateKind::BootSmoke),
@@ -427,6 +437,7 @@ mod tests {
427 437 // so step 3 (events use the types) doesn't change the wire shape.
428 438 for k in [
429 439 GateKind::CargoTest,
440 + GateKind::HardeningTest,
430 441 GateKind::MigrationDryRun,
431 442 GateKind::CodeSmoke,
432 443 GateKind::BootSmoke,
@@ -442,6 +453,14 @@ mod tests {
442 453 }
443 454
444 455 #[test]
456 + fn gate_kind_hardening_test_round_trips() {
457 + // It is written into gate_runs.gate_kind and read back by
458 + // unsatisfied_gates, so the two directions must agree exactly.
459 + assert_eq!(GateKind::HardeningTest.as_str(), "hardening_test");
460 + assert_eq!("hardening_test".parse::<GateKind>().unwrap(), GateKind::HardeningTest);
461 + }
462 +
463 + #[test]
445 464 fn gate_kind_from_str_rejects_unknown() {
446 465 assert!("not_a_gate".parse::<GateKind>().is_err());
447 466 }
@@ -74,6 +74,8 @@ pub async fn run(ctx: &GateCtx, gate: &Gate) -> Result<GateOutcome> {
74 74 let outcome = match gate {
75 75 // cargo_test bounds its own run internally (it kills the specific child).
76 76 Gate::CargoTest => cargo_test(ctx, run_id).await,
77 + // hardening_test bounds its own run internally, same as cargo_test.
78 + Gate::HardeningTest => hardening_test(ctx, run_id).await,
77 79 // migration_dry_run's psql restore + sqlx migrate could wedge; bound the
78 80 // whole gate here. Its bash restore sets kill_on_drop, so a timeout-drop
79 81 // doesn't orphan it.
@@ -195,7 +197,7 @@ async fn cargo_test(ctx: &GateCtx, run_id: GateRunId) -> Result<GateOutcome> {
195 197 // `#[cfg(test)]`-only binary like `load`) otherwise compiles fine under
196 198 // the build step + `--test integration` and only blows up here, after a
197 199 // full build, as an opaque mass test failure.
198 - let mut pre = match cargo_test_command(ctx, &server_dir, &["--no-run"]).spawn() {
200 + let mut pre = match cargo_test_command(ctx, &server_dir, &["fast-tests"], &["--no-run"]).spawn() {
199 201 Ok(c) => c,
200 202 Err(e) => {
201 203 return Ok(GateOutcome::failed(GateFailure::SpawnFailed {
@@ -212,7 +214,7 @@ async fn cargo_test(ctx: &GateCtx, run_id: GateRunId) -> Result<GateOutcome> {
212 214
213 215 // Full run: the test binaries are already built above, so cargo's
214 216 // up-to-date check skips compilation and this just runs the tests.
215 - let mut child = match cargo_test_command(ctx, &server_dir, &[]).spawn() {
217 + let mut child = match cargo_test_command(ctx, &server_dir, &["fast-tests"], &[]).spawn() {
216 218 Ok(c) => c,
217 219 Err(e) => {
218 220 return Ok(GateOutcome::failed(GateFailure::SpawnFailed {
@@ -246,18 +248,138 @@ async fn cargo_test(ctx: &GateCtx, run_id: GateRunId) -> Result<GateOutcome> {
246 248 }
247 249 }
248 250
249 - /// Configure (but don't spawn) `cargo test --release --features fast-tests
251 + /// The tests `cargo_test` cannot reach, run against production constants.
252 + ///
253 + /// `cargo_test` builds with `--features fast-tests`, which relaxes
254 + /// `AUTH_RATE_LIMIT_BURST` 5 → 20, `SANDBOX_RATE_LIMIT_MS` 30s → 10ms, and
255 + /// argon2 from 46 MiB/t=2 to 8 MiB/t=1 — and the rate-limiting suite is
256 + /// `#[cfg_attr(feature = "fast-tests", ignore)]`d on top of that, because a
257 + /// bucket refilling at 100/sec never depletes under parallel test threads. The
258 + /// net effect was that Sando's only code gate silently skipped every test of
259 + /// the auth hardening it most needs to protect.
260 + ///
261 + /// So: no features, `--test-threads=1` (these tests key on a shared per-IP
262 + /// bucket and must not interleave), and a name filter rather than the whole
263 + /// suite — the rest of the suite is tuned for `fast-tests` and would only go
264 + /// slow and flaky here. The filter is a substring match, so it catches
265 + /// `..._rate_limit_...` and `..._rate_limited` alike.
266 + ///
267 + /// This costs a second compile of the lib + integration binary (a different
268 + /// feature set is a different cfg, so no artifact sharing with `cargo_test`).
269 + /// That is the price of the coverage; the filter keeps the *run* to seconds.
270 + async fn hardening_test(ctx: &GateCtx, run_id: GateRunId) -> Result<GateOutcome> {
271 + let server_dir = ctx.worktree.join("server");
272 + let log_path = gate_log_path(ctx, GateKind::HardeningTest);
273 + let log_ref = LogRef::new(&ctx.version, GateKind::HardeningTest);
274 +
275 + if let Some(scratch_url) = ctx.cfg.scratch_db_url.as_deref() {
276 + clean_stale_test_dbs(scratch_url).await;
277 + }
278 +
279 + let started = std::time::Instant::now();
280 +
281 + // Same two-step shape as cargo_test: compile first so a test-target break
282 + // reports as a compile error rather than an opaque mass test failure.
283 + let mut pre = match cargo_test_command(ctx, &server_dir, &[], &["--no-run", "--test", "integration"]).spawn() {
284 + Ok(c) => c,
285 + Err(e) => {
286 + return Ok(GateOutcome::failed(GateFailure::SpawnFailed {
287 + message: e.to_string(),
288 + }).with_log_ref(log_ref));
289 + }
290 + };
291 + let (pre_out, pre_err, pre_status) =
292 + stream_child_to_live_log(&mut pre, ctx.events.clone(), run_id, log_path.clone()).await?;
293 + if !pre_status.success() {
294 + let failure = classify::classify_compile_error(&pre_out, &pre_err);
295 + return Ok(GateOutcome::failed(failure).with_log_ref(log_ref));
296 + }
297 +
298 + let mut child = match cargo_test_command(
299 + ctx,
300 + &server_dir,
301 + &[],
302 + &["--test", "integration", "--", "--test-threads=1", "rate_limit"],
303 + )
304 + .spawn()
305 + {
306 + Ok(c) => c,
307 + Err(e) => {
308 + return Ok(GateOutcome::failed(GateFailure::SpawnFailed {
309 + message: e.to_string(),
310 + }).with_log_ref(log_ref));
311 + }
312 + };
313 + let ceiling = std::time::Duration::from_secs(ctx.cfg.gate_timeout_secs);
314 + let stream = stream_child_to_live_log(&mut child, ctx.events.clone(), run_id, log_path);
315 + let (stdout_buf, stderr_buf, status) = match tokio::time::timeout(ceiling, stream).await {
316 + Ok(res) => res?,
317 + Err(_elapsed) => {
318 + child.start_kill().ok();
319 + let _ = child.wait().await;
320 + return Ok(GateOutcome::failed(GateFailure::Timeout {
321 + gate: GateKind::HardeningTest,
322 + after_s: started.elapsed().as_secs() as u32,
323 + })
324 + .with_log_ref(log_ref));
325 + }
326 + };
327 + let duration_s = started.elapsed().as_secs() as u32;
328 + if status.success() {
329 + // A filter that matches nothing exits 0, which would make this gate a
330 + // green no-op the day someone renames the tests. Fail closed instead.
331 + if tests_run(&stdout_buf) == 0 {
332 + return Ok(GateOutcome::failed(GateFailure::Unclassified {
333 + legacy_detail: Some(
334 + "hardening_test ran 0 tests: the `rate_limit` filter matched nothing. \
335 + The suite was renamed or moved — this gate is proving nothing."
336 + .into(),
337 + ),
338 + })
339 + .with_log_ref(log_ref));
340 + }
341 + Ok(GateOutcome::passed(PassNote::TestsPassed { duration_s }).with_log_ref(log_ref))
342 + } else {
343 + let failure = classify::classify_cargo_test(&stdout_buf, &stderr_buf);
344 + Ok(GateOutcome::failed(failure).with_log_ref(log_ref))
345 + }
346 + }
347 +
348 + /// Count the tests libtest reports as run, from its `test result: ok. N passed`
349 + /// summary line. Returns 0 when no summary is present, which is itself the
350 + /// "nothing ran" case the caller fails on.
351 + fn tests_run(stdout: &[u8]) -> u32 {
352 + String::from_utf8_lossy(stdout)
353 + .lines()
354 + .filter_map(|l| l.trim().strip_prefix("test result:"))
355 + .filter_map(|rest| rest.split_once(" passed"))
356 + .filter_map(|(head, _)| head.rsplit(' ').find(|t| !t.is_empty())?.parse::<u32>().ok())
357 + .sum()
358 + }
359 +
360 + /// Configure (but don't spawn) `cargo test --release [--features <features>]
250 361 /// <extra>` in `dir`, wired to the scratch DB. Shared by the `--no-run`
251 - /// pre-gate compile and the full test run so both go through one env setup.
362 + /// pre-gate compile and the full test run so both go through one env setup,
363 + /// and by both test gates so they can't drift apart on env.
252 364 ///
253 - /// `--features fast-tests` matches CI (`server/deploy/run-ci.sh`): it relaxes
254 - /// the auth rate-limit burst (5 → 20) and argon2 cost so signup-heavy +
255 - /// lockout workflow tests complete without hitting Governor before the
256 - /// hand-rolled lockout check (documented at `server/src/constants.rs:87`).
257 - fn cargo_test_command(ctx: &GateCtx, dir: &std::path::Path, extra: &[&str]) -> Command {
365 + /// `cargo_test` passes `fast-tests`, matching CI (`server/deploy/run-ci.sh`):
366 + /// it relaxes the auth rate-limit burst (5 → 20) and argon2 cost so
367 + /// signup-heavy + lockout workflow tests complete without hitting Governor
368 + /// before the hand-rolled lockout check (documented at
369 + /// `server/src/constants.rs:87`). `hardening_test` passes no features, which is
370 + /// the whole point of that gate — see `GateKind::HardeningTest`.
371 + fn cargo_test_command(
372 + ctx: &GateCtx,
373 + dir: &std::path::Path,
374 + features: &[&str],
375 + extra: &[&str],
376 + ) -> Command {
258 377 let mut cmd = Command::new("cargo");
259 - cmd.args(["test", "--release", "--features", "fast-tests"])
260 - .args(extra)
378 + cmd.args(["test", "--release"]);
379 + if !features.is_empty() {
380 + cmd.args(["--features", &features.join(",")]);
381 + }
382 + cmd.args(extra)
261 383 .current_dir(dir)
262 384 .stdout(std::process::Stdio::piped())
263 385 .stderr(std::process::Stdio::piped())
@@ -1330,6 +1452,56 @@ mod tests {
1330 1452 use crate::events;
1331 1453 use sqlx::sqlite::SqlitePoolOptions;
1332 1454
1455 + #[test]
1456 + fn tests_run_reads_the_libtest_summary() {
1457 + let out = b"running 6 tests\ntest auth_rate_limit_triggers_on_burst ... ok\n\n\
1458 + test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 118 filtered out\n";
1459 + assert_eq!(tests_run(out), 6);
1460 + }
1461 +
1462 + #[test]
1463 + fn tests_run_sums_across_test_binaries() {
1464 + let out = b"test result: ok. 6 passed; 0 failed\ntest result: ok. 2 passed; 0 failed\n";
1465 + assert_eq!(tests_run(out), 8);
1466 + }
1467 +
1468 + #[test]
1469 + fn tests_run_is_zero_when_the_filter_matched_nothing() {
1470 + // The case hardening_test fails closed on: a filter matching no tests
1471 + // exits 0, so a rename would otherwise make the gate a green no-op.
1472 + let out = b"running 0 tests\n\n\
1473 + test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 124 filtered out\n";
1474 + assert_eq!(tests_run(out), 0);
1475 + }
1476 +
1477 + #[test]
1478 + fn tests_run_is_zero_without_a_summary_line() {
1479 + assert_eq!(tests_run(b"error: could not compile `makenotwork`\n"), 0);
1480 + }
1481 +
1482 + #[tokio::test]
1483 + async fn hardening_test_command_carries_no_features() {
1484 + // The entire point of the gate: production constants, which means no
1485 + // `fast-tests`. A stray feature here silently restores the blind spot.
1486 + let ctx = GateCtx {
1487 + pool: SqlitePoolOptions::new().max_connections(1).connect_lazy("sqlite::memory:").unwrap(),
1488 + cfg: std::sync::Arc::new(crate::config::Config::for_tests()),
1489 + tier: TierId::new("host"),
1490 + version: "0.1.0".parse().unwrap(),
1491 + worktree: std::path::PathBuf::from("/tmp/wt"),
1492 + events: events::channel(),
1493 + nodes: Vec::new(),
1494 + };
1495 + let cmd = cargo_test_command(&ctx, std::path::Path::new("/tmp/wt/server"), &[], &["--test", "integration"]);
1496 + let args: Vec<_> = cmd.as_std().get_args().map(|a| a.to_string_lossy().into_owned()).collect();
1497 + assert!(!args.iter().any(|a| a == "--features"), "hardening_test must pass no features: {args:?}");
1498 + assert!(!args.iter().any(|a| a.contains("fast-tests")), "got: {args:?}");
1499 +
1500 + let fast = cargo_test_command(&ctx, std::path::Path::new("/tmp/wt/server"), &["fast-tests"], &[]);
1501 + let fast_args: Vec<_> = fast.as_std().get_args().map(|a| a.to_string_lossy().into_owned()).collect();
1502 + assert!(fast_args.windows(2).any(|w| w == ["--features", "fast-tests"]), "got: {fast_args:?}");
1503 + }
1504 +
1333 1505 /// Spawn a one-shot loopback server that answers the first connection with
1334 1506 /// `status_line` + a tiny body, then closes. Returns the bound port.
1335 1507 async fn oneshot_http(status_line: &'static str) -> u16 {
@@ -479,7 +479,8 @@ pub(super) async fn unsatisfied_gates(
479 479 // compile error here until its promotion semantics are decided —
480 480 // a transient-`blocked` kind silently falling into "needs a passed
481 481 // row" would be permanently unsatisfiable.
482 - Gate::CargoTest | Gate::MigrationDryRun | Gate::CodeSmoke | Gate::BootSmoke | Gate::NodeHealth => {
482 + Gate::CargoTest | Gate::HardeningTest | Gate::MigrationDryRun | Gate::CodeSmoke
483 + | Gate::BootSmoke | Gate::NodeHealth => {
483 484 // Latest row for this configured gate kind; NULL/missing/any
484 485 // non-'passed' status all count as unsatisfied (fail closed).
485 486 let status: Option<String> = sqlx::query_scalar(
@@ -129,6 +129,7 @@ impl CanaryPolicy {
129 129 #[serde(tag = "kind", rename_all = "snake_case")]
130 130 pub enum Gate {
131 131 CargoTest,
132 + HardeningTest,
132 133 MigrationDryRun,
133 134 CodeSmoke,
134 135 BootSmoke,
@@ -144,6 +145,7 @@ impl Gate {
144 145 pub fn kind(&self) -> GateKind {
145 146 match self {
146 147 Gate::CargoTest => GateKind::CargoTest,
148 + Gate::HardeningTest => GateKind::HardeningTest,
147 149 Gate::MigrationDryRun => GateKind::MigrationDryRun,
148 150 Gate::CodeSmoke => GateKind::CodeSmoke,
149 151 Gate::BootSmoke => GateKind::BootSmoke,
@@ -38,9 +38,18 @@ canary = "sequential"
38 38 # /health. Fast + infra-light (no prod-dump restore), so a green here proves the
39 39 # code is sound and isolates a later cargo_test / migration_dry_run red as an
40 40 # environment problem rather than a code one.
41 + #
42 + # hardening_test is cargo_test's blind spot: cargo_test builds with
43 + # --features fast-tests, which relaxes the auth/sandbox rate limits and argon2
44 + # cost AND #[ignore]s the whole rate-limiting suite, so the gate never touched
45 + # the auth hardening it exists to protect. hardening_test re-runs that suite
46 + # with no features, single-threaded, against production constants. It pays for
47 + # a second compile of the server's test binary; that is the cost of the
48 + # coverage.
41 49 gates = [
42 50 { kind = "code_smoke" },
43 51 { kind = "cargo_test" },
52 + { kind = "hardening_test" },
44 53 { kind = "migration_dry_run" },
45 54 { kind = "boot_smoke" },
46 55 ]
M server/Cargo.lock +35 -151
@@ -483,7 +483,7 @@ dependencies = [
483 483 "async-stripe-shared",
484 484 "bytes",
485 485 "http-body-util",
486 - "hyper 1.10.1",
486 + "hyper",
487 487 "hyper-tls",
488 488 "hyper-util",
489 489 "miniserde",
@@ -998,23 +998,17 @@ dependencies = [
998 998 "aws-smithy-async",
999 999 "aws-smithy-runtime-api",
1000 1000 "aws-smithy-types",
1001 - "h2 0.3.27",
1002 - "h2 0.4.15",
1003 - "http 0.2.12",
1001 + "h2",
1004 1002 "http 1.4.2",
1005 - "http-body 0.4.6",
1006 - "hyper 0.14.32",
1007 - "hyper 1.10.1",
1008 - "hyper-rustls 0.24.2",
1009 - "hyper-rustls 0.27.9",
1003 + "hyper",
1004 + "hyper-rustls",
1010 1005 "hyper-util",
1011 1006 "pin-project-lite",
1012 - "rustls 0.21.12",
1013 - "rustls 0.23.41",
1007 + "rustls",
1014 1008 "rustls-native-certs 0.8.4",
1015 1009 "rustls-pki-types",
1016 1010 "tokio",
1017 - "tokio-rustls 0.26.4",
1011 + "tokio-rustls",
1018 1012 "tower",
1019 1013 "tracing",
1020 1014 ]
@@ -1179,7 +1173,7 @@ dependencies = [
1179 1173 "http 1.4.2",
1180 1174 "http-body 1.0.1",
1181 1175 "http-body-util",
1182 - "hyper 1.10.1",
1176 + "hyper",
1183 1177 "hyper-util",
1184 1178 "itoa",
1185 1179 "matchit",
@@ -3283,25 +3277,6 @@ dependencies = [
3283 3277
3284 3278 [[package]]
3285 3279 name = "h2"
3286 - version = "0.3.27"
3287 - source = "registry+https://github.com/rust-lang/crates.io-index"
3288 - checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
3289 - dependencies = [
3290 - "bytes",
3291 - "fnv",
3292 - "futures-core",
3293 - "futures-sink",
3294 - "futures-util",
3295 - "http 0.2.12",
3296 - "indexmap",
3297 - "slab",
3298 - "tokio",
3299 - "tokio-util",
3300 - "tracing",
3301 - ]
3302 -
3303 - [[package]]
3304 - name = "h2"
3305 3280 version = "0.4.15"
3306 3281 source = "registry+https://github.com/rust-lang/crates.io-index"
3307 3282 checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
@@ -3556,30 +3531,6 @@ dependencies = [
3556 3531
3557 3532 [[package]]
3558 3533 name = "hyper"
3559 - version = "0.14.32"
3560 - source = "registry+https://github.com/rust-lang/crates.io-index"
3561 - checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
3562 - dependencies = [
3563 - "bytes",
3564 - "futures-channel",
3565 - "futures-core",
3566 - "futures-util",
3567 - "h2 0.3.27",
3568 - "http 0.2.12",
3569 - "http-body 0.4.6",
3570 - "httparse",
3571 - "httpdate",
3572 - "itoa",
3573 - "pin-project-lite",
3574 - "socket2 0.5.10",
3575 - "tokio",
3576 - "tower-service",
3577 - "tracing",
3578 - "want",
3579 - ]
3580 -
3581 - [[package]]
3582 - name = "hyper"
3583 3534 version = "1.10.1"
3584 3535 source = "registry+https://github.com/rust-lang/crates.io-index"
3585 3536 checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
@@ -3588,7 +3539,7 @@ dependencies = [
3588 3539 "bytes",
3589 3540 "futures-channel",
3590 3541 "futures-core",
3591 - "h2 0.4.15",
3542 + "h2",
3592 3543 "http 1.4.2",
3593 3544 "http-body 1.0.1",
3594 3545 "httparse",
@@ -3602,32 +3553,17 @@ dependencies = [
3602 3553
3603 3554 [[package]]
3604 3555 name = "hyper-rustls"
3605 - version = "0.24.2"
3606 - source = "registry+https://github.com/rust-lang/crates.io-index"
3607 - checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
3608 - dependencies = [
3609 - "futures-util",
3610 - "http 0.2.12",
3611 - "hyper 0.14.32",
3612 - "log",
3613 - "rustls 0.21.12",
3614 - "tokio",
3615 - "tokio-rustls 0.24.1",
3616 - ]
3617 -
3618 - [[package]]
3619 - name = "hyper-rustls"
3620 3556 version = "0.27.9"
3621 3557 source = "registry+https://github.com/rust-lang/crates.io-index"
3622 3558 checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
3623 3559 dependencies = [
3624 3560 "http 1.4.2",
3625 - "hyper 1.10.1",
3561 + "hyper",
3626 3562 "hyper-util",
3627 - "rustls 0.23.41",
3563 + "rustls",
3628 3564 "rustls-native-certs 0.8.4",
3629 3565 "tokio",
3630 - "tokio-rustls 0.26.4",
3566 + "tokio-rustls",
3631 3567 "tower-service",
3632 3568 "webpki-roots",
3633 3569 ]
@@ -3640,7 +3576,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
3640 3576 dependencies = [
3641 3577 "bytes",
3642 3578 "http-body-util",
3643 - "hyper 1.10.1",
3579 + "hyper",
3644 3580 "hyper-util",
3645 3581 "native-tls",
3646 3582 "tokio",
@@ -3660,12 +3596,12 @@ dependencies = [
3660 3596 "futures-util",
3661 3597 "http 1.4.2",
3662 3598 "http-body 1.0.1",
3663 - "hyper 1.10.1",
3599 + "hyper",
3664 3600 "ipnet",
3665 3601 "libc",
3666 3602 "percent-encoding",
3667 3603 "pin-project-lite",
3668 - "socket2 0.6.4",
3604 + "socket2",
3669 3605 "system-configuration",
3670 3606 "tokio",
3671 3607 "tower-service",
@@ -5686,8 +5622,8 @@ dependencies = [
5686 5622 "quinn-proto",
5687 5623 "quinn-udp",
5688 5624 "rustc-hash 2.1.2",
5689 - "rustls 0.23.41",
5690 - "socket2 0.6.4",
5625 + "rustls",
5626 + "socket2",
5691 5627 "thiserror 2.0.18",
5692 5628 "tokio",
5693 5629 "tracing",
@@ -5707,7 +5643,7 @@ dependencies = [
5707 5643 "rand 0.9.4",
5708 5644 "ring",
5709 5645 "rustc-hash 2.1.2",
5710 - "rustls 0.23.41",
5646 + "rustls",
5711 5647 "rustls-pki-types",
5712 5648 "slab",
5713 5649 "thiserror 2.0.18",
@@ -5725,7 +5661,7 @@ dependencies = [
5725 5661 "cfg_aliases",
5726 5662 "libc",
5727 5663 "once_cell",
5728 - "socket2 0.6.4",
5664 + "socket2",
5729 5665 "tracing",
5730 5666 "windows-sys 0.60.2",
5731 5667 ]
@@ -6026,19 +5962,19 @@ dependencies = [
6026 5962 "futures-channel",
6027 5963 "futures-core",
6028 5964 "futures-util",
6029 - "h2 0.4.15",
5965 + "h2",
6030 5966 "http 1.4.2",
6031 5967 "http-body 1.0.1",
6032 5968 "http-body-util",
6033 - "hyper 1.10.1",
6034 - "hyper-rustls 0.27.9",
5969 + "hyper",
5970 + "hyper-rustls",
6035 5971 "hyper-util",
6036 5972 "js-sys",
6037 5973 "log",
6038 5974 "percent-encoding",
6039 5975 "pin-project-lite",
6040 5976 "quinn",
6041 - "rustls 0.23.41",
5977 + "rustls",
6042 5978 "rustls-native-certs 0.8.4",
6043 5979 "rustls-pki-types",
6044 5980 "serde",
@@ -6046,7 +5982,7 @@ dependencies = [
6046 5982 "serde_urlencoded",
6047 5983 "sync_wrapper",
6048 5984 "tokio",
6049 - "tokio-rustls 0.26.4",
5985 + "tokio-rustls",
6050 5986 "tower",
6051 5987 "tower-http",
6052 5988 "tower-service",
@@ -6069,12 +6005,12 @@ dependencies = [
6069 6005 "cookie_store",
6070 6006 "encoding_rs",
6071 6007 "futures-core",
6072 - "h2 0.4.15",
6008 + "h2",
6073 6009 "http 1.4.2",
6074 6010 "http-body 1.0.1",
6075 6011 "http-body-util",
6076 - "hyper 1.10.1",
6077 - "hyper-rustls 0.27.9",
6012 + "hyper",
6013 + "hyper-rustls",
6078 6014 "hyper-util",
6079 6015 "js-sys",
6080 6016 "log",
@@ -6082,7 +6018,7 @@ dependencies = [
6082 6018 "percent-encoding",
6083 6019 "pin-project-lite",
6084 6020 "quinn",
6085 - "rustls 0.23.41",
6021 + "rustls",
6086 6022 "rustls-pki-types",
6087 6023 "rustls-platform-verifier",
6088 6024 "serde",
@@ -6090,7 +6026,7 @@ dependencies = [
6090 6026 "serde_urlencoded",
6091 6027 "sync_wrapper",
6092 6028 "tokio",
6093 - "tokio-rustls 0.26.4",
6029 + "tokio-rustls",
6094 6030 "tower",
6095 6031 "tower-http",
6096 6032 "tower-service",
@@ -6277,18 +6213,6 @@ dependencies = [
6277 6213
6278 6214 [[package]]
6279 6215 name = "rustls"
6280 - version = "0.21.12"
6281 - source = "registry+https://github.com/rust-lang/crates.io-index"
6282 - checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
6283 - dependencies = [
6284 - "log",
6285 - "ring",
6286 - "rustls-webpki 0.101.7",
6287 - "sct",
6288 - ]
6289 -
6290 - [[package]]
6291 - name = "rustls"
6292 6216 version = "0.23.41"
6293 6217 source = "registry+https://github.com/rust-lang/crates.io-index"
6294 6218 checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f"
@@ -6297,7 +6221,7 @@ dependencies = [
6297 6221 "once_cell",
6298 6222 "ring",
6299 6223 "rustls-pki-types",
6300 - "rustls-webpki 0.103.13",
6224 + "rustls-webpki",
6301 6225 "subtle",
6302 6226 "zeroize",
6303 6227 ]
@@ -6357,10 +6281,10 @@ dependencies = [
6357 6281 "jni",
6358 6282 "log",
6359 6283 "once_cell",
6360 - "rustls 0.23.41",
6284 + "rustls",
6361 6285 "rustls-native-certs 0.8.4",
6362 6286 "rustls-platform-verifier-android",
6363 - "rustls-webpki 0.103.13",
6287 + "rustls-webpki",
6364 6288 "security-framework 3.7.0",
6365 6289 "security-framework-sys",
6366 6290 "webpki-root-certs",
@@ -6375,16 +6299,6 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
6375 6299
6376 6300 [[package]]
6377 6301 name = "rustls-webpki"
6378 - version = "0.101.7"
6379 - source = "registry+https://github.com/rust-lang/crates.io-index"
6380 - checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
6381 - dependencies = [
6382 - "ring",
6383 - "untrusted",
6384 - ]
6385 -
6386 - [[package]]
6387 - name = "rustls-webpki"
6388 6302 version = "0.103.13"
6389 6303 source = "registry+https://github.com/rust-lang/crates.io-index"
6390 6304 checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
@@ -6519,16 +6433,6 @@ dependencies = [
6519 6433 ]
6520 6434
6521 6435 [[package]]
6522 - name = "sct"
6523 - version = "0.7.1"
6524 - source = "registry+https://github.com/rust-lang/crates.io-index"
6525 - checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
6526 - dependencies = [
6527 - "ring",
6528 - "untrusted",
6529 - ]
6530 -
6531 - [[package]]
6532 6436 name = "sec1"
6533 6437 version = "0.7.3"
6534 6438 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6924,16 +6828,6 @@ dependencies = [
6924 6828
6925 6829 [[package]]
6926 6830 name = "socket2"
6927 - version = "0.5.10"
6928 - source = "registry+https://github.com/rust-lang/crates.io-index"
6929 - checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
6930 - dependencies = [
6931 - "libc",
6932 - "windows-sys 0.52.0",
6933 - ]
6934 -
6935 - [[package]]
6936 - name = "socket2"
6937 6831 version = "0.6.4"
6938 6832 source = "registry+https://github.com/rust-lang/crates.io-index"
6939 6833 checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
@@ -7537,7 +7431,7 @@ dependencies = [
7537 7431 "mio",
7538 7432 "pin-project-lite",
7539 7433 "signal-hook-registry",
7540 - "socket2 0.6.4",
7434 + "socket2",
7541 7435 "tokio-macros",
7542 7436 "windows-sys 0.61.2",
7543 7437 ]
@@ -7565,21 +7459,11 @@ dependencies = [
7565 7459
7566 7460 [[package]]
7567 7461 name = "tokio-rustls"
7568 - version = "0.24.1"
7569 - source = "registry+https://github.com/rust-lang/crates.io-index"
7570 - checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
7571 - dependencies = [
7572 - "rustls 0.21.12",
7573 - "tokio",
7574 - ]
7575 -
7576 - [[package]]
7577 - name = "tokio-rustls"
7578 7462 version = "0.26.4"
7579 7463 source = "registry+https://github.com/rust-lang/crates.io-index"
7580 7464 checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
7581 7465 dependencies = [
7582 - "rustls 0.23.41",
7466 + "rustls",
7583 7467 "tokio",
7584 7468 ]
7585 7469
@@ -7952,7 +7836,7 @@ dependencies = [
7952 7836 "httparse",
7953 7837 "log",
7954 7838 "rand 0.8.6",
7955 - "rustls 0.23.41",
7839 + "rustls",
7956 7840 "rustls-native-certs 0.7.3",
7957 7841 "rustls-pki-types",
7958 7842 "sha1 0.10.6",
@@ -9093,7 +8977,7 @@ dependencies = [
9093 8977 "futures",
9094 8978 "http 1.4.2",
9095 8979 "http-body-util",
9096 - "hyper 1.10.1",
8980 + "hyper",
9097 8981 "hyper-util",
9098 8982 "log",
9099 8983 "once_cell",