diff --git a/CHANGELOG.md b/CHANGELOG.md index 75144c6..839bf73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ One line per change, grouped by the release that shipped it. Newest first. A release is a Cargo.toml bump plus a `v` tag; the content repos pin the tag they run (see the end of this file). +## 0.5.2 (2026-09-28) + +- The rate limiter answered 500 to any request that arrived without `X-Forwarded-For`. It reads the caller from that header or `X-Real-IP` and falls back to the socket, and 0.5.0 served the app without connect info, so there was no socket to fall back to and no key to rate-limit by. Through Caddy every request carries the header and the site was fine; a health check on localhost got a 500, which is how the first deploy of 0.5.1 failed. + ## 0.5.1 (2026-09-28) - A `self_transition` that names no `from` means the bucket's own initial state, not the word `open`. 0.5.0 read the literal, which is only right for a bucket whose records start there: redoal's watchers arrive at `pending` and confirm themselves into `open`, so the rule compiled as `open -> open` and the confirm link in the mail could never match. Wrong the same way before 0.5.0, and now visible because the lint checks the edge exists. diff --git a/Cargo.lock b/Cargo.lock index 574c57c..04495fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3357,7 +3357,7 @@ dependencies = [ [[package]] name = "portal" -version = "0.5.1" +version = "0.5.2" dependencies = [ "anyhow", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index e027020..a7449ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "portal" -version = "0.5.1" +version = "0.5.2" edition = "2021" [lib] diff --git a/src/main.rs b/src/main.rs index 2aff238..4f4e772 100644 --- a/src/main.rs +++ b/src/main.rs @@ -278,7 +278,17 @@ async fn main() -> anyhow::Result<()> { tracing::info!("listening on http://{addr}"); let listener = tokio::net::TcpListener::bind(&addr).await?; - axum::serve(listener, app.into_make_service()).await?; + // With connect info, not without: the rate limiter reads the + // caller from `X-Forwarded-For`/`X-Real-IP` and falls back to the + // socket, and without this there is no socket to fall back to. A + // request that arrives without those headers - a health check on + // localhost, anything not through Caddy - then has no key at all, + // and the limiter answers 500 to every one of them. + axum::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await?; Ok(()) }