Module: Reputable::Tracker

Defined in:
lib/reputable/tracker.rb

Overview

Request tracking functionality

RESILIENCE: All tracking methods are designed to fail silently.

  • Returns false on any failure
  • Never raises exceptions
  • Async mode runs in separate thread

Class Method Summary collapse

Class Method Details

.track_request(ip:, path:, **options) ⇒ Boolean

Track a request for behavioral analysis

Examples:

Basic usage

Reputable::Tracker.track_request(
  ip: "203.0.113.45",
  path: "/products/123"
)

Full usage with request_id for JS reconciliation

Reputable::Tracker.track_request(
  ip: request.ip,
  path: request.path,
  query: request.query_string,
  method: request.request_method,
  user_agent: request.user_agent,
  referer: request.referer,
  request_id: env['reputable.request_id'],
  tags: ["view:page:product", "trust:channel:organic"]
)

Parameters:

  • ip (String)

    Client IP address (required)

  • path (String)

    Request path (required)

  • options (Hash)

    Additional options

Options Hash (**options):

  • :query (String)

    Query string

  • :method (String)

    HTTP method (GET, POST, etc.)

  • :fingerprint (String)

    Browser fingerprint hash

  • :user_agent (String)

    User-Agent header

  • :referer (String)

    Referer header

  • :country (String)

    Country code (ISO 3166-1 alpha-2)

  • :tags (Array<String>)

    Custom classification tags

  • :metadata (Hash)

    Additional metadata

  • :request_id (String)

    Unique request ID for reconciliation with JS tracking

Returns:

  • (Boolean)

    true if successfully pushed to buffer, false otherwise



47
48
49
50
51
52
53
54
55
# File 'lib/reputable/tracker.rb', line 47

def track_request(ip:, path:, **options)
  return false unless Reputable.enabled?
  
  entry = build_request_entry(ip, path, options)
  push_to_buffer(:request, entry)
rescue StandardError => e
  Reputable.logger&.debug("Reputable track_request error: #{e.class} - #{e.message}")
  false
end

.track_request_async(ip:, path:, **options) ⇒ Boolean

Track a request asynchronously (fire-and-forget) Uses a thread to avoid blocking the request

Returns:

  • (Boolean)

    Always returns true (async)



60
61
62
63
64
65
66
67
68
69
# File 'lib/reputable/tracker.rb', line 60

def track_request_async(ip:, path:, **options)
  return true unless Reputable.enabled?
  
  Thread.new do
    track_request(ip: ip, path: path, **options)
  rescue StandardError
    # Silently ignore all errors in async thread
  end
  true
end

.track_security_event(event_type:, ip: nil, account_id: nil, user_id: nil, session_id: nil, request_id: nil, outcome: nil, reason_code: nil, context: {}, resource: nil, metadata: {}, occurred_at_ms: nil, path: "/_trusted/security_event", **options) ⇒ Object

Track a trusted backend security event (risk_event_v1 envelope).

This is transported through the request buffer with metadata.risk_event_v1 so existing classification/rule pipelines can consume resulting tags.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/reputable/tracker.rb', line 75

def track_security_event(
  event_type:,
  ip: nil,
  account_id: nil,
  user_id: nil,
  session_id: nil,
  request_id: nil,
  outcome: nil,
  reason_code: nil,
  context: {},
  resource: nil,
  metadata: {},
  occurred_at_ms: nil,
  path: "/_trusted/security_event",
  **options
)
  return false unless Reputable.enabled?
  normalized_event_type = normalize_token(event_type)
  return false if normalized_event_type.nil? || normalized_event_type.empty?

  event_context = normalize_risk_context(context)
  effective_ip = ip || event_context[:ip]
  return false if effective_ip.nil? || effective_ip.to_s.strip.empty?

  risk_event = build_risk_event_v1(
    event_type: normalized_event_type,
    occurred_at_ms: occurred_at_ms,
    account_id:  || options[:account_id],
    user_id: user_id || options[:user_id],
    session_id: session_id || options[:session_id],
    request_id: request_id || options[:request_id],
    outcome: outcome,
    reason_code: reason_code,
    context: event_context,
    resource: resource,
    metadata: 
  )

   = (options[:metadata], )
  [:risk_event_v1] = risk_event

  tags = Array(options[:tags]) + security_tags_for_event(risk_event)

  entry = build_request_entry(effective_ip, path, options.merge(
    method: options[:method] || "POST",
    account_id:  || options[:account_id],
    user_id: user_id || options[:user_id],
    session_id: session_id || options[:session_id],
    request_id: request_id || options[:request_id],
    tags: tags,
    metadata: 
  ))

  push_to_buffer(:request, entry)
rescue StandardError => e
  Reputable.logger&.debug("Reputable track_security_event error: #{e.class} - #{e.message}")
  false
end

.track_security_event_async(**kwargs) ⇒ Object

Fire-and-forget variant of track_security_event.



135
136
137
138
139
140
141
142
143
144
# File 'lib/reputable/tracker.rb', line 135

def track_security_event_async(**kwargs)
  return true unless Reputable.enabled?

  Thread.new do
    track_security_event(**kwargs)
  rescue StandardError
    # Silently ignore all errors in async thread
  end
  true
end