ESC
Type to search...

Rapina 0.6.0

Route auto-discovery, Prometheus metrics, and security hardening

Rapina 0.6.0 shipped on February 22, 2026. The headline feature was route auto-discovery: handlers annotated with #[get], #[post], #[put], or #[delete] registered themselves at link time via inventory, removing the need to wire routes manually in main.rs. The release also added a Prometheus metrics endpoint and a round of security and CI hardening.


Route auto-discovery

Before 0.6.0, every route had to be registered explicitly:

let router = Router::new()
    .get("/users", list_users)
    .post("/users", create_user)
    .get("/users/:id", get_user);

Rapina::new().router(router).listen("127.0.0.1:3000").await

With auto-discovery, calling .discover() was enough. The handler macros emitted an inventory::submit! at link time, and Rapina collected them automatically at startup:

use rapina::prelude::*;

#[get("/users")]
async fn list_users() -> &'static str { "[]" }

#[post("/users")]
async fn create_user() -> StatusCode { StatusCode::CREATED }

#[tokio::main]
async fn main() -> std::io::Result<()> {
    Rapina::new()
        .discover()
        .listen("127.0.0.1:3000")
        .await
}

Discovery was additive — .router() and .discover() worked together. Routes registered manually co-existed with discovered ones.

Public routes

The #[public] attribute worked in both orderings with discovery enabled. Placing it above or below the route macro both marked the route as accessible without authentication, with no extra configuration:

// Both orderings are equivalent
#[public]
#[get("/health")]
async fn health() -> &'static str { "ok" }

#[get("/ping")]
#[public]
async fn ping() -> &'static str { "pong" }

At startup, Rapina logged the number of discovered routes and how many were public.


Prometheus metrics

A /metrics endpoint was added as an optional feature. It was enabled with the metrics feature flag and a single builder call:

[dependencies]
rapina = { version = "0.6.0", features = ["metrics"] }
Rapina::new()
    .with_metrics(true)
    .router(router)
    .listen("127.0.0.1:3000")
    .await

The endpoint returned metrics in Prometheus text format. Three metrics were collected automatically:

MetricTypeLabelsDescription
http_requests_totalCountermethod, path, statusTotal completed HTTP requests
http_request_duration_secondsHistogrammethod, pathRequest duration in seconds
http_requests_in_flightGaugeRequests currently being processed

Path normalisation

To prevent label cardinality explosion, pure-numeric path segments were replaced with :id in metric labels:

Raw requestLabel
/users/42/users/:id
/users/123/posts/456/users/:id/posts/:id
/users/profile/users/profile

Requests to /users/1, /users/2, and /users/999 all mapped to the same label set.


Security hardening

The repository received a set of security and supply-chain hardening changes:

  • SECURITY.md — a security policy was added with a contact address for responsible disclosure.
  • deny.tomlcargo-deny was added with rules for license compliance, duplicate dependencies, and known advisories.
  • audit.yml — a new CI workflow ran cargo audit on every push to check for security advisories.
  • scorecard.ymlOpenSSF Scorecard was added to track supply-chain security practices.
  • CODEOWNERS — a CODEOWNERS file established code review ownership.
  • dependabot.yml — Dependabot was configured for both Cargo and GitHub Actions dependencies.

The CI workflow was also hardened: steps gained pinned permissions, and the release workflow was updated to separate build and publish steps.


Community tooling

Three GitHub Actions workflows were added to improve the contributor experience:

  • Auto-labeler — pull requests were automatically labelled by path using labeler.yml. Changes under docs/ received a docs label, changes under .github/ a ci label, and so on.
  • Welcome bot — first-time contributors received a welcome comment when their first issue or pull request was opened.
  • /release command — maintainers could trigger the release workflow with a /release comment on a pull request.

Dependency updates

  • toml was bumped to 1.0.0+spec-1.1.0, bringing TOML spec 1.1 support. TOML 1.1 adds Unicode escape sequences and allows more characters in bare keys.

Upgrade by bumping the version in your Cargo.toml:

rapina = "0.6.0"