Skip to main content

systemprompt_analytics/models/
fingerprint.rs

1//! Fingerprint stats and `FlagReason` bot-flag tags.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use sqlx::FromRow;
9use systemprompt_identifiers::UserId;
10
11#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
12pub struct FingerprintReputation {
13    pub fingerprint_hash: String,
14    pub first_seen_at: DateTime<Utc>,
15    pub last_seen_at: DateTime<Utc>,
16    pub total_session_count: i32,
17    pub active_session_count: i32,
18    pub total_request_count: i64,
19    pub requests_last_hour: i32,
20    pub peak_requests_per_minute: f32,
21    pub sustained_high_velocity_minutes: i32,
22    pub is_flagged: bool,
23    pub flag_reason: Option<String>,
24    pub flagged_at: Option<DateTime<Utc>>,
25    pub reputation_score: i32,
26    pub abuse_incidents: i32,
27    pub last_abuse_at: Option<DateTime<Utc>>,
28    pub last_ip_address: Option<String>,
29    pub last_user_agent: Option<String>,
30    pub associated_user_ids: Vec<UserId>,
31    pub updated_at: DateTime<Utc>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum FlagReason {
36    HighRequestCount,
37    SustainedVelocity,
38    ExcessiveSessions,
39    ReputationDecay,
40}
41
42impl FlagReason {
43    pub const fn as_str(&self) -> &'static str {
44        match self {
45            Self::HighRequestCount => "request_count_exceeded_100",
46            Self::SustainedVelocity => "sustained_velocity_10rpm_1hr",
47            Self::ExcessiveSessions => "session_count_exceeded_10",
48            Self::ReputationDecay => "reputation_score_below_threshold",
49        }
50    }
51}
52
53impl std::fmt::Display for FlagReason {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        write!(f, "{}", self.as_str())
56    }
57}
58
59#[derive(Debug, Clone)]
60pub struct FingerprintAnalysisResult {
61    pub fingerprint_hash: String,
62    pub should_flag: bool,
63    pub flag_reasons: Vec<FlagReason>,
64    pub new_reputation_score: i32,
65    pub should_ban_ip: bool,
66    pub ip_to_ban: Option<String>,
67}