This commit is contained in:
Mateusz Gruszczyński committed 2026-10-07 14:50:13 +02:00
1 parent e0194fbaff
commit a631068cbe
39 files changed
+246 -138

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.27.1. compatibility. Current source version: 0.27.2.
## Delivery ## Delivery
+7
View File
@@ -4,6 +4,13 @@
> test-only changes and packaging implementation notes were moved to > test-only changes and packaging implementation notes were moved to
> [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md). > [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md).
## 0.27.2
- Fix managed ACME issuance/renewal after the 0.27.1 private-Python change: Certbot is now launched through the final managed interpreter (`acme-python/bin/python -m certbot`) instead of the pip console script whose absolute shebang still referenced the temporary `acme-python.installing` path after the atomic directory rename.
- Validate managed Certbot through the same module-based launch path, so already-created 0.27.1 environments recover immediately after upgrade without deleting or rebuilding `/var/lib/rustproxy/acme-python`.
- Keep the distribution Certbot fallback unchanged for systems where the managed environment cannot be used.
- Keep SQLite schema 8 and the saved configuration format unchanged.
## 0.27.1 ## 0.27.1
- Show ready-to-run first-administrator setup-token commands instead of requiring a `$DATA_DIR` placeholder substitution. - Show ready-to-run first-administrator setup-token commands instead of requiring a `$DATA_DIR` placeholder substitution.
Generated
+1 -1
View File
@@ -1688,7 +1688,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-proxy" name = "rust-proxy"
version = "0.27.1" version = "0.27.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rust-proxy" name = "rust-proxy"
version = "0.27.1" version = "0.27.2"
edition = "2021" edition = "2021"
rust-version = "1.88" rust-version = "1.88"
description = "RustProxy - independent reverse proxy with local administration" description = "RustProxy - independent reverse proxy with local administration"
+11 -13
View File
@@ -1,9 +1,9 @@
# RustProxy 0.27.1 # RustProxy 0.27.2
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.27.1 release source.** **0.27.2 release source.**
## Layer-4 streams ## Layer-4 streams
@@ -11,16 +11,14 @@ 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.27.1 ## What changed in 0.27.2
- Show a complete first-administrator setup-token command for the default `./data` path during first launch, plus a standard service-installation example using `/var/lib/rustproxy`. - Fix ACME issuance and renewal with the RustProxy-managed Python environment introduced in 0.27.1.
- Manage Certbot in a RustProxy-owned Python environment under `<data-dir>/acme-python`. Missing environments are initialized automatically with `venv`/`pip`; when `venv` is split out by a distribution, RustProxy can bootstrap `virtualenv` into its own data directory. - Run managed Certbot through the final venv interpreter (`acme-python/bin/python -m certbot`) instead of the pip console script whose shebang still points at the temporary `acme-python.installing` directory after the atomic rename.
- Fall back to a runnable distribution `certbot` when the managed environment cannot be prepared. The Certificates panel reports preparing/managed/system/unavailable state and explicitly reports missing Python/pip while keeping PEM import and existing-certificate workflows available. - Validate the managed runtime through the same module launch path, so an existing 0.27.1 `acme-python` directory starts working immediately after upgrade; no manual venv deletion/rebuild is required.
- Add account-scoped column + direction sorting for ordinary vhosts, Docker vhosts, redirects, 404 hosts and streams, with tab-specific sort columns matching each feature. Redirects and 404 hosts use the same search/sort/action toolbar presentation as Vhosts. - Keep the distribution Certbot fallback, SQLite schema 8 and saved configuration format unchanged.
- Use the installed production RustProxy binary (not `dev.sh`) in the Certbot deploy-hook example. The startup banner and About/runtime views now show the official source repository (`https://git.linuxiarz.pl/gru/RustProxy`), package repository (`https://rustproxy.linuxiarz.pl/`) and Python/pip/Certbot certificate-runtime state.
- Keep SQLite schema 8. The new list-sort preferences are additive/defaulted and require no database migration.
0.27.1 retains the 0.27.0 proxy/compression and Docker-publication behavior. Historical details are in [CHANGELOG.md](CHANGELOG.md). 0.27.2 retains the 0.27.1 first-admin, managed-runtime UI and route-list sorting changes plus the 0.27.0 proxy/compression and Docker-publication behavior. Historical details are in [CHANGELOG.md](CHANGELOG.md).
### Historical 0.26.7 release notes ### Historical 0.26.7 release notes
@@ -88,7 +86,7 @@ Named HTTP/HTTPS listener bind addresses use the same chip-style editor as vhost
- Replace backend sort text (`A-Z` / `Z-A`) with direction arrows (`↑` / `↓`). - Replace backend sort text (`A-Z` / `Z-A`) with direction arrows (`↑` / `↓`).
- Keep SQLite schema 8 and the saved proxy configuration format unchanged; upgrading from 0.26.1 requires no data migration. - Keep SQLite schema 8 and the saved proxy configuration format unchanged; upgrading from 0.26.1 requires no data migration.
The detailed 0.25.5 implementation audit remains available as historical baseline evidence. Current 0.27.1 verification status is maintained in [docs/TEST_REPORT.md](docs/TEST_REPORT.md). The detailed 0.25.5 implementation audit remains available as historical baseline evidence. Current 0.27.2 verification status is maintained in [docs/TEST_REPORT.md](docs/TEST_REPORT.md).
## Documentation ## Documentation
@@ -168,7 +166,7 @@ CARGO_BUILD_JOBS=1 CARGO_PROFILE_RELEASE_LTO=false \
Ready Debian packages are published separately at `https://rustproxy.linuxiarz.pl`; Debian package-build metadata is intentionally not shipped in this source tree. Ready Debian packages are published separately at `https://rustproxy.linuxiarz.pl`; Debian package-build metadata is intentionally not shipped in this source tree.
The 0.27.1 source archive preserves the resolved `Cargo.lock` supplied with the source tree and The 0.27.2 source archive preserves the resolved `Cargo.lock` supplied with the source tree and
updates only this package's own version entry. This packaging environment has no Cargo, so the updates only this package's own version entry. This packaging environment has no Cargo, so the
dependency graph was not re-resolved here. On the compiler-enabled deployment host, run the dependency graph was not re-resolved here. On the compiler-enabled deployment host, run the
locked check/test/release gates below and retain the verified lockfile. `dev.sh` can generate a locked check/test/release gates below and retain the verified lockfile. `dev.sh` can generate a
@@ -414,7 +412,7 @@ python3 scripts/release_smoke.py
# Runs those checks first, then the Rust suite when Cargo is available: # Runs those checks first, then the Rust suite when Cargo is available:
bash dev.sh test bash dev.sh test
# Source-only package, with no Git checkout required: # Source-only package, with no Git checkout required:
python3 make_zip.py /tmp/RustProxy-0.27.1.zip python3 make_zip.py /tmp/RustProxy-0.27.2.zip
``` ```
## New management and test-origin guide ## New management and test-origin guide
+8
View File
@@ -1,5 +1,13 @@
# RustProxy roadmap # RustProxy roadmap
## 0.27.2 - Managed ACME renewal repair
Current direction: 0.27.1 -> 0.27.2
- Make the managed Certbot runtime independent of pip console-script shebang relocation.
- Reuse existing 0.27.1 `acme-python` environments without operator cleanup.
- Preserve the system Certbot fallback and schema/config compatibility.
## 0.27.1 - Portable certificate runtime and list sorting ## 0.27.1 - Portable certificate runtime and list sorting
Current direction: 0.27.0 -> 0.27.1 Current direction: 0.27.0 -> 0.27.1
+40 -39
View File
@@ -1,18 +1,18 @@
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore 61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore 9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
6479dfef0344de33714f8503944876a3641c8381e9a7ccaaa4faed06b65e67c6 AGENTS.md 4595fb01446f9e9e08a459085dea96c2b27a9f0aff0fab1f40c8a62e95761902 AGENTS.md
59ff94da1b39a85b9738c8265ed3b871f483451871182d73c8a678509ee222c1 CHANGELOG.md 49f5f9b0490a8b411f16f8a1a2477e96f832d6d21155727bb80c4895f9877ea1 CHANGELOG.md
0b0ed48165c10747c1a2f524fbdb27488f3edc7ff0e6fdccc845625d885ef61c Cargo.lock 3002fea4dac2c78c11003b6ef669e53415702337062ab737958ac48471e24422 Cargo.lock
f6d19f4e9b1453cc3b80d47f6cb27870b205f9aa4592f82a872c53da44e1308d Cargo.toml 5823157f89f6d8576a038866ff5bc2be8733f226e345f4ff9be68d1b59d1d34c Cargo.toml
75b008f237b2226c62a7721b7901018ce83bfc2726f047a83a17c66fcb799f3b Dockerfile 75b008f237b2226c62a7721b7901018ce83bfc2726f047a83a17c66fcb799f3b Dockerfile
37da924319eef96622afb996a528989780a3733cc44eda7944e4f914a81eb70e README.md 3ef21bb67c050fb7189a032e55e4794c4e07a1b20bcdc3f2f6222f5a37fa8fb5 README.md
fbeb203b4ca4f02c9d35c02561cd9c098bdd2bf58688ea7039e987e24ee8e8e1 ROADMAP.md e62a31c08cc123b39031e95d10d149d36dbccf73a78a18a7578bb88521c89983 ROADMAP.md
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
928ba6a504bc431ae100c6e587861e5088b28822d2cb19da5d437b39d26c864a build.rs 928ba6a504bc431ae100c6e587861e5088b28822d2cb19da5d437b39d26c864a build.rs
385ac7970fa94f043d0bcf04a91184e6e2c807587b85e55517ed064bb4bddba6 deploy/README.md 385ac7970fa94f043d0bcf04a91184e6e2c807587b85e55517ed064bb4bddba6 deploy/README.md
31d4e0cd454060b46f359fdb84a14b16a5fe0d3baabb387dd584d76e555558bb deploy/rustproxy.service 31d4e0cd454060b46f359fdb84a14b16a5fe0d3baabb387dd584d76e555558bb deploy/rustproxy.service
18121e6fb922fb0e8d8b582486cb029256b305bd0f14bb989f9a16484b619674 dev.sh c90c8a484ff15bb516a810c01e19260ba941b652d5d67afc4405f444ab3b06ab 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
@@ -21,25 +21,25 @@ bf687a851374d8f1fa59d050f3f76532f7c12f16a6daa7d3c36102c2d9a8ec9c docker-agent/d
9d77c90767fa929ee8338734d72ce83284d3d965673270f62cb2b1613d587a33 docker-agent/install.sh 9d77c90767fa929ee8338734d72ce83284d3d965673270f62cb2b1613d587a33 docker-agent/install.sh
b68234472bec5cedd67c7cfe1de7d8806a965dfd4101f944aa6320132f18c14e docker-agent/main.go b68234472bec5cedd67c7cfe1de7d8806a965dfd4101f944aa6320132f18c14e docker-agent/main.go
3d4051a1f805d145df3877476e4b87773e74be8e123045065955a5133afdc68c docker-agent/main_test.go 3d4051a1f805d145df3877476e4b87773e74be8e123045065955a5133afdc68c docker-agent/main_test.go
dcd840bb3fd542379d3554ddc78e597c64bff22a4bd832b466f0db867840c17e docker-agent/model.go 4e9eb90294d936e0e4f63618bbadd54b8063ec73d4beda9aa4d70d768b16ab97 docker-agent/model.go
e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/network.go e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/network.go
634790f35ff4627df2a78cec2ed371f6e9ec9df00afb386075595fc7f3ed72eb docker-agent/rustproxy-docker-agent.service 634790f35ff4627df2a78cec2ed371f6e9ec9df00afb386075595fc7f3ed72eb docker-agent/rustproxy-docker-agent.service
5220d669171ec3ad65f50e0bc8de5178dadaa4904def8e1a4f61002aa9e9a55b docker-agent/server.go 5220d669171ec3ad65f50e0bc8de5178dadaa4904def8e1a4f61002aa9e9a55b docker-agent/server.go
875faa3969df9409d79f050a84b4183496f557edda3d34aaec19fe556cf8c471 docs/ACME_PROVIDERS.md 875faa3969df9409d79f050a84b4183496f557edda3d34aaec19fe556cf8c471 docs/ACME_PROVIDERS.md
ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md
c7b75ed137094cade2b2047c1c8d304e936e0fe1121ff91a858f77c2e969fa39 docs/API.md c7b75ed137094cade2b2047c1c8d304e936e0fe1121ff91a858f77c2e969fa39 docs/API.md
386d1d9b120e05b8b776af87237294d1674ac6f37c6ca4fbd74368014d09c0d7 docs/API_ACCESS.md d7b224425930cf06cf34d0547e445e0363f6ac4aced43e182d1d88faa2d28cdb docs/API_ACCESS.md
0f7f159ad5b4b5277f80c19f3c47f4e7823ea439ef02e5913e1391f1b34d129f docs/ARCHITECTURE.md 0f7f159ad5b4b5277f80c19f3c47f4e7823ea439ef02e5913e1391f1b34d129f docs/ARCHITECTURE.md
86469d17f9786c3e6f20c66791b47e10e11695d2f1a88c66c9ded157a585c85f docs/CACHE.md 86469d17f9786c3e6f20c66791b47e10e11695d2f1a88c66c9ded157a585c85f docs/CACHE.md
6266163e17d2921468abdae92c1620266af8351321530e2c9b7dca6f3dd89c88 docs/CACHE_DIAGNOSTICS.md 6266163e17d2921468abdae92c1620266af8351321530e2c9b7dca6f3dd89c88 docs/CACHE_DIAGNOSTICS.md
c6003dcbc0b95e2cfa9523b7017186cc8b4826248b588af77111ac5eb15174f9 docs/CHANGELOG_TECHNICAL_ARCHIVE.md c6003dcbc0b95e2cfa9523b7017186cc8b4826248b588af77111ac5eb15174f9 docs/CHANGELOG_TECHNICAL_ARCHIVE.md
0031861b52c00fc1cffc73df1410c9dee5838d445699326deddc2488ad1193ad docs/CONTAINER.md 0a92a2d4ee88d02e9b460c680305680227c3ef82cf6b57d6f92b1ef5a6433154 docs/CONTAINER.md
9538a52d508d8d7053c1387c42eef60e6d5dc1879c300b6fe951b3374c16884c docs/CUSTOM_LOCATIONS.md 9538a52d508d8d7053c1387c42eef60e6d5dc1879c300b6fe951b3374c16884c docs/CUSTOM_LOCATIONS.md
16d8f59908c66e595d6003495cbf5c068c68b10aa9495eb8612c38b2de0e4a7c docs/DOCKER_DISCOVERY.md 16d8f59908c66e595d6003495cbf5c068c68b10aa9495eb8612c38b2de0e4a7c docs/DOCKER_DISCOVERY.md
9ea8db4d034ecb573976dd323da29f044538525c230df9f43a47be1ac867b24c docs/ERROR_LOGGING.md 9ea8db4d034ecb573976dd323da29f044538525c230df9f43a47be1ac867b24c docs/ERROR_LOGGING.md
adb4c6c43023f09d291eee1027944453eb7667b4c702339346a75aa23c986b5a docs/FILE_LOGS.md 5c46b311f48e143a3e43e61a69c8bc3cfad7748c7dfef87bd76df333b64023bd docs/FILE_LOGS.md
df6ecd4a50796640243676ad4b3a87789f6e5d20cbebea6255e92fbfff1c7131 docs/HTTP3.md df6ecd4a50796640243676ad4b3a87789f6e5d20cbebea6255e92fbfff1c7131 docs/HTTP3.md
69b7ede915818a52bb68f94983f0fcefe77ff6c58d776c92758e2f7007678eab docs/INSTALL.md 10f378674dced022f063bfc1b15dc9a5cfc0afd03639deb4db5de091c139288c docs/INSTALL.md
3b5c31c805e5aee0dc2100b81246a9b631b5abb996faa598ae94f87ba39305f3 docs/INTEGRATIONS_040.md 3b5c31c805e5aee0dc2100b81246a9b631b5abb996faa598ae94f87ba39305f3 docs/INTEGRATIONS_040.md
4bb56add544f15d54695cf797cd16a51c09024360cdeef803a88988f4a54b3a5 docs/LIVE_LOGS.md 4bb56add544f15d54695cf797cd16a51c09024360cdeef803a88988f4a54b3a5 docs/LIVE_LOGS.md
7c35c4cdbe7bbb81d94f3bf6aeb22b8a866af3e7f0d62bbedc299e4d6f264e0d docs/MANAGEMENT.md 7c35c4cdbe7bbb81d94f3bf6aeb22b8a866af3e7f0d62bbedc299e4d6f264e0d docs/MANAGEMENT.md
@@ -50,7 +50,7 @@ e7516646dfb107d6240370f4e7460b1189f2eb21f398740381d5d102847bbfe6 docs/NOT_FOUND
0c2241828e01b12f749562e2ac206a4519908fff63f46eba207b1cffa932dc5b docs/NPM_MIGRATION.md 0c2241828e01b12f749562e2ac206a4519908fff63f46eba207b1cffa932dc5b docs/NPM_MIGRATION.md
712cb1cbf84515384b081fd2929e86e40af7c10f65bc69919b78b7b3e2fd12b5 docs/PHP_FPM.md 712cb1cbf84515384b081fd2929e86e40af7c10f65bc69919b78b7b3e2fd12b5 docs/PHP_FPM.md
572c70fbd2f61fecd80a8ff9fa5de4a94c4f158de5e90da8e920f99b051241fa docs/PROMETHEUS.md 572c70fbd2f61fecd80a8ff9fa5de4a94c4f158de5e90da8e920f99b051241fa docs/PROMETHEUS.md
72a1cfc792f4cc35d3463c312a88bf0053689df0bb52d89889bb7ed15e35462b docs/README.md a5faed54574e23cc5940205883a9731aa1572c2499783ecba138a621fcfb4a42 docs/README.md
6e46a137b9c88dac8cb9e3fdca877430092392de9913d746278dbff8652460c2 docs/REDIRECTS.md 6e46a137b9c88dac8cb9e3fdca877430092392de9913d746278dbff8652460c2 docs/REDIRECTS.md
0dfd0d431c5aedc67abac1ce5de040ffa790ec9578fd7d6f91a987c333d73358 docs/REFERENCES.md 0dfd0d431c5aedc67abac1ce5de040ffa790ec9578fd7d6f91a987c333d73358 docs/REFERENCES.md
be661dbf81af840d4d8aeeb5347c24c7faecdecc73a8cf0f6f09053f1abe04c1 docs/RENEWAL.md be661dbf81af840d4d8aeeb5347c24c7faecdecc73a8cf0f6f09053f1abe04c1 docs/RENEWAL.md
@@ -60,7 +60,7 @@ be661dbf81af840d4d8aeeb5347c24c7faecdecc73a8cf0f6f09053f1abe04c1 docs/RENEWAL.m
620b69e566d27af04f0604f4ca3f88cf95cdfea7fbc6ae9dd92446d2b2b5d9be docs/SECURITY_AUDIT.md 620b69e566d27af04f0604f4ca3f88cf95cdfea7fbc6ae9dd92446d2b2b5d9be docs/SECURITY_AUDIT.md
632976125cbd9e96e91d1771331c73d1ac5adaa796476a20f8546971787c2529 docs/SETTINGS.md 632976125cbd9e96e91d1771331c73d1ac5adaa796476a20f8546971787c2529 docs/SETTINGS.md
3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md 3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md
6f1d1ced81d32c2efae10b0743e6fbd45648734439ce1adbc4a62baa07341dd8 docs/TEST_REPORT.md 6a6f48614fcf64b675ef0b7efb49f65878da3f42a6ee110f0d608c70e737be2a docs/TEST_REPORT.md
cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md
8ee6187a5460ca45643b7fd789e27bffd264449e3a92ff70e9359edee011bbbb docs/TRANSFORM.md 8ee6187a5460ca45643b7fd789e27bffd264449e3a92ff70e9359edee011bbbb docs/TRANSFORM.md
a1e9c6d38d21067d1ce7115ca3395ae8ff1f6a4c40fb0ebf00a6940ef79df439 docs/TRANSPORT.md a1e9c6d38d21067d1ce7115ca3395ae8ff1f6a4c40fb0ebf00a6940ef79df439 docs/TRANSPORT.md
@@ -97,6 +97,7 @@ dd9b5dcdbd53a04fd32458caaf03b56aa335f0153b1a0e332fdf230fec12680e docs/V0.26.6_I
f9667fe81b31762e64d36126a7e94b609316dfa856de8f8e3b96661dd2a918c6 docs/V0.26.7_IMPLEMENTATION.md f9667fe81b31762e64d36126a7e94b609316dfa856de8f8e3b96661dd2a918c6 docs/V0.26.7_IMPLEMENTATION.md
efd5935852d4cfd09758bf67c70e437490cfce2baade46c8733201e506dbcc42 docs/V0.27.0_IMPLEMENTATION.md efd5935852d4cfd09758bf67c70e437490cfce2baade46c8733201e506dbcc42 docs/V0.27.0_IMPLEMENTATION.md
9ba6417417a0eb293879b26b019449f143e773d1482434bee88f5eaf1fe34b90 docs/V0.27.1_IMPLEMENTATION.md 9ba6417417a0eb293879b26b019449f143e773d1482434bee88f5eaf1fe34b90 docs/V0.27.1_IMPLEMENTATION.md
6aafee24d7badabe00e23554c8c1ec868f586e09f2d915e9f48422639a7348fb docs/V0.27.2_IMPLEMENTATION.md
bc2ac0117528ed4616c68cf7fe05cb1d9bbd36ecaa8573828cb3b3136923f265 docs/VHOSTS.md bc2ac0117528ed4616c68cf7fe05cb1d9bbd36ecaa8573828cb3b3136923f265 docs/VHOSTS.md
09b60fb7b40db4d0139fa2a9e69a2aa9f28d4e61b8643521cfc7e936d7df8774 docs/VHOST_ACCESS.md 09b60fb7b40db4d0139fa2a9e69a2aa9f28d4e61b8643521cfc7e936d7df8774 docs/VHOST_ACCESS.md
4026a4adeee70f98a96cc7aa8d8be16cda42ffb2e5890d38f1ba638134ab0675 docs/WEB_SERVER.md 4026a4adeee70f98a96cc7aa8d8be16cda42ffb2e5890d38f1ba638134ab0675 docs/WEB_SERVER.md
@@ -111,14 +112,14 @@ b26833cb4251f9390a44192ecc827ff7f052fd19016a5ccf7498c94503ab4ce3 examples/mock-
5a8f3a8a054abc89c6e78896b9fe2f903212199a24dc3d2d2bcbd0696a31bcde examples/transport.json 5a8f3a8a054abc89c6e78896b9fe2f903212199a24dc3d2d2bcbd0696a31bcde examples/transport.json
d242de9614fbb52933b4accd32f4715ca94107abe4a3b1d7e088362b07a6ba60 examples/vhosts-balanced.json d242de9614fbb52933b4accd32f4715ca94107abe4a3b1d7e088362b07a6ba60 examples/vhosts-balanced.json
af2bdb1343a8141069e94ccd2c7f92574ad848437e2f9fdb5c7576a9a18ef4a5 examples/vhosts-private.json af2bdb1343a8141069e94ccd2c7f92574ad848437e2f9fdb5c7576a9a18ef4a5 examples/vhosts-private.json
9cab73d7de1f54bc9e9e8b889a6eeaf2c293ab454c93ce904e9f7f5c6b7c588c grafana/README.md cc99b14927c696adc17c9d0689041798f0a99cbc372b35a81652d6e38af69311 grafana/README.md
2e23d77477bd3eea895c2afe8c5e836559f435419c066e995dec9910c5bbb942 grafana/rustproxy-overview.json b6401012c55a9cdf7ca7ac5f90b4142c623230903877309e427142de1a81f9d0 grafana/rustproxy-overview.json
fe1fc76bcb639cac0609a8969cd92725cfd77d87f1d8519756ef16825e04914a install-debian13.sh fe1fc76bcb639cac0609a8969cd92725cfd77d87f1d8519756ef16825e04914a install-debian13.sh
858b39fa6886b71d20690bf369e76580922364b3cc7f0cf6ba9b040df9cd76e3 install.sh 858b39fa6886b71d20690bf369e76580922364b3cc7f0cf6ba9b040df9cd76e3 install.sh
29cada0936762f26495064d1524a668262ed729b21f20aebddc8b6e5cfea363b langs/README.md 29cada0936762f26495064d1524a668262ed729b21f20aebddc8b6e5cfea363b langs/README.md
55a50c4071a05e4c24fef7fe9da10c0d9a3d5aaf19bafdb39fbacfbaa0b953e8 langs/en_US.json 917efd1646d06ac34c5729ce5a569482d7512cf285dfc603bca24b3608d36e7c langs/en_US.json
41707ded727ff98fc3672f69f7cb16e7950eca8efa248fd328108ca1b262c22e langs/pl_pl.json bab5f124ee1e1e823fe310217560c2abaff8a07c9c23a4daf7ab0f60d766eb4b langs/pl_pl.json
95f980b8420d14addf02c4c0139986ba317239426a3c91a1a821df410147c2c4 make_zip.py 300b8cbb49e44d6ce11a4445df01b1a7467b81acfafa9c2d2ba62b5a611501e4 make_zip.py
e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql
20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql 20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql
5c62da30fa9db594a4d656c77ae676d3c388072b6a85f91170ec7d911d7cde43 migrations/0004_admin_preferences.sql 5c62da30fa9db594a4d656c77ae676d3c388072b6a85f91170ec7d911d7cde43 migrations/0004_admin_preferences.sql
@@ -133,15 +134,15 @@ ee50f38c037765fab742e888d4b9fc4d874c31c785c6064d4eca3d2c4d24d944 nagios/check_r
1465396cf1358067e9fe48b3d61f28fd4ac65c577b67c502950ce6b42ecbe3b3 nagios/check_rustproxy_latency 1465396cf1358067e9fe48b3d61f28fd4ac65c577b67c502950ce6b42ecbe3b3 nagios/check_rustproxy_latency
6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues 6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues
7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml 7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml
edb6ee1445b01cd6f3a74a3a607f39fb107d411b49df028d2e0f4679e093de31 scripts/acme_provider.py 5d4ae015f1c8199c2ed02b34576b74ec6f2b3b7a90c8d7178e5070a850bb6e72 scripts/acme_provider.py
e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/acme_rfc2136.py e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/acme_rfc2136.py
a5b4451e45ce975685772aa8409b64a57a20a7548466584c3ea7fb975e64729c scripts/admin_audit.py a5b4451e45ce975685772aa8409b64a57a20a7548466584c3ea7fb975e64729c scripts/admin_audit.py
20e83f8b6347b819d1d1ea3de5c691c36f0816954a4210b0d3f39557fefe1931 scripts/backup.py 20e83f8b6347b819d1d1ea3de5c691c36f0816954a4210b0d3f39557fefe1931 scripts/backup.py
4ab1fc13e7447e752c476bc8bfd15ad4745900bf2a40fc5469174aacbc3aaef6 scripts/check_source.py 6e656850ca0bafae0aeb2fdf3b8f7e9342b027830df8e4ca19190a8b1cfeb7a9 scripts/check_source.py
187ee92c8029cdd59e89b4447cf4d170e856017a2091ceb85ae7c22dd1a79c72 scripts/deploy.py 187ee92c8029cdd59e89b4447cf4d170e856017a2091ceb85ae7c22dd1a79c72 scripts/deploy.py
11321aa4c5744dd2a3322123c00182e9565891e3b2742cda5bf3fc437b3d0c60 scripts/deploy_core.py 6960ff75a235289d270a9d8e7205a4432b51d3bca83e2c599bd20bcc90caac81 scripts/deploy_core.py
f014472588e889488976970dbaa95a8745b6907516aa81a7cca21d0f81eaaad3 scripts/deploy_runtime.py f014472588e889488976970dbaa95a8745b6907516aa81a7cca21d0f81eaaad3 scripts/deploy_runtime.py
f818039f8a2e35922596122cde671a5e1af264b03d48938a3ca8261ec3c8e431 scripts/docs_check.py fc1a74246478cb477b3892812634cb28f8b8ad45604a098327f7a14a60f25c96 scripts/docs_check.py
d391681dc3f6786552ca44929979286a5c76a2b86785635871b59d2eb4d73389 scripts/langs_check.py d391681dc3f6786552ca44929979286a5c76a2b86785635871b59d2eb4d73389 scripts/langs_check.py
2e35b7c78887fd56f83ca9017caa168630963a9f47da71dfc53b1a8f9ad436c2 scripts/memory_contracts.py 2e35b7c78887fd56f83ca9017caa168630963a9f47da71dfc53b1a8f9ad436c2 scripts/memory_contracts.py
2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migrate_npm.py 2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migrate_npm.py
@@ -151,8 +152,8 @@ d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a scripts/mock_a
2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 scripts/mock_server.py 2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 scripts/mock_server.py
2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 scripts/os-acme-client/README.md 2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 scripts/os-acme-client/README.md
c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 scripts/os-acme-client/opnsense-rustproxy-cert-deploy.sh c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 scripts/os-acme-client/opnsense-rustproxy-cert-deploy.sh
f7474501b9ab7102d9f9b51c6d62c83dac3e6111e982f12e13480bcfd0b1c909 scripts/preflight.py 65ee67092955aae5216c304453f88066ad9f51dc27e682f36866540eebbf621b scripts/preflight.py
3019322686f3eccb141a9587b5bff78aabf46fb2ef766d3ddf1de274fea4dc8e scripts/release_smoke.py 1df736690e3de94f1ddc363ecef9e0e86acee4e98a78c30b82936324f4747484 scripts/release_smoke.py
45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 scripts/routeros-oci-build/.env.routeros.example 45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 scripts/routeros-oci-build/.env.routeros.example
3b48cff8506e5a6ae7d3876f47721f5de7d63e2e02264a78cf5b6ddd52d9fbc6 scripts/routeros-oci-build/Dockerfile.routeros 3b48cff8506e5a6ae7d3876f47721f5de7d63e2e02264a78cf5b6ddd52d9fbc6 scripts/routeros-oci-build/Dockerfile.routeros
7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e scripts/routeros-oci-build/Dockerfile.routeros.dockerignore 7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e scripts/routeros-oci-build/Dockerfile.routeros.dockerignore
@@ -164,11 +165,11 @@ ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed scripts/rust_s
1c83054336b3508d7ad72cd259f039394682642439877fe69639c0b76be09154 scripts/security_contracts.py 1c83054336b3508d7ad72cd259f039394682642439877fe69639c0b76be09154 scripts/security_contracts.py
295b6f2625b1875950ea3eecccd142f49be327afdd00c92641286e6de530ec51 scripts/security_dependency_contracts.py 295b6f2625b1875950ea3eecccd142f49be327afdd00c92641286e6de530ec51 scripts/security_dependency_contracts.py
6fefb5c459aacdef3dcabe9c5e6e7718a2145a0190b165c29d7a46216ac53bad scripts/source_module_contracts.py 6fefb5c459aacdef3dcabe9c5e6e7718a2145a0190b165c29d7a46216ac53bad scripts/source_module_contracts.py
6c1b64db378fd6ab351882ecf91d7be3db7114b6650a88d402408b34794260cc scripts/swagger_assets.py df2bffcd729a998c5c7ec5452235cc07e834d8f86017d65ccd0b1754d91d11b0 scripts/swagger_assets.py
538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba scripts/test_compression.py 538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba scripts/test_compression.py
19bfa4dca422cebe8b41d9374f2e5ad480a34228995eddcf12acfc21e0ebed6f scripts/upgrade.py 45a6a4cca69419da94b2389d73d3f1eb42ccc4aafa5825da14242eb236bdd963 scripts/upgrade.py
0aaa80453e25aae7ea953fadb91b3ae3eaff627977c81c9ab5a2779f9a93582f scripts/web_assets.py 0aaa80453e25aae7ea953fadb91b3ae3eaff627977c81c9ab5a2779f9a93582f scripts/web_assets.py
71bc04c6d71ce14362c7668e6798e0f13f2fda052a081ae52d04d7845e208adb scripts/web_server_contracts.py 12d2e324b2685f2d20d7bde4b68d2cb1565a3b5aff49e30850238e05d57bff9f scripts/web_server_contracts.py
c245f8f832bd2c36a3fbd5e66fe98e7367c70a8db840c79e6b640f1d9d74f2ac scripts/web_service_ui_test.mjs c245f8f832bd2c36a3fbd5e66fe98e7367c70a8db840c79e6b640f1d9d74f2ac scripts/web_service_ui_test.mjs
ad62f73bb42906d617cc1b479f02d4648c7324cb89fc11d2c6aa6bf13774eec9 src/acl.rs ad62f73bb42906d617cc1b479f02d4648c7324cb89fc11d2c6aa6bf13774eec9 src/acl.rs
ea272433e7f92e983509a8ab1aa54242440abef21df526c2f99944ef279e09b9 src/acl/tests.rs ea272433e7f92e983509a8ab1aa54242440abef21df526c2f99944ef279e09b9 src/acl/tests.rs
@@ -177,20 +178,20 @@ ea272433e7f92e983509a8ab1aa54242440abef21df526c2f99944ef279e09b9 src/acl/tests.
3f158f46e80b403b658b4e9b1e3710e38c70e47b2d476b8223d9c394ffeeba95 src/acme/model.rs 3f158f46e80b403b658b4e9b1e3710e38c70e47b2d476b8223d9c394ffeeba95 src/acme/model.rs
c5334de6c732d76b2bea7bfc17b3ac305a250bbaad31a5420d6a7d7abb200449 src/acme/providers.rs c5334de6c732d76b2bea7bfc17b3ac305a250bbaad31a5420d6a7d7abb200449 src/acme/providers.rs
b9e57db4bd80ef37564dfc533a96897d8a494671d53acea9fb4bf28d23ba370d src/acme/providers/tests.rs b9e57db4bd80ef37564dfc533a96897d8a494671d53acea9fb4bf28d23ba370d src/acme/providers/tests.rs
a5dad04694780f0c6da3c277a110930f3c2ed925fc122d4be770678055931017 src/acme/runtime_env.rs f8c4bb96814f669be8be40d013a6217c32f66943de0961ad827a184be6879645 src/acme/runtime_env.rs
7acbaf34e7b953b753ab074893e47b5024f0cd756dc4c7a1074067dd936c2e8e src/acme/tests.rs 7acbaf34e7b953b753ab074893e47b5024f0cd756dc4c7a1074067dd936c2e8e src/acme/tests.rs
55ef7ceb44c25c7a8ddcbfd5e0d4b647526f9bdcde901ee08db5314e4d055b39 src/admin/access.rs 55ef7ceb44c25c7a8ddcbfd5e0d4b647526f9bdcde901ee08db5314e4d055b39 src/admin/access.rs
d18efa3f797f62b3916265e30ba546725a41e7ccfd9b123965ff9f522e3baa88 src/admin/certificates.rs d18efa3f797f62b3916265e30ba546725a41e7ccfd9b123965ff9f522e3baa88 src/admin/certificates.rs
1de4c7c116eeab2452ccccbaba3f2b7b63c6eb1c1e146ab870d19c5a5ca16f80 src/admin/documentation.rs 1de4c7c116eeab2452ccccbaba3f2b7b63c6eb1c1e146ab870d19c5a5ca16f80 src/admin/documentation.rs
9cac4393318cd34afcfdd02527291aaeb8eb373d38f9b219f71cb963c6d8e883 src/admin/documentation/documents.rs e8892cca11b3d686d1e5f6ef091a01d7b28cf4289993876a0a4d91bedb751ec4 src/admin/documentation/documents.rs
67b901485d5858d5b5bf78a6260035db32241531d99481754c3533fdf404facb src/admin/documentation/tests.rs 67b901485d5858d5b5bf78a6260035db32241531d99481754c3533fdf404facb src/admin/documentation/tests.rs
002922c91aa07cbea17cb0ee11127bb6e7e015de108ee032761b34507dcfa06d src/admin/file_log_search.rs 002922c91aa07cbea17cb0ee11127bb6e7e015de108ee032761b34507dcfa06d src/admin/file_log_search.rs
d3210e037481e1f95e840550428e088ffd83ec9a42f1810e4c976298fb555a9a src/admin/file_logs.rs d3210e037481e1f95e840550428e088ffd83ec9a42f1810e4c976298fb555a9a src/admin/file_logs.rs
beb8be241e4f1a8cea7b6c4e02b42f0359fb1fff10ab5ce2c70ccd0e63a554b0 src/admin/logging_api.rs beb8be241e4f1a8cea7b6c4e02b42f0359fb1fff10ab5ce2c70ccd0e63a554b0 src/admin/logging_api.rs
58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs 58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs
dd40e2e8f1cac463532b113f91b4474a97a118fcd96aa27ee9f26bd78821064b src/admin/openapi.json 01bde7e69062ad85449a03c05188c8af4299952921993e9828f9c03439cff104 src/admin/openapi.json
a53309daf118381031122ca70764f7a2f92adc44af3fc91df5f946e6856f76ae src/admin/operations.rs a53309daf118381031122ca70764f7a2f92adc44af3fc91df5f946e6856f76ae src/admin/operations.rs
a01160fd4f6743a9e8c1aa2e03ddf1514ff68a192ac9523c25be09252d2ac7a4 src/admin/preferences.rs 5287fff6b3ad436ac695e9b66feb1b0b5705b744a9f00cec7ea6a3e951e5eebf src/admin/preferences.rs
b4fa393b780b2676b5d72e0f90cd7290292f424b8062d4b5983e722d6ca72c32 src/admin/public.rs b4fa393b780b2676b5d72e0f90cd7290292f424b8062d4b5983e722d6ca72c32 src/admin/public.rs
684d5d28e0d2b10fb2baf2a56857b226d6334cc3346e1ca86de173ff914bee73 src/admin/router.rs 684d5d28e0d2b10fb2baf2a56857b226d6334cc3346e1ca86de173ff914bee73 src/admin/router.rs
333d5f9f4c27941c535ac689e5826264ffa2bb86b00832b597a9bb9f811f01e8 src/admin/session.rs 333d5f9f4c27941c535ac689e5826264ffa2bb86b00832b597a9bb9f811f01e8 src/admin/session.rs
@@ -226,7 +227,7 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/
0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg 0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg
af6e7a2e890df2fb7385a374bcb3fba52fb5a53dbe18adc1fb7865660fcba842 src/admin/web/file-logs.js af6e7a2e890df2fb7385a374bcb3fba52fb5a53dbe18adc1fb7865660fcba842 src/admin/web/file-logs.js
b14e7b3578322f309627f6fe965cc16abcf85da3437c9070be6caecae1908743 src/admin/web/host404.js b14e7b3578322f309627f6fe965cc16abcf85da3437c9070be6caecae1908743 src/admin/web/host404.js
8f306bfa5635dd126a965125e11179f5475b2feec3471f36b460eb67c571bb19 src/admin/web/index.html 9e236b9a2e9505a0eda738e4cec92efb57b1db60288499e0bf797cb67df7eecf src/admin/web/index.html
24db88842707f764064657712d3660c6ac129fbfb47d188c0903925cf8244ce5 src/admin/web/integrations.js 24db88842707f764064657712d3660c6ac129fbfb47d188c0903925cf8244ce5 src/admin/web/integrations.js
7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json 7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json
af0fbd94d9b88ce1cce28e39dfd2c35de031adcba412dd4a9a17d9b416c256dd src/admin/web/login.html af0fbd94d9b88ce1cce28e39dfd2c35de031adcba412dd4a9a17d9b416c256dd src/admin/web/login.html
@@ -235,7 +236,7 @@ e169d77359785ba604ce1a728420571fdc9b491fc1c0088642399466fbed8c1b src/admin/web/
90d9a0836662135b79fb6f183c4f3eff60f8a64392d6c5795ccf1d8ef6862263 src/admin/web/redirects.js 90d9a0836662135b79fb6f183c4f3eff60f8a64392d6c5795ccf1d8ef6862263 src/admin/web/redirects.js
20670b3167a56e0c79b061ea1b9af74a0d1e4dedb4b57cb8c211463610df28eb src/admin/web/settings.js 20670b3167a56e0c79b061ea1b9af74a0d1e4dedb4b57cb8c211463610df28eb src/admin/web/settings.js
bb67e77af5514478485cfb69f339d63d7eb0cbadd093fa054814a416260ccc1d src/admin/web/streams.js bb67e77af5514478485cfb69f339d63d7eb0cbadd093fa054814a416260ccc1d src/admin/web/streams.js
2a2543859506ac69b41e706c3e2eeb4b500b054c7302a902f71e598cc67d9007 src/admin/web/style.css 625702e6e58ef6d78615909d02a0a236eee1a8b22cce451ca811ed30bb35967e src/admin/web/style.css
a8a73a4fa849ae49391fcd6143e900c515c8b577f71b55cbae9701fa9f5bbe47 src/admin/web/traffic-panel.js a8a73a4fa849ae49391fcd6143e900c515c8b577f71b55cbae9701fa9f5bbe47 src/admin/web/traffic-panel.js
e686d68fd6a368110a85dc4c5dc64e8093950d20d63e9aa44790c03dea6fc193 src/admin/web/transform-panel.js e686d68fd6a368110a85dc4c5dc64e8093950d20d63e9aa44790c03dea6fc193 src/admin/web/transform-panel.js
f746b0aa758c1f9fbf5e656ab269f3ffe32675b90c83f0ed5b1e398322095e34 src/admin/web/vhosts/core.js f746b0aa758c1f9fbf5e656ab269f3ffe32675b90c83f0ed5b1e398322095e34 src/admin/web/vhosts/core.js
@@ -345,11 +346,11 @@ a16613114273c54b6d2a16a94fa8748550e91468585469642622bd2c86087baa src/logging/fo
17425ebbca6bbbb0dcaeb65ebc44b1e51488a4092b7b6add556e764c6d486d6a src/logging/tests.rs 17425ebbca6bbbb0dcaeb65ebc44b1e51488a4092b7b6add556e764c6d486d6a src/logging/tests.rs
3836ed14b6710e183f048ee4d7807a35366c27a27338057fe4a2811129b63302 src/main.rs 3836ed14b6710e183f048ee4d7807a35366c27a27338057fe4a2811129b63302 src/main.rs
c66a9803e43bdd87101825e8c71ab33f890ac4c3428467ba5d43c17fdfa1439e src/main/admin_runtime.rs c66a9803e43bdd87101825e8c71ab33f890ac4c3428467ba5d43c17fdfa1439e src/main/admin_runtime.rs
0cc35b47d2264816009160e2416a4052d0118fee4c41c35d428dc088ee68a352 src/main/bootstrap.rs 04084e87167a8c47a24d5a4e89e0bde0f30398ea95c38fe34c057a1149ac33e8 src/main/bootstrap.rs
5a98d0e249338e73aa57ae2e352dc16db2f79c2d042c1544ce1c2022da76a31c src/main/proxy_runtime.rs 5a98d0e249338e73aa57ae2e352dc16db2f79c2d042c1544ce1c2022da76a31c src/main/proxy_runtime.rs
4dd7c1084a6214c0f2513ebf86fe8ef144470695557772bfe25979edd64491ab src/main/shutdown.rs 4dd7c1084a6214c0f2513ebf86fe8ef144470695557772bfe25979edd64491ab src/main/shutdown.rs
0a6578a0f4023b9f8d73043bf942dc5e096ef9afb8c0d2f131c14f65d08ac01d src/main/shutdown/tests.rs 0a6578a0f4023b9f8d73043bf942dc5e096ef9afb8c0d2f131c14f65d08ac01d src/main/shutdown/tests.rs
a72eb4ae7ffecd5e1e566fbece270a7956bb3393d3fc18be108de959e65a99c0 src/main/tasks.rs 7e97938fd9209f2335af5d6304abcb4193e80bb33e4ccf480938e8c3ab4644d6 src/main/tasks.rs
49d7e104585c8791ac9a203dddd95802784b68c0ba65d6f90f3d57154b4c7871 src/metrics.rs 49d7e104585c8791ac9a203dddd95802784b68c0ba65d6f90f3d57154b4c7871 src/metrics.rs
201ab702722d6d21585fe8bb49939150d61bfac7ed8dc198b98c2cc6098ffb5f src/metrics/global.rs 201ab702722d6d21585fe8bb49939150d61bfac7ed8dc198b98c2cc6098ffb5f src/metrics/global.rs
49b11df65b4fb0b5b727f9f867436584680b45e3f4fbd53f74bc0cab8b9a88ec src/metrics/vhost.rs 49b11df65b4fb0b5b727f9f867436584680b45e3f4fbd53f74bc0cab8b9a88ec src/metrics/vhost.rs
@@ -456,7 +457,7 @@ ff4980f0a091da9ff1722d6b9f6662529cbd7c5e2ac68dcd22a507ce6aaf3011 src/vhost/vali
c1fef6dfb79275479c15097b211cd5f063ad7e0852a0767b0145cf4f32f751da src/web_server/config/model.rs c1fef6dfb79275479c15097b211cd5f063ad7e0852a0767b0145cf4f32f751da src/web_server/config/model.rs
f1d3e427c53f8b5be5768d85e69ead746d83647fcd3b355acc98e3f9d5e217e0 src/web_server/config/validation.rs f1d3e427c53f8b5be5768d85e69ead746d83647fcd3b355acc98e3f9d5e217e0 src/web_server/config/validation.rs
86c744216d7132d8294a6e90ba9be5fd871646006313d1c62506d40c81db9b12 src/web_server/fastcgi.rs 86c744216d7132d8294a6e90ba9be5fd871646006313d1c62506d40c81db9b12 src/web_server/fastcgi.rs
fbc035f68621ac6f3c31e363712c4e06f9bc4382d94511bf93f0fdba39b627b2 src/web_server/fastcgi/protocol.rs be75d0bf057a9b17d8c4a1cab67a1cbf349f542c8e57e4a822ebb9192b1c9582 src/web_server/fastcgi/protocol.rs
134c4998f6a0aa27e121af1ae3355b7e335488b2ae867ac0a934c58b4eb77191 src/web_server/fastcgi/response.rs 134c4998f6a0aa27e121af1ae3355b7e335488b2ae867ac0a934c58b4eb77191 src/web_server/fastcgi/response.rs
8186363315bd998919c6c783e6f7501528f257a5c0da77c5d60f423e8670db5e src/web_server/filesystem.rs 8186363315bd998919c6c783e6f7501528f257a5c0da77c5d60f423e8670db5e src/web_server/filesystem.rs
0aafd7f37c4fe784b047646e4ce5ddcb5fe821231a41ed0a9f519529385288a8 src/web_server/htaccess.rs 0aafd7f37c4fe784b047646e4ce5ddcb5fe821231a41ed0a9f519529385288a8 src/web_server/htaccess.rs
@@ -466,14 +467,14 @@ fbc035f68621ac6f3c31e363712c4e06f9bc4382d94511bf93f0fdba39b627b2 src/web_server
80f946570c14c8915fd21bfdb1a84dfb6a14c633ab6060acb828fab4c7c21daa src/web_server/mod.rs 80f946570c14c8915fd21bfdb1a84dfb6a14c633ab6060acb828fab4c7c21daa src/web_server/mod.rs
67ab8bef567452f123c4b9b46a942b39c93f1f765c128edca7ee808ff1c5e304 src/web_server/request.rs 67ab8bef567452f123c4b9b46a942b39c93f1f765c128edca7ee808ff1c5e304 src/web_server/request.rs
c5cb561b0f276e8082f904b9446fa5885f6b3c0608f25ea2ef425061e9f7f6b4 src/web_server/response.rs c5cb561b0f276e8082f904b9446fa5885f6b3c0608f25ea2ef425061e9f7f6b4 src/web_server/response.rs
bbd66ca7e3cab7b5a8b42402567b99fe060f19d513bb2de9f4423aaa14513d18 src/web_server/response/file.rs 51b0878fcf6e8ef2458f1b8ded65f843951c122f0ff0afe2854f467c5e192273 src/web_server/response/file.rs
9aa68251a5f549f4ce3b1bda88d4abb9efc629caf444989381b4117c9488e37b src/web_server/response/headers.rs 9aa68251a5f549f4ce3b1bda88d4abb9efc629caf444989381b4117c9488e37b src/web_server/response/headers.rs
2268a6aeed2f565f9791af33e1cd21c56c228c8873b031725b39a6c57184f2d1 src/web_server/routing.rs 2268a6aeed2f565f9791af33e1cd21c56c228c8873b031725b39a6c57184f2d1 src/web_server/routing.rs
400e1c8aa2fe857d96036a64499fbc3de44c1e3a53811c5b25101fb843359792 src/web_server/security.rs 400e1c8aa2fe857d96036a64499fbc3de44c1e3a53811c5b25101fb843359792 src/web_server/security.rs
3e0b4e92600a7d5abcec92efed532b3a01e0781cb4f2681daac6d6ae13b0a6e9 src/web_server/serve.rs 3e0b4e92600a7d5abcec92efed532b3a01e0781cb4f2681daac6d6ae13b0a6e9 src/web_server/serve.rs
cf86c4692080723bbdf8f4105e1abfae33d7fcf020d2ad9e13ebae3943bd0cbe src/web_server/tests.rs cf86c4692080723bbdf8f4105e1abfae33d7fcf020d2ad9e13ebae3943bd0cbe src/web_server/tests.rs
c62752fe2fa6b74ddc9d94d36f8d191376b1575b15d21fc434c5a5da4c932692 src/web_server/tests/part_01.rs c62752fe2fa6b74ddc9d94d36f8d191376b1575b15d21fc434c5a5da4c932692 src/web_server/tests/part_01.rs
bb09d550a0dacb6169ffabe2877f510e3a1b7ebd5a6bf527c0f19395c77b753e src/web_server/tests/part_02.rs ef49a4230a1cb46c51866c4c0a49eae8c711377245df50c4f36a86693c040c1e src/web_server/tests/part_02.rs
e54f44f6ca1ed2c41e620de9195d32c4f8bb6936c3881745316a07160f87b4fa src/web_server/tests/part_03.rs e54f44f6ca1ed2c41e620de9195d32c4f8bb6936c3881745316a07160f87b4fa src/web_server/tests/part_03.rs
f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json
1725c10331745dc04a07675784d385260da7b14567b8dba3cbae5e891eb5dadf tests/http2.rs 1725c10331745dc04a07675784d385260da7b14567b8dba3cbae5e891eb5dadf tests/http2.rs
+2 -2
View File
@@ -18,7 +18,7 @@ if [[ $# -gt 0 ]]; then shift; fi
case "$mode" in case "$mode" in
help|-h|--help) help|-h|--help)
cat <<'HELP' cat <<'HELP'
RustProxy 0.27.1 RustProxy 0.27.2
./dev.sh Build and start proxy + local panel ./dev.sh Build and start proxy + local panel
./dev.sh run [serve flags] Same, with extra server options ./dev.sh run [serve flags] Same, with extra server options
./dev.sh admin setup-token Generate/print a one-time setup capability before first login ./dev.sh admin setup-token Generate/print a one-time setup capability before first login
@@ -139,7 +139,7 @@ if [[ "$mode" == "test" ]]; then
printf 'Checking all Rust targets before tests...\n' >&2 printf 'Checking all Rust targets before tests...\n' >&2
cargo check --locked --all-targets cargo check --locked --all-targets
fi fi
printf 'Building RustProxy 0.27.1...\n' >&2 printf 'Building RustProxy 0.27.2...\n' >&2
cargo build --locked cargo build --locked
BIN="$CARGO_TARGET_DIR/debug/rust-proxy" BIN="$CARGO_TARGET_DIR/debug/rust-proxy"
case "$mode" in case "$mode" in
+1 -1
View File
@@ -1,6 +1,6 @@
package main package main
const version = "0.27.1" const version = "0.27.2"
type config struct { type config struct {
Listen string Listen string
+1 -1
View File
@@ -1,4 +1,4 @@
# Administration API access - 0.27.1 # Administration API access - 0.27.2
## Sign-in boundary ## Sign-in boundary
+2 -2
View File
@@ -9,7 +9,7 @@ each Docker host, so the proxy container never needs Docker-socket access. See
Build locally with a normal OCI builder: Build locally with a normal OCI builder:
```bash ```bash
docker build -t rustproxy:0.27.1 . docker build -t rustproxy:0.27.2 .
``` ```
Persist `/var/lib/rustproxy`; it contains the SQLite database, certificates and the Persist `/var/lib/rustproxy`; it contains the SQLite database, certificates and the
@@ -22,7 +22,7 @@ docker volume create rustproxy-data
docker run --rm -d --name rustproxy \ docker run --rm -d --name rustproxy \
-p 8080:8080 \ -p 8080:8080 \
-v rustproxy-data:/var/lib/rustproxy \ -v rustproxy-data:/var/lib/rustproxy \
rustproxy:0.27.1 rustproxy:0.27.2
docker exec rustproxy rust-proxy admin setup-token docker exec rustproxy rust-proxy admin setup-token
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
# Stored log explorer # Stored log explorer
RustProxy 0.27.1 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent. RustProxy 0.27.2 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent.
## Separation from Live Logs ## Separation from Live Logs
+1 -1
View File
@@ -1,4 +1,4 @@
# Installing and updating RustProxy 0.27.1 # Installing and updating RustProxy 0.27.2
The scripts prepare a Linux/systemd installation. This source archive is **not a The scripts prepare a Linux/systemd installation. This source archive is **not a
verified production build**: Rust/Cargo, real CA operations, live systemd startup verified production build**: Rust/Cargo, real CA operations, live systemd startup
+2 -1
View File
@@ -15,6 +15,7 @@ This is the canonical index for the current project documentation. The authentic
- [0.25.1 release audit](V0.25.1_AUDIT.md) - historical Traffic/module-split release scope and compiler-regression guards. - [0.25.1 release audit](V0.25.1_AUDIT.md) - historical Traffic/module-split release scope and compiler-regression guards.
- [0.25.0 specification audit](V0.25.0_AUDIT.md) - Web Server foundation point-by-point implementation status. - [0.25.0 specification audit](V0.25.0_AUDIT.md) - Web Server foundation point-by-point implementation status.
- [0.25.0 pre-production security audit](V0.25.0_SECURITY_PREPROD_AUDIT.md) - threat-oriented source/configuration review, fixes made in-session and mandatory runtime security gates. - [0.25.0 pre-production security audit](V0.25.0_SECURITY_PREPROD_AUDIT.md) - threat-oriented source/configuration review, fixes made in-session and mandatory runtime security gates.
- [0.27.2 implementation note](V0.27.2_IMPLEMENTATION.md) - managed Certbot relocation/renewal repair.
- [0.27.1 implementation note](V0.27.1_IMPLEMENTATION.md) - first-admin command, managed/fallback certificate runtime and account-scoped route-list sorting. - [0.27.1 implementation note](V0.27.1_IMPLEMENTATION.md) - first-admin command, managed/fallback certificate runtime and account-scoped route-list sorting.
- [0.27.0 implementation note](V0.27.0_IMPLEMENTATION.md) - compression defaults/scoped policy, persistent Docker publications and visible cache occupancy meter fill. - [0.27.0 implementation note](V0.27.0_IMPLEMENTATION.md) - compression defaults/scoped policy, persistent Docker publications and visible cache occupancy meter fill.
- [0.26.7 implementation note](V0.26.7_IMPLEMENTATION.md) - HOT/COLD occupancy meters, backend rotation continuity and container Certbot packaging. - [0.26.7 implementation note](V0.26.7_IMPLEMENTATION.md) - HOT/COLD occupancy meters, backend rotation continuity and container Certbot packaging.
@@ -69,7 +70,7 @@ This is the canonical index for the current project documentation. The authentic
## Historical or version-labelled context ## Historical or version-labelled context
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.27.1_IMPLEMENTATION.md](V0.27.1_IMPLEMENTATION.md); [V0.27.0_IMPLEMENTATION.md](V0.27.0_IMPLEMENTATION.md) is retained as the prior compression/Docker-publication milestone; [V0.26.7_IMPLEMENTATION.md](V0.26.7_IMPLEMENTATION.md) is retained as the prior cache/backend/container milestone; [V0.26.6_IMPLEMENTATION.md](V0.26.6_IMPLEMENTATION.md) is retained as the prior access-policy/administration milestone; [V0.26.5_IMPLEMENTATION.md](V0.26.5_IMPLEMENTATION.md) is retained as the prior interface-scaling/listener-navigation milestone, [V0.26.4_IMPLEMENTATION.md](V0.26.4_IMPLEMENTATION.md) as the prior administration/backend-lifecycle milestone, [V0.26.3_IMPLEMENTATION.md](V0.26.3_IMPLEMENTATION.md) as the prior appearance/UI milestone, [V0.26.2_IMPLEMENTATION.md](V0.26.2_IMPLEMENTATION.md) as the prior Logs/UI milestone and [V0.26.1_IMPLEMENTATION.md](V0.26.1_IMPLEMENTATION.md) as the prior protocol-attribution milestone. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain historical evidence. Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.27.2_IMPLEMENTATION.md](V0.27.2_IMPLEMENTATION.md); [V0.27.1_IMPLEMENTATION.md](V0.27.1_IMPLEMENTATION.md) is retained as the prior managed-runtime/list-sorting milestone; [V0.27.0_IMPLEMENTATION.md](V0.27.0_IMPLEMENTATION.md) is retained as the prior compression/Docker-publication milestone; [V0.26.7_IMPLEMENTATION.md](V0.26.7_IMPLEMENTATION.md) is retained as the prior cache/backend/container milestone; [V0.26.6_IMPLEMENTATION.md](V0.26.6_IMPLEMENTATION.md) is retained as the prior access-policy/administration milestone; [V0.26.5_IMPLEMENTATION.md](V0.26.5_IMPLEMENTATION.md) is retained as the prior interface-scaling/listener-navigation milestone, [V0.26.4_IMPLEMENTATION.md](V0.26.4_IMPLEMENTATION.md) as the prior administration/backend-lifecycle milestone, [V0.26.3_IMPLEMENTATION.md](V0.26.3_IMPLEMENTATION.md) as the prior appearance/UI milestone, [V0.26.2_IMPLEMENTATION.md](V0.26.2_IMPLEMENTATION.md) as the prior Logs/UI milestone and [V0.26.1_IMPLEMENTATION.md](V0.26.1_IMPLEMENTATION.md) as the prior protocol-attribution milestone. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain historical evidence.
Older context documents such as [INTEGRATIONS_040](INTEGRATIONS_040.md), [MANAGEMENT](MANAGEMENT.md), [SECURITY_AUDIT](SECURITY_AUDIT.md), [ACME_RFC2136](ACME_RFC2136.md) and older version audits remain available for traceability but should not override the current functional/technical documents above. Older context documents such as [INTEGRATIONS_040](INTEGRATIONS_040.md), [MANAGEMENT](MANAGEMENT.md), [SECURITY_AUDIT](SECURITY_AUDIT.md), [ACME_RFC2136](ACME_RFC2136.md) and older version audits remain available for traceability but should not override the current functional/technical documents above.
+8 -7
View File
@@ -1,18 +1,18 @@
# Verification report - RustProxy 0.27.1 # Verification report - RustProxy 0.27.2
## Scope ## Scope
RustProxy 0.27.1 changes first-administrator onboarding, ACME/Certbot runtime preparation and account-scoped sorting for vhosts, Docker vhosts, redirects, 404 hosts and streams while keeping SQLite schema 8. RustProxy 0.27.2 is a focused repair for managed ACME/Certbot execution while keeping SQLite schema 8 and the saved configuration format unchanged.
The first-run administrator screen now presents a complete command using the default `./data` directory and a second standard-service example using `/var/lib/rustproxy`. 0.27.1 created the private environment under `<data-dir>/acme-python.installing`, installed Certbot there, validated it, and then renamed the directory to `<data-dir>/acme-python`. pip console scripts contain an absolute interpreter path, so `bin/certbot` could retain a shebang pointing at the removed staging directory and fail with `OSError` during issuance/renewal.
Certificate automation now prefers a RustProxy-owned Python environment at `<data-dir>/acme-python`. Missing environments are prepared with `venv` and pip; when a distribution omits the stdlib `venv` module but provides pip, RustProxy can bootstrap `virtualenv` into its own data directory rather than changing distribution site-packages. Certbot 5.8.0 and the Cloudflare, OVH and RFC2136 plugins are installed into that managed environment. A runnable distribution Certbot remains the fallback. The Certificates panel reports managed/system/preparing/unavailable state and missing Python/pip diagnostics; manual PEM import and existing-file workflows do not depend on ACME runtime availability. 0.27.2 validates and launches managed Certbot through the final venv interpreter with `python -I -m certbot`. Existing 0.27.1 environments can therefore be reused without deleting `/var/lib/rustproxy/acme-python`. The system/distribution Certbot fallback remains unchanged.
The route/list pages gain feature-specific sort-column and sort-direction controls. Their values are saved in the existing account-scoped administrator preferences and default safely when absent. Redirects and 404 hosts use the same search/sort/action toolbar structure as Vhosts. The 0.27.1 first-administrator, runtime-status and route/list sorting behavior remains unchanged.
## Regression coverage ## Regression coverage
Static/source contracts cover the new first-run commands, ACME runtime status UI/API, managed environment path and pinned package set, distro fallback markers, preference fields and list-specific sort controls. They also reject the development-only `./dev.sh tls deploy` UI hook and require the production binary/data-directory example, both official repository links and Python/pip/Certbot runtime metadata in the startup banner and authenticated application information. Existing release-smoke, language/OpenAPI, source-module, security, web-server, browser-service and Docker-agent gates remain part of the Rust-independent verification set. Static/source contracts cover the managed runtime path and pinned package set, distro fallback markers and the new module-based Certbot launch path. A dedicated relocation regression reproduces the stale-shebang failure after renaming a venv and verifies that module execution through the final interpreter remains valid. Existing release-smoke, language/OpenAPI, source-module, security, web-server, browser-service and Docker-agent gates remain part of the Rust-independent verification set.
## Verification status in this workspace ## Verification status in this workspace
@@ -20,6 +20,7 @@ Rust/Cargo is not installed in this workspace. Rust-independent checks are run h
| Check | Status | | Check | Status |
|---|---| |---|---|
| managed ACME relocated-venv regression (`release_smoke.py`) | PASS |
| changed administration JavaScript syntax (`node --check`) | PASS | | changed administration JavaScript syntax (`node --check`) | PASS |
| language/OpenAPI JSON parse | PASS | | language/OpenAPI JSON parse | PASS |
| `python3 scripts/langs_check.py` | PASS | | `python3 scripts/langs_check.py` | PASS |
@@ -41,4 +42,4 @@ Rust/Cargo is not installed in this workspace. Rust-independent checks are run h
| `cargo test --all-targets --all-features` | BLOCKED: Rust/Cargo toolchain unavailable | | `cargo test --all-targets --all-features` | BLOCKED: Rust/Cargo toolchain unavailable |
| complete release build | BLOCKED at Rust compilation: Rust/Cargo toolchain unavailable | | complete release build | BLOCKED at Rust compilation: Rust/Cargo toolchain unavailable |
The 0.27.1 source archive must not be described as compiler-verified or production-ready until the Rust-enabled release-host gates pass. Static/source contracts do not substitute for Rust parsing, type checking, Clippy, unit tests or real protocol runtime tests. The 0.27.2 source archive must not be described as compiler-verified or production-ready until the Rust-enabled release-host gates pass. Static/source contracts do not substitute for Rust parsing, type checking, Clippy, unit tests or real protocol runtime tests.
+27
View File
@@ -0,0 +1,27 @@
# RustProxy 0.27.2 implementation note
## Scope
0.27.2 is a focused ACME runtime repair. It keeps SQLite schema 8 and the saved configuration format unchanged.
## Managed Certbot relocation bug
0.27.1 creates the private environment under `<data-dir>/acme-python.installing`, installs Certbot and its DNS plugins there, validates it, and then atomically renames the directory to `<data-dir>/acme-python`.
Python virtual-environment console scripts created by pip contain an absolute interpreter path in their shebang. After the directory rename, `bin/certbot` can still point at the removed `acme-python.installing/bin/python`. Executing that console script can therefore fail with a local `OSError`; the private worker intentionally reduced such errors to `acme.local_io_or_json_error`, which hid the relocation detail from the UI.
## Repair
Managed-environment validation now runs Certbot as:
```text
<data-dir>/acme-python/bin/python -I -m certbot --version
```
ACME issuance and renewal use the same final interpreter with `-m certbot`. The provider worker receives the managed interpreter path from RustProxy and no longer depends on the relocated pip console-script shebang.
The existing system/distribution Certbot fallback continues to execute its discovered `certbot` program normally.
## Upgrade behavior
No ACME profile, account, certificate or credential migration is required. An `acme-python` environment created by 0.27.1 can be reused as-is because 0.27.2 bypasses the stale console-script shebang and imports Certbot from the environment's installed Python modules.
+1 -1
View File
@@ -19,7 +19,7 @@ classes, cache outcomes, capture-state gauges and configured-route traffic. Raw
are intentionally absent from Prometheus labels. are intentionally absent from Prometheus labels.
Import the JSON in Grafana and select a Prometheus datasource when prompted. The Import the JSON in Grafana and select a Prometheus datasource when prompted. The
dashboard expects RustProxy 0.27.1 metric names and a scrape interval around 15s. dashboard expects RustProxy 0.27.2 metric names and a scrape interval around 15s.
Collectors can be disabled in RustProxy settings; panels for disabled collectors Collectors can be disabled in RustProxy settings; panels for disabled collectors
will simply show no series. will simply show no series.
+2 -2
View File
@@ -4529,9 +4529,9 @@
}, },
"timepicker": {}, "timepicker": {},
"timezone": "browser", "timezone": "browser",
"title": "RustProxy 0.27.1 - Operations", "title": "RustProxy 0.27.2 - Operations",
"uid": "rustproxy-0170", "uid": "rustproxy-0170",
"version": 5, "version": 5,
"weekStart": "", "weekStart": "",
"description": "RustProxy 0.27.1 operations dashboard including backend client lifecycle/retry diagnostics, bounded Traffic analytics, country and User-Agent-family visibility, and built-in Web Server traffic." "description": "RustProxy 0.27.2 operations dashboard including backend client lifecycle/retry diagnostics, bounded Traffic analytics, country and User-Agent-family visibility, and built-in Web Server traffic."
} }
+1 -1
View File
@@ -183,7 +183,7 @@
"saveCertificate": "Apply certificate", "saveCertificate": "Apply certificate",
"reloadCertificates": "Reload certificate files", "reloadCertificates": "Reload certificate files",
"certificateHelp": "Upload a PEM pair, reference files already in data/certs, or deploy an externally issued certificate with the installed RustProxy binary. Reload re-reads configured files without changing the configuration revision.", "certificateHelp": "Upload a PEM pair, reference files already in data/certs, or deploy an externally issued certificate with the installed RustProxy binary. Reload re-reads configured files without changing the configuration revision.",
"certificateHint": "External Certbot hook (standard service install): rust-proxyopt/rustproxy/current/ --data-dir /var/lib/rustproxy tls deploy --id shared-cert --cert /etc/letsencrypt/live/example/fullchain.pem --key /etc/letsencrypt/live/example/privkey.pem", "certificateHint": "External Certbot hook (standard service install): /opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy tls deploy --id shared-cert --cert /etc/letsencrypt/live/example/fullchain.pem --key /etc/letsencrypt/live/example/privkey.pem",
"emptyCertificates": "No explicit SNI bindings. The generated default certificate is used while the TLS listener is enabled.", "emptyCertificates": "No explicit SNI bindings. The generated default certificate is used while the TLS listener is enabled.",
"certificateSafety": "Certificates secure TLS connections, not routing. Assign a certificate to a vhost. Changes affect new handshakes.", "certificateSafety": "Certificates secure TLS connections, not routing. Assign a certificate to a vhost. Changes affect new handshakes.",
"wsActive": "WebSocket tunnels", "wsActive": "WebSocket tunnels",
+1 -1
View File
@@ -183,7 +183,7 @@
"saveCertificate": "Zastosuj certyfikat", "saveCertificate": "Zastosuj certyfikat",
"reloadCertificates": "Wczytaj pliki certyfikatów", "reloadCertificates": "Wczytaj pliki certyfikatów",
"certificateHelp": "Wgraj parę PEM, wskaż pliki w data/certs albo wdrażaj certyfikat z zewnętrznego wystawcy zainstalowaną binarką RustProxy. Ponowne wczytanie odczytuje skonfigurowane pliki bez zmiany rewizji.", "certificateHelp": "Wgraj parę PEM, wskaż pliki w data/certs albo wdrażaj certyfikat z zewnętrznego wystawcy zainstalowaną binarką RustProxy. Ponowne wczytanie odczytuje skonfigurowane pliki bez zmiany rewizji.",
"certificateHint": "Hook Certbota (standardowa instalacja usługi): rust-proxy --data-dir /var/lib/rustproxy tls deploy --id shared-cert --cert /etc/letsencrypt/live/example/fullchain.pem --key /etc/letsencrypt/live/example/privkey.pem", "certificateHint": "Hook Certbota (standardowa instalacja usługi): /opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy tls deploy --id shared-cert --cert /etc/letsencrypt/live/example/fullchain.pem --key /etc/letsencrypt/live/example/privkey.pem",
"emptyCertificates": "Brak jawnych powiązań SNI. Aktywny listener TLS używa wygenerowanego certyfikatu domyślnego.", "emptyCertificates": "Brak jawnych powiązań SNI. Aktywny listener TLS używa wygenerowanego certyfikatu domyślnego.",
"certificateSafety": "Certyfikaty samopodpisane służą do testów. Certyfikat nie tworzy trasy. Zmiana dotyczy nowych połączeń TLS.", "certificateSafety": "Certyfikaty samopodpisane służą do testów. Certyfikat nie tworzy trasy. Zmiana dotyczy nowych połączeń TLS.",
"wsActive": "Tunele WebSocket", "wsActive": "Tunele WebSocket",
+1 -1
View File
@@ -86,7 +86,7 @@ def add_file(archive: zipfile.ZipFile, rel: Path, data: bytes, timestamp: tuple[
def main() -> None: def main() -> None:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('output', nargs='?', type=Path, default=ROOT.parent / 'RustProxy-0.27.1.zip') parser.add_argument('output', nargs='?', type=Path, default=ROOT.parent / 'RustProxy-0.27.2.zip')
output = parser.parse_args().output.resolve() output = parser.parse_args().output.resolve()
paths = files() paths = files()
checksum = ROOT / 'SHA256SUMS' checksum = ROOT / 'SHA256SUMS'
+18 -10
View File
@@ -165,8 +165,20 @@ def credentials(value: dict) -> bytes:
return "".join(f"dns_rfc2136_{k} = {v}\n" for k, v in lines.items()).encode("ascii") return "".join(f"dns_rfc2136_{k} = {v}\n" for k, v in lines.items()).encode("ascii")
def certbot_command(value: dict, work: Path, credential_file: Path, executable: str) -> list[str]: def certbot_prefix(executable: str | None = None) -> list[str]:
args = [executable, "certonly", "--non-interactive", "--agree-tos", "--no-eff-email", if executable:
return [executable]
managed_python = os.environ.get("RUSTPROXY_CERTBOT_PYTHON")
if managed_python:
return [managed_python, "-I", "-m", "certbot"]
discovered = shutil.which("certbot")
if discovered:
return [discovered]
raise AcmeError("acme.certbot_not_installed")
def certbot_command(value: dict, work: Path, credential_file: Path, command_prefix: list[str]) -> list[str]:
args = [*command_prefix, "certonly", "--non-interactive", "--agree-tos", "--no-eff-email",
"--email", value["email"], "--server", DIRECTORIES[value["environment"]], "--email", value["email"], "--server", DIRECTORIES[value["environment"]],
"--cert-name", "rustproxy", "--keep-until-expiring", "--key-type", "ecdsa", "--cert-name", "rustproxy", "--keep-until-expiring", "--key-type", "ecdsa",
"--config-dir", str(work / "config"), "--work-dir", str(work / "work"), "--config-dir", str(work / "config"), "--work-dir", str(work / "work"),
@@ -203,9 +215,7 @@ def read_generated(work: Path, name: str) -> bytes:
def issue(raw: dict, certificate_directory: Path, executable: str | None = None) -> dict: def issue(raw: dict, certificate_directory: Path, executable: str | None = None) -> dict:
value = validate(raw) value = validate(raw)
executable = executable or shutil.which("certbot") command_prefix = certbot_prefix(executable)
if not executable:
raise AcmeError("acme.certbot_not_installed")
certificate_directory = certificate_directory.absolute() certificate_directory = certificate_directory.absolute()
private_directory(certificate_directory) private_directory(certificate_directory)
root = certificate_directory / "acme" root = certificate_directory / "acme"
@@ -242,7 +252,7 @@ def issue(raw: dict, certificate_directory: Path, executable: str | None = None)
temporary = work / ("profile-" + uuid.uuid4().hex) temporary = work / ("profile-" + uuid.uuid4().hex)
write_new(temporary, json.dumps(profile).encode()) write_new(temporary, json.dumps(profile).encode())
os.replace(temporary, work / "profile.json") os.replace(temporary, work / "profile.json")
args = certbot_command(value, work, credential_file, executable) args = certbot_command(value, work, credential_file, command_prefix)
try: try:
result = subprocess.run(args, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, result = subprocess.run(args, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, timeout=1200 + value["propagation_seconds"], check=False) stderr=subprocess.DEVNULL, timeout=1200 + value["propagation_seconds"], check=False)
@@ -354,9 +364,7 @@ def renew(raw: dict, certificate_directory: Path, executable: str | None = None)
Dry-run validates challenge access but never stages/deploys its test output. Dry-run validates challenge access but never stages/deploys its test output.
""" """
value = renewal_request(raw) value = renewal_request(raw)
executable = executable or shutil.which("certbot") command_prefix = certbot_prefix(executable)
if not executable:
raise AcmeError("acme.certbot_not_installed")
certificate_directory = certificate_directory.absolute() certificate_directory = certificate_directory.absolute()
private_directory(certificate_directory) private_directory(certificate_directory)
work = profile_directory(certificate_directory, value["provider"], value["environment"], value["certificate_id"]) work = profile_directory(certificate_directory, value["provider"], value["environment"], value["certificate_id"])
@@ -400,7 +408,7 @@ def renew(raw: dict, certificate_directory: Path, executable: str | None = None)
installed_key = read_private(certificate_directory / value["key_file"], MAX_PEM) installed_key = read_private(certificate_directory / value["key_file"], MAX_PEM)
# Check dependencies/installed PEM before invoking a CA operation. # Check dependencies/installed PEM before invoking a CA operation.
old_dates = certificate_dates(installed_cert) old_dates = certificate_dates(installed_cert)
args = [executable, "renew", "--cert-name", "rustproxy", "--non-interactive", args = [*command_prefix, "renew", "--cert-name", "rustproxy", "--non-interactive",
"--no-random-sleep-on-renew", "--no-directory-hooks", "--no-random-sleep-on-renew", "--no-directory-hooks",
"--config-dir", str(work / "config"), "--work-dir", str(work / "work"), "--logs-dir", str(work / "logs")] "--config-dir", str(work / "config"), "--work-dir", str(work / "work"), "--logs-dir", str(work / "logs")]
if value["dry_run"]: if value["dry_run"]:
+15 -11
View File
@@ -24,7 +24,7 @@ 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.27.1" assert manifest["package"]["version"] == "0.27.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.19" and "quinn" in h3dep["features"]
@@ -32,14 +32,14 @@ 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.27.1"' in lock assert 'name = "rust-proxy"\nversion = "0.27.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.27.1", "README.md": "# RustProxy 0.27.2",
"CHANGELOG.md": "## 0.27.1", "CHANGELOG.md": "## 0.27.2",
"ROADMAP.md": "## 0.27.1", "ROADMAP.md": "## 0.27.2",
"AGENTS.md": "Current source version: 0.27.1.", "AGENTS.md": "Current source version: 0.27.2.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.27.1", "docs/TEST_REPORT.md": "# Verification report - RustProxy 0.27.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"
@@ -287,7 +287,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.27.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.27.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
@@ -613,12 +613,16 @@ assert 'runtime_setup_once' in acme_source
assert 'include!("acme/runtime_env.rs")' in (root / "src/acme.rs").read_text(encoding="utf-8") assert 'include!("acme/runtime_env.rs")' in (root / "src/acme.rs").read_text(encoding="utf-8")
for marker in ['CERTBOT_PACKAGES', 'certbot==5.8.0', 'certbot-dns-cloudflare==5.8.0', 'certbot-dns-ovh==5.8.0', 'certbot-dns-rfc2136==5.8.0', 'join("acme-python")', 'virtualenv>=20,<21', 'system_has_pip', 'distro_certbot_available', 'acme.runtime_python_missing', 'acme.runtime_pip_missing']: for marker in ['CERTBOT_PACKAGES', 'certbot==5.8.0', 'certbot-dns-cloudflare==5.8.0', 'certbot-dns-ovh==5.8.0', 'certbot-dns-rfc2136==5.8.0', 'join("acme-python")', 'virtualenv>=20,<21', 'system_has_pip', 'distro_certbot_available', 'acme.runtime_python_missing', 'acme.runtime_pip_missing']:
assert marker in acme_runtime_source, marker assert marker in acme_runtime_source, marker
for marker in ['"-I", "-m", "certbot", "--version"', 'RUSTPROXY_CERTBOT_PYTHON', 'managed_certbot_python']:
assert marker in acme_runtime_source, marker
main_source = rust_source(root, "src/main.rs") main_source = rust_source(root, "src/main.rs")
logging_source = rust_source(root, "src/logging.rs") logging_source = rust_source(root, "src/logging.rs")
secret_store_source = rust_source(root, "src/secret_store.rs") secret_store_source = rust_source(root, "src/secret_store.rs")
backup_source = rust_source(root, "src/backup.rs") backup_source = rust_source(root, "src/backup.rs")
config_source = rust_source(root, "src/config.rs") config_source = rust_source(root, "src/config.rs")
acme_worker_source = (root / "scripts/acme_provider.py").read_text() acme_worker_source = (root / "scripts/acme_provider.py").read_text()
for marker in ['def certbot_prefix', 'RUSTPROXY_CERTBOT_PYTHON', '"-I", "-m", "certbot"', '[*command_prefix, "renew"', 'certbot_command(value, work, credential_file, command_prefix)']:
assert marker in acme_worker_source, marker
for feature_marker in [ for feature_marker in [
"AES_256_GCM", "master.key", "O_NOFOLLOW", "migrate_acme_credentials", "AES_256_GCM", "master.key", "O_NOFOLLOW", "migrate_acme_credentials",
"secret_store", "SecretMetadata", "secret_store", "SecretMetadata",
@@ -816,7 +820,7 @@ assert any(variable.get('name') == 'traffic_vhost' for variable in grafana['temp
assert any(panel.get('title') == 'Traffic analytics and Web Server' for panel in grafana['panels']) assert any(panel.get('title') == 'Traffic analytics and Web Server' for panel in grafana['panels'])
assert any(panel.get('title') == 'Traffic capture state' for panel in grafana['panels']) assert any(panel.get('title') == 'Traffic capture state' for panel in grafana['panels'])
assert any(panel.get('title') == 'Top configured routes' for panel in grafana['panels']) assert any(panel.get('title') == 'Top configured routes' for panel in grafana['panels'])
# 0.27.1 compression/protocol/dashboard UX regressions. # 0.27.2 compression/protocol/dashboard UX regressions.
compression_source = rust_source(root, "src/compression.rs") compression_source = rust_source(root, "src/compression.rs")
metrics_source = rust_source(root, "src/metrics.rs") metrics_source = rust_source(root, "src/metrics.rs")
admin_source = rust_source(root, "src/admin/mod.rs") admin_source = rust_source(root, "src/admin/mod.rs")
@@ -1373,7 +1377,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.27.1"' in openapi assert '"RuntimeInformation"' in openapi and '"version": "0.27.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")
@@ -1418,5 +1422,5 @@ for schema_name in ["RedirectHost", "NotFoundHost"]:
node = openapi_0241["components"]["schemas"][schema_name]["properties"]["frontend"]["properties"]["hosts"] node = openapi_0241["components"]["schemas"][schema_name]["properties"]["frontend"]["properties"]["hosts"]
assert "maxItems" not in node, schema_name assert "maxItems" not in node, schema_name
print("PASS: 0.27.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-v8 migrations.") print("PASS: 0.27.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-v8 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.")
+1 -1
View File
@@ -97,7 +97,7 @@ def digest(path: Path) -> str:
def fetch_https(url: str, max_bytes: int) -> bytes: def fetch_https(url: str, max_bytes: int) -> bytes:
if urllib.parse.urlparse(url).scheme != 'https': if urllib.parse.urlparse(url).scheme != 'https':
raise ValueError('GeoIP downloads require HTTPS.') raise ValueError('GeoIP downloads require HTTPS.')
request = urllib.request.Request(url, headers={'User-Agent': 'RustProxy-installer/0.27.1'}) request = urllib.request.Request(url, headers={'User-Agent': 'RustProxy-installer/0.27.2'})
with urllib.request.urlopen(request, timeout=30) as response: with urllib.request.urlopen(request, timeout=30) as response:
if urllib.parse.urlparse(response.geturl()).scheme != 'https': if urllib.parse.urlparse(response.geturl()).scheme != 'https':
raise ValueError('GeoIP download redirected away from HTTPS.') raise ValueError('GeoIP download redirected away from HTTPS.')
+6 -5
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Rust-independent documentation/Reader consistency checks for the 0.27.1 release.""" """Rust-independent documentation/Reader consistency checks for the 0.27.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
@@ -14,6 +14,7 @@ required = [
DOCS / "README.md", DOCS / "README.md",
DOCS / "WEB_SERVER.md", DOCS / "WEB_SERVER.md",
DOCS / "TODO.md", DOCS / "TODO.md",
DOCS / "V0.27.2_IMPLEMENTATION.md",
DOCS / "V0.27.1_IMPLEMENTATION.md", DOCS / "V0.27.1_IMPLEMENTATION.md",
DOCS / "V0.27.0_IMPLEMENTATION.md", DOCS / "V0.27.0_IMPLEMENTATION.md",
DOCS / "V0.26.7_IMPLEMENTATION.md", DOCS / "V0.26.7_IMPLEMENTATION.md",
@@ -58,16 +59,16 @@ 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.27.1") assert readme.startswith("# RustProxy 0.27.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
assert roadmap.startswith("# RustProxy roadmap") and "## 0.27.1" in roadmap and "## 0.27.0" in roadmap and "## 0.26.7" in roadmap and "## 0.26.6" in roadmap and "## 0.26.5" in roadmap and "## 0.26.4" in roadmap and "## 0.26.0" in roadmap and "## 0.25.5" in roadmap and "## 0.25.4" in roadmap and "## 0.25.3" in roadmap and "## 0.25.2" in roadmap and "## 0.25.1" in roadmap and "## 0.25.0" in roadmap assert roadmap.startswith("# RustProxy roadmap") and "## 0.27.2" in roadmap and "## 0.27.1" in roadmap and "## 0.27.0" in roadmap and "## 0.26.7" in roadmap and "## 0.26.6" in roadmap and "## 0.26.5" in roadmap and "## 0.26.4" in roadmap and "## 0.26.0" in roadmap and "## 0.25.5" in roadmap and "## 0.25.4" in roadmap and "## 0.25.3" in roadmap and "## 0.25.2" in roadmap and "## 0.25.1" in roadmap and "## 0.25.0" in roadmap
assert "## 0.24." not in roadmap and "## Web Server follow-up" in roadmap and "docs/ROADMAP_HISTORY.md" in roadmap assert "## 0.24." not in roadmap and "## Web Server follow-up" in roadmap and "docs/ROADMAP_HISTORY.md" in roadmap
assert changelog.startswith("# Changelog\n") assert changelog.startswith("# Changelog\n")
for release in ["0.27.1", "0.27.0", "0.26.7", "0.26.6", "0.26.5", "0.26.4", "0.26.1", "0.26.0", "0.25.6", "0.25.5", "0.25.4", "0.25.3"]: for release in ["0.27.2", "0.27.1", "0.27.0", "0.26.7", "0.26.6", "0.26.5", "0.26.4", "0.26.1", "0.26.0", "0.25.6", "0.25.5", "0.25.4", "0.25.3"]:
assert f"## {release}" in changelog, release assert f"## {release}" in changelog, release
assert changelog.index("## 0.27.1") < changelog.index("## 0.27.0") < changelog.index("## 0.26.7") < changelog.index("## 0.26.6") < changelog.index("## 0.26.5") < changelog.index("## 0.26.4") < changelog.index("## 0.26.1") < changelog.index("## 0.26.0") < changelog.index("## 0.25.6") < changelog.index("## 0.25.5") assert changelog.index("## 0.27.2") < changelog.index("## 0.27.1") < changelog.index("## 0.27.0") < changelog.index("## 0.26.7") < changelog.index("## 0.26.6") < changelog.index("## 0.26.5") < changelog.index("## 0.26.4") < changelog.index("## 0.26.1") < changelog.index("## 0.26.0") < changelog.index("## 0.25.6") < changelog.index("## 0.25.5")
assert "CHANGELOG_TECHNICAL_ARCHIVE.md" in changelog assert "CHANGELOG_TECHNICAL_ARCHIVE.md" in changelog
for developer_only in ["Clean the remaining compiler warnings", "Update package/source/UI/OpenAPI/release identity", "source regressions and update release identity"]: for developer_only in ["Clean the remaining compiler warnings", "Update package/source/UI/OpenAPI/release identity", "source regressions and update release identity"]:
assert developer_only not in changelog, f"developer-only CHANGELOG detail: {developer_only}" assert developer_only not in changelog, f"developer-only CHANGELOG detail: {developer_only}"
+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/proxy/websocket.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/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/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", "scripts/mock_server.py", "scripts/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", "scripts/docs_check.py", "scripts/web_server_contracts.py", "scripts/rust_source.py", "scripts/rust_module_contracts.py", "scripts/source_module_contracts.py", "scripts/acme_provider.py", "src/basic_auth.rs", "scripts/deploy.py", "scripts/deploy_core.py", "scripts/deploy_runtime.py", "scripts/migrate_npm.py", "scripts/migrate_npm_core.py", "scripts/migrate_npm_api.py", "docs/NPM_MIGRATION.md", "docs/STREAMS.md", "install.sh", "install-debian13.sh", "update.sh", "build.rs", "scripts/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/proxy/websocket.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/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/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", "scripts/mock_server.py", "scripts/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", "scripts/docs_check.py", "scripts/web_server_contracts.py", "scripts/rust_source.py", "scripts/rust_module_contracts.py", "scripts/source_module_contracts.py", "scripts/acme_provider.py", "src/basic_auth.rs", "scripts/deploy.py", "scripts/deploy_core.py", "scripts/deploy_runtime.py", "scripts/migrate_npm.py", "scripts/migrate_npm_core.py", "scripts/migrate_npm_api.py", "docs/NPM_MIGRATION.md", "docs/STREAMS.md", "install.sh", "install-debian13.sh", "update.sh", "build.rs", "scripts/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.27.1 source tree: missing {path}") raise ValueError(f"Incomplete RustProxy 0.27.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.27.1 entry point; extract the complete package, not individual files.") raise ValueError("src/lib.rs is not the 0.27.2 entry point; extract the complete package, not individual files.")
return messages return messages
+53 -5
View File
@@ -7,6 +7,7 @@ then checks the 0.24.1 shared-statistics/runtime/settings contracts and retained
from __future__ import annotations from __future__ import annotations
import hashlib import hashlib
import importlib.util
import json import json
import os import os
import re import re
@@ -123,7 +124,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.27.1"' in (target / "Cargo.toml").read_text(encoding="utf-8") assert 'version = "0.27.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()
@@ -134,7 +135,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.27.1" assert manifest["to_version"] == "0.27.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()
@@ -604,7 +605,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.27.1"' in openapi_0241 assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.27.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
@@ -1087,7 +1088,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.27.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.27.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"']:
@@ -1238,6 +1239,52 @@ def reproducible_archive_smoke(tmp: Path) -> None:
assert len(timestamps) == 1 assert len(timestamps) == 1
def managed_acme_relocation_smoke(tmp: Path) -> None:
"""Reproduce the 0.27.1 relocated console-script shebang failure."""
stage = tmp / "acme-python.installing"
final = tmp / "acme-python"
bindir = stage / "bin"
bindir.mkdir(parents=True)
python = bindir / "python"
python.write_text(
"#!/bin/sh\n"
"test \"$1\" = -I && test \"$2\" = -m && test \"$3\" = certbot && test \"$4\" = --version || exit 2\n"
"echo 'certbot 5.8.0'\n",
encoding="utf-8",
)
python.chmod(0o755)
console = bindir / "certbot"
console.write_text(f"#!{python}\necho 'certbot 5.8.0'\n", encoding="utf-8")
console.chmod(0o755)
stage.rename(final)
relocated_console = final / "bin/certbot"
try:
subprocess.run([str(relocated_console), "--version"], check=True, capture_output=True, text=True)
except OSError:
pass
else:
raise AssertionError("relocated pip console script unexpectedly kept a valid staging shebang")
worker_path = ROOT / "scripts/acme_provider.py"
spec = importlib.util.spec_from_file_location("rustproxy_acme_provider", worker_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
old = os.environ.get("RUSTPROXY_CERTBOT_PYTHON")
os.environ["RUSTPROXY_CERTBOT_PYTHON"] = str(final / "bin/python")
try:
spec.loader.exec_module(module)
prefix = module.certbot_prefix()
assert prefix == [str(final / "bin/python"), "-I", "-m", "certbot"]
result = subprocess.run(prefix + ["--version"], check=True, capture_output=True, text=True)
assert result.stdout.strip() == "certbot 5.8.0"
finally:
if old is None:
os.environ.pop("RUSTPROXY_CERTBOT_PYTHON", None)
else:
os.environ["RUSTPROXY_CERTBOT_PYTHON"] = old
def main() -> None: def main() -> None:
subprocess.run(["python3", str(ROOT / "scripts/rust_module_contracts.py")], check=True) subprocess.run(["python3", str(ROOT / "scripts/rust_module_contracts.py")], check=True)
subprocess.run(["python3", str(ROOT / "scripts/source_module_contracts.py")], check=True) subprocess.run(["python3", str(ROOT / "scripts/source_module_contracts.py")], check=True)
@@ -1245,6 +1292,7 @@ def main() -> None:
tmp = Path(directory) tmp = Path(directory)
sql_and_backup_smoke(tmp / "sql") sql_and_backup_smoke(tmp / "sql")
upgrade_smoke(tmp / "upgrade") upgrade_smoke(tmp / "upgrade")
managed_acme_relocation_smoke(tmp / "acme-relocation")
reproducible_archive_smoke(tmp / "archives") reproducible_archive_smoke(tmp / "archives")
deployment_gate_smoke(tmp) deployment_gate_smoke(tmp)
npm_migration_smoke(tmp) npm_migration_smoke(tmp)
@@ -1252,7 +1300,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.27.1 SQLite/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.27.2 SQLite/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__":
+1 -1
View File
@@ -85,7 +85,7 @@ def download(destination: Path) -> None:
"""TLS verification uses Python's default trust store; never executes a shell.""" """TLS verification uses Python's default trust store; never executes a shell."""
started = time.monotonic() started = time.monotonic()
opener = build_opener(HttpsRedirects()) opener = build_opener(HttpsRedirects())
request = Request(DOWNLOAD_URL, headers={"User-Agent": "RustProxy-build/0.27.1"}) request = Request(DOWNLOAD_URL, headers={"User-Agent": "RustProxy-build/0.27.2"})
with opener.open(request, timeout=30) as response, destination.open("wb") as output: with opener.open(request, timeout=30) as response, destination.open("wb") as output:
length = response.headers.get("Content-Length") length = response.headers.get("Content-Length")
if length and int(length) > MAX_ARCHIVE: if length and int(length) > MAX_ARCHIVE:
+3 -3
View File
@@ -46,7 +46,7 @@ def upgrade(source: Path, target: Path, *, stopped: bool, data_dir: Path | None
for name in MANAGED: for name in MANAGED:
if (target / name).is_symlink(): if (target / name).is_symlink():
raise ValueError(f"Managed target {name} is a symlink; refusing to replace it.") raise ValueError(f"Managed target {name} is a symlink; refusing to replace it.")
backup = target / ".upgrade-backup" / ("before-0.27.1-" + uuid.uuid4().hex[:12]) backup = target / ".upgrade-backup" / ("before-0.27.2-" + uuid.uuid4().hex[:12])
backup.mkdir(parents=True, mode=0o700) backup.mkdir(parents=True, mode=0o700)
installed: list[str] = [] installed: list[str] = []
moved: list[str] = [] moved: list[str] = []
@@ -84,7 +84,7 @@ def upgrade(source: Path, target: Path, *, stopped: bool, data_dir: Path | None
remove_legacy = legacy.exists() remove_legacy = legacy.exists()
if remove_legacy and (legacy.is_symlink() or not legacy.is_file()): if remove_legacy and (legacy.is_symlink() or not legacy.is_file()):
raise ValueError("Obsolete renewal observation sidecar must be a regular file.") raise ValueError("Obsolete renewal observation sidecar must be a regular file.")
(backup / "upgrade.json").write_text(json.dumps({"from_directory":str(target), "to_version":"0.27.1", "managed_paths":MANAGED, "removed_obsolete_runtime_state": remove_legacy}, indent=2)+"\n", encoding="utf-8") (backup / "upgrade.json").write_text(json.dumps({"from_directory":str(target), "to_version":"0.27.2", "managed_paths":MANAGED, "removed_obsolete_runtime_state": remove_legacy}, indent=2)+"\n", encoding="utf-8")
if remove_legacy: if remove_legacy:
legacy.unlink() legacy.unlink()
except BaseException: except BaseException:
@@ -115,7 +115,7 @@ def main() -> None:
backup = upgrade(Path(__file__).resolve().parents[1], args.target, stopped=args.stopped, data_dir=data_dir) backup = upgrade(Path(__file__).resolve().parents[1], args.target, stopped=args.stopped, data_dir=data_dir)
except (OSError, ValueError) as error: except (OSError, ValueError) as error:
parser.exit(2, f"Upgrade refused or rolled back: {error}\n") parser.exit(2, f"Upgrade refused or rolled back: {error}\n")
print(f"Installed RustProxy 0.27.1. Previous managed source files: {backup}") print(f"Installed RustProxy 0.27.2. Previous managed source files: {backup}")
print("Preserved data, .env, .git, target and other unmanaged files. No database migration was run.") print("Preserved data, .env, .git, target and other unmanaged files. No database migration was run.")
print(f"Next: cd {args.target}; ./dev.sh test; ./dev.sh") print(f"Next: cd {args.target}; ./dev.sh test; ./dev.sh")
+1 -1
View File
@@ -96,7 +96,7 @@ need(web,
'HeaderValue::from_static("no-store")', 'HeaderValue::from_static("no-store")',
"builtin_error_response(StatusCode::FORBIDDEN, &method)", "builtin_error_response(StatusCode::FORBIDDEN, &method)",
"empty_index_can_go_directly_to_directory_listing", "empty_index_can_go_directly_to_directory_listing",
"RustProxy <b>0.27.1</b>", "RustProxy <b>0.27.2</b>",
"traversal_and_symlink_escape_are_not_served", "traversal_and_symlink_escape_are_not_served",
"try_files_rewrites_cache_rules_headers_and_downloads_work", "try_files_rewrites_cache_rules_headers_and_downloads_work",
"precompressed_sidecars_keep_mime_validators_and_range_semantics", "precompressed_sidecars_keep_mime_validators_and_range_semantics",
+16 -13
View File
@@ -60,6 +60,7 @@ impl Manager {
struct RuntimeLaunch { struct RuntimeLaunch {
python: PathBuf, python: PathBuf,
prepend_path: Option<PathBuf>, prepend_path: Option<PathBuf>,
managed_certbot_python: Option<PathBuf>,
} }
fn find_on_path(name: &str) -> Option<PathBuf> { fn find_on_path(name: &str) -> Option<PathBuf> {
@@ -96,15 +97,6 @@ fn venv_python(directory: &Path) -> PathBuf {
venv_bin(directory).join("python") venv_bin(directory).join("python")
} }
#[cfg(windows)]
fn venv_certbot(directory: &Path) -> PathBuf {
venv_bin(directory).join("certbot.exe")
}
#[cfg(not(windows))]
fn venv_certbot(directory: &Path) -> PathBuf {
venv_bin(directory).join("certbot")
}
async fn command_success( async fn command_success(
mut command: Command, mut command: Command,
timeout: Duration, timeout: Duration,
@@ -131,8 +123,7 @@ async fn managed_environment_valid(
shutdown: &tokio_util::sync::CancellationToken, shutdown: &tokio_util::sync::CancellationToken,
) -> bool { ) -> bool {
let python = venv_python(directory); let python = venv_python(directory);
let certbot = venv_certbot(directory); if !python.is_file() {
if !python.is_file() || !certbot.is_file() {
return false; return false;
} }
let mut imports = Command::new(&python); let mut imports = Command::new(&python);
@@ -144,8 +135,13 @@ async fn managed_environment_valid(
if !command_success(imports, Duration::from_secs(20), shutdown).await { if !command_success(imports, Duration::from_secs(20), shutdown).await {
return false; return false;
} }
let mut version = Command::new(&certbot); // A venv is created under an atomic staging directory and then renamed
version.arg("--version"); // into place. pip-generated console scripts contain an absolute shebang,
// so the staged `bin/certbot` is not relocatable after that rename. Invoke
// Certbot as a module through the final venv interpreter instead. This also
// makes existing 0.27.1 environments immediately usable after upgrade.
let mut version = Command::new(&python);
version.args(["-I", "-m", "certbot", "--version"]);
command_success(version, Duration::from_secs(20), shutdown).await command_success(version, Duration::from_secs(20), shutdown).await
} }
@@ -307,6 +303,7 @@ async fn ensure_runtime(state: &App) -> Result<RuntimeLaunch, String> {
return Ok(RuntimeLaunch { return Ok(RuntimeLaunch {
python: venv_python(&managed), python: venv_python(&managed),
prepend_path: Some(venv_bin(&managed)), prepend_path: Some(venv_bin(&managed)),
managed_certbot_python: Some(venv_python(&managed)),
}); });
} }
@@ -355,6 +352,7 @@ async fn ensure_runtime(state: &App) -> Result<RuntimeLaunch, String> {
return Ok(RuntimeLaunch { return Ok(RuntimeLaunch {
python: venv_python(&managed), python: venv_python(&managed),
prepend_path: Some(venv_bin(&managed)), prepend_path: Some(venv_bin(&managed)),
managed_certbot_python: Some(venv_python(&managed)),
}); });
} }
@@ -371,6 +369,7 @@ async fn ensure_runtime(state: &App) -> Result<RuntimeLaunch, String> {
return Ok(RuntimeLaunch { return Ok(RuntimeLaunch {
python, python,
prepend_path: None, prepend_path: None,
managed_certbot_python: None,
}); });
} }
@@ -434,9 +433,13 @@ async fn worker(state: &App, directory: PathBuf, raw: Zeroizing<String>) -> Resu
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::null()) .stderr(Stdio::null())
.kill_on_drop(true); .kill_on_drop(true);
command.env_remove("RUSTPROXY_CERTBOT_PYTHON");
if let Some(prepend) = launch.prepend_path.as_deref() { if let Some(prepend) = launch.prepend_path.as_deref() {
command.env("PATH", merged_path(prepend)?); command.env("PATH", merged_path(prepend)?);
} }
if let Some(python) = launch.managed_certbot_python.as_deref() {
command.env("RUSTPROXY_CERTBOT_PYTHON", python);
}
#[cfg(unix)] #[cfg(unix)]
{ {
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
+2 -1
View File
@@ -409,7 +409,8 @@ pub(super) static READER_DOCUMENTS: &[(&str, &str, &str)] = &[
"/docs/V0.25.1_AUDIT.md" "/docs/V0.25.1_AUDIT.md"
)), )),
), ),
reader_doc!("docs/V0.27.1_IMPLEMENTATION.md", "technical"), reader_doc!("docs/V0.27.2_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.27.1_IMPLEMENTATION.md", "historical"),
reader_doc!("docs/V0.27.0_IMPLEMENTATION.md", "technical"), reader_doc!("docs/V0.27.0_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.7_IMPLEMENTATION.md", "technical"), reader_doc!("docs/V0.26.7_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.6_IMPLEMENTATION.md", "technical"), reader_doc!("docs/V0.26.6_IMPLEMENTATION.md", "technical"),
+1 -1
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.27.1", "version": "0.27.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": [
+2 -2
View File
@@ -2823,7 +2823,7 @@
</section> </section>
</div> </div>
</section> </section>
<footer class="page-footer"><span>RustProxy <b>0.27.1</b></span><span id="footer-status">API - / <footer class="page-footer"><span>RustProxy <b>0.27.2</b></span><span id="footer-status">API - /
dropped: 0</span></footer> dropped: 0</span></footer>
</div> </div>
</main> </main>
@@ -3567,7 +3567,7 @@ max_execution_time = 30</code></pre>
<article class="card about-card"> <article class="card about-card">
<div class="about-product"> <div class="about-product">
<span aria-hidden="true" class="brand-mark">r<span>/</span>p</span> <span aria-hidden="true" class="brand-mark">r<span>/</span>p</span>
<div><span class="small-label">RustProxy</span><strong>0.27.1</strong></div> <div><span class="small-label">RustProxy</span><strong>0.27.2</strong></div>
</div> </div>
<dl class="about-meta"> <dl class="about-meta">
<div> <div>
+1 -1
View File
@@ -41,7 +41,7 @@ use zeroize::Zeroizing;
#[command( #[command(
name = "rust-proxy", name = "rust-proxy",
version, version,
about = "RustProxy - independent Rust reverse proxy and static Web Server 0.27.1" about = "RustProxy - independent Rust reverse proxy and static Web Server 0.27.2"
)] )]
struct Cli { struct Cli {
#[arg( #[arg(
+1 -1
View File
@@ -239,7 +239,7 @@ async fn live_logs_command(dir: PathBuf, addr: SocketAddr) -> Result<()> {
} }
}); });
let interactive = io::stdin().is_terminal() && io::stdout().is_terminal(); let interactive = io::stdin().is_terminal() && io::stdout().is_terminal();
println!("RustProxy live logs 0.27.1 - all vhosts. Type 'help' for interactive filters; Ctrl-C or 'quit' exits."); println!("RustProxy live logs 0.27.2 - all vhosts. Type 'help' for interactive filters; Ctrl-C or 'quit' exits.");
if interactive { print!("> "); io::stdout().flush()?; } if interactive { print!("> "); io::stdout().flush()?; }
let mut filters = LiveLogFilters::default(); let mut filters = LiveLogFilters::default();
let mut history = std::collections::BTreeMap::<u64, serde_json::Value>::new(); let mut history = std::collections::BTreeMap::<u64, serde_json::Value>::new();
+1 -1
View File
@@ -159,7 +159,7 @@ pub(super) fn fastcgi_params(
if context.tls { "on" } else { "off" }.into(), if context.tls { "on" } else { "off" }.into(),
); );
params.insert("GATEWAY_INTERFACE".into(), "CGI/1.1".into()); params.insert("GATEWAY_INTERFACE".into(), "CGI/1.1".into());
params.insert("SERVER_SOFTWARE".into(), "RustProxy/0.27.1".into()); params.insert("SERVER_SOFTWARE".into(), "RustProxy/0.27.2".into());
params.insert("REDIRECT_STATUS".into(), "200".into()); params.insert("REDIRECT_STATUS".into(), "200".into());
if let Some(value) = headers if let Some(value) = headers
.get(CONTENT_TYPE) .get(CONTENT_TYPE)
+1 -1
View File
@@ -185,7 +185,7 @@ pub(super) fn directory_listing(path: &str, entries: &[DirectoryEntry]) -> Vec<u
"<a class=\"{class}\" href=\"{href}\"><span class=\"type\">{type_label}</span><span class=\"name\">{label}{slash}</span><span class=\"kind\">{kind}</span></a>" "<a class=\"{class}\" href=\"{href}\"><span class=\"type\">{type_label}</span><span class=\"name\">{label}{slash}</span><span class=\"kind\">{kind}</span></a>"
)); ));
} }
html.push_str(r#"</div></section><footer class="page-footer"><span>RustProxy <b>0.27.1</b></span><span>Web Server · Directory listing</span></footer></main></body></html>"#); html.push_str(r#"</div></section><footer class="page-footer"><span>RustProxy <b>0.27.2</b></span><span>Web Server · Directory listing</span></footer></main></body></html>"#);
html.into_bytes() html.into_bytes()
} }
+1 -1
View File
@@ -202,7 +202,7 @@ async fn empty_index_can_go_directly_to_directory_listing() {
let body = std::str::from_utf8(&body).unwrap(); let body = std::str::from_utf8(&body).unwrap();
assert!(body.contains("file.txt")); assert!(body.contains("file.txt"));
assert!(body.contains("DIRECTORY LISTING")); assert!(body.contains("DIRECTORY LISTING"));
assert!(body.contains("RustProxy <b>0.27.1</b>")); assert!(body.contains("RustProxy <b>0.27.2</b>"));
} }
#[tokio::test] #[tokio::test]