From 86c59b03885f8423be86ee940892a44a194ad712 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 20 Oct 2025 11:31:11 -0400 Subject: [PATCH 1/2] feat: add metrics to perms middleware --- Cargo.toml | 2 +- src/perms/middleware.rs | 50 +++++++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 868ab8f..c84f0df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "init4-bin-base" description = "Internal utilities for binaries produced by the init4 team" keywords = ["init4", "bin", "base"] -version = "0.14.0" +version = "0.14.1" edition = "2021" rust-version = "1.85" authors = ["init4", "James Prestwich", "evalir"] diff --git a/src/perms/middleware.rs b/src/perms/middleware.rs index 279090c..7dbdf4a 100644 --- a/src/perms/middleware.rs +++ b/src/perms/middleware.rs @@ -11,10 +11,35 @@ use axum::{ Json, }; use core::fmt; +use metrics::{counter, describe_counter}; +use opentelemetry::trace::Status; use serde::Serialize; -use std::{future::Future, pin::Pin, sync::Arc}; +use std::{ + borrow::Cow, + future::Future, + pin::Pin, + sync::{Arc, LazyLock}, +}; use tower::{Layer, Service}; use tracing::info; +use tracing_opentelemetry::OpenTelemetrySpanExt; + +const MISSING_HEADER: &str = "init4.perms.missing_header"; +const MISSING_HEADER_DESCR: &str = + "Counts the number of requests missing the authentication header"; + +const PERMISSION_DENIED: &str = "init4.perms.permission_denied"; +const PERMISSION_DENIED_DESCR: &str = + "Counts the number of requests denied due to builder permissioning"; + +const SUCCESS: &str = "init4.perms.success"; +const SUCCESS_DESCR: &str = "Counts the number of auths allowed due to builder permissioning"; + +static DESCRIBE: LazyLock<()> = LazyLock::new(|| { + describe_counter!(MISSING_HEADER, MISSING_HEADER_DESCR); + describe_counter!(PERMISSION_DENIED, PERMISSION_DENIED_DESCR); + describe_counter!(SUCCESS, SUCCESS_DESCR); +}); /// Possible API error responses when a builder permissioning check fails. #[derive(Serialize)] @@ -155,6 +180,8 @@ where fn call(&mut self, req: Request) -> Self::Future { let mut this = self.clone(); + LazyLock::force(&DESCRIBE); + Box::pin(async move { let span = tracing::info_span!( "builder::permissioning", @@ -167,19 +194,20 @@ where .calc() .current_point_within_slot() .expect("host chain has started"), - permissioning_error = tracing::field::Empty, + otel.status_code = tracing::field::Empty ); let guard = span.enter(); - info!("builder permissioning check started"); - // Check if the sub is in the header. let sub = match validate_header_sub(req.headers().get("x-jwt-claim-sub")) { Ok(sub) => sub, Err(err) => { - span.record("permissioning_error", err.1.message); + span.set_status(Status::Error { + description: Cow::Owned(err.1.message.to_string()), + }); info!(api_err = %err.1.message, "permission denied"); + counter!("init4.perms.missing_header").increment(1); return Ok(err.into_response()); } }; @@ -187,17 +215,21 @@ where span.record("requesting_builder", sub); if let Err(err) = this.builders.is_builder_permissioned(sub) { - span.record("permissioning_error", err.to_string()); - info!(api_err = %err, "permission denied"); + span.set_status(Status::Error { + description: Cow::Owned(err.to_string()), + }); let hint = builder_permissioning_hint(&err); + counter!("init4.perms.permission_denied", "builder" => sub.to_string()) + .increment(1); + return Ok(ApiError::permission_denied(hint).into_response()); } - info!("builder permissioned successfully"); - drop(guard); + info!("builder permissioned successfully"); + counter!(SUCCESS, "builder" => sub.to_string()).increment(1); this.inner.call(req).await }) From 50a77e35f3ee773bf61ed47e632befcc8d890057 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 20 Oct 2025 12:16:07 -0400 Subject: [PATCH 2/2] feat: attempts --- Cargo.toml | 6 +++--- src/perms/middleware.rs | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c84f0df..f48d531 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "init4-bin-base" description = "Internal utilities for binaries produced by the init4 team" keywords = ["init4", "bin", "base"] -version = "0.14.1" +version = "0.15.0" edition = "2021" rust-version = "1.85" authors = ["init4", "James Prestwich", "evalir"] @@ -16,8 +16,8 @@ repository = "https://github.com/init4tech/bin-base" init4-from-env-derive = "0.1.0" # Signet -signet-constants = { version = "0.11.1" } -signet-tx-cache = { version = "0.11.1", optional = true } +signet-constants = { version = "0.12" } +signet-tx-cache = { version = "0.12", optional = true } # alloy alloy = { version = "1.0.35", optional = true, default-features = false, features = ["std", "signer-local", "consensus", "network"] } diff --git a/src/perms/middleware.rs b/src/perms/middleware.rs index 7dbdf4a..f297de7 100644 --- a/src/perms/middleware.rs +++ b/src/perms/middleware.rs @@ -24,6 +24,9 @@ use tower::{Layer, Service}; use tracing::info; use tracing_opentelemetry::OpenTelemetrySpanExt; +const ATTEMPTS: &str = "init4.perms.attempts"; +const ATTEMPTS_DESCR: &str = "Counts the number of builder permissioning attempts"; + const MISSING_HEADER: &str = "init4.perms.missing_header"; const MISSING_HEADER_DESCR: &str = "Counts the number of requests missing the authentication header"; @@ -36,6 +39,7 @@ const SUCCESS: &str = "init4.perms.success"; const SUCCESS_DESCR: &str = "Counts the number of auths allowed due to builder permissioning"; static DESCRIBE: LazyLock<()> = LazyLock::new(|| { + describe_counter!(ATTEMPTS, ATTEMPTS_DESCR); describe_counter!(MISSING_HEADER, MISSING_HEADER_DESCR); describe_counter!(PERMISSION_DENIED, PERMISSION_DENIED_DESCR); describe_counter!(SUCCESS, SUCCESS_DESCR); @@ -199,6 +203,8 @@ where let guard = span.enter(); + counter!(ATTEMPTS).increment(1); + // Check if the sub is in the header. let sub = match validate_header_sub(req.headers().get("x-jwt-claim-sub")) { Ok(sub) => sub,