Sentinel exists because of Pingora. Cloudflare open-sourced their proxy framework in 2024, and it gave us what would have taken years to build: async I/O, connection pooling, HTTP/1.1 and HTTP/2 handling, TLS termination — all in Rust, all battle-tested at Cloudflare’s scale. Sentinel is the security and routing layer on top; Pingora is the engine underneath.
On January 30, Cloudflare released Pingora 0.7.0. We’ve upgraded Sentinel to run on it. This post covers what changed in Pingora, what it means for Sentinel, and why this upgrade let us drop a fork we’d been maintaining since January.
Dropping the fork
Since early January, Sentinel shipped with a patched fork of Pingora 0.6. The fork carried three security fixes that upstream hadn’t released yet:
| Issue | Severity | What we patched |
|---|---|---|
| RUSTSEC-2026-0002 | Medium | lru crate vulnerability — upgraded to safe version |
atty dependency | Low | Unmaintained crate pulled in transitively via clap — removed |
protobuf recursion | Low | Deeply nested protobuf messages could cause stack overflow — bounded recursion depth |
Maintaining a fork of your foundation framework is operational overhead. Every upstream commit needs evaluation. CI runs against both upstream and fork. Contributors have to understand the patch set. It works, but it’s not where you want to be long-term.
Pingora 0.7.0 includes all three fixes. The lru crate is upgraded, atty is removed, and protobuf handling is hardened. Our [patch.crates-io] section — 16 lines of git overrides pointing at our fork — is gone. Sentinel now builds against upstream Pingora with zero patches.
What’s new in Pingora 0.7
The full release notes cover everything. Here are the changes most relevant to a reverse proxy like Sentinel.
ConnectionFilter trait
Pingora 0.7 adds a ConnectionFilter trait that fires immediately after TCP accept — before TLS handshake, before HTTP parsing, before any application logic. This is the earliest point you can inspect a connection:
async fn connection_filter(&self, conn: &TcpStream) -> Result<bool> {
let peer_addr = conn.peer_addr()?;
// Block at TCP level — no TLS overhead for blocked connections
Ok(!self.is_blocked(peer_addr))
}
For Sentinel, this opens the door to TCP-level IP blocking and connection-rate limiting that runs before spending CPU on TLS handshakes. A connection from a blocked IP never allocates a TLS session, never parses HTTP headers, never hits the routing engine. We haven’t integrated this yet, but it’s the right place for Sentinel’s GeoIP filtering and IP reputation checks to eventually live.
Extensible TLS context
SslDigestExtensions lets you attach custom data to the TLS digest on both downstream (client) and upstream (backend) connections. This is useful for propagating mutual TLS client certificate information through the proxy pipeline without re-parsing the certificate at every stage.
Background subrequests
You can now spawn subrequests from a main session that continue independently. This is relevant for Sentinel’s shadow traffic feature — where a copy of the request is sent to a secondary backend for testing — and for fire-and-forget webhook notifications.
Body-byte tracking
Pingora now tracks request and response body sizes across both HTTP/1.1 and HTTP/2. Sentinel already tracks body sizes for metrics and rate limiting, but having framework-level counters means the numbers are accurate even when the proxy doesn’t read the full body (streaming, early termination).
Cache improvements
Several cache changes affect Sentinel’s caching layer:
ForcedInvalidationKindrenamed toForcedFreshness— a clearer name for what it does (forcing a cached response to be treated as fresh or expired)- Multipart range request limits —
range_header_filternow accepts amax_multipart_rangesparameter, defaulting to 200. This bounds the work done for range requests with many byte ranges, preventing potential abuse - Cache lock improvements — Lock age timeouts are fixed, preventing unnecessary lock reacquisition under contention
- Header-only cache admission — Corrected logic for responses that have headers but no body
Breaking changes we adapted
Two API changes required code modifications in Sentinel:
ForcedInvalidationKind→ForcedFreshness— import and usage renamerange_header_filternow takes a third parameter (max_multipart_ranges: Option<usize>) — we passNoneto use the default limit of 200
Both were straightforward. No behavior changes, no configuration impact.
What this means for Sentinel users
Nothing breaks. Sentinel’s configuration format, behavior, and APIs are unchanged. The upgrade is purely internal — a better foundation underneath the same proxy.
What you get:
- No more fork — Sentinel tracks upstream Pingora directly. Security fixes from Cloudflare reach Sentinel faster.
- Security fixes included — The
lru,atty, andprotobufissues are resolved via upstream, not patches. - Future capabilities — Connection-level filtering, extensible TLS context, and background subrequests are available for upcoming Sentinel features.
- Minimum Rust version — Pingora 0.7 requires Rust 1.83+. Sentinel already requires 1.85+, so no change for our users.
Alongside Pingora: dependency sweep
This Pingora upgrade was part of a broader dependency update. In the same session, we merged 10 Dependabot PRs covering the full dependency tree:
| Dependency | Change | Notes |
|---|---|---|
thiserror | 1.x → 2.0 | Error derive macro |
redis | 0.27 → 1.0 | Distributed rate limiting backend |
criterion | 0.6 → 0.8 | Benchmarking framework |
instant-acme | 0.7 → 0.8 | ACME certificate management (major API rewrite) |
jsonschema | 0.18 → 0.40 | API schema validation (major API rewrite) |
quick-xml | 0.37 → 0.39 | XML parsing for data masking agent |
async-memcached | 0.5 → 0.6 | Distributed rate limiting backend |
tiktoken-rs | 0.6 → 0.9 | Token counting for inference routing |
sysinfo | 0.37 → 0.38 | System information |
Three of these — instant-acme, jsonschema, and quick-xml — had breaking API changes that required code modifications. The ACME client needed a significant rewrite for instant-acme 0.8’s new builder pattern and async authorization stream. All changes are in the main branch.
The full test suite passes. All 25 test crates, zero failures.
Sentinel v0.4.7 ships with Pingora 0.7 and all the dependency updates described above. Grab it:
# From source
cargo install sentinel-proxy
# Container
docker pull ghcr.io/raskell-io/sentinel:26.02_1
# Binary
curl -fsSL https://getsentinel.raskell.io | sh