Skip to main content

Maquina Generators

Rails generators that write standalone code: passwordless auth, Rack::Attack scanner bans with a Security dashboard, job queues and error tracking.

Rails generators that produce standalone application code. No runtime dependency — generate once, own the code forever. Delete the gem when you’re done.


What Is This?

After rails new, every developer follows the same steps: configure authentication, set up request throttling, wire up a job queue, add error tracking. These steps aren’t gaps in the framework — they’re workflow choices that are repetitive and time-consuming.

Maquina Generators make the post-rails new setup as deterministic as the framework itself. The gem is development-only. Everything it produces lives in your app and is yours to modify.

rails new myapp --css tailwind
bundle add maquina-generators --group development
rails generate maquina:app --auth clave
bin/rails db:migrate
bin/dev

Five commands. Auth, multi-tenancy, roles, job queue, error tracking, request protection and a dashboard of what it refused — all generated, all yours.

Generated app homepage showing authentication, background jobs, error tracking, rate limiting, caching, and real-time features


Quick Start

1. Create a Rails App

rails new myapp --css tailwind
cd myapp

2. Add the Gem

bundle add maquina-generators --group development

3. Run the App Generator

rails generate maquina:app --auth registration

4. Finish Setup

bin/rails db:migrate
bin/rails credentials:edit
# Add: backstage: { username: admin, password: your_password }
bin/dev

Available Generators

Generator Command Purpose
App rails g maquina:app Full application setup (orchestrator)
Clave rails g maquina:clave Passwordless email-code authentication
Registration rails g maquina:registration Password-based auth with accounts and roles
Rack Attack rails g maquina:rack_attack Scanner and flood bans, request throttling
Security rails g maquina:security Dashboard of every Rack::Attack refusal
Solid Queue rails g maquina:solid_queue Background job processing
Solid Errors rails g maquina:solid_errors Error tracking dashboard
Mission Control rails g maquina:mission_control_jobs Job queue monitoring dashboard

The App Generator

The orchestrator. Runs after rails new and configures a complete, production-ready application in a single command.

rails g maquina:app --auth clave --prefix /admin --port 3000

What It Does

  1. Adds gems — brakeman, standard, rails-i18n, maquina-components, aws-sdk-s3
  2. Creates configs — Procfile.dev, .rubocop.yml, .standard.yml
  3. Configures environments — letter_opener for dev, APPLICATION_HOST for production
  4. Installs Rails features — Action Text, Active Storage, Turbo morphing
  5. Runs auth generator — your choice of clave, registration, or none
  6. Runs sub-generators — solid_queue, mission_control_jobs, solid_errors, and security (which brings the rack_attack rules)
  7. Installs Solid adapters — Solid Queue, Solid Cache, Solid Cable, Solid Errors
  8. Installs Maquina Components — UI library ready to use
  9. Creates HomeController — with root route
  10. Sets up multi-database — primary, queue, cache, cable, errors, security

Options

Option Default Description
--auth none Authentication: none, clave, or registration
--prefix /admin URL prefix for ops dashboards
--port 3000 Development server port

Generated Database Configuration

development:
  primary:
    database: storage/development.sqlite3
  queue:
    database: storage/development_queue.sqlite3
  cache:
    database: storage/development_cache.sqlite3
  cable:
    database: storage/development_cable.sqlite3
  errors:
    database: storage/development_errors.sqlite3
  security:
    database: storage/development_security.sqlite3

Authentication: Clave (Passwordless)

Complete passwordless authentication using email verification codes. Users receive a 6-digit code via email to sign in — no passwords to manage, no password resets to build.

rails g maquina:clave

What You Get

Models:

  • Account — multi-tenant container (has_many :users)
  • User — with role enum (member/admin), account association, blocking support
  • Session — browser session tracking with IP and user agent
  • EmailVerification — verification codes with expiry and attempt tracking
  • Current — ActiveSupport::CurrentAttributes with session, user, and account

Controllers:

  • SessionsController — email entry for sign-in
  • Session::VerificationsController — code verification
  • Session::VerificationResendsController — resend with 15-minute cooldown
  • RegistrationsController — account creation (optional)
  • Registration verification controllers

Additional:

  • VerificationMailer — HTML + text email templates
  • AuthenticationCleanupJob — daily cleanup of expired sessions and codes
  • SessionTestHelper — sign_in_as(user) and sign_out for tests
  • Full i18n support (English and Spanish)

How It Works

User enters email → receives 6-digit code → enters code → signed in
  • Codes expire in 15 minutes
  • 15-minute cooldown before resend
  • Rate limited: 10 attempts per 3 minutes
  • Sessions last 30 days (configurable)
  • + characters blocked in emails to prevent alias attacks

Multi-Tenancy

Every user belongs to an Account. The first user who creates an account becomes its admin.

# Access anywhere in your app
Current.user          # The signed-in user
Current.account       # The user's account
Current.user.admin?   # Check role

Scoping Queries

class ProjectsController < ApplicationController
  def index
    @projects = Current.account.projects
  end

  def create
    @project = Current.account.projects.build(project_params)
    # ...
  end

  private

  def set_project
    @project = Current.account.projects.find(params[:id])
  end
end

Options

Option Default Description
--skip-views false Skip view templates
--skip-registration false Skip sign-up flow (sign-in only)

Authentication: Registration (Password-Based)

Password-based authentication that builds on Rails 8’s built-in rails generate authentication. Adds multi-tenancy with an Account model, user roles, and a registration flow.

rails g maquina:registration

What It Adds to Rails Auth

Rails 8’s authentication generator gives you login but no signup. Registration adds:

  • Account model with has_many :users
  • User gains belongs_to :account and role enum (admin/member)
  • Current.account delegation
  • RegistrationsController — creates Account + User in a single transaction
  • Tailwind-styled views
  • English and Spanish translations

Generated Models

class Account < ApplicationRecord
  has_many :users, dependent: :destroy
  validates :name, presence: true
end

class User < ApplicationRecord
  has_secure_password
  has_many :sessions, dependent: :destroy
  belongs_to :account
  validates :name, presence: true
  enum :role, { member: "member", admin: "admin" }, default: "member"
end

class Current < ActiveSupport::CurrentAttributes
  attribute :session
  delegate :user, to: :session, allow_nil: true
  delegate :account, to: :user, allow_nil: true
end

Registration Flow

class RegistrationsController < ApplicationController
  allow_unauthenticated_access
  rate_limit to: 10, within: 3.minutes, only: :create

  def create
    ActiveRecord::Base.transaction do
      account = Account.create!(name: params[:account_name])
      user = account.users.create!(
        name: params[:name],
        email_address: params[:email_address],
        password: params[:password],
        role: :admin
      )
    end
    start_new_session_for user
    redirect_to root_path
  end
end

Options

Option Default Description
--skip-views false Skip view templates

Rack Attack

Request protection with Rack::Attack. Bans vulnerability scanners and addresses that ignore the throttle, instead of refusing one request at a time.

rails g maquina:rack_attack
rails g maquina:rack_attack --login-path /sign_in

Default Protections

Rule Trigger Response
Scanner ban (fail2ban/scanners) 3 scanner paths in 10 minutes 403, IP banned for 7 days
Flood ban (allow2ban/flood) 600 requests in 5 minutes 403, IP banned for 1 day
General throttle (req/ip) Over 300 requests in 5 minutes 429
Sign-in throttle (login/ip) Over 5 POST /session in 20 seconds 429
Safelist 127.0.0.1, ::1 Never refused

A scanner sprays hundreds of guesses in a couple of minutes. Refusing each matching path lets every other guess through to routing, so the third scanner path bans the address for a week. The flood ban is twice the general throttle: an address still sending at that rate has ignored every 429 since it hit the limit.

Scanner paths are PHP files (*.php), WordPress paths (/wp-admin, /wp-login, /xmlrpc.php), sensitive files anywhere in the path (.env, .git, .aws, .ssh, /etc/passwd), backup archives (.zip, .sql, .tar.gz, .bak) and scanner targets (/cgi-bin, /phpmyadmin, /actuator). /rails/active_storage is exempt from the scanner rule and the general throttle, and /assets from the throttle.

Every refusal is logged as an [ATTACK] line with its IP — Rack::Attack answers before Rails, so it is the only trace in the log. To see refusals on a page, add the Security Dashboard.

Tuning

The knobs are constants on Rack::Attack in config/initializers/rack_attack.rb:

SCANNER_MAXRETRY = 3
SCANNER_FINDTIME = 10.minutes
SCANNER_BANTIME = 7.days

GENERAL_LIMIT = 300
GENERAL_PERIOD = 5.minutes
FLOOD_MAXRETRY = GENERAL_LIMIT * 2
FLOOD_BANTIME = 1.day

LOGIN_PATH = "/session"
LOGIN_LIMIT = 5
LOGIN_PERIOD = 20.seconds

Rack::Attack.banned?(ip) answers whether an address is banned right now. Bans and counters live in Rails.cache, so production needs a shared store such as Solid Cache.

Options

Option Default Description
--login-path /session Path whose POST the sign-in throttle counts

Solid Queue

Sets up Solid Queue as your Active Job backend with a separate database and Procfile integration.

rails g maquina:solid_queue --database sqlite3

Configuration

# config/solid_queue.yml
default: &default
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 3
      polling_interval: 0.1
  recurring:
    authentication_cleanup:
      class: AuthenticationCleanupJob
      schedule: every day at 3am

Options

Option Default Description
--database sqlite3 Database adapter (sqlite3 or postgresql)

Solid Errors

Error tracking dashboard with custom Tailwind views and HTTP basic auth.

rails g maquina:solid_errors --prefix /admin

What You Get

  • Custom Tailwind-styled error views
  • HTTP basic auth (credentials-first, ENV fallback)
  • Severity badge helpers
  • Clipboard and backtrace filter Stimulus controllers
  • Engine mounted at <prefix>/errors
  • Self-contained mailer templates, so send_emails works without the dashboard’s helpers
  • Shared admin navigation bar (Overview, Errors, Jobs, Security) and the admin overview at <prefix>

Authentication

# Checks in order:
# 1. Rails.application.credentials.backstage.username / .password
# 2. ENV["SOLID_ERRORS_USER"] / ENV["SOLID_ERRORS_PASSWORD"]

Set up credentials:

bin/rails credentials:edit
backstage:
  username: admin
  password: your_secure_password

Options

Option Default Description
--prefix required URL prefix (e.g., /admin)
--user-env-var SOLID_ERRORS_USER Custom env var for username
--password-env-var SOLID_ERRORS_PASSWORD Custom env var for password
--copy-views true Include custom Tailwind views

Mission Control Jobs

Job queue monitoring dashboard with custom Tailwind views. 41 view files styled to match your application.

rails g maquina:mission_control_jobs --prefix /admin

What You Get

  • Full Tailwind-styled dashboard for Solid Queue
  • Job status badges, queue views, worker monitoring
  • Recurring task management
  • Engine mounted at <prefix>/jobs
  • Shared admin navigation (Overview, Errors, Jobs, Security) and the admin overview at <prefix>
  • HTTP basic auth (same credentials as Solid Errors)

Options

Option Default Description
--prefix required URL prefix (e.g., /admin)
--user-env-var MISSION_CONTROL_JOBS_USER Custom env var for username
--password-env-var MISSION_CONTROL_JOBS_PASSWORD Custom env var for password
--copy-views true Include custom Tailwind views

Security Dashboard

A backstage page for everything Rack::Attack refused in the last 7 days, next to Solid Errors and Mission Control Jobs.

Each throttled, blocked or banned request is recorded as a Security::AbuseEvent in its own security database and kept for 30 days.

rails g maquina:security --prefix /admin
bin/rails db:prepare
Security dashboard in light mode: refused request stats, throttled addresses, and blocked addresses marked banned now, expired or not banned Security dashboard in dark mode: 3,247 refused requests from 21 addresses, with throttled and blocked address tables

What the Page Shows

The top five of each, for the last 7 days. Times are UTC. No filters and no pagination: it is a briefing, not a log viewer.

Section What it answers
Stats Refused requests, throttled, blocked or banned, distinct addresses
Throttled addresses Who went over the general or sign-in limit, and which rules they hit
Blocked addresses Every address a blocklist refused: Banned now, Expired, or Not banned
Most requested blocked paths What the scanners were after
Sign-in throttles Addresses refused on POST /session, kept apart from scanner noise
Targeted hosts Which hostnames the refusals were aimed at
By day Throttled, blocked and banned per day, zeros kept so a spike reads against its neighbours
Recent refusals The last 20, newest first

Banned now is Rack::Attack’s own answer at render time, so an expired ban flips its badge instead of disappearing. Not banned is an address with a strike or two on the scanner rule — or a slow scanner that stays under three paths in ten minutes, which the page makes visible.

Once an address is banned, every request it sends is refused, its ordinary pages included. The paths table lists only the paths Rack::Attack.scanner_path? recognises, so / and /up never crowd out /.env.

What You Get

  • maquina:rack_attack rules, unless config/initializers/rack_attack.rb already defines Rack::Attack.banned?
  • config/initializers/rack_attack_events.rb — the subscriber that records each refusal; a failed write never turns a 403 into a 500
  • Security::Record, Security::AbuseEvent (30-day retention) and Security::AbuseReport (the page’s figures)
  • db/security_schema.rb and a security: entry in every environment of config/database.yml
  • A daily purge_abuse_events task in config/recurring.yml
  • Backstage::SecurityController, layout and views at <prefix>/security, and a Security tab in the shared admin navigation

Authentication

Same backstage credentials as Solid Errors and Mission Control Jobs. The page answers 503 until they are set, so it is never left open.

# Checks in order:
# 1. Rails.application.credentials.backstage.username / .password
# 2. ENV["SECURITY_USER"] / ENV["SECURITY_PASSWORD"]

Trying It Locally

Localhost is safelisted, so your own requests are never refused. Rack::Attack trusts X-Forwarded-For from 127.0.0.1, so send one to stand in for another address:

for path in /wp-login.php /.env /backup.zip /; do
  curl -s -o /dev/null -w "%{http_code} $path\n" \
    -H "X-Forwarded-For: 203.0.113.10" http://localhost:3000$path
done
# 403 /wp-login.php
# 403 /.env
# 403 /backup.zip   <- third scanner path: banned for 7 days
# 403 /             <- banned, so every path is refused

Development’s :memory_store keeps bans inside the server process: they reset on restart, and a bin/rails runner script cannot see them.

Options

Option Default Description
--prefix required URL prefix (e.g., /admin)
--login-path /session Sign-in path for the Rack::Attack throttle
--user-env-var SECURITY_USER Custom env var for username
--password-env-var SECURITY_PASSWORD Custom env var for password
--copy-views true Include the dashboard views

Architecture Overview

After running maquina:app --auth clave, your project structure looks like this:

app/
  controllers/
    concerns/
      authentication.rb          # Session management
    sessions_controller.rb       # Sign-in
    registrations_controller.rb  # Sign-up
    home_controller.rb           # Root page
  models/
    security/
      abuse_event.rb             # One Rack::Attack refusal
      abuse_report.rb            # The Security page's figures
    account.rb                   # Multi-tenant container
    user.rb                      # Roles + auth
    current.rb                   # Request context
    session.rb                   # Browser sessions
    email_verification.rb        # Verification codes
  mailers/
    verification_mailer.rb       # Email codes
  jobs/
    authentication_cleanup_job.rb  # Daily cleanup

config/
  initializers/
    rack_attack.rb               # Bans and throttles
    rack_attack_events.rb        # Records each refusal
    solid_errors.rb              # Error tracking auth
    mission_control.rb           # Job dashboard auth
  solid_queue.yml                # Queue configuration

Security Defaults

  • Rate limiting on registration and login
  • Rack Attack bans scanners for 7 days and floods for 1 day
  • All controllers require authentication by default
  • Account scoping prevents cross-tenant data access

Ops Dashboards

  • /admin — overview, with a card per installed dashboard and a slot for your own aggregate counts
  • /admin/errors — error tracking (Solid Errors)
  • /admin/jobs — job queue monitoring (Mission Control Jobs)
  • /admin/security — Rack::Attack refusals

All four share one admin navigation and the backstage credentials. The overview and Security pages answer 503 until those credentials are set.


Role-Based Authorization

Use the role enum to restrict actions:

class ProjectsController < ApplicationController
  before_action :require_admin, only: [:destroy]

  private

  def require_admin
    unless Current.user.admin?
      redirect_to projects_path, alert: t("flash.general.forbidden")
    end
  end
end

Roles:

  • admin — first user created with account, full access
  • member — default role, restricted from destructive actions

Customization

All generated code lives in your app. Common customization points:

What Where
Redirect after login app/controllers/concerns/authentication.rb → after_authentication_url
Session duration Change 30.days.from_now in authentication.rb
Code expiration Change 15.minutes.from_now in verification controllers
Resend cooldown EmailVerification::COOLDOWN_MINUTES (default: 15)
View styling Edit view templates directly
Email sender app/mailers/verification_mailer.rb
Translations config/locales/clave.*.yml or registration.*.yml
Rack Attack rules config/initializers/rack_attack.rb — SCANNER_*, FLOOD_*, GENERAL_*, LOGIN_* constants
Security retention Security::AbuseEvent::RETENTION (default: 30 days)
Dashboard credentials bin/rails credentials:edit → backstage:
Queue config config/solid_queue.yml

Requirements

  • Ruby >= 3.2.0
  • Rails >= 7.2
  • Tailwind CSS (for generated views)

The gem has zero runtime dependencies. Add it to your development group, generate your code, and remove it.

# Gemfile
group :development do
  gem "maquina-generators"
end

Next Steps