v0.28.0
This commit is contained in:
1 parent
c24bc4279b
commit
3b3f0db377
84 files changed
+2686
-392
No files matched your search
+2
-2
@@ -96,7 +96,7 @@ snapshot publication finish before a successful response. Disconnecting the call
|
||||
after the detached writer starts does not cancel an accepted write.
|
||||
The document limit is 256 KiB, with an additional 1024-byte request-envelope allowance.
|
||||
For ordinary Settings saves prefer `PUT /api/config/{section}`. Supported sections are
|
||||
`listeners`, `response`, `cache`, `logging`, `forwarding`, `headers`, `prometheus`,
|
||||
`listeners`, `response`, `cache`, `logging`, `forwarding`, `headers`, `prometheus`, `geoip`,
|
||||
`administration`, `tls`, `vhosts`, `web-services`, `redirects`, `not_found_hosts`, `streams`, `acl-profiles` and `host-policies`.
|
||||
Each section write still uses revision CAS, full validation and atomic persistence.
|
||||
Only a change to the derived cache-compatibility identity purges hot/cold cache;
|
||||
@@ -105,7 +105,7 @@ unrelated section saves do not.
|
||||
## Configuration fields
|
||||
|
||||
The canonical document uses `vhosts`, `web_services`, `redirects`, `listeners`, `administration`, `tls`, `logging`,
|
||||
`prometheus`, `cache`, `compression`, `server_header`, `headers`, `host_policies` and other defaulted
|
||||
`prometheus`, `geoip_updates`, `cache`, `compression`, `server_header`, `headers`, `host_policies` and other defaulted
|
||||
settings. Unknown fields are rejected. `backends`/`routes` at the top level are
|
||||
input-only legacy migration fields, omitted from saved canonical documents.
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Administration API access - 0.27.3
|
||||
# Administration API access - 0.28.0
|
||||
|
||||
## Sign-in boundary
|
||||
|
||||
|
||||
+3
-2
@@ -55,6 +55,7 @@ behavior exactly.
|
||||
| expose_hits | false | Emit per-object `X-Cache-Hits` |
|
||||
| expose_cache_status | false | Emit standardized `Cache-Status` (RFC 9211) |
|
||||
| expose_resident_age | false | Emit `X-Cache-Resident-Age` on reused stored responses |
|
||||
| via_header | `RustProxy` | Append this `Via` response value; `null` disables it |
|
||||
| collapse_enabled | true | Collapse concurrent foreground fills for the same primary cache key |
|
||||
| collapse_wait_timeout_ms | 1500 | 10..60000 ms follower wait before fallback |
|
||||
| collapse_timeout_fallback | origin | `origin` or `stale_if_available` after follower timeout |
|
||||
@@ -148,7 +149,7 @@ Cache invalidation remains explicit:
|
||||
- BAN records a bounded TTL rule without synchronously scanning the entire store; objects created before a matching active BAN are rejected during lookup/insertion and are removed later by bounded GC,
|
||||
- new fills created after a BAN rule are allowed, so BAN invalidates the old representation set rather than permanently blocking a URL,
|
||||
- regex rules and prefix `/` are treated as broad operations and require explicit confirmation,
|
||||
- only cache-compatibility changes (cache policy, configured header/origin semantics or vhosts) advance the normal cache generation/revision; unrelated logging/TLS/admin/metrics/response-presentation saves retain objects.
|
||||
- only cache-compatibility changes advance the normal cache generation/revision; unrelated logging/TLS/admin/metrics/response-presentation saves retain objects, and cache request-bypass edits plus response-only `X-Cache`/`Cache-Status`/resident-age/`Via` diagnostics also apply without purging stored objects.
|
||||
|
||||
There is no public unauthenticated PURGE/BAN endpoint. If an application changes a GET
|
||||
resource after POST/PUT/PATCH/DELETE, use origin freshness/validators or the authenticated
|
||||
@@ -272,7 +273,7 @@ bytes. Purge-all and purge-vhost remain the full-clear mechanisms; selective inv
|
||||
is deliberately separate.
|
||||
|
||||
`X-Cache`, `X-Cache-Hits`, `Cache-Status`, `X-Cache-Reason` and
|
||||
`X-Cache-Resident-Age` are optional diagnostics. `Cache-Status` is the standardized
|
||||
`X-Cache-Resident-Age` are optional diagnostics. `Via` is a separate configurable response header, enabled by default with value `RustProxy`; RustProxy appends it instead of deleting an upstream `Via` chain. `Cache-Status` is the standardized
|
||||
RFC 9211 field name; RustProxy does not rename it to the legacy/custom `X-Cache-Status`.
|
||||
HTTP/2 and HTTP/3 serialize field names in lowercase, so `cache-status:` on the wire is
|
||||
normal. When the cached representation was produced by the transform pipeline,
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ each Docker host, so the proxy container never needs Docker-socket access. See
|
||||
Build locally with a normal OCI builder:
|
||||
|
||||
```bash
|
||||
docker build -t rustproxy:0.27.3 .
|
||||
docker build -t rustproxy:0.28.0 .
|
||||
```
|
||||
|
||||
Persist `/var/lib/rustproxy`; it contains the SQLite database, certificates and the
|
||||
@@ -22,7 +22,7 @@ docker volume create rustproxy-data
|
||||
docker run --rm -d --name rustproxy \
|
||||
-p 8080:8080 \
|
||||
-v rustproxy-data:/var/lib/rustproxy \
|
||||
rustproxy:0.27.3
|
||||
rustproxy:0.28.0
|
||||
|
||||
docker exec rustproxy rust-proxy admin setup-token
|
||||
```
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Stored log explorer
|
||||
|
||||
RustProxy 0.27.3 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.28.0 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
|
||||
|
||||
|
||||
+18
-12
@@ -1,4 +1,4 @@
|
||||
# Installing and updating RustProxy 0.27.3
|
||||
# Installing and updating RustProxy 0.28.0
|
||||
|
||||
The scripts prepare a Linux/systemd installation. This source archive is **not a
|
||||
verified production build**: Rust/Cargo, real CA operations, live systemd startup
|
||||
@@ -67,12 +67,15 @@ precompiled binary in the archive. Set `--binary` explicitly when using a custom
|
||||
|
||||
## First installation
|
||||
|
||||
The normal first install also downloads `user-country-ipv4.csv` and
|
||||
`user-country-ipv6.csv` from the `sapics/ip-location-db` latest release over HTTPS and
|
||||
verifies each file against the publisher SHA-256 release before writing it privately to
|
||||
`DATA_DIR/geoip`. This enables country access/rate rules. Use `--skip-geoip` only when
|
||||
you intentionally do not want the database; allow-only country policies then fail
|
||||
closed for unresolved clients. Updates preserve the installed database.
|
||||
`install.sh` also downloads the direct `user-country-ipv4.csv` and `user-country-ipv6.csv`
|
||||
assets from the `sapics/ip-location-db` latest release over HTTPS, validates their CSV
|
||||
structure and writes them privately to `DATA_DIR/geoip`. Package/plugin installs may omit
|
||||
that packaging-time download: after all listeners bind successfully, RustProxy starts an
|
||||
enabled-by-default GeoIP worker, performs an immediate background refresh and then checks
|
||||
every 24 hours by default. Country allow-only policies continue to fail closed until a valid
|
||||
database is available. Settings can use separate IPv4/IPv6 CSV files or one native MaxMind
|
||||
MMDB file; direct source URLs, optional per-file SHA-256 URLs, maximum file size and interval
|
||||
are configurable under **Settings -> GeoIP updates**.
|
||||
|
||||
|
||||
```bash
|
||||
@@ -225,11 +228,14 @@ bash update.sh --plan --binary target/release/rust-proxy
|
||||
sudo bash update.sh --binary target/release/rust-proxy
|
||||
```
|
||||
|
||||
Before stopping the service, the updater fetches the current publisher SHA-256
|
||||
files for the `user-country` GeoIP IPv4/IPv6 assets. Matching local files are left
|
||||
untouched; stale or missing assets are staged, verified and published only after
|
||||
the private-data backup exists. Use `--skip-geoip` only when intentionally accepting
|
||||
a stale/missing country database (for example during an upstream outage).
|
||||
Before stopping the service, the updater downloads the current direct `user-country`
|
||||
IPv4/IPv6 CSV assets, validates their structure and compares their local SHA-256 with the
|
||||
installed files. Unchanged files are left untouched; changed or missing assets are staged
|
||||
and published only after the private-data backup exists. This intentionally avoids requiring
|
||||
a separately moving publisher-checksum release. The manual updater and the in-process GeoIP
|
||||
worker use the same `.geoip-update.lock`, so they cannot publish concurrently. Use
|
||||
`--skip-geoip` only when intentionally accepting a stale/missing country database (for
|
||||
example during an upstream outage).
|
||||
|
||||
Use the original `--prefix` for a non-default installation. Other startup/account/
|
||||
data settings are read from root-owned `install.json`; update does not silently
|
||||
|
||||
+3
-2
@@ -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.28.0 implementation note](V0.28.0_IMPLEMENTATION.md) - manual/hot-reloaded GeoIP, listener status UX, cache Via/narrow invalidation and header-rule layout.
|
||||
- [0.27.3 implementation note](V0.27.3_IMPLEMENTATION.md) - HTTP/3 HEAD cache parity, COLD/rate-limit memory reduction, backup scope and package-path update.
|
||||
- [0.27.2 implementation note](V0.27.2_IMPLEMENTATION.md) - managed Certbot relocation/renewal repair.
|
||||
- [0.27.1 implementation note](V0.27.1_IMPLEMENTATION.md) - first-admin command, managed/fallback certificate runtime and account-scoped route-list sorting.
|
||||
@@ -71,12 +72,12 @@ This is the canonical index for the current project documentation. The authentic
|
||||
|
||||
## Historical or version-labelled context
|
||||
|
||||
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.27.3_IMPLEMENTATION.md](V0.27.3_IMPLEMENTATION.md); [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 [V0.28.0_IMPLEMENTATION.md](V0.28.0_IMPLEMENTATION.md); [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.
|
||||
|
||||
The pre-cleanup developer-level changelog is retained as [CHANGELOG_TECHNICAL_ARCHIVE](CHANGELOG_TECHNICAL_ARCHIVE.md) when low-level historical detail is needed. The root `CHANGELOG.md` remains the operator-facing release record.
|
||||
Completed roadmap history is retained separately as [ROADMAP_HISTORY](ROADMAP_HISTORY.md); the root `ROADMAP.md` contains only the active 0.25 milestone, release gate and future direction.
|
||||
Completed roadmap history is retained separately as [ROADMAP_HISTORY](ROADMAP_HISTORY.md); the root `ROADMAP.md` contains the current release scope and future direction.
|
||||
|
||||
## Maintenance rules
|
||||
|
||||
|
||||
@@ -52,6 +52,31 @@ The historical `/configuration` browser route is retained only as a compatibilit
|
||||
opens that Settings block; it is no longer a primary-navigation item. Prefer the structured
|
||||
section editors for routine changes so unrelated sections are not republished.
|
||||
|
||||
## GeoIP updates
|
||||
|
||||
**Settings -> GeoIP updates** controls the background country-database refresh and is enabled by default. The worker starts only after all server sockets have bound successfully, attempts the first refresh immediately, and then refreshes every 24 hours by default. Sources are direct HTTPS file URLs rather than coupled release/checksum base URLs. Publisher SHA-256 verification is optional per file; every downloaded payload is still size-bounded, streamed to a private staging file and parsed as the selected database format before publication.
|
||||
|
||||
Two formats are supported. **CSV ranges** uses separate IPv4 and IPv6 files and defaults to the direct `sapics/ip-location-db` `user-country-ipv4.csv` and `user-country-ipv6.csv` assets. **MaxMind DB (MMDB)** uses one `country.mmdb` file for both address families and defaults to the combined sapics `user-country.mmdb` asset. Standard MaxMind country databases are read from `country.iso_code`, then `registered_country.iso_code`; custom country MMDBs may expose top-level `country_code` or a root two-letter country string. To manage a private MMDB outside the updater, place it at `<data-dir>/geoip/country.mmdb`, select MMDB, disable automatic updates and leave the MMDB source URL empty; **Download now** then validates/reloads the local file instead of fetching it.
|
||||
|
||||
When a checksum URL is empty, RustProxy deliberately skips publisher-checksum comparison. It still computes a local SHA-256 for the validated download and compares it with the installed file, so unchanged content is not republished or hot-reloaded. This avoids coupling a moving `latest` data URL to a separately moving checksum release. If a checksum URL is supplied, a mismatch rejects the staged file. The configured maximum is 1 MiB..256 MiB and the interval is 60 seconds..31 days. Failures are warnings only; proxy traffic continues and the worker retries. Manual UI refresh, the background worker and managed upgrades share `.geoip-update.lock`. After publication the `ArcSwap` database is replaced atomically: requests already holding a snapshot may finish on it, while subsequent country ACL/rate-limit/logging lookups and status/metrics reads use the new database without a restart.
|
||||
|
||||
```json
|
||||
{
|
||||
"geoip_updates": {
|
||||
"enabled": true,
|
||||
"format": "csv_pair",
|
||||
"ipv4_url": "https://github.com/sapics/ip-location-db/releases/download/latest/user-country-ipv4.csv",
|
||||
"ipv6_url": "https://github.com/sapics/ip-location-db/releases/download/latest/user-country-ipv6.csv",
|
||||
"mmdb_url": "https://github.com/sapics/ip-location-db/releases/download/latest/user-country.mmdb",
|
||||
"ipv4_checksum_url": null,
|
||||
"ipv6_checksum_url": null,
|
||||
"mmdb_checksum_url": null,
|
||||
"max_bytes": 268435456,
|
||||
"interval_seconds": 86400
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Administration HTTPS
|
||||
|
||||
`administration` is separate from public proxy listeners. It defaults to the startup
|
||||
|
||||
+17
-15
@@ -1,38 +1,40 @@
|
||||
# Verification report - RustProxy 0.27.3
|
||||
# Verification report - RustProxy 0.28.0
|
||||
|
||||
## Scope
|
||||
|
||||
RustProxy 0.27.3 fixes HTTP/3 `HEAD` cache eligibility, reduces persistent COLD-cache and rate-limit/IP memory retention, excludes rebuildable managed Python environments from backups, and updates the documented installation path. SQLite schema 8, the saved configuration format and COLD format 3 remain unchanged.
|
||||
RustProxy 0.28.0 adds authenticated on-demand GeoIP refresh through the same staging/lock path as the automatic worker, replaces coupled release/checksum bases with direct per-database sources, makes publisher SHA-256 URLs optional, supports either separate IPv4/IPv6 CSV ranges or one native MaxMind DB (`.mmdb`), and verifies hot replacement of the shared `ArcSwap` database. It also improves active-listener presentation with running `H1`/`H2`/`H2C`/`H3` badges, reports the completed ACME Python/Certbot probe in the INFO log instead of printing speculative startup state, adds configurable cache `Via` metadata, narrows cache compatibility invalidation for bypass/response-only diagnostics, and fixes the vhost Security header-rule Delete/Operation layout. It retains the 0.27.3 restore/listener/WebSocket/update-check/automatic-GeoIP fixes.
|
||||
|
||||
The H3 repair is intentionally narrow: cache policy already accepts both `GET` and `HEAD`; the forwarding path now performs the existing bounded empty-body probe for both methods. This prevents an initially unpolled HTTP/3 receive body from being mistaken for request content.
|
||||
SQLite schema 8 and COLD cache format 3 remain unchanged. Older saved configurations remain compatible: legacy GeoIP `release_url`/`checksum_url` values are accepted and migrated into the direct-source model, while the old moving default checksum source is deliberately treated as unpinned and therefore optional. `geoip_updates` and the new `cache.via_header` are serde-defaulted when absent. The cache `Via` value defaults to `RustProxy`; `null` disables it.
|
||||
|
||||
The COLD index no longer stores full response headers or origin validator values for every disk object. These are loaded and validated from the existing metadata JSON only on an actual COLD read. The in-memory SHA-256 is 32 bytes and the map-owned key is not duplicated inside `ColdEntry`. Rate limiting now groups IP buckets below one vhost string, sweeps expired fixed-window buckets during requests and periodic runtime maintenance, and shrinks sparse hash tables before allocator trimming.
|
||||
The GeoIP request path reads `state.geoip.load()` when a frontend/location policy, country rate limit or country log capture needs a country. Manual and automatic updates both publish through the same `ArcSwap<Database>`. The regression test keeps an old `Arc`, publishes a replacement database and verifies that the old snapshot remains valid while subsequent slot loads return the new country mapping. CSV mode validates both IP families and ordering; MMDB mode validates MaxMind DB v2 structure and supports standard `country.iso_code`, `registered_country.iso_code`, sapics-style top-level `country_code`, and a two-letter root string used by simple custom databases.
|
||||
|
||||
Compression was reviewed but its wire behavior was not changed: it remains streaming through `tower_http`, uses `CompressionLevel::Fastest`, and does not aggregate a complete response body before encoding. Its primary RAM cost is therefore per concurrently active compressed response/codec state, not long-lived retained objects.
|
||||
When publisher checksum URLs are empty, downloads remain size-bounded and structurally validated. RustProxy computes the staged file's local SHA-256 and compares it with the installed file, so unchanged content is not republished. If an optional checksum URL is configured, the publisher digest is additionally enforced. A disabled MMDB downloader may use a locally managed `<data-dir>/geoip/country.mmdb`; the manual refresh action validates and hot-reloads that local file.
|
||||
|
||||
Application-coupled Python is now separated under `scripts/runtime`, `scripts/build`, `scripts/deploy` and `scripts/migration`; QA/dev/audit/legacy helpers live under `tools/`, while OPNsense/RouterOS helpers live under `integrations/`. Release checks validate the new paths and archive contents.
|
||||
Cache compatibility hashing intentionally ignores `bypass_query`, `bypass_prefixes` and response-only cache diagnostic/Via fields. Those settings affect admission/wire behavior on subsequent requests but do not mutate a stored representation. Representation/origin-affecting cache, transform, header, forwarding and vhost changes remain in the compatibility identity.
|
||||
|
||||
## Verification status in this workspace
|
||||
|
||||
Rust/Cargo is not installed in this workspace. Rust-independent checks are run here; compiler/type/runtime gates and real H2/H3 protocol execution remain open until this source is checked on a Rust-enabled host.
|
||||
|
||||
The final status table is refreshed from the release checks performed for this archive. Static/source contracts do not substitute for Rust parsing, type checking, unit tests or real protocol runtime tests.
|
||||
Rust/Cargo is not installed in this workspace. Rust-independent release checks can run here; compiler/type/runtime gates and real H2/H3 protocol execution require a Rust-enabled host.
|
||||
|
||||
## Release checks
|
||||
|
||||
| Check | Result |
|
||||
| --- | --- |
|
||||
| Rust/source module contracts | PASS |
|
||||
| Bounded-memory/cache/rate-limit/compression contracts | PASS |
|
||||
| GeoIP direct-source/optional-checksum/MMDB/manual endpoint/UI/hot-swap source contracts | PASS |
|
||||
| Cache Via/narrow invalidation source contracts | PASS |
|
||||
| Listener protocol-badge/header-editor UI source contracts | PASS |
|
||||
| ACME startup reporting contract (no speculative banner state; final INFO event) | PASS |
|
||||
| Documentation and language-pack checks | PASS |
|
||||
| Web Server, security and dependency source contracts | PASS |
|
||||
| `tools/checks/check_source.py` | PASS (compiler/runtime explicitly not verified) |
|
||||
| `tools/checks/release_smoke.py` | PASS |
|
||||
| Python syntax (`py_compile`) for changed release scripts | PASS |
|
||||
| Python syntax (`py_compile`) and direct-CSV installer validation checks | PASS |
|
||||
| Browser Web Service UI regression (`node tools/checks/web_service_ui_test.mjs`) | PASS |
|
||||
| Docker Agent (`go test ./...`) | PASS |
|
||||
| `./dev.sh test` static/pre-compiler stages | PASS |
|
||||
| Rust `cargo fmt/check/test`, linking and Rust unit tests | NOT VERIFIED - Cargo/Rust is not installed in this workspace |
|
||||
| Live rebuilt HTTP/2 + HTTP/3 `HEAD` cache test | NOT VERIFIED - requires a compiled 0.27.3 binary and QUIC-capable runtime |
|
||||
| `./dev.sh test` static/pre-compiler stages | PASS; command then exits 127 at the Rust/Cargo gate |
|
||||
| Rust `cargo fmt/check/test`, linking and Rust unit tests (including synthetic standard/sapics/custom-MMDB lookup fixtures, GeoIP hot-swap, cache Via and cache-revision tests) | NOT VERIFIED - Cargo/Rust is not installed in this workspace |
|
||||
| Live manual/automatic GeoIP direct-source download and real-world MMDB lookup | NOT VERIFIED - requires network access and a compiled 0.28.0 binary |
|
||||
| Live HTTP/2 + HTTP/3 protocol tests | NOT VERIFIED - requires a compiled 0.28.0 binary and QUIC-capable runtime |
|
||||
|
||||
`./dev.sh test` exits with status 127 only when it reaches the Rust toolchain gate; all earlier release/static checks complete successfully.
|
||||
`./dev.sh test` is expected to exit with status 127 only when it reaches the Rust toolchain gate; all earlier release/static checks must complete successfully for this report to remain valid.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Scope
|
||||
|
||||
0.27.3 aligns HTTP/3 `HEAD` cache handling with HTTP/2, reduces long-lived cache/rate-limit memory, excludes the rebuildable managed Python runtime from backups, and promotes package repositories as the normal installation path. SQLite schema 8, saved configuration compatibility and the COLD cache on-disk format remain unchanged.
|
||||
0.27.3 aligns HTTP/3 `HEAD` cache handling with HTTP/2, reduces long-lived cache/rate-limit memory, excludes the rebuildable managed Python runtime from backups, promotes package repositories as the normal installation path, and includes maintenance fixes for restore ownership, startup listener overrides, strict WebSocket 101 framing, resilient update checks, scaled sidebar layout and automatic GeoIP maintenance. SQLite schema 8 and the COLD cache on-disk format remain unchanged; existing saved configurations remain compatible because the new `geoip_updates` object is defaulted when absent.
|
||||
|
||||
## HTTP/3 HEAD cache parity
|
||||
|
||||
@@ -51,6 +51,22 @@ The managed ACME runtime now distinguishes private-environment creation, pip pre
|
||||
|
||||
On distributions enforcing PEP 668, the application-local `virtualenv` bootstrap first uses normal `pip --target`; if the distribution rejects that operation, RustProxy retries with `--break-system-packages` while still writing exclusively into the private bootstrap directory under the RustProxy data tree. This does not install or modify packages in the distribution Python environment.
|
||||
|
||||
## Restore ownership and production data directory
|
||||
|
||||
The production CLI default is `/var/lib/rustproxy`; `dev.sh` explicitly sets `RUST_PROXY_DATA_DIR` to repository-local `./data`. During restore, RustProxy records the UID/GID of an existing target data directory and recursively applies that ownership to the validated staging tree before the atomic rename. This prevents a root-run restore from converting service data to `root:root`; symlinks are ownership-adjusted with `lchown` and are not followed.
|
||||
|
||||
## GeoIP background maintenance
|
||||
|
||||
`Config.geoip_updates` defaults to enabled with the sapics latest/checksum release URLs, 256 MiB maximum file size and a 24-hour interval. The worker starts after successful listener binding, attempts immediately, compares publisher SHA-256 files with local files and downloads only stale assets. Database payloads are streamed to private staging files with a transfer limit and incremental SHA-256, the staged pair is parsed before publication, and the in-memory GeoIP database is atomically replaced without a proxy restart. Failures are fail-soft and retry later. The worker and managed manual updater share `.geoip-update.lock`; that runtime lock is excluded from backups.
|
||||
|
||||
## Listener, WebSocket and control-plane startup fixes
|
||||
|
||||
Explicit CLI/systemd HTTP/HTTPS binds now replace the corresponding canonical saved `http`/`https` bind rather than appending a second listener that can leave `:80`/`:443` active. Strict WebSocket response validation accepts `101` with no `Content-Length` or exactly `Content-Length: 0`, while rejecting positive lengths and any `Transfer-Encoding`. The release-version checker starts only after all sockets bind and logs polling failures as warnings, keeping network/DNS availability outside the proxy startup success path.
|
||||
|
||||
## Scaled administration sidebar
|
||||
|
||||
The desktop shell/sidebar height compensates for CSS `zoom` via `100vh / --ui-scale`. At scales below 100%, the flex sidebar therefore still spans the visual viewport and its bottom runtime information remains at the bottom edge. Mobile rules continue to use automatic height.
|
||||
|
||||
## Source-tool separation
|
||||
|
||||
0.27.3 also separates application-coupled scripts from auxiliary tooling without changing proxy behavior. `scripts/runtime`, `scripts/build`, `scripts/deploy` and `scripts/migration` contain code directly tied to application/build/deployment workflows. Compiler-independent checks, mocks, audit probes and the retired Python backup helper moved to `tools/`. OPNsense and RouterOS helpers moved to `integrations/`.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# RustProxy 0.28.0 implementation note
|
||||
|
||||
## Scope
|
||||
|
||||
0.28.0 extends the 0.27.3 maintenance release with operator-controlled GeoIP refresh, clearer listener status, cache `Via` metadata, narrower cache invalidation and a vhost Security header-rule layout repair. SQLite schema 8 and COLD cache format 3 are unchanged.
|
||||
|
||||
## GeoIP manual refresh and hot reload
|
||||
|
||||
`POST /api/geoip/update` is an authenticated/CSRF-protected control-plane action used by **Settings -> GeoIP updates -> Download now**. It invokes the same updater function as the background worker. The updater consumes direct source URLs, with optional publisher SHA-256 URLs per file, streams payloads into a private staging directory under the configured size limit, validates the selected database format, publishes only content whose local SHA-256 changed, and replaces the in-memory database only after validation succeeds. The UI action works when scheduled updates are disabled.
|
||||
|
||||
CSV mode uses separate IPv4 and IPv6 range files. MMDB mode uses one native MaxMind DB for both families and recognizes standard `country.iso_code` / `registered_country.iso_code` records plus a top-level `country_code` compatibility record or a root two-letter country string. The combined sapics `user-country.mmdb` is the default MMDB source. When the publisher checksum URL is empty, no remote checksum is required; structural validation and local content hashing remain mandatory. An updater-disabled MMDB configuration may leave the URL empty and manage `<data-dir>/geoip/country.mmdb` locally; the manual action then validates/reloads that local file.
|
||||
|
||||
The automatic worker, manual UI action and managed upgrade path serialize through `.geoip-update.lock`. Publishing uses the application's existing `ArcSwap<Database>`: a request that already owns the prior `Arc` remains memory-safe, while subsequent lookups load the new database. The request path uses this slot for country access rules, country rate limiting and optional country logging; administration and Prometheus status read the same slot. No process restart is needed.
|
||||
|
||||
## Listener status UX
|
||||
|
||||
Settings no longer compresses active endpoints into one `HTTP ... | HTTPS ...` sentence. Each active endpoint is rendered as a separate compact card with protocol and address plus badges for the protocols that are actually enabled on the running listener: `H1` is always present, TLS listeners add `H2` when ALPN HTTP/2 is enabled and `H3/UDP` when QUIC is enabled, while cleartext listeners add `H2C` when prior-knowledge HTTP/2 is enabled. Saved-listener restart semantics are unchanged.
|
||||
|
||||
## Cache Via and invalidation
|
||||
|
||||
`cache.via_header` is an optional string. Its default is `RustProxy`, producing `Via: RustProxy`; `null` disables RustProxy's Via value. Existing upstream Via fields are preserved and RustProxy appends its value. Validation bounds the configured field value to 256 valid HTTP-header bytes.
|
||||
|
||||
Cache compatibility hashing now normalizes request-only bypass fields and response-only cache diagnostics (`X-Cache`, hit counters, `Cache-Status`, resident age and `Via`). These changes apply on subsequent requests without discarding HOT/COLD objects. Representation/origin-affecting configuration remains in the compatibility identity and continues to invalidate incompatible objects.
|
||||
|
||||
## Vhost Security header rules
|
||||
|
||||
The request/response header-rule editor now uses the same structured header editor layout as location rules. Delete is contained in the rule heading and aligned to the right, while Operation, Name and Value remain in the field grid below it.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- SQLite schema: **8**, unchanged.
|
||||
- COLD cache format: **3**, unchanged.
|
||||
- Existing saved configurations remain valid because `Settings` uses serde defaults; an absent `cache.via_header` becomes `RustProxy`.
|
||||
- Legacy 0.27.3 release/checksum base URLs are accepted during configuration migration. The old default checksum base migrates to optional/no publisher checksum; custom legacy bases are translated into per-file URLs.
|
||||
|
||||
### Startup ACME runtime reporting
|
||||
|
||||
The human-readable startup banner no longer prints the initial `checking/preparing` ACME Python state, because that value can race the asynchronous runtime probe. When the probe finishes, RustProxy emits an `INFO` event named `ACME runtime check complete` containing the final status, mode, Python/pip/Certbot availability and managed-environment path.
|
||||
|
||||
Reference in new issue
Block a user