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.
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
+7
View File
@@ -4,6 +4,13 @@
> test-only changes and packaging implementation notes were moved to
> [`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
- 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]]
name = "rust-proxy"
version = "0.27.1"
version = "0.27.2"
dependencies = [
"anyhow",
"arc-swap",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "rust-proxy"
version = "0.27.1"
version = "0.27.2"
edition = "2021"
rust-version = "1.88"
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,
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
@@ -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.
## 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`.
- 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.
- 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.
- 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.
- 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.
- Fix ACME issuance and renewal with the RustProxy-managed Python environment introduced in 0.27.1.
- 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.
- 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.
- Keep the distribution Certbot fallback, SQLite schema 8 and saved configuration format unchanged.
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
@@ -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 (`↑` / `↓`).
- 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
@@ -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.
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
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
@@ -414,7 +412,7 @@ python3 scripts/release_smoke.py
# Runs those checks first, then the Rust suite when Cargo is available:
bash dev.sh test
# 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
+8
View File
@@ -1,5 +1,13 @@
# 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
Current direction: 0.27.0 -> 0.27.1
+40 -39
View File
@@ -1,18 +1,18 @@
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
6479dfef0344de33714f8503944876a3641c8381e9a7ccaaa4faed06b65e67c6 AGENTS.md
59ff94da1b39a85b9738c8265ed3b871f483451871182d73c8a678509ee222c1 CHANGELOG.md
0b0ed48165c10747c1a2f524fbdb27488f3edc7ff0e6fdccc845625d885ef61c Cargo.lock
f6d19f4e9b1453cc3b80d47f6cb27870b205f9aa4592f82a872c53da44e1308d Cargo.toml
4595fb01446f9e9e08a459085dea96c2b27a9f0aff0fab1f40c8a62e95761902 AGENTS.md
49f5f9b0490a8b411f16f8a1a2477e96f832d6d21155727bb80c4895f9877ea1 CHANGELOG.md
3002fea4dac2c78c11003b6ef669e53415702337062ab737958ac48471e24422 Cargo.lock
5823157f89f6d8576a038866ff5bc2be8733f226e345f4ff9be68d1b59d1d34c Cargo.toml
75b008f237b2226c62a7721b7901018ce83bfc2726f047a83a17c66fcb799f3b Dockerfile
37da924319eef96622afb996a528989780a3733cc44eda7944e4f914a81eb70e README.md
fbeb203b4ca4f02c9d35c02561cd9c098bdd2bf58688ea7039e987e24ee8e8e1 ROADMAP.md
3ef21bb67c050fb7189a032e55e4794c4e07a1b20bcdc3f2f6222f5a37fa8fb5 README.md
e62a31c08cc123b39031e95d10d149d36dbccf73a78a18a7578bb88521c89983 ROADMAP.md
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
928ba6a504bc431ae100c6e587861e5088b28822d2cb19da5d437b39d26c864a build.rs
385ac7970fa94f043d0bcf04a91184e6e2c807587b85e55517ed064bb4bddba6 deploy/README.md
31d4e0cd454060b46f359fdb84a14b16a5fe0d3baabb387dd584d76e555558bb deploy/rustproxy.service
18121e6fb922fb0e8d8b582486cb029256b305bd0f14bb989f9a16484b619674 dev.sh
c90c8a484ff15bb516a810c01e19260ba941b652d5d67afc4405f444ab3b06ab dev.sh
5f51efe621f45512a466fa7f2656867c6502a135ca040f4bfeec06a6919b39b6 docker-agent/README.md
7fee7d51a7deb67e73e53a0bdff3aa4630aea9bab9b23a0f9b8001b2169892b1 docker-agent/certificate.go
5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example
@@ -21,25 +21,25 @@ bf687a851374d8f1fa59d050f3f76532f7c12f16a6daa7d3c36102c2d9a8ec9c docker-agent/d
9d77c90767fa929ee8338734d72ce83284d3d965673270f62cb2b1613d587a33 docker-agent/install.sh
b68234472bec5cedd67c7cfe1de7d8806a965dfd4101f944aa6320132f18c14e docker-agent/main.go
3d4051a1f805d145df3877476e4b87773e74be8e123045065955a5133afdc68c docker-agent/main_test.go
dcd840bb3fd542379d3554ddc78e597c64bff22a4bd832b466f0db867840c17e docker-agent/model.go
4e9eb90294d936e0e4f63618bbadd54b8063ec73d4beda9aa4d70d768b16ab97 docker-agent/model.go
e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/network.go
634790f35ff4627df2a78cec2ed371f6e9ec9df00afb386075595fc7f3ed72eb docker-agent/rustproxy-docker-agent.service
5220d669171ec3ad65f50e0bc8de5178dadaa4904def8e1a4f61002aa9e9a55b docker-agent/server.go
875faa3969df9409d79f050a84b4183496f557edda3d34aaec19fe556cf8c471 docs/ACME_PROVIDERS.md
ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md
c7b75ed137094cade2b2047c1c8d304e936e0fe1121ff91a858f77c2e969fa39 docs/API.md
386d1d9b120e05b8b776af87237294d1674ac6f37c6ca4fbd74368014d09c0d7 docs/API_ACCESS.md
d7b224425930cf06cf34d0547e445e0363f6ac4aced43e182d1d88faa2d28cdb docs/API_ACCESS.md
0f7f159ad5b4b5277f80c19f3c47f4e7823ea439ef02e5913e1391f1b34d129f docs/ARCHITECTURE.md
86469d17f9786c3e6f20c66791b47e10e11695d2f1a88c66c9ded157a585c85f docs/CACHE.md
6266163e17d2921468abdae92c1620266af8351321530e2c9b7dca6f3dd89c88 docs/CACHE_DIAGNOSTICS.md
c6003dcbc0b95e2cfa9523b7017186cc8b4826248b588af77111ac5eb15174f9 docs/CHANGELOG_TECHNICAL_ARCHIVE.md
0031861b52c00fc1cffc73df1410c9dee5838d445699326deddc2488ad1193ad docs/CONTAINER.md
0a92a2d4ee88d02e9b460c680305680227c3ef82cf6b57d6f92b1ef5a6433154 docs/CONTAINER.md
9538a52d508d8d7053c1387c42eef60e6d5dc1879c300b6fe951b3374c16884c docs/CUSTOM_LOCATIONS.md
16d8f59908c66e595d6003495cbf5c068c68b10aa9495eb8612c38b2de0e4a7c docs/DOCKER_DISCOVERY.md
9ea8db4d034ecb573976dd323da29f044538525c230df9f43a47be1ac867b24c docs/ERROR_LOGGING.md
adb4c6c43023f09d291eee1027944453eb7667b4c702339346a75aa23c986b5a docs/FILE_LOGS.md
5c46b311f48e143a3e43e61a69c8bc3cfad7748c7dfef87bd76df333b64023bd docs/FILE_LOGS.md
df6ecd4a50796640243676ad4b3a87789f6e5d20cbebea6255e92fbfff1c7131 docs/HTTP3.md
69b7ede915818a52bb68f94983f0fcefe77ff6c58d776c92758e2f7007678eab docs/INSTALL.md
10f378674dced022f063bfc1b15dc9a5cfc0afd03639deb4db5de091c139288c docs/INSTALL.md
3b5c31c805e5aee0dc2100b81246a9b631b5abb996faa598ae94f87ba39305f3 docs/INTEGRATIONS_040.md
4bb56add544f15d54695cf797cd16a51c09024360cdeef803a88988f4a54b3a5 docs/LIVE_LOGS.md
7c35c4cdbe7bbb81d94f3bf6aeb22b8a866af3e7f0d62bbedc299e4d6f264e0d docs/MANAGEMENT.md
@@ -50,7 +50,7 @@ e7516646dfb107d6240370f4e7460b1189f2eb21f398740381d5d102847bbfe6 docs/NOT_FOUND
0c2241828e01b12f749562e2ac206a4519908fff63f46eba207b1cffa932dc5b docs/NPM_MIGRATION.md
712cb1cbf84515384b081fd2929e86e40af7c10f65bc69919b78b7b3e2fd12b5 docs/PHP_FPM.md
572c70fbd2f61fecd80a8ff9fa5de4a94c4f158de5e90da8e920f99b051241fa docs/PROMETHEUS.md
72a1cfc792f4cc35d3463c312a88bf0053689df0bb52d89889bb7ed15e35462b docs/README.md
a5faed54574e23cc5940205883a9731aa1572c2499783ecba138a621fcfb4a42 docs/README.md
6e46a137b9c88dac8cb9e3fdca877430092392de9913d746278dbff8652460c2 docs/REDIRECTS.md
0dfd0d431c5aedc67abac1ce5de040ffa790ec9578fd7d6f91a987c333d73358 docs/REFERENCES.md
be661dbf81af840d4d8aeeb5347c24c7faecdecc73a8cf0f6f09053f1abe04c1 docs/RENEWAL.md
@@ -60,7 +60,7 @@ be661dbf81af840d4d8aeeb5347c24c7faecdecc73a8cf0f6f09053f1abe04c1 docs/RENEWAL.m
620b69e566d27af04f0604f4ca3f88cf95cdfea7fbc6ae9dd92446d2b2b5d9be docs/SECURITY_AUDIT.md
632976125cbd9e96e91d1771331c73d1ac5adaa796476a20f8546971787c2529 docs/SETTINGS.md
3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md
6f1d1ced81d32c2efae10b0743e6fbd45648734439ce1adbc4a62baa07341dd8 docs/TEST_REPORT.md
6a6f48614fcf64b675ef0b7efb49f65878da3f42a6ee110f0d608c70e737be2a docs/TEST_REPORT.md
cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md
8ee6187a5460ca45643b7fd789e27bffd264449e3a92ff70e9359edee011bbbb docs/TRANSFORM.md
a1e9c6d38d21067d1ce7115ca3395ae8ff1f6a4c40fb0ebf00a6940ef79df439 docs/TRANSPORT.md
@@ -97,6 +97,7 @@ dd9b5dcdbd53a04fd32458caaf03b56aa335f0153b1a0e332fdf230fec12680e docs/V0.26.6_I
f9667fe81b31762e64d36126a7e94b609316dfa856de8f8e3b96661dd2a918c6 docs/V0.26.7_IMPLEMENTATION.md
efd5935852d4cfd09758bf67c70e437490cfce2baade46c8733201e506dbcc42 docs/V0.27.0_IMPLEMENTATION.md
9ba6417417a0eb293879b26b019449f143e773d1482434bee88f5eaf1fe34b90 docs/V0.27.1_IMPLEMENTATION.md
6aafee24d7badabe00e23554c8c1ec868f586e09f2d915e9f48422639a7348fb docs/V0.27.2_IMPLEMENTATION.md
bc2ac0117528ed4616c68cf7fe05cb1d9bbd36ecaa8573828cb3b3136923f265 docs/VHOSTS.md
09b60fb7b40db4d0139fa2a9e69a2aa9f28d4e61b8643521cfc7e936d7df8774 docs/VHOST_ACCESS.md
4026a4adeee70f98a96cc7aa8d8be16cda42ffb2e5890d38f1ba638134ab0675 docs/WEB_SERVER.md
@@ -111,14 +112,14 @@ b26833cb4251f9390a44192ecc827ff7f052fd19016a5ccf7498c94503ab4ce3 examples/mock-
5a8f3a8a054abc89c6e78896b9fe2f903212199a24dc3d2d2bcbd0696a31bcde examples/transport.json
d242de9614fbb52933b4accd32f4715ca94107abe4a3b1d7e088362b07a6ba60 examples/vhosts-balanced.json
af2bdb1343a8141069e94ccd2c7f92574ad848437e2f9fdb5c7576a9a18ef4a5 examples/vhosts-private.json
9cab73d7de1f54bc9e9e8b889a6eeaf2c293ab454c93ce904e9f7f5c6b7c588c grafana/README.md
2e23d77477bd3eea895c2afe8c5e836559f435419c066e995dec9910c5bbb942 grafana/rustproxy-overview.json
cc99b14927c696adc17c9d0689041798f0a99cbc372b35a81652d6e38af69311 grafana/README.md
b6401012c55a9cdf7ca7ac5f90b4142c623230903877309e427142de1a81f9d0 grafana/rustproxy-overview.json
fe1fc76bcb639cac0609a8969cd92725cfd77d87f1d8519756ef16825e04914a install-debian13.sh
858b39fa6886b71d20690bf369e76580922364b3cc7f0cf6ba9b040df9cd76e3 install.sh
29cada0936762f26495064d1524a668262ed729b21f20aebddc8b6e5cfea363b langs/README.md
55a50c4071a05e4c24fef7fe9da10c0d9a3d5aaf19bafdb39fbacfbaa0b953e8 langs/en_US.json
41707ded727ff98fc3672f69f7cb16e7950eca8efa248fd328108ca1b262c22e langs/pl_pl.json
95f980b8420d14addf02c4c0139986ba317239426a3c91a1a821df410147c2c4 make_zip.py
917efd1646d06ac34c5729ce5a569482d7512cf285dfc603bca24b3608d36e7c langs/en_US.json
bab5f124ee1e1e823fe310217560c2abaff8a07c9c23a4daf7ab0f60d766eb4b langs/pl_pl.json
300b8cbb49e44d6ce11a4445df01b1a7467b81acfafa9c2d2ba62b5a611501e4 make_zip.py
e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql
20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql
5c62da30fa9db594a4d656c77ae676d3c388072b6a85f91170ec7d911d7cde43 migrations/0004_admin_preferences.sql
@@ -133,15 +134,15 @@ ee50f38c037765fab742e888d4b9fc4d874c31c785c6064d4eca3d2c4d24d944 nagios/check_r
1465396cf1358067e9fe48b3d61f28fd4ac65c577b67c502950ce6b42ecbe3b3 nagios/check_rustproxy_latency
6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues
7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml
edb6ee1445b01cd6f3a74a3a607f39fb107d411b49df028d2e0f4679e093de31 scripts/acme_provider.py
5d4ae015f1c8199c2ed02b34576b74ec6f2b3b7a90c8d7178e5070a850bb6e72 scripts/acme_provider.py
e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/acme_rfc2136.py
a5b4451e45ce975685772aa8409b64a57a20a7548466584c3ea7fb975e64729c scripts/admin_audit.py
20e83f8b6347b819d1d1ea3de5c691c36f0816954a4210b0d3f39557fefe1931 scripts/backup.py
4ab1fc13e7447e752c476bc8bfd15ad4745900bf2a40fc5469174aacbc3aaef6 scripts/check_source.py
6e656850ca0bafae0aeb2fdf3b8f7e9342b027830df8e4ca19190a8b1cfeb7a9 scripts/check_source.py
187ee92c8029cdd59e89b4447cf4d170e856017a2091ceb85ae7c22dd1a79c72 scripts/deploy.py
11321aa4c5744dd2a3322123c00182e9565891e3b2742cda5bf3fc437b3d0c60 scripts/deploy_core.py
6960ff75a235289d270a9d8e7205a4432b51d3bca83e2c599bd20bcc90caac81 scripts/deploy_core.py
f014472588e889488976970dbaa95a8745b6907516aa81a7cca21d0f81eaaad3 scripts/deploy_runtime.py
f818039f8a2e35922596122cde671a5e1af264b03d48938a3ca8261ec3c8e431 scripts/docs_check.py
fc1a74246478cb477b3892812634cb28f8b8ad45604a098327f7a14a60f25c96 scripts/docs_check.py
d391681dc3f6786552ca44929979286a5c76a2b86785635871b59d2eb4d73389 scripts/langs_check.py
2e35b7c78887fd56f83ca9017caa168630963a9f47da71dfc53b1a8f9ad436c2 scripts/memory_contracts.py
2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migrate_npm.py
@@ -151,8 +152,8 @@ d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a scripts/mock_a
2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 scripts/mock_server.py
2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 scripts/os-acme-client/README.md
c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 scripts/os-acme-client/opnsense-rustproxy-cert-deploy.sh
f7474501b9ab7102d9f9b51c6d62c83dac3e6111e982f12e13480bcfd0b1c909 scripts/preflight.py
3019322686f3eccb141a9587b5bff78aabf46fb2ef766d3ddf1de274fea4dc8e scripts/release_smoke.py
65ee67092955aae5216c304453f88066ad9f51dc27e682f36866540eebbf621b scripts/preflight.py
1df736690e3de94f1ddc363ecef9e0e86acee4e98a78c30b82936324f4747484 scripts/release_smoke.py
45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 scripts/routeros-oci-build/.env.routeros.example
3b48cff8506e5a6ae7d3876f47721f5de7d63e2e02264a78cf5b6ddd52d9fbc6 scripts/routeros-oci-build/Dockerfile.routeros
7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e scripts/routeros-oci-build/Dockerfile.routeros.dockerignore
@@ -164,11 +165,11 @@ ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed scripts/rust_s
1c83054336b3508d7ad72cd259f039394682642439877fe69639c0b76be09154 scripts/security_contracts.py
295b6f2625b1875950ea3eecccd142f49be327afdd00c92641286e6de530ec51 scripts/security_dependency_contracts.py
6fefb5c459aacdef3dcabe9c5e6e7718a2145a0190b165c29d7a46216ac53bad scripts/source_module_contracts.py
6c1b64db378fd6ab351882ecf91d7be3db7114b6650a88d402408b34794260cc scripts/swagger_assets.py
df2bffcd729a998c5c7ec5452235cc07e834d8f86017d65ccd0b1754d91d11b0 scripts/swagger_assets.py
538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba scripts/test_compression.py
19bfa4dca422cebe8b41d9374f2e5ad480a34228995eddcf12acfc21e0ebed6f scripts/upgrade.py
45a6a4cca69419da94b2389d73d3f1eb42ccc4aafa5825da14242eb236bdd963 scripts/upgrade.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
ad62f73bb42906d617cc1b479f02d4648c7324cb89fc11d2c6aa6bf13774eec9 src/acl.rs
ea272433e7f92e983509a8ab1aa54242440abef21df526c2f99944ef279e09b9 src/acl/tests.rs
@@ -177,20 +178,20 @@ ea272433e7f92e983509a8ab1aa54242440abef21df526c2f99944ef279e09b9 src/acl/tests.
3f158f46e80b403b658b4e9b1e3710e38c70e47b2d476b8223d9c394ffeeba95 src/acme/model.rs
c5334de6c732d76b2bea7bfc17b3ac305a250bbaad31a5420d6a7d7abb200449 src/acme/providers.rs
b9e57db4bd80ef37564dfc533a96897d8a494671d53acea9fb4bf28d23ba370d src/acme/providers/tests.rs
a5dad04694780f0c6da3c277a110930f3c2ed925fc122d4be770678055931017 src/acme/runtime_env.rs
f8c4bb96814f669be8be40d013a6217c32f66943de0961ad827a184be6879645 src/acme/runtime_env.rs
7acbaf34e7b953b753ab074893e47b5024f0cd756dc4c7a1074067dd936c2e8e src/acme/tests.rs
55ef7ceb44c25c7a8ddcbfd5e0d4b647526f9bdcde901ee08db5314e4d055b39 src/admin/access.rs
d18efa3f797f62b3916265e30ba546725a41e7ccfd9b123965ff9f522e3baa88 src/admin/certificates.rs
1de4c7c116eeab2452ccccbaba3f2b7b63c6eb1c1e146ab870d19c5a5ca16f80 src/admin/documentation.rs
9cac4393318cd34afcfdd02527291aaeb8eb373d38f9b219f71cb963c6d8e883 src/admin/documentation/documents.rs
e8892cca11b3d686d1e5f6ef091a01d7b28cf4289993876a0a4d91bedb751ec4 src/admin/documentation/documents.rs
67b901485d5858d5b5bf78a6260035db32241531d99481754c3533fdf404facb src/admin/documentation/tests.rs
002922c91aa07cbea17cb0ee11127bb6e7e015de108ee032761b34507dcfa06d src/admin/file_log_search.rs
d3210e037481e1f95e840550428e088ffd83ec9a42f1810e4c976298fb555a9a src/admin/file_logs.rs
beb8be241e4f1a8cea7b6c4e02b42f0359fb1fff10ab5ce2c70ccd0e63a554b0 src/admin/logging_api.rs
58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs
dd40e2e8f1cac463532b113f91b4474a97a118fcd96aa27ee9f26bd78821064b src/admin/openapi.json
01bde7e69062ad85449a03c05188c8af4299952921993e9828f9c03439cff104 src/admin/openapi.json
a53309daf118381031122ca70764f7a2f92adc44af3fc91df5f946e6856f76ae src/admin/operations.rs
a01160fd4f6743a9e8c1aa2e03ddf1514ff68a192ac9523c25be09252d2ac7a4 src/admin/preferences.rs
5287fff6b3ad436ac695e9b66feb1b0b5705b744a9f00cec7ea6a3e951e5eebf src/admin/preferences.rs
b4fa393b780b2676b5d72e0f90cd7290292f424b8062d4b5983e722d6ca72c32 src/admin/public.rs
684d5d28e0d2b10fb2baf2a56857b226d6334cc3346e1ca86de173ff914bee73 src/admin/router.rs
333d5f9f4c27941c535ac689e5826264ffa2bb86b00832b597a9bb9f811f01e8 src/admin/session.rs
@@ -226,7 +227,7 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/
0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg
af6e7a2e890df2fb7385a374bcb3fba52fb5a53dbe18adc1fb7865660fcba842 src/admin/web/file-logs.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
7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json
af0fbd94d9b88ce1cce28e39dfd2c35de031adcba412dd4a9a17d9b416c256dd src/admin/web/login.html
@@ -235,7 +236,7 @@ e169d77359785ba604ce1a728420571fdc9b491fc1c0088642399466fbed8c1b src/admin/web/
90d9a0836662135b79fb6f183c4f3eff60f8a64392d6c5795ccf1d8ef6862263 src/admin/web/redirects.js
20670b3167a56e0c79b061ea1b9af74a0d1e4dedb4b57cb8c211463610df28eb src/admin/web/settings.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
e686d68fd6a368110a85dc4c5dc64e8093950d20d63e9aa44790c03dea6fc193 src/admin/web/transform-panel.js
f746b0aa758c1f9fbf5e656ab269f3ffe32675b90c83f0ed5b1e398322095e34 src/admin/web/vhosts/core.js
@@ -345,11 +346,11 @@ a16613114273c54b6d2a16a94fa8748550e91468585469642622bd2c86087baa src/logging/fo
17425ebbca6bbbb0dcaeb65ebc44b1e51488a4092b7b6add556e764c6d486d6a src/logging/tests.rs
3836ed14b6710e183f048ee4d7807a35366c27a27338057fe4a2811129b63302 src/main.rs
c66a9803e43bdd87101825e8c71ab33f890ac4c3428467ba5d43c17fdfa1439e src/main/admin_runtime.rs
0cc35b47d2264816009160e2416a4052d0118fee4c41c35d428dc088ee68a352 src/main/bootstrap.rs
04084e87167a8c47a24d5a4e89e0bde0f30398ea95c38fe34c057a1149ac33e8 src/main/bootstrap.rs
5a98d0e249338e73aa57ae2e352dc16db2f79c2d042c1544ce1c2022da76a31c src/main/proxy_runtime.rs
4dd7c1084a6214c0f2513ebf86fe8ef144470695557772bfe25979edd64491ab src/main/shutdown.rs
0a6578a0f4023b9f8d73043bf942dc5e096ef9afb8c0d2f131c14f65d08ac01d src/main/shutdown/tests.rs
a72eb4ae7ffecd5e1e566fbece270a7956bb3393d3fc18be108de959e65a99c0 src/main/tasks.rs
7e97938fd9209f2335af5d6304abcb4193e80bb33e4ccf480938e8c3ab4644d6 src/main/tasks.rs
49d7e104585c8791ac9a203dddd95802784b68c0ba65d6f90f3d57154b4c7871 src/metrics.rs
201ab702722d6d21585fe8bb49939150d61bfac7ed8dc198b98c2cc6098ffb5f src/metrics/global.rs
49b11df65b4fb0b5b727f9f867436584680b45e3f4fbd53f74bc0cab8b9a88ec src/metrics/vhost.rs
@@ -456,7 +457,7 @@ ff4980f0a091da9ff1722d6b9f6662529cbd7c5e2ac68dcd22a507ce6aaf3011 src/vhost/vali
c1fef6dfb79275479c15097b211cd5f063ad7e0852a0767b0145cf4f32f751da src/web_server/config/model.rs
f1d3e427c53f8b5be5768d85e69ead746d83647fcd3b355acc98e3f9d5e217e0 src/web_server/config/validation.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
8186363315bd998919c6c783e6f7501528f257a5c0da77c5d60f423e8670db5e src/web_server/filesystem.rs
0aafd7f37c4fe784b047646e4ce5ddcb5fe821231a41ed0a9f519529385288a8 src/web_server/htaccess.rs
@@ -466,14 +467,14 @@ fbc035f68621ac6f3c31e363712c4e06f9bc4382d94511bf93f0fdba39b627b2 src/web_server
80f946570c14c8915fd21bfdb1a84dfb6a14c633ab6060acb828fab4c7c21daa src/web_server/mod.rs
67ab8bef567452f123c4b9b46a942b39c93f1f765c128edca7ee808ff1c5e304 src/web_server/request.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
2268a6aeed2f565f9791af33e1cd21c56c228c8873b031725b39a6c57184f2d1 src/web_server/routing.rs
400e1c8aa2fe857d96036a64499fbc3de44c1e3a53811c5b25101fb843359792 src/web_server/security.rs
3e0b4e92600a7d5abcec92efed532b3a01e0781cb4f2681daac6d6ae13b0a6e9 src/web_server/serve.rs
cf86c4692080723bbdf8f4105e1abfae33d7fcf020d2ad9e13ebae3943bd0cbe src/web_server/tests.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
f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json
1725c10331745dc04a07675784d385260da7b14567b8dba3cbae5e891eb5dadf tests/http2.rs
+2 -2
View File
@@ -18,7 +18,7 @@ if [[ $# -gt 0 ]]; then shift; fi
case "$mode" in
help|-h|--help)
cat <<'HELP'
RustProxy 0.27.1
RustProxy 0.27.2
./dev.sh Build and start proxy + local panel
./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
@@ -139,7 +139,7 @@ if [[ "$mode" == "test" ]]; then
printf 'Checking all Rust targets before tests...\n' >&2
cargo check --locked --all-targets
fi
printf 'Building RustProxy 0.27.1...\n' >&2
printf 'Building RustProxy 0.27.2...\n' >&2
cargo build --locked
BIN="$CARGO_TARGET_DIR/debug/rust-proxy"
case "$mode" in
+1 -1
View File
@@ -1,6 +1,6 @@
package main
const version = "0.27.1"
const version = "0.27.2"
type config struct {
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
+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:
```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
@@ -22,7 +22,7 @@ docker volume create rustproxy-data
docker run --rm -d --name rustproxy \
-p 8080:8080 \
-v rustproxy-data:/var/lib/rustproxy \
rustproxy:0.27.1
rustproxy:0.27.2
docker exec rustproxy rust-proxy admin setup-token
```
+1 -1
View File
@@ -1,6 +1,6 @@
# 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
+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
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.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.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.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.
@@ -69,7 +70,7 @@ This is the canonical index for the current project documentation. The authentic
## 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.
+8 -7
View File
@@ -1,18 +1,18 @@
# Verification report - RustProxy 0.27.1
# Verification report - RustProxy 0.27.2
## 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
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
@@ -20,6 +20,7 @@ Rust/Cargo is not installed in this workspace. Rust-independent checks are run h
| Check | Status |
|---|---|
| managed ACME relocated-venv regression (`release_smoke.py`) | PASS |
| changed administration JavaScript syntax (`node --check`) | PASS |
| language/OpenAPI JSON parse | 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 |
| 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.
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
will simply show no series.
+2 -2
View File
@@ -4529,9 +4529,9 @@
},
"timepicker": {},
"timezone": "browser",
"title": "RustProxy 0.27.1 - Operations",
"title": "RustProxy 0.27.2 - Operations",
"uid": "rustproxy-0170",
"version": 5,
"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",
"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.",
"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.",
"certificateSafety": "Certificates secure TLS connections, not routing. Assign a certificate to a vhost. Changes affect new handshakes.",
"wsActive": "WebSocket tunnels",
+1 -1
View File
@@ -183,7 +183,7 @@
"saveCertificate": "Zastosuj certyfikat",
"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.",
"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.",
"certificateSafety": "Certyfikaty samopodpisane służą do testów. Certyfikat nie tworzy trasy. Zmiana dotyczy nowych połączeń TLS.",
"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:
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()
paths = files()
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")
def certbot_command(value: dict, work: Path, credential_file: Path, executable: str) -> list[str]:
args = [executable, "certonly", "--non-interactive", "--agree-tos", "--no-eff-email",
def certbot_prefix(executable: str | None = None) -> list[str]:
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"]],
"--cert-name", "rustproxy", "--keep-until-expiring", "--key-type", "ecdsa",
"--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:
value = validate(raw)
executable = executable or shutil.which("certbot")
if not executable:
raise AcmeError("acme.certbot_not_installed")
command_prefix = certbot_prefix(executable)
certificate_directory = certificate_directory.absolute()
private_directory(certificate_directory)
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)
write_new(temporary, json.dumps(profile).encode())
os.replace(temporary, work / "profile.json")
args = certbot_command(value, work, credential_file, executable)
args = certbot_command(value, work, credential_file, command_prefix)
try:
result = subprocess.run(args, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
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.
"""
value = renewal_request(raw)
executable = executable or shutil.which("certbot")
if not executable:
raise AcmeError("acme.certbot_not_installed")
command_prefix = certbot_prefix(executable)
certificate_directory = certificate_directory.absolute()
private_directory(certificate_directory)
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)
# Check dependencies/installed PEM before invoking a CA operation.
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",
"--config-dir", str(work / "config"), "--work-dir", str(work / "work"), "--logs-dir", str(work / "logs")]
if value["dry_run"]:
+15 -11
View File
@@ -24,7 +24,7 @@ def read_docker_agent_source() -> str:
preflight(root)
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"
h3dep = manifest["dependencies"]["h3"]
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"
if lock_path.exists():
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"
for release_doc, marker in {
"README.md": "# RustProxy 0.27.1",
"CHANGELOG.md": "## 0.27.1",
"ROADMAP.md": "## 0.27.1",
"AGENTS.md": "Current source version: 0.27.1.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.27.1",
"README.md": "# RustProxy 0.27.2",
"CHANGELOG.md": "## 0.27.2",
"ROADMAP.md": "## 0.27.2",
"AGENTS.md": "Current source version: 0.27.2.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.27.2",
}.items():
assert marker in (root / release_doc).read_text(), f"stale release identity in {release_doc}"
assert tomllib.loads((root / "rust-toolchain.toml").read_text())["toolchain"]["channel"] == "stable"
@@ -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 '"backend_header"' in openapi and '"backend_timing_header"' 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
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
@@ -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")
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
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")
logging_source = rust_source(root, "src/logging.rs")
secret_store_source = rust_source(root, "src/secret_store.rs")
backup_source = rust_source(root, "src/backup.rs")
config_source = rust_source(root, "src/config.rs")
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 [
"AES_256_GCM", "master.key", "O_NOFOLLOW", "migrate_acme_credentials",
"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 capture state' 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")
metrics_source = rust_source(root, "src/metrics.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 'logging_section_persists_error_log_settings_atomically' in admin_0241 and 'error_log_is_not_a_standalone_configuration_section' in admin_0241
assert 'location.pathname === "/logging"' in settings_js_0241
assert '"RuntimeInformation"' in openapi and '"version": "0.27.1"' in openapi
assert '"RuntimeInformation"' in openapi and '"version": "0.27.2"' in openapi
# 0.24.1 compiler and hostname-cardinality regressions.
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"]
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.")
+1 -1
View File
@@ -97,7 +97,7 @@ def digest(path: Path) -> str:
def fetch_https(url: str, max_bytes: int) -> bytes:
if urllib.parse.urlparse(url).scheme != '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:
if urllib.parse.urlparse(response.geturl()).scheme != 'https':
raise ValueError('GeoIP download redirected away from HTTPS.')
+6 -5
View File
@@ -1,5 +1,5 @@
#!/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
import re
from rust_source import rust_source
@@ -14,6 +14,7 @@ required = [
DOCS / "README.md",
DOCS / "WEB_SERVER.md",
DOCS / "TODO.md",
DOCS / "V0.27.2_IMPLEMENTATION.md",
DOCS / "V0.27.1_IMPLEMENTATION.md",
DOCS / "V0.27.0_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")
roadmap = (ROOT / "ROADMAP.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 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 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 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 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
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}"
+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)}")
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():
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")
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
+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
import hashlib
import importlib.util
import json
import os
import re
@@ -123,7 +124,7 @@ def upgrade_smoke(tmp: Path) -> None:
assert backup.is_dir()
assert (backup / "src/obsolete.rs").is_file()
assert not (target / "src/obsolete.rs").exists()
assert 'version = "0.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 / ".env").read_text(encoding="utf-8") == "RUST_PROXY_DEMO=0\n"
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/server.go").is_file()
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 not legacy.exists()
assert not (backup / "legacy-data").exists()
@@ -604,7 +605,7 @@ def source_contract_smoke() -> None:
assert marker in admin, marker
anonymous_block = admin.split("fn anonymous_allowed", 1)[1].split("const CERTIFICATE_API_KEY_HEADER", 1)[0]
assert '"/api/status"' not in anonymous_block
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.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
for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="error-log-form"']:
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
openapi = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8")
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 '"software_update"' in openapi and '"update_available"' in openapi
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
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:
subprocess.run(["python3", str(ROOT / "scripts/rust_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)
sql_and_backup_smoke(tmp / "sql")
upgrade_smoke(tmp / "upgrade")
managed_acme_relocation_smoke(tmp / "acme-relocation")
reproducible_archive_smoke(tmp / "archives")
deployment_gate_smoke(tmp)
npm_migration_smoke(tmp)
@@ -1252,7 +1300,7 @@ def main() -> None:
source_contract_smoke()
container_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__":
+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."""
started = time.monotonic()
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:
length = response.headers.get("Content-Length")
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:
if (target / name).is_symlink():
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)
installed: 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()
if remove_legacy and (legacy.is_symlink() or not legacy.is_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:
legacy.unlink()
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)
except (OSError, ValueError) as error:
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(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")',
"builtin_error_response(StatusCode::FORBIDDEN, &method)",
"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",
"try_files_rewrites_cache_rules_headers_and_downloads_work",
"precompressed_sidecars_keep_mime_validators_and_range_semantics",
+16 -13
View File
@@ -60,6 +60,7 @@ impl Manager {
struct RuntimeLaunch {
python: PathBuf,
prepend_path: Option<PathBuf>,
managed_certbot_python: 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")
}
#[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(
mut command: Command,
timeout: Duration,
@@ -131,8 +123,7 @@ async fn managed_environment_valid(
shutdown: &tokio_util::sync::CancellationToken,
) -> bool {
let python = venv_python(directory);
let certbot = venv_certbot(directory);
if !python.is_file() || !certbot.is_file() {
if !python.is_file() {
return false;
}
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 {
return false;
}
let mut version = Command::new(&certbot);
version.arg("--version");
// A venv is created under an atomic staging directory and then renamed
// 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
}
@@ -307,6 +303,7 @@ async fn ensure_runtime(state: &App) -> Result<RuntimeLaunch, String> {
return Ok(RuntimeLaunch {
python: venv_python(&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 {
python: venv_python(&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 {
python,
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())
.stderr(Stdio::null())
.kill_on_drop(true);
command.env_remove("RUSTPROXY_CERTBOT_PYTHON");
if let Some(prepend) = launch.prepend_path.as_deref() {
command.env("PATH", merged_path(prepend)?);
}
if let Some(python) = launch.managed_certbot_python.as_deref() {
command.env("RUSTPROXY_CERTBOT_PYTHON", python);
}
#[cfg(unix)]
{
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"
)),
),
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.26.7_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.6_IMPLEMENTATION.md", "technical"),
+1 -1
View File
@@ -2,7 +2,7 @@
"openapi": "3.0.3",
"info": {
"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."
},
"servers": [
+2 -2
View File
@@ -2823,7 +2823,7 @@
</section>
</div>
</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>
</div>
</main>
@@ -3567,7 +3567,7 @@ max_execution_time = 30</code></pre>
<article class="card about-card">
<div class="about-product">
<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>
<dl class="about-meta">
<div>
+1 -1
View File
@@ -41,7 +41,7 @@ use zeroize::Zeroizing;
#[command(
name = "rust-proxy",
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 {
#[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();
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()?; }
let mut filters = LiveLogFilters::default();
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(),
);
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());
if let Some(value) = headers
.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>"
));
}
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()
}
+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();
assert!(body.contains("file.txt"));
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]