This commit is contained in:
Mateusz Gruszczyński committed 2026-10-10 23:41:54 +02:00
1 parent 24af7478b8
commit 0facfe6f5c
44 files changed
+898 -321

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@
Read README.md, ROADMAP.md and docs/TEST_REPORT.md before making changes. 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 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 ## Delivery
+5
View File
@@ -3,6 +3,11 @@
> Operator-facing release history. Historical engineering notes are kept in > Operator-facing release history. Historical engineering notes are kept in
> [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md). > [`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 ## 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. - 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.
Generated
+322 -106
View File
@@ -23,13 +23,28 @@ version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-no-stdlib"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a"
[[package]] [[package]]
name = "alloc-stdlib" name = "alloc-stdlib"
version = "0.2.4" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
dependencies = [ 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]] [[package]]
@@ -105,16 +120,55 @@ dependencies = [
[[package]] [[package]]
name = "argon2" name = "argon2"
version = "0.5.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" checksum = "134c52ddac6d63c576bef8168db10c83c49c26444ecbc68060fef078925a901c"
dependencies = [ dependencies = [
"base64ct", "base64ct",
"blake2", "blake2",
"cpufeatures 0.2.17", "cpufeatures 0.3.1",
"password-hash", "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]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.48" version = "0.4.48"
@@ -155,7 +209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
dependencies = [ dependencies = [
"axum-core", "axum-core",
"base64", "base64 0.22.1",
"bytes", "bytes",
"form_urlencoded", "form_urlencoded",
"futures-util", "futures-util",
@@ -207,6 +261,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.8.3" version = "1.8.3"
@@ -231,6 +291,15 @@ dependencies = [
"syn 2.0.119", "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]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.13.2" version = "2.13.2"
@@ -239,11 +308,11 @@ checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
[[package]] [[package]]
name = "blake2" name = "blake2"
version = "0.10.6" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" checksum = "5b5d4d889834ee8ecfc0f8426ad30faf7cdcb10f741a8e6d7224d95325479f6f"
dependencies = [ dependencies = [
"digest", "digest 0.11.3",
] ]
[[package]] [[package]]
@@ -255,15 +324,35 @@ dependencies = [
"generic-array", "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]] [[package]]
name = "brotli" name = "brotli"
version = "8.0.4" version = "8.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib 2.0.4",
"alloc-stdlib", "alloc-stdlib 0.2.4",
"brotli-decompressor", "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]] [[package]]
@@ -272,8 +361,18 @@ version = "5.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib 2.0.4",
"alloc-stdlib", "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]] [[package]]
@@ -389,6 +488,12 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486"
[[package]]
name = "cmov"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.5" version = "1.0.5"
@@ -401,7 +506,7 @@ version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef16c47ba2797aa6a909cc37d39911f3a6743811fe7408ac0b0cc0276b656e9" checksum = "bef16c47ba2797aa6a909cc37d39911f3a6743811fe7408ac0b0cc0276b656e9"
dependencies = [ dependencies = [
"brotli", "brotli 8.0.4",
"compression-core", "compression-core",
"flate2", "flate2",
"memchr", "memchr",
@@ -482,12 +587,44 @@ dependencies = [
"typenum", "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]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.11.1" version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" 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]] [[package]]
name = "deranged" name = "deranged"
version = "0.5.8" version = "0.5.8"
@@ -500,9 +637,19 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [ dependencies = [
"block-buffer", "block-buffer 0.10.4",
"crypto-common", "crypto-common 0.1.7",
"subtle", ]
[[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]] [[package]]
@@ -582,9 +729,9 @@ dependencies = [
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.11.1" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@@ -599,9 +746,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
@@ -781,9 +928,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.5" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
@@ -798,11 +945,11 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]] [[package]]
name = "hashlink" name = "hashlink"
version = "0.10.0" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f"
dependencies = [ dependencies = [
"hashbrown 0.15.5", "hashbrown 0.16.1",
] ]
[[package]] [[package]]
@@ -819,9 +966,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hpx-h3" name = "hpx-h3"
version = "2.5.19" version = "2.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4b773a1c31de56cc61c87de6d1db73ed838c1d23d8fad454b302a38ae178ad7" checksum = "f137abcec3be4197580d1429894c2c33dc438a6d830500dd11bf0a1b958472ff"
dependencies = [ dependencies = [
"bytes", "bytes",
"fastrand", "fastrand",
@@ -879,10 +1026,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]] [[package]]
name = "hyper" name = "hybrid-array"
version = "1.11.1" version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
@@ -1288,12 +1444,31 @@ dependencies = [
"windows-sys 0.61.2", "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]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" 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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -1322,6 +1497,15 @@ dependencies = [
"objc2-core-foundation", "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]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.4" version = "1.21.4"
@@ -1365,22 +1549,21 @@ dependencies = [
[[package]] [[package]]
name = "password-hash" name = "password-hash"
version = "0.5.0" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
dependencies = [ dependencies = [
"base64ct", "getrandom 0.4.3",
"rand_core 0.6.4", "phc",
"subtle",
] ]
[[package]] [[package]]
name = "pem" name = "pem"
version = "3.0.6" version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120"
dependencies = [ dependencies = [
"base64", "base64 0.23.1",
"serde_core", "serde_core",
] ]
@@ -1390,6 +1573,17 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 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]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.17" version = "0.2.17"
@@ -1534,24 +1728,13 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" 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]] [[package]]
name = "rand" name = "rand"
version = "0.9.5" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
dependencies = [ dependencies = [
"rand_chacha 0.9.0", "rand_chacha",
"rand_core 0.9.5", "rand_core 0.9.5",
] ]
@@ -1566,16 +1749,6 @@ dependencies = [
"rand_core 0.10.1", "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]] [[package]]
name = "rand_chacha" name = "rand_chacha"
version = "0.9.0" version = "0.9.0"
@@ -1586,15 +1759,6 @@ dependencies = [
"rand_core 0.9.5", "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]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.9.5" version = "0.9.5"
@@ -1621,14 +1785,15 @@ dependencies = [
[[package]] [[package]]
name = "rcgen" name = "rcgen"
version = "0.13.2" version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8"
dependencies = [ dependencies = [
"pem", "pem",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
"time", "time",
"x509-parser",
"yasna", "yasna",
] ]
@@ -1716,14 +1881,14 @@ dependencies = [
[[package]] [[package]]
name = "rust-proxy" name = "rust-proxy"
version = "0.31.1" version = "0.31.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
"argon2", "argon2",
"axum", "axum",
"base64", "base64 0.22.1",
"brotli", "brotli 9.0.0",
"bytes", "bytes",
"clap", "clap",
"flate2", "flate2",
@@ -1741,7 +1906,7 @@ dependencies = [
"libc", "libc",
"metrics-process", "metrics-process",
"quinn", "quinn",
"rand 0.8.8", "rand 0.10.3",
"rcgen", "rcgen",
"regex", "regex",
"ring", "ring",
@@ -1776,6 +1941,15 @@ version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" 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]] [[package]]
name = "rustix" name = "rustix"
version = "1.1.5" version = "1.1.5"
@@ -1925,7 +2099,7 @@ checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.17", "cpufeatures 0.2.17",
"digest", "digest 0.10.7",
] ]
[[package]] [[package]]
@@ -1936,7 +2110,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.17", "cpufeatures 0.2.17",
"digest", "digest 0.10.7",
] ]
[[package]] [[package]]
@@ -2009,9 +2183,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx" name = "sqlx"
version = "0.8.6" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71"
dependencies = [ dependencies = [
"sqlx-core", "sqlx-core",
"sqlx-macros", "sqlx-macros",
@@ -2020,12 +2194,13 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-core" name = "sqlx-core"
version = "0.8.6" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"bytes", "bytes",
"cfg-if",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"either", "either",
@@ -2034,12 +2209,11 @@ dependencies = [
"futures-intrusive", "futures-intrusive",
"futures-io", "futures-io",
"futures-util", "futures-util",
"hashbrown 0.15.5", "hashbrown 0.16.1",
"hashlink", "hashlink",
"indexmap", "indexmap",
"log", "log",
"memchr", "memchr",
"once_cell",
"percent-encoding", "percent-encoding",
"serde", "serde",
"sha2", "sha2",
@@ -2053,9 +2227,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros" name = "sqlx-macros"
version = "0.8.6" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2066,15 +2240,15 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros-core" name = "sqlx-macros-core"
version = "0.8.6" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3"
dependencies = [ dependencies = [
"cfg-if",
"dotenvy", "dotenvy",
"either", "either",
"heck", "heck",
"hex", "hex",
"once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
@@ -2089,12 +2263,13 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-sqlite" name = "sqlx-sqlite"
version = "0.8.6" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c"
dependencies = [ dependencies = [
"atoi", "atoi",
"flume", "flume",
"form_urlencoded",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor", "futures-executor",
@@ -2104,7 +2279,6 @@ dependencies = [
"log", "log",
"percent-encoding", "percent-encoding",
"serde", "serde",
"serde_urlencoded",
"sqlx-core", "sqlx-core",
"thiserror 2.0.21", "thiserror 2.0.21",
"tracing", "tracing",
@@ -2157,6 +2331,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 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]] [[package]]
name = "synstructure" name = "synstructure"
version = "0.14.0" version = "0.14.0"
@@ -2170,9 +2355,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.38.4" version = "0.39.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6"
dependencies = [ dependencies = [
"libc", "libc",
"memchr", "memchr",
@@ -2271,6 +2456,7 @@ dependencies = [
"powerfmt", "powerfmt",
"serde_core", "serde_core",
"time-core", "time-core",
"time-macros",
] ]
[[package]] [[package]]
@@ -2279,6 +2465,16 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" 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]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.4" version = "0.8.4"
@@ -2297,9 +2493,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.53.1" version = "1.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044"
dependencies = [ dependencies = [
"bytes", "bytes",
"libc", "libc",
@@ -2387,9 +2583,9 @@ dependencies = [
[[package]] [[package]]
name = "tower-http" name = "tower-http"
version = "0.6.11" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" checksum = "08a05a66a4fdd61cbbe0a1d755ffe0ca6aba159dd4820936a0ff8a8278245b9c"
dependencies = [ dependencies = [
"async-compression", "async-compression",
"bitflags", "bitflags",
@@ -2397,6 +2593,7 @@ dependencies = [
"futures-core", "futures-core",
"http", "http",
"http-body", "http-body",
"percent-encoding",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -2562,9 +2759,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "want" name = "want"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b"
dependencies = [ dependencies = [
"try-lock", "try-lock",
] ]
@@ -2875,11 +3072,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
[[package]] [[package]]
name = "yasna" name = "x509-parser"
version = "0.5.2" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
dependencies = [ 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", "time",
] ]
@@ -2903,7 +3119,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.6", "syn 3.0.6",
"synstructure", "synstructure 0.14.0",
] ]
[[package]] [[package]]
@@ -2944,7 +3160,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.6", "syn 3.0.6",
"synstructure", "synstructure 0.14.0",
] ]
[[package]] [[package]]
+13 -13
View File
@@ -1,13 +1,13 @@
[package] [package]
name = "rust-proxy" name = "rust-proxy"
version = "0.31.1" version = "0.31.2"
edition = "2021" edition = "2021"
rust-version = "1.88" rust-version = "1.99"
description = "RustProxy - independent reverse proxy with local administration" description = "RustProxy - independent reverse proxy with local administration"
publish = false publish = false
[dependencies] [dependencies]
argon2 = { version = "0.5.3", features = ["std"] } argon2 = "0.6"
sha2 = "0.10" sha2 = "0.10"
ipnet = "2.11" ipnet = "2.11"
rpassword = "7.4" rpassword = "7.4"
@@ -15,7 +15,7 @@ zeroize = "1.8"
anyhow = "1.0" anyhow = "1.0"
libc = "0.2" libc = "0.2"
arc-swap = "1.7" 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" bytes = "1.10"
clap = { version = "4.5", features = ["derive", "env"] } clap = { version = "4.5", features = ["derive", "env"] }
futures-util = "0.3" futures-util = "0.3"
@@ -23,15 +23,15 @@ fs2 = "0.4"
http = "1.3" http = "1.3"
http-body = "1.0" http-body = "1.0"
http-body-util = "0.1" 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"] } hyper-util = { version = "0.1", features = ["client-legacy", "server-auto", "http1", "http2", "tokio"] }
rand = "0.8" rand = "0.10"
regex = "1.11" regex = "1.11"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" 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" 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"] } tokio-util = { version = "0.7", features = ["rt", "io"] }
tower = { version = "0.5", features = ["util"] } tower = { version = "0.5", features = ["util"] }
tracing = "0.1" 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"] } hyper-rustls = { version = "0.27", default-features = false, features = ["ring", "http1", "http2", "tls12"] }
rustls-pemfile = "2.2" rustls-pemfile = "2.2"
webpki-roots = "1.0" 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" 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" flate2 = "1.1"
brotli = "8" brotli = "9"
zstd = "0.14" 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"] } quinn = { version = "0.11.12", default-features = false, features = ["runtime-tokio", "rustls-ring"] }
metrics-process = { version = "=2.4.3", default-features = false, features = ["dummy"] } 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] [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" systemstat = "=0.2.7"
+1 -1
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM rust:1.88-trixie AS build FROM rust:1.99-trixie AS build
WORKDIR /build WORKDIR /build
COPY . . COPY . .
RUN if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi \ RUN if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi \
+14 -3
View File
@@ -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). **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, 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. 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 ## 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. 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. - Improved mobile panel loading behind reverse proxies: one ordered JavaScript resource, versioned assets and retry of temporary configuration errors.
+6
View File
@@ -1,5 +1,11 @@
# RustProxy roadmap # 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 ## 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. - [x] Load dashboard modules as one ordered JavaScript resource and wait for document/module readiness before session initialization.
+39 -37
View File
@@ -1,18 +1,18 @@
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore 61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore 9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
a414a328c21b7c4f81e5448a452258d8069ce40243a41e4e06b8950386494b96 AGENTS.md 047139f3c7c40778001602e77ef69302e3bef8f495d48d780953c97c401fa2d4 AGENTS.md
15a02c3476a51deade17d25b56c45068ebe50e5f127680f387c98777a8854c5b CHANGELOG.md 9b42590541917e8d92fa26a5831b9dd43503f179337f8ed6c3a4d7d1de84d3b7 CHANGELOG.md
e95030916a57faaa3fceb509f4aeec167b789022703f2e0cc09afaa0a071c89b Cargo.lock 50238f33805987e7301f9ed06a8fd51b74b0e01330dbbae42c3a2fd8bd0d8e86 Cargo.lock
a1aba61dca0fa20436ce0651baf6c59fe2602d2e9b4ee48b6900ba0351d7c208 Cargo.toml c6cc53538b89420b82526bc766d73a5fc93a82017f8fdb4bf767335dd613c90a Cargo.toml
75b008f237b2226c62a7721b7901018ce83bfc2726f047a83a17c66fcb799f3b Dockerfile 25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
3a0ee0e90ee5a75cfbc6b5497546861ae56b4fc5f343a5d83f722cdddbd4b8e1 README.md f3ad7c8b4b450bcb32a7c68f90e868726154b3f1ea88c7d9ba5173850334d24e README.md
d1914a8afe65d73620bafb6cb4d9b13206ebde5674f79da77fab5cec6bbe81c7 ROADMAP.md e2187e12e611638f85bae7808947b46a5de3c12db234ec217767ac8aefd1919a ROADMAP.md
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs
ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md
4834c278430ddf3bf842995735195368f1ac2c3b5c1d749df4f9238caf1e2b25 deploy/rustproxy.service 4834c278430ddf3bf842995735195368f1ac2c3b5c1d749df4f9238caf1e2b25 deploy/rustproxy.service
90c5b561079142ff094330066a6a551d7b0e1705012613341ec3defb7b3dc199 dev.sh 31d018c9ef676d397bafa66c5099a57bc41ee56bbb272df59bc521574d920c82 dev.sh
5f51efe621f45512a466fa7f2656867c6502a135ca040f4bfeec06a6919b39b6 docker-agent/README.md 5f51efe621f45512a466fa7f2656867c6502a135ca040f4bfeec06a6919b39b6 docker-agent/README.md
7fee7d51a7deb67e73e53a0bdff3aa4630aea9bab9b23a0f9b8001b2169892b1 docker-agent/certificate.go 7fee7d51a7deb67e73e53a0bdff3aa4630aea9bab9b23a0f9b8001b2169892b1 docker-agent/certificate.go
5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example 5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example
@@ -28,7 +28,7 @@ e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/n
875faa3969df9409d79f050a84b4183496f557edda3d34aaec19fe556cf8c471 docs/ACME_PROVIDERS.md 875faa3969df9409d79f050a84b4183496f557edda3d34aaec19fe556cf8c471 docs/ACME_PROVIDERS.md
ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md
8ded74f422426606238fa9876b49277a7bc56ddd96062a836cdd6ddbf78bd335 docs/API.md 8ded74f422426606238fa9876b49277a7bc56ddd96062a836cdd6ddbf78bd335 docs/API.md
f054206a09bed81018708608cdeecbfd09018ee7cec9d3abc038f8a37d47b815 docs/API_ACCESS.md 6ed12684b970fb7173fc9d1ad1da8ecce784eb38b29b7e6e631309fb25446807 docs/API_ACCESS.md
4c5288c9ed427cfb919bd023ed9a321dcf6d64bc4cad3e1768393356c5d835ba docs/ARCHITECTURE.md 4c5288c9ed427cfb919bd023ed9a321dcf6d64bc4cad3e1768393356c5d835ba docs/ARCHITECTURE.md
e9e2327684a4ae2d1ca4a0b3fb67d0ff83b5a150d38d4de4cfad2c5d3102eab6 docs/CACHE.md e9e2327684a4ae2d1ca4a0b3fb67d0ff83b5a150d38d4de4cfad2c5d3102eab6 docs/CACHE.md
9113f3abbfe81bafc4855d8680df8aa2a381ecc358cc3e1a0f78b6b756421117 docs/CACHE_DIAGNOSTICS.md 9113f3abbfe81bafc4855d8680df8aa2a381ecc358cc3e1a0f78b6b756421117 docs/CACHE_DIAGNOSTICS.md
@@ -65,7 +65,7 @@ a2bb837cdd3559d58158880c1f832badf64035376a3623050bb4e40c2bca104c docs/ROADMAP_H
007537ee971463072b2482dfa4fdcf1955f837ced0b711d4eef3b6267d16d4f6 docs/SECURITY_AUDIT_0.31.0.md 007537ee971463072b2482dfa4fdcf1955f837ced0b711d4eef3b6267d16d4f6 docs/SECURITY_AUDIT_0.31.0.md
2cd9dadc49a5474e4ab06d5c01fa38c27cab436578cb9c9f327a37b3406d2e13 docs/SETTINGS.md 2cd9dadc49a5474e4ab06d5c01fa38c27cab436578cb9c9f327a37b3406d2e13 docs/SETTINGS.md
3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md 3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md
14ef0d7935c0ff9a1557ca3a8cac97274be7e2b8e0fd68ea6cce87372989865d docs/TEST_REPORT.md 8ce2502e64a159a23eadebafdc9f0d65d384e8099eb63368748ee7e0269e299d docs/TEST_REPORT.md
cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md
751c9b13b47d0959a8575d6e82bdecba988b950a374ba0b084fbba08b4b65ebe docs/TRANSFORM.md 751c9b13b47d0959a8575d6e82bdecba988b950a374ba0b084fbba08b4b65ebe docs/TRANSFORM.md
660687fc961f71aae2bdf71595aa47a336fa782f4e78ceee8c1d1e63e46393ee docs/TRANSPORT.md 660687fc961f71aae2bdf71595aa47a336fa782f4e78ceee8c1d1e63e46393ee docs/TRANSPORT.md
@@ -131,7 +131,7 @@ fde541cd76bc3fa2b6765822bf04baf9f9ea3e4b6e185e76ff467270ddbd945e install-debian
2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 integrations/opnsense-acme/README.md 2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 integrations/opnsense-acme/README.md
c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 integrations/opnsense-acme/opnsense-rustproxy-cert-deploy.sh c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 integrations/opnsense-acme/opnsense-rustproxy-cert-deploy.sh
45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 integrations/routeros-oci-build/.env.routeros.example 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 7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e integrations/routeros-oci-build/Dockerfile.routeros.dockerignore
cb01fdef286f38b442dd9252c23c4f8e44eb72e9ecdd99adb926e8e9f6318bb1 integrations/routeros-oci-build/README.md cb01fdef286f38b442dd9252c23c4f8e44eb72e9ecdd99adb926e8e9f6318bb1 integrations/routeros-oci-build/README.md
96563a83e87cfb33e238c2a970ce36b1a1b51e91318d09b123c3755002ba2646 integrations/routeros-oci-build/build-routeros-oci.sh 96563a83e87cfb33e238c2a970ce36b1a1b51e91318d09b123c3755002ba2646 integrations/routeros-oci-build/build-routeros-oci.sh
@@ -156,7 +156,7 @@ ee50f38c037765fab742e888d4b9fc4d874c31c785c6064d4eca3d2c4d24d944 nagios/check_r
6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues 6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues
7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml 7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml
2145ad17ae7a823756371ceab6bdbeb332211df93193ffb4e486f7a80c4f29ae scripts/README.md 2145ad17ae7a823756371ceab6bdbeb332211df93193ffb4e486f7a80c4f29ae scripts/README.md
2044e6d979712457d6ebc3483efa029cc6de22416ee85898d950300015bd815d scripts/build/swagger_assets.py a1f55872201b5178f8af5e2beee6b3a68e37026831e41426ee62a8c15c1cf333 scripts/build/swagger_assets.py
e4d15aa6aa864d04b9b30aca33826b833632af02ca643f8b23e219a5ea1298dd scripts/deploy/deploy.py e4d15aa6aa864d04b9b30aca33826b833632af02ca643f8b23e219a5ea1298dd scripts/deploy/deploy.py
cc268d9d3dd125c9cbff2522fe3137a561fb4fcbd5ee461d70ef2e3a8241cdb1 scripts/deploy/deploy_core.py cc268d9d3dd125c9cbff2522fe3137a561fb4fcbd5ee461d70ef2e3a8241cdb1 scripts/deploy/deploy_core.py
8c7da13e95bcddd2bd1b4f1b22ade1c5ac1ecf54448601daa8416905d4239751 scripts/deploy/deploy_runtime.py 8c7da13e95bcddd2bd1b4f1b22ade1c5ac1ecf54448601daa8416905d4239751 scripts/deploy/deploy_runtime.py
@@ -164,6 +164,7 @@ a641bd35400c56f3b68b0a92e97b602d06090f03972ca8f01fa744e90dd97403 scripts/deploy
2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migration/migrate_npm.py 2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migration/migrate_npm.py
a459d95bbb0076f33ade8b273b813308167f9d8d45242b309a2d18320ca0093c scripts/migration/migrate_npm_api.py a459d95bbb0076f33ade8b273b813308167f9d8d45242b309a2d18320ca0093c scripts/migration/migrate_npm_api.py
31ccad3ec3c880119f3b7002f0c6652f8b43634edad8ab3763cb0e4497d0bb1f scripts/migration/migrate_npm_core.py 31ccad3ec3c880119f3b7002f0c6652f8b43634edad8ab3763cb0e4497d0bb1f scripts/migration/migrate_npm_core.py
308ceeca51ddfb43db8be8286d5286aff1a44979f84af87d9346fb4176499c0c scripts/resolve_dependencies_0312.sh
20124173452b4514bad8850c1637569a9a191882478451e549bd16b432605fc1 scripts/runtime/acme_provider.py 20124173452b4514bad8850c1637569a9a191882478451e549bd16b432605fc1 scripts/runtime/acme_provider.py
e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/runtime/acme_rfc2136.py e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/runtime/acme_rfc2136.py
22e880e98c7b9d700c7a4c02b9b1d543e5dbd1f4ecdca42813a1083165b32a19 scripts/version.py 22e880e98c7b9d700c7a4c02b9b1d543e5dbd1f4ecdca42813a1083165b32a19 scripts/version.py
@@ -188,14 +189,14 @@ bc9eb32f0d2785bac122d5eff44742cd24306c503daa065564383d46416b2211 src/admin/acce
d7aa68a78a4cb5a61342d92c1f3209af230d0f9ca3fea1115f67078372e6f37c src/admin/file_logs_io.rs d7aa68a78a4cb5a61342d92c1f3209af230d0f9ca3fea1115f67078372e6f37c src/admin/file_logs_io.rs
899e87a73bfe46c4078a46f9e1eb1c17b384472e8a30d2bab5486872b43fc809 src/admin/logging_api.rs 899e87a73bfe46c4078a46f9e1eb1c17b384472e8a30d2bab5486872b43fc809 src/admin/logging_api.rs
58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs 58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs
2f7e9ff687147cbf751f88828479e970727594d7c21726b1470e8c59ea40289e src/admin/openapi.json 8c58aec76463fc512ee87a8cceff7b905488c6f70185c055c496d235badea6d7 src/admin/openapi.json
0d1a5324e8ad32f5ab0861b722663f50f066e256bc5fd5dcced6a63f2bd399d1 src/admin/operations.rs 0d1a5324e8ad32f5ab0861b722663f50f066e256bc5fd5dcced6a63f2bd399d1 src/admin/operations.rs
2b0ec6df0ed9e1bdc5c9e4ff1758b59dd14abc150f98365ac3edcf4672908e48 src/admin/panel_assets.rs 2b0ec6df0ed9e1bdc5c9e4ff1758b59dd14abc150f98365ac3edcf4672908e48 src/admin/panel_assets.rs
75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs 75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs
7c113cd9d8642a808c73ebee24e695f7a318995e39dc2b81e747d2b2246e277f src/admin/preferences.rs 7c113cd9d8642a808c73ebee24e695f7a318995e39dc2b81e747d2b2246e277f src/admin/preferences.rs
d9e90166d2199152585a5208feedfc1dd1395dc32ddbd2a094ae4a1aee45e115 src/admin/preferences_columns.rs d9e90166d2199152585a5208feedfc1dd1395dc32ddbd2a094ae4a1aee45e115 src/admin/preferences_columns.rs
9baf3c0ec4c543bda51605930ac57d6425c5e03e06d29ff755a7ce3ff611aeaf src/admin/public.rs 9baf3c0ec4c543bda51605930ac57d6425c5e03e06d29ff755a7ce3ff611aeaf src/admin/public.rs
5135f743f628103deda9f50629065ca343f7a5bf872dcd3cc0033ac2971e361b src/admin/public/tests.rs c9f952bec99dfe1d5a60998a38bd177f82f54de567230413e0c322ee561eba7c src/admin/public/tests.rs
6125419b099cf4d330767bc07424d52a2b400f273d31084c058c43a72a6427b7 src/admin/refresh_preferences.rs 6125419b099cf4d330767bc07424d52a2b400f273d31084c058c43a72a6427b7 src/admin/refresh_preferences.rs
f99655cf19b04b1eb7fb49d12bfd2ba1ef2c095b81e80ef46cfddfacefc91936 src/admin/router.rs f99655cf19b04b1eb7fb49d12bfd2ba1ef2c095b81e80ef46cfddfacefc91936 src/admin/router.rs
8708427263cd4caf8454b9115e89acb09172483a090c6b0bd43811dd9e52c7f6 src/admin/session.rs 8708427263cd4caf8454b9115e89acb09172483a090c6b0bd43811dd9e52c7f6 src/admin/session.rs
@@ -207,7 +208,7 @@ cadd7dca29b9aad8c77c7bc23dd249ea37a15adfa4d636a8ef00273a91ce35dc src/admin/tail
5df98246c46135a176b4b8b705c92fd328719e82eb7342f088256abb976d5ae7 src/admin/tests/part_01.rs 5df98246c46135a176b4b8b705c92fd328719e82eb7342f088256abb976d5ae7 src/admin/tests/part_01.rs
6f8c1e30babb2cc4cb7accf4bfa28338651a469b16341f6e7eaec881dd9abf2b src/admin/tests/part_02.rs 6f8c1e30babb2cc4cb7accf4bfa28338651a469b16341f6e7eaec881dd9abf2b src/admin/tests/part_02.rs
34cf08cafb63cf7a8a6336e5c81fc99abfce8669662e387a3702c3a532be8056 src/admin/tests/part_03.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 5f0062abc42a3b81c79088dc0824ab4c81a22c6ea765e77d2527d07d13bffd58 src/admin/tests/part_05.rs
311c4ad16b3ad5b295a339241afd4ca8ea0d3c7f54f609c27518b4de1b4a4b71 src/admin/tests/part_06.rs 311c4ad16b3ad5b295a339241afd4ca8ea0d3c7f54f609c27518b4de1b4a4b71 src/admin/tests/part_06.rs
5fa006413fd93529ac9aaa4ffb28bf6b0b88d0a55a0bf0c49576db4a06fe9fc3 src/admin/tests/part_07.rs 5fa006413fd93529ac9aaa4ffb28bf6b0b88d0a55a0bf0c49576db4a06fe9fc3 src/admin/tests/part_07.rs
@@ -255,28 +256,28 @@ a6e7bbff4fd2d50ecf759ed28161a672d7b483af2b35c9f912746a7a06a9f476 src/admin/web/
f2689283cfe43d6aae911b0df778a69335089be230fbfc6de5dcce1801c38427 src/admin/web/vhosts/render.js f2689283cfe43d6aae911b0df778a69335089be230fbfc6de5dcce1801c38427 src/admin/web/vhosts/render.js
91e3417fba7e8f59023cd64813befccc8341741cd4aa53dfa1ec68a03c7f4632 src/admin/web/web-server-profiles.js 91e3417fba7e8f59023cd64813befccc8341741cd4aa53dfa1ec68a03c7f4632 src/admin/web/web-server-profiles.js
de3326d4fae254eb0b8c190f02e762f4094dcba86bfdcb24c5fb472676ebcc4b src/admin/web/web-server.js de3326d4fae254eb0b8c190f02e762f4094dcba86bfdcb24c5fb472676ebcc4b src/admin/web/web-server.js
98c00d99822f9be54fc58afb6d4b577901712effd2c5eddce014312681864625 src/auth/files.rs 76b0d92e4360e06ae97f87c0f6ddde4962c44efdeec9727e5b9a720598302891 src/auth/files.rs
b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs
97b5a060f24a102bbaa2f406426e985236c0d719b8038db210c25b34fcd8e923 src/auth/mod.rs 105cb934956457a68adc14f2c64569d9d1d40b8da0a7908a9a59fc2153a939c9 src/auth/mod.rs
c96a87e82ff0c1c9ce3d0cd5135903bb53af78d1c4fb6ebfe5fea503bf5644ea src/auth/store.rs c96a87e82ff0c1c9ce3d0cd5135903bb53af78d1c4fb6ebfe5fea503bf5644ea src/auth/store.rs
745226cabb007819370fc6aa680995a03a8a13fa057dacfab0fcc21d0c43bbe4 src/auth/tests.rs 745226cabb007819370fc6aa680995a03a8a13fa057dacfab0fcc21d0c43bbe4 src/auth/tests.rs
09c3a622a7d2169e49e3369ea5f0f816eab2f717eaf1675c625d518c8f34d367 src/backup.rs 09c3a622a7d2169e49e3369ea5f0f816eab2f717eaf1675c625d518c8f34d367 src/backup.rs
ebce56a154c57bf20d6618de161359c7c49392d7d24e8d23428b7d78a7a060c1 src/backup/archive.rs 76aa2afe9e7302be39b3512f30d0448d9134300f8ae930d64ada40cf3ad4a8a3 src/backup/archive.rs
732cd4f9ed38ae69f8bbae61b1db8baaa6f86b07ca21c41f24a206a30f0a96fc src/backup/ownership.rs 732cd4f9ed38ae69f8bbae61b1db8baaa6f86b07ca21c41f24a206a30f0a96fc src/backup/ownership.rs
076350506215faec3cfe7d9de4e60baf29672cc22a41eabe601dcc510c257799 src/backup/snapshot.rs 076350506215faec3cfe7d9de4e60baf29672cc22a41eabe601dcc510c257799 src/backup/snapshot.rs
a00571b409d305af86ab2a57a25434b5ab58706772df440d017fc1fec097f7b4 src/backup/tests.rs a00571b409d305af86ab2a57a25434b5ab58706772df440d017fc1fec097f7b4 src/backup/tests.rs
b2ce83a48736c082e6b820e4e363cca7a5018f3ae2a1ce5adfd6af1a03d62598 src/backup/validation.rs f5afae218f3a6f91bc2f1f5a90094630ce5b7ca574de61807b5a317b13937ec2 src/backup/validation.rs
426a946b2c9e244f09f6cbc5f9664b8ad0737529a9bb5a3d549d27196a1f8043 src/balance.rs 426a946b2c9e244f09f6cbc5f9664b8ad0737529a9bb5a3d549d27196a1f8043 src/balance.rs
3d4b3986f54bed99212063b6d135d40a202d556a3e4d253e24c74be4cb9e8ebc src/balance/health.rs 3d4b3986f54bed99212063b6d135d40a202d556a3e4d253e24c74be4cb9e8ebc src/balance/health.rs
a1e5d43f5417d71f3974812cb539c0932f206e2290090917f067494f3587fc72 src/balance/lease_runtime.rs a1e5d43f5417d71f3974812cb539c0932f206e2290090917f067494f3587fc72 src/balance/lease_runtime.rs
a57f352aad3b5a9a6debb59625864808f0a0ff7b11db83b47d6825a5932ca16b src/balance/model.rs 46d739f14b510b35aaccf18fb9f21593c92d8b8d3ea47828271574df5ed008e5 src/balance/model.rs
7628aa331c9dbb02650392afeb65838cc23e32939c002d5d0488a8f3b8c11bd4 src/balance/pool_core.rs 2a6b20ffc2b326cb2380beb0805b52181306d3b9cc57330d67930000535c03f9 src/balance/pool_core.rs
477132dd381e23f1be197c32ff58a0925f41d3e6aa63fe77c10973cefb2b76f6 src/balance/pool_selection.rs 477132dd381e23f1be197c32ff58a0925f41d3e6aa63fe77c10973cefb2b76f6 src/balance/pool_selection.rs
519cb798040ac9a65d9ebe43ae11ef729bd78c5c4a5c756490458add4b6fdc24 src/balance/pool_view.rs 519cb798040ac9a65d9ebe43ae11ef729bd78c5c4a5c756490458add4b6fdc24 src/balance/pool_view.rs
7d94d1bd4eb698e0588b822d93e56e3dc5594c46edac4aa9713f148893ebe6d5 src/balance/tests.rs 7d94d1bd4eb698e0588b822d93e56e3dc5594c46edac4aa9713f148893ebe6d5 src/balance/tests.rs
81141aedaec495183b88a4f6362a71f56ee14cbfa83d71fce51d8abed8df3f5d src/balance/tests/part_01.rs 81141aedaec495183b88a4f6362a71f56ee14cbfa83d71fce51d8abed8df3f5d src/balance/tests/part_01.rs
490b38a8b1472dbd9fcb823ec0ed0e92498fb9528b982bcee340c8579e89ae36 src/balance/tests/part_02.rs 490b38a8b1472dbd9fcb823ec0ed0e92498fb9528b982bcee340c8579e89ae36 src/balance/tests/part_02.rs
ccb3e1988d6ae5825450e894eea896d53ae9fac150d0175ea5e742413e55105a src/basic_auth.rs 5049d2bd01a9c866f3bad2288d8d594df96bba6045dfe15cd22b4173562b7388 src/basic_auth.rs
9a8858b7ff461a4d961ba701ca3ed6eaec15c3f7f05eef8e0e30edc63617605a src/basic_auth/tests.rs 9a8858b7ff461a4d961ba701ca3ed6eaec15c3f7f05eef8e0e30edc63617605a src/basic_auth/tests.rs
7def03ace07dc38a4806277d75b6ce673d33ddaaf1c4855fa0a3372be1cf9489 src/cache/cold.rs 7def03ace07dc38a4806277d75b6ce673d33ddaaf1c4855fa0a3372be1cf9489 src/cache/cold.rs
a9eeb93519aae2de7a1282296ced91d2eb8307cc966f8fbf6085af0fd21b0ad2 src/cache/cold/cleanup.rs a9eeb93519aae2de7a1282296ced91d2eb8307cc966f8fbf6085af0fd21b0ad2 src/cache/cold/cleanup.rs
@@ -309,7 +310,7 @@ d426d341a7678f8ec70406d86052c97d0f49804bbcd8402951103d61a735b9da src/cache/poli
7e2bcfbb2b57732cdc704d9df7a9298fb80e735e37984431bc936630e41e7f8a src/cache/range.rs 7e2bcfbb2b57732cdc704d9df7a9298fb80e735e37984431bc936630e41e7f8a src/cache/range.rs
7ad96b3baae36ec49c90d03b380e749944fb3d54abb045c90db3a35f6b89b1c1 src/cache/tests.rs 7ad96b3baae36ec49c90d03b380e749944fb3d54abb045c90db3a35f6b89b1c1 src/cache/tests.rs
13dab7b7a587cfc146826a92ab94ec9e04f7e500b7a84e678d77226829844923 src/cache/tests/cold_tier_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 f43aa6c231f2c85ee3390dddab74aab2eea2a3f874acb35f145c23866266cb52 src/cache/tests/cold_tier_tests/part_02.rs
22cb9bf20a0d25bee30c1e2213dfe8278ad7d0d5d294275d47d4a6ff9ac646ab src/cache/tests/dashboard_tests.rs 22cb9bf20a0d25bee30c1e2213dfe8278ad7d0d5d294275d47d4a6ff9ac646ab src/cache/tests/dashboard_tests.rs
d7a02eb7bad37e6a5524600aabb5e60ccf50e26c7a487e2806891c9d8acd48f3 src/cache/tests/http_invalidation_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 cc682f0d5754e0b806a4eb5950e4cc00611aca5ce4668414fddef1d692489805 src/proxy/tests/authority_tests.rs
d134b0eb617f82f9971b3f3461379ea37c57bd0079bf54748d0c434b2ef1b475 src/proxy/tests/http_invalidation_tests.rs d134b0eb617f82f9971b3f3461379ea37c57bd0079bf54748d0c434b2ef1b475 src/proxy/tests/http_invalidation_tests.rs
e80c7fe44abb7607c64ea02d56542753dc25290b3bcf4c4d45b75f2db6ca90d0 src/proxy/types.rs e80c7fe44abb7607c64ea02d56542753dc25290b3bcf4c4d45b75f2db6ca90d0 src/proxy/types.rs
26afb4dcda99a54abdd85f7950ecac6d608eca78067323f695a43382d21fa2fb src/proxy/upstream_helpers.rs 55c44a5d0c6588beea67439d62e3c9f57041e4d07cb1f8cf293efd856ceb8eff src/proxy/upstream_helpers.rs
69f775f6746b4c6d729dc10961dbef54168846f4ce9cf7fe453ab5a2b879c9b8 src/proxy/websocket.rs 69f775f6746b4c6d729dc10961dbef54168846f4ce9cf7fe453ab5a2b879c9b8 src/proxy/websocket.rs
016a60b2471edd3df9bbe9f92a9cebea11047178b83ff0e3b2351c12814a4939 src/proxy/websocket/handshake.rs d866e7e72dee6ff54c2418c34e9899cbcd7e4e027d9cbe2b625044dc9062e4ee src/proxy/websocket/handshake.rs
7e9c4d4c207badabf90630223964bb2ad641f06d4149843cab751f192b8b4adf src/proxy/websocket/tests.rs 7e9c4d4c207badabf90630223964bb2ad641f06d4149843cab751f192b8b4adf src/proxy/websocket/tests.rs
a44421714aab37f7ceda19e3ec2512c645d2a3ce5dcb3da5bc7a0d87a442d6a8 src/proxy/websocket/tunnel.rs a44421714aab37f7ceda19e3ec2512c645d2a3ce5dcb3da5bc7a0d87a442d6a8 src/proxy/websocket/tunnel.rs
15fabd38576b61f650c039acdbf7ac5a5bd24330062475b94315285b2dc258b6 src/proxy/websocket/validation.rs 15fabd38576b61f650c039acdbf7ac5a5bd24330062475b94315285b2dc258b6 src/proxy/websocket/validation.rs
@@ -428,7 +429,7 @@ d0939e1b0829c25356a0b67ecb3a178c74936dd07baa15fee24eb32544db6cad src/runtime.rs
e90df3fff8a25465425b099ae358d19085809e56d948ba47e01e53f8b93d5dd0 src/runtime/tests.rs e90df3fff8a25465425b099ae358d19085809e56d948ba47e01e53f8b93d5dd0 src/runtime/tests.rs
0046525317ffbd5d3b34845656b0f58d9d31f38b3d8acd71e90d94bd7cdfb8ab src/runtime/types.rs 0046525317ffbd5d3b34845656b0f58d9d31f38b3d8acd71e90d94bd7cdfb8ab src/runtime/types.rs
88bc546fdda20d3f1f8f5e7d7872ede1fa334b8bb5102c729bd72f56e3fb94b9 src/runtime/updates.rs 88bc546fdda20d3f1f8f5e7d7872ede1fa334b8bb5102c729bd72f56e3fb94b9 src/runtime/updates.rs
b4c17b304166592e63c81884076589fbaf725c3f57043f8924c46167b061ac5b src/secret_store.rs b5062082d7fc7c9b08533283e1f0a841f85a42917b6ebf135760b04023884d9a src/secret_store.rs
6dca8da072a3bd610704a391bb6e25c9042971f4ebc60edb47665ce1d74cbc58 src/secret_store/tests.rs 6dca8da072a3bd610704a391bb6e25c9042971f4ebc60edb47665ce1d74cbc58 src/secret_store/tests.rs
6fa1d8c352bce7a16ff0208bc4cdbe80404715df0aff4ed26910a5df57cbbcd4 src/security.rs 6fa1d8c352bce7a16ff0208bc4cdbe80404715df0aff4ed26910a5df57cbbcd4 src/security.rs
0e5fc2aff856bf6fd959cba12b5ddd1e3b3cbeb0fbaadde906c811cdfafdaae7 src/security/tests.rs 0e5fc2aff856bf6fd959cba12b5ddd1e3b3cbeb0fbaadde906c811cdfafdaae7 src/security/tests.rs
@@ -451,12 +452,12 @@ d9123c623e2a97d09e41edaa29993b4b2c8cba39a3abdf2b48a205aa9a9c2939 src/system_sta
7467d83066bb6986d8c9103597f7a7617ac677579ed46d573c573ff6ca57bbd9 src/tinyauth.rs 7467d83066bb6986d8c9103597f7a7617ac677579ed46d573c573ff6ca57bbd9 src/tinyauth.rs
0e1d87554db55e724f30c41ec4a5c80bdb8fc9c8cba8700ac509864d4abf8871 src/tinyauth/tests.rs 0e1d87554db55e724f30c41ec4a5c80bdb8fc9c8cba8700ac509864d4abf8871 src/tinyauth/tests.rs
1084cccd9de28acd6cb0f0813de7c0953910f51726f73f4acdb2e3ada4593976 src/tls.rs 1084cccd9de28acd6cb0f0813de7c0953910f51726f73f4acdb2e3ada4593976 src/tls.rs
5919db3296523dd0b7a8e0cccbe44266500a6a5183a7a08cc195d7f9847886dc src/tls/certificates.rs 4972a436f8878dec5f9eb8ff17eb82670b0bfd9e94e821c516575479f5fc1919 src/tls/certificates.rs
436575144aa352498a4696f12c2258bef3a898f3f50e6fbc6d67c3b93afa099f src/tls/external_paths.rs 436575144aa352498a4696f12c2258bef3a898f3f50e6fbc6d67c3b93afa099f src/tls/external_paths.rs
3e6947dde167f82a5efc407ccf9bd4c9f5b26cc8633d1418a10284bc1a1503d4 src/tls/server_config.rs 3e6947dde167f82a5efc407ccf9bd4c9f5b26cc8633d1418a10284bc1a1503d4 src/tls/server_config.rs
893d1077fc0d9a08598596a1d3f4bde7019357fac27ebe13f52c11a4859a1d25 src/tls/tests.rs 31c0f51a3a0bab433f438c3ba1b1cdaf3c63fae6086e7d99b3464a537f75a5e1 src/tls/tests.rs
e0ab65406911d4b95cb26594df65779a88d9011d5b11adb22c1a28831cda3826 src/tls/tests/acme.rs 2af2164ecdf2b7fd9eef7c6e358dec053dfac9aedaa501dbb75df742d18638b2 src/tls/tests/acme.rs
cfab6b61f0d121a15d6f78943091c770cb5a2a023d68d66be3c0228f9c8adf9c src/tls/tests/external_paths.rs 93b359e9defd62506729bc90e56c96783e2f14dd477dd67f337ca83149da7168 src/tls/tests/external_paths.rs
10c4a187e3aeb0c72a8c8968ea0cc8bfae4e3cb2f3d834ebd3da1056c4171ec5 src/tls/types.rs 10c4a187e3aeb0c72a8c8968ea0cc8bfae4e3cb2f3d834ebd3da1056c4171ec5 src/tls/types.rs
c2f802d95da4332fb8480e83f0c7502334457b3408141c1c4516c9ba320a0463 src/traffic.rs c2f802d95da4332fb8480e83f0c7502334457b3408141c1c4516c9ba320a0463 src/traffic.rs
36301bcbd20b557dd3d58e77a9175f3581947f577c0bb0b972899cec6786b03f src/traffic/tests.rs 36301bcbd20b557dd3d58e77a9175f3581947f577c0bb0b972899cec6786b03f src/traffic/tests.rs
@@ -509,7 +510,7 @@ c5cb561b0f276e8082f904b9446fa5885f6b3c0608f25ea2ef425061e9f7f6b4 src/web_server
518856a221161430cb0ab3e411fa81cab07e1a6e52edd470f026d25c5307943e src/web_server/serve.rs 518856a221161430cb0ab3e411fa81cab07e1a6e52edd470f026d25c5307943e src/web_server/serve.rs
63d71a336187779e9cbc1cc6afa065c5fa9472835a8c2cc15dbf4527279d0c90 src/web_server/tests.rs 63d71a336187779e9cbc1cc6afa065c5fa9472835a8c2cc15dbf4527279d0c90 src/web_server/tests.rs
9426ad19fd6234e944d6e122a995d2cf8ebe4177d27e42fd157f64d05abd1e23 src/web_server/tests/part_01.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 8f9a0e2b0a69a1f2b8cf7505a654606150da4a53bc8c09d1d6c4b8751185b134 src/web_server/tests/part_03.rs
b25b6e0082b5153272119c7faba9d6f2ccdd288ce5c066526431c56954c66c4c src/web_server/tests/part_04.rs b25b6e0082b5153272119c7faba9d6f2ccdd288ce5c066526431c56954c66c4c src/web_server/tests/part_04.rs
f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json
@@ -522,23 +523,24 @@ b04362ad082a32c9ccc4cf38c46f44d71347adecf7aba3224b3af3ea4872e3d0 tools/README.m
fd082812411cff7afb71b2289a6f1a469b8d3c8a7cfdcdd9e567a2a4baf81be4 tools/checks/admin_surface_audit_0310.py fd082812411cff7afb71b2289a6f1a469b8d3c8a7cfdcdd9e567a2a4baf81be4 tools/checks/admin_surface_audit_0310.py
412255d383fcc8306c6086784fb416d9814e3ec790e440a0927431ca615f3411 tools/checks/backend_cache_0297_ui_test.mjs 412255d383fcc8306c6086784fb416d9814e3ec790e440a0927431ca615f3411 tools/checks/backend_cache_0297_ui_test.mjs
2a493dbce2991a23d807c01952f58608a364f541560836b4cb385e75e108e9e7 tools/checks/backend_health_0300_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 0b583ee59dcccc35b15ef4e211d7e6546a8aaa7b0286ceab03c0b0dc084c1e07 tools/checks/dashboard_boot_0311_ui_test.mjs
5f798699502c7a18e7b45aa84bdcf7ad0ccdb77f11cf2003527c97f0391c7abe tools/checks/dashboard_startup_0310_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 e33a9cc51d58bc92a637c0e6a1c88cbd24ba4605be55748212e296c3c35a4060 tools/checks/high_traffic_memory_model.py
4f7821e7e25b09ce251d25a0b3fb4d8ce8396c36a5ed971c2637aeb325425909 tools/checks/langs_check.py 4f7821e7e25b09ce251d25a0b3fb4d8ce8396c36a5ed971c2637aeb325425909 tools/checks/langs_check.py
f8f87ba891338b565249d02c3cf5f02cec44e606b4205de99fcbea1eb2c24d57 tools/checks/memory_contracts.py f8f87ba891338b565249d02c3cf5f02cec44e606b4205de99fcbea1eb2c24d57 tools/checks/memory_contracts.py
5b7901747d879e12a39ed82b6efc513bcdddd15d508b5828210d2c83398fb6a4 tools/checks/panel_cache_http_0310_ui_test.mjs 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 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 f69845bc26ec33160fc756953991cf32aa8ad6a7fb4a131caa2093db91e783ef tools/checks/rust_module_contracts.py
ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed tools/checks/rust_source.py ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed tools/checks/rust_source.py
adf84a983e0dbba228e47d71ec2d5c9964f7ca9989d5139de58f96ee961b035a tools/checks/security_contracts.py adf84a983e0dbba228e47d71ec2d5c9964f7ca9989d5139de58f96ee961b035a tools/checks/security_contracts.py
23b9250245e6bec5ef728c73cee4c88f64924afc3597f8a9b58a1924235df98e tools/checks/security_dependency_contracts.py 23b9250245e6bec5ef728c73cee4c88f64924afc3597f8a9b58a1924235df98e tools/checks/security_dependency_contracts.py
5c1d428cbda5f08b8d6847732672e45e9da96b545337334e473986e119197de2 tools/checks/source_module_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 543a488951ea6ec8fe06e04f168ccc4d4f2af3cfc281c9a728ab7bd87377fa98 tools/checks/system_info_no_subprocess_contracts.py
614ed0c775e32ec5bfb4ee75aaa90206a10da0f43659747a7ec76fd93e33767a tools/checks/version_contracts.py 614ed0c775e32ec5bfb4ee75aaa90206a10da0f43659747a7ec76fd93e33767a tools/checks/version_contracts.py
2020e171be1a044f7ca2ab83af7604a014c36fe4ff04791d0620ccbf1bf8e000 tools/checks/vhost_origin_errors_0297_ui_test.mjs 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 0398892be30fe416a00578915c588f360fd93244e3af77ece834e560e547d1c8 tools/checks/web_server_contracts.py
35f76060e585a96c623ef83bd5f771969f5be16a0e1ae626a3092d7458b84e84 tools/checks/web_service_ui_test.mjs 35f76060e585a96c623ef83bd5f771969f5be16a0e1ae626a3092d7458b84e84 tools/checks/web_service_ui_test.mjs
adaa1ed9e5958739df40190055c780c80d0a8e270ec8dc105ed3fa2dbb8fb74f tools/checks/www_redirect_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 d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a tools/dev/mock_auth.py
2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 tools/dev/mock_server.py 2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 tools/dev/mock_server.py
538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba tools/dev/test_compression.py 538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba tools/dev/test_compression.py
+1
View File
@@ -101,6 +101,7 @@ if [[ "$mode" == "test" ]]; then
python3 tools/checks/security_contracts.py python3 tools/checks/security_contracts.py
python3 tools/checks/admin_surface_audit_0310.py python3 tools/checks/admin_surface_audit_0310.py
python3 tools/checks/security_dependency_contracts.py python3 tools/checks/security_dependency_contracts.py
python3 tools/checks/dependency_upgrade_0312.py
if command -v node >/dev/null 2>&1; then if command -v node >/dev/null 2>&1; then
node tools/checks/web_service_ui_test.mjs node tools/checks/web_service_ui_test.mjs
node tools/checks/backend_cache_0297_ui_test.mjs node tools/checks/backend_cache_0297_ui_test.mjs
+4 -4
View File
@@ -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) ## First build / offline assets (fixed in 0.7.2)
No `SWAGGER_UI_DOWNLOAD_URL` environment variable or `.cargo/config.toml` is required. 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 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 is removed: there is no separate dependency build silently choosing its older
historical browser default. Direct Cargo builds using `--manifest-path` use the 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. 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 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 ```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. # Optional: require the archive digest independently verified by your build team.
# export SWAGGER_UI_ARCHIVE_SHA256=<64-hex-digit-verified-digest> # export SWAGGER_UI_ARCHIVE_SHA256=<64-hex-digit-verified-digest>
bash dev.sh check 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/ - 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 configuration discovery: https://doc.rust-lang.org/cargo/reference/config.html
- Cargo build scripts: https://doc.rust-lang.org/cargo/reference/build-scripts.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
+13 -1
View File
@@ -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) ## 0.31.1 — mobile/reverse-proxy dashboard startup (2026-10-10)
Executable → Regular
+94 -4
View File
@@ -27,6 +27,9 @@ Environment:
RUSTPROXY_ALLOW_REMOTE_ADMIN Set to 1 only to permit plain-HTTP non-loopback admin on first install 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_PREFIX Managed install prefix (default /opt/rustproxy)
RUSTPROXY_BUILD_JOBS Cargo jobs for low-memory builds (default 1) 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 USAGE
exit 0 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" "$@" 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 git clone --depth 1 "$REPO_URL" "$work/RustProxy"
run_builder bash -lc "wget -qO- --https-only --secure-protocol=TLSv1_2 https://sh.rustup.rs | sh -s -- -y --profile minimal"
# 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 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 # Keep source builds reliable on small Debian VPSes. The repository release
# profile may use LTO/codegen settings that can make rustc exceed RAM limits. # 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 exit 1
fi 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" cd "$work/RustProxy"
if [[ "$MODE" == install ]]; then if [[ "$MODE" == install ]]; then
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM rust:1.88-trixie AS build FROM rust:1.99-trixie AS build
WORKDIR /build WORKDIR /build
COPY . . COPY . .
+2 -2
View File
@@ -22,7 +22,7 @@ from urllib.request import HTTPRedirectHandler, Request, build_opener, url2pathn
import zipfile import zipfile
import zlib 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" DOWNLOAD_URL = f"https://github.com/swagger-api/swagger-ui/archive/refs/tags/v{VERSION}.zip"
PREFIX = f"swagger-ui-{VERSION}/" PREFIX = f"swagger-ui-{VERSION}/"
MAX_ARCHIVE = 64 * 1024 * 1024 MAX_ARCHIVE = 64 * 1024 * 1024
@@ -32,7 +32,7 @@ ASSETS = {
} }
LICENSE_MEMBERS = ("LICENSE", "NOTICE", "dist/swagger-ui-bundle.js.LICENSE.txt") 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, " 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.") "to a reviewed copy of the same tag archive. See docs/API_ACCESS.md.")
+29
View File
@@ -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'
+2 -2
View File
@@ -2,7 +2,7 @@
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
"title": "RustProxy Administration API", "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." "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": [ "servers": [
@@ -10893,4 +10893,4 @@
} }
} }
} }
} }
+1
View File
@@ -1,5 +1,6 @@
use super::*; use super::*;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use http::header;
use http_body_util::BodyExt; use http_body_util::BodyExt;
use std::io::Read; use std::io::Read;
+2 -2
View File
@@ -284,7 +284,7 @@
let first_body = json!({ let first_body = json!({
"certificate_id": "opnsense-cert", "certificate_id": "opnsense-cert",
"cert_pem": first.cert.pem(), "cert_pem": first.cert.pem(),
"key_pem": first.key_pair.serialize_pem(), "key_pem": first.signing_key.serialize_pem(),
}) })
.to_string(); .to_string();
@@ -326,7 +326,7 @@
let second_body = json!({ let second_body = json!({
"certificate_id": "opnsense-cert", "certificate_id": "opnsense-cert",
"cert_pem": second.cert.pem(), "cert_pem": second.cert.pem(),
"key_pem": second.key_pair.serialize_pem(), "key_pem": second.signing_key.serialize_pem(),
}) })
.to_string(); .to_string();
let response = router.oneshot(deploy(second_body)).await.unwrap(); let response = router.oneshot(deploy(second_body)).await.unwrap();
+9 -7
View File
@@ -46,14 +46,16 @@
interception.append(toggle, codesLabel, hint); parent.append(interception); interception.append(toggle, codesLabel, hint); parent.append(interception);
const errors = errorsEditor(parent, values.errors); const errors = errorsEditor(parent, values.errors);
parent.append(label("p", "pagesSafetyHint", "fine")); parent.append(label("p", "pagesSafetyHint", "fine"));
return { read: () => { return {
const selectedTokens = enabled.checked ? statuses.value.split(/[\s,]+/).filter(Boolean) : []; read: () => {
const selected = selectedTokens.map(Number); const selectedTokens = enabled.checked ? statuses.value.split(/[\s,]+/).filter(Boolean) : [];
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)))) { const selected = selectedTokens.map(Number);
throw new Error(t("vhostInterceptOriginInvalid")); 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; 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() }) }; } 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() }) }; }
+2 -2
View File
@@ -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-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-hosts").value = (admin.allowed_hosts || []).join("\n");
$("admin-session-days").value = Number(admin.session_days || 30); $("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; const listenLocked = !!adminStatus?.listen_locked;
$("admin-listen").disabled = listenLocked; $("admin-listen-lock-note").hidden = !listenLocked; $("admin-listen").disabled = listenLocked; $("admin-listen-lock-note").hidden = !listenLocked;
@@ -300,7 +300,7 @@ curl --fail --show-error --request POST \
}, "prometheus"); }, "prometheus");
bindSettingsForm("administration-form", next => { bindSettingsForm("administration-form", next => {
const locked = !!latestStatus?.administration?.listen_locked; 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"); }, "administration");
bindSettingsForm("tls-policy-form", next => { bindSettingsForm("tls-policy-form", next => {
if (!$("tls-policy-12").checked && !$("tls-policy-13").checked) throw fieldError($("tls-policy-12"), t("tlsPolicyVersionRequired")); if (!$("tls-policy-12").checked && !$("tls-policy-13").checked) throw fieldError($("tls-policy-12"), t("tlsPolicyVersionRequired"));
+93 -25
View File
@@ -252,15 +252,21 @@ html.app-startup-failed .app-startup-recovery {
} }
@keyframes app-startup-spin { @keyframes app-startup-spin {
to { transform: rotate(360deg) } to {
transform: rotate(360deg)
}
} }
@keyframes app-startup-reveal { @keyframes app-startup-reveal {
to { visibility: visible } to {
visibility: visible
}
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.app-startup-spinner { animation: none } .app-startup-spinner {
animation: none
}
} }
body { body {
@@ -5416,7 +5422,9 @@ dialog .window-heading {
} }
@media (max-width: 650px) { @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 { .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. */ /* 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-h1 i {
.stat-chart .stat-chart-legend .legend-h2 i { background: var(--traffic-h2); } background: var(--traffic-h1);
.stat-chart .stat-chart-legend .legend-h3 i { background: var(--traffic-h3); } }
.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 { .stat-chart .stat-chart-legend b {
font-family: var(--mono); font-family: var(--mono);
@@ -9946,6 +9962,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
width: 100% width: 100%
} }
} }
.listener-active-summary { .listener-active-summary {
margin-top: 10px; margin-top: 10px;
padding: 10px 12px; padding: 10px 12px;
@@ -10002,7 +10019,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
margin-top: 10px margin-top: 10px
} }
.location-header-editor .vh-header-rule-head > button { .location-header-editor .vh-header-rule-head>button {
margin-left: auto; margin-left: auto;
flex: 0 0 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 { .php-switch-row {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -10023,6 +10039,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
min-height: 30px; min-height: 30px;
margin: 4px 0; margin: 4px 0;
} }
.php-switch-row .php-switch-label { .php-switch-row .php-switch-label {
display: inline-flex !important; display: inline-flex !important;
flex: 0 1 auto; flex: 0 1 auto;
@@ -10032,14 +10049,26 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
margin: 0 !important; margin: 0 !important;
line-height: 1.5; line-height: 1.5;
} }
.php-switch-row .php-switch-label input[type="checkbox"] { .php-switch-row .php-switch-label input[type="checkbox"] {
position: static; position: static;
flex: 0 0 var(--ui-check-size); flex: 0 0 var(--ui-check-size);
margin: 0; 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-switch-label span {
.php-switch-row .php-field-info .metric-info-popover { right: auto; left: 0; } 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 { .php-reference pre {
overflow-x: auto; overflow-x: auto;
white-space: pre; white-space: pre;
@@ -10049,6 +10078,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
background: var(--panel2); background: var(--panel2);
font: 12px/1.6 var(--mono); font: 12px/1.6 var(--mono);
} }
.php-profile-help-divider { .php-profile-help-divider {
width: 100%; width: 100%;
height: 0; height: 0;
@@ -10056,12 +10086,27 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
border: 0; border: 0;
border-top: 1px solid var(--line); 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-copy,
.page-intro-title-row { .page-intro-title-row {
min-width: 0; min-width: 0;
@@ -10090,30 +10135,47 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
font-size: clamp(18px, 5vw, 24px); font-size: clamp(18px, 5vw, 24px);
letter-spacing: -.6px letter-spacing: -.6px
} }
.page-intro-title-row .overview-refresh { .page-intro-title-row .overview-refresh {
padding: 7px 9px; padding: 7px 9px;
font-size: 11px font-size: 11px
} }
} }
/* Keep the system API indicator next to its action, aligned as one unit. */ .system-info-heading-actions {
.system-info-heading-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; } display: flex;
.system-info-heading-actions .pill { white-space: nowrap; } 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 { .cache-maintenance-help {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
position: relative; 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 { .cache-maintenance-info .metric-info-popover {
max-height: min(60vh, 420px); max-height: min(60vh, 420px);
overflow-y: auto; overflow-y: auto;
width: min(440px, calc(100vw - 56px)); width: min(440px, calc(100vw - 56px));
} }
.cache-maintenance-info .metric-info-popover code { .cache-maintenance-info .metric-info-popover code {
display: block; display: block;
white-space: pre-wrap; white-space: pre-wrap;
@@ -10122,5 +10184,11 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
font-size: 11px; font-size: 11px;
line-height: 1.55; 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;
}
+4 -2
View File
@@ -1,7 +1,7 @@
//! Local filesystem authority. The process owner can initialize or recover the sole admin. //! Local filesystem authority. The process owner can initialize or recover the sole admin.
use anyhow::{ensure, Context, Result}; use anyhow::{ensure, Context, Result};
use fs2::FileExt; use fs2::FileExt;
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::SysRng, TryRng};
use std::{ use std::{
fs::{self, File, OpenOptions}, fs::{self, File, OpenOptions},
io::Write, io::Write,
@@ -86,7 +86,9 @@ pub fn load_or_create_setup_token(dir: &Path) -> Result<String> {
return load_setup_token(dir); return load_setup_token(dir);
} }
let mut bytes = [0u8; 32]; 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 token: String = bytes.iter().map(|b| format!("{b:02x}")).collect();
let mut file = match private_options().create_new(true).open(&path) { let mut file = match private_options().create_new(true).open(&path) {
Ok(file) => file, Ok(file) => file,
+9 -9
View File
@@ -5,7 +5,7 @@ mod store;
use crate::{db::Store, runtime::ApiError}; use crate::{db::Store, runtime::ApiError};
use anyhow::{ensure, Context, Result}; use anyhow::{ensure, Context, Result};
use argon2::{ use argon2::{
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, password_hash::{phc::PasswordHash, PasswordHasher, PasswordVerifier},
Algorithm, Argon2, Params, Version, Algorithm, Argon2, Params, Version,
}; };
pub use files::{ pub use files::{
@@ -13,7 +13,7 @@ pub use files::{
prepare_directory, try_lock_directory, prepare_directory, try_lock_directory,
}; };
use http::{header::COOKIE, HeaderMap, StatusCode}; use http::{header::COOKIE, HeaderMap, StatusCode};
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::SysRng, TryRng};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::{ use std::{
path::PathBuf, path::PathBuf,
@@ -56,7 +56,9 @@ pub fn now() -> i64 {
} }
pub fn random_secret() -> String { pub fn random_secret() -> String {
let mut bytes = [0_u8; 32]; 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() bytes.iter().map(|b| format!("{b:02x}")).collect()
} }
pub fn secret_hash(secret: &str) -> String { 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<String> { pub(crate) fn hash_sync(password: &str) -> Result<String> {
let params = Params::new(19_456, 2, 1, None).context("Argon2 parameters")?; 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) Ok(Argon2::new(Algorithm::Argon2id, Version::V0x13, params)
.hash_password(password.as_bytes(), &salt)? .hash_password(password.as_bytes())?
.to_string()) .to_string())
} }
fn verify_sync(password: &str, encoded: &str) -> bool { fn verify_sync(password: &str, encoded: &str) -> bool {
PasswordHash::new(encoded).ok().is_some_and(|hash| { PasswordHash::new(encoded).is_ok()
Argon2::default() && Argon2::default()
.verify_password(password.as_bytes(), &hash) .verify_password(password.as_bytes(), encoded)
.is_ok() .is_ok()
})
} }
pub async fn hash_password_offline(password: String) -> Result<String> { pub async fn hash_password_offline(password: String) -> Result<String> {
validate_password(&password)?; validate_password(&password)?;
+4 -4
View File
@@ -1,7 +1,7 @@
use anyhow::{bail, ensure, Context, Result}; use anyhow::{bail, ensure, Context, Result};
use argon2::{Algorithm, Argon2, Params, Version}; use argon2::{Algorithm, Argon2, Params, Version};
use base64::Engine as _; 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 ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
@@ -231,8 +231,8 @@ fn derive_key(password: &str, salt: &[u8]) -> Result<Zeroizing<[u8; 32]>> {
fn new_crypto(password: &str) -> Result<CryptoContext> { fn new_crypto(password: &str) -> Result<CryptoContext> {
let mut salt = [0u8; 16]; let mut salt = [0u8; 16];
let mut nonce_prefix = [0u8; 4]; let mut nonce_prefix = [0u8; 4];
OsRng.fill_bytes(&mut salt); SysRng.try_fill_bytes(&mut salt).expect("secure operating system random source unavailable");
OsRng.fill_bytes(&mut nonce_prefix); SysRng.try_fill_bytes(&mut nonce_prefix).expect("secure operating system random source unavailable");
let header = CryptoHeader { let header = CryptoHeader {
format_version: FORMAT_VERSION, format_version: FORMAT_VERSION,
kdf: "Argon2id".into(), kdf: "Argon2id".into(),
@@ -395,7 +395,7 @@ fn decrypt_archive(input: &Path, output: &Path, password: &str) -> Result<Zeroiz
fn seal_master_key(master: &[u8], backup_key: &[u8]) -> Result<Vec<u8>> { fn seal_master_key(master: &[u8], backup_key: &[u8]) -> Result<Vec<u8>> {
ensure!(master.len() == 32, "invalid secret store master key"); ensure!(master.len() == 32, "invalid secret store master key");
let mut nonce_bytes = [0u8; 12]; 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 cipher = cipher(backup_key)?;
let mut encrypted = Zeroizing::new(master.to_vec()); let mut encrypted = Zeroizing::new(master.to_vec());
cipher cipher
+1 -1
View File
@@ -426,6 +426,6 @@ pub async fn restore(archive: &Path, data_dir: &Path, password: &str) -> Result<
pub fn generated_password() -> String { pub fn generated_password() -> String {
let mut bytes = [0u8; 32]; 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) base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes)
} }
+5 -2
View File
@@ -3,7 +3,7 @@ use crate::{config::CompiledBackend, vhost::PoolMember};
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use axum::body::Body; use axum::body::Body;
use http::{HeaderMap, HeaderValue, Request}; use http::{HeaderMap, HeaderValue, Request};
use rand::{distributions::Alphanumeric, Rng}; use rand::{distr::Alphanumeric, RngExt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
@@ -218,7 +218,10 @@ impl Default for Settings {
impl Settings { impl Settings {
/// Resolve active health per member; a disabled monitor never probes. /// Resolve active health per member; a disabled monitor never probes.
pub fn health_for(&self, member: &PoolMember) -> HealthCheck { 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 { if health.host.is_none() && health.mode == HealthMode::Http {
health.host = self.health_check.host.clone(); health.host = self.health_check.host.clone();
} }
+10 -8
View File
@@ -24,8 +24,8 @@ impl Pool {
state: Arc::new(BackendState::new(settings.health_for(&definition).mode)), state: Arc::new(BackendState::new(settings.health_for(&definition).mode)),
definition, definition,
backend, backend,
token: rand::thread_rng() token: rand::rng()
.sample_iter(&Alphanumeric) .sample_iter(Alphanumeric)
.take(32) .take(32)
.map(char::from) .map(char::from)
.collect(), .collect(),
@@ -53,14 +53,16 @@ impl Pool {
}; };
let same_origin = serde_json::to_vec(&member.definition.backend()).ok() let same_origin = serde_json::to_vec(&member.definition.backend()).ok()
== serde_json::to_vec(&old.definition.backend()).ok(); == serde_json::to_vec(&old.definition.backend()).ok();
let same_health = let same_health = serde_json::to_vec(&(
serde_json::to_vec(&( self.settings.health_for(&member.definition),
self.settings.health_for(&member.definition), self.settings.passive_for(&member.definition),
self.settings.passive_for(&member.definition), ))
)).ok() == serde_json::to_vec(&( .ok()
== serde_json::to_vec(&(
previous.settings.health_for(&old.definition), previous.settings.health_for(&old.definition),
previous.settings.passive_for(&old.definition), previous.settings.passive_for(&old.definition),
)).ok(); ))
.ok();
let same_clients = member.backend.client.reusable_with(&old.backend.client) let same_clients = member.backend.client.reusable_with(&old.backend.client)
&& member && member
.backend .backend
+7 -6
View File
@@ -2,7 +2,7 @@
//! ACLs run first; credentials are never cached with content or written to logs. //! ACLs run first; credentials are never cached with content or written to logs.
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use argon2::{ use argon2::{
password_hash::{PasswordHash, PasswordVerifier}, password_hash::{phc::PasswordHash, PasswordVerifier},
Argon2, Argon2,
}; };
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
@@ -88,8 +88,10 @@ impl Settings {
); );
let hash = PasswordHash::new(&user.password_hash) let hash = PasswordHash::new(&user.password_hash)
.map_err(|_| anyhow::anyhow!("basic.invalid_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!( 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), && hash.hash.is_some_and(|h| h.len() == 32),
"basic.invalid_hash" "basic.invalid_hash"
); );
@@ -221,11 +223,10 @@ impl Compiled {
.map_err(|_| denied(StatusCode::SERVICE_UNAVAILABLE, None))?; .map_err(|_| denied(StatusCode::SERVICE_UNAVAILABLE, None))?;
let verified = tokio::task::spawn_blocking(move || { let verified = tokio::task::spawn_blocking(move || {
let _permit = permit; let _permit = permit;
PasswordHash::new(&encoded).ok().is_some_and(|hash| { PasswordHash::new(&encoded).is_ok()
Argon2::default() && Argon2::default()
.verify_password(password.as_bytes(), &hash) .verify_password(password.as_bytes(), encoded.as_str())
.is_ok() .is_ok()
})
}) })
.await .await
.unwrap_or(false); .unwrap_or(false);
+22 -16
View File
@@ -256,23 +256,29 @@ async fn cold_restart_preserves_transformed_validators_stale_metadata_and_no_tra
restart_shutdown.clone(), restart_shutdown.clone(),
); );
assert_eq!(restarted.view().cold_entries, 2); 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 cold = restarted.cold.lock().unwrap();
let transformed = cold cold.entries
.entries .iter()
.values() .find(|(_, entry)| entry.path == "/transformed.json")
.find(|entry| entry.path == "/transformed.json") .map(|(key, entry)| (*key, entry.clone()))
.expect("transformed cold entry"); .expect("transformed cold entry")
assert_eq!(transformed.stale_while_revalidate, 30); };
assert_eq!(transformed.stale_if_error, 90); assert_eq!(transformed.stale_while_revalidate, 30);
assert_eq!(transformed.origin_etag.as_ref().unwrap(), "\"origin-v1\""); assert_eq!(transformed.stale_if_error, 90);
assert_eq!( assert!(transformed.has_validator);
transformed.origin_last_modified.as_ref().unwrap(), assert!(transformed.transformed);
"Mon, 26 Jan 2026 23:11:40 GMT" let metadata = read_cold_metadata(&key, &transformed)
); .await
assert_eq!(transformed.headers["etag"], "\"rustproxy-representation\""); .expect("persisted transformed metadata");
assert!(transformed.transformed); 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 let transformed_hit = restarted
.get_tiered(&transformed_ticket, &settings) .get_tiered(&transformed_ticket, &settings)
+24 -9
View File
@@ -1,10 +1,11 @@
fn request_id_hash(sequence: u64) -> String { fn request_id_hash(sequence: u64) -> String {
use rand::RngCore; use rand::TryRng;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
let mut nonce = [0u8; 32]; let mut nonce = [0u8; 32];
let mut rng = rand::rngs::OsRng; let mut rng = rand::rngs::SysRng;
rng.fill_bytes(&mut nonce); rng.try_fill_bytes(&mut nonce)
.expect("secure operating system random source unavailable");
let mut hash = Sha256::new(); let mut hash = Sha256::new();
hash.update(sequence.to_be_bytes()); hash.update(sequence.to_be_bytes());
hash.update(nonce); 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 { fn should_replace_error_page(response: &Response, minimal_welcome: bool) -> bool {
!minimal_welcome !minimal_welcome
&& response.extensions().get::<ErrorFallbackResponse>().is_none() && response
&& response.extensions().get::<crate::pages::OriginProvidedResponse>().is_none() .extensions()
&& response.extensions().get::<crate::pages::PageServed>().is_none() .get::<ErrorFallbackResponse>()
.is_none()
&& response
.extensions()
.get::<crate::pages::OriginProvidedResponse>()
.is_none()
&& response
.extensions()
.get::<crate::pages::PageServed>()
.is_none()
&& response && response
.extensions() .extensions()
.get::<crate::tinyauth::AuthenticationFailure>() .get::<crate::tinyauth::AuthenticationFailure>()
@@ -298,14 +308,19 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
let global_compression = &snapshot.envelope.config.compression; let global_compression = &snapshot.envelope.config.compression;
let mut compression = global_compression.clone(); let mut compression = global_compression.clone();
if let Some(frontend) = frontend_context.as_ref() { 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 Some(route) = effective_route_context {
if let crate::config::CompiledSource::WebServer(service) = &route.source { 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 if let Some(encoding) = response
.extensions() .extensions()
.get::<crate::compression::AppliedEncoding>() .get::<crate::compression::AppliedEncoding>()
+3 -3
View File
@@ -13,7 +13,7 @@ use http_body::{Body as HttpBody, Frame, SizeHint};
use http_body_util::BodyExt; use http_body_util::BodyExt;
use hyper::upgrade::{OnUpgrade, Upgraded}; use hyper::upgrade::{OnUpgrade, Upgraded};
use hyper_util::rt::TokioIo; use hyper_util::rt::TokioIo;
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::SysRng, TryRng};
use serde::Serialize; use serde::Serialize;
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
use std::{ use std::{
@@ -186,7 +186,7 @@ pub fn validate_h3(request: &Request) -> Result<Handshake> {
"WebSocket version must be 13" "WebSocket version must be 13"
); );
let mut nonce = [0u8; 16]; 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); let key = STANDARD.encode(nonce);
Ok(Handshake { Ok(Handshake {
accept: accept_for_key(&key), accept: accept_for_key(&key),
@@ -229,7 +229,7 @@ pub fn validate_h2(request: &Request) -> Result<Handshake> {
"WebSocket version must be 13" "WebSocket version must be 13"
); );
let mut nonce = [0u8; 16]; 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); let key = STANDARD.encode(nonce);
Ok(Handshake { Ok(Handshake {
accept: accept_for_key(&key), accept: accept_for_key(&key),
+35 -11
View File
@@ -1,7 +1,7 @@
//! Central encrypted secret storage for provider/ACME credentials. //! Central encrypted secret storage for provider/ACME credentials.
//! Plaintext exists only for the duration of an explicit read/write operation. //! Plaintext exists only for the duration of an explicit read/write operation.
use anyhow::{ensure, Context, Result}; 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 ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};
use serde::Serialize; use serde::Serialize;
use sqlx::Row; use sqlx::Row;
@@ -59,7 +59,10 @@ fn read_key(path: &Path) -> Result<Zeroizing<[u8; KEY_BYTES]>> {
metadata.is_file() && !metadata.file_type().is_symlink(), metadata.is_file() && !metadata.file_type().is_symlink(),
"secret store master key must be a regular file" "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)] #[cfg(unix)]
{ {
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
@@ -79,7 +82,10 @@ fn read_key(path: &Path) -> Result<Zeroizing<[u8; KEY_BYTES]>> {
let mut file = options.open(path)?; let mut file = options.open(path)?;
file.read_exact(bytes.as_mut())?; file.read_exact(bytes.as_mut())?;
let mut extra = [0u8; 1]; 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) Ok(bytes)
} }
@@ -92,7 +98,9 @@ pub fn initialize(data_dir: &Path) -> Result<()> {
return Ok(()); return Ok(());
} }
let mut key = Zeroizing::new([0u8; KEY_BYTES]); 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(); let mut options = OpenOptions::new();
options.write(true).create_new(true); options.write(true).create_new(true);
#[cfg(unix)] #[cfg(unix)]
@@ -158,13 +166,18 @@ pub async fn put(
metadata: serde_json::Value, metadata: serde_json::Value,
) -> Result<()> { ) -> Result<()> {
validate_identity(secret_id, provider)?; 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)?; let metadata = serde_json::to_string(&metadata)?;
ensure!(metadata.len() <= 4096, "secret metadata is too large"); ensure!(metadata.len() <= 4096, "secret metadata is too large");
initialize(data_dir)?; initialize(data_dir)?;
let cipher = key(data_dir)?; let cipher = key(data_dir)?;
let mut nonce = [0u8; NONCE_BYTES]; 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()); let mut ciphertext = Zeroizing::new(plaintext.to_vec());
cipher cipher
.seal_in_place_append_tag( .seal_in_place_append_tag(
@@ -212,12 +225,16 @@ pub async fn get(
} }
validate_identity(secret_id, &provider)?; validate_identity(secret_id, &provider)?;
let version: i64 = row.try_get("encryption_version")?; 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<u8> = row.try_get("nonce")?; let nonce: Vec<u8> = row.try_get("nonce")?;
ensure!(nonce.len() == NONCE_BYTES, "invalid secret nonce"); ensure!(nonce.len() == NONCE_BYTES, "invalid secret nonce");
let mut ciphertext = Zeroizing::new(row.try_get::<Vec<u8>, _>("ciphertext")?); let mut ciphertext = Zeroizing::new(row.try_get::<Vec<u8>, _>("ciphertext")?);
ensure!( 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" "invalid encrypted secret size"
); );
let mut nonce_bytes = [0u8; NONCE_BYTES]; let mut nonce_bytes = [0u8; NONCE_BYTES];
@@ -287,7 +304,10 @@ pub async fn migrate_acme_credentials(
return Ok(0); return Ok(0);
} }
let root_meta = fs::symlink_metadata(&root)?; 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; let mut migrated = 0usize;
for entry in fs::read_dir(&root)? { for entry in fs::read_dir(&root)? {
let entry = entry?; let entry = entry?;
@@ -301,7 +321,10 @@ pub async fn migrate_acme_credentials(
continue; continue;
} }
let profile: serde_json::Value = serde_json::from_slice(&fs::read(&profile_path)?)?; 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 let certificate_id = profile
.get("certificate_id") .get("certificate_id")
.and_then(serde_json::Value::as_str) .and_then(serde_json::Value::as_str)
@@ -341,7 +364,8 @@ pub async fn migrate_acme_credentials(
) )
.await?; .await?;
plaintext.zeroize(); 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; migrated += 1;
} }
Ok(migrated) Ok(migrated)
+2 -2
View File
@@ -80,8 +80,8 @@ fn generate_locked(directory: &Path, name: &str, hosts: Vec<String>) -> Result<(
} }
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
let rcgen::CertifiedKey { cert, key_pair } = rcgen::generate_simple_self_signed(hosts)?; let rcgen::CertifiedKey { cert, signing_key } = rcgen::generate_simple_self_signed(hosts)?;
let key = Zeroizing::new(key_pair.serialize_pem()); let key = Zeroizing::new(signing_key.serialize_pem());
store_pair(directory, name, cert.pem().as_bytes(), key.as_bytes()) store_pair(directory, name, cert.pem().as_bytes(), key.as_bytes())
} }
pub fn initialize_default(directory: &Path) -> Result<()> { pub fn initialize_default(directory: &Path) -> Result<()> {
+5 -5
View File
@@ -77,7 +77,7 @@ mod tests {
dir.path(), dir.path(),
"shared-cert", "shared-cert",
generated.cert.pem().as_bytes(), generated.cert.pem().as_bytes(),
generated.key_pair.serialize_pem().as_bytes(), generated.signing_key.serialize_pem().as_bytes(),
) )
.unwrap(); .unwrap();
assert!(cert_file.starts_with("external-shared-cert-")); assert!(cert_file.starts_with("external-shared-cert-"));
@@ -100,7 +100,7 @@ mod tests {
let generated = let generated =
rcgen::generate_simple_self_signed(vec!["reconcile.example.test".into()]).unwrap(); rcgen::generate_simple_self_signed(vec!["reconcile.example.test".into()]).unwrap();
let certificate = generated.cert.pem(); 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( let (cert_file, key_file, _) = stage_external_pair(
dir.path(), dir.path(),
"reconcile-cert", "reconcile-cert",
@@ -281,7 +281,7 @@ mod tests {
let b = rcgen::generate_simple_self_signed(vec!["b.localhost".into()]).unwrap(); let b = rcgen::generate_simple_self_signed(vec!["b.localhost".into()]).unwrap();
assert!(parse_pair( assert!(parse_pair(
a.cert.pem().as_bytes(), a.cert.pem().as_bytes(),
b.key_pair.serialize_pem().as_bytes() b.signing_key.serialize_pem().as_bytes()
) )
.is_err()); .is_err());
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
@@ -338,7 +338,7 @@ mod tests {
assert!(upload( assert!(upload(
dir.path(), dir.path(),
pair.cert.pem().as_bytes(), pair.cert.pem().as_bytes(),
pair.key_pair.serialize_pem().as_bytes(), pair.signing_key.serialize_pem().as_bytes(),
hosts, hosts,
) )
.is_ok()); .is_ok());
@@ -353,7 +353,7 @@ mod tests {
]) ])
.unwrap(); .unwrap();
let pem = pair.cert.pem(); let pem = pair.cert.pem();
let key = pair.key_pair.serialize_pem(); let key = pair.signing_key.serialize_pem();
assert!(upload( assert!(upload(
dir.path(), dir.path(),
pem.as_bytes(), pem.as_bytes(),
+2 -2
View File
@@ -10,7 +10,7 @@ mod acme_tests {
dir.path(), dir.path(),
"acme-fixture", "acme-fixture",
cert.cert.pem().as_bytes(), cert.cert.pem().as_bytes(),
cert.key_pair.serialize_pem().as_bytes(), cert.signing_key.serialize_pem().as_bytes(),
) )
.unwrap(); .unwrap();
let requested = vec!["example.org".into(), "*.example.org".into()]; let requested = vec!["example.org".into(), "*.example.org".into()];
@@ -62,7 +62,7 @@ mod acme_tests {
dir.path(), dir.path(),
"acme-many", "acme-many",
cert.cert.pem().as_bytes(), cert.cert.pem().as_bytes(),
cert.key_pair.serialize_pem().as_bytes(), cert.signing_key.serialize_pem().as_bytes(),
) )
.unwrap(); .unwrap();
assert!(validate_acme( assert!(validate_acme(
+2 -2
View File
@@ -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(); 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("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(); 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/fullchain1.pem", live.join("fullchain.pem")).unwrap();
symlink("../../archive/example.test/privkey1.pem", live.join("privkey.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(); .unwrap();
fs::write(archive.join("fullchain2.pem"), second.cert.pem()).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::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("fullchain.pem")).unwrap();
fs::remove_file(live.join("privkey.pem")).unwrap(); fs::remove_file(live.join("privkey.pem")).unwrap();
+1 -1
View File
@@ -40,7 +40,7 @@ async fn php_fpm_gzip_response_finishes_after_fastcgi_eof() {
let tmp = TempDir::new().unwrap(); let tmp = TempDir::new().unwrap();
std::fs::write(tmp.path().join("index.php"), b"<?php echo 'placeholder';").unwrap(); std::fs::write(tmp.path().join("index.php"), b"<?php echo 'placeholder';").unwrap();
let address = fastcgi_fixture(expected).await; let address = fastcgi_fixture(expected).await;
let configured = Service { let mut configured = Service {
id: "php-compression-regression".into(), id: "php-compression-regression".into(),
name: "PHP compression regression".into(), name: "PHP compression regression".into(),
document_root: tmp.path().to_path_buf(), document_root: tmp.path().to_path_buf(),
+12 -12
View File
@@ -24,22 +24,22 @@ def read_docker_agent_source() -> str:
preflight(root) preflight(root)
manifest = tomllib.loads((root / "Cargo.toml").read_text()) 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" assert manifest["dependencies"]["quinn"]["version"] == "0.11.12"
h3dep = manifest["dependencies"]["h3"] 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"] assert "h3-quinn" not in manifest["dependencies"]
lock_path = root / "Cargo.lock" lock_path = root / "Cargo.lock"
if lock_path.exists(): if lock_path.exists():
lock = lock_path.read_text() 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" 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 { for release_doc, marker in {
"README.md": "# RustProxy 0.31.1", "README.md": "# RustProxy 0.31.2",
"CHANGELOG.md": "## 0.31.1", "CHANGELOG.md": "## 0.31.2",
"ROADMAP.md": "## 0.31.1", "ROADMAP.md": "## 0.31.2",
"AGENTS.md": "Current source version: 0.31.1.", "AGENTS.md": "Current source version: 0.31.2.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.1", "docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.2",
}.items(): }.items():
assert marker in (root / release_doc).read_text(), f"stale release identity in {release_doc}" 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" 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 '"force_ssl"' in openapi and '"forward_client_ip"' in openapi
assert '"backend_header"' in openapi and '"backend_timing_header"' in openapi assert '"backend_header"' in openapi and '"backend_timing_header"' in openapi
assert '"StreamMetrics"' in openapi and '"streams"' 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 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"']: 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 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["version"] == "=2.4.3" and metrics_dep["default-features"] is False
assert metrics_dep["features"] == ["dummy"] assert metrics_dep["features"] == ["dummy"]
sysinfo_dep = manifest["dependencies"]["sysinfo"] 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"} assert set(sysinfo_dep["features"]) == {"network", "system"}
for marker_0241 in [ for marker_0241 in [
"systemstat::{Platform, System}", "metrics_process::collector::collect", "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 '"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 '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 '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. # 0.24.1 compiler and hostname-cardinality regressions.
tls_0241 = rust_source(root, "src/tls.rs") 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 '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() 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.") print("NOT VERIFIED: Rust parsing, types, compilation, linking or runtime behavior. Run ./dev.sh test.")
+81
View File
@@ -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)')
+2 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/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 from pathlib import Path
import re import re
from rust_source import rust_source 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") readme = (ROOT / "README.md").read_text(encoding="utf-8")
roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8") roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8")
changelog = (ROOT / "CHANGELOG.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 "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 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 assert "## Release history" in readme and "docs/ROADMAP_HISTORY.md" in readme
+2 -2
View File
@@ -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)}") 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"]: 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(): 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") text = (root / "src/lib.rs").read_text(encoding="utf-8")
if '#[path = "auth/mod.rs"]' not in text: 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 return messages
+7 -7
View File
@@ -135,7 +135,7 @@ def upgrade_smoke(tmp: Path) -> None:
assert backup.is_dir() assert backup.is_dir()
assert (backup / "src/obsolete.rs").is_file() assert (backup / "src/obsolete.rs").is_file()
assert not (target / "src/obsolete.rs").exists() 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 / "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 / ".env").read_text(encoding="utf-8") == "RUST_PROXY_DEMO=0\n"
assert (target / "langs/site.json").is_file() 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/certificate.go").is_file()
assert (target / "docker-agent/server.go").is_file() assert (target / "docker-agent/server.go").is_file()
manifest = json.loads((backup / "upgrade.json").read_text(encoding="utf-8")) 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 manifest["removed_obsolete_runtime_state"] is True
assert not legacy.exists() assert not legacy.exists()
assert not (backup / "legacy-data").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["default-features"] is False
assert metrics_process["features"] == ["dummy"] assert metrics_process["features"] == ["dummy"]
sysinfo_network = manifest["dependencies"]["sysinfo"] 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 sysinfo_network["default-features"] is False
assert set(sysinfo_network["features"]) == {"network", "system"} 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"))' 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 assert marker in admin, marker
anonymous_block = admin.split("fn anonymous_allowed", 1)[1].split("const CERTIFICATE_API_KEY_HEADER", 1)[0] 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 '"/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 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"']: for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="error-log-form"']:
assert marker in html, marker 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 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") openapi = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8")
assert '"/api/config/history/restore"' in openapi 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 marker in openapi, marker
assert '"software_update"' in openapi and '"update_available"' in openapi assert '"software_update"' in openapi and '"update_available"' in openapi
for marker in ['"/api/certificates/deploy"', '"CertificateDeploy"', '"CertificateDeployResult"', '"/api/certificates/reload"']: 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 "LogsDirectory=rustproxy" in generated_unit and "/var/log/rustproxy" in generated_unit
assert "Environment=MALLOC_ARENA_MAX=2" in generated_unit assert "Environment=MALLOC_ARENA_MAX=2" in generated_unit
for marker in [ for marker in [
"FROM rust:1.88-trixie AS build", "FROM rust:1.99-trixie AS build",
"cargo generate-lockfile", "cargo generate-lockfile",
"cargo build --locked --release", "cargo build --locked --release",
"FROM debian:trixie-slim AS runtime", "FROM debian:trixie-slim AS runtime",
@@ -1313,7 +1313,7 @@ def main() -> None:
source_contract_smoke() source_contract_smoke()
container_contract_smoke() container_contract_smoke()
grafana_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__": if __name__ == "__main__":
+3 -3
View File
@@ -43,7 +43,7 @@ const code = js.slice(js.indexOf(' const runtime = latestStatus?.runtime || {
const elements = new Map(); const elements = new Map();
const dollar = id => { if (!elements.has(id)) elements.set(id, { textContent: '' }); return elements.get(id); }; const dollar = id => { if (!elements.has(id)) elements.set(id, { textContent: '' }); return elements.get(id); };
const status = { const status = {
version: '0.31.1', uptime_seconds: 4201, version: '0.31.2', uptime_seconds: 4201,
runtime: { runtime: {
platform: 'Linux', operating_system: 'Debian GNU/Linux 13', kernel_version: '6.12.0', architecture: 'x86_64', 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', 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); vm.runInNewContext(code, context);
assert.equal(dollar('runtime-operating-system').textContent, 'Debian GNU/Linux 13'); 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-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-logical-cpus').textContent, '8');
assert.equal(dollar('runtime-cpu-usage').textContent, '17.2%'); assert.equal(dollar('runtime-cpu-usage').textContent, '17.2%');
assert.equal(dollar('runtime-python').textContent, 'Available · 3.13.5'); 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.ok(runtimeSchema.properties.certificate_runtime.properties[key]);
assert.equal(runtimeSchema.properties.certificate_runtime.properties[key].nullable, true); 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.');
+1 -1
View File
@@ -54,7 +54,7 @@ def run_load(
port = parsed.port or (443 if parsed.scheme == "https" else 80) port = parsed.port or (443 if parsed.scheme == "https" else 80)
tls = ssl._create_unverified_context() if insecure else ssl.create_default_context() 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 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: if host_header:
headers["host"] = host_header headers["host"] = host_header