19 KiB
Verification report - RustProxy 0.25.5
Scope
RustProxy 0.25.5 is a patch release for dashboard consistency and observability: Revision History used a separate rounded modal style, Cloudflare-backed vhosts resolved Traffic country from local GeoIP without consulting Cloudflare's verified country header, Prometheus did not expose the bounded Traffic dimensions or an explicit built-in Web Server serving-source view, the mobile dashboard rendered the full section list permanently across the top of the page, and the dashboard lacked a compact About surface. The top bar now exposes project About information and uses the full Documentation label. The patch keeps SQLite schema 8 and the existing configuration format.
Source contracts reviewed
- Revision History now uses the shared dashboard dialog rule with square corners; the dedicated history dialog retains only layout dimensions/overflow and no longer duplicates color, background, border, shadow or backdrop appearance declarations. Calendar/diff sub-panels no longer add local rounded corners.
- Dashboard widths up to 850 px now hide the main section list behind an accessible
aria-expandedMenu control. Opening shows a two-column section grid; selecting a section, pressing Escape or clicking outside closes it. Desktop navigation keeps the existing sidebar behavior. - The authenticated top bar exposes an About dialog with author Mateusz Griszczynski, an Open Source notice and the canonical Git repository link; the documentation action is labeled Documentation. The About dialog uses the same square-corner modal styling and adds no new server route.
- Cloudflare client-IP mode still requires the direct transport peer to match the built-in Cloudflare edge CIDRs. After a successful verified
CF-Connecting-IPoverride, one valid two-letterCF-IPCountryvalue is normalized and preferred for country policy/logging/Traffic; missing, duplicate, malformed andXXvalues fall back to local GeoIP of the verified effective client IP. - Prometheus
include_trafficdefaults on and exports bounded per-vhost Traffic totals, country top values, fixed User-Agent families, fixed method families, status classes, cache outcomes, serving source and configured route IDs. Raw effective client IPs, raw request paths and full User-Agent strings remain excluded from exporter labels. - Traffic serving source is recorded as one fixed value (
web_server,reverse_proxy,local_response,unresolved) and the exporter adds configured Web Service inventory plus vhost/route source metadata. This makes pure Web Server traffic explicit while preserving the existing shared vhost request/status/byte/duration counters. - The bundled Grafana dashboard adds a Traffic vhost variable and panels for Web Server request rate, queue drops, countries, User-Agent families, source rates, methods, status classes, cache outcomes, capture-state gauges and configured-route traffic.
- Vhost and location
method_policyvalues validate and normalize HTTP methods; host and selected-location policies are cumulative and deny with 405 before upstream work. Allow-list denial includesAllow. - Custom locations can select an
acl_profile_idand add inline IP ACL plus country access policy. Profile and inline rules are compiled with the route and checked before authentication/backend selection; a location cannot weaken a vhost denial. - Every observed exchange receives a fresh 64-hex SHA-256
request_id, overwrites inboundX-Request-IDbefore forwarding, and reuses the same value for diagnostics/logging. Custom header rules continue to protectX-Request-ID. - SQLite schema 8 stores revision/time/actor/document in bounded
configuration_history; successful CAS writes record history transactionally and retain the newest 512 revisions. The session-protected/api/config/historyAPI supports metadata listing and one-revision detail with the previous retained document. POST /api/config/history/restoreaccepts an older retained revision plus the currently observedexpected_revision. It rejects stale or non-older targets, reloads the retained document and passes it through the same startup-lock, certificate-removal, compile, cache-invalidation, persistence and hot-publish path as a normal configuration update.- Restore persistence uses the existing storage CAS and appends the restored document as the next monotonic revision. The same transaction records
config.restorewithsource_revision=<old> revision=<new>; the selected historical row is never overwritten. - The Manual config history UI groups retained revisions by month/day, lists revisions for a selected day and renders a bounded recursive path-level before/after diff using text-only DOM operations. An older selected revision exposes a confirmation-gated restore action; success refreshes the active envelope and history.
Config.web_servicesis a separate persisted collection; vhost/locationsourcedefaults to Reverse Proxy for old documents and Web Server stores onlyweb_service_id.- Full configuration compilation validates Web Service IDs, absolute/existing document roots, index names, duplicate IDs and every active Web Service reference. A pure static vhost does not require a backend pool; mixed Web Server and Reverse Proxy locations do.
- Web Server dispatch occurs after normal frontend routing, ACL and Basic/Tinyauth authorization and before backend selection/DNS/cache/upstream work. Static responses continue through the existing custom error-page, response-header/security, compression and transport pipeline.
- Static files use streamed Tokio file bodies with explicit lengths rather than whole-file buffering.
- Files support metadata-based ETag/Last-Modified, If-None-Match/If-Modified-Since, one Range/If-Range and 304/206/416 behavior.
- Directory requests redirect to a slash path, then search the optional ordered index list (which may be empty), then optionally render an HTML-escaped and URL-encoded RustProxy-styled listing without exposing filesystem paths.
- Root directory listing opens a fresh directory file description per request rather than a
dupsharing the persistent root descriptor offset; a regression test requests the same listing twice. Nested directories independently apply index lookup first and listing fallback only when no index exists. Generated listings sendCache-Control: no-store. - A directory with no matching index and directory listing disabled returns the built-in generic 403 page (or the configured vhost/global 403 replacement in the outer response pass), not a Web Server implementation-detail string. HEAD keeps an empty body.
- Web Services support up to 64 normalized MIME overrides, a validated fallback MIME header and an optional validated default
Cache-Controlheader. Static files remain backed by the filesystem/OS page cache; existing vhost/location response-header rules run later and can override the source-level cache policy. - Web Services additionally support ordered
ext:/mime:Cache-Control rules, validated fixed response headers, orderedtry_files, regex internal/redirect rewrites, precompressed.br/.gzsidecars and attachment extensions. Protected framing/transport headers cannot be overridden by the source header map. - Optional
.htaccesscompatibility compiles a bounded root/subdirectory set at Web Service Apply time rather than parsing per request. It supports common RewriteRule/RewriteCond front controllers, DirectoryIndex/Indexes, AddType, Header, Expires, PHP AddHandler and simple Require all granted/denied. Unsupported access/handler scopes fail closed,.ht*control files are never served, and PHP AddHandler requires PHP-FPM. - Web Service presets are UI-only generators: they populate ordinary configuration fields and do not persist a hidden CMS mode. ID/name/document root and PHP-FPM endpoint/tuning remain operator-owned. Optional hardening is persisted explicitly and defaults off for compatibility.
block_common_attacksrejects TRACE/CONNECT/TRACK, sensitive dot/control/backup paths and a bounded set of high-confidence exploit probes; it also suppresses FastCGIHTTP_PROXYderivation from the inboundProxyheader. It does not claim generic WAF/SQLi/XSS filtering.strip_unnecessary_headersremoves common source/PHP fingerprint headers after Web Service and.htaccessresponse handling while leaving the later vhost/location response-header pipeline authoritative.- Optional PHP handling executes configured extensions through a bounded FastCGI connector over Unix socket or loopback TCP. PHP concurrency uses a fixed semaphore without an unbounded wait queue, request/response bodies use bounded streaming, and connection failure cannot fall back to serving PHP source. Advanced configuration adds Authorization forwarding control, a streaming request-body ceiling, bounded CGI response headers/stderr, trusted custom FastCGI parameters and validated
PHP_VALUEoverrides that cannot replace core CGI/HTTP fields orPHP_ADMIN_VALUE. - Web Service symlink policy defaults to
deny/O_NOFOLLOW; Linux-onlyinside_rootverifies opened descriptors through/proc/self/fdagainst the canonical root. PHP-FPM is intentionally limited todenybecause the external worker reopensSCRIPT_FILENAME. - Routing rejects malformed/ambiguous encoded paths before filesystem lookup. Unix filesystem lookup walks from an opened root descriptor;
denyusesopenat+O_NOFOLLOW, while Linux-onlyinside_rootverifies every opened descriptor against the canonical root. PHP-FPM requiresdeny. The secure Web Server compile path remains Unix-only. - No second URL router, custom-error subsystem or object cache was introduced. Reverse Proxy remains the default source for existing configuration.
- Dashboard CRUD, source selectors, English/Polish strings and OpenAPI schemas expose the new model.
- Web Service index files are optional (0..16). An empty list skips index lookup, allowing directory-listing-only services without synthetic index names.
- Generated directory listings use a self-contained RustProxy panel-style layout (matching palette/typography, responsive light/dark treatment and RustProxy footer) while preserving HTML escaping, URL encoding and filesystem-path secrecy.
- Pure Web Server vhosts no longer present backend diagnostics as though an origin pool existed; their Backends summary identifies
Web Server, while mixed static/proxy vhosts retain backend diagnostics. - Status payloads expose fixed-cardinality per-vhost metrics independently of backend pools. The Vhosts dashboard uses those counters for Web Server-only request/active/error values, EWMA latency and byte-rate traffic while retaining backend health details for reverse-proxy sources.
- Logging settings are split into Outputs, JSON format and Error log sub-tabs inside one card, matching the Cache subsection layout.
route,cache_effective_enabledandtransform_effective_enabledare accepted by the same backend validator that saves custom JSON mappings, and the regression test validates then renders those fields. - Structured logging exposes
user_agentandcountry. User-Agent capture defaults on; country capture defaults off. Country resolution is skipped unless capture or an existing country ACL/rate-limit needs it; verified Cloudflare mode prefers trustedCF-IPCountry, otherwise local GeoIP is used. - The authenticated Traffic workspace uses a bounded 2048-sample non-blocking queue, at most 64 vhost buckets and 32-entry approximate top tables for countries, User-Agents, effective client IPs, paths, routes, methods, status classes and cache outcomes. It never writes per-request analytics to SQLite. The Traffic UI is split into Overview, Clients, Requests and Delivery sub-tabs. Countries/methods/status/cache use donut summaries; User-Agent, effective client IP, path, route and request/response byte volume use bounded horizontal bar charts from the same in-memory TOP state.
- Sustained-traffic memory maintenance calls
malloc_trim(0)every 60 seconds on Linux/glibc to release already-free allocator pages and trims HOT objects only when above the configured waterline and idle past the configured age. It does not periodically reload vhosts, purge active objects or reset bounded upstream keep-alive pools. - Cache compatibility currently includes vhost configuration, so a cache-affecting vhost save can purge cache during snapshot publication; replacing the compiled snapshot also retires its upstream clients after outstanding references drain. These mechanisms explain why a manual vhost save can visibly reduce RSS without implying that reloads are required for steady-state operation.
- The Web Server list now uses the same vhost card/detail-cell visual structure as the Vhosts list. Reverse Proxy vhost creation reports a dedicated “at least one backend” validation error instead of the generic paths/ACL/headers JSON error.
- Web Service cards use only the shared
el(...)DOM helper; a regression contract rejects the previous undefinedtext(...)call that aborted list rendering. Authenticated-shell startup now verifies/api/auth/sessionfirst and never reveals the legacy embedded sign-in form for non-authentication JavaScript/API failures, preventing F5 from falsely presenting the user as logged out. - The Web Server UI module performs a catch-up render when it registers after
app.js; this closes the race where a very fast/api/configresponse could finish beforeweb-server.jsexisted and leave the Web Service list blank until a later render. - The Web Service editor now builds its configuration tabs with the same
vh-tabs+quietbutton pattern and active/ARIA state handling as the Vhost editor; the source contract also checks this shared modal-tab pattern and invalid-field navigation switches to the owning tab. - Static dispatch clones only
Method,HeaderMapandUribefore awaiting the filesystem source; it does not hold&Request<Body>across an await, keeping the Axum 0.8 fallback handler futureSend. This addresses the reportedHandler<_, _>/ E0277 failure at.fallback(forward). /docsis an authenticated documentation surface with a Markdown Reader, searchable embedded docs bundle and a separate API/OpenAPI tab. Reader Markdown files are directinclude_str!compiler inputs and no longer depend on a generatedOUT_DIR/docs-content.json, preventing clean/incremental builds from failing on a missing documentation artifact.- Documentation cleanup keeps README and the active ROADMAP current-only; operator release history, developer-level changelog history and completed roadmap milestones are separated into CHANGELOG/audit/archive documents.
Implemented automated coverage
Rust unit tests in src/web_server/tests.rs cover GET/HEAD/index/slash redirect, MIME/length, validators, valid/invalid range handling, If-Range, escaped/repeated directory listings, per-directory index-before-listing fallback, generic 403/HEAD behavior, MIME overrides/default MIME, cache rules, source headers, try_files, rewrite redirect/internal behavior, .htaccess WordPress-style front-controller routing, nested DirectoryIndex/MIME/Header/Expires behavior, strict-mode rejection and PHP-source protection, hardening probe/method blocking and fingerprint-header stripping, precompressed representations/ranges, download headers, PHP FastCGI fixture execution/failure-source-disclosure behavior, request-body 413 enforcement, custom FastCGI/PHP_VALUE validation and reserved-parameter rejection, direct traversal rejection and Unix/Linux symlink policy. src/traffic/tests.rs includes bounded-table, effective-client-IP view and fixed Prometheus User-Agent/method family coverage. Configuration tests cover pure static vhosts without a pool, mixed Web Server/Reverse Proxy routing, reuse of one Web Service by multiple vhosts, unknown service references and backward-compatible default source behavior. Vhost tests cover method-policy normalization/allow/block behavior, database tests cover retained configuration revision documents, actor metadata and previous-revision lookup, and admin tests cover restoring revision 1 as revision 3 while preserving the previous document and emitting config.restore. ACL tests include encoded traversal/separator/malformed-encoding cases.
Rust-independent release checks additionally validate UI/JSON/language/source contracts and reproducible packaging.
During the prior 0.25.4 compiler gate, an external build exposed one module-privacy error in the history split (CONFIG_HISTORY_CAPACITY was private to db::history) and one unused country_resolved assignment in the location GeoIP branch. Both were corrected before the 0.25.5 baseline: the capacity is exposed to its parent module with pub(super), referenced through history::CONFIG_HISTORY_CAPACITY, and the dead assignment is removed. This workspace still has no Rust/Cargo toolchain, so compiler confirmation of the 0.25.5 tree remains a release-host gate.
Modular source-tree verification
python3 scripts/rust_module_contracts.py: PASS. Every repository.rsfile is at most 500 lines.python3 scripts/source_module_contracts.py: PASS. Dashboard JavaScript, Docker Agent Go implementation and operational deploy/NPM-migration Python modules are at most 500 lines; Docker tests remain in_test.go, and browser split boundaries/public bundles parse with Node when available.- Oversized
app.js/vhosts.jssource is split while the public/assets/app.jsand/assets/vhosts.jsURLs remain stable; the Docker Agent is split into model/Docker/network/certificate/server/entry-point files. - Rust unit-test bodies are isolated under
tests.rs/tests/; implementation files contain only guarded test includes/declarations. - All
include!("*.rs")targets resolve, split fragments have balanced delimiters, and no fragment ends in a dangling Rust attribute or doc comment. - Regression guards cover the Web Server split errors previously observed around
#[derive],htaccess,server_protocolandanyhow::Context. - This is a compiler-independent structural check; Cargo parsing/type checking remains a release-host requirement when the Rust toolchain is unavailable in the packaging environment.
Verification status in this workspace
| Check | Status |
|---|---|
python3 scripts/langs_check.py |
PASS |
python3 scripts/docs_check.py |
PASS |
python3 scripts/memory_contracts.py |
PASS |
python3 scripts/web_server_contracts.py |
PASS |
Browser JavaScript node --check |
PASS for split app source, vhost split boundaries/public bundle, Settings, Docs Reader, Web Server and Traffic |
| OpenAPI/language JSON parse | PASS |
Shell bash -n |
PASS in source/release checks |
go test ./... in docker-agent |
PASS |
| Python source parsing/contract checks | PASS |
python3 scripts/check_source.py |
PASS |
python3 scripts/release_smoke.py |
PASS |
./dev.sh test Rust-independent phases |
PASS; Rust phase BLOCKED because Cargo is unavailable |
cargo fmt --all / ./dev.sh fmt |
BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
cargo check --locked --all-targets / ./dev.sh check |
BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
cargo test --locked --all-targets / Rust phase of ./dev.sh test |
BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
| Live H1/H2/H3 Web Server integration and large-file acceptance | release-host acceptance gate |
The source package therefore has static/source-contract verification here, but compiler-enabled and live transport acceptance must still be run on the release host before production release.