This commit is contained in:
Mateusz Gruszczyński committed 2026-10-06 23:08:43 +02:00
1 parent 5d2aaeabe6
commit 9d6614c1b4
62 files changed
+558 -230

No files matched your search

+4
View File
@@ -33,6 +33,10 @@ or execute sudo. Check the CLI installation with `bash dev.sh acme --doctor` and
DNS uses the API/plugin appropriate to the selected provider, not a Cloudflare
Tunnel integration or a hand-written replacement for ACME.
The shipped OCI image already contains Certbot plus the Cloudflare, OVH and RFC2136
plugins in one dedicated Python environment. Host/source installations remain under
the operator's package-management policy and must provide the plugins they use.
## HTTP-01 requirements and scope
Public TCP port **80** for every requested name must reach an active plaintext
+1 -1
View File
@@ -1,4 +1,4 @@
# Administration API access - 0.26.6
# Administration API access - 0.26.7
## Sign-in boundary
+5 -3
View File
@@ -246,9 +246,11 @@ existing deduplication guard.
`GET /api/cache` reports settings, HIT/MISS/BYPASS, hot/cold counts and bytes,
configured COLD byte/object limits, promotions/demotions, errors, fill pressure, object
views and per-domain reason counters. The overview dashboard shows COLD disk occupancy
separately from RAM accounting. Hit ratio is `hits / (hits + misses)` and intentionally
excludes BYPASS traffic.
views and per-domain reason counters. The `/cache` dashboard shows HOT and COLD
occupancy against the configured object-count and byte limits with progress bars and
explicit percentages. HOT byte occupancy uses the charged/allocated HOT value rather
than logical body bytes; COLD byte occupancy uses persisted storage bytes. Hit ratio is
`hits / (hits + misses)` and intentionally excludes BYPASS traffic.
`POST /api/cache/purge` requires the existing administrator session, same-origin marker
and CSRF token. `?vhost=<id>` purges only that vhost scope; omitting it purges all cache
+11 -5
View File
@@ -42,14 +42,20 @@ explicit invalidation.
These are different clocks:
- `Age` is HTTP freshness age. It can already be thousands of seconds when RustProxy
receives an object because the origin/CDN `Date`/`Age` contributes to it.
receives an object because the origin/CDN `Date`/`Age` contributes to it. On an ordinary
fresh HIT it advances from RustProxy's stored receive time even when the backend never
supplied an `Age` header. A successful validator revalidation (backend `304`) starts a
new freshness calculation, so the next `Age` can legitimately be `0`. Requests carrying
`Cache-Control: no-cache`, `max-age=0` or `Pragma: no-cache` can therefore make repeated
validated HITs appear with `Age: 0`.
- `X-Cache-Resident-Age` is RustProxy-specific and counts whole seconds that the stored
representation has remained resident in this cache.
In 0.11.5 resident age is preserved across a successful backend 304 and across
hot-to-cold/cold-to-hot movement. An immediate first HIT can legitimately show `0`
because less than one full second has elapsed; a long-lived object should then keep
increasing rather than repeatedly resetting to zero.
Resident age is preserved across a successful backend 304 and across hot-to-cold/cold-to-hot
movement. An immediate first HIT can legitimately show `0` because less than one full second
has elapsed; a long-lived resident object should then keep increasing rather than repeatedly
resetting to zero. Use this header when the diagnostic question is cache residency rather than
HTTP freshness.
## Practical HIT test
+14
View File
@@ -7,6 +7,20 @@
# Changelog
## 0.26.7
### Backend client lifecycle and cache diagnostics
- Preserve compiled upstream clients when an active-snapshot rebuild leaves the backend origin, address policy, keepalive policy and connect timeout unchanged. This retains Hyper connection pools together with generation age, per-generation request counters and cumulative rotation totals instead of resetting those diagnostics on unrelated configuration rebuilds.
- Keep incompatible backend changes isolated: changing the origin or any client-construction input still creates a fresh upstream client and fresh generation state.
- Add a pool regression proving compatible snapshot rebuilds reuse the existing backend/client object; existing upstream-client tests continue to cover age-triggered recycling.
- Extend `/cache` with explicit HOT and COLD occupancy meters for both configured object limits and byte/disk budgets. Percentages are clamped for progress rendering while the raw counters remain visible.
### Container certificate tooling
- 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
+7 -6
View File
@@ -9,7 +9,7 @@ each Docker host, so the proxy container never needs Docker-socket access. See
Build locally with a normal OCI builder:
```bash
docker build -t rustproxy:0.22.3 .
docker build -t rustproxy:0.26.7 .
```
Persist `/var/lib/rustproxy`; it contains the SQLite database, certificates and the
@@ -22,7 +22,7 @@ docker volume create rustproxy-data
docker run --rm -d --name rustproxy \
-p 8080:8080 \
-v rustproxy-data:/var/lib/rustproxy \
rustproxy:0.22.3
rustproxy:0.26.7
docker exec rustproxy rust-proxy admin setup-token
```
@@ -32,10 +32,11 @@ RustProxy configuration and choose a certificate, then bind the administration
listener to a non-loopback address. Publish the admin port only after HTTPS is
active. Plain remote HTTP keeps the legacy explicit risk gate.
The runtime image includes Python, Certbot and OpenSSL for the built-in HTTP-01
certificate helper. Provider-specific Certbot DNS plugins are not installed in the
baseline image; extend the image with the required plugin package before using a DNS
provider.
The runtime image includes Python, OpenSSL and a dedicated Certbot environment with
all DNS plugins currently supported by RustProxy: Cloudflare, OVH and RFC2136.
HTTP-01 needs no DNS plugin. The Certbot core and all three DNS plugins are pinned to
the same release so they stay in one compatible Python environment; no image extension
is required for the built-in certificate providers.
For access-log files, prefer a writable path under `/var/lib/rustproxy` or mount a
separate writable log volume. The built-in JSON file sink is disabled by default.
+1 -1
View File
@@ -1,6 +1,6 @@
# Stored log explorer
RustProxy 0.26.6 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent.
RustProxy 0.26.7 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent.
## Separation from Live Logs
+1 -1
View File
@@ -1,4 +1,4 @@
# Installing and updating RustProxy 0.26.6
# Installing and updating RustProxy 0.26.7
The scripts prepare a Linux/systemd installation. This source archive is **not a
verified production build**: Rust/Cargo, real CA operations, live systemd startup
+2 -1
View File
@@ -15,6 +15,7 @@ This is the canonical index for the current project documentation. The authentic
- [0.25.1 release audit](V0.25.1_AUDIT.md) - historical Traffic/module-split release scope and compiler-regression guards.
- [0.25.0 specification audit](V0.25.0_AUDIT.md) - Web Server foundation point-by-point implementation status.
- [0.25.0 pre-production security audit](V0.25.0_SECURITY_PREPROD_AUDIT.md) - threat-oriented source/configuration review, fixes made in-session and mandatory runtime security gates.
- [0.26.7 implementation note](V0.26.7_IMPLEMENTATION.md) - HOT/COLD occupancy meters, backend rotation continuity and container Certbot packaging.
- [0.26.6 implementation note](V0.26.6_IMPLEMENTATION.md) - composed ACL allow semantics, certificate-form reset, backend table preferences and NPM migration helper documentation.
- [0.26.5 implementation note](V0.26.5_IMPLEMENTATION.md) - interface-scaled form/select alignment and corrected Streams listener navigation.
- [0.26.4 implementation note](V0.26.4_IMPLEMENTATION.md) - 404 drafts, consistent modal controls, compact Overview settings and idle age-based backend rotation.
@@ -66,7 +67,7 @@ This is the canonical index for the current project documentation. The authentic
## Historical or version-labelled context
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.26.6_IMPLEMENTATION.md](V0.26.6_IMPLEMENTATION.md); [V0.26.5_IMPLEMENTATION.md](V0.26.5_IMPLEMENTATION.md) is retained as the prior interface-scaling/listener-navigation milestone, [V0.26.4_IMPLEMENTATION.md](V0.26.4_IMPLEMENTATION.md) as the prior administration/backend-lifecycle milestone, [V0.26.3_IMPLEMENTATION.md](V0.26.3_IMPLEMENTATION.md) as the prior appearance/UI milestone, [V0.26.2_IMPLEMENTATION.md](V0.26.2_IMPLEMENTATION.md) as the prior Logs/UI milestone and [V0.26.1_IMPLEMENTATION.md](V0.26.1_IMPLEMENTATION.md) as the prior protocol-attribution milestone. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain historical evidence.
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.26.7_IMPLEMENTATION.md](V0.26.7_IMPLEMENTATION.md); [V0.26.6_IMPLEMENTATION.md](V0.26.6_IMPLEMENTATION.md) is retained as the prior access-policy/administration milestone; [V0.26.5_IMPLEMENTATION.md](V0.26.5_IMPLEMENTATION.md) is retained as the prior interface-scaling/listener-navigation milestone, [V0.26.4_IMPLEMENTATION.md](V0.26.4_IMPLEMENTATION.md) as the prior administration/backend-lifecycle milestone, [V0.26.3_IMPLEMENTATION.md](V0.26.3_IMPLEMENTATION.md) as the prior appearance/UI milestone, [V0.26.2_IMPLEMENTATION.md](V0.26.2_IMPLEMENTATION.md) as the prior Logs/UI milestone and [V0.26.1_IMPLEMENTATION.md](V0.26.1_IMPLEMENTATION.md) as the prior protocol-attribution milestone. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain historical evidence.
Older context documents such as [INTEGRATIONS_040](INTEGRATIONS_040.md), [MANAGEMENT](MANAGEMENT.md), [SECURITY_AUDIT](SECURITY_AUDIT.md), [ACME_RFC2136](ACME_RFC2136.md) and older version audits remain available for traceability but should not override the current functional/technical documents above.
+2 -2
View File
@@ -121,8 +121,8 @@ update and purge paths. Purge is authenticated, CSRF-protected and audited, not
public PURGE/BAN capability.
Compression is opt-in, with authenticated/private/no-transform exclusions. Response
rules cannot overwrite protected framing and identity fields. Required Age remains
on cache hits even when debug headers are disabled. Server hiding is cosmetic and
rules cannot overwrite protected framing/identity fields or the cache-controlled `Age`
header. Required Age remains on cache hits even when debug headers are disabled. Server hiding is cosmetic and
must not be considered an access-control or vulnerability mitigation substitute.
Standard public 80/443 ports are supported without globally forbidding remote
+7 -5
View File
@@ -232,11 +232,13 @@ can be streamed without pre-buffering merely to discover their size.
Accept-Encoding quality negotiation is delegated to tower-http. Text, JSON,
JavaScript, XML, SVG and wasm are eligible; already encoded, SSE, range, upgrade,
HEAD/204/304 and privacy-sensitive responses are excluded. Cookies/authorization
bypass even when an origin marks a response public. Origin no-transform/privacy
exclusions cannot be defeated by deleting a response header in the editor. Cached
identity bodies can be streamed through compression on HIT; origin encodings are
passed through without recompression.
HEAD/204/304 and privacy-sensitive responses are excluded. Authorization and arbitrary
cookie-bearing dynamic responses remain excluded. A cookie-bearing request may be
compressed only when RustProxy is already serving a shared-cache HIT accepted by cache
policy (including the configured static-cookie exception) or a local static Web Server
response. Origin `private`, `no-store`, `no-transform` and `Set-Cookie` exclusions still
win. Cached identity bodies can be streamed through compression on HIT; origin encodings
are passed through without recompression.
Vary: Accept-Encoding is added by compression; content length is removed for the
transformed body. Strong ETags are weakened, and identity-body content digests and
+13 -13
View File
@@ -1,25 +1,26 @@
# Verification report - RustProxy 0.26.6
# Verification report - RustProxy 0.26.7
## Scope
RustProxy 0.26.6 is a compatibility-preserving access-policy and administration workflow patch on top of 0.26.5. SQLite remains schema 8 and the saved proxy configuration format is unchanged.
RustProxy 0.26.7 is a compatibility-preserving cache observability, backend-client lifecycle and documentation patch on top of 0.26.6. SQLite remains schema 8 and the saved proxy configuration format is unchanged.
The release keeps backend diagnostics column/sort state synchronized with account-scoped administrator preferences, clears the certificate-creation form only after an ACME issue job reaches `ready`, documents all Python modules required by the NPM migrator, and composes explicit IP/country exceptions inside one ACL policy scope while retaining deny precedence and cumulative profile/vhost/location policies. The ACL profile UI now exposes guided block-by-default and allow-by-default modes with a live OR-policy preview, plus custom mode for mixed rules. Plain host IPs are accepted in addition to CIDRs. Linux Process diagnostics also classify the runtime environment and, only for detected LXC, replace host-oriented uptime/load collection with PID 1 age and container-visible `/proc/loadavg`.
The release adds explicit HOT/COLD cache occupancy meters on `/cache` for configured object and byte limits. It also preserves compatible upstream client instances across unrelated active-snapshot rebuilds, preventing generation age, generation request counts and cumulative `Rotations` from being reset by configuration publication. Backend changes that alter origin, address policy, keepalive settings or connect timeout still create fresh clients. The OCI/container documentation now matches the shipped certificate environment: Certbot plus the Cloudflare, OVH and RFC2136 DNS plugins are provided together. The same 0.26.7 source now also hardens cache `Age` ownership and aligns downstream compression with the static-cookie cache exception.
## Source-level regression coverage
## Regression coverage
Rust-independent contracts require the shared preference application path to reapply backend table state, the ACME UI to defer form reset until the matching issue job succeeds, the NPM migration documentation to ship and mount all three helper modules, frontend/location routing to use the composed IP/country ACL evaluator, and the ACL profile editor to retain its guided modes, preview and IP normalization path. Rust unit coverage in `src/acl/tests.rs` exercises allow-country OR allow-IP, block-country OR deny-IP, explicit deny precedence and plain IPv4/IPv6 host rules, but those Rust tests require a compiler-enabled release host.
Static source contracts require all four `/cache` occupancy meters (HOT objects, HOT bytes, COLD objects and COLD bytes) and their configured-limit inputs. Browser JavaScript syntax is checked with Node.
Source contracts additionally require the Linux environment classifier, LXC-only uptime/load override, PID 1 start-time parser and Process UI `Linux / <environment>` composition. Rust unit coverage includes LXC-vs-VM precedence, VM/host classification, LXCFS proc detection, `/proc/loadavg` parsing and PID 1 stat parsing.
The existing upstream-client unit coverage retains age-triggered rotation without user traffic. A new pool regression, `compatible_pool_rebuild_reuses_upstream_clients`, verifies that a compatible rebuild reuses the previous compiled backend/client object so rotation state remains continuous. Source contracts also require the `reusable_with` compatibility check, maintenance calls for normal/WebSocket clients and backend reuse path. These Rust tests require a compiler-enabled release host.
Existing source, security, memory, Web Server, documentation, packaging, upgrade, SQLite migration and administration JavaScript contracts remain enabled.
Cache regressions now require a stored HIT/dashboard object to advance `Age`, while compression regressions require `Age` to survive encoding and require cookie-bearing compression to stay blocked unless the internal cache/static safety marker is present. Source contracts protect the `Age` header from custom response rules and require the bounded HIT/static cookie-compression exception. The bundled compression tester no longer forces `Cache-Control: no-cache` unless `--revalidate` is requested.
A compiler follow-up also removes a redundant route-level `country_resolved = true` write reported as `unused_assignments`; the write occurred after the flag's final read and therefore does not change GeoIP or ACL behavior.
Documentation checks cover the 0.26.7 release note, Reader index, local Markdown links, cache diagnostics and container/ACME provider guidance. Packaging smoke tests verify the 0.26.7 release identity and deterministic source archive contract.
## Verification status in this workspace
| Check | Status |
|---|---|
| `node --check` for changed administration JavaScript | PASS |
| `python3 scripts/langs_check.py` | PASS |
| `python3 scripts/memory_contracts.py` | PASS |
| `python3 scripts/web_server_contracts.py` | PASS |
@@ -28,17 +29,16 @@ A compiler follow-up also removes a redundant route-level `country_resolved = tr
| `python3 scripts/source_module_contracts.py` | PASS |
| `python3 scripts/rust_module_contracts.py` | PASS |
| `python3 scripts/docs_check.py` | PASS |
| `python3 scripts/check_source.py` | PASS; explicitly reports Rust parsing/types/runtime as not verified |
| `python3 scripts/release_smoke.py` | PASS, including SQLite migrations, stopped-source upgrade and deterministic source packaging contracts |
| `python3 scripts/release_smoke.py` | PASS |
| `node scripts/web_service_ui_test.mjs` | PASS |
| `node --check` for changed administration JavaScript | PASS |
| language/OpenAPI JSON parse | PASS |
| `go test ./...` in `docker-agent` | PASS |
| `./dev.sh test` | Rust-independent gates through security contracts PASS; the wrapper hit the workspace command-time limit before its final non-Rust steps, which were then run individually and PASS. Cargo remains unavailable for the Rust stage. |
| `python3 scripts/check_source.py` | PASS; explicitly reports Rust parsing/types/runtime as not verified |
| `./dev.sh test` | Rust-independent gates PASS; wrapper exits 127 at the Rust stage because Cargo is unavailable |
| `./dev.sh check` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
| `cargo fmt --check` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
| `cargo clippy --all-targets --all-features -- -D warnings` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
| `cargo test --all-targets --all-features` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
| complete release build | BLOCKED at Rust compilation: Rust/Cargo toolchain unavailable in this workspace |
The 0.26.6 source archive must not be described as compiler-verified until the Rust-enabled release-host gates pass. Rust-independent checks do not substitute for Rust parsing, type checking, Clippy or runtime tests.
The 0.26.7 source archive must not be described as compiler-verified until the Rust-enabled release-host gates pass. Rust-independent checks do not substitute for Rust parsing, type checking, Clippy or runtime tests.
+50
View File
@@ -0,0 +1,50 @@
# RustProxy 0.26.7 implementation note
## Scope
0.26.7 is a compatibility-preserving cache observability, backend lifecycle and container documentation patch on top of 0.26.6. SQLite remains schema 8 and the saved proxy configuration document is unchanged.
## HOT/COLD cache occupancy
The authenticated `/cache` page now renders explicit occupancy meters for both cache tiers. HOT and COLD object cards compare the current entry count with `max_entries` / `cold_max_entries`; HOT memory compares charged HOT bytes with `max_bytes`; COLD disk compares persisted bytes with `cold_max_bytes`. The cards also show the configured limit and percentage so the progress bars remain interpretable without estimating from the visual fill alone.
The HOT byte meter intentionally uses allocated/charged HOT bytes, not only logical body length. This matches the budget enforced by cache admission. COLD uses persisted storage bytes.
## Backend rotation continuity
Age-based recycling continues to run from the backend maintenance loop and request-count recycling remains request-driven. The isolated client rotation logic already advanced generation and rotation counters correctly.
The release fixes a separate observability/lifecycle problem: rebuilding an otherwise compatible active routing snapshot created a new upstream client object, which restarted generation age, request count and rotation totals at zero. Pool rebuilds now retain the previous compiled backend when both ordinary and WebSocket upstream clients have exactly the same client factory: backend definition, address policy, keepalive settings, protocol mode and connect timeout. Health observations retain their existing compatibility rules independently.
A real backend-policy change still creates a fresh client. This avoids retaining stale DNS/listener safety policy, TLS identity, keepalive or timeout behavior while keeping long-running rotation diagnostics continuous through unrelated snapshot publications.
## Container certificate tooling and documentation
The OCI runtime image now creates a dedicated `/opt/rustproxy-certbot` Python environment and pins Certbot plus the supported Cloudflare, OVH and RFC2136 DNS plugins to the same release. The image therefore supports every certificate provider exposed by RustProxy without an extra image-extension step. Container documentation was refreshed to remove the obsolete statement that DNS plugins are absent and to use the current release tag in examples.
Current cache, vhost/backend and installation documentation was reviewed alongside the release notes so the dashboard limits, rotation semantics and container prerequisites describe the implemented behavior.
## Compatibility
- SQLite schema: 8, unchanged.
- Saved configuration format: unchanged.
- No migration is required from 0.26.6.
- Backend rotation counters intentionally restart when the backend client policy itself changes.
## Cache Age and downstream compression follow-up
The cache freshness clock remains RFC-oriented: ordinary stored HITs advance `Age` from
`initial_age + resident elapsed time`, while a successful validator revalidation starts a
new freshness calculation. `X-Cache-Resident-Age` remains the monotonic residency clock
across revalidation and tier movement. `Age` is now protected from generic response-header
rules, and regression coverage explicitly checks that both the wire HIT header and `/cache`
object age advance from the stored clock.
Compression still refuses Authorization, private/no-store/no-transform, Set-Cookie, ranges,
SSE, upgrades and already encoded responses. Cookie-bearing requests now have one bounded
exception: a response can be compressed when it is already a shared-cache HIT accepted by
cache policy or a local static Web Server response. This aligns the static-cookie cache
exception with downstream compression without enabling compression for arbitrary dynamic
cookie responses. The bundled compression tester no longer sends `Cache-Control: no-cache`
by default because that header forces validator revalidation and can make cache `Age` appear
to stay at zero; `--revalidate` retains the old diagnostic behavior explicitly.
+4
View File
@@ -206,6 +206,10 @@ it does not reload the vhost, purge cache or intentionally abort active exchange
request counter is per backend client generation, not per TCP connection. Setting either
recycle value to `0` disables that trigger independently. `http1_max_buffer_bytes` bounds
Hyper's adaptive upstream HTTP/1 read buffer per connection; it does not apply to HTTP/2.
Compatible backend clients are retained across unrelated runtime snapshot rebuilds, so
generation age, request count and rotation totals remain continuous. A changed backend
origin, address policy, keepalive settings or connect timeout creates a fresh client
generation state instead of reusing an incompatible pool.
Disabling reuse sets the idle pool limit to zero and disables keepalive probes. The idle
limit is not a separate per-backend active-request cap; global connection/exchange bounds
still apply. HTTP health probes use separate no-reuse HTTP/1.1 connections.