# Beskar 0.2.0 - Rails Security Engine | AuditBadger

🛡️ Open Source • Rails 8+ • Version 0.2.0 

# Beskar Rails Security Engine

Beskar is an open-source security engine for Rails 8+ with authentication rate limits, configurable account locking, persistent IP bans, scanner detection, and a security dashboard with administrative audit history. It supports Devise and Rails’ built-in authentication.

[View on GitHub](https://github.com/AuditBadger-com/beskar)[Get Started](#installation)

Authentication Limits

Coordinated through your database

Persistent IP Bans

Shared across workers and restarts

Auditable Administration

Operator identity and reasons

Released September 21, 2026

## What’s new in 0.2.0

Coordinated enforcement, durable session revocation, and administration you can trace back to an operator.

### Admission before password verification

IP and account limits coordinate through the database, with enforced backoff. Devise password attempts are checked before credential verification. Redis or another shared cache is not required.

### Locks revoke existing sessions

Supported account locks invalidate existing sessions, including Devise remember-me credentials. Unlocking does not restore old credentials. Rails-native applications must integrate the session guards below.

### Administration with a history

Dashboard ban changes, export preparation, and authorized runtime configuration changes record operator identity and reasons. Runtime configuration changes require separate authorization and remain local to the process.

### Start with observation

Monitor-only mode suppresses Beskar’s automatic bans, account locks, and emergency resets. Observed counters are separate from enforcement counters. Your host’s own authentication policies still apply.

### Controlled exports

Filtered CSV and JSON exports have separate permissions, require an actor and reason, and use cursor pages capped at 1,000 rows. CSV output includes formula-injection defenses.

### Optional notifications

Opt-in email jobs run after commit for supported lock and reset events. Delivery uses your application’s mail and job infrastructure; recovery remains owned by your application or Devise.

See the [0.2.0 changelog](https://github.com/AuditBadger-com/beskar/blob/master/CHANGELOG.md) and [RubyGems release](https://rubygems.org/gems/beskar/versions/0.2.0). Upgrading from 0.1.0 requires the [integration and rollout steps](#integration) below.

 Layered Protection 

## Security Features

Explicit rules, persistent state, and visibility into authentication and scanner activity

### Scanner-Path Detection

Detects scanner paths and narrowly scoped Rails exception signals. Cumulative scoring can trigger escalating IP bans when enforcement is enabled. General SQL injection/XSS filtering, JavaScript challenges, and honeypots are not implemented.

Review scanner signals before enabling blocking

### Geographic Anomaly Signals

Timestamped login history can identify geographic anomalies that contribute to risk. Travel evidence alone does not reach the default locking threshold. Real geographic evidence requires a configured MaxMind database.

Default mock geography does not generate geographic risk

### Coordinated Rate Limiting

The database coordinates IP/account admission windows and backoff across workers. Enforcement does not depend on Rails.cache. Global login budgets and request-wide blocking for exhausted IP login quotas are opt-in.

Shared authoritative database required

### Risk-Based Locking

Opt-in locking uses explicit heuristics: User-Agent signals, recent authentication failures, and geographic anomalies. These are not verified device identities or an IP reputation feed. Automatic trust discounts for repeated IPs and account unlocks were removed.

Heuristic scores, not proof of account takeover

### Persistent IP Banning

The database is authoritative for every ban decision, across workers and restarts. Ban duration can escalate with scanner activity. Stale cache entries do not decide whether a request is blocked.

Database-backed decisions

### Security Events and Administrative History

Security-event logging is optional; enforcement does not depend on saving those events. Required administrative history records supported operator actions. Neither provides universal auditing of activity in your application.

Separate observation and administration records

## A dashboard with explicit permissions

Review events, manage bans, and inspect administrative history. Permissions are server-enforced: Beskar checks each request against the permissions granted by your application.

_Security overview
          Review recorded events, risk bands, and IP bans._
 ![Beskar dashboard showing security event statistics and IP bans](https://humadroid-static-assets.s3.amazonaws.com/beskar/beskar-dashboard.png)

_IP ban management
            Inspect banned addresses, reasons, and expiry times._
 ![Beskar IP ban list showing addresses, ban reasons, and durations](https://humadroid-static-assets.s3.amazonaws.com/beskar/beskar-banned-ips.png)

_Security event details
            Inspect the evidence and metadata recorded for an event._
 ![Beskar security event detail with risk score and request metadata](https://humadroid-static-assets.s3.amazonaws.com/beskar/beskar-security-event.png)

### Separate permissions

Grant read, ban management, export, and administrative-history access separately. Authentication alone grants none of them.

### Reasons and administrative history

Writes and exports require an actor and reason. Supported administrative actions record operator identity and the relevant changes.

### Bounded exports

Export filtered CSV or JSON in cursor pages of up to 1,000 rows, with a reason for every page and CSV formula-injection defenses.

## Install, integrate, then enforce

Start in observation mode. Choose the integration that matches your application’s authentication.

### 1. Install 0.2.0 with the generator

```
# Gemfile
gem "beskar", "~> 0.2.0"
```

```
bundle install
bin/rails generate beskar:install
bin/rails db:migrate
```

The generator copies migrations, mounts the dashboard at `/beskar`, and creates an initializer with current authentication examples. Use this generator for a fresh install; the older `beskar:install` rake task still prints Devise-only next steps.

### 2. Choose your authentication path

#### Devise

```
# app/models/user.rb, alongside your existing Devise modules
include Beskar::Models::SecurityTrackable
```

Add the concern to each protected Devise model. Beskar checks IP/account admission before password verification. Risk-based locking is opt-in and requires Devise’s `:lockable` module; Devise owns its unlock policy.

#### Rails built-in authentication

```
# app/models/user.rb, alongside has_secure_password
include Beskar::Models::SecurityTrackableAuthenticable
```

The concern alone does not complete protection. Add login admission before password verification, guard session creation, and check revocation whenever a session resumes.

#Rails-native login and session guards

Adapt your sessions controller to the generated Rails authentication flow:

```
class SessionsController < ApplicationController
  include Authentication
  include Beskar::Controllers::SecurityTracking

  allow_unauthenticated_access only: %i[new create]
  before_action -> { admit_authentication_attempt(User, :user) }, only: :create

  def create
    if (user = User.authenticate_by(params.permit(:email_address, :password)))
      return unless complete_authentication(user) { start_new_session_for(user) }
      redirect_to after_authentication_url
    else
      track_authentication_failure(User, :user)
      redirect_to new_session_path, alert: "Try another email address or password."
    end
  end
end
```

In your `Authentication` concern, update `resume_session`:

```
def resume_session
  Current.session ||= find_session_by_cookie
  if Current.session && !Beskar::Services::SessionRevocation.native_session_allowed?(Current.session, request: request)
    Current.session = nil
    cookies.delete(:session_id)
  end
  Current.session
end
```

Keep the session-creation block limited to session persistence and response-cookie assignment: it runs in a retryable transaction. Users, sessions, and security state must share the writer connection pool. Adapt the identity lookup if your application does not use `email_address`.

### 3. Authenticate, authorize, and identify the operator

Configure all three callbacks in `config/initializers/beskar.rb`: `authenticate_admin`, `authorize_admin`, and `audit_actor`. Authentication alone does not grant dashboard access.

Permissions are `read`, `manage_bans`, `export`, and `read_audit`. Writes and exports also require a trusted actor and a reason. In these examples, `admin?` and `beskar_permissions` are application-defined methods; return permission names as strings and adapt the checks to your policy.

#Devise dashboard callbacks

Devise resolves the operator through Warden. `:user` is the Devise mapping scope, not an admin role; change it only if your application has a different mapping.

```
Beskar.configure do |config|
  config.authenticate_admin = ->(request) do
    user = request.env['warden']&.authenticate(scope: :user)
    user&.admin?
  end
  config.authorize_admin = ->(request, permission) do
    user = request.env['warden']&.user(scope: :user)
    user&.admin? && user.beskar_permissions.include?(permission.to_s)
  end
  config.audit_actor = ->(request) do
    user = request.env['warden']&.user(scope: :user)
    "User:#{user.id}" if user&.admin?
  end
end
```
#Rails-native dashboard callbacks

Beskar does not inherit your application’s authentication concern, so `Current.session` cannot be assumed populated. Resolve the signed cookie and `Session` model directly, then check revocation. Adapt their names if your host changed Rails’ defaults.

```
Beskar.configure do |config|
  config.authenticate_admin = ->(request) do
    @beskar_admin_user = nil
    session_id = cookies.signed[:session_id]
    auth_session = ::Session.find_by(id: session_id) if session_id
    if Beskar::Services::SessionRevocation.native_session_allowed?(auth_session, request: request)
      @beskar_admin_user = auth_session.user
    end
    @beskar_admin_user&.admin?
  end
  config.authorize_admin = ->(_request, permission) do
    @beskar_admin_user&.admin? && @beskar_admin_user.beskar_permissions.include?(permission.to_s)
  end
  config.audit_actor = ->(_request) do
    "User:#{@beskar_admin_user.id}" if @beskar_admin_user&.admin?
  end
end
```

Examples follow the [0.2.0 initializer template](https://github.com/AuditBadger-com/beskar/blob/master/lib/generators/beskar/install/templates/initializer.rb.tt) and [authentication guide](https://github.com/AuditBadger-com/beskar/blob/master/docs/guides/authentication.md).

### 4. Review traffic and tune the rules

Installation starts with `config.monitor_only = true`. Review scanner matches, authentication limits, and recorded risk factors against representative traffic before enabling enforcement. Account locking, emergency resets, and notifications each need separate opt-in configuration.

Once the integration and thresholds are validated, update your initializer and restart workers together:

```
Beskar.configure do |config|
  config.monitor_only = false
  config.waf[:enabled] = true
end
```

## Integration and upgrading from 0.1.0

- **Share authoritative state.** All workers need the same database; users, sessions, and security state must share the writer connection pool. Separate SQLite files in different containers do not coordinate. Enforcement depends on database availability.
- **Enable optional features deliberately.** Risk locking, emergency resets, notifications, global login budgets, and background analysis require explicit enablement. Background analysis requires a host-provided job. Real geographic evidence needs a configured MaxMind database; the default mock provider does not generate geographic risk.
- **Plan the rollout.** Copy migrations with `bin/rails beskar:install:migrations` and run `bin/rails db:migrate` before starting upgraded workers. Update authentication hooks and dashboard permissions, drain old workers, and coordinate restarts. Plan a one-time Devise sign-out. Old cache counters are not imported; review legacy bans, including bans created by older observation-mode versions.
- **Own the data lifecycle.** Account deletion retains security events and their original identifiers. Automatic anonymization and audit-event purging are not supplied. Define your own retention policy.
- **Integrate other entry points.** API and Action Cable protection requires explicit integration. There is no standalone versioned administration API; CSV/JSON exports are dashboard resources.

Follow the [rollout checklist](https://github.com/AuditBadger-com/beskar/blob/master/docs/operations/security-hardening.md#rollout) and [current documentation](https://github.com/AuditBadger-com/beskar/blob/master/docs/README.md) for host-specific setup.

### What has been verified

The [database verification notes](https://github.com/AuditBadger-com/beskar/blob/master/docs/operations/state-storage.md) report passing PostgreSQL 17 and MySQL 8.4 full-suite CI jobs, plus local MySQL and SQLite runs of 849 tests and 4,619 assertions, with three MaxMind-data skips.

The [browser verification notes](https://github.com/AuditBadger-com/beskar/blob/master/docs/guides/dashboard-and-search.md#browser-verification-and-remaining-limits) report 11 local Chromium tests and 118 assertions across five seeds. They cover forms, UTC expiry handling, exports, and navigation; hosted confirmation of the navigation repair remains open in those notes.

These checks do not establish throughput or production-security guarantees. Validate database capacity, host authentication and recovery, mail delivery, and your deployment before relying on enforcement.

 More from AuditBadger 

## Explore our other [open source projects](/open-source/)

All MIT licensed. All battle-tested in AuditBadger production. All free.

[ 

Self-hosted widget
 
## Ideabug
 

Drop-in feedback platform. One script tag gives your users in-app announcements, bug reports, feature requests with voting, and a public roadmap — anonymous-first, JWT optional.

 
ChangelogFeedbackRoadmap
 
 Learn more 
 ](/open-source/ideabug/)

## Free & Open Source

Beskar is our contribution to the Rails community. Built by AuditBadger as part of our commitment to open source security.

MIT

Licensed

100%

Free Forever

Rails

Native Engine

[Star on GitHub](https://github.com/AuditBadger-com/beskar)[Report an Issue](https://github.com/AuditBadger-com/beskar/issues)

Built with ❤️ by the team at [AuditBadger](/)

Using Beskar in production? We'd love to hear about it. Share your story on GitHub or reach out to us.

We use only essential cookies and privacy-friendly, cookieless analytics ([Plausible](https://plausible.io/privacy-focused-web-analytics)). No advertising or cross-site tracking. [Cookie Policy](/cookie/).

 Got it