Files
RustProxy/docs/V0.26.1_IMPLEMENTATION.md
T
2026-10-05 19:05:55 +02:00

105 lines
5.2 KiB
Markdown

# RustProxy 0.26.1 implementation note
## Scope
Version 0.26.1 is a focused follow-up to the persistent protocol diagnostics and Stored Logs explorer introduced in 0.26.0. It does not change SQLite schema 8 or the saved proxy configuration format.
The release has two goals:
1. Attribute persistent `protocol_error` records to a configured vhost when that attribution is safe and deterministic.
2. Make the Stored Logs explorer practical on installations with many vhosts and useful for protocol diagnostics.
Live Logs remains a separate bounded in-memory request view. Low-level `protocol_error` records are persistence-only and must not be published to its history/broadcast path.
## One shared vhost-resolution function
All protocol adapters use the same resolver; TLS, HTTP/1, HTTP/2 and HTTP/3 must not implement independent hostname matching.
```rust
resolve_logging_vhost(
local,
tls,
routed_vhost,
authority,
host,
sni,
) -> Option<ResolvedLogVhost>
```
Resolution priority is:
1. a vhost already selected by normal routing;
2. HTTP `:authority`;
3. HTTP `Host`;
4. TLS/QUIC SNI;
5. no match, which means `_unmatched.jsonl` in per-vhost file mode.
The helper normalizes authority/Host/SNI with the existing host parser and delegates selection to the same `Snapshot::frontend` exact/wildcard and listener-binding logic used by normal request routing. It returns the configured internal vhost ID and `matched_by`; client input is never turned directly into a filename.
## Persistent protocol records
A protocol record keeps the compatibility fields from 0.26.0 and also exposes canonical diagnostics when available:
- `event = protocol_error`;
- `reason` and compatibility `protocol_error`;
- `error` and compatibility `error_detail`;
- `client_ip` and `local_addr`;
- `transport` and `http_version`;
- `vhost` when safely resolved;
- `sni` and compatibility `tls_sni`;
- `authority`;
- `matched_by = router | authority | host | sni | none`.
Diagnostic identity strings are control-character stripped and length-bounded before persistence. Existing secret redaction and error-detail limits remain in force.
In per-vhost file mode, a resolved record uses the existing configured vhost sink. Invalid internal vhost identifiers fall back to `_unmatched` instead of being sanitized into a new filename. In combined-file mode, sink behavior is unchanged.
## Protocol integration
TLS reads SNI from ClientHello before completing the handshake. A later handshake failure/timeout carries that SNI into the shared resolver. If ClientHello itself cannot be parsed, attribution remains unavailable and the record is unmatched.
HTTP/2 retains the TLS SNI for post-handshake connection/drain diagnostics. Request paths that have an HTTP request object can additionally provide authority/Host.
HTTP/3 obtains SNI from Quinn/Rustls handshake data after a successful QUIC handshake and uses it for later request, stream and connection protocol failures. Failures that happen before usable handshake identity exists remain unmatched.
No protocol path infers a vhost solely from a shared listener or local port.
## Stored Logs explorer changes
The `/logs` workspace now:
- highlights the selected log row;
- expands Record details inline directly below that row;
- removes the separate details panel below the table;
- keeps the table header sticky while the log list scrolls;
- lets each administrator choose visible Stored Logs columns and persists the choice in existing account preferences;
- offers protocol columns for transport, match source, SNI and authority;
- keeps the configured-file inventory collapsed by default;
- canonicalizes fields through the active structured-log field mapping so `client_ip` remains visible when the JSON output key was renamed;
- searches record values case-insensitively and includes `reason`, `error`, `sni`, `authority` and other stored values;
- refreshes keyword results with a short debounce while typing;
- retains the existing bounded source/byte/line/page limits and reports truncation when an `all` query contains more sources than can be scanned safely.
## Security invariants
The browser still accepts only opaque configured source IDs; it never accepts arbitrary filesystem paths. File reads remain regular-file-only and retain Unix `O_NOFOLLOW` protection.
SNI, Host and authority are lookup inputs only. They cannot select a filesystem path directly. Only a configured vhost resolved through the existing routing model can select a per-vhost log sink.
## Regression coverage
Source tests cover:
- exact and wildcard hostname precedence using the shared resolver;
- router priority over authority/Host/SNI;
- Host with port and case-insensitive normalization;
- unknown and missing SNI returning no vhost;
- mapped `client_ip` normalization in Stored Logs;
- keyword matching against protocol diagnostic values;
- safe per-vhost filename fallback;
- bounded protocol identity fields;
- protocol errors remaining outside Live Logs;
- preference validation for selectable Stored Logs columns.
A Rust-enabled release host must still run `cargo fmt --check`, Clippy with warnings denied, all-target/all-feature tests and the complete `dev.sh` release build before the package is considered compiler-verified.