From 0facfe6f5c1cf020ff4561b808101b08e8604e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sat, 10 Oct 2026 23:41:54 +0200 Subject: [PATCH] v0.31.2 --- AGENTS.md | 2 +- CHANGELOG.md | 5 + Cargo.lock | 428 +++++++++++++----- Cargo.toml | 26 +- Dockerfile | 2 +- README.md | 17 +- ROADMAP.md | 6 + SHA256SUMS | 76 ++-- dev.sh | 1 + docs/API_ACCESS.md | 8 +- docs/TEST_REPORT.md | 14 +- install-debian13.sh | 98 +++- .../routeros-oci-build/Dockerfile.routeros | 2 +- scripts/build/swagger_assets.py | 4 +- scripts/resolve_dependencies_0312.sh | 29 ++ src/admin/openapi.json | 4 +- src/admin/public/tests.rs | 1 + src/admin/tests/part_04.rs | 4 +- src/admin/web/management.js | 16 +- src/admin/web/settings.js | 4 +- src/admin/web/style.css | 118 ++++- src/auth/files.rs | 6 +- src/auth/mod.rs | 18 +- src/backup/archive.rs | 8 +- src/backup/validation.rs | 2 +- src/balance/model.rs | 7 +- src/balance/pool_core.rs | 18 +- src/basic_auth.rs | 13 +- src/cache/tests/cold_tier_tests/part_01.rs | 38 +- src/proxy/upstream_helpers.rs | 33 +- src/proxy/websocket/handshake.rs | 6 +- src/secret_store.rs | 46 +- src/tls/certificates.rs | 4 +- src/tls/tests.rs | 10 +- src/tls/tests/acme.rs | 4 +- src/tls/tests/external_paths.rs | 4 +- src/web_server/tests/part_02.rs | 2 +- tools/checks/check_source.py | 24 +- tools/checks/dependency_upgrade_0312.py | 81 ++++ tools/checks/docs_check.py | 4 +- tools/checks/preflight.py | 4 +- tools/checks/release_smoke.py | 14 +- tools/checks/system_info_0301_ui_test.mjs | 6 +- tools/dev/memory_load.py | 2 +- 44 files changed, 898 insertions(+), 321 deletions(-) mode change 100755 => 100644 install-debian13.sh create mode 100755 scripts/resolve_dependencies_0312.sh create mode 100755 tools/checks/dependency_upgrade_0312.py diff --git a/AGENTS.md b/AGENTS.md index 45f49c0..1ef7325 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ Read README.md, ROADMAP.md and docs/TEST_REPORT.md before making changes. The project name is RustProxy; the Cargo package/binary remains `rust-proxy` for -compatibility. Current source version: 0.31.1. +compatibility. Current source version: 0.31.2. ## Delivery diff --git a/CHANGELOG.md b/CHANGELOG.md index ca83b85..d9c3611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ > Operator-facing release history. Historical engineering notes are kept in > [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md). +## 0.31.2 + +- Refresh core runtime, security, TLS certificate generation, database, Swagger UI and monitoring libraries for current stable versions. Keep persisted data, cache format, configuration, panel behavior, HTTP/3 and proxy routing compatible. +- Move the build toolchain baseline to Rust 1.99. This dependency refresh must pass compilation, runtime and integration checks before production deployment. + ## 0.31.1 - Make mobile and reverse-proxied panel reloads reliable by loading the dashboard JavaScript as one ordered resource, waiting for its initialization before fetching settings, and showing a recoverable error if essential resources are unavailable. Preserve the loading indicator and preferences. diff --git a/Cargo.lock b/Cargo.lock index 97adfae..00107da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,13 +23,28 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" +[[package]] +name = "alloc-no-stdlib" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a" + [[package]] name = "alloc-stdlib" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" dependencies = [ - "alloc-no-stdlib", + "alloc-no-stdlib 2.0.4", +] + +[[package]] +name = "alloc-stdlib" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5c1865780388bfa186411ab5f247819487fc4864c6e9c3106611fa347586e1" +dependencies = [ + "alloc-no-stdlib 3.0.0", ] [[package]] @@ -105,16 +120,55 @@ dependencies = [ [[package]] name = "argon2" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +checksum = "134c52ddac6d63c576bef8168db10c83c49c26444ecbc68060fef078925a901c" dependencies = [ "base64ct", "blake2", - "cpufeatures 0.2.17", + "cpufeatures 0.3.1", "password-hash", ] +[[package]] +name = "asn1-rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.21", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure 0.13.2", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "async-compression" version = "0.4.48" @@ -155,7 +209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", - "base64", + "base64 0.22.1", "bytes", "form_urlencoded", "futures-util", @@ -207,6 +261,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64ct" version = "1.8.3" @@ -231,6 +291,15 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "2.13.2" @@ -239,11 +308,11 @@ checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "blake2" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +checksum = "5b5d4d889834ee8ecfc0f8426ad30faf7cdcb10f741a8e6d7224d95325479f6f" dependencies = [ - "digest", + "digest 0.11.3", ] [[package]] @@ -255,15 +324,35 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "brotli" version = "8.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", + "alloc-no-stdlib 2.0.4", + "alloc-stdlib 0.2.4", + "brotli-decompressor 5.0.3", +] + +[[package]] +name = "brotli" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8b851b75c23ca7873623d612fe49bd1989aeb03d08fb9432187eb253d3d4c6b" +dependencies = [ + "alloc-no-stdlib 3.0.0", + "alloc-stdlib 0.3.0", + "brotli-decompressor 6.0.1", ] [[package]] @@ -272,8 +361,18 @@ version = "5.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", + "alloc-no-stdlib 2.0.4", + "alloc-stdlib 0.2.4", +] + +[[package]] +name = "brotli-decompressor" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941cd9bd4ddab83cb46fa5a2d428f1c857b24ac78cb876cf7beb710840934bd7" +dependencies = [ + "alloc-no-stdlib 3.0.0", + "alloc-stdlib 0.3.0", ] [[package]] @@ -389,6 +488,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "colorchoice" version = "1.0.5" @@ -401,7 +506,7 @@ version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef16c47ba2797aa6a909cc37d39911f3a6743811fe7408ac0b0cc0276b656e9" dependencies = [ - "brotli", + "brotli 8.0.4", "compression-core", "flate2", "memchr", @@ -482,12 +587,44 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctutils" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" +dependencies = [ + "cmov", +] + [[package]] name = "data-encoding" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.5.8" @@ -500,9 +637,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", - "subtle", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", + "ctutils", ] [[package]] @@ -582,9 +729,9 @@ dependencies = [ [[package]] name = "flume" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" dependencies = [ "futures-core", "futures-sink", @@ -599,9 +746,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "form_urlencoded" @@ -781,9 +928,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", @@ -798,11 +945,11 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hashlink" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", ] [[package]] @@ -819,9 +966,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hpx-h3" -version = "2.5.19" +version = "2.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b773a1c31de56cc61c87de6d1db73ed838c1d23d8fad454b302a38ae178ad7" +checksum = "f137abcec3be4197580d1429894c2c33dc438a6d830500dd11bf0a1b958472ff" dependencies = [ "bytes", "fastrand", @@ -879,10 +1026,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "hyper" -version = "1.11.1" +name = "hybrid-array" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" dependencies = [ "atomic-waker", "bytes", @@ -1288,12 +1444,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1322,6 +1497,15 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1365,22 +1549,21 @@ dependencies = [ [[package]] name = "password-hash" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b" dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", + "getrandom 0.4.3", + "phc", ] [[package]] name = "pem" -version = "3.0.6" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120" dependencies = [ - "base64", + "base64 0.23.1", "serde_core", ] @@ -1390,6 +1573,17 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phc" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892" +dependencies = [ + "base64ct", + "ctutils", + "getrandom 0.4.3", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1534,24 +1728,13 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] @@ -1566,16 +1749,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_chacha" version = "0.9.0" @@ -1586,15 +1759,6 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - [[package]] name = "rand_core" version = "0.9.5" @@ -1621,14 +1785,15 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.13.2" +version = "0.14.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8" dependencies = [ "pem", "ring", "rustls-pki-types", "time", + "x509-parser", "yasna", ] @@ -1716,14 +1881,14 @@ dependencies = [ [[package]] name = "rust-proxy" -version = "0.31.1" +version = "0.31.2" dependencies = [ "anyhow", "arc-swap", "argon2", "axum", - "base64", - "brotli", + "base64 0.22.1", + "brotli 9.0.0", "bytes", "clap", "flate2", @@ -1741,7 +1906,7 @@ dependencies = [ "libc", "metrics-process", "quinn", - "rand 0.8.8", + "rand 0.10.3", "rcgen", "regex", "ring", @@ -1776,6 +1941,15 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "1.1.5" @@ -1925,7 +2099,7 @@ checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", ] [[package]] @@ -1936,7 +2110,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", ] [[package]] @@ -2009,9 +2183,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" dependencies = [ "sqlx-core", "sqlx-macros", @@ -2020,12 +2194,13 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", + "cfg-if", "crc", "crossbeam-queue", "either", @@ -2034,12 +2209,11 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "hashlink", "indexmap", "log", "memchr", - "once_cell", "percent-encoding", "serde", "sha2", @@ -2053,9 +2227,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" dependencies = [ "proc-macro2", "quote", @@ -2066,15 +2240,15 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" dependencies = [ + "cfg-if", "dotenvy", "either", "heck", "hex", - "once_cell", "proc-macro2", "quote", "serde", @@ -2089,12 +2263,13 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" dependencies = [ "atoi", "flume", + "form_urlencoded", "futures-channel", "futures-core", "futures-executor", @@ -2104,7 +2279,6 @@ dependencies = [ "log", "percent-encoding", "serde", - "serde_urlencoded", "sqlx-core", "thiserror 2.0.21", "tracing", @@ -2157,6 +2331,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "synstructure" version = "0.14.0" @@ -2170,9 +2355,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.4" +version = "0.39.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" +checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6" dependencies = [ "libc", "memchr", @@ -2271,6 +2456,7 @@ dependencies = [ "powerfmt", "serde_core", "time-core", + "time-macros", ] [[package]] @@ -2279,6 +2465,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.8.4" @@ -2297,9 +2493,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" -version = "1.53.1" +version = "1.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" dependencies = [ "bytes", "libc", @@ -2387,9 +2583,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.11" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +checksum = "08a05a66a4fdd61cbbe0a1d755ffe0ca6aba159dd4820936a0ff8a8278245b9c" dependencies = [ "async-compression", "bitflags", @@ -2397,6 +2593,7 @@ dependencies = [ "futures-core", "http", "http-body", + "percent-encoding", "pin-project-lite", "tokio", "tokio-util", @@ -2562,9 +2759,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" dependencies = [ "try-lock", ] @@ -2875,11 +3072,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] -name = "yasna" -version = "0.5.2" +name = "x509-parser" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror 2.0.21", + "time", +] + +[[package]] +name = "yasna" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" +dependencies = [ + "bit-vec", "time", ] @@ -2903,7 +3119,7 @@ dependencies = [ "proc-macro2", "quote", "syn 3.0.6", - "synstructure", + "synstructure 0.14.0", ] [[package]] @@ -2944,7 +3160,7 @@ dependencies = [ "proc-macro2", "quote", "syn 3.0.6", - "synstructure", + "synstructure 0.14.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c693733..5ccdd64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "rust-proxy" -version = "0.31.1" +version = "0.31.2" edition = "2021" -rust-version = "1.88" +rust-version = "1.99" description = "RustProxy - independent reverse proxy with local administration" publish = false [dependencies] -argon2 = { version = "0.5.3", features = ["std"] } +argon2 = "0.6" sha2 = "0.10" ipnet = "2.11" rpassword = "7.4" @@ -15,7 +15,7 @@ zeroize = "1.8" anyhow = "1.0" libc = "0.2" arc-swap = "1.7" -axum = { version = "0.8", default-features = false, features = ["http1", "http2", "json", "query", "tokio", "ws"] } +axum = { version = "0.8.9", default-features = false, features = ["http1", "http2", "json", "query", "tokio", "ws"] } bytes = "1.10" clap = { version = "4.5", features = ["derive", "env"] } futures-util = "0.3" @@ -23,15 +23,15 @@ fs2 = "0.4" http = "1.3" http-body = "1.0" http-body-util = "0.1" -hyper = { version = "1.6", features = ["client", "server", "http1", "http2"] } +hyper = { version = "1.12.0", features = ["client", "server", "http1", "http2"] } hyper-util = { version = "0.1", features = ["client-legacy", "server-auto", "http1", "http2", "tokio"] } -rand = "0.8" +rand = "0.10" regex = "1.11" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite"] } +sqlx = { version = "0.9", default-features = false, features = ["runtime-tokio", "sqlite"] } subtle = "2.6" -tokio = { version = "1.44", features = ["macros", "rt-multi-thread", "net", "signal", "time", "sync", "fs", "io-util", "process"] } +tokio = { version = "1.53.2", features = ["macros", "rt-multi-thread", "net", "signal", "time", "sync", "fs", "io-util", "process"] } tokio-util = { version = "0.7", features = ["rt", "io"] } tower = { version = "0.5", features = ["util"] } tracing = "0.1" @@ -46,19 +46,19 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["ring", hyper-rustls = { version = "0.27", default-features = false, features = ["ring", "http1", "http2", "tls12"] } rustls-pemfile = "2.2" webpki-roots = "1.0" -rcgen = { version = "0.13.2", default-features = false, features = ["crypto", "pem", "ring"] } +rcgen = { version = "0.14.10", default-features = false, features = ["crypto", "pem", "ring"] } httpdate = "1.0" -tower-http = { version = "0.6.8", default-features = false, features = ["compression-gzip", "compression-br", "compression-zstd", "compression-deflate"] } +tower-http = { version = "0.7.1", default-features = false, features = ["compression-gzip", "compression-br", "compression-zstd", "compression-deflate"] } flate2 = "1.1" -brotli = "8" +brotli = "9" zstd = "0.14" -h3 = { package = "hpx-h3", version = "=2.5.19", features = ["quinn"] } +h3 = { package = "hpx-h3", version = "=2.5.20", features = ["quinn"] } quinn = { version = "0.11.12", default-features = false, features = ["runtime-tokio", "rustls-ring"] } metrics-process = { version = "=2.4.3", default-features = false, features = ["dummy"] } -sysinfo = { version = "=0.38.4", default-features = false, features = ["network", "system"] } +sysinfo = { version = "0.39", default-features = false, features = ["network", "system"] } [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "macos", target_os = "windows"))'.dependencies] systemstat = "=0.2.7" diff --git a/Dockerfile b/Dockerfile index 96c2116..bbd89aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM rust:1.88-trixie AS build +FROM rust:1.99-trixie AS build WORKDIR /build COPY . . RUN if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi \ diff --git a/README.md b/README.md index dcf8dba..eec1213 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# RustProxy 0.31.1 +# RustProxy 0.31.2 **0.31.0 panel/cache additions:** browser-negotiated Zstd/Brotli/gzip for HTML, JS and CSS (Settings → Administration); optional HTTP BAN/PURGE restricted to explicit IP/CIDR allowlist (Settings → Cache, disabled by default); System information Refresh button beside the API badge. See [Settings](docs/SETTINGS.md) and [Cache](docs/CACHE.md). Independent Rust reverse proxy and static Web Server with a dashboard, SQLite configuration, streaming HTTP/TLS/WebSocket forwarding, raw TCP/UDP stream hosts, IP-bound vhosts and backend pools. -**0.31.1 release source.** +**0.31.2 dependency-refresh candidate source (Cargo verification required).** ## Layer-4 streams @@ -13,7 +13,18 @@ Raw TCP/UDP forwarding has its own **Streams** page in the primary navigation, a Named HTTP/HTTPS listener bind addresses use the same chip-style editor as vhost hostnames, including multi-address listeners. -## What changed in 0.31.1 +## What changed in 0.31.2 + +- Refresh Rust dependencies to current stable API families, including SQLx 0.9, Rand 0.10, Argon2 0.6, rcgen 0.14, Tower HTTP 0.7, Brotli 9 and Sysinfo 0.39. Retain SQLite schema 9, serialized settings, TLS/QUIC behavior and administration contracts. +- Raise the minimum compiler version to Rust 1.99 for current dependency requirements, including Docker and RouterOS build images. +- **Release gate open:** the updated dependency graph must be resolved with Cargo; the bundled lockfile is a prior-resolution snapshot and must be refreshed and tested before release. The source package is not production-verified. +- **Complete the dependency upgrade on a Rust 1.99+ build host** with `bash scripts/resolve_dependencies_0312.sh` (registry and the pinned Swagger UI asset must be reachable, or supply an audited local archive). This calls `cargo update`, verifies the refreshed lockfile and runs formatting, clippy, build and tests. Review the lockfile diff and fix any Rust compiler errors before publishing. + +## Previously in 0.31.1 + +- Improve mobile panel startup via an ordered JavaScript resource, versioned URLs and retry of temporary config failures. + +## Earlier panel improvements - Improved mobile panel loading behind reverse proxies: one ordered JavaScript resource, versioned assets and retry of temporary configuration errors. diff --git a/ROADMAP.md b/ROADMAP.md index 96d09d6..73bf805 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,11 @@ # RustProxy roadmap +## 0.31.2 - Dependency updates + +- [x] Prepare stable dependency-family and code API migrations, preserving existing wire/configuration/SQLite formats. +- [ ] Resolve and commit an actual Cargo.lock using Rust 1.99; run format, clippy, tests and release builds. +- [ ] Confirm HTTP/3/QUIC, TLS, SQLite migration/restore, auth, compression, panel and cache BAN/PURGE in real integration tests. + ## 0.31.1 - Mobile panel loading stability - [x] Load dashboard modules as one ordered JavaScript resource and wait for document/module readiness before session initialization. diff --git a/SHA256SUMS b/SHA256SUMS index 3d156c0..5662d82 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -1,18 +1,18 @@ 61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example 9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore -a414a328c21b7c4f81e5448a452258d8069ce40243a41e4e06b8950386494b96 AGENTS.md -15a02c3476a51deade17d25b56c45068ebe50e5f127680f387c98777a8854c5b CHANGELOG.md -e95030916a57faaa3fceb509f4aeec167b789022703f2e0cc09afaa0a071c89b Cargo.lock -a1aba61dca0fa20436ce0651baf6c59fe2602d2e9b4ee48b6900ba0351d7c208 Cargo.toml -75b008f237b2226c62a7721b7901018ce83bfc2726f047a83a17c66fcb799f3b Dockerfile -3a0ee0e90ee5a75cfbc6b5497546861ae56b4fc5f343a5d83f722cdddbd4b8e1 README.md -d1914a8afe65d73620bafb6cb4d9b13206ebde5674f79da77fab5cec6bbe81c7 ROADMAP.md +047139f3c7c40778001602e77ef69302e3bef8f495d48d780953c97c401fa2d4 AGENTS.md +9b42590541917e8d92fa26a5831b9dd43503f179337f8ed6c3a4d7d1de84d3b7 CHANGELOG.md +50238f33805987e7301f9ed06a8fd51b74b0e01330dbbae42c3a2fd8bd0d8e86 Cargo.lock +c6cc53538b89420b82526bc766d73a5fc93a82017f8fdb4bf767335dd613c90a Cargo.toml +25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile +f3ad7c8b4b450bcb32a7c68f90e868726154b3f1ea88c7d9ba5173850334d24e README.md +e2187e12e611638f85bae7808947b46a5de3c12db234ec217767ac8aefd1919a ROADMAP.md da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md 4834c278430ddf3bf842995735195368f1ac2c3b5c1d749df4f9238caf1e2b25 deploy/rustproxy.service -90c5b561079142ff094330066a6a551d7b0e1705012613341ec3defb7b3dc199 dev.sh +31d018c9ef676d397bafa66c5099a57bc41ee56bbb272df59bc521574d920c82 dev.sh 5f51efe621f45512a466fa7f2656867c6502a135ca040f4bfeec06a6919b39b6 docker-agent/README.md 7fee7d51a7deb67e73e53a0bdff3aa4630aea9bab9b23a0f9b8001b2169892b1 docker-agent/certificate.go 5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example @@ -28,7 +28,7 @@ e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/n 875faa3969df9409d79f050a84b4183496f557edda3d34aaec19fe556cf8c471 docs/ACME_PROVIDERS.md ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md 8ded74f422426606238fa9876b49277a7bc56ddd96062a836cdd6ddbf78bd335 docs/API.md -f054206a09bed81018708608cdeecbfd09018ee7cec9d3abc038f8a37d47b815 docs/API_ACCESS.md +6ed12684b970fb7173fc9d1ad1da8ecce784eb38b29b7e6e631309fb25446807 docs/API_ACCESS.md 4c5288c9ed427cfb919bd023ed9a321dcf6d64bc4cad3e1768393356c5d835ba docs/ARCHITECTURE.md e9e2327684a4ae2d1ca4a0b3fb67d0ff83b5a150d38d4de4cfad2c5d3102eab6 docs/CACHE.md 9113f3abbfe81bafc4855d8680df8aa2a381ecc358cc3e1a0f78b6b756421117 docs/CACHE_DIAGNOSTICS.md @@ -65,7 +65,7 @@ a2bb837cdd3559d58158880c1f832badf64035376a3623050bb4e40c2bca104c docs/ROADMAP_H 007537ee971463072b2482dfa4fdcf1955f837ced0b711d4eef3b6267d16d4f6 docs/SECURITY_AUDIT_0.31.0.md 2cd9dadc49a5474e4ab06d5c01fa38c27cab436578cb9c9f327a37b3406d2e13 docs/SETTINGS.md 3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md -14ef0d7935c0ff9a1557ca3a8cac97274be7e2b8e0fd68ea6cce87372989865d docs/TEST_REPORT.md +8ce2502e64a159a23eadebafdc9f0d65d384e8099eb63368748ee7e0269e299d docs/TEST_REPORT.md cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md 751c9b13b47d0959a8575d6e82bdecba988b950a374ba0b084fbba08b4b65ebe docs/TRANSFORM.md 660687fc961f71aae2bdf71595aa47a336fa782f4e78ceee8c1d1e63e46393ee docs/TRANSPORT.md @@ -131,7 +131,7 @@ fde541cd76bc3fa2b6765822bf04baf9f9ea3e4b6e185e76ff467270ddbd945e install-debian 2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 integrations/opnsense-acme/README.md c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 integrations/opnsense-acme/opnsense-rustproxy-cert-deploy.sh 45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 integrations/routeros-oci-build/.env.routeros.example -cdb15e23f04b4a330b7ab8869eb38da72b3c27729fa7fcf164e3d9e8e893a651 integrations/routeros-oci-build/Dockerfile.routeros +11aa102de1089ecc375f20c4bd5420fcfd3b2ef23fe0c8bdf04953fcfaf40ef0 integrations/routeros-oci-build/Dockerfile.routeros 7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e integrations/routeros-oci-build/Dockerfile.routeros.dockerignore cb01fdef286f38b442dd9252c23c4f8e44eb72e9ecdd99adb926e8e9f6318bb1 integrations/routeros-oci-build/README.md 96563a83e87cfb33e238c2a970ce36b1a1b51e91318d09b123c3755002ba2646 integrations/routeros-oci-build/build-routeros-oci.sh @@ -156,7 +156,7 @@ ee50f38c037765fab742e888d4b9fc4d874c31c785c6064d4eca3d2c4d24d944 nagios/check_r 6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues 7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml 2145ad17ae7a823756371ceab6bdbeb332211df93193ffb4e486f7a80c4f29ae scripts/README.md -2044e6d979712457d6ebc3483efa029cc6de22416ee85898d950300015bd815d scripts/build/swagger_assets.py +a1f55872201b5178f8af5e2beee6b3a68e37026831e41426ee62a8c15c1cf333 scripts/build/swagger_assets.py e4d15aa6aa864d04b9b30aca33826b833632af02ca643f8b23e219a5ea1298dd scripts/deploy/deploy.py cc268d9d3dd125c9cbff2522fe3137a561fb4fcbd5ee461d70ef2e3a8241cdb1 scripts/deploy/deploy_core.py 8c7da13e95bcddd2bd1b4f1b22ade1c5ac1ecf54448601daa8416905d4239751 scripts/deploy/deploy_runtime.py @@ -164,6 +164,7 @@ a641bd35400c56f3b68b0a92e97b602d06090f03972ca8f01fa744e90dd97403 scripts/deploy 2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migration/migrate_npm.py a459d95bbb0076f33ade8b273b813308167f9d8d45242b309a2d18320ca0093c scripts/migration/migrate_npm_api.py 31ccad3ec3c880119f3b7002f0c6652f8b43634edad8ab3763cb0e4497d0bb1f scripts/migration/migrate_npm_core.py +308ceeca51ddfb43db8be8286d5286aff1a44979f84af87d9346fb4176499c0c scripts/resolve_dependencies_0312.sh 20124173452b4514bad8850c1637569a9a191882478451e549bd16b432605fc1 scripts/runtime/acme_provider.py e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/runtime/acme_rfc2136.py 22e880e98c7b9d700c7a4c02b9b1d543e5dbd1f4ecdca42813a1083165b32a19 scripts/version.py @@ -188,14 +189,14 @@ bc9eb32f0d2785bac122d5eff44742cd24306c503daa065564383d46416b2211 src/admin/acce d7aa68a78a4cb5a61342d92c1f3209af230d0f9ca3fea1115f67078372e6f37c src/admin/file_logs_io.rs 899e87a73bfe46c4078a46f9e1eb1c17b384472e8a30d2bab5486872b43fc809 src/admin/logging_api.rs 58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs -2f7e9ff687147cbf751f88828479e970727594d7c21726b1470e8c59ea40289e src/admin/openapi.json +8c58aec76463fc512ee87a8cceff7b905488c6f70185c055c496d235badea6d7 src/admin/openapi.json 0d1a5324e8ad32f5ab0861b722663f50f066e256bc5fd5dcced6a63f2bd399d1 src/admin/operations.rs 2b0ec6df0ed9e1bdc5c9e4ff1758b59dd14abc150f98365ac3edcf4672908e48 src/admin/panel_assets.rs 75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs 7c113cd9d8642a808c73ebee24e695f7a318995e39dc2b81e747d2b2246e277f src/admin/preferences.rs d9e90166d2199152585a5208feedfc1dd1395dc32ddbd2a094ae4a1aee45e115 src/admin/preferences_columns.rs 9baf3c0ec4c543bda51605930ac57d6425c5e03e06d29ff755a7ce3ff611aeaf src/admin/public.rs -5135f743f628103deda9f50629065ca343f7a5bf872dcd3cc0033ac2971e361b src/admin/public/tests.rs +c9f952bec99dfe1d5a60998a38bd177f82f54de567230413e0c322ee561eba7c src/admin/public/tests.rs 6125419b099cf4d330767bc07424d52a2b400f273d31084c058c43a72a6427b7 src/admin/refresh_preferences.rs f99655cf19b04b1eb7fb49d12bfd2ba1ef2c095b81e80ef46cfddfacefc91936 src/admin/router.rs 8708427263cd4caf8454b9115e89acb09172483a090c6b0bd43811dd9e52c7f6 src/admin/session.rs @@ -207,7 +208,7 @@ cadd7dca29b9aad8c77c7bc23dd249ea37a15adfa4d636a8ef00273a91ce35dc src/admin/tail 5df98246c46135a176b4b8b705c92fd328719e82eb7342f088256abb976d5ae7 src/admin/tests/part_01.rs 6f8c1e30babb2cc4cb7accf4bfa28338651a469b16341f6e7eaec881dd9abf2b src/admin/tests/part_02.rs 34cf08cafb63cf7a8a6336e5c81fc99abfce8669662e387a3702c3a532be8056 src/admin/tests/part_03.rs -a1e14bfad1ba42045421fb761e0cc490cc30530409b22416db7e727548c06b93 src/admin/tests/part_04.rs +2137a53991c9337988545c4472b4ffcb9a111cac9c460ebbec7e51c0be8f5630 src/admin/tests/part_04.rs 5f0062abc42a3b81c79088dc0824ab4c81a22c6ea765e77d2527d07d13bffd58 src/admin/tests/part_05.rs 311c4ad16b3ad5b295a339241afd4ca8ea0d3c7f54f609c27518b4de1b4a4b71 src/admin/tests/part_06.rs 5fa006413fd93529ac9aaa4ffb28bf6b0b88d0a55a0bf0c49576db4a06fe9fc3 src/admin/tests/part_07.rs @@ -255,28 +256,28 @@ a6e7bbff4fd2d50ecf759ed28161a672d7b483af2b35c9f912746a7a06a9f476 src/admin/web/ f2689283cfe43d6aae911b0df778a69335089be230fbfc6de5dcce1801c38427 src/admin/web/vhosts/render.js 91e3417fba7e8f59023cd64813befccc8341741cd4aa53dfa1ec68a03c7f4632 src/admin/web/web-server-profiles.js de3326d4fae254eb0b8c190f02e762f4094dcba86bfdcb24c5fb472676ebcc4b src/admin/web/web-server.js -98c00d99822f9be54fc58afb6d4b577901712effd2c5eddce014312681864625 src/auth/files.rs +76b0d92e4360e06ae97f87c0f6ddde4962c44efdeec9727e5b9a720598302891 src/auth/files.rs b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs -97b5a060f24a102bbaa2f406426e985236c0d719b8038db210c25b34fcd8e923 src/auth/mod.rs +105cb934956457a68adc14f2c64569d9d1d40b8da0a7908a9a59fc2153a939c9 src/auth/mod.rs c96a87e82ff0c1c9ce3d0cd5135903bb53af78d1c4fb6ebfe5fea503bf5644ea src/auth/store.rs 745226cabb007819370fc6aa680995a03a8a13fa057dacfab0fcc21d0c43bbe4 src/auth/tests.rs 09c3a622a7d2169e49e3369ea5f0f816eab2f717eaf1675c625d518c8f34d367 src/backup.rs -ebce56a154c57bf20d6618de161359c7c49392d7d24e8d23428b7d78a7a060c1 src/backup/archive.rs +76aa2afe9e7302be39b3512f30d0448d9134300f8ae930d64ada40cf3ad4a8a3 src/backup/archive.rs 732cd4f9ed38ae69f8bbae61b1db8baaa6f86b07ca21c41f24a206a30f0a96fc src/backup/ownership.rs 076350506215faec3cfe7d9de4e60baf29672cc22a41eabe601dcc510c257799 src/backup/snapshot.rs a00571b409d305af86ab2a57a25434b5ab58706772df440d017fc1fec097f7b4 src/backup/tests.rs -b2ce83a48736c082e6b820e4e363cca7a5018f3ae2a1ce5adfd6af1a03d62598 src/backup/validation.rs +f5afae218f3a6f91bc2f1f5a90094630ce5b7ca574de61807b5a317b13937ec2 src/backup/validation.rs 426a946b2c9e244f09f6cbc5f9664b8ad0737529a9bb5a3d549d27196a1f8043 src/balance.rs 3d4b3986f54bed99212063b6d135d40a202d556a3e4d253e24c74be4cb9e8ebc src/balance/health.rs a1e5d43f5417d71f3974812cb539c0932f206e2290090917f067494f3587fc72 src/balance/lease_runtime.rs -a57f352aad3b5a9a6debb59625864808f0a0ff7b11db83b47d6825a5932ca16b src/balance/model.rs -7628aa331c9dbb02650392afeb65838cc23e32939c002d5d0488a8f3b8c11bd4 src/balance/pool_core.rs +46d739f14b510b35aaccf18fb9f21593c92d8b8d3ea47828271574df5ed008e5 src/balance/model.rs +2a6b20ffc2b326cb2380beb0805b52181306d3b9cc57330d67930000535c03f9 src/balance/pool_core.rs 477132dd381e23f1be197c32ff58a0925f41d3e6aa63fe77c10973cefb2b76f6 src/balance/pool_selection.rs 519cb798040ac9a65d9ebe43ae11ef729bd78c5c4a5c756490458add4b6fdc24 src/balance/pool_view.rs 7d94d1bd4eb698e0588b822d93e56e3dc5594c46edac4aa9713f148893ebe6d5 src/balance/tests.rs 81141aedaec495183b88a4f6362a71f56ee14cbfa83d71fce51d8abed8df3f5d src/balance/tests/part_01.rs 490b38a8b1472dbd9fcb823ec0ed0e92498fb9528b982bcee340c8579e89ae36 src/balance/tests/part_02.rs -ccb3e1988d6ae5825450e894eea896d53ae9fac150d0175ea5e742413e55105a src/basic_auth.rs +5049d2bd01a9c866f3bad2288d8d594df96bba6045dfe15cd22b4173562b7388 src/basic_auth.rs 9a8858b7ff461a4d961ba701ca3ed6eaec15c3f7f05eef8e0e30edc63617605a src/basic_auth/tests.rs 7def03ace07dc38a4806277d75b6ce673d33ddaaf1c4855fa0a3372be1cf9489 src/cache/cold.rs a9eeb93519aae2de7a1282296ced91d2eb8307cc966f8fbf6085af0fd21b0ad2 src/cache/cold/cleanup.rs @@ -309,7 +310,7 @@ d426d341a7678f8ec70406d86052c97d0f49804bbcd8402951103d61a735b9da src/cache/poli 7e2bcfbb2b57732cdc704d9df7a9298fb80e735e37984431bc936630e41e7f8a src/cache/range.rs 7ad96b3baae36ec49c90d03b380e749944fb3d54abb045c90db3a35f6b89b1c1 src/cache/tests.rs 13dab7b7a587cfc146826a92ab94ec9e04f7e500b7a84e678d77226829844923 src/cache/tests/cold_tier_tests.rs -b4f609ee900c556b613136bbd3fce11c242a415d277ac3d25bdcb72275b66832 src/cache/tests/cold_tier_tests/part_01.rs +9cd6a8407888c8f9cbf7652774186d1ca588d2d78308750e8aa1316b4e3e5cb9 src/cache/tests/cold_tier_tests/part_01.rs f43aa6c231f2c85ee3390dddab74aab2eea2a3f874acb35f145c23866266cb52 src/cache/tests/cold_tier_tests/part_02.rs 22cb9bf20a0d25bee30c1e2213dfe8278ad7d0d5d294275d47d4a6ff9ac646ab src/cache/tests/dashboard_tests.rs d7a02eb7bad37e6a5524600aabb5e60ccf50e26c7a487e2806891c9d8acd48f3 src/cache/tests/http_invalidation_tests.rs @@ -411,9 +412,9 @@ c438066616527a3108f2ad53e3698ae9b2a80961714394cdd0a450c92f99d992 src/proxy/head cc682f0d5754e0b806a4eb5950e4cc00611aca5ce4668414fddef1d692489805 src/proxy/tests/authority_tests.rs d134b0eb617f82f9971b3f3461379ea37c57bd0079bf54748d0c434b2ef1b475 src/proxy/tests/http_invalidation_tests.rs e80c7fe44abb7607c64ea02d56542753dc25290b3bcf4c4d45b75f2db6ca90d0 src/proxy/types.rs -26afb4dcda99a54abdd85f7950ecac6d608eca78067323f695a43382d21fa2fb src/proxy/upstream_helpers.rs +55c44a5d0c6588beea67439d62e3c9f57041e4d07cb1f8cf293efd856ceb8eff src/proxy/upstream_helpers.rs 69f775f6746b4c6d729dc10961dbef54168846f4ce9cf7fe453ab5a2b879c9b8 src/proxy/websocket.rs -016a60b2471edd3df9bbe9f92a9cebea11047178b83ff0e3b2351c12814a4939 src/proxy/websocket/handshake.rs +d866e7e72dee6ff54c2418c34e9899cbcd7e4e027d9cbe2b625044dc9062e4ee src/proxy/websocket/handshake.rs 7e9c4d4c207badabf90630223964bb2ad641f06d4149843cab751f192b8b4adf src/proxy/websocket/tests.rs a44421714aab37f7ceda19e3ec2512c645d2a3ce5dcb3da5bc7a0d87a442d6a8 src/proxy/websocket/tunnel.rs 15fabd38576b61f650c039acdbf7ac5a5bd24330062475b94315285b2dc258b6 src/proxy/websocket/validation.rs @@ -428,7 +429,7 @@ d0939e1b0829c25356a0b67ecb3a178c74936dd07baa15fee24eb32544db6cad src/runtime.rs e90df3fff8a25465425b099ae358d19085809e56d948ba47e01e53f8b93d5dd0 src/runtime/tests.rs 0046525317ffbd5d3b34845656b0f58d9d31f38b3d8acd71e90d94bd7cdfb8ab src/runtime/types.rs 88bc546fdda20d3f1f8f5e7d7872ede1fa334b8bb5102c729bd72f56e3fb94b9 src/runtime/updates.rs -b4c17b304166592e63c81884076589fbaf725c3f57043f8924c46167b061ac5b src/secret_store.rs +b5062082d7fc7c9b08533283e1f0a841f85a42917b6ebf135760b04023884d9a src/secret_store.rs 6dca8da072a3bd610704a391bb6e25c9042971f4ebc60edb47665ce1d74cbc58 src/secret_store/tests.rs 6fa1d8c352bce7a16ff0208bc4cdbe80404715df0aff4ed26910a5df57cbbcd4 src/security.rs 0e5fc2aff856bf6fd959cba12b5ddd1e3b3cbeb0fbaadde906c811cdfafdaae7 src/security/tests.rs @@ -451,12 +452,12 @@ d9123c623e2a97d09e41edaa29993b4b2c8cba39a3abdf2b48a205aa9a9c2939 src/system_sta 7467d83066bb6986d8c9103597f7a7617ac677579ed46d573c573ff6ca57bbd9 src/tinyauth.rs 0e1d87554db55e724f30c41ec4a5c80bdb8fc9c8cba8700ac509864d4abf8871 src/tinyauth/tests.rs 1084cccd9de28acd6cb0f0813de7c0953910f51726f73f4acdb2e3ada4593976 src/tls.rs -5919db3296523dd0b7a8e0cccbe44266500a6a5183a7a08cc195d7f9847886dc src/tls/certificates.rs +4972a436f8878dec5f9eb8ff17eb82670b0bfd9e94e821c516575479f5fc1919 src/tls/certificates.rs 436575144aa352498a4696f12c2258bef3a898f3f50e6fbc6d67c3b93afa099f src/tls/external_paths.rs 3e6947dde167f82a5efc407ccf9bd4c9f5b26cc8633d1418a10284bc1a1503d4 src/tls/server_config.rs -893d1077fc0d9a08598596a1d3f4bde7019357fac27ebe13f52c11a4859a1d25 src/tls/tests.rs -e0ab65406911d4b95cb26594df65779a88d9011d5b11adb22c1a28831cda3826 src/tls/tests/acme.rs -cfab6b61f0d121a15d6f78943091c770cb5a2a023d68d66be3c0228f9c8adf9c src/tls/tests/external_paths.rs +31c0f51a3a0bab433f438c3ba1b1cdaf3c63fae6086e7d99b3464a537f75a5e1 src/tls/tests.rs +2af2164ecdf2b7fd9eef7c6e358dec053dfac9aedaa501dbb75df742d18638b2 src/tls/tests/acme.rs +93b359e9defd62506729bc90e56c96783e2f14dd477dd67f337ca83149da7168 src/tls/tests/external_paths.rs 10c4a187e3aeb0c72a8c8968ea0cc8bfae4e3cb2f3d834ebd3da1056c4171ec5 src/tls/types.rs c2f802d95da4332fb8480e83f0c7502334457b3408141c1c4516c9ba320a0463 src/traffic.rs 36301bcbd20b557dd3d58e77a9175f3581947f577c0bb0b972899cec6786b03f src/traffic/tests.rs @@ -509,7 +510,7 @@ c5cb561b0f276e8082f904b9446fa5885f6b3c0608f25ea2ef425061e9f7f6b4 src/web_server 518856a221161430cb0ab3e411fa81cab07e1a6e52edd470f026d25c5307943e src/web_server/serve.rs 63d71a336187779e9cbc1cc6afa065c5fa9472835a8c2cc15dbf4527279d0c90 src/web_server/tests.rs 9426ad19fd6234e944d6e122a995d2cf8ebe4177d27e42fd157f64d05abd1e23 src/web_server/tests/part_01.rs -6f0b68d732fa0d1385744ef7ac1b36d558f9532f9ef1dea44d3dcc47d149840a src/web_server/tests/part_02.rs +5591a0b994d4392265799f881790d15abd8b6263b5475a84766455d66572bfcf src/web_server/tests/part_02.rs 8f9a0e2b0a69a1f2b8cf7505a654606150da4a53bc8c09d1d6c4b8751185b134 src/web_server/tests/part_03.rs b25b6e0082b5153272119c7faba9d6f2ccdd288ce5c066526431c56954c66c4c src/web_server/tests/part_04.rs f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json @@ -522,23 +523,24 @@ b04362ad082a32c9ccc4cf38c46f44d71347adecf7aba3224b3af3ea4872e3d0 tools/README.m fd082812411cff7afb71b2289a6f1a469b8d3c8a7cfdcdd9e567a2a4baf81be4 tools/checks/admin_surface_audit_0310.py 412255d383fcc8306c6086784fb416d9814e3ec790e440a0927431ca615f3411 tools/checks/backend_cache_0297_ui_test.mjs 2a493dbce2991a23d807c01952f58608a364f541560836b4cb385e75e108e9e7 tools/checks/backend_health_0300_ui_test.mjs -daa718b6a3b519234cbb3614bd321f5b4b4ebd704bc9fb1c1dc18ba742759203 tools/checks/check_source.py +4bb77602254ff8ed1673b6d31518c77ae40c9b9c44e85ac436b93a300115ca18 tools/checks/check_source.py 0b583ee59dcccc35b15ef4e211d7e6546a8aaa7b0286ceab03c0b0dc084c1e07 tools/checks/dashboard_boot_0311_ui_test.mjs 5f798699502c7a18e7b45aa84bdcf7ad0ccdb77f11cf2003527c97f0391c7abe tools/checks/dashboard_startup_0310_ui_test.mjs -7428c63a80be49c57b6aad15738679175bad24c4271964cf01a67805ec22c0eb tools/checks/docs_check.py +3a5703cd3e0e511e0ea8da0c7802ccdc5cf53289f9b9169b9cf96ea5209b4a57 tools/checks/dependency_upgrade_0312.py +5d4fa0b66ae05241236551c07c900ad0c437bd10e34406b17a8a002188caebc3 tools/checks/docs_check.py e33a9cc51d58bc92a637c0e6a1c88cbd24ba4605be55748212e296c3c35a4060 tools/checks/high_traffic_memory_model.py 4f7821e7e25b09ce251d25a0b3fb4d8ce8396c36a5ed971c2637aeb325425909 tools/checks/langs_check.py f8f87ba891338b565249d02c3cf5f02cec44e606b4205de99fcbea1eb2c24d57 tools/checks/memory_contracts.py 5b7901747d879e12a39ed82b6efc513bcdddd15d508b5828210d2c83398fb6a4 tools/checks/panel_cache_http_0310_ui_test.mjs -274019cdcdccff5a5c7f76107daf096d111df1834b7280ccc3da9c564b65a04e tools/checks/preflight.py +8c5c64b681de7f72f01b414f2c77d1f9a44c13b47972d8252a0d392fd71c6ab4 tools/checks/preflight.py 3485681d369b5f486dfc2d14660f19b4098df670bafd9a296cc9dabe68003a51 tools/checks/refresh_0310_ui_test.mjs -c8b9216936300dac4ebb582a70efe2286124f0d14e501a6c22fc10cc2caaddbb tools/checks/release_smoke.py +db468bc0ad68704e70a15d7dda894d1913db1e2ce94e04a84b20c7dadfaa80a6 tools/checks/release_smoke.py f69845bc26ec33160fc756953991cf32aa8ad6a7fb4a131caa2093db91e783ef tools/checks/rust_module_contracts.py ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed tools/checks/rust_source.py adf84a983e0dbba228e47d71ec2d5c9964f7ca9989d5139de58f96ee961b035a tools/checks/security_contracts.py 23b9250245e6bec5ef728c73cee4c88f64924afc3597f8a9b58a1924235df98e tools/checks/security_dependency_contracts.py 5c1d428cbda5f08b8d6847732672e45e9da96b545337334e473986e119197de2 tools/checks/source_module_contracts.py -b78240c3ccead8c023609fc71560be7ce2f2aa5d31c78059a24cfe4b0412d9c5 tools/checks/system_info_0301_ui_test.mjs +49c1fe96340d97c326943826dd8b3331b884c19f4a9631beaa820f01a12108e0 tools/checks/system_info_0301_ui_test.mjs 543a488951ea6ec8fe06e04f168ccc4d4f2af3cfc281c9a728ab7bd87377fa98 tools/checks/system_info_no_subprocess_contracts.py 614ed0c775e32ec5bfb4ee75aaa90206a10da0f43659747a7ec76fd93e33767a tools/checks/version_contracts.py 2020e171be1a044f7ca2ab83af7604a014c36fe4ff04791d0620ccbf1bf8e000 tools/checks/vhost_origin_errors_0297_ui_test.mjs @@ -547,7 +549,7 @@ b78240c3ccead8c023609fc71560be7ce2f2aa5d31c78059a24cfe4b0412d9c5 tools/checks/s 0398892be30fe416a00578915c588f360fd93244e3af77ece834e560e547d1c8 tools/checks/web_server_contracts.py 35f76060e585a96c623ef83bd5f771969f5be16a0e1ae626a3092d7458b84e84 tools/checks/web_service_ui_test.mjs adaa1ed9e5958739df40190055c780c80d0a8e270ec8dc105ed3fa2dbb8fb74f tools/checks/www_redirect_ui_test.mjs -c8bf1c3ef876b4f87840414f5de19c183309550572437cca7ba1fe02df496462 tools/dev/memory_load.py +05382366fd801a17f8d7c1166fd4e64b8aa70e465cb69121519b239ebf8ed55f tools/dev/memory_load.py d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a tools/dev/mock_auth.py 2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 tools/dev/mock_server.py 538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba tools/dev/test_compression.py diff --git a/dev.sh b/dev.sh index 2a4b1bf..a0741a1 100755 --- a/dev.sh +++ b/dev.sh @@ -101,6 +101,7 @@ if [[ "$mode" == "test" ]]; then python3 tools/checks/security_contracts.py python3 tools/checks/admin_surface_audit_0310.py python3 tools/checks/security_dependency_contracts.py + python3 tools/checks/dependency_upgrade_0312.py if command -v node >/dev/null 2>&1; then node tools/checks/web_service_ui_test.mjs node tools/checks/backend_cache_0297_ui_test.mjs diff --git a/docs/API_ACCESS.md b/docs/API_ACCESS.md index 3a22a1c..c5e0758 100644 --- a/docs/API_ACCESS.md +++ b/docs/API_ACCESS.md @@ -132,7 +132,7 @@ promise to erase browser/OS screenshots or data intentionally downloaded by an a ## First build / offline assets (fixed in 0.7.2) No `SWAGGER_UI_DOWNLOAD_URL` environment variable or `.cargo/config.toml` is required. -The default is Swagger UI **5.32.11**, selected by `scripts/build/swagger_assets.py` and +The default is Swagger UI **5.33.1**, selected by `scripts/build/swagger_assets.py` and called from this application's `build.rs`. The former `utoipa-swagger-ui` adapter is removed: there is no separate dependency build silently choosing its older historical browser default. Direct Cargo builds using `--manifest-path` use the @@ -160,10 +160,10 @@ release binaries**, served behind the existing admin guard. No runtime download, CDN, public asset endpoint or alternate authentication mechanism was introduced. An offline or GitHub-restricted build can use a reviewed local copy of the same -GitHub **tag archive**, with its original `swagger-ui-5.32.11/` directory structure: +GitHub **tag archive**, with its original `swagger-ui-5.33.1/` directory structure: ```bash -export SWAGGER_UI_DOWNLOAD_URL=file:///srv/build-assets/swagger-ui-5.32.11.zip +export SWAGGER_UI_DOWNLOAD_URL=file:///srv/build-assets/swagger-ui-5.33.1.zip # Optional: require the archive digest independently verified by your build team. # export SWAGGER_UI_ARCHIVE_SHA256=<64-hex-digit-verified-digest> bash dev.sh check @@ -237,4 +237,4 @@ unchanged. No configuration schema migration is needed for existing header rules - Swagger UI configuration: https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/ - Cargo configuration discovery: https://doc.rust-lang.org/cargo/reference/config.html - Cargo build scripts: https://doc.rust-lang.org/cargo/reference/build-scripts.html -- Pinned browser release: https://github.com/swagger-api/swagger-ui/tree/v5.32.11 +- Pinned browser release: https://github.com/swagger-api/swagger-ui/tree/v5.33.1 diff --git a/docs/TEST_REPORT.md b/docs/TEST_REPORT.md index 54248ab..0b91192 100644 --- a/docs/TEST_REPORT.md +++ b/docs/TEST_REPORT.md @@ -1,4 +1,16 @@ -# Verification report - RustProxy 0.31.1 +# Verification report - RustProxy 0.31.2 + +## 0.31.2 — stable library upgrade candidate (2026-10-10) + +- **Source basis:** shipped RustProxy 0.31.1 archive, without local Git history; no confirmation of current remote `master`. +- **Changed dependency branches:** Rust 1.99 (manifest and Docker builders), SQLx 0.9, Argon2 0.6, Rand 0.10, rcgen 0.14, Tower HTTP 0.7, Brotli 9 and sysinfo 0.39; HTTP/3 hpx-h3 is pinned to reviewed 2.5.20. Existing SemVer-compatible crates remain range-defined for a full `cargo update`. Embedded Swagger UI is pinned to upstream 5.33.1. +- **Code adaptations:** secure RNG API, Argon2 PHC generation/verification and TLS certificate signing key representation. No intended changes to JSON schema, SQLite schema 9, cache format, credential storage, TLS policy, or proxy traffic processing. +- **Executed locally:** `./dev.sh test` passed all offline code/API/SQLite/upgrade/security/translation/UI/Node/documentation checks; then exited 127 because Rust/Cargo is absent. `cd docker-agent && go test ./...` passed. `python3 tools/checks/dependency_upgrade_0312.py` reports PENDING for the old lockfile. Python compilation and Bash syntax checks passed. This does not prove a Rust build. +- **CRITICAL:** This sandbox has no Rust/Cargo/rustfmt and cannot reach the crates registry. The existing `Cargo.lock` carries the previous dependency graph: only the root package version has been updated, with all third-party resolved entries left untouched. It is **NOT the resolved lockfile for 0.31.2** and `--locked` is expected to reject it until `cargo update` runs. Do not treat this source package as a built or production-accepted release. Do not fake checksums or mark compilation/tests as passing. +- **Required on a Rust 1.99 build host:** run `cargo update`, review and commit the newly resolved `Cargo.lock`, `cargo metadata --locked`, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `./dev.sh check`, `./dev.sh test`, `./dev.sh release`, plus `cargo audit` when installed. Fix all compilation/API errors revealed by real Cargo before approving a release. +- **Runtime regression gate:** real H1/H2/h2c/H3 QUIC, WebSocket, TLS and certificates, SQLite upgrade/backup/restore with preexisting data, sessions/Basic Auth/API security, SSE and panel on Safari behind reverse proxy, cache HOT/COLD and BAN/PURGE, and zstd/br/gzip negotiation. Compare load/RSS and avoid overwriting production data. + +# Historical verification records (0.31.1 and earlier) ## 0.31.1 — mobile/reverse-proxy dashboard startup (2026-10-10) diff --git a/install-debian13.sh b/install-debian13.sh old mode 100755 new mode 100644 index ba21c49..aac20da --- a/install-debian13.sh +++ b/install-debian13.sh @@ -27,6 +27,9 @@ Environment: RUSTPROXY_ALLOW_REMOTE_ADMIN Set to 1 only to permit plain-HTTP non-loopback admin on first install RUSTPROXY_PREFIX Managed install prefix (default /opt/rustproxy) RUSTPROXY_BUILD_JOBS Cargo jobs for low-memory builds (default 1) + +The bootstrap installs/updates the builder Rust toolchain only when required +by Cargo.toml (and respects rust-toolchain.toml, if present). USAGE exit 0 ;; @@ -101,11 +104,94 @@ run_builder() { runuser -u "$builder" -- env HOME="$builder_home" PATH="$builder_home/.cargo/bin:/usr/local/bin:/usr/bin:/bin" "$@" } -if ! run_builder bash -lc 'command -v cargo >/dev/null 2>&1'; then - run_builder bash -lc "wget -qO- --https-only --secure-protocol=TLSv1_2 https://sh.rustup.rs | sh -s -- -y --profile minimal" +run_builder git clone --depth 1 "$REPO_URL" "$work/RustProxy" + +# The builder, not root, owns the toolchain. Never update a system-wide +# apt-managed compiler or change the user's default rustup toolchain. +# rustup is needed even when an older system-installed cargo exists. +if ! run_builder bash -c 'command -v rustup >/dev/null 2>&1'; then + echo "Installing rustup for build user: $builder" + run_builder env RUSTUP_INIT_SKIP_PATH_CHECK=yes bash -c \ + 'set -o pipefail; wget -qO- --https-only --secure-protocol=TLSv1_2 https://sh.rustup.rs | sh -s -- -y --profile minimal --no-modify-path --default-toolchain none' fi -run_builder git clone --depth 1 "$REPO_URL" "$work/RustProxy" +# Read the actual requirements from the checked-out sources, not a hardcoded +# Rust version. The project may inherit rust-version from [workspace.package]. +# The channel from rust-toolchain.toml (or legacy rust-toolchain) takes precedence. +rust_settings=$(python3 - "$work/RustProxy" <<'PY_RUST' +import pathlib +import re +import sys +import tomllib + +repo = pathlib.Path(sys.argv[1]) +with (repo / "Cargo.toml").open("rb") as f: + manifest = tomllib.load(f) +minimum = manifest.get("package", {}).get("rust-version") +if isinstance(minimum, dict) and minimum.get("workspace"): + minimum = manifest.get("workspace", {}).get("package", {}).get("rust-version") +if not isinstance(minimum, str) or not re.fullmatch(r"[0-9]+\.[0-9]+(?:\.[0-9]+)?", minimum): + sys.exit("Cargo.toml must declare a valid package rust-version for safe automatic toolchain updates") + +channel = "stable" +toolchain_toml = repo / "rust-toolchain.toml" +toolchain_legacy = repo / "rust-toolchain" +if toolchain_toml.exists(): + with toolchain_toml.open("rb") as f: + channel = tomllib.load(f).get("toolchain", {}).get("channel", "") +elif toolchain_legacy.exists(): + channel = toolchain_legacy.read_text(encoding="utf-8").strip() +if not isinstance(channel, str) or not re.fullmatch( + r"(?:stable|beta|nightly|[0-9]+\.[0-9]+(?:\.[0-9]+)?(?:-[A-Za-z0-9_\-]+)?)", channel +): + sys.exit(f"Unsupported rust-toolchain channel: {channel!r}") +print(f"{minimum}|{channel}") +PY_RUST +) +required_rust=${rust_settings%%|*} +build_toolchain=${rust_settings#*|} + +# Compare numerically (1.100 > 1.99); do not use lexicographic ordering. +rust_version_at_least() { + python3 - "$1" "$2" <<'PY_VERSION' +import sys + +def version(value): + parts = value.split(".") + if len(parts) not in (2, 3) or not all(part.isdecimal() for part in parts): + sys.exit(2) + return tuple(map(int, (parts + ["0"] * (3 - len(parts))))) + +sys.exit(0 if version(sys.argv[1]) >= version(sys.argv[2]) else 1) +PY_VERSION +} + +# rustup run is a non-updating check: it fails when the channel is missing. +installed_rust=$(run_builder rustup run "$build_toolchain" rustc --version 2>/dev/null | awk '$1 == "rustc" { print $2; exit }' || true) +if [[ -z "$installed_rust" ]] || ! rust_version_at_least "$installed_rust" "$required_rust"; then + echo "Rust toolchain '$build_toolchain' needs installation/update (installed: ${installed_rust:-none}; required: >= $required_rust)." + case "$build_toolchain" in + stable|beta|nightly) + run_builder rustup update "$build_toolchain" + ;; + *) + # A fixed toolchain must be respected. Do not silently override a pin + # that is older than the manifest's minimum supported Rust version. + if [[ -n "$installed_rust" ]]; then + echo "Pinned toolchain '$build_toolchain' is older than rust-version $required_rust; fix rust-toolchain.toml in the repository." >&2 + exit 1 + fi + run_builder rustup toolchain install "$build_toolchain" --profile minimal + ;; + esac + installed_rust=$(run_builder rustup run "$build_toolchain" rustc --version | awk '$1 == "rustc" { print $2; exit }') +fi +if ! rust_version_at_least "$installed_rust" "$required_rust"; then + echo "Toolchain '$build_toolchain' provides Rust $installed_rust, but Cargo.toml requires $required_rust. Aborting before installation/update." >&2 + exit 1 +fi +run_builder rustup run "$build_toolchain" cargo --version +echo "Building with Rust $installed_rust ($build_toolchain); required >= $required_rust." # Keep source builds reliable on small Debian VPSes. The repository release # profile may use LTO/codegen settings that can make rustc exceed RAM limits. @@ -119,7 +205,11 @@ if [[ "$ALLOW_REMOTE_ADMIN" != "0" && "$ALLOW_REMOTE_ADMIN" != "1" ]]; then exit 1 fi -run_builder bash -lc "cd '$work/RustProxy' && CARGO_BUILD_JOBS='$BUILD_JOBS' CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=8 cargo build --release --locked" +run_builder env RUSTUP_TOOLCHAIN="$build_toolchain" \ + CARGO_BUILD_JOBS="$BUILD_JOBS" \ + CARGO_PROFILE_RELEASE_LTO=false \ + CARGO_PROFILE_RELEASE_CODEGEN_UNITS=8 \ + bash -c 'cd "$1" && exec cargo build --release --locked' _ "$work/RustProxy" cd "$work/RustProxy" if [[ "$MODE" == install ]]; then diff --git a/integrations/routeros-oci-build/Dockerfile.routeros b/integrations/routeros-oci-build/Dockerfile.routeros index 8bf9fcb..2cc41a2 100644 --- a/integrations/routeros-oci-build/Dockerfile.routeros +++ b/integrations/routeros-oci-build/Dockerfile.routeros @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM rust:1.88-trixie AS build +FROM rust:1.99-trixie AS build WORKDIR /build COPY . . diff --git a/scripts/build/swagger_assets.py b/scripts/build/swagger_assets.py index 17911a2..d8ad374 100755 --- a/scripts/build/swagger_assets.py +++ b/scripts/build/swagger_assets.py @@ -22,7 +22,7 @@ from urllib.request import HTTPRedirectHandler, Request, build_opener, url2pathn import zipfile import zlib -VERSION = "5.32.11" +VERSION = "5.33.1" DOWNLOAD_URL = f"https://github.com/swagger-api/swagger-ui/archive/refs/tags/v{VERSION}.zip" PREFIX = f"swagger-ui-{VERSION}/" MAX_ARCHIVE = 64 * 1024 * 1024 @@ -32,7 +32,7 @@ ASSETS = { } LICENSE_MEMBERS = ("LICENSE", "NOTICE", "dist/swagger-ui-bundle.js.LICENSE.txt") HELP = ("Install Python 3.10+ and allow HTTPS access to github.com/codeload.github.com, " - "or set SWAGGER_UI_DOWNLOAD_URL=file:///absolute/path/swagger-ui-5.32.11.zip " + "or set SWAGGER_UI_DOWNLOAD_URL=file:///absolute/path/swagger-ui-5.33.1.zip " "to a reviewed copy of the same tag archive. See docs/API_ACCESS.md.") diff --git a/scripts/resolve_dependencies_0312.sh b/scripts/resolve_dependencies_0312.sh new file mode 100755 index 0000000..17c2e2c --- /dev/null +++ b/scripts/resolve_dependencies_0312.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Run on a clean Rust 1.99+ build machine with registry and Swagger asset access. +set -euo pipefail +ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" +if ! command -v cargo >/dev/null 2>&1 || ! command -v rustc >/dev/null 2>&1; then + printf 'Rust 1.99+ and Cargo are required; no lockfile was regenerated.\n' >&2 + exit 127 +fi +python3 tools/checks/preflight.py +if [[ "$(python3 scripts/version.py)" != '0.31.2' ]]; then + printf 'Expected RustProxy 0.31.2 sources.\n' >&2 + exit 1 +fi +printf 'Toolchain: '; rustc --version +cargo update +python3 tools/checks/dependency_upgrade_0312.py --require-lock +cargo metadata --locked --format-version 1 --no-deps >/dev/null +cargo fmt --all -- --check +cargo clippy --locked --all-targets -- -D warnings +./dev.sh check +./dev.sh test +./dev.sh release +if cargo audit --version >/dev/null 2>&1; then + cargo audit +else + printf 'NOTICE: cargo-audit is unavailable; install it and run cargo audit before release.\n' >&2 +fi +printf 'Source and locked Rust build complete. Still required: live H1/H2/H3, auth/TLS, SQLite, cache, UI and performance acceptance.\n' diff --git a/src/admin/openapi.json b/src/admin/openapi.json index a030d43..a78cad2 100644 --- a/src/admin/openapi.json +++ b/src/admin/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "RustProxy Administration API", - "version": "0.31.1", + "version": "0.31.2", "description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust." }, "servers": [ @@ -10893,4 +10893,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/admin/public/tests.rs b/src/admin/public/tests.rs index 86ec0d3..a5a1fc8 100644 --- a/src/admin/public/tests.rs +++ b/src/admin/public/tests.rs @@ -1,5 +1,6 @@ use super::*; use flate2::read::GzDecoder; +use http::header; use http_body_util::BodyExt; use std::io::Read; diff --git a/src/admin/tests/part_04.rs b/src/admin/tests/part_04.rs index fe5428c..09e4e2b 100644 --- a/src/admin/tests/part_04.rs +++ b/src/admin/tests/part_04.rs @@ -284,7 +284,7 @@ let first_body = json!({ "certificate_id": "opnsense-cert", "cert_pem": first.cert.pem(), - "key_pem": first.key_pair.serialize_pem(), + "key_pem": first.signing_key.serialize_pem(), }) .to_string(); @@ -326,7 +326,7 @@ let second_body = json!({ "certificate_id": "opnsense-cert", "cert_pem": second.cert.pem(), - "key_pem": second.key_pair.serialize_pem(), + "key_pem": second.signing_key.serialize_pem(), }) .to_string(); let response = router.oneshot(deploy(second_body)).await.unwrap(); diff --git a/src/admin/web/management.js b/src/admin/web/management.js index c845a6a..95899ca 100644 --- a/src/admin/web/management.js +++ b/src/admin/web/management.js @@ -46,14 +46,16 @@ interception.append(toggle, codesLabel, hint); parent.append(interception); const errors = errorsEditor(parent, values.errors); parent.append(label("p", "pagesSafetyHint", "fine")); - return { read: () => { - const selectedTokens = enabled.checked ? statuses.value.split(/[\s,]+/).filter(Boolean) : []; - const selected = selectedTokens.map(Number); - if (enabled.checked && (!selected.length || selectedTokens.some(token => !/^\d{3}$/.test(token)) || selected.length > codes.length || new Set(selected).size !== selected.length || selected.some(code => !codes.includes(code)))) { - throw new Error(t("vhostInterceptOriginInvalid")); + return { + read: () => { + const selectedTokens = enabled.checked ? statuses.value.split(/[\s,]+/).filter(Boolean) : []; + const selected = selectedTokens.map(Number); + if (enabled.checked && (!selected.length || selectedTokens.some(token => !/^\d{3}$/.test(token)) || selected.length > codes.length || new Set(selected).size !== selected.length || selected.some(code => !codes.includes(code)))) { + throw new Error(t("vhostInterceptOriginInvalid")); + } + return { response: response.read(), fallback: fallback.read(), errors: errors.read(), intercept_statuses: selected }; } - return { response: response.read(), fallback: fallback.read(), errors: errors.read(), intercept_statuses: selected }; - } }; + }; } let global = null; function render() { if (!envelope || settingsDirty.has("pages-form")) return; const values = envelope.config.pages || {}, parent = $("global-pages-fields"); parent.replaceChildren(); const ip = pageEditor(parent, "pageDirectIp", values.direct_ip), unknown = pageEditor(parent, "pageUnknownHost", values.unknown_host), errors = errorsEditor(parent, values.errors); global = { read: () => ({ direct_ip: ip.read(), unknown_host: unknown.read(), errors: errors.read() }) }; } diff --git a/src/admin/web/settings.js b/src/admin/web/settings.js index 0406ed8..6890247 100644 --- a/src/admin/web/settings.js +++ b/src/admin/web/settings.js @@ -214,7 +214,7 @@ curl --fail --show-error --request POST \ $("admin-certificate").replaceChildren(generated, ...(envelope.config.tls?.certificates || []).map(cert => { const o = document.createElement("option"); o.value = cert.id; o.textContent = cert.id; return o; })); $("admin-certificate").value = current; $("admin-hosts").value = (admin.allowed_hosts || []).join("\n"); $("admin-session-days").value = Number(admin.session_days || 30); - for (const codec of ["zstd", "br", "gzip"]) $( `admin-compression-${codec}` ).checked = admin.panel_compression?.[codec] !== false; + for (const codec of ["zstd", "br", "gzip"]) $(`admin-compression-${codec}`).checked = admin.panel_compression?.[codec] !== false; } const listenLocked = !!adminStatus?.listen_locked; $("admin-listen").disabled = listenLocked; $("admin-listen-lock-note").hidden = !listenLocked; @@ -300,7 +300,7 @@ curl --fail --show-error --request POST \ }, "prometheus"); bindSettingsForm("administration-form", next => { const locked = !!latestStatus?.administration?.listen_locked; - next.administration = { listen: locked ? null : $("admin-listen").value.trim(), tls: $("admin-tls").checked, certificate_id: $("admin-tls").checked ? ($("admin-certificate").value || null) : null, allowed_hosts: $("admin-hosts").value.split(/[\n,]+/).map(v => v.trim()).filter(Boolean), session_days: Number($("admin-session-days").value), panel_compression: Object.fromEntries(["zstd", "br", "gzip"].map(codec => [codec, $( `admin-compression-${codec}` ).checked])) }; + next.administration = { listen: locked ? null : $("admin-listen").value.trim(), tls: $("admin-tls").checked, certificate_id: $("admin-tls").checked ? ($("admin-certificate").value || null) : null, allowed_hosts: $("admin-hosts").value.split(/[\n,]+/).map(v => v.trim()).filter(Boolean), session_days: Number($("admin-session-days").value), panel_compression: Object.fromEntries(["zstd", "br", "gzip"].map(codec => [codec, $(`admin-compression-${codec}`).checked])) }; }, "administration"); bindSettingsForm("tls-policy-form", next => { if (!$("tls-policy-12").checked && !$("tls-policy-13").checked) throw fieldError($("tls-policy-12"), t("tlsPolicyVersionRequired")); diff --git a/src/admin/web/style.css b/src/admin/web/style.css index 3d9b6e5..00084bf 100644 --- a/src/admin/web/style.css +++ b/src/admin/web/style.css @@ -252,15 +252,21 @@ html.app-startup-failed .app-startup-recovery { } @keyframes app-startup-spin { - to { transform: rotate(360deg) } + to { + transform: rotate(360deg) + } } @keyframes app-startup-reveal { - to { visibility: visible } + to { + visibility: visible + } } @media (prefers-reduced-motion: reduce) { - .app-startup-spinner { animation: none } + .app-startup-spinner { + animation: none + } } body { @@ -5416,7 +5422,9 @@ dialog .window-heading { } @media (max-width: 650px) { - .vh-www-pair-directions { grid-template-columns: minmax(0, 1fr) } + .vh-www-pair-directions { + grid-template-columns: minmax(0, 1fr) + } } .vh-www-choice { @@ -7701,9 +7709,17 @@ body[data-density="compact"] .page-footer { } /* The legend keys must use the same theme/preset palette as the SVG strokes. */ -.stat-chart .stat-chart-legend .legend-h1 i { background: var(--traffic-h1); } -.stat-chart .stat-chart-legend .legend-h2 i { background: var(--traffic-h2); } -.stat-chart .stat-chart-legend .legend-h3 i { background: var(--traffic-h3); } +.stat-chart .stat-chart-legend .legend-h1 i { + background: var(--traffic-h1); +} + +.stat-chart .stat-chart-legend .legend-h2 i { + background: var(--traffic-h2); +} + +.stat-chart .stat-chart-legend .legend-h3 i { + background: var(--traffic-h3); +} .stat-chart .stat-chart-legend b { font-family: var(--mono); @@ -9946,6 +9962,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { width: 100% } } + .listener-active-summary { margin-top: 10px; padding: 10px 12px; @@ -10002,7 +10019,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { margin-top: 10px } -.location-header-editor .vh-header-rule-head > button { +.location-header-editor .vh-header-rule-head>button { margin-left: auto; flex: 0 0 auto } @@ -10013,7 +10030,6 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { } } -/* 0.29.7: PHP-FPM local/profile toggle rows with accessible info popovers. */ .php-switch-row { display: flex; align-items: center; @@ -10023,6 +10039,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { min-height: 30px; margin: 4px 0; } + .php-switch-row .php-switch-label { display: inline-flex !important; flex: 0 1 auto; @@ -10032,14 +10049,26 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { margin: 0 !important; line-height: 1.5; } + .php-switch-row .php-switch-label input[type="checkbox"] { position: static; flex: 0 0 var(--ui-check-size); margin: 0; } -.php-switch-row .php-switch-label span { min-width: 0; } -.php-switch-row .php-field-info { flex: 0 0 auto; } -.php-switch-row .php-field-info .metric-info-popover { right: auto; left: 0; } + +.php-switch-row .php-switch-label span { + min-width: 0; +} + +.php-switch-row .php-field-info { + flex: 0 0 auto; +} + +.php-switch-row .php-field-info .metric-info-popover { + right: auto; + left: 0; +} + .php-reference pre { overflow-x: auto; white-space: pre; @@ -10049,6 +10078,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { background: var(--panel2); font: 12px/1.6 var(--mono); } + .php-profile-help-divider { width: 100%; height: 0; @@ -10056,12 +10086,27 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { border: 0; border-top: 1px solid var(--line); } -.php-origin-errors { grid-column: 1 / -1; display: grid; gap: 8px; padding: 10px 0; border-top: 1px solid var(--line); } -.php-origin-errors > label { max-width: 430px; } -.php-origin-errors > .check { margin: 0 !important; } -.php-origin-errors .fine { margin: 0; } -/* Keep the Overview refresh action on the same row as the page title. */ +.php-origin-errors { + grid-column: 1 / -1; + display: grid; + gap: 8px; + padding: 10px 0; + border-top: 1px solid var(--line); +} + +.php-origin-errors>label { + max-width: 430px; +} + +.php-origin-errors>.check { + margin: 0 !important; +} + +.php-origin-errors .fine { + margin: 0; +} + .page-intro-copy, .page-intro-title-row { min-width: 0; @@ -10090,30 +10135,47 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { font-size: clamp(18px, 5vw, 24px); letter-spacing: -.6px } + .page-intro-title-row .overview-refresh { padding: 7px 9px; font-size: 11px } } -/* Keep the system API indicator next to its action, aligned as one unit. */ -.system-info-heading-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; } -.system-info-heading-actions .pill { white-space: nowrap; } +.system-info-heading-actions { + display: flex; + align-items: center; + gap: 8px; + margin-left: auto; + flex-shrink: 0; +} + +.system-info-heading-actions .pill { + white-space: nowrap; +} -/* Contextual, keyboard-accessible curl examples next to the cache allowlist. */ .cache-maintenance-help { display: flex; align-items: center; gap: 8px; position: relative; } -.cache-maintenance-help .subheading { margin: 0; } -.cache-maintenance-info { position: static; flex-shrink: 0; } + +.cache-maintenance-help .subheading { + margin: 0; +} + +.cache-maintenance-info { + position: static; + flex-shrink: 0; +} + .cache-maintenance-info .metric-info-popover { max-height: min(60vh, 420px); overflow-y: auto; width: min(440px, calc(100vw - 56px)); } + .cache-maintenance-info .metric-info-popover code { display: block; white-space: pre-wrap; @@ -10122,5 +10184,11 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline { font-size: 11px; line-height: 1.55; } -.cache-maintenance-info .metric-info-popover .fine + code { margin-top: 6px; } -.cache-maintenance-info .metric-info-popover code + .fine { margin-top: 8px; } + +.cache-maintenance-info .metric-info-popover .fine+code { + margin-top: 6px; +} + +.cache-maintenance-info .metric-info-popover code+.fine { + margin-top: 8px; +} \ No newline at end of file diff --git a/src/auth/files.rs b/src/auth/files.rs index 9eba5ea..edcfff2 100644 --- a/src/auth/files.rs +++ b/src/auth/files.rs @@ -1,7 +1,7 @@ //! Local filesystem authority. The process owner can initialize or recover the sole admin. use anyhow::{ensure, Context, Result}; use fs2::FileExt; -use rand::{rngs::OsRng, RngCore}; +use rand::{rngs::SysRng, TryRng}; use std::{ fs::{self, File, OpenOptions}, io::Write, @@ -86,7 +86,9 @@ pub fn load_or_create_setup_token(dir: &Path) -> Result { return load_setup_token(dir); } let mut bytes = [0u8; 32]; - OsRng.fill_bytes(&mut bytes); + SysRng + .try_fill_bytes(&mut bytes) + .expect("secure operating system random source unavailable"); let token: String = bytes.iter().map(|b| format!("{b:02x}")).collect(); let mut file = match private_options().create_new(true).open(&path) { Ok(file) => file, diff --git a/src/auth/mod.rs b/src/auth/mod.rs index ae7050a..d7d14ef 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -5,7 +5,7 @@ mod store; use crate::{db::Store, runtime::ApiError}; use anyhow::{ensure, Context, Result}; use argon2::{ - password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, + password_hash::{phc::PasswordHash, PasswordHasher, PasswordVerifier}, Algorithm, Argon2, Params, Version, }; pub use files::{ @@ -13,7 +13,7 @@ pub use files::{ prepare_directory, try_lock_directory, }; use http::{header::COOKIE, HeaderMap, StatusCode}; -use rand::{rngs::OsRng, RngCore}; +use rand::{rngs::SysRng, TryRng}; use sha2::{Digest, Sha256}; use std::{ path::PathBuf, @@ -56,7 +56,9 @@ pub fn now() -> i64 { } pub fn random_secret() -> String { let mut bytes = [0_u8; 32]; - OsRng.fill_bytes(&mut bytes); + SysRng + .try_fill_bytes(&mut bytes) + .expect("secure operating system random source unavailable"); bytes.iter().map(|b| format!("{b:02x}")).collect() } pub fn secret_hash(secret: &str) -> String { @@ -118,17 +120,15 @@ pub fn validate_password(value: &str) -> Result<()> { } pub(crate) fn hash_sync(password: &str) -> Result { let params = Params::new(19_456, 2, 1, None).context("Argon2 parameters")?; - let salt = SaltString::generate(&mut OsRng); Ok(Argon2::new(Algorithm::Argon2id, Version::V0x13, params) - .hash_password(password.as_bytes(), &salt)? + .hash_password(password.as_bytes())? .to_string()) } fn verify_sync(password: &str, encoded: &str) -> bool { - PasswordHash::new(encoded).ok().is_some_and(|hash| { - Argon2::default() - .verify_password(password.as_bytes(), &hash) + PasswordHash::new(encoded).is_ok() + && Argon2::default() + .verify_password(password.as_bytes(), encoded) .is_ok() - }) } pub async fn hash_password_offline(password: String) -> Result { validate_password(&password)?; diff --git a/src/backup/archive.rs b/src/backup/archive.rs index 1c5791a..035b03f 100644 --- a/src/backup/archive.rs +++ b/src/backup/archive.rs @@ -1,7 +1,7 @@ use anyhow::{bail, ensure, Context, Result}; use argon2::{Algorithm, Argon2, Params, Version}; use base64::Engine as _; -use rand::{rngs::OsRng, RngCore}; +use rand::{rngs::SysRng, TryRng}; use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -231,8 +231,8 @@ fn derive_key(password: &str, salt: &[u8]) -> Result> { fn new_crypto(password: &str) -> Result { let mut salt = [0u8; 16]; let mut nonce_prefix = [0u8; 4]; - OsRng.fill_bytes(&mut salt); - OsRng.fill_bytes(&mut nonce_prefix); + SysRng.try_fill_bytes(&mut salt).expect("secure operating system random source unavailable"); + SysRng.try_fill_bytes(&mut nonce_prefix).expect("secure operating system random source unavailable"); let header = CryptoHeader { format_version: FORMAT_VERSION, kdf: "Argon2id".into(), @@ -395,7 +395,7 @@ fn decrypt_archive(input: &Path, output: &Path, password: &str) -> Result Result> { ensure!(master.len() == 32, "invalid secret store master key"); let mut nonce_bytes = [0u8; 12]; - OsRng.fill_bytes(&mut nonce_bytes); + SysRng.try_fill_bytes(&mut nonce_bytes).expect("secure operating system random source unavailable"); let cipher = cipher(backup_key)?; let mut encrypted = Zeroizing::new(master.to_vec()); cipher diff --git a/src/backup/validation.rs b/src/backup/validation.rs index 90f3b68..51ddc43 100644 --- a/src/backup/validation.rs +++ b/src/backup/validation.rs @@ -426,6 +426,6 @@ pub async fn restore(archive: &Path, data_dir: &Path, password: &str) -> Result< pub fn generated_password() -> String { let mut bytes = [0u8; 32]; - OsRng.fill_bytes(&mut bytes); + SysRng.try_fill_bytes(&mut bytes).expect("secure operating system random source unavailable"); base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes) } diff --git a/src/balance/model.rs b/src/balance/model.rs index 712c671..9da2b48 100644 --- a/src/balance/model.rs +++ b/src/balance/model.rs @@ -3,7 +3,7 @@ use crate::{config::CompiledBackend, vhost::PoolMember}; use anyhow::{ensure, Result}; use axum::body::Body; use http::{HeaderMap, HeaderValue, Request}; -use rand::{distributions::Alphanumeric, Rng}; +use rand::{distr::Alphanumeric, RngExt}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use sha2::{Digest, Sha256}; @@ -218,7 +218,10 @@ impl Default for Settings { impl Settings { /// Resolve active health per member; a disabled monitor never probes. pub fn health_for(&self, member: &PoolMember) -> HealthCheck { - let mut health = member.health_check.clone().unwrap_or_else(|| self.health_check.clone()); + let mut health = member + .health_check + .clone() + .unwrap_or_else(|| self.health_check.clone()); if health.host.is_none() && health.mode == HealthMode::Http { health.host = self.health_check.host.clone(); } diff --git a/src/balance/pool_core.rs b/src/balance/pool_core.rs index 1925605..c50651f 100644 --- a/src/balance/pool_core.rs +++ b/src/balance/pool_core.rs @@ -24,8 +24,8 @@ impl Pool { state: Arc::new(BackendState::new(settings.health_for(&definition).mode)), definition, backend, - token: rand::thread_rng() - .sample_iter(&Alphanumeric) + token: rand::rng() + .sample_iter(Alphanumeric) .take(32) .map(char::from) .collect(), @@ -53,14 +53,16 @@ impl Pool { }; let same_origin = serde_json::to_vec(&member.definition.backend()).ok() == serde_json::to_vec(&old.definition.backend()).ok(); - let same_health = - serde_json::to_vec(&( - self.settings.health_for(&member.definition), - self.settings.passive_for(&member.definition), - )).ok() == serde_json::to_vec(&( + let same_health = serde_json::to_vec(&( + self.settings.health_for(&member.definition), + self.settings.passive_for(&member.definition), + )) + .ok() + == serde_json::to_vec(&( previous.settings.health_for(&old.definition), previous.settings.passive_for(&old.definition), - )).ok(); + )) + .ok(); let same_clients = member.backend.client.reusable_with(&old.backend.client) && member .backend diff --git a/src/basic_auth.rs b/src/basic_auth.rs index bf62c3d..2e7994f 100644 --- a/src/basic_auth.rs +++ b/src/basic_auth.rs @@ -2,7 +2,7 @@ //! ACLs run first; credentials are never cached with content or written to logs. use anyhow::{ensure, Result}; use argon2::{ - password_hash::{PasswordHash, PasswordVerifier}, + password_hash::{phc::PasswordHash, PasswordVerifier}, Argon2, }; use axum::response::{IntoResponse, Response}; @@ -88,8 +88,10 @@ impl Settings { ); let hash = PasswordHash::new(&user.password_hash) .map_err(|_| anyhow::anyhow!("basic.invalid_hash"))?; + // Match the former 16-character minimum for the PHC-encoded salt. + // phc 0.6 Salt::len() now measures decoded bytes instead. ensure!( - hash.salt.is_some_and(|s| s.as_str().len() >= 16) + hash.salt.is_some_and(|s| s.to_salt_string().len() >= 16) && hash.hash.is_some_and(|h| h.len() == 32), "basic.invalid_hash" ); @@ -221,11 +223,10 @@ impl Compiled { .map_err(|_| denied(StatusCode::SERVICE_UNAVAILABLE, None))?; let verified = tokio::task::spawn_blocking(move || { let _permit = permit; - PasswordHash::new(&encoded).ok().is_some_and(|hash| { - Argon2::default() - .verify_password(password.as_bytes(), &hash) + PasswordHash::new(&encoded).is_ok() + && Argon2::default() + .verify_password(password.as_bytes(), encoded.as_str()) .is_ok() - }) }) .await .unwrap_or(false); diff --git a/src/cache/tests/cold_tier_tests/part_01.rs b/src/cache/tests/cold_tier_tests/part_01.rs index 9542e76..f621890 100644 --- a/src/cache/tests/cold_tier_tests/part_01.rs +++ b/src/cache/tests/cold_tier_tests/part_01.rs @@ -256,23 +256,29 @@ async fn cold_restart_preserves_transformed_validators_stale_metadata_and_no_tra restart_shutdown.clone(), ); assert_eq!(restarted.view().cold_entries, 2); - { + // COLD entries intentionally keep validators and headers on disk to bound RAM. + // Inspect them through the same verified metadata loader used on COLD hits. + let (key, transformed) = { let cold = restarted.cold.lock().unwrap(); - let transformed = cold - .entries - .values() - .find(|entry| entry.path == "/transformed.json") - .expect("transformed cold entry"); - assert_eq!(transformed.stale_while_revalidate, 30); - assert_eq!(transformed.stale_if_error, 90); - assert_eq!(transformed.origin_etag.as_ref().unwrap(), "\"origin-v1\""); - assert_eq!( - transformed.origin_last_modified.as_ref().unwrap(), - "Mon, 26 Jan 2026 23:11:40 GMT" - ); - assert_eq!(transformed.headers["etag"], "\"rustproxy-representation\""); - assert!(transformed.transformed); - } + cold.entries + .iter() + .find(|(_, entry)| entry.path == "/transformed.json") + .map(|(key, entry)| (*key, entry.clone())) + .expect("transformed cold entry") + }; + assert_eq!(transformed.stale_while_revalidate, 30); + assert_eq!(transformed.stale_if_error, 90); + assert!(transformed.has_validator); + assert!(transformed.transformed); + let metadata = read_cold_metadata(&key, &transformed) + .await + .expect("persisted transformed metadata"); + assert_eq!(metadata.origin_etag.as_ref().unwrap(), "\"origin-v1\""); + assert_eq!( + metadata.origin_last_modified.as_ref().unwrap(), + "Mon, 26 Jan 2026 23:11:40 GMT" + ); + assert_eq!(metadata.headers["etag"], "\"rustproxy-representation\""); let transformed_hit = restarted .get_tiered(&transformed_ticket, &settings) diff --git a/src/proxy/upstream_helpers.rs b/src/proxy/upstream_helpers.rs index 78ffa66..c8a723a 100644 --- a/src/proxy/upstream_helpers.rs +++ b/src/proxy/upstream_helpers.rs @@ -1,10 +1,11 @@ fn request_id_hash(sequence: u64) -> String { - use rand::RngCore; + use rand::TryRng; use sha2::{Digest, Sha256}; let mut nonce = [0u8; 32]; - let mut rng = rand::rngs::OsRng; - rng.fill_bytes(&mut nonce); + let mut rng = rand::rngs::SysRng; + rng.try_fill_bytes(&mut nonce) + .expect("secure operating system random source unavailable"); let mut hash = Sha256::new(); hash.update(sequence.to_be_bytes()); hash.update(nonce); @@ -13,9 +14,18 @@ fn request_id_hash(sequence: u64) -> String { fn should_replace_error_page(response: &Response, minimal_welcome: bool) -> bool { !minimal_welcome - && response.extensions().get::().is_none() - && response.extensions().get::().is_none() - && response.extensions().get::().is_none() + && response + .extensions() + .get::() + .is_none() + && response + .extensions() + .get::() + .is_none() + && response + .extensions() + .get::() + .is_none() && response .extensions() .get::() @@ -298,14 +308,19 @@ async fn observe(State(state): State>, mut request: Request, next: Next let global_compression = &snapshot.envelope.config.compression; let mut compression = global_compression.clone(); if let Some(frontend) = frontend_context.as_ref() { - compression = frontend.compression.resolve(global_compression, &compression); + compression = frontend + .compression + .resolve(global_compression, &compression); } if let Some(route) = effective_route_context { if let crate::config::CompiledSource::WebServer(service) = &route.source { - compression = service.compression.resolve(global_compression, &compression); + compression = service + .compression + .resolve(global_compression, &compression); } } - response = crate::compression::encode(response, &original_headers, &method, &compression).await; + response = + crate::compression::encode(response, &original_headers, &method, &compression).await; if let Some(encoding) = response .extensions() .get::() diff --git a/src/proxy/websocket/handshake.rs b/src/proxy/websocket/handshake.rs index 3d3609e..9706a58 100644 --- a/src/proxy/websocket/handshake.rs +++ b/src/proxy/websocket/handshake.rs @@ -13,7 +13,7 @@ use http_body::{Body as HttpBody, Frame, SizeHint}; use http_body_util::BodyExt; use hyper::upgrade::{OnUpgrade, Upgraded}; use hyper_util::rt::TokioIo; -use rand::{rngs::OsRng, RngCore}; +use rand::{rngs::SysRng, TryRng}; use serde::Serialize; use sha1::{Digest, Sha1}; use std::{ @@ -186,7 +186,7 @@ pub fn validate_h3(request: &Request) -> Result { "WebSocket version must be 13" ); let mut nonce = [0u8; 16]; - OsRng.fill_bytes(&mut nonce); + SysRng.try_fill_bytes(&mut nonce).expect("secure operating system random source unavailable"); let key = STANDARD.encode(nonce); Ok(Handshake { accept: accept_for_key(&key), @@ -229,7 +229,7 @@ pub fn validate_h2(request: &Request) -> Result { "WebSocket version must be 13" ); let mut nonce = [0u8; 16]; - OsRng.fill_bytes(&mut nonce); + SysRng.try_fill_bytes(&mut nonce).expect("secure operating system random source unavailable"); let key = STANDARD.encode(nonce); Ok(Handshake { accept: accept_for_key(&key), diff --git a/src/secret_store.rs b/src/secret_store.rs index e9f556b..02e5a25 100644 --- a/src/secret_store.rs +++ b/src/secret_store.rs @@ -1,7 +1,7 @@ //! Central encrypted secret storage for provider/ACME credentials. //! Plaintext exists only for the duration of an explicit read/write operation. use anyhow::{ensure, Context, Result}; -use rand::{rngs::OsRng, RngCore}; +use rand::{rngs::SysRng, TryRng}; use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM}; use serde::Serialize; use sqlx::Row; @@ -59,7 +59,10 @@ fn read_key(path: &Path) -> Result> { metadata.is_file() && !metadata.file_type().is_symlink(), "secret store master key must be a regular file" ); - ensure!(metadata.len() == KEY_BYTES as u64, "invalid secret store master key size"); + ensure!( + metadata.len() == KEY_BYTES as u64, + "invalid secret store master key size" + ); #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; @@ -79,7 +82,10 @@ fn read_key(path: &Path) -> Result> { let mut file = options.open(path)?; file.read_exact(bytes.as_mut())?; let mut extra = [0u8; 1]; - ensure!(file.read(&mut extra)? == 0, "invalid secret store master key size"); + ensure!( + file.read(&mut extra)? == 0, + "invalid secret store master key size" + ); Ok(bytes) } @@ -92,7 +98,9 @@ pub fn initialize(data_dir: &Path) -> Result<()> { return Ok(()); } let mut key = Zeroizing::new([0u8; KEY_BYTES]); - OsRng.fill_bytes(key.as_mut()); + SysRng + .try_fill_bytes(key.as_mut()) + .expect("secure operating system random source unavailable"); let mut options = OpenOptions::new(); options.write(true).create_new(true); #[cfg(unix)] @@ -158,13 +166,18 @@ pub async fn put( metadata: serde_json::Value, ) -> Result<()> { validate_identity(secret_id, provider)?; - ensure!(!plaintext.is_empty() && plaintext.len() <= MAX_SECRET_BYTES, "invalid secret size"); + ensure!( + !plaintext.is_empty() && plaintext.len() <= MAX_SECRET_BYTES, + "invalid secret size" + ); let metadata = serde_json::to_string(&metadata)?; ensure!(metadata.len() <= 4096, "secret metadata is too large"); initialize(data_dir)?; let cipher = key(data_dir)?; let mut nonce = [0u8; NONCE_BYTES]; - OsRng.fill_bytes(&mut nonce); + SysRng + .try_fill_bytes(&mut nonce) + .expect("secure operating system random source unavailable"); let mut ciphertext = Zeroizing::new(plaintext.to_vec()); cipher .seal_in_place_append_tag( @@ -212,12 +225,16 @@ pub async fn get( } validate_identity(secret_id, &provider)?; let version: i64 = row.try_get("encryption_version")?; - ensure!(version == ENCRYPTION_VERSION, "unsupported secret encryption version"); + ensure!( + version == ENCRYPTION_VERSION, + "unsupported secret encryption version" + ); let nonce: Vec = row.try_get("nonce")?; ensure!(nonce.len() == NONCE_BYTES, "invalid secret nonce"); let mut ciphertext = Zeroizing::new(row.try_get::, _>("ciphertext")?); ensure!( - ciphertext.len() >= AES_256_GCM.tag_len() && ciphertext.len() <= MAX_SECRET_BYTES + AES_256_GCM.tag_len(), + ciphertext.len() >= AES_256_GCM.tag_len() + && ciphertext.len() <= MAX_SECRET_BYTES + AES_256_GCM.tag_len(), "invalid encrypted secret size" ); let mut nonce_bytes = [0u8; NONCE_BYTES]; @@ -287,7 +304,10 @@ pub async fn migrate_acme_credentials( return Ok(0); } let root_meta = fs::symlink_metadata(&root)?; - ensure!(root_meta.is_dir() && !root_meta.file_type().is_symlink(), "invalid ACME directory"); + ensure!( + root_meta.is_dir() && !root_meta.file_type().is_symlink(), + "invalid ACME directory" + ); let mut migrated = 0usize; for entry in fs::read_dir(&root)? { let entry = entry?; @@ -301,7 +321,10 @@ pub async fn migrate_acme_credentials( continue; } let profile: serde_json::Value = serde_json::from_slice(&fs::read(&profile_path)?)?; - let provider = profile.get("provider").and_then(serde_json::Value::as_str).unwrap_or(""); + let provider = profile + .get("provider") + .and_then(serde_json::Value::as_str) + .unwrap_or(""); let certificate_id = profile .get("certificate_id") .and_then(serde_json::Value::as_str) @@ -341,7 +364,8 @@ pub async fn migrate_acme_credentials( ) .await?; plaintext.zeroize(); - fs::remove_file(&credential_path).context("legacy ACME credential was encrypted but could not be removed")?; + fs::remove_file(&credential_path) + .context("legacy ACME credential was encrypted but could not be removed")?; migrated += 1; } Ok(migrated) diff --git a/src/tls/certificates.rs b/src/tls/certificates.rs index 483ba62..b0aef75 100644 --- a/src/tls/certificates.rs +++ b/src/tls/certificates.rs @@ -80,8 +80,8 @@ fn generate_locked(directory: &Path, name: &str, hosts: Vec) -> Result<( } }) .collect::>>()?; - let rcgen::CertifiedKey { cert, key_pair } = rcgen::generate_simple_self_signed(hosts)?; - let key = Zeroizing::new(key_pair.serialize_pem()); + let rcgen::CertifiedKey { cert, signing_key } = rcgen::generate_simple_self_signed(hosts)?; + let key = Zeroizing::new(signing_key.serialize_pem()); store_pair(directory, name, cert.pem().as_bytes(), key.as_bytes()) } pub fn initialize_default(directory: &Path) -> Result<()> { diff --git a/src/tls/tests.rs b/src/tls/tests.rs index 19536e1..68d40fa 100644 --- a/src/tls/tests.rs +++ b/src/tls/tests.rs @@ -77,7 +77,7 @@ mod tests { dir.path(), "shared-cert", generated.cert.pem().as_bytes(), - generated.key_pair.serialize_pem().as_bytes(), + generated.signing_key.serialize_pem().as_bytes(), ) .unwrap(); assert!(cert_file.starts_with("external-shared-cert-")); @@ -100,7 +100,7 @@ mod tests { let generated = rcgen::generate_simple_self_signed(vec!["reconcile.example.test".into()]).unwrap(); let certificate = generated.cert.pem(); - let key = generated.key_pair.serialize_pem(); + let key = generated.signing_key.serialize_pem(); let (cert_file, key_file, _) = stage_external_pair( dir.path(), "reconcile-cert", @@ -281,7 +281,7 @@ mod tests { let b = rcgen::generate_simple_self_signed(vec!["b.localhost".into()]).unwrap(); assert!(parse_pair( a.cert.pem().as_bytes(), - b.key_pair.serialize_pem().as_bytes() + b.signing_key.serialize_pem().as_bytes() ) .is_err()); let dir = tempfile::tempdir().unwrap(); @@ -338,7 +338,7 @@ mod tests { assert!(upload( dir.path(), pair.cert.pem().as_bytes(), - pair.key_pair.serialize_pem().as_bytes(), + pair.signing_key.serialize_pem().as_bytes(), hosts, ) .is_ok()); @@ -353,7 +353,7 @@ mod tests { ]) .unwrap(); let pem = pair.cert.pem(); - let key = pair.key_pair.serialize_pem(); + let key = pair.signing_key.serialize_pem(); assert!(upload( dir.path(), pem.as_bytes(), diff --git a/src/tls/tests/acme.rs b/src/tls/tests/acme.rs index 873909b..f29b4c6 100644 --- a/src/tls/tests/acme.rs +++ b/src/tls/tests/acme.rs @@ -10,7 +10,7 @@ mod acme_tests { dir.path(), "acme-fixture", cert.cert.pem().as_bytes(), - cert.key_pair.serialize_pem().as_bytes(), + cert.signing_key.serialize_pem().as_bytes(), ) .unwrap(); let requested = vec!["example.org".into(), "*.example.org".into()]; @@ -62,7 +62,7 @@ mod acme_tests { dir.path(), "acme-many", cert.cert.pem().as_bytes(), - cert.key_pair.serialize_pem().as_bytes(), + cert.signing_key.serialize_pem().as_bytes(), ) .unwrap(); assert!(validate_acme( diff --git a/src/tls/tests/external_paths.rs b/src/tls/tests/external_paths.rs index 26bf23b..b7b0fe5 100644 --- a/src/tls/tests/external_paths.rs +++ b/src/tls/tests/external_paths.rs @@ -15,7 +15,7 @@ fn external_absolute_paths_follow_certbot_style_symlinks_on_each_reload() { let first = rcgen::generate_simple_self_signed(vec!["example.test".into()]).unwrap(); fs::write(archive.join("fullchain1.pem"), first.cert.pem()).unwrap(); - fs::write(archive.join("privkey1.pem"), first.key_pair.serialize_pem()).unwrap(); + fs::write(archive.join("privkey1.pem"), first.signing_key.serialize_pem()).unwrap(); fs::set_permissions(archive.join("privkey1.pem"), fs::Permissions::from_mode(0o650)).unwrap(); symlink("../../archive/example.test/fullchain1.pem", live.join("fullchain.pem")).unwrap(); symlink("../../archive/example.test/privkey1.pem", live.join("privkey.pem")).unwrap(); @@ -47,7 +47,7 @@ fn external_absolute_paths_follow_certbot_style_symlinks_on_each_reload() { ]) .unwrap(); fs::write(archive.join("fullchain2.pem"), second.cert.pem()).unwrap(); - fs::write(archive.join("privkey2.pem"), second.key_pair.serialize_pem()).unwrap(); + fs::write(archive.join("privkey2.pem"), second.signing_key.serialize_pem()).unwrap(); fs::set_permissions(archive.join("privkey2.pem"), fs::Permissions::from_mode(0o640)).unwrap(); fs::remove_file(live.join("fullchain.pem")).unwrap(); fs::remove_file(live.join("privkey.pem")).unwrap(); diff --git a/src/web_server/tests/part_02.rs b/src/web_server/tests/part_02.rs index 36843ae..583fbdb 100644 --- a/src/web_server/tests/part_02.rs +++ b/src/web_server/tests/part_02.rs @@ -40,7 +40,7 @@ async fn php_fpm_gzip_response_finishes_after_fastcgi_eof() { let tmp = TempDir::new().unwrap(); std::fs::write(tmp.path().join("index.php"), b" str: preflight(root) manifest = tomllib.loads((root / "Cargo.toml").read_text()) -assert manifest["package"]["version"] == "0.31.1" +assert manifest["package"]["version"] == "0.31.2" assert manifest["dependencies"]["quinn"]["version"] == "0.11.12" h3dep = manifest["dependencies"]["h3"] -assert h3dep["package"] == "hpx-h3" and h3dep["version"] == "=2.5.19" and "quinn" in h3dep["features"] +assert h3dep["package"] == "hpx-h3" and h3dep["version"] == "=2.5.20" and "quinn" in h3dep["features"] assert "h3-quinn" not in manifest["dependencies"] lock_path = root / "Cargo.lock" if lock_path.exists(): lock = lock_path.read_text() - assert 'name = "rust-proxy"\nversion = "0.31.1"' in lock + assert 'name = "rust-proxy"\nversion = "0.31.2"' in lock assert 'name = "systemstat"' in lock and 'name = "metrics-process"' in lock and 'name = "sysinfo"' in lock, "Cargo.lock must be resolver-generated after adding cross-platform statistics libraries" for release_doc, marker in { - "README.md": "# RustProxy 0.31.1", - "CHANGELOG.md": "## 0.31.1", - "ROADMAP.md": "## 0.31.1", - "AGENTS.md": "Current source version: 0.31.1.", - "docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.1", + "README.md": "# RustProxy 0.31.2", + "CHANGELOG.md": "## 0.31.2", + "ROADMAP.md": "## 0.31.2", + "AGENTS.md": "Current source version: 0.31.2.", + "docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.2", }.items(): assert marker in (root / release_doc).read_text(), f"stale release identity in {release_doc}" assert tomllib.loads((root / "rust-toolchain.toml").read_text())["toolchain"]["channel"] == "stable" @@ -285,7 +285,7 @@ assert 'Opt-in health decisions based on ordinary user traffic. Defaults to disa assert '"force_ssl"' in openapi and '"forward_client_ip"' in openapi assert '"backend_header"' in openapi and '"backend_timing_header"' in openapi assert '"StreamMetrics"' in openapi and '"streams"' in openapi -for marker in ['"version": "0.31.1"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']: +for marker in ['"version": "0.31.2"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']: assert marker in openapi, marker for marker in ['"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']: assert marker in openapi, marker @@ -1362,7 +1362,7 @@ metrics_dep = manifest["dependencies"]["metrics-process"] assert metrics_dep["version"] == "=2.4.3" and metrics_dep["default-features"] is False assert metrics_dep["features"] == ["dummy"] sysinfo_dep = manifest["dependencies"]["sysinfo"] -assert sysinfo_dep["version"] == "=0.38.4" and sysinfo_dep["default-features"] is False +assert sysinfo_dep["version"] == "0.39" and sysinfo_dep["default-features"] is False assert set(sysinfo_dep["features"]) == {"network", "system"} for marker_0241 in [ "systemstat::{Platform, System}", "metrics_process::collector::collect", @@ -1396,7 +1396,7 @@ assert '"error_log" => {' not in admin_0241 and 'request("/api/config/error_log" assert '"error_log"' in openapi and 'logging, error_log, forwarding' not in openapi assert 'logging_section_persists_error_log_settings_atomically' in admin_0241 and 'error_log_is_not_a_standalone_configuration_section' in admin_0241 assert 'location.pathname === "/logging"' in settings_js_0241 -assert '"RuntimeInformation"' in openapi and '"version": "0.31.1"' in openapi +assert '"RuntimeInformation"' in openapi and '"version": "0.31.2"' in openapi # 0.24.1 compiler and hostname-cardinality regressions. tls_0241 = rust_source(root, "src/tls.rs") @@ -1635,5 +1635,5 @@ for marker_0282 in [ assert 'Zewnętrzne ścieżki plików' in (root / 'langs/pl_pl.json').read_text() assert 'setfacl -m u:rustproxy:--x /etc/ssl/' in (root / 'docs/V0.28.2_IMPLEMENTATION.md').read_text() -print("PASS: 0.31.1 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, external-packaging/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v9 migrations.") +print("PASS: 0.31.2 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, external-packaging/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v9 migrations.") print("NOT VERIFIED: Rust parsing, types, compilation, linking or runtime behavior. Run ./dev.sh test.") diff --git a/tools/checks/dependency_upgrade_0312.py b/tools/checks/dependency_upgrade_0312.py new file mode 100755 index 0000000..322df50 --- /dev/null +++ b/tools/checks/dependency_upgrade_0312.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Check the 0.31.2 dependency/source migration; optionally require a resolved lock. + +This is not a substitute for rustc, cargo metadata or runtime tests. +""" +from __future__ import annotations +import argparse +from pathlib import Path +import re +import tomllib + +ROOT = Path(__file__).resolve().parents[2] +parser = argparse.ArgumentParser() +parser.add_argument('--require-lock', action='store_true', help='Require the post-upgrade Cargo.lock produced by cargo update') +args = parser.parse_args() +manifest = tomllib.loads((ROOT / 'Cargo.toml').read_text()) +assert manifest['package']['version'] == '0.31.2' +assert manifest['package']['rust-version'] == '1.99' +deps = manifest['dependencies'] +expected_requirements = { + 'argon2': '0.6', 'rand': '0.10', 'sqlx': '0.9', 'rcgen': '0.14.10', + 'tower-http': '0.7.1', 'brotli': '9', 'sysinfo': '0.39', + 'h3': '=2.5.20', 'tokio': '1.53.2', 'hyper': '1.12.0', +} +for name, version in expected_requirements.items(): + entry = deps[name] + found = entry if isinstance(entry, str) else entry['version'] + assert found == version, f'{name} requirement changed: {found!r} != {version!r}' +assert deps['h3']['package'] == 'hpx-h3' +assert deps['h3']['features'] == ['quinn'] +assert deps['sqlx']['default-features'] is False +assert deps['sqlx']['features'] == ['runtime-tokio', 'sqlite'] +assert set(deps['sysinfo']['features']) == {'network','system'} +assert set(deps['rcgen']['features']) == {'crypto', 'pem', 'ring'} +assert 'tls12' in deps['rustls']['features'] and 'ring' in deps['rustls']['features'] +assert 'tls12' in deps['tokio-rustls']['features'] +for target in ('Dockerfile', 'integrations/routeros-oci-build/Dockerfile.routeros'): + assert 'FROM rust:1.99-trixie AS build' in (ROOT / target).read_text(), target +files = [p for p in (ROOT / 'src').rglob('*.rs')] +sources = '\n'.join(p.read_text() for p in files) +for old in ('rngs::OsRng', 'rand::thread_rng()', 'SaltString::generate(', + 'distributions::Alphanumeric', '.key_pair.serialize_pem()', + 'hash_password(password.as_bytes(), &salt)'): + assert old not in sources, f'obsolete dependency API: {old}' +for new in ('SysRng.try_fill_bytes(', 'phc::PasswordHash', + 'hash_password(password.as_bytes())', 'verify_password(password.as_bytes()', + 'signing_key.serialize_pem()', 'rand::rng()'): + assert new in sources, f'missing dependency migration: {new}' +assert 'VERSION = "5.33.1"' in (ROOT / 'scripts/build/swagger_assets.py').read_text() +lock = tomllib.loads((ROOT / 'Cargo.lock').read_text()) +installed: dict[str,set[str]] = {} +for item in lock['package']: + installed.setdefault(item['name'], set()).add(item['version']) +latest_required = { + 'argon2': '0.6.0', 'rand': '0.10.3', 'sqlx': '0.9.0', + 'rcgen': '0.14.10', 'tower-http': '0.7.1', 'brotli': '9.0.0', + 'sysinfo': '0.39.6', 'hpx-h3': '2.5.20', 'hyper': '1.12.0', + 'tokio': '1.53.2', +} +def compatible_update(package: str, installed_version: str, minimum: str) -> bool: + candidate = tuple(int(x) for x in installed_version.split('.')[:3]) + baseline = tuple(int(x) for x in minimum.split('.')[:3]) + if package == 'hpx-h3': + return candidate == baseline # Intentionally exact-pinned for the QUIC integration. + if candidate[0] != baseline[0]: + return False + if baseline[0] == 0 and candidate[1] != baseline[1]: + return False + return candidate >= baseline + +missing = {name: version for name,version in latest_required.items() + if not any(compatible_update(name, candidate, version) + for candidate in installed.get(name, set()))} +if missing: + details = ', '.join(f'{name} {version}' for name,version in missing.items()) + if args.require_lock: + raise SystemExit(f'FAIL: Cargo.lock not resolved to current deps: {details}. Run cargo update first.') + print(f'PENDING: lockfile still requires cargo update ({details})') +else: + print('PASS: pinned key packages found in post-upgrade lock; cargo --locked verification is still required') +print('PASS: 0.31.2 source dependency APIs, manifest, TLS features, toolchain images and Swagger pin (static only)') diff --git a/tools/checks/docs_check.py b/tools/checks/docs_check.py index f2a433b..8508662 100755 --- a/tools/checks/docs_check.py +++ b/tools/checks/docs_check.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Rust-independent documentation/Reader consistency checks for the 0.31.1 release.""" +"""Rust-independent documentation/Reader consistency checks for the 0.31.2 release.""" from pathlib import Path import re from rust_source import rust_source @@ -65,7 +65,7 @@ assert '"/docs/content.json"' in admin readme = (ROOT / "README.md").read_text(encoding="utf-8") roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8") changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8") -assert readme.startswith("# RustProxy 0.31.1") +assert readme.startswith("# RustProxy 0.31.2") assert "docs/WEB_SERVER.md" in readme and "docs/README.md" in readme assert readme.count("## What changed in ") == 1, "README duplicates historical release notes; use CHANGELOG/archive" assert "## Release history" in readme and "docs/ROADMAP_HISTORY.md" in readme diff --git a/tools/checks/preflight.py b/tools/checks/preflight.py index 44cea36..208ff2d 100755 --- a/tools/checks/preflight.py +++ b/tools/checks/preflight.py @@ -75,10 +75,10 @@ def check(root: Path, repair_known: bool = False) -> list[str]: raise ValueError(f"Conflicting Rust modules: {flat.relative_to(root)} and {module.relative_to(root)}") for path in ["src/auth/mod.rs", "src/auth/store.rs", "src/backup/snapshot.rs", "src/upstream.rs", "src/tls.rs", "src/config/internal_routing.rs", "src/proxy/websocket.rs", "src/proxy/error_fallback.rs", "src/cache/mod.rs", "src/cache/model.rs", "src/cache/lifecycle.rs", "src/cache/cold.rs", "src/cache/dashboard.rs", "src/cache/tickets.rs", "src/cache/lookup.rs", "src/cache/policy.rs", "src/cache/fill.rs", "src/cache/tests.rs", "src/settings.rs", "src/compression.rs", "src/diagnostics.rs", "src/traffic.rs", "src/system_stats/mod.rs", "src/system_stats/linux.rs", "src/vhost.rs", "src/vhost/error_fallback.rs", "src/web_server/mod.rs", "src/web_server/config.rs", "src/web_server/filesystem.rs", "src/web_server/security.rs", "src/web_server/response.rs", "src/web_server/request.rs", "src/web_server/routing.rs", "src/web_server/fastcgi.rs", "src/web_server/htaccess.rs", "src/web_server/serve.rs", "src/web_server/tests.rs", "src/balance.rs", "src/security.rs", "src/admin/web/vhosts/core.js", "src/admin/web/vhosts/health.js", "src/admin/web/vhosts/editor.js", "src/admin/web/vhosts/render.js", "src/admin/web/web-server.js", "src/admin/web/streams.js", "src/admin/web/host404.js", "src/admin/web/app/core.js", "src/admin/web/app/preferences.js", "src/admin/web/app/config-history.js", "src/admin/web/app/runtime.js", "src/admin/web/app/settings.js", "src/admin/web/management.js", "src/admin/web/drafts.js", "src/pages.rs", "tools/dev/mock_server.py", "tools/dev/mock_auth.py", "src/forwarding.rs", "src/tinyauth.rs", "src/http3.rs", "src/stream.rs", "src/admin/web/integrations.js", "src/admin/web/traffic-panel.js", "src/prometheus.rs", "src/admin/web/settings.js", "src/admin/web/docker.js", "src/docker.rs", "docker-agent/main.go", "docker-agent/model.go", "docker-agent/docker.go", "docker-agent/network.go", "docker-agent/certificate.go", "docker-agent/server.go", "docker-agent/main_test.go", "docker-agent/rustproxy-docker-agent.service", "docker-agent/install.sh", "docs/DOCKER_DISCOVERY.md", "docs/WEB_SERVER.md", "docs/PHP_FPM.md", "docs/README.md", "docs/TODO.md", "docs/V0.25.5_AUDIT.md", "docs/V0.25.4_AUDIT.md", "docs/V0.25.3_AUDIT.md", "docs/V0.25.2_AUDIT.md", "docs/CHANGELOG_TECHNICAL_ARCHIVE.md", "docs/ROADMAP_HISTORY.md", "tools/checks/docs_check.py", "tools/checks/web_server_contracts.py", "tools/checks/rust_source.py", "tools/checks/rust_module_contracts.py", "tools/checks/source_module_contracts.py", "scripts/runtime/acme_provider.py", "src/basic_auth.rs", "scripts/deploy/deploy.py", "scripts/deploy/deploy_core.py", "scripts/deploy/deploy_runtime.py", "scripts/migration/migrate_npm.py", "scripts/migration/migrate_npm_core.py", "scripts/migration/migrate_npm_api.py", "docs/NPM_MIGRATION.md", "docs/STREAMS.md", "install.sh", "install-debian13.sh", "update.sh", "build.rs", "scripts/build/swagger_assets.py", "src/admin/documentation.rs", "src/admin/openapi.json", "langs/en_US.json", "langs/pl_pl.json", "Dockerfile", ".dockerignore", "docs/CONTAINER.md", "docs/PROMETHEUS.md", "docs/NOT_FOUND_HOSTS.md", "grafana/rustproxy-overview.json"]: if not (root / path).is_file(): - raise ValueError(f"Incomplete RustProxy 0.31.1 source tree: missing {path}") + raise ValueError(f"Incomplete RustProxy 0.31.2 source tree: missing {path}") text = (root / "src/lib.rs").read_text(encoding="utf-8") if '#[path = "auth/mod.rs"]' not in text: - raise ValueError("src/lib.rs is not the 0.31.1 entry point; extract the complete package, not individual files.") + raise ValueError("src/lib.rs is not the 0.31.2 entry point; extract the complete package, not individual files.") return messages diff --git a/tools/checks/release_smoke.py b/tools/checks/release_smoke.py index 63390e3..7dc54ae 100755 --- a/tools/checks/release_smoke.py +++ b/tools/checks/release_smoke.py @@ -135,7 +135,7 @@ def upgrade_smoke(tmp: Path) -> None: assert backup.is_dir() assert (backup / "src/obsolete.rs").is_file() assert not (target / "src/obsolete.rs").exists() - assert 'version = "0.31.1"' in (target / "Cargo.toml").read_text(encoding="utf-8") + assert 'version = "0.31.2"' in (target / "Cargo.toml").read_text(encoding="utf-8") assert (target / "data/keep.txt").read_text(encoding="utf-8") == "preserve\n" assert (target / ".env").read_text(encoding="utf-8") == "RUST_PROXY_DEMO=0\n" assert (target / "langs/site.json").is_file() @@ -146,7 +146,7 @@ def upgrade_smoke(tmp: Path) -> None: assert (target / "docker-agent/certificate.go").is_file() assert (target / "docker-agent/server.go").is_file() manifest = json.loads((backup / "upgrade.json").read_text(encoding="utf-8")) - assert manifest["to_version"] == "0.31.1" + assert manifest["to_version"] == "0.31.2" assert manifest["removed_obsolete_runtime_state"] is True assert not legacy.exists() assert not (backup / "legacy-data").exists() @@ -582,7 +582,7 @@ def source_contract_smoke() -> None: assert metrics_process["default-features"] is False assert metrics_process["features"] == ["dummy"] sysinfo_network = manifest["dependencies"]["sysinfo"] - assert sysinfo_network["version"] == "=0.38.4" + assert sysinfo_network["version"] == "0.39" assert sysinfo_network["default-features"] is False assert set(sysinfo_network["features"]) == {"network", "system"} target_key = 'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "macos", target_os = "windows"))' @@ -617,7 +617,7 @@ def source_contract_smoke() -> None: assert marker in admin, marker anonymous_block = admin.split("fn anonymous_allowed", 1)[1].split("const CERTIFICATE_API_KEY_HEADER", 1)[0] assert '"/api/status"' not in anonymous_block - assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.31.1"' in openapi_0241 + assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.31.2"' in openapi_0241 assert 'id="panel-logging"' not in html for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="error-log-form"']: assert marker in html, marker @@ -1108,7 +1108,7 @@ def source_contract_smoke() -> None: assert '.managed-update' in main and "flush_hot_to_cold_for_update" in main openapi = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8") assert '"/api/config/history/restore"' in openapi - for marker in ['"version": "0.31.1"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']: + for marker in ['"version": "0.31.2"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']: assert marker in openapi, marker assert '"software_update"' in openapi and '"update_available"' in openapi for marker in ['"/api/certificates/deploy"', '"CertificateDeploy"', '"CertificateDeployResult"', '"/api/certificates/reload"']: @@ -1177,7 +1177,7 @@ def container_contract_smoke() -> None: assert "LogsDirectory=rustproxy" in generated_unit and "/var/log/rustproxy" in generated_unit assert "Environment=MALLOC_ARENA_MAX=2" in generated_unit for marker in [ - "FROM rust:1.88-trixie AS build", + "FROM rust:1.99-trixie AS build", "cargo generate-lockfile", "cargo build --locked --release", "FROM debian:trixie-slim AS runtime", @@ -1313,7 +1313,7 @@ def main() -> None: source_contract_smoke() container_contract_smoke() grafana_contract_smoke() - print("PASS: 0.31.1 SQLite/schema-9/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, external Debian-packaging separation, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.") + print("PASS: 0.31.2 SQLite/schema-9/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, external Debian-packaging separation, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.") if __name__ == "__main__": diff --git a/tools/checks/system_info_0301_ui_test.mjs b/tools/checks/system_info_0301_ui_test.mjs index f451f18..859a72b 100644 --- a/tools/checks/system_info_0301_ui_test.mjs +++ b/tools/checks/system_info_0301_ui_test.mjs @@ -43,7 +43,7 @@ const code = js.slice(js.indexOf(' const runtime = latestStatus?.runtime || { const elements = new Map(); const dollar = id => { if (!elements.has(id)) elements.set(id, { textContent: '' }); return elements.get(id); }; const status = { - version: '0.31.1', uptime_seconds: 4201, + version: '0.31.2', uptime_seconds: 4201, runtime: { platform: 'Linux', operating_system: 'Debian GNU/Linux 13', kernel_version: '6.12.0', architecture: 'x86_64', statistics_source: 'systemstat+sysinfo', data_directory: '/var/lib/rustproxy', database_path: '/var/lib/rustproxy/config.sqlite3', @@ -63,7 +63,7 @@ const context = { vm.runInNewContext(code, context); assert.equal(dollar('runtime-operating-system').textContent, 'Debian GNU/Linux 13'); assert.equal(dollar('runtime-kernel-version').textContent, '6.12.0'); -assert.equal(dollar('runtime-version').textContent, '0.31.1'); +assert.equal(dollar('runtime-version').textContent, '0.31.2'); assert.equal(dollar('runtime-logical-cpus').textContent, '8'); assert.equal(dollar('runtime-cpu-usage').textContent, '17.2%'); assert.equal(dollar('runtime-python').textContent, 'Available · 3.13.5'); @@ -124,4 +124,4 @@ for (const key of ['python_version','pip_version','certbot_version']) { assert.ok(runtimeSchema.properties.certificate_runtime.properties[key]); assert.equal(runtimeSchema.properties.certificate_runtime.properties[key].nullable, true); } -console.log('PASS: 0.31.1 legend palette, About cleanup, system tab/navigation, read-only API schema and simulated status rendering.'); +console.log('PASS: 0.31.2 legend palette, About cleanup, system tab/navigation, read-only API schema and simulated status rendering.'); diff --git a/tools/dev/memory_load.py b/tools/dev/memory_load.py index 7321a0d..690ac5b 100755 --- a/tools/dev/memory_load.py +++ b/tools/dev/memory_load.py @@ -54,7 +54,7 @@ def run_load( port = parsed.port or (443 if parsed.scheme == "https" else 80) tls = ssl._create_unverified_context() if insecure else ssl.create_default_context() connection_type = http.client.HTTPSConnection if parsed.scheme == "https" else http.client.HTTPConnection - headers = {"user-agent": "RustProxy-Memory-Stress/0.31.1", "accept": "*/*"} + headers = {"user-agent": "RustProxy-Memory-Stress/0.31.2", "accept": "*/*"} if host_header: headers["host"] = host_header