Reputable Ruby Client

Ruby gem for integrating with Reputable - bot detection and reputation scoring for Rails applications.

Resilience First: This gem is designed to never break your application. All operations fail silently with safe defaults.

Release notes and version bumps: see clients/ruby/RELEASING.md.

Installation

Add to your Gemfile:

gem 'reputable'

Then run:

bundle install

Rails Quick Start

1. Create Initializer

# config/initializers/reputable.rb
Reputable.configure do |config|
  # Required: Redis/Dragonfly URL (TLS supported via rediss://)
  config.redis_url = ENV['REPUTABLE_REDIS_URL']

  # Optional: Verbose logging (logs every middleware decision)
  # config.verbose = true
end

# Optional: Enable logging
Reputable.logger = Rails.logger

2. Add Middleware

# config/application.rb
module YourApp
  class Application < Rails::Application
    # Add Reputable middleware (recommended: async mode, non-blocking)
    config.middleware.use Reputable::Middleware, async: true
  end
end

3. Use Controller Helpers

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Reputable::Rails::ControllerHelpers
end

# app/controllers/payments_controller.rb
class PaymentsController < ApplicationController
  def create
    @order = Order.create!(order_params)
    
    if @order.payment_successful?
      # Trust this IP after successful payment (forever)
      trust_current_ip(reason: 'payment_completed', order_id: @order.id)
    end
  end
end

That's it! Reputable will now track all requests and you can query/apply reputations.


Configuration Reference

Environment Variables

All configuration can be set via environment variables:

# Required
REPUTABLE_REDIS_URL=rediss://user:password@your-dragonfly.example.com:6379

# Optional: Base URL for verification and API endpoints (domain only)
REPUTABLE_BASE_URL=https://api.reputable.click

# Optional: Blocked page branding/support info
REPUTABLE_SITE_NAME="Example Store"
REPUTABLE_SUPPORT_EMAIL=support@example.com
REPUTABLE_SUPPORT_URL=https://example.com/support

# Optional: Disable entirely (useful for test environments)
REPUTABLE_ENABLED=false

# Optional: Connection tuning
REPUTABLE_CONNECT_TIMEOUT=0.5   # Redis connect timeout (seconds)
REPUTABLE_READ_TIMEOUT=0.5      # Redis read timeout (seconds)
REPUTABLE_WRITE_TIMEOUT=0.5     # Redis write timeout (seconds)
REPUTABLE_POOL_SIZE=5           # Connection pool size
REPUTABLE_POOL_TIMEOUT=1.0      # Pool checkout timeout (seconds)

# Optional: Verbose logging (logs every middleware decision at info level)
REPUTABLE_VERBOSE=true

# Optional: SSL (for custom certificates)
REPUTABLE_SSL_VERIFY=false      # Disable SSL verification (NOT recommended for production)

Full Configuration Options

Reputable.configure do |config|
  # Redis connection (supports redis:// and rediss:// for TLS)
  config.redis_url = ENV['REPUTABLE_REDIS_URL']
  
  # Connection pool settings
  config.pool_size = 5           # Number of Redis connections
  config.pool_timeout = 1.0      # Max wait for connection (seconds)
  
  # Redis operation timeouts
  config.connect_timeout = 0.5   # Connection timeout
  config.read_timeout = 0.5      # Read timeout  
  config.write_timeout = 0.5     # Write timeout
  
  # Custom SSL parameters (for self-signed certs, etc.)
  config.ssl_params = {
    ca_file: '/path/to/ca.crt',
    verify_mode: OpenSSL::SSL::VERIFY_PEER
  }
  
  # Customize TTLs (in seconds, 0 = forever)
  config.default_ttls = {
    trusted_verified: 0,              # Forever
    trusted_behavior: 365 * 24 * 3600, # 1 year
    untrusted_challenge: 7 * 24 * 3600,
    untrusted_block: 7 * 24 * 3600,
    untrusted_ignore: 7 * 24 * 3600
  }
  
  # IP header priority (for proxy environments)
  # Default covers Heroku, Cloudflare, AWS ALB, nginx, HAProxy
  config.ip_header_priority = %w[
    HTTP_CF_CONNECTING_IP
    HTTP_X_FORWARDED_FOR
    HTTP_X_REAL_IP
    HTTP_TRUE_CLIENT_IP
    REMOTE_ADDR
  ]
  
  # Verification Configuration
  # Supports comma-separated list in REPUTABLE_TRUSTED_KEYS or single key in REPUTABLE_TRUSTED_KEY
  config.trusted_keys = ENV['REPUTABLE_TRUSTED_KEYS']&.split(',') || ENV['REPUTABLE_TRUSTED_KEY']
  config.base_url = ENV['REPUTABLE_BASE_URL'] # Domain only

  # Optional: blocked page branding/support info
  config.site_name = ENV['REPUTABLE_SITE_NAME']
  config.support_email = ENV['REPUTABLE_SUPPORT_EMAIL']
  config.support_url = ENV['REPUTABLE_SUPPORT_URL']
  
  # Verbose logging — logs every middleware decision at info level
  # Also available via REPUTABLE_VERBOSE=true
  config.verbose = true

  # Error callback (optional)
  config.on_error = ->(error, context) {
    # Report to your error tracking service
    Sentry.capture_exception(error, extra: { context: context })
  }
end

# Enable logging (required for verbose output)
Reputable.logger = Rails.logger

Proxy & Load Balancer Support

The gem automatically handles IP extraction in proxy environments including:

  • Heroku - Uses X-Forwarded-For
  • Cloudflare - Uses CF-Connecting-IP (highest priority by default)
  • AWS ALB/ELB - Uses X-Forwarded-For
  • nginx - Uses X-Real-IP or X-Forwarded-For
  • HAProxy - Uses X-Forwarded-For
  • Google Cloud Load Balancer - Uses X-Forwarded-For

How IP Extraction Works

  1. Headers are checked in priority order (configurable via ip_header_priority)
  2. For X-Forwarded-For, we parse the leftmost public IP (skipping private ranges)
  3. Private IP ranges (10.x, 172.16.x, 192.168.x, etc.) are automatically filtered

Custom Proxy Configuration

# If your proxy uses a non-standard header
config.ip_header_priority = %w[
  HTTP_X_MY_CUSTOM_IP
  HTTP_X_FORWARDED_FOR
  REMOTE_ADDR
]

TLS/SSL Support

The gem fully supports TLS connections to Redis/Dragonfly:

# Use rediss:// scheme for TLS
config.redis_url = "rediss://user:password@your-server:6379"

SSL Error Handling

All SSL errors are caught and logged, never breaking your application:

  • Certificate verification failures
  • Handshake timeouts
  • Protocol errors
  • Self-signed certificate issues

Custom Certificates

config.ssl_params = {
  ca_file: '/path/to/custom-ca.crt',
  cert: OpenSSL::X509::Certificate.new(File.read('/path/to/client.crt')),
  key: OpenSSL::PKey::RSA.new(File.read('/path/to/client.key'))
}

Disable SSL Verification (Development Only)

# NOT recommended for production
REPUTABLE_SSL_VERIFY=false

Hosted Measurement

Hosted measurement is the shared cloud.reputable.click path for per-request proxy, bot, timing, ASN, geo, and anomaly checks. It does not require Redis and does not replace the dedicated Reputable middleware path above.

Configure the customer credentials issued from the Reputable /measure page:

Reputable.configure do |config|
  config.measurement_base_url = ENV["REPUTABLE_MEASURE_BASE_URL"] # e.g. https://cloud.reputable.click
  config.measurement_customer_id = ENV["REPUTABLE_MEASURE_CUSTOMER_ID"]
  config.measurement_capability = ENV["REPUTABLE_MEASURE_CAPABILITY"]
  config.measurement_private_jwk = JSON.parse(ENV.fetch("REPUTABLE_MEASURE_PRIVATE_JWK"))
end

Mint a short-lived token for the browser to send to the hosted collect endpoint:

token = Reputable.measurement_token(
  mode: "collect",
  session_ref: session.id,
  request_id: request.request_id,
  origin: request.base_url,
  request_context: {
    scheme: request.scheme,
    host: request.host,
    method: request.request_method,
    path: request.path,
    query: request.query_string,
    page_type: "product",
    action_type: "view"
  }
)

collect_url = Reputable.measurement_collect_url(token)

After the browser returns the signed resultToken, verify it server-side before allowing an expensive or abuse-sensitive action:

result = Reputable.verify_measurement_result(result_token)

if result && result["outcome"] != "deny"
  # Continue with the protected action.
else
  # Review, challenge, or block according to your app policy.
end

For offline verification without fetching JWKS from the measurement fleet, set:

config.measurement_result_jwks = JSON.parse(ENV.fetch("REPUTABLE_MEASURE_RESULT_JWKS"))

Relevant environment variables:

REPUTABLE_MEASURE_BASE_URL=https://cloud.reputable.click
REPUTABLE_MEASURE_CUSTOMER_ID=cus_...
REPUTABLE_MEASURE_CAPABILITY=eyJ...
REPUTABLE_MEASURE_PRIVATE_JWK='{"kty":"EC","crv":"P-256",...}'
REPUTABLE_MEASURE_RESULT_JWKS='{"keys":[...]}'
REPUTABLE_MEASURE_TOKEN_TTL=120

Middleware Configuration

Basic Usage

# config/application.rb
config.middleware.use Reputable::Middleware

With Options

config.middleware.use Reputable::Middleware,
  # Skip certain paths (health checks, assets, etc.)
  skip_paths: ['/health', '/healthz', '/assets', '/packs'],

  # Skip all requests under a path prefix
  # Example: /bot_verifications/new, /bot_verifications/123, etc.
  skip_path_prefixes: ['/bot_verifications'],
  
  # Skip by file extension
  skip_extensions: ['.js', '.css', '.png', '.jpg', '.svg'],
  
  # Custom skip logic
  skip_if: ->(env) { 
    env['HTTP_X_INTERNAL'] == 'true' ||
    env['PATH_INFO'].start_with?('/admin')
  },
  
  # Add custom tags based on request
  tag_builder: ->(env) {
    tags = []
    tags << "env:#{Rails.env}"
    tags << "mobile:true" if env['HTTP_USER_AGENT']&.include?('Mobile')
    tags
  },
  
  # Async mode (default: true) - tracking runs in background thread
  async: true,

  # Request tracking (default: false) - push request context to Redis
  # Falls back to ENV REPUTABLE_TRACK_REQUEST if not set
  track_request: true,

  # Ignore XHR requests (default: false)
  # Useful for suppressing tracking of background widgets or polling
  ignore_xhr: true,

  # Expose reputation flags in request env for views/controllers (default: true)
  # Sets request.env['reputable.ignore_analytics'] for any untrusted_* status
  expose_reputation: true

Optional Reputation Gate

If you want the middleware to enforce IP reputation decisions, enable the gate:

config.middleware.use Reputable::Middleware,
  reputation_gate: true,
  challenge_action: :verify,      # Redirect to verification for untrusted_challenge
  block_action: :blocked_page_remote, # Redirect to hosted blocked page (uses app UI settings)
  blocked_page_path: "/_reputable/blocked" # Only used for local blocked page

Notes:

  • For untrusted_challenge, the middleware redirects to the Reputable verification URL.
  • For untrusted_block, the default is to redirect to the hosted blocked page (/_reputable/verify/blocked).
  • The hosted blocked page uses the same app UI settings as the verify/failure pages (siteName, supportEmail).
  • To render a local blocked page instead, set block_action: :blocked_page and pass blocked_page options.
  • To use a custom hosted page, set blocked_redirect_url: "https://example.com/blocked".
  • Use blocked_page_path only for local blocked pages (or to build a custom failure_url).
  • Override challenge_redirect_status (default 302) or verification_force_challenge if needed.

Server/JS Request Reconciliation

When using both server-side tracking (Rack middleware) and client-side JavaScript tracking, requests can be double-counted. The reconciliation system prevents this by correlating requests using a unique request_id.

Automatic Request ID: The middleware automatically generates a UUID for each request and stores it in env['reputable.request_id']. This ID is included when pushing to the Redis buffer.

Exposing to JavaScript: To enable reconciliation, expose the request_id in your views:

<%# In your layout (app/views/layouts/application.html.erb) %>
<meta name="reputable-request-id" content="<%= request.env['reputable.request_id'] %>">

<%# Or via JavaScript variable %>
<script>
  window.reputableConfig = {
    requestId: '<%= request.env['reputable.request_id'] %>'
  };
</script>

The JavaScript snippet will automatically read the request_id from:

  1. data-reputable-request-id attribute on the script tag
  2. window.reputableConfig.requestId
  3. <meta name="reputable-request-id"> tag

Bot Detection Signal: If the middleware tracks a request but JavaScript never fires (after a 10-second grace period), the request is flagged with risk:no_js. This is a strong bot signal—bots and crawlers typically don't render JavaScript.

Default Skipped Paths

The middleware automatically skips:

  • /health, /healthz, /ready, /readyz, /live, /livez
  • /metrics, /favicon.ico
  • Static assets (.js, .css, .png, .jpg, .svg, .woff, etc.)

Controller Helpers (Rails)

class ApplicationController < ActionController::Base
  include Reputable::Rails::ControllerHelpers
end

Available Methods

# Track current request manually (if not using middleware)
track_reputable_request(tags: ['custom:tag'])

# Trust methods (after successful actions)
trust_current_ip(reason: 'payment_completed', order_id: '123')

# Challenge/Block methods
challenge_current_ip(reason: 'suspicious_activity')
block_current_ip(reason: 'abuse_detected')

# Lookup methods
if current_ip_trusted?
  # Skip CAPTCHA, higher rate limits
end

# View/helper flag for untrusted_ignore
if reputable_ignore_analytics?
  # Skip analytics / tracking in views
end

if current_ip_blocked?
  render status: 403
  return
end

# Get full reputation data
rep = current_ip_reputation
# => { status: 'trusted_verified', reason: 'payment', ... }

Verification Redirect Helpers

class SessionsController < ApplicationController
  def new
    require_reputable_verification!
    # If verified, continue
  end
end

Optional args:

  • return_url (default: request.original_url)
  • failure_url (default: API /verify/failure page)
  • session_id (default: session.id)
  • force_challenge (default: false)
  • session_key (default: :reputable_verified_at)

You can check or clear the session flag:

reputable_verified?
clear_reputable_verification!

Reputation Gate Helpers

class SessionsController < ApplicationController
  def new
    require_reputable_reputation!
    # If not blocked/challenged, continue
  end
end

require_reputable_reputation! will:

  • Render a blocked page for untrusted_block
  • Run verification flow for untrusted_challenge

You can also render the blocked page directly:

render_reputable_blocked_page(
  site_name: "Example Store",
  support_email: "support@example.com",
  support_url: "https://example.com/support"
)

Manual API Usage

Request Tracking

# Synchronous (blocks until complete)
Reputable.track_request(
  ip: request.ip,
  path: request.path,
  query: request.query_string,
  method: request.request_method,
  user_agent: request.user_agent,
  referer: request.referer,
  tags: ['view:page:product']
)

# Asynchronous (fire-and-forget, recommended)
Reputable.track_request_async(
  ip: request.ip,
  path: request.path
)

Trusted Security Events

For the full cross-service contract and Go API behavior, see docs/SECURITY_EVENTS.md.

Reputable.track_security_event(
  event_type: "login_failed",
  ip: request.ip,
  account_id: .id,
  user_id: current_user.id,
  session_id: session.id.to_s,
  request_id: request.request_id,
  reason_code: "invalid_password",
  context: {
    country: "US",
    proxy_class: "none",
    ja4: request.headers["X-JA4"],
    ua_family: "chrome"
  },
  resource: {
    type: "account",
    id: .id,
    action: "login"
  },
  metadata: { auth_method: "password" }
)

# Async (recommended)
Reputable.track_security_event_async(
  event_type: "permission_denied",
  ip: request.ip,
  account_id: .id,
  user_id: current_user.id,
  reason_code: "missing_scope"
)

Reputation Management

# Trust IP (behavioral by default, uses default TTL)
Reputable.trust_ip(request.ip, reason: 'behavior_trust', order_id: order.id)

# Trust IP as verified (forever, explicitly)
Reputable.trust_ip(
  request.ip,
  reason: 'payment_completed',
  status: :trusted_verified,
  ttl: 0,
  order_id: order.id
)

# Challenge (require CAPTCHA, etc.)
Reputable.challenge_ip(request.ip, reason: 'unusual_activity')

# Block (with custom TTL)
Reputable.block_ip(request.ip, reason: 'abuse', ttl: 7.days.to_i)

# Ignore in analytics (internal monitoring, etc.)
Reputable.ignore_ip(request.ip, reason: 'internal_monitoring')

Reputation Lookup (O(1) Redis)

# Quick boolean checks
Reputable.trusted_ip?(request.ip)    # => true/false
Reputable.blocked_ip?(request.ip)    # => true/false
Reputable.challenged_ip?(request.ip) # => true/false

# Get status string
Reputable.lookup_ip(request.ip)
# => "trusted_verified" or "untrusted_block" or nil

# Full lookup with metadata
Reputable.lookup_reputation(:ip, request.ip)
# => { status: "trusted_verified", reason: "payment_completed", 
#      source: "app_server", updated_at: 1703123456789, 
#      expires_at: 0, metadata: { order_id: "123" } }

ASN Reputation

Apply and lookup reputations for entire ASNs (Autonomous System Numbers). Useful for blocking datacenter traffic or known-bad networks.

# Apply reputation to an ASN
Reputable.block_asn("15169", reason: "datacenter_abuse")
Reputable.challenge_asn("7922", reason: "suspicious_traffic")
Reputable.trust_asn("16509", reason: "known_partner")
Reputable.ignore_asn("32934", reason: "internal_monitoring")

# Quick boolean checks
Reputable.blocked_asn?("15169")    # => true/false
Reputable.challenged_asn?("7922")  # => true/false
Reputable.trusted_asn?("16509")    # => true/false

# Get status string
Reputable.lookup_asn("15169")
# => "untrusted_block" or nil

# Full lookup with metadata
Reputable.lookup_reputation(:asn, "15169")
# => { status: "untrusted_block", reason: "datacenter_abuse", ... }

Note: ASNs are normalized automatically - both "15169" and "AS15169" work.

Enforcing ASN Reputation in Controllers

If you want to challenge or block requests based on ASN reputation (independent of the middleware's IP-based gate), add a controller filter:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :enforce_asn_reputation

  private

  def enforce_asn_reputation
    return if reputable_verified?
    
    asn = request_asn
    return unless asn
    
    if Reputable.challenged_asn?(asn)
      redirect_to Reputable.verification_url(
        return_url: request.original_url,
        session_id: session.id.to_s
      ), status: 302, allow_other_host: true
    elsif Reputable.blocked_asn?(asn)
      redirect_to Reputable.blocked_page_url, status: 302, allow_other_host: true
    end
  end

  def reputable_verified?
    session[:reputable_verified] || session[:reputable_verified_at].present?
  end

  def request_asn
    # Option 1: From a header (if HAProxy/Cloudflare provides it)
    # request.headers["X-ASN"]&.sub(/^AS/i, "")
    
    # Option 2: From your own GeoIP lookup
    # GeoIP.lookup(request.remote_ip)&.asn
    
    # Option 3: Stored in request.env from earlier middleware
    # request.env["app.asn"]
  end
end

To apply only to specific controllers or actions:

class CheckoutController < ApplicationController
  before_action :enforce_asn_reputation, only: [:create]
end

User Verification & Trust Flow

When you identify a suspicious user (e.g., high risk score or specific tag), you can redirect them to the Reputable verification page. This page performs advanced browser checks and challenges (CAPTCHA) if necessary.

1. Generating a Signed Redirect URL

To effectively hand off verification handling to Reputable, generate a signed verification URL:

# In your controller
if suspicious_activity_detected?
  redirect_url = Reputable.verification_url(
    return_url: request.original_url, # Where to send them back after verification
    failure_url: root_url,            # Optional: where to send if they fail/garbage token
    session_id: session.id,           # Optional: link specific session
    force_challenge: false            # Optional: if true, always show CAPTCHA (for testing)
  )
  
  redirect_to redirect_url
end

Options:

  • return_url (required): Where to redirect after successful verification
  • failure_url (optional): Where to redirect on failure (defaults to API /verify/failure page)
  • session_id (optional): Bind verification to a specific session
  • force_challenge (optional): If true, always show CAPTCHA even for trusted users. Useful for testing the challenge flow.

2. Handling the Return Redirect

When the user passes verification (or is determined to be already trusted/clean), they are immediately redirected back to your return_url with signed parameters.

Middleware (Automatic Handling): If you are using Reputable::Middleware (recommended), this is handled automatically. The middleware detects the return parameters, verifies the signature, and sets env['reputable.verified'] = true.

# In your controller
if request.env['reputable.verified']
  # User just passed verification!
  # You might want to graduate them to trusted status locally
  trust_current_ip(reason: 'interactive_verification')
end

Manual Verification: If you need to verify manually (e.g., custom controller logic):

# In your controller action (the return_url target)
if params[:reputable_s] || params[:reputable_signature]
  if Reputable.verify_redirect_return(params)
    # Signature is VALID. Meaning Reputable actually sent this user back.

    decoded = Reputable.decode_reputable_response(params)
    status = decoded&.dig("status") || params[:reputable_status]
    outcome = decoded&.dig("outcome") || params[:reputable_outcome]
    country = decoded&.dig("country") || params[:reputable_country]
    
    if status == 'pass'
      # Grant access
    end
  else
    # Invalid signature - potential tampering attempt!
    render plain: "Verification failed", status: 403
  end
end

Return Parameters: The current Go API returns compact signed parameters:

  • reputable_r: Base64url-encoded response payload
  • reputable_s: HMAC-SHA256 signature of the decoded payload

Decode reputable_r with Reputable.decode_reputable_response(params) to read:

  • status: pass or fail
  • session_id: The session ID you provided
  • outcome: The specific reputation outcome (e.g., trusted_verified)
  • ignore_analytics: true/false flag
  • country: ISO country code
  • challenge_passed: true when an interactive challenge was completed

The Ruby client still accepts the legacy expanded parameters (reputable_status, reputable_session_id, reputable_outcome, reputable_ignore_analytics, reputable_country, reputable_challenge_passed, reputable_signature) for backward compatibility.


Resilience & Failsafe Features

Never Breaks Your App

The gem is designed with resilience as the top priority:

  1. All operations fail silently - Returns false/nil on any error
  2. No exceptions propagate - Everything is wrapped in rescue blocks
  3. Circuit breaker - After 5 failures, stops trying for 30 seconds

Disable via Environment

# Completely disable (useful for test/CI environments)
REPUTABLE_ENABLED=false
# Enable request tracking (push request context to Redis)
REPUTABLE_TRACK_REQUEST=true
# Check in code
if Reputable.enabled?
  # Only when enabled
end

Safe Return Values

Operation Returns on Failure
track_request false
trust_ip, block_ip, etc. false
lookup_ip, lookup_reputation nil
trusted_ip?, blocked_ip? false
Middleware tracking silently skipped

Circuit Breaker

  • Opens after 5 consecutive failures
  • While open, returns defaults immediately (no Redis calls)
  • Resets after 30 seconds

Error Callback

config.on_error = ->(error, context) {
  # Log to your error service
  Rails.logger.warn("Reputable error: #{error.class} in #{context}")
  Sentry.capture_exception(error)
}

Verbose Logging / Debugging

When troubleshooting middleware behavior (e.g., unexpected redirects or blocked requests), enable verbose mode to see every decision the gem makes:

# config/initializers/reputable.rb
Reputable.configure do |config|
  config.verbose = true
  # ...
end
Reputable.logger = Rails.logger

Or via environment variable:

REPUTABLE_VERBOSE=true

When enabled, the middleware logs at info level with a [Reputable] prefix. Example output for a challenged request:

[Reputable] request: GET / ip=68.6.118.12 rid=abc-123
[Reputable] state: enabled=true operational=true gate=true track=false
[Reputable] extract_ip: 68.6.118.12 (from HTTP_X_FORWARDED_FOR: 68.6.118.12, 10.0.0.1)
[Reputable] lookup: reputation:ip:68.6.118.12 → status=untrusted_challenge reason=inherited:asn:7922 source=inherited
[Reputable] gate: ip=68.6.118.12 status="untrusted_challenge"
[Reputable] verified_session?: false (reputable_verified_at= reputable_verified=)
[Reputable] gate: CHALLENGING ip=68.6.118.12 action=verify
[Reputable] challenge: REDIRECTING 302 → https://api.reputable.click/_reputable/verify?token=...

When verbose is false (the default), none of these logs are emitted.


Tags for Classification

Use tags to classify requests for behavioral analysis:

# Page context
tags: ['view:page:product']
tags: ['view:page:checkout']
tags: ['view:page:cart']
tags: ['view:page:login']

# Traffic source
tags: ['trust:channel:email']
tags: ['trust:channel:ad']
tags: ['trust:channel:organic']

# Trust signals (from verified actions)
tags: ['trust:financial:payment']
tags: ['trust:auth:login']
tags: ['trust:identity:verified']
tags: ['trust:interactive:captcha']

Testing

Disable in Test Environment

# config/environments/test.rb
ENV['REPUTABLE_ENABLED'] = 'false'

# Or in spec_helper.rb
RSpec.configure do |config|
  config.before(:suite) do
    ENV['REPUTABLE_ENABLED'] = 'false'
  end
end

Mock Lookups

# In tests, lookups return nil when disabled
expect(Reputable.trusted_ip?('1.2.3.4')).to eq(false)
expect(Reputable.lookup_ip('1.2.3.4')).to be_nil

How It Works

  1. Request Tracking: Your Rails app pushes request data to Redis buffers (enable with track_request: true or REPUTABLE_TRACK_REQUEST=true)
  2. Async Processing: Reputable API processes buffers asynchronously
  3. Behavioral Analysis: Requests go through classification and pattern analysis
  4. Reputation Storage: Scores stored in Redis for O(1) lookups

What's Available from Rails

  • IP reputation and history
  • Request classification
  • UA churn detection
  • Cross-request pattern analysis
  • Manual reputation overrides

What Requires Edge Deployment

  • JA4/JA3 TLS fingerprints
  • HAProxy timing analysis
  • Geo-latency heuristics

License

MIT