CmAdmin Configuration
All cm-admin global settings live in the initializer file (config/initializers/zcm_admin.rb by default) and are exposed through the CmAdmin.configure block. The leading z in the filename ensures the initializer is loaded last in the Rails boot sequence, so any models it references are already defined.
# config/initializers/zcm_admin.rb
CmAdmin.configure do |config|
config.layout = 'admin'
config.included_models = [User, Post]
config.project_name = 'My App'
# ...see sections below for every option
end
Quick Referenceâ
| Option | Type | Default | Description |
|---|---|---|---|
layout | String | 'admin' | Name of the Rails layout used to render cm-admin pages. |
included_models | Array<Class> | [] | Models registered with cm-admin (the ones that appear in the sidebar and routes). |
cm_admin_models | Array<CmAdmin::Model> | [] | Resolved CmAdmin::Model wrappers. Populated automatically â do not set manually. |
project_name | String | '' | Display name shown in the browser title and header. |
sidebar | Array | [] | Custom sidebar navigation entries. When empty, the sidebar is auto-generated from included_models. |
auth_method | Symbol | :otp | Authentication strategy. Valid values: :otp, :password, :custom. |
use_catalyst_otp | Boolean | false | Opt-in flag to register the gem's Devise strategy and model for OTP auth. |
user_class | String|Class | 'User' | The host app user model. Used by auth lookup, mentions, support tickets, and history. Accepts a class or the model name as a string. |
user_lookup | Proc | email/phone lookup | Lambda that resolves a user from an identifier (email or phone). Override for custom lookup logic. |
otp_delivery_channel | Symbol | :email | How OTPs are delivered. Valid values: :email, :phone, :phone_or_email. |
otp_delivery_provider | Symbol|Object | nil | Phone delivery provider. :gupshup or any object responding to #deliver. Required when channel is :phone or :phone_or_email. |
display_role_on_sidebar | Boolean | true | Whether to show the current user's role on the sidebar. |
display_date_format | String | 'M d, Y' | Flatpickr/JS date format used when rendering date fields. |
display_datetime_format | String | 'M d, Y, H:i K' | Flatpickr/JS datetime format used when rendering datetime fields. |
global_search_models | Array<Class> | [] | Models searchable from the global search bar. |
enable_tracking | Boolean | false | Enables Google Tag Manager / tracking snippets in the layout. |
enable_email_logging | Boolean | false | Logs every CmAdmin.send_email delivery to the cm_email_logs table. |
email_template_settings | Hash | { show_copyright: true, footer_content: nil } | Default email template options (copyright footer, custom footer content). |
mcp_enabled | Boolean | false | Mounts the built-in MCP server endpoint at <mount_path>/mcp. |
mcp_system_prompt | String|nil | nil | Custom system prompt sent to MCP AI clients during the initialize handshake. |
layout_css_files | Array<String> | [] | Extra host-app SCSS files compiled into the cm-admin CSS bundle. |
stylesheet_import_paths | Array<String> | [] | Extra directories the Sass compiler searches when resolving @import/@use in bundled SCSS. Useful for exposing stylesheet directories from Ruby gems. |
deployment_alerts | Boolean | true | When false, suppresses Hatchbox deployment Slack notifications during build. |
Layout & Brandingâ
layoutâ
Name of the Rails layout used to render cm-admin pages. Defaults to 'admin'.
config.layout = 'admin'
project_nameâ
Shown in the browser title and the cm-admin header. Commonly wired to your Rails project settings:
config.project_name = Rails.configuration.x.project_settings.name
sidebarâ
When provided, replaces the auto-generated sidebar with custom navigation entries. When left empty (default), cm-admin builds the sidebar from included_models, filtered by the current user's policies.
config.sidebar = [
{ label: 'Dashboard', path: '/dashboard', icon: 'fa-solid fa-gauge' },
{ label: 'Users', path: '/cm_admin/users', icon: 'fa-solid fa-users' }
]
display_role_on_sidebarâ
When true (default), the current user's role name is displayed on the sidebar. Set to false to hide it.
config.display_role_on_sidebar = false
Modelsâ
included_modelsâ
The list of ActiveRecord models that cm-admin should register. Each model must have a corresponding CmAdmin::<Model> concern with a cm_admin DSL block. Only models listed here appear in the sidebar, routes, and MCP tool discovery.
config.included_models = [User, Post, CmRole, ApiToken]
Generator-based features (scheduler, support tickets, etc.) will prompt you to add their models here.
cm_admin_modelsâ
This is a runtime-populated collection of CmAdmin::Model wrappers built from included_models. Do not set this manually â it is managed by cm-admin during boot.
global_search_modelsâ
Models that should be searchable from the global search bar in the header. The first three are surfaced as quick links in the search dropdown.
config.global_search_models = [User, Post]
Authenticationâ
auth_methodâ
Controls which login flow cm-admin renders. Valid values are :otp (default), :password, and :custom. Setting an invalid value raises ArgumentError.
config.auth_method = :password
:otpâ identifier-based OTP login. The user enters their email or phone and receives a one-time code.:passwordâ identifier + password login.:customâ host app provides its own authentication flow. The Devise strategy signs the user in directly viasuper(user).
The chosen method also toggles password-related UI (e.g. "forgot password" links are only shown for :password).
See OtpAndAuth for the full setup guide including migration from host-app controllers to the gem.
use_catalyst_otpâ
Opt-in flag that registers the gem's Devise strategy (CmAdminAuthenticatable) and model module. When false (default), the gem does not interfere with the host app's Devise setup. When true, the gem registers a Warden strategy and Devise module via ActiveSupport::Reloader.to_prepare so it runs after app initializers but before eager load.
config.use_catalyst_otp = true
user_classâ
The host app's user model. Defaults to 'User' and is lazily constantized, so it can be set before the model is loaded or after a Rails code reload. Pass either the class or its name as a string.
config.user_class = 'AdminUser'
# or
config.user_class = AdminUser
This value is used by the default user_lookup, the support-ticket @mentions model, the history page (whodunnit resolution), and any other cm-admin feature that needs to talk to the user table. Changing it avoids hardcoded ::User references.
user_lookupâ
A lambda that resolves a user from an identifier string (email or phone). The default lookup tries config.user_class.find_by(email:) first, then falls back to a LIKE match on phone_number using the trailing digits.
config.user_lookup = lambda { |identifier|
AdminUser.find_by(email: identifier) || AdminUser.find_by(phone_number: identifier)
}
The lambda is called by the Devise strategy and the OtpSessions concern. Override it to customize lookup logic (e.g. normalized phone formats).
otp_delivery_channelâ
Controls how OTP codes are delivered. Valid values: :email (default), :phone, :phone_or_email. Setting an invalid value raises ArgumentError.
config.otp_delivery_channel = :phone_or_email
:emailâ sends OTP via email only (usingCmAdmin::DeliveryProviders::Email).:phoneâ sends OTP via the configuredotp_delivery_provideronly.:phone_or_emailâ the login screen shows a toggle between phone and email login. A phone field (with country-code selector) is shown by default, with a "Use email instead" link to switch to an email field (and a "Use phone instead" link to switch back). OTP delivery follows the chosen identifier: phone numbers are sent via the phone provider only, emails via email only.
otp_delivery_providerâ
The provider used for phone-based OTP delivery. Accepts a Symbol (resolved to a class under CmAdmin::DeliveryProviders) or any object responding to #deliver(user:, otp:, template:).
# Using the built-in Gupshup provider
config.otp_delivery_provider = :gupshup
The gem ships with CmAdmin::DeliveryProviders::Gupshup for WhatsApp OTP delivery. Gupshup credentials are read from Rails.application.credentials[:gupshup] and should include :api_key, :source_number, and :templates (a hash mapping template names to Gupshup template IDs).
To use a custom provider, pass any object that responds to #deliver:
class MySmsProvider
def deliver(user:, otp:, template:)
# send SMS via your provider
end
end
config.otp_delivery_provider = MySmsProvider.new
Date & Time Formattingâ
display_date_format and display_datetime_formatâ
These are Flatpickr formatting strings passed to the JS date/datetime pickers used across index and form views.
config.display_date_format = 'M d, Y' # e.g. "Aug 6, 2026"
config.display_datetime_format = 'M d, Y, H:i K' # e.g. "Aug 6, 2026, 14:30 PM"
These only affect the picker UI. Use the
format:option on individualcolumn/fielddeclarations to override how stored values are rendered.
Emailâ
enable_email_loggingâ
When true, every email sent via CmAdmin.send_email is recorded in the cm_email_logs table with delivery status, recipients, failure reasons, and record association. Disabled by default.
config.enable_email_logging = true
To set up the required table:
rails g cm_admin:add_email_logs
rails db:migrate
See CmAdminEmail for the full email sending API.
email_template_settingsâ
Default options applied to every email template unless overridden per-call:
config.email_template_settings = {
show_copyright: true, # render the © footer
footer_content: 'Acme Inc.' # custom HTML/text shown above the copyright
}
Both keys can be overridden on individual CmAdmin.send_email calls via the email_template_settings: argument.
Trackingâ
enable_trackingâ
When true, cm-admin injects Google Tag Manager snippets into the layout head and body. Use this only in environments where GTM is configured.
config.enable_tracking = true
MCP Serverâ
cm-admin ships with a built-in Model Context Protocol server that exposes your admin-registered models to AI clients. See McpServer for the full setup guide.
mcp_enabledâ
When true, the MCP Rack endpoint is mounted at <cm_admin_engine_mount_path>/mcp and models with mcp_actions in their DSL block become available as list/show tools. Defaults to false.
config.mcp_enabled = true
mcp_system_promptâ
A custom system prompt sent to the AI client during the MCP initialize handshake. This is the right place to inject domain-specific knowledge, business semantics, and known limitations the AI should respect.
config.mcp_system_prompt = <<~PROMPT
You are reading records from the Acme Platform via MCP tools.
Data model notes:
- `assignee` is the current owner of a work item, not the original creator.
- `internal_feedback_rating` is unused; QA ratings live inside each work
item's history and cannot be bulk-aggregated through MCP tools.
Best practices:
- Use aggregate tools for counts/sums/averages instead of listing all rows.
- Use `fetch_options` to resolve select filter values before filtering.
PROMPT
When left nil (default), the server falls back to a generic one-line instruction. Keep the prompt focused on semantic guidance â avoid pasting the full schema, which the AI can already discover through list_models and tool schemas.
CSS Bundlingâ
layout_css_filesâ
Extra SCSS files from the host app (app/assets/stylesheets/) that should be compiled into the cm-admin CSS bundle. Each entry is a filename relative to app/assets/stylesheets/.
config.layout_css_files = ['application.scss', 'theme_overrides.scss']
This is used by the cm-admin CSS bundler (CmAdminCssBundler) when building the Tailwind/Sass output. Each file is compiled to app/assets/builds/<name>.css.
stylesheet_import_pathsâ
Extra directories the Sass compiler should search when resolving @import/@use directives in the bundled stylesheets. This is commonly used to expose stylesheet directories from Ruby gems so SCSS can find their partials.
config.stylesheet_import_paths = [
"#{Gem.loaded_specs['bootstrap'].full_gem_path}/assets/stylesheets",
"#{Gem.loaded_specs['select2-rails'].full_gem_path}/vendor/assets/stylesheets",
"#{Gem.loaded_specs['intl-tel-input-rails'].full_gem_path}/app/assets/stylesheets"
]
These paths are passed to the Sass compiler as --load-path=<path> flags and are combined with the default layout_css_files build.
Deploymentâ
deployment_alertsâ
When false, the Hatchbox deployment scripts (.hatchbox/lib/hatchbox_notification.rb) detect this setting via CmAdmin.config.deployment_alerts and exit before sending any Slack notification. Useful for suppressing notifications in certain environments (e.g. CI builds, preview apps).
config.deployment_alerts = false
See HatchboxScripts for the full deployment scripts guide.