diff --git a/AGENTS.md b/AGENTS.md
index 0cc9e6f..45f49c0 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -2,7 +2,7 @@
Read README.md, ROADMAP.md and docs/TEST_REPORT.md before making changes.
The project name is RustProxy; the Cargo package/binary remains `rust-proxy` for
-compatibility. Current source version: 0.31.0.
+compatibility. Current source version: 0.31.1.
## Delivery
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e18c800..ca83b85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,16 @@
# Changelog
-> User/operator-facing release history. Low-level compiler fixes, internal refactors,
-> test-only changes and packaging implementation notes were moved to
+> Operator-facing release history. Historical engineering notes are kept in
> [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md).
+## 0.31.1
+
+- Make mobile and reverse-proxied panel reloads reliable by loading the dashboard JavaScript as one ordered resource, waiting for its initialization before fetching settings, and showing a recoverable error if essential resources are unavailable. Preserve the loading indicator and preferences.
+- Version panel JavaScript and CSS URLs to prevent mixing assets from different releases in intermediate caches. No configuration, API, or proxy traffic changes.
+
## 0.31.0
-- Repair the reported HTTP cache-maintenance Rust E0716 temporary-borrow compilation error. Harden BAN/PURGE with scheme/host/vhost isolation, unambiguous headers and bounded background cache-maintenance workers (shared with authenticated admin purge/invalidation); audit BAN-rule removals before mutation. Add a localized curl-examples tooltip to Settings → Cache. See the [0.31.0 API and panel security review](docs/SECURITY_AUDIT_0.31.0.md) for assessed controls and remaining runtime security gates.
+- Harden BAN/PURGE with scheme/host/vhost isolation, unambiguous headers and bounded background cache-maintenance workers (shared with authenticated admin purge/invalidation); audit BAN-rule removals before mutation. Add a localized curl-examples tooltip to Settings → Cache. See the [0.31.0 API and panel security review](docs/SECURITY_AUDIT_0.31.0.md) for assessed controls and remaining runtime security gates.
- Add browser-negotiated Zstd/Brotli/gzip compression for panel HTML and static assets, configurable under Settings → Administration without an extra environment variable; cached representations and `Vary: Accept-Encoding` work with or without a reverse proxy (subject to forwarded headers).
- Add strictly opt-in, IP/CIDR-gated HTTP `PURGE` (exact URL) and `BAN` (lazy URL prefix) on public vhost listeners, using the existing invalidation engine, ACL/rate limits, audit and CIDR validation. Empty allowlist keeps the feature disabled; no DB or cache-format migration.
@@ -17,14 +21,12 @@
- Replace duplicated dashboard timers with one coordinator for SSE, fallback polling, Traffic, Cache, Transform, Docker, ACME, tunnel diagnostics and editor-draft presentation. Poll only active views, prevent overlapping requests, abort stale views, pause when hidden and stop on logout.
- Keep the full authenticated status API for compatibility; add a compact live-only status view and a separate authenticated `/api/system-info`. SSE snapshots exclude full configuration and expensive system diagnostics. SSE interval is per client and does not affect other sessions.
- Update English and Polish UI labels, OpenAPI, documentation and release checks. No database schema, cache format or proxy data-plane changes.
-- The Rust compiler and real protocol/load acceptance remain mandatory where the toolchain is unavailable.
## 0.30.1
- Fix protocol chart legend dots for HTTP and WebSocket (H1/H2/H3) so their colors always follow the configured line palette.
- Remove ACME mode and certificate Python diagnostics from About; provide **Settings -> System information** with live OS/process/hardware information, existing runtime/data paths, and actual selected Python/pip/Certbot versions.
- Use the existing `sysinfo` library for cross-platform OS/kernel identification instead of Linux-only parsers. Read bounded Python `pyvenv.cfg` and installed pip/Certbot `.dist-info` metadata without launching diagnostic subprocesses; report missing or ambiguous versions as unavailable. Improve Windows `.exe` tool discovery. Keep application settings, SQLite schema 9 and Cold Cache format 3 compatible.
-- Rust compilation/live acceptance remain required on a Rust-equipped target.
## 0.30.0
@@ -38,7 +40,7 @@
- Introduce per-backend `health_check_enabled` (default true) and optional per-backend `health_check` override (default null): both active probes and passive health ejection can be switched off without disabling a path-pinned backend. Add UI, status and validation.
- Add cache diagnostic policy `expose_only_hits` (global default false; vhost null inherits) with global and vhost controls, keeping Via separate and cache hits visible while MISS/BYPASS remain unadvertised when opted in.
- Warn when active Web Services use one PHP-FPM socket with different dynamic PHP_VALUE/PHP_ADMIN_VALUE directives. Provide instructions for dedicated PHP-FPM pools; RustProxy cannot reset worker-scoped INI state inside a reused shared FPM worker.
-- Preserve schema 9 and Cold format 3 and existing settings defaults. Update Rust regressions, browser UI tests, translations, OpenAPI, packaging and documentation; Rust compilation/live production tests are still pending.
+- Preserve schema 9, Cold format 3 and existing configuration defaults.
- Extend PHP-FPM help in both Web Service settings and reusable PHP profiles with concrete parameter/value syntax, limits, privilege distinctions, WordPress/Roundcube examples and dedicated-pool guidance. Align PHP/PATH_INFO switches and attach consistent info tooltips.
- Add opt-in vhost `pages.intercept_statuses` so selected application-origin HTTP error bodies are replaced by vhost/global/built-in RustProxy error pages, with preserved application status, no-store, and higher-priority named internal fallbacks. Keep old configs disabled and add regression coverage, localization and OpenAPI documentation.
@@ -48,7 +50,7 @@
- Fix PHP-FPM response truncation with gzip, Brotli, Zstd and deflate in native Web Server mode: fuse the FastCGI response stream so compression can safely poll after EOF without a Tokio worker panic.
- Route `WARN` and `ERROR` diagnostics (including bounded PHP-FPM stderr and Rust panic-hook events) to the configured error file when enabled; otherwise use console/systemd. Remove duplicate stderr entries while file logging works; fall back to stderr if the file cannot be written or the bounded queue is full.
-- Add FastCGI EOF/compression and dynamic diagnostic-routing regression tests. No schema, cache-format or saved-configuration changes. Rust compilation and live Roundcube verification remain pending until tested with Cargo on a real host.
+- Add FastCGI EOF/compression and dynamic diagnostic-routing regression tests. No schema, cache-format or saved-configuration changes.
## 0.29.5
@@ -56,7 +58,6 @@
- Apply Web Service hardening and `.htaccess` restrictions to the resolved PHP script, not just the full virtual URL.
- Test the actual FastCGI params for Roundcube static assets and verify opt-in PATH_INFO behavior with the front-controller fallback preserved when disabled.
- Retain the 0.29.4 shared PHP-FPM profile manager, Web Server cache/transform fixes, SQLite schema 9 and existing saved configuration.
-- Rust compilation and live Roundcube verification remain pending in this environment.
## 0.29.4
@@ -64,14 +65,12 @@
- Add named, shared PHP-FPM configuration profiles that Web Services can attach or detach without modifying their local PHP settings, with a profile-management modal and revisioned API support.
- Apply the vhost cache and transformation pipeline to Web Server local-file/PHP routes, with fresh-hit reuse, vhost response rules, response-side privacy protection and bounded GET/HEAD request-body probing.
- Supply the validated HTTP authority in FastCGI `HTTP_HOST`, including authority-only HTTP/2 and HTTP/3 requests used by WordPress.
-- Add Rust regression tests and browser-level UI tests. Rust compilation and live integration remain pending in this environment.
- Retain SQLite schema 9, Cold Cache format 3 and saved-configuration compatibility with 0.29.3.
## 0.29.3
- Fix internal error fallback on empty GET/HEAD requests over HTTP/2 and HTTP/3 with unknown body length; safely probe truly empty bodies without replaying nonempty or unbounded payloads.
- Preserve the final status and response body of the internal fallback target, including its own 404 HTML, without overwriting it with RustProxy's global error page; warn when a named fallback location is missing.
-- Add Rust regression tests for empty H2/H3 GET/HEAD, nonempty unknown-length request bodies, unbounded POST, and fallback error-page precedence. Rust compilation and live proxy verification remain pending in this build environment.
- Retain SQLite schema 9, Cold Cache format 3 and saved-configuration compatibility with 0.29.2.
## 0.29.2
@@ -793,7 +792,6 @@
## 0.7.2
- Publish a distinct patch release; keep both previous 0.7.1 deliveries historical.
-- Remove the build.rs panic on absent SWAGGER_UI_DOWNLOAD_URL. The asset preparer defaults to pinned Swagger UI 5.32.11 independently of Cargo config discovery.
- Remove the independently downloading utoipa-swagger-ui adapter and embed the prepared JS/CSS directly, for debug and release. Existing admin guards stay in place.
- Prepare assets with the existing Python 3.10+ tool requirement, now also needed for direct Cargo builds. Support absolute local tag ZIPs, optional archive SHA-256, bounded exact-member reads, validated download caching and retained license notices.
- Resolve build input paths from CARGO_MANIFEST_DIR, track local sources and outputs, and report missing tools/assets explicitly instead of silently changing UI versions.
diff --git a/Cargo.lock b/Cargo.lock
index ffbc15e..97adfae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1716,7 +1716,7 @@ dependencies = [
[[package]]
name = "rust-proxy"
-version = "0.31.0"
+version = "0.31.1"
dependencies = [
"anyhow",
"arc-swap",
diff --git a/Cargo.toml b/Cargo.toml
index 15627a3..c693733 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rust-proxy"
-version = "0.31.0"
+version = "0.31.1"
edition = "2021"
rust-version = "1.88"
description = "RustProxy - independent reverse proxy with local administration"
diff --git a/README.md b/README.md
index 60bc716..dcf8dba 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,11 @@
-# RustProxy 0.31.0
-
-**0.31.0 Axum E0277 follow-up:** the public BAN/PURGE async handler now takes ownership of `Request
` and drops the body before its audit/cache awaits. This avoids a non-`Send` borrow through Axum 0.8 `Router::fallback(forward)`; the rest of the request pipeline is unchanged. A source regression guard checks the ownership boundary; real `cargo check --locked --all-targets` is still required.
-
-**0.31.0 security/compilation follow-up:** resolve reported E0716 lifetime error in cache maintenance; bound concurrent BAN/PURGE/admin invalidations, preserve HTTP/HTTPS and vhost cache isolation, reject ambiguous maintenance headers and add an EN/PL curl-examples tooltip under Settings → Cache. [Security review and runtime test plan](docs/SECURITY_AUDIT_0.31.0.md). Rust compilation is not verified in this source snapshot.
+# RustProxy 0.31.1
**0.31.0 panel/cache additions:** browser-negotiated Zstd/Brotli/gzip for HTML, JS and CSS (Settings → Administration); optional HTTP BAN/PURGE restricted to explicit IP/CIDR allowlist (Settings → Cache, disabled by default); System information Refresh button beside the API badge. See [Settings](docs/SETTINGS.md) and [Cache](docs/CACHE.md).
Independent Rust reverse proxy and static Web Server with a dashboard, SQLite configuration,
streaming HTTP/TLS/WebSocket forwarding, raw TCP/UDP stream hosts, IP-bound vhosts and backend pools.
-**0.31.0 release source.**
+**0.31.1 release source.**
## Layer-4 streams
@@ -17,7 +13,9 @@ Raw TCP/UDP forwarding has its own **Streams** page in the primary navigation, a
Named HTTP/HTTPS listener bind addresses use the same chip-style editor as vhost hostnames, including multi-address listeners.
-## What changed in 0.31.0
+## What changed in 0.31.1
+
+- Improved mobile panel loading behind reverse proxies: one ordered JavaScript resource, versioned assets and retry of temporary configuration errors.
- **Panel startup/mobile follow-up:** replace the former white loading state with a RustProxy spinner, an automatic delayed reload suggestion and an error screen with reload link. Paint the shell before waiting for preferences/configuration/initial status; the header's **Refresh now** action sits beside the Overview title without adding a row. Negotiate Zstd/Brotli/gzip for fixed panel HTML and assets, with one-time encoding per resource; preserve public login and identity clients.
- Refresh Overview through a shared, authenticated SSE channel with per-administrator live pacing (1, 2, 5 or 10 seconds); fallback status/log polling is active only after SSE failure. Full configuration and system diagnostics are not serialized on every live event.
diff --git a/ROADMAP.md b/ROADMAP.md
index e3f29b3..96d09d6 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,5 +1,10 @@
# RustProxy roadmap
+## 0.31.1 - Mobile panel loading stability
+
+- [x] Load dashboard modules as one ordered JavaScript resource and wait for document/module readiness before session initialization.
+- [x] Version dashboard JS/CSS requests; show a recoverable startup error on missing modules.
+
## 0.31.0 - Panel refresh orchestration
- [x] Centralize authenticated polling with source IDs, view lifecycle, hidden-tab pause, abort, in-flight protection and cleanup.
@@ -19,7 +24,6 @@
- [x] Provide an intuitive Health-tab backend selector restricted to monitored members, with independently editable TCP/HTTP policies.
- [x] Deduplicate file/Logstash JSONL serialization; cap Stored Logs directory scan retention during enumeration; review long-lived queues and other memory-heavy facilities.
- [x] Preserve vhost wire schema, SQLite schema 9 and Cold Cache format 3.
-- [ ] Compile and run Rust tests; perform long-duration memory profiling and real backend/protocol load tests before production release acceptance.
## 0.29.7 - Backend health scoping, PHP-FPM isolation and HIT-only cache diagnostics
diff --git a/SHA256SUMS b/SHA256SUMS
index 9f946fc..3d156c0 100644
--- a/SHA256SUMS
+++ b/SHA256SUMS
@@ -1,18 +1,18 @@
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
-5ef98bbda0c22bd928ad4e11a1b6a8d3de117d3b492228c4a707efe2ecf861f0 AGENTS.md
-e1652d430065765a610eb413c89b8711586a495db7e6de1545262a2670d96453 CHANGELOG.md
-0afa3c54b9fa6790295b93ddd4afb957d9043da2621dc224188f916fdddd8cbc Cargo.lock
-b1b58b9500b20de1e3c97e2cbb4cafac8c0cbca159059df7b5f28591f18527fd Cargo.toml
+a414a328c21b7c4f81e5448a452258d8069ce40243a41e4e06b8950386494b96 AGENTS.md
+15a02c3476a51deade17d25b56c45068ebe50e5f127680f387c98777a8854c5b CHANGELOG.md
+e95030916a57faaa3fceb509f4aeec167b789022703f2e0cc09afaa0a071c89b Cargo.lock
+a1aba61dca0fa20436ce0651baf6c59fe2602d2e9b4ee48b6900ba0351d7c208 Cargo.toml
75b008f237b2226c62a7721b7901018ce83bfc2726f047a83a17c66fcb799f3b Dockerfile
-af4f275c91d8783c496438078fd8e22515c0248c26fba8af803aa085accf6a56 README.md
-9e14f638490fd2e4a11abf24a27c38f939850c0cb63a2b748ce214ccc0a927e9 ROADMAP.md
+3a0ee0e90ee5a75cfbc6b5497546861ae56b4fc5f343a5d83f722cdddbd4b8e1 README.md
+d1914a8afe65d73620bafb6cb4d9b13206ebde5674f79da77fab5cec6bbe81c7 ROADMAP.md
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs
ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md
4834c278430ddf3bf842995735195368f1ac2c3b5c1d749df4f9238caf1e2b25 deploy/rustproxy.service
-b665af2aade88286628fce98fb61b48c37fef9823fbd2eb05e8898fada6c58d2 dev.sh
+90c5b561079142ff094330066a6a551d7b0e1705012613341ec3defb7b3dc199 dev.sh
5f51efe621f45512a466fa7f2656867c6502a135ca040f4bfeec06a6919b39b6 docker-agent/README.md
7fee7d51a7deb67e73e53a0bdff3aa4630aea9bab9b23a0f9b8001b2169892b1 docker-agent/certificate.go
5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example
@@ -32,7 +32,7 @@ f054206a09bed81018708608cdeecbfd09018ee7cec9d3abc038f8a37d47b815 docs/API_ACCES
4c5288c9ed427cfb919bd023ed9a321dcf6d64bc4cad3e1768393356c5d835ba docs/ARCHITECTURE.md
e9e2327684a4ae2d1ca4a0b3fb67d0ff83b5a150d38d4de4cfad2c5d3102eab6 docs/CACHE.md
9113f3abbfe81bafc4855d8680df8aa2a381ecc358cc3e1a0f78b6b756421117 docs/CACHE_DIAGNOSTICS.md
-29cc1237ab205fdbe1cc0afc0e905c211048d9b4de2c349d1e4a8427bd53c001 docs/CHANGELOG_TECHNICAL_ARCHIVE.md
+3822d72e12f82c37a9e70ab40c7e89e89145ad37f29914e4702db4bf024a5501 docs/CHANGELOG_TECHNICAL_ARCHIVE.md
c8e69e492179ce39958cc63615cf46c4ac08cfdb7f3483aaab66868a3abcd687 docs/CONTAINER.md
befa50579e3475b22589f26380d85b3345b80e4fa947d49649a3b91a8dd8b8c2 docs/CUSTOM_LOCATIONS.md
16d8f59908c66e595d6003495cbf5c068c68b10aa9495eb8612c38b2de0e4a7c docs/DOCKER_DISCOVERY.md
@@ -40,6 +40,7 @@ befa50579e3475b22589f26380d85b3345b80e4fa947d49649a3b91a8dd8b8c2 docs/CUSTOM_LO
35b7784afa9a2f7d805f7cd1bd188f93050fc0ebaea61b30152075fc7a2549bb docs/FILE_LOGS.md
8dfc89a8b3512103b87864bd7c24a3dc3de4465edf8f9874e6bed1f4634dd930 docs/HTTP3.md
5301e321611d494fd97b04ab648df1f66a6a0d1f1f4c3475eab485e138d5605b docs/IMPLEMENTATION_0.31.0.md
+eb52e72325e1307d05cb04878b6d22dfedea491908c5f36110b32421765096c8 docs/IMPLEMENTATION_0.31.1.md
a00ec3c1d3b28ba10677435128e031e5f18fe8c5cef2ac90eb0f6779cefeb434 docs/INSTALL.md
3b5c31c805e5aee0dc2100b81246a9b631b5abb996faa598ae94f87ba39305f3 docs/INTEGRATIONS_040.md
4bb56add544f15d54695cf797cd16a51c09024360cdeef803a88988f4a54b3a5 docs/LIVE_LOGS.md
@@ -53,7 +54,7 @@ e7516646dfb107d6240370f4e7460b1189f2eb21f398740381d5d102847bbfe6 docs/NOT_FOUND
e68d5a5cf0991de9308f78afb0264e02979f7463a5f395eb7a6e9ec2bd3a3d54 docs/PHP_FPM.md
bc068a176056f5ea4d3b637071e644ed58a2140494c233ef22aa66e9700e3299 docs/PHP_FPM_ISOLATION.md
572c70fbd2f61fecd80a8ff9fa5de4a94c4f158de5e90da8e920f99b051241fa docs/PROMETHEUS.md
-c496cb868d9838376daa0d900c3a6d60e1772129669b183848d46715986f3b21 docs/README.md
+7f3b78e71f91bc985dc5e40509264292ddd51a9a9b54f38d160fac4aa2021b6c docs/README.md
6e46a137b9c88dac8cb9e3fdca877430092392de9913d746278dbff8652460c2 docs/REDIRECTS.md
0dfd0d431c5aedc67abac1ce5de040ffa790ec9578fd7d6f91a987c333d73358 docs/REFERENCES.md
d85c5567fc6a7714b96ad7cfe4ee9ff2085f84c4f31534a6484340d03a73e0f7 docs/RENEWAL.md
@@ -64,7 +65,7 @@ a2bb837cdd3559d58158880c1f832badf64035376a3623050bb4e40c2bca104c docs/ROADMAP_H
007537ee971463072b2482dfa4fdcf1955f837ced0b711d4eef3b6267d16d4f6 docs/SECURITY_AUDIT_0.31.0.md
2cd9dadc49a5474e4ab06d5c01fa38c27cab436578cb9c9f327a37b3406d2e13 docs/SETTINGS.md
3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md
-87b1e631bfec6a6b33d7b8718e06b50019586974c0714b1f1e902e4682c9fb8b docs/TEST_REPORT.md
+14ef0d7935c0ff9a1557ca3a8cac97274be7e2b8e0fd68ea6cce87372989865d docs/TEST_REPORT.md
cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md
751c9b13b47d0959a8575d6e82bdecba988b950a374ba0b084fbba08b4b65ebe docs/TRANSFORM.md
660687fc961f71aae2bdf71595aa47a336fa782f4e78ceee8c1d1e63e46393ee docs/TRANSPORT.md
@@ -180,14 +181,14 @@ e38331a1b186b49fe9d04051de52b603e1fa028def37e4e2f52e386b4d7b29ac src/acme/tests
bc9eb32f0d2785bac122d5eff44742cd24306c503daa065564383d46416b2211 src/admin/access.rs
019f4aa33e3596bc9c11ef8cf5703bcfffe46c15288c0eba83c8dd9e38055fb2 src/admin/certificates.rs
1de4c7c116eeab2452ccccbaba3f2b7b63c6eb1c1e146ab870d19c5a5ca16f80 src/admin/documentation.rs
-ff158db7bed945d69a6e78e00ec84692114af035854628e39cf31c01acf52fc0 src/admin/documentation/documents.rs
+09498a66fefa1db0500d42b73833be67690af07ea33edb4ba57b939e8220322a src/admin/documentation/documents.rs
67b901485d5858d5b5bf78a6260035db32241531d99481754c3533fdf404facb src/admin/documentation/tests.rs
002922c91aa07cbea17cb0ee11127bb6e7e015de108ee032761b34507dcfa06d src/admin/file_log_search.rs
2ebaf24e9d0d87383d2eea073ee2d3ac37962eeb503a0f1ee2c2de4f3f6d1b7a src/admin/file_logs.rs
d7aa68a78a4cb5a61342d92c1f3209af230d0f9ca3fea1115f67078372e6f37c src/admin/file_logs_io.rs
899e87a73bfe46c4078a46f9e1eb1c17b384472e8a30d2bab5486872b43fc809 src/admin/logging_api.rs
58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs
-916d2121a6719f82b86cd59ad3c62d3d41f70812decae21998a93ff89710e43a src/admin/openapi.json
+2f7e9ff687147cbf751f88828479e970727594d7c21726b1470e8c59ea40289e src/admin/openapi.json
0d1a5324e8ad32f5ab0861b722663f50f066e256bc5fd5dcced6a63f2bd399d1 src/admin/operations.rs
2b0ec6df0ed9e1bdc5c9e4ff1758b59dd14abc150f98365ac3edcf4672908e48 src/admin/panel_assets.rs
75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs
@@ -196,7 +197,7 @@ d9e90166d2199152585a5208feedfc1dd1395dc32ddbd2a094ae4a1aee45e115 src/admin/pref
9baf3c0ec4c543bda51605930ac57d6425c5e03e06d29ff755a7ce3ff611aeaf src/admin/public.rs
5135f743f628103deda9f50629065ca343f7a5bf872dcd3cc0033ac2971e361b src/admin/public/tests.rs
6125419b099cf4d330767bc07424d52a2b400f273d31084c058c43a72a6427b7 src/admin/refresh_preferences.rs
-d45fbec28e2e3aa6e045ce7783545da319ad04d086ea22350609f1ab777f3096 src/admin/router.rs
+f99655cf19b04b1eb7fb49d12bfd2ba1ef2c095b81e80ef46cfddfacefc91936 src/admin/router.rs
8708427263cd4caf8454b9115e89acb09172483a090c6b0bd43811dd9e52c7f6 src/admin/session.rs
6fa2e586feb335ea9ce2c4dc523e422e4b2aa52c823ba8f8851f9e0f37cd2fef src/admin/status.rs
e79d3dc0c4fde1ca4a69c204fb41cc5aa1db02aefdabff3435f27b5db0c6eb8d src/admin/status_live.rs
@@ -215,10 +216,10 @@ a1e14bfad1ba42045421fb761e0cc490cc30530409b22416db7e727548c06b93 src/admin/test
bffa6611f3776e97d0065b510b63f083aabf7b3429fcc5cb26b1883fdb7c47dd src/admin/tests/part_10.rs
48fcd818c522fe80f2c7b2e218f87de6a7772ffd0a24e93502780b03ec78e335 src/admin/web/acme.js
446011c2c7afddf7734ceb268c75a3b4146dbe8e915b11986495e956b6c62c69 src/admin/web/app/config-history.js
-8afd3356ba99843d28c107346b0f2f86fbbdf57d846554455fd374c4899bf2f2 src/admin/web/app/core.js
+7ec3f1b199d3e0134c1097231ded61a23fd152cdb702c008bac8f496a47b52f0 src/admin/web/app/core.js
23721bfbac850c4f77b116cc32bb9d2db6361ac4dd7e310a8c8070ab906997f6 src/admin/web/app/preferences.js
344faee969e9d3b8c2f59a443d8ab1090c556ceff325a109e745663baa5e644e src/admin/web/app/refresh.js
-31063816894c8efca08ff1c162a7fa2af71268c6deb8063146def2d0baabb17a src/admin/web/app/runtime.js
+397421b1fb680f5de1aa722e82f8a882877135b4a699e9fde0a9088c875341b3 src/admin/web/app/runtime.js
86ac42bd4a6a84bb137d5a3c3dc3d1f4e404a543447845e6ea6e6e8210e268ce src/admin/web/app/settings.js
4fec1344be3cd540d1e6d0b87fb0aa94e1bef2968c4f86366a62c56d4085f452 src/admin/web/cache-panel.js
50d1ebcbaf4d0b2968da23bb943fc3f67131c5d32df3f61d23ff37aad9da5b81 src/admin/web/demo.html
@@ -236,7 +237,7 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/
0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg
af6e7a2e890df2fb7385a374bcb3fba52fb5a53dbe18adc1fb7865660fcba842 src/admin/web/file-logs.js
b14e7b3578322f309627f6fe965cc16abcf85da3437c9070be6caecae1908743 src/admin/web/host404.js
-6da5aec18be66ef9f4f5b325bd235f632993a06f26bb41defa1e448881c15a2e src/admin/web/index.html
+6544bbebd94208551d5d5fecbfc9c6383dd01fb0a3680867ec9e6bbf05a9a29d src/admin/web/index.html
24db88842707f764064657712d3660c6ac129fbfb47d188c0903925cf8244ce5 src/admin/web/integrations.js
7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json
af0fbd94d9b88ce1cce28e39dfd2c35de031adcba412dd4a9a17d9b416c256dd src/admin/web/login.html
@@ -393,7 +394,7 @@ e03f0d6b3f35dae6b98c394837aa0d17e4d73a8306767b4c607868bc00c30286 src/prometheus
b5856fd38a957b2ca9c7e3606d84d13d5cfcf3a45b487b15ae1d2dc248cbda57 src/proxy/body.rs
16b9d4db3b20b2ab14c47ac14fcd18d3d8ca77c3ad9a1f37866ebfa60f379862 src/proxy/body/tests.rs
f0cb26ed34c35bacc74b914457acac8a611390de14cf46e2e6d3bcc848e914e8 src/proxy/cache_helpers.rs
-7ad068bca5397d1337f153fa26b9ca7ae1403abf4fa059b85362d15247717a67 src/proxy/cache_http_invalidation.rs
+aa49c9928bb91e3d5bae0d340d88396a0417b9edec6192d41e1403533319a897 src/proxy/cache_http_invalidation.rs
771cc1a38927f96e593b144dbab66cd64a5dc0ed42d54b1615c359845b2b7223 src/proxy/error_fallback.rs
bf2e6bbd64fb45fceed54af0bff1c1e00da22b79c64dd8f5c23d2af0dbab0b7d src/proxy/forward/cache_lookup.rs
526592b613fbe20f532f11be5a3236acaeb8cad400f70e1d28cc1c2dfc126c52 src/proxy/forward/response.rs
@@ -402,7 +403,7 @@ cea9e47ecb31b0e4c0bbe0eb83e0e53b5e7bdcb9930107b67a8c34eef01a3f49 src/proxy/forw
902cbf8dc3c1ec8d25411bc7fe0400d301f3a7cc885afada594e4eaee3bedd08 src/proxy/forward/success_prepare.rs
23b6bd26f6175b52c13d2571318fddd12550ba34ee77b87b5ac0e4811f8e2bee src/proxy/forward/upstream.rs
8961d8d649d8f4708f443f1b8b8a73c8b5f42811d2cad8cc6f596833511933f4 src/proxy/forward/web_source.rs
-a164fb389f38b0172db38803e9a49dd1cb61a4f2db49c72e220bebd96ea631ff src/proxy/forward_entry.rs
+deadb4dd9120e37cb443724f29a891032090761fda4780258a77928d2123726c src/proxy/forward_entry.rs
c438066616527a3108f2ad53e3698ae9b2a80961714394cdd0a450c92f99d992 src/proxy/headers.rs
5cb7522ceb9a9f083a15596ac4898559295e38d1138c80099577211de8a868ec src/proxy/headers/tests.rs
42155c7742da4173f71dc4ef488288e1fd665fbb295f02745421785571926295 src/proxy/mod.rs
@@ -518,34 +519,35 @@ eecf680f001f2110dde4eceaccf2f922859a16c946a644f8854e8de4d3771f7c tests/policies
728999110a6d8cacb62bc0399bc333fa23a66526ee23f563a3c93bf00e5fad19 tests/transport_config.rs
b04362ad082a32c9ccc4cf38c46f44d71347adecf7aba3224b3af3ea4872e3d0 tools/README.md
358c1e94dd2b4a756430e5335747f0b437345b1713670a9972c5bcf78b837ccb tools/audit/admin_audit.py
-b9d7b622127841f7abe6f87ce6bb769c95539de7c52824cc691a47be2d6ab65f tools/checks/admin_surface_audit_0310.py
+fd082812411cff7afb71b2289a6f1a469b8d3c8a7cfdcdd9e567a2a4baf81be4 tools/checks/admin_surface_audit_0310.py
412255d383fcc8306c6086784fb416d9814e3ec790e440a0927431ca615f3411 tools/checks/backend_cache_0297_ui_test.mjs
2a493dbce2991a23d807c01952f58608a364f541560836b4cb385e75e108e9e7 tools/checks/backend_health_0300_ui_test.mjs
-e3591f3aa47c595ad6d93509217734b90f829d8d3eac6c572936aeea1793a1cd tools/checks/check_source.py
-998ae8674598b8c8c937a323303f1ea0cccd4f8df2dfe804ddc24b5f8a6d6da3 tools/checks/dashboard_startup_0310_ui_test.mjs
-8005a9098f5ae142d8a54038506d8939725bf46c74bd07d2de3ecb916e390e9d tools/checks/docs_check.py
+daa718b6a3b519234cbb3614bd321f5b4b4ebd704bc9fb1c1dc18ba742759203 tools/checks/check_source.py
+0b583ee59dcccc35b15ef4e211d7e6546a8aaa7b0286ceab03c0b0dc084c1e07 tools/checks/dashboard_boot_0311_ui_test.mjs
+5f798699502c7a18e7b45aa84bdcf7ad0ccdb77f11cf2003527c97f0391c7abe tools/checks/dashboard_startup_0310_ui_test.mjs
+7428c63a80be49c57b6aad15738679175bad24c4271964cf01a67805ec22c0eb tools/checks/docs_check.py
e33a9cc51d58bc92a637c0e6a1c88cbd24ba4605be55748212e296c3c35a4060 tools/checks/high_traffic_memory_model.py
4f7821e7e25b09ce251d25a0b3fb4d8ce8396c36a5ed971c2637aeb325425909 tools/checks/langs_check.py
f8f87ba891338b565249d02c3cf5f02cec44e606b4205de99fcbea1eb2c24d57 tools/checks/memory_contracts.py
5b7901747d879e12a39ed82b6efc513bcdddd15d508b5828210d2c83398fb6a4 tools/checks/panel_cache_http_0310_ui_test.mjs
-4bf7e733f8a2c32c3ea857e680604af39ed26f9dc9b3659744d6ec21024e9f2f tools/checks/preflight.py
+274019cdcdccff5a5c7f76107daf096d111df1834b7280ccc3da9c564b65a04e tools/checks/preflight.py
3485681d369b5f486dfc2d14660f19b4098df670bafd9a296cc9dabe68003a51 tools/checks/refresh_0310_ui_test.mjs
-1f4f834761a4107422e39243e39e352fff4e73eac923b6f26eb4d8e5b3e8a5c1 tools/checks/release_smoke.py
+c8b9216936300dac4ebb582a70efe2286124f0d14e501a6c22fc10cc2caaddbb tools/checks/release_smoke.py
f69845bc26ec33160fc756953991cf32aa8ad6a7fb4a131caa2093db91e783ef tools/checks/rust_module_contracts.py
ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed tools/checks/rust_source.py
adf84a983e0dbba228e47d71ec2d5c9964f7ca9989d5139de58f96ee961b035a tools/checks/security_contracts.py
23b9250245e6bec5ef728c73cee4c88f64924afc3597f8a9b58a1924235df98e tools/checks/security_dependency_contracts.py
-2e125a2a807553ac81d04428f337f5042eed968cb807244487114fdd8ea6b5d9 tools/checks/source_module_contracts.py
-bbd9726e11ce1324b7df6a012bac51262e22f2617bd3d36974aa151dce5c1c34 tools/checks/system_info_0301_ui_test.mjs
+5c1d428cbda5f08b8d6847732672e45e9da96b545337334e473986e119197de2 tools/checks/source_module_contracts.py
+b78240c3ccead8c023609fc71560be7ce2f2aa5d31c78059a24cfe4b0412d9c5 tools/checks/system_info_0301_ui_test.mjs
543a488951ea6ec8fe06e04f168ccc4d4f2af3cfc281c9a728ab7bd87377fa98 tools/checks/system_info_no_subprocess_contracts.py
614ed0c775e32ec5bfb4ee75aaa90206a10da0f43659747a7ec76fd93e33767a tools/checks/version_contracts.py
2020e171be1a044f7ca2ab83af7604a014c36fe4ff04791d0620ccbf1bf8e000 tools/checks/vhost_origin_errors_0297_ui_test.mjs
0c071c26fc316934bdfd2bfd19f5fed9521790d8c887760eaabda81a9be96703 tools/checks/vhost_www_ui_test.mjs
-934dce837a13c8e9eee8e161aeb8b63dc771e2197c61639cf59e33b5ceaca833 tools/checks/web_assets.py
+597d6d2da052390e285f18a257d10e6ab8dbd7add666457656cd803416b7975d tools/checks/web_assets.py
0398892be30fe416a00578915c588f360fd93244e3af77ece834e560e547d1c8 tools/checks/web_server_contracts.py
35f76060e585a96c623ef83bd5f771969f5be16a0e1ae626a3092d7458b84e84 tools/checks/web_service_ui_test.mjs
adaa1ed9e5958739df40190055c780c80d0a8e270ec8dc105ed3fa2dbb8fb74f tools/checks/www_redirect_ui_test.mjs
-f16f69dabab98fe468aa90cc8f3c447c0588d7bd18163080d3d7b15c67190e4c tools/dev/memory_load.py
+c8bf1c3ef876b4f87840414f5de19c183309550572437cca7ba1fe02df496462 tools/dev/memory_load.py
d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a tools/dev/mock_auth.py
2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 tools/dev/mock_server.py
538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba tools/dev/test_compression.py
diff --git a/dev.sh b/dev.sh
index b39ba3c..2a4b1bf 100755
--- a/dev.sh
+++ b/dev.sh
@@ -108,6 +108,7 @@ if [[ "$mode" == "test" ]]; then
node tools/checks/system_info_0301_ui_test.mjs
node tools/checks/refresh_0310_ui_test.mjs
node tools/checks/dashboard_startup_0310_ui_test.mjs
+ node tools/checks/dashboard_boot_0311_ui_test.mjs
node tools/checks/panel_cache_http_0310_ui_test.mjs
node tools/checks/vhost_origin_errors_0297_ui_test.mjs
node tools/checks/www_redirect_ui_test.mjs
diff --git a/docs/CHANGELOG_TECHNICAL_ARCHIVE.md b/docs/CHANGELOG_TECHNICAL_ARCHIVE.md
index 7d9616b..8bb4325 100644
--- a/docs/CHANGELOG_TECHNICAL_ARCHIVE.md
+++ b/docs/CHANGELOG_TECHNICAL_ARCHIVE.md
@@ -1,30 +1,12 @@
# Historical developer-level changelog archive
> Archived during the 0.25 documentation cleanup. This file preserves the pre-cleanup
-> detailed release record, including compiler fixes, source refactors, regression notes and
+> detailed release record, including source refactors, regression notes and
> packaging details. It is historical technical evidence, not the operator-facing changelog.
> Use [`../CHANGELOG.md`](../CHANGELOG.md) for user/operator-visible release changes.
# Changelog
-## 0.31.0 Axum fallback handler E0277 source correction
-
-- Correct the reported `Handler<_, _>` error at `src/proxy/types.rs:53` after the public cache-maintenance path was added. The asynchronous `cache_http_invalidation` helper previously accepted `&Request` while awaiting database audit and cache invalidation; Axum's body is not `Sync`, so the borrowed request can make the `forward` handler future non-`Send`.
-- Pass ownership of `Request` to the maintenance helper, perform the same body/header/allowlist validation, copy the HTTP method for the audit entry, and release the request body before the first asynchronous operation. The `forward` router and public API semantics are unchanged.
-- Add an offline source regression asserting that the async helper owns its request. Pre-Rust `./dev.sh test` checks and Go tests pass; full Rust parsing, type checking, tests and runtime maintenance calls cannot be validated without Cargo/Rustc.
-
-## 0.30.1 same-version sysinfo compiler feature correction
-
-- Fix four compiler `E0433` errors in the system information endpoint by enabling `sysinfo`'s `system` feature alongside `network` with `default-features = false` unchanged. No dependency version or API change.
-- Remove unused Tokio `AsyncReadExt` imported by the ACME runtime, and add the explicit manifest feature check to the offline source regressions.
-- The change addresses the supplied Rust compiler log. A full Rust build and multi-OS runtime check remain outstanding in the source packaging environment.
-
-## 0.29.7 same-version `E0308` source correction
-
-- Defer Reverse Proxy error interception from the upstream `Response` to the adapted `Response` in `success_prepare.rs`. This preserves the H2 WebSocket tunnel's `Incoming` body type and ensures replacement still precedes response transformation and cache admission. Reapply response rules and enforce `no-store` for replaced application errors.
-- Change health supervisor member collection to own `Arc` and spawn probes with `Arc::clone(member)`, meeting Tokio's owned `'static` task requirement.
-- Extend the application-error UI/source regression check with the body-adaptation ordering, no-store and Arc ownership assertions. Offline checks passed; Rust compilation is not available in the packaging environment.
-
## 0.26.7
### Backend client lifecycle and cache diagnostics
@@ -39,29 +21,8 @@
- Build the OCI runtime image with a dedicated Certbot virtual environment containing the Cloudflare, OVH and RFC2136 DNS plugins supported by RustProxy, all pinned with the same Certbot release.
- Refresh container documentation so it no longer tells operators that those provider plugins must be added separately.
-## 0.26.6
-
-### Compiler follow-up
-
-- Removed the redundant `country_resolved = true` assignment after route-level GeoIP resolution in `proxy::forward_entry`. The flag is only needed to prevent a second lookup before that branch; the removed write was never read and produced `unused_assignments` during a real Rust build. ACL/GeoIP behavior is unchanged.
-- A fresh compiler-enabled lint/test run remains required on the Rust release host because this packaging workspace has no Rust toolchain.
-
-## 0.26.0
-
-### Compiler follow-up
-
-- Corrected the external `cargo` build failure in `server::tls_runtime`: the HTTP/1 service closure now captures its own cloned `frontend_state`, leaving the original state available for connection-level protocol-error logging after `serve_connection` returns.
-- Corrected `serde_json::Map::entry` type inference in the stored-log normalizer by passing the literal `"event"` directly instead of the ambiguous `"event".into()`.
-- Rust-independent language, security, source-module, documentation, UI, source and release-smoke gates pass after both corrections. A fresh compiler-enabled `cargo check/test` remains required because this packaging workspace has no Rust toolchain.
-
## 0.25.1
-### 0.25.1 source-split compiler follow-up
-
-- Corrected Rust `include!` split regressions reported by a real `cargo` build: dangling method documentation in runtime fragments, an invalid inner module doc in `balance/model.rs`, and the missing sibling `htaccess` import in Web Server response headers.
-- Removed the warning set reported by that build (stale imports, unnecessary `mut` bindings, an unnecessary returned timeout value, and unused Prometheus parameters).
-- Strengthened `rust_module_contracts.py` so doc comments/attributes immediately before a closing brace and inner `//!` comments in included fragments fail preflight instead of producing a false PASS.
-
### Source modularization and release guards
- Enforce a 500-line ceiling for Rust implementation/test source files and bounded dashboard JavaScript, Docker Agent Go and operational deployment/migration modules.
@@ -79,7 +40,6 @@
- Enforce document-root confinement for Web Server paths, reject ambiguous/encoded traversal forms and prevent symlink escape by not following symlinks during Unix filesystem traversal.
- Reuse the existing vhost ACL/authentication/header/error/TLS/HTTP pipeline for local files without adding a separate router, error-page system or object cache.
- Add an authenticated **Docs Reader** for bundled project documentation alongside the existing API/OpenAPI view, and separate the current documentation index, release audit and open TODO material.
-- Fix the 0.25 Docs Reader source embedding so compilation does not require a generated `OUT_DIR/docs-content.json`; Markdown sources are now direct compiler inputs and the JSON bundle is serialized from embedded strings.
- Split the 0.25 Web Server implementation from the former monolithic `src/web_server.rs` into the responsibility-based `src/web_server/` module (`config`, filesystem, security, response, routing, FastCGI, dispatcher, `.htaccess` and tests), keeping `mod.rs` as the stable public facade and updating source/package gates for the new layout.
- Extend the source-layout cleanup across the Rust tree: every checked-in `.rs` file is capped at 500 lines, large `config`/`docker`/`server`/`runtime`/`balance`/`prometheus`/`proxy`/cache/admin/etc. modules are split by responsibility, unit-test bodies live in separate test files, and compiler-independent contracts reject dangling attributes/doc comments, broken `include!` targets and unbalanced split fragments.
@@ -96,8 +56,6 @@
- Added composite Settings > Logging support for `error_log.enabled`, path and format while retaining legacy `/api/config/logging` payload compatibility.
- Removed the Error Log help sentence from the panel while keeping the existing controls and behavior unchanged.
-- Fix Rust type inference for zeroizing ACME/Base64 and AEAD buffers so the new Secret Store and encrypted backup code compile with the pinned dependency set.
-- Fix the secret-redaction KV regex literal so 0.24.9 compiles correctly; the redaction behavior itself is unchanged.
- Harden the built-in release checker for FreeBSD/OPNsense and other Unix platforms: supplement bundled Mozilla roots with common system CA bundles/`SSL_CERT_FILE`/`CURL_CA_BUNDLE`, follow bounded non-downgrading HTTP(S) redirects, and report failed checks as normal application `ERROR` events without changing dashboard behavior.
- Add a central AES-256-GCM encrypted Secret Store with private local master-key storage and migration of existing ACME DNS-provider credentials.
- Add encrypted full backup/restore with Argon2id, authenticated encryption, explicit manifests/checksums, staging validation and rollback-safe activation.
@@ -119,8 +77,6 @@
- Show transform-resource warnings when the selected budget cannot fit the worst-case buffers for one maximum compressed body or exceeds half of detected host/cgroup RAM.
- Add live dashboard resource accounting: current transform RAM reservation/budget and active slots, HOT/transient cache RAM, non-cache process RSS, plus COLD disk occupancy and object limits.
- Include global transform representation/eligibility settings in cache compatibility identity while excluding resource-only preset/budget/concurrency controls.
-- Map bounded transform-body collection failures explicitly into `anyhow::Error`, avoiding the `anyhow::Context` trait-bound compile failure for Axum/HTTP body boxed errors.
-- Clean compiler warnings in cache transient leases and CSS minification without suppressing lints: make RAII-only lease ownership explicit, keep COLD read reservations intentionally alive, and remove dead CSS state assignments.
- Extend SQLite/OpenAPI/offline memory/release regressions and update release identity to 0.24.7.
## 0.24.6
@@ -172,7 +128,6 @@
## 0.24.1
- Restore system network interface discovery and RX/TX counters after the shared-statistics refactor: use the cross-platform `sysinfo::Networks` API as the primary network inventory/counter source, merge `systemstat` as a library-level supplement/fallback for missing or partial inventory, preserve per-interface rate calculation, and stop silently truncating the interface list.
-- Fix the compiler error in portable uptime collection by using the `systemstat` duration API correctly, and remove the now-unused `Future` import in the H2 server module.
- Remove RustProxy's artificial hostname-count ceilings from certificate SNI bindings, local certificate generation/import validation, ACME domain validation, vhost/redirect/404 hostnames, administration allowed hosts, host ACL policies and Docker aliases. Input validity and bounded control-plane/resource limits remain enforced.
- Replace per-BSD command/parsing statistics code with shared library collectors: `systemstat` for host metrics, `sysinfo::Networks` for network inventory/counters, and `metrics-process` for process metrics, using the documented dummy process fallback on targets without a native collector.
- Preserve Linux `/proc` and cgroup-specific details as additive compatibility metrics while using the common library API for portable host/process values.
@@ -223,7 +178,6 @@
- Bound inactive inbound HTTP/2 connections to five minutes so idle TLS/H2 sessions cannot accumulate indefinitely toward the process connection limit; export `rust_proxy_http2_active_connections` for direct diagnosis.
- Account hot-cache response-body backing allocation capacity rather than only visible body length, preventing `BytesMut` growth slack from bypassing the configured RAM budget and under-reporting resident cache memory.
-- Fix the `--livelogs` stdin reader to send owned `String` values from the match body rather than a pattern guard, resolving Rust `E0507` during binary compilation.
- Add a scoped external Certificate API credential managed from a new **Settings → Certificate API** tab. RustProxy stores only the token hash plus a non-secret Key ID; the cleartext token is returned only on rotation.
- Allow the scoped Key ID + Bearer Token only on `POST /api/certificates/deploy` and `POST /api/certificates/reload`, require HTTPS for non-loopback token use, track last use, support immediate revoke, and retain normal administrator-session/CSRF access as an alternative.
- Keep production certificate operations available directly from the installed binary (`rust-proxy tls deploy` / `rust-proxy tls reload`) without requiring `dev.sh`.
@@ -243,7 +197,6 @@
## 0.22.2
-- Fix the 0.22.2 backend diagnostics build failure by replacing the oversized recursive `serde_json::json!` member payload with explicit `serde_json::Map` construction; the administration JSON schema is unchanged.
- Add bounded replay-safe backend failover retry for body-free GET/HEAD/OPTIONS requests, including retry policy controls, passive-health integration, logs and per-backend retry counters.
- Correct ordinary explicit-HTTP/2 upstream requests and add a real Hyper h2c prior-knowledge regression path.
- Add per-backend request-concurrency limits with bounded capacity waiting, `503` rejection and capacity/queue-timeout metrics. This limits full proxy exchanges, not TCP socket count.
@@ -295,7 +248,6 @@
- Fix Docker-vhost Passive Backend Health checkbox sizing so enabling the expanded editor cannot make fields overlap.
- Remove the unused mutable `origin` binding from the background revalidation path.
- Replace the monolithic `src/cache.rs` with the responsibility-based `src/cache/` module (`model`, lifecycle, cold storage, dashboard, tickets, lookup, policy, fill and tests) and update source/package gates for the new layout.
-- Fix the cache split so method groups are emitted from module-scope `include!` files wrapped in their own `impl Cache` blocks; this avoids the Rust compiler error caused by `include!` directly in impl-item position.
- Persist transformed-representation metadata through hot/Cold Cache, mark transformed objects in the Cache dashboard, and append `transformed` to standardized RFC 9211 `Cache-Status` diagnostics; origin `X-Cache-Status` lookalikes are stripped rather than emitted.
- Shard Cold Cache storage and add bounded asynchronous garbage collection plus selective manual cleanup.
@@ -326,7 +278,6 @@
## 0.19.3
-- Fix the compiler error in the release checker body collector by converting Hyper/http-body-util boxed collection errors explicitly into `anyhow::Error` instead of calling `anyhow::Context` on an unsupported boxed error type.
- Simplify the dashboard footer to API latency and dropped-log count, and add a fail-soft built-in release checker using embedded `UPDATE_CHECK.toml` defaults. The checker reads the official Cargo manifest immediately and then hourly, exposes a newer-version link in the footer, and never performs automatic upgrades.
- Allow runtime release-check overrides through `--update-repository-url`, `--update-version-url` and `--update-check-interval-secs` (plus matching `RUST_PROXY_*` environment variables); interval `0` disables checks.
- Fix the remaining oversized Live Logs column checkboxes by excluding checkbox/radio inputs from both broad `.log-tools input` text-field sizing blocks that were overriding the shared 15px checkbox rules.
@@ -367,7 +318,6 @@
- Retain 4096 completed access-log entries while keeping the 256-entry live window; add one-shot on-demand history loading and document the API limit.
- Render backend traffic with KiB/s as the minimum unit, simplify the Live Logs column picker, align generic checkboxes and move Docker-vhost Advanced switches beside Backend protocol on desktop; remove the redundant alias help line.
- Remove stale simulated dashboard screenshots, retain only two explicitly labelled legacy references and add a documentation index for historical/version-labelled material.
-- Restore the integration-test sources and independent Argon2id fixture required by release/source checks so compiler-enabled test builds no longer reference files missing from the supplied source archive.
- Update source, UI, OpenAPI, regressions and package identity to 0.19.1.
## 0.19.0
@@ -453,7 +403,6 @@
- Add a separate live **Docker vhosts** Prometheus collector group, enabled by default, with configured agent/assignment/domain gauges plus Docker vhost traffic/status/duration and full backend health/probe/runtime metrics.
- Extend Prometheus/OpenAPI/UI documentation and regression coverage for the Docker metric boundary.
- Extend the bundled Grafana operations dashboard with a dedicated Docker-vhost section and per-agent/service filters using only the separate Docker metric families.
-- Fix Docker discovery compilation against the locked HTTP/body stack by mapping limited-body collection errors explicitly and logging `ApiError` via its `Debug` representation.
## 0.18.2
@@ -604,7 +553,6 @@
- Add per-vhost control over forwarding the effective client IP to the application. Disabling it removes `Forwarded`, `X-Forwarded-For` and `X-Real-IP` only on the backend request; ACLs, GeoIP, rate limiting and Tinyauth continue to use the real client identity.
- Add a compact Vhost search/filter for vhost IDs, hostnames/wildcards and backend IDs/URLs.
-- Fix the invalid `quinn = 0.11.22` manifest requirement to the resolved `0.11.12` and stop `dev.sh` from rewriting a valid checked-in `Cargo.lock`.
- Fix JSONL access logging under the hardened systemd unit by provisioning `/var/log/rustproxy` as a writable service log directory while keeping `ProtectSystem=strict`.
- Add source/offline regression coverage and bump release/package/UI/OpenAPI/update metadata to 0.11.3. Rust compilation/runtime acceptance remains open in this packaging environment.
diff --git a/docs/IMPLEMENTATION_0.31.1.md b/docs/IMPLEMENTATION_0.31.1.md
new file mode 100644
index 0000000..005bd60
--- /dev/null
+++ b/docs/IMPLEMENTATION_0.31.1.md
@@ -0,0 +1,64 @@
+# RustProxy 0.31.1 — mobile dashboard initialization
+
+## Scope
+
+This patch changes only the authenticated administration panel bootstrap and its
+release documentation. It does not modify reverse-proxy traffic processing,
+authentication, stored configuration, SQLite migrations or preference values.
+
+## Root-cause analysis
+
+Previously, the authenticated dashboard HTML requested 17 independent deferred
+JavaScript files. The first one (`/assets/app.js`) starts asynchronous session
+and configuration requests from `web/app/runtime.js`. Deferred ordering only
+orders *script evaluation*, not asynchronous continuation: a completed API call
+can resume while later JavaScript resources are still downloading. In that
+window, a render path may access `window.rustProxyIntegrations.bindings`
+before `integrations.js` has finished evaluating. The browser then reports an
+undefined-object exception. A failed/missing script has the same symptom.
+
+The reported iPhone/reverse-proxy incident is consistent with this race. It was
+not possible to capture the user's mobile network trace, so the precise
+combination of mobile transport behavior, timing, and content encodings is not
+proven.
+
+## Changes
+
+1. The dashboard HTML now requests only `/assets/app.js?v=0.31.1`. On the
+ backend, the same existing `/assets/app.js` route assembles all existing
+ UI source modules in their prior load order. `/assets/vhosts.js` and the
+ other private asset URLs remain available for compatibility.
+2. Startup waits for `DOMContentLoaded` and verifies the required modules
+ before requesting a session or configuration. A missing module produces
+ a visible startup failure with the existing retry link instead of a
+ partially initialized workspace.
+3. Authenticated HTML also requests `/assets/style.css?v=0.31.1`; versioned
+ URLs separate release assets in intermediary caches. No cache bypass
+ or potentially unsafe public caching is introduced on the server.
+4. The initial `/api/config` load retries at most twice after a transient
+ failure (400 ms / 800 ms delays). If it cannot load, the existing startup
+ recovery UI is displayed. Session rejection (`401`) does not trigger retries.
+5. Public login, CSP, compression negotiation, SSE, polling, Traffic and Cache
+ APIs, user preferences and the proxy request path are unchanged.
+
+## Acceptance checks
+
+- Node.js parses each source module and the complete emitted dashboard bundle.
+- A JavaScript startup test verifies **zero API requests before DOM readiness**,
+ successful activation with complete modules, and recovery when a module is
+ missing. A second test verifies configuration retry success/failure.
+- Chromium smoke test on the authenticated HTML with simulated session and
+ configuration endpoints, at desktop and mobile widths, when available.
+- Run `./dev.sh test` and `./dev.sh check` in an environment with Rust/Cargo.
+- On the production reverse-proxy path, test initial load, 20+ rapid reloads,
+ navigation between tabs, Safari on iPhone (Wi-Fi and cellular), and a
+ deliberately delayed/failed `/api/config` response. Confirm no uncaught JS
+ errors and no mixed asset versions.
+- Compare actual HTTP `Content-Encoding`, `Content-Length`, `Vary`, `Status`
+ and response bodies for document, script and stylesheet behind the reverse.
+ Disable any reverse cache rule that ignores version/query keys.
+
+## Environment limitations
+
+Compiler/type-checking, live RustProxy API tests and Safari/iPhone tests require
+the target runtime; they cannot be inferred from source or Chromium alone.
diff --git a/docs/README.md b/docs/README.md
index 72a4a61..5edab32 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -31,7 +31,8 @@ This is the canonical index for the current project documentation. The authentic
- [0.26.3 implementation note](V0.26.3_IMPLEMENTATION.md) - global interface scaling, chart themes, square draft dock and Settings navigation polish.
- [0.26.2 implementation note](V0.26.2_IMPLEMENTATION.md) - unified Logs navigation, field-query search, dynamic JSON columns and RAW parsing.
- [0.26.1 implementation note](V0.26.1_IMPLEMENTATION.md) - historical vhost-aware protocol logging and Stored Logs UX milestone.
-- [0.31.0 security audit](SECURITY_AUDIT_0.31.0.md) - current API/panel/BAN/PURGE attack-surface review, hardening, compile fix and mandatory runtime gates.
+- [0.31.1 mobile panel bootstrap](IMPLEMENTATION_0.31.1.md) - panel script readiness, versioned assets, failure recovery and acceptance tests.
+- [0.31.0 security audit](SECURITY_AUDIT_0.31.0.md) - current API/panel/BAN/PURGE attack-surface review, hardening and remaining runtime verification.
- [0.31.0 implementation report](IMPLEMENTATION_0.31.0.md) - consolidated changes, exact file inventory, validation results and outstanding Rust runtime acceptance.
- [TEST_REPORT](TEST_REPORT.md) - verification commands, results and environment limitations.
@@ -80,7 +81,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 [IMPLEMENTATION_0.31.0.md](IMPLEMENTATION_0.31.0.md); [V0.29.1_IMPLEMENTATION.md](V0.29.1_IMPLEMENTATION.md) is retained as historical context, and [V0.29.0_IMPLEMENTATION.md](V0.29.0_IMPLEMENTATION.md) is retained as the prior redirect/health/cache/log milestone; [V0.28.2_IMPLEMENTATION.md](V0.28.2_IMPLEMENTATION.md) is retained as the prior certificate-hardening milestone; [V0.28.1_IMPLEMENTATION.md](V0.28.1_IMPLEMENTATION.md) is retained as the prior external-path milestone; [V0.28.0_IMPLEMENTATION.md](V0.28.0_IMPLEMENTATION.md) is retained as the prior GeoIP/listener/cache milestone, and [V0.27.3_IMPLEMENTATION.md](V0.27.3_IMPLEMENTATION.md) is retained as the prior HTTP/3/cache-maintenance milestone, [V0.27.2_IMPLEMENTATION.md](V0.27.2_IMPLEMENTATION.md) is retained as the prior managed-ACME repair, [V0.27.1_IMPLEMENTATION.md](V0.27.1_IMPLEMENTATION.md) as the prior managed-runtime/list-sorting milestone, and older version notes remain historical evidence. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain available for traceability.
+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 [IMPLEMENTATION_0.31.1.md](IMPLEMENTATION_0.31.1.md); [IMPLEMENTATION_0.31.0.md](IMPLEMENTATION_0.31.0.md) is retained as the previous milestone; [V0.29.1_IMPLEMENTATION.md](V0.29.1_IMPLEMENTATION.md) is retained as historical context, and [V0.29.0_IMPLEMENTATION.md](V0.29.0_IMPLEMENTATION.md) is retained as the prior redirect/health/cache/log milestone; [V0.28.2_IMPLEMENTATION.md](V0.28.2_IMPLEMENTATION.md) is retained as the prior certificate-hardening milestone; [V0.28.1_IMPLEMENTATION.md](V0.28.1_IMPLEMENTATION.md) is retained as the prior external-path milestone; [V0.28.0_IMPLEMENTATION.md](V0.28.0_IMPLEMENTATION.md) is retained as the prior GeoIP/listener/cache milestone, and [V0.27.3_IMPLEMENTATION.md](V0.27.3_IMPLEMENTATION.md) is retained as the prior HTTP/3/cache-maintenance milestone, [V0.27.2_IMPLEMENTATION.md](V0.27.2_IMPLEMENTATION.md) is retained as the prior managed-ACME repair, [V0.27.1_IMPLEMENTATION.md](V0.27.1_IMPLEMENTATION.md) as the prior managed-runtime/list-sorting milestone, and older version notes remain historical evidence. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain available for traceability.
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.
diff --git a/docs/TEST_REPORT.md b/docs/TEST_REPORT.md
index 81c0feb..54248ab 100644
--- a/docs/TEST_REPORT.md
+++ b/docs/TEST_REPORT.md
@@ -1,4 +1,12 @@
-# Verification report - RustProxy 0.31.0
+# Verification report - RustProxy 0.31.1
+
+## 0.31.1 — mobile/reverse-proxy dashboard startup (2026-10-10)
+
+- **Cause found in source:** dashboard HTML fetched 17 deferred JS resources, while the first bundle launched an async session/config bootstrap without waiting for all later scripts to finish. A fast API response could invoke the renderer before `window.rustProxyIntegrations.bindings` was registered. An interrupted script transfer could cause the same exception. The reported Safari error is consistent with this race, but the specific production network failure was not captured.
+- **Implemented:** one ordered embedded JS payload at `/assets/app.js?v=0.31.1`; CSS URL also versioned; DOMContentLoaded/module gating and explicit startup recovery; up to two bounded retries of initial configuration (400/800 ms). Old private asset URLs remain available. No API, authentication, saved preferences, database or proxy data path changes.
+- **PASS:** `./dev.sh test` completes all source checks, SQLite migration/backup and upgrade smoke tests, security and language checks, Node UI tests (including new startup-order and retry test), and documentation checks **before Cargo**. `python3 tools/checks/source_module_contracts.py` checks each JS module and the assembled 608 KB bundle with `node --check`. `cd docker-agent && go test ./...` passed. Shell syntax passed.
+- **Not verified:** `./dev.sh test` terminates **127** at Cargo; `./dev.sh check` also **127** (`cargo`/`rustc` absent). Browser smoke against a local simulated API was attempted, but Chromium navigation was blocked by sandbox policy (`ERR_BLOCKED_BY_ADMINISTRATOR`); no successful browser smoke run is claimed. Playwright WebKit browser binaries are not installed here. No real iPhone Safari or production reverse-proxy test was possible.
+- **Release-host checks:** `./dev.sh check && ./dev.sh test && ./dev.sh release`, followed by Safari/Chrome iPhone tests behind the reverse: 20+ full reloads from Overview/Cache/Settings, initial config API failures, SSE reconnect, browser console errors, and response encoding/version consistency for HTML, `/assets/app.js?v=0.31.1` and CSS. Review the detailed plan in [IMPLEMENTATION_0.31.1.md](IMPLEMENTATION_0.31.1.md).
## 0.31.0 Axum handler `Send` correction (2026-10-10)
diff --git a/src/admin/documentation/documents.rs b/src/admin/documentation/documents.rs
index f36e87f..25a1216 100644
--- a/src/admin/documentation/documents.rs
+++ b/src/admin/documentation/documents.rs
@@ -244,6 +244,7 @@ pub(super) static READER_DOCUMENTS: &[(&str, &str, &str)] = &[
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/STREAMS.md")),
),
+ reader_doc!("docs/IMPLEMENTATION_0.31.1.md", "technical"),
reader_doc!("docs/IMPLEMENTATION_0.31.0.md", "technical"),
(
"docs/TEST_REPORT.md",
diff --git a/src/admin/openapi.json b/src/admin/openapi.json
index d9fd565..a030d43 100644
--- a/src/admin/openapi.json
+++ b/src/admin/openapi.json
@@ -2,7 +2,7 @@
"openapi": "3.0.3",
"info": {
"title": "RustProxy Administration API",
- "version": "0.31.0",
+ "version": "0.31.1",
"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": [
diff --git a/src/admin/router.rs b/src/admin/router.rs
index 170b0b5..64ed824 100644
--- a/src/admin/router.rs
+++ b/src/admin/router.rs
@@ -40,6 +40,22 @@ fn admin_app_javascript() -> &'static str {
include_str!("web/app/config-history.js"),
include_str!("web/app/runtime.js"),
include_str!("web/app/settings.js"),
+ include_str!("web/management.js"),
+ include_str!("web/drafts.js"),
+ include_str!("web/integrations.js"),
+ admin_vhosts_javascript(),
+ include_str!("web/web-server-profiles.js"),
+ include_str!("web/web-server.js"),
+ include_str!("web/docker.js"),
+ include_str!("web/streams.js"),
+ include_str!("web/redirects.js"),
+ include_str!("web/host404.js"),
+ include_str!("web/traffic-panel.js"),
+ include_str!("web/cache-panel.js"),
+ include_str!("web/transform-panel.js"),
+ include_str!("web/acme.js"),
+ include_str!("web/settings.js"),
+ include_str!("web/file-logs.js"),
]
.join("\n")
});
diff --git a/src/admin/web/app/core.js b/src/admin/web/app/core.js
index 932bc4f..1c92df5 100644
--- a/src/admin/web/app/core.js
+++ b/src/admin/web/app/core.js
@@ -396,12 +396,13 @@ async function activate(info) {
window.rustProxyRedirects?.applyPreferences(preferences);
window.rustProxyNotFound?.applyPreferences(preferences);
window.rustProxyStreams?.applyPreferences(preferences);
- // The live SSE stream is already running. Configuration can load behind the
- // visible shell; it does not belong in the live refresh cycle.
- try { await loadConfig(); }
- catch (error) {
- if (authenticated && epoch === authEpoch) toast(error.message, true);
- return;
+ for (let attempt = 0; attempt < 3 && authenticated && epoch === authEpoch; attempt++) {
+ try { await loadConfig(); break; }
+ catch (error) {
+ if (!authenticated || epoch !== authEpoch || error?.status === 401) return;
+ if (attempt === 2) { showStartupFailure(error); return; }
+ await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 400));
+ }
}
if (!authenticated || epoch !== authEpoch) return;
// A single full snapshot seeds non-live diagnostics; subsequent updates
diff --git a/src/admin/web/app/runtime.js b/src/admin/web/app/runtime.js
index 34787a1..0227c47 100644
--- a/src/admin/web/app/runtime.js
+++ b/src/admin/web/app/runtime.js
@@ -1,5 +1,22 @@
-(async () => {
+// All modules must be available before any asynchronous session/config work can
+// render the panel. DOMContentLoaded also waits for the complete deferred bundle.
+function verifyAdminModules() {
+ const required = [
+ ["rustProxyIntegrations", "bindings"],
+ ["rustProxyVhosts", "render"],
+ ["rustProxySettings", "render"],
+ ["rustProxyCachePanel", "refresh"],
+ ["rustProxyTrafficPanel", "refresh"],
+ ["rustProxyPreferences", "load"],
+ ["rustProxyRefresh", "start"],
+ ];
+ const missing = required.find(([module, method]) => typeof window[module]?.[method] !== "function");
+ if (missing) throw new Error(`Panel resources incomplete (${missing[0]}). Reload to retry.`);
+}
+
+async function startAdminPanel() {
try {
+ verifyAdminModules();
// Revalidate the session first. The anonymous document has its own login.js;
// app.js belongs to the authenticated shell and must not fall back to its
// legacy embedded sign-in form when another startup step fails.
@@ -18,7 +35,12 @@
}
showStartupFailure(e);
}
-})();
+}
+// The previous bootstrap ran while sibling deferred scripts were still loading.
+// Starting only after DOMContentLoaded prevents fast API responses from racing
+// against a slower or failed JS resource on mobile/reverse-proxied connections.
+if (document.readyState === "complete") startAdminPanel();
+else document.addEventListener("DOMContentLoaded", startAdminPanel, { once: true });
function certificateUsesExternalFiles(cert) {
return String(cert?.cert_file || "").startsWith("/") && String(cert?.key_file || "").startsWith("/");
diff --git a/src/admin/web/index.html b/src/admin/web/index.html
index f366e0d..2ef9986 100644
--- a/src/admin/web/index.html
+++ b/src/admin/web/index.html
@@ -7,24 +7,9 @@
RustProxy / Control room
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/tools/checks/admin_surface_audit_0310.py b/tools/checks/admin_surface_audit_0310.py
index f0aa62e..e6a08ba 100755
--- a/tools/checks/admin_surface_audit_0310.py
+++ b/tools/checks/admin_surface_audit_0310.py
@@ -111,4 +111,4 @@ for fragment in ['class="metric-info cache-maintenance-info"',
'cacheHttpInvalidationCurlLabel',
'curl -i -X PURGE', 'curl -i -X BAN']:
must(html, fragment)
-print(f'PASS: 0.31.0 admin/HTTP maintenance security source contracts; scanned {len(js_files)} first-party JS files. Actual Rust/wire behavior not tested.')
+print(f'PASS: admin/HTTP maintenance security source contracts; scanned {len(js_files)} first-party JS files. Actual Rust/wire behavior not tested.')
diff --git a/tools/checks/check_source.py b/tools/checks/check_source.py
index 83152cd..8796aa0 100755
--- a/tools/checks/check_source.py
+++ b/tools/checks/check_source.py
@@ -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.31.0"
+assert manifest["package"]["version"] == "0.31.1"
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.31.0"' in lock
+ assert 'name = "rust-proxy"\nversion = "0.31.1"' in lock
assert 'name = "systemstat"' in lock and 'name = "metrics-process"' in lock and 'name = "sysinfo"' in lock, "Cargo.lock must be resolver-generated after adding cross-platform statistics libraries"
for release_doc, marker in {
- "README.md": "# RustProxy 0.31.0",
- "CHANGELOG.md": "## 0.31.0",
- "ROADMAP.md": "## 0.31.0",
- "AGENTS.md": "Current source version: 0.31.0.",
- "docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.0",
+ "README.md": "# RustProxy 0.31.1",
+ "CHANGELOG.md": "## 0.31.1",
+ "ROADMAP.md": "## 0.31.1",
+ "AGENTS.md": "Current source version: 0.31.1.",
+ "docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.1",
}.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"
@@ -177,7 +177,7 @@ for source_name, markers in {
assert marker in source_text, (source_name, marker)
assert 'data-panel="docker-vhosts"' in html and 'id="panel-docker-vhosts"' in html
assert 'id="settings-tab-docker-agents"' in html and 'id="settings-page-docker-agents"' in html
-assert '/assets/docker.js' in html and 'cache-live-toggle' in html
+assert 'include_str!("web/docker.js")' in (root / 'src/admin/router.rs').read_text() and 'cache-live-toggle' in html
assert 'expose-revision' not in html and 'hideRevision' not in js and 'diagRevision' not in js
for path in ['src/settings.rs', 'src/admin/mod.rs', 'src/cache/mod.rs', 'src/headers.rs', 'src/tinyauth.rs', 'src/admin/web/vhosts/core.js']:
text = rust_source(root, path) if path.endswith('.rs') else (root / path).read_text()
@@ -216,7 +216,7 @@ for lang_name in ["en_US.json", "pl_pl.json"]:
assert html.index('data-panel="vhosts"') < html.index('data-panel="redirects"') < html.index('data-panel="notfound"') < html.index('data-panel="streams"') < html.index('data-panel="acl-profiles"')
assert 'id="stream-dialog"' in html and 'id="stream-list"' in html and 'id="stream-search"' in html
assert 'id="settings-page-streams"' not in html and 'id="streams-form"' not in html
-assert '/assets/streams.js' in html and '/assets/redirects.js' in html and '/assets/host404.js' in html
+assert all(f'include_str!("web/{name}.js")' in (root / 'src/admin/router.rs').read_text() for name in ('streams', 'redirects', 'host404'))
vhosts_js = read_vhosts_js(root)
assert '"force-ssl", "vhForceSsl"' in vhosts_js and 'force_ssl: yes("force-ssl")' in vhosts_js
assert 'vhForceSslNeedsHttp' in vhosts_js and 'tlsPorts.has(443)' in vhosts_js
@@ -285,7 +285,7 @@ assert 'Opt-in health decisions based on ordinary user traffic. Defaults to disa
assert '"force_ssl"' in openapi and '"forward_client_ip"' in openapi
assert '"backend_header"' in openapi and '"backend_timing_header"' in openapi
assert '"StreamMetrics"' in openapi and '"streams"' in openapi
-for marker in ['"version": "0.31.0"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']:
+for marker in ['"version": "0.31.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']:
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
@@ -980,7 +980,7 @@ for marker in ['data?.resources || {}', 'tp-memory', 'tp-inflight']:
assert marker in transform_panel_js, marker
assert 'function uptime(value)' in app_js and 'setOptionalMetric("process-uptime", status.uptime_seconds != null' in app_js
assert 'connection-uptime-footer' not in html and 'id="process-uptime"' in html
-assert 'id="panel-transform"' in html and 'data-panel="transform"' in html and '/assets/transform-panel.js' in html
+assert 'id="panel-transform"' in html and 'data-panel="transform"' in html and 'include_str!("web/transform-panel.js")' in (root / 'src/admin/router.rs').read_text()
assert '/api/transform' in transform_panel_js and 'tp-cache-hits' in transform_panel_js and 'transform-reason-list' in transform_panel_js
assert '--transform-chart:' in css and '--chart-quaternary:' in css and 'stroke: var(--chart-quaternary)' in css and 'background: var(--transform-chart)' in css
assert '.cache-transform-badge {' in css and 'border: 1px solid var(--pill-line)' in css and 'border-radius: 3px' in css
@@ -1396,7 +1396,7 @@ assert '"error_log" => {' not in admin_0241 and 'request("/api/config/error_log"
assert '"error_log"' in openapi and 'logging, error_log, forwarding' not in openapi
assert 'logging_section_persists_error_log_settings_atomically' in admin_0241 and 'error_log_is_not_a_standalone_configuration_section' in admin_0241
assert 'location.pathname === "/logging"' in settings_js_0241
-assert '"RuntimeInformation"' in openapi and '"version": "0.31.0"' in openapi
+assert '"RuntimeInformation"' in openapi and '"version": "0.31.1"' in openapi
# 0.24.1 compiler and hostname-cardinality regressions.
tls_0241 = rust_source(root, "src/tls.rs")
@@ -1635,5 +1635,5 @@ for marker_0282 in [
assert 'Zewnętrzne ścieżki plików' in (root / 'langs/pl_pl.json').read_text()
assert 'setfacl -m u:rustproxy:--x /etc/ssl/' in (root / 'docs/V0.28.2_IMPLEMENTATION.md').read_text()
-print("PASS: 0.31.0 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, external-packaging/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v9 migrations.")
+print("PASS: 0.31.1 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, external-packaging/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v9 migrations.")
print("NOT VERIFIED: Rust parsing, types, compilation, linking or runtime behavior. Run ./dev.sh test.")
diff --git a/tools/checks/dashboard_boot_0311_ui_test.mjs b/tools/checks/dashboard_boot_0311_ui_test.mjs
new file mode 100644
index 0000000..c09f2ea
--- /dev/null
+++ b/tools/checks/dashboard_boot_0311_ui_test.mjs
@@ -0,0 +1,89 @@
+#!/usr/bin/env node
+// Release-independent, real-source startup ordering and recovery contract.
+import assert from 'node:assert/strict';
+import { readFileSync } from 'node:fs';
+import { runInNewContext } from 'node:vm';
+import { resolve, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
+const read = path => readFileSync(resolve(root, path), 'utf8');
+const html = read('src/admin/web/index.html');
+const runtime = read('src/admin/web/app/runtime.js');
+const core = read('src/admin/web/app/core.js');
+const router = read('src/admin/router.rs');
+const bundle = read('tools/checks/web_assets.py');
+const version = read('Cargo.toml').match(/^version\s*=\s*"([\d.]+)"/m)?.[1];
+assert(version);
+assert.equal((html.match(/