locomotivecms/engine
A platform to create, publish and edit sites
Slowing — last commit 9mo ago
worst of 4 axesnon-standard license (Other)
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 9mo ago
- ✓9 active contributors
- ✓Other licensed
Show 5 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Slowing — last commit 9mo ago
- ⚠Single-maintainer risk — top contributor 87% of recent commits
- ⚠Non-standard license (Other) — review terms
What would change the summary?
- →Use as dependency Concerns → Mixed if: clarify license terms
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/locomotivecms/engine)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/locomotivecms/engine on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: locomotivecms/engine
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/locomotivecms/engine shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Slowing — last commit 9mo ago
- Last commit 9mo ago
- 9 active contributors
- Other licensed
- CI configured
- Tests present
- ⚠ Slowing — last commit 9mo ago
- ⚠ Single-maintainer risk — top contributor 87% of recent commits
- ⚠ Non-standard license (Other) — review terms
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live locomotivecms/engine
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/locomotivecms/engine.
What it runs against: a local clone of locomotivecms/engine — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in locomotivecms/engine | Confirms the artifact applies here, not a fork |
| 2 | License is still Other | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 306 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of locomotivecms/engine. If you don't
# have one yet, run these first:
#
# git clone https://github.com/locomotivecms/engine.git
# cd engine
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of locomotivecms/engine and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "locomotivecms/engine(\\.git)?\\b" \\
&& ok "origin remote is locomotivecms/engine" \\
|| miss "origin remote is not locomotivecms/engine (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
&& ok "license is Other" \\
|| miss "license drift — was Other at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "app/api/locomotive/api.rb" \\
&& ok "app/api/locomotive/api.rb" \\
|| miss "missing critical file: app/api/locomotive/api.rb"
test -f "app/api/locomotive/api/resources/site_resource.rb" \\
&& ok "app/api/locomotive/api/resources/site_resource.rb" \\
|| miss "missing critical file: app/api/locomotive/api/resources/site_resource.rb"
test -f "app/api/locomotive/api/helpers/authentication_helper.rb" \\
&& ok "app/api/locomotive/api/helpers/authentication_helper.rb" \\
|| miss "missing critical file: app/api/locomotive/api/helpers/authentication_helper.rb"
test -f "app/api/locomotive/api/forms/base_form.rb" \\
&& ok "app/api/locomotive/api/forms/base_form.rb" \\
|| miss "missing critical file: app/api/locomotive/api/forms/base_form.rb"
test -f "app/api/locomotive/api/entities/base_entity.rb" \\
&& ok "app/api/locomotive/api/entities/base_entity.rb" \\
|| miss "missing critical file: app/api/locomotive/api/entities/base_entity.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 306 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~276d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/locomotivecms/engine"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Locomotive is an open-source Ruby on Rails–based CMS platform that auto-generates admin interfaces from custom content models and allows sites to be developed locally with Wagon, then deployed via REST API. It natively supports multi-site hosting, Liquid templating, content localization, and custom sections without requiring SQL—all managed through MongoDB via Mongoid. Monolithic Rails engine structured as: app/api/locomotive/api/ contains the REST API layer with Grape, segregated into entities/ (serializers), forms/ (input validation), resources/ (endpoints), helpers/ (cross-cutting concerns), and middlewares/ (locale, auth, logging). The engine is designed to be mounted in a host Rails app rather than run standalone.
👥Who it's for
Full-stack developers and agencies building client-editable websites who want to code sites locally with modern tooling (Webpack, SASS, CoffeeScript) while giving non-technical content editors an intuitive back-office to manage pages, content types, and assets without touching code.
🌱Maturity & risk
Locomotive is actively maintained and production-ready. The repo shows strong CI/CD setup (Travis CI, Coveralls, Code Climate badges in README), comprehensive test coverage, and modern dependency stack (Rails 7, MongoDB 6, Ruby 3+). The project has a public Trello roadmap and active Gitter community support.
Risk is moderate: the codebase is large (842K Ruby LOC) with deep coupling between the API layer (app/api), forms, and entities, making breaking changes likely during major Rails/Mongoid upgrades. No indication of recent commit date in provided data; multi-language i18n dependency on Transifex adds external service risk. Heavy reliance on Mongoid 7 locks you to MongoDB ecosystem.
Active areas of work
No specific recent changes visible in provided data, but the existence of .travis.yml, .tx/config (Transifex), and Babel/SCSS setup suggests active work on CI/CD, internationalization, and front-end tooling. The Trello board is referenced as the source of truth for upcoming features.
🚀Get running
Clone with git clone https://github.com/locomotivecms/engine.git && cd engine. Install dependencies with bundle install (Gemfile present). Run tests with rspec (.rspec config present). Check Rakefile and README.md for development server startup commands (likely rails s for the host app).
Daily commands:
Exact startup command not visible in snippet, but standard Rails engine approach: likely rails s in a host Rails app that mounts the engine. Run tests: rspec or rake spec (.rspec file present). Webpack/asset pipeline managed separately or via Wagon for site development.
🗺️Map of the codebase
app/api/locomotive/api.rb— Entry point for the REST API framework, defines all resource routes and middleware stack that handle site, page, and content management operationsapp/api/locomotive/api/resources/site_resource.rb— Core resource handler for site CRUD operations; sets the pattern for how all domain objects map to API endpointsapp/api/locomotive/api/helpers/authentication_helper.rb— Implements token-based auth across all API endpoints; critical security layer protecting every resourceapp/api/locomotive/api/forms/base_form.rb— Base form validation class that all entities inherit from; enforces data shape before persistenceapp/api/locomotive/api/entities/base_entity.rb— Base entity serializer used across all API responses; defines the standard contract for what data clients receiveapp/api/locomotive/api/middlewares/locale_middleware.rb— Handles multi-language routing and context switching; essential for the multi-site, multi-locale platform
🛠️How to make changes
Add a new API endpoint for a domain resource
- Create a new resource class in app/api/locomotive/api/resources/ that inherits from Grape::API (
app/api/locomotive/api/resources/custom_resource.rb) - Define GET, POST, PUT, DELETE routes and mount them in the main API router (
app/api/locomotive/api.rb) - Create a form validator for input in app/api/locomotive/api/forms/ (
app/api/locomotive/api/forms/custom_form.rb) - Create a response entity serializer in app/api/locomotive/api/entities/ (
app/api/locomotive/api/entities/custom_entity.rb) - Use the form in your resource's POST/PUT endpoints and the entity in response serialization (
app/api/locomotive/api/resources/custom_resource.rb)
Add a new validation rule to a form
- Open the target form class that inherits from base_form.rb (
app/api/locomotive/api/forms/page_form.rb) - Add a custom validator method or include a Dry::Validation schema (
app/api/locomotive/api/forms/page_form.rb) - Call the validator in the resource endpoint before calling persistence_helper save method (
app/api/locomotive/api/resources/page_resource.rb)
Add middleware to intercept requests globally
- Create a new middleware class in app/api/locomotive/api/middlewares/ (
app/api/locomotive/api/middlewares/custom_middleware.rb) - Implement call(env) method to inspect/modify request or response (
app/api/locomotive/api/middlewares/custom_middleware.rb) - Register the middleware in the main API class using use() directive (
app/api/locomotive/api.rb)
Add a new translatable field to entities
- Update the entity serializer to expose i18n-aware field using locale_helper (
app/api/locomotive/api/entities/page_entity.rb) - Register field in the form to accept localized variants (
app/api/locomotive/api/forms/page_form.rb) - Ensure locale_middleware is loaded to provide current locale context (
app/api/locomotive/api/middlewares/locale_middleware.rb)
🔧Why these technologies
- Grape (REST API framework) — Lightweight Sinatra-based DSL perfect for versioned APIs with built-in validation, entity rendering, and middleware hooks
- Dry::Validation or custom form objects — Separates input validation from models; enables reusable schemas and clear error messaging before persistence
- Entity/Presenter pattern for serialization — Decouples API responses from domain models; enables selective field exposure and locale-aware formatting
- Middleware pipeline for cross-cutting concerns — Centralizes authentication, locale routing, and parameter parsing; reduces boilerplate in each endpoint
- Form-based persistence helpers — Abstracts atomic save/rollback logic; ensures consistent transaction handling across all CRUD operations
⚖️Trade-offs already made
-
API-first architecture with separate client (Wagon)
- Why: Decouples frontend tooling from backend, enabling local development workflow without server coupling
- Consequence: Adds complexity in deployments and synchronization; requires careful versioning of API contracts
-
Multi-language support baked into core via locale_middleware
- Why: Sites are multi-locale by design; locale must be resolved early in request pipeline
- Consequence: Every endpoint carries locale overhead; simplifies i18n but adds per-request context switching
-
Entity/form separation instead of single model
- Why: Clear separation of input validation, persistence, and output formatting
- Consequence: More files per domain object; requires discipline to keep them synchronized
🚫Non-goals (don't propose these)
- Real-time updates or WebSocket support (polling/HTTP-based only)
- GraphQL API (REST-only design)
- Built-in frontend UI (back-office generated separately; this is the engine/API only)
- OAuth2 provider (uses token-based auth only)
- Horizontal scaling without external coordination (no distributed session/cache layer shown)
🪤Traps & gotchas
Mongoid binding: All models use Mongoid, not ActiveRecord—associations and validations work differently; migrations are non-standard. Multi-tenancy via Devise: Sites are scoped by account/membership; queries must account for current site context or data will leak across tenants. Grape DSL quirks: The API uses Grape's DSL which differs from Rails controllers—params, authentication, and response serialization follow Grape conventions, not standard Rails. Form object pattern: Most endpoints require corresponding form classes in app/api/locomotive/api/forms/; forgetting this causes silent failures. Transifex dependency: Editing i18n strings requires access to their Transifex project; local changes may be overwritten on sync. No visible strong_parameters: Form objects may use custom validation instead of Rails' strong parameters.
🏗️Architecture
💡Concepts to learn
- Liquid templating — Locomotive sites use Liquid (a safe, minimal templating language) instead of ERB; understanding Liquid syntax and rendering pipeline is essential for working with page and snippet logic
- Grape REST API DSL — The entire
app/api/folder uses Grape's declarative DSL instead of Rails controllers; params, authentication, versioning, and response serialization all follow Grape conventions that differ from standard Rails - MongoDB document-oriented storage via Mongoid — Locomotive uses Mongoid (not SQL) for persistence; schemas are flexible, associations use references/embeds instead of foreign keys, and there are no migrations—understanding document design is critical
- Form object pattern — Input validation and data transformation are centralized in
app/api/locomotive/api/forms/classes rather than Rails' strong_parameters; this pattern isolates business logic and makes the API contract explicit - Entity serializers (Grape entities) — API responses are defined by classes in
app/api/locomotive/api/entities/using Grape's Entity DSL; these control field visibility, nesting, and conditional inclusion in JSON responses - Multi-site tenancy with Devise scoping — Locomotive supports multiple independent sites in one instance; data is scoped by account and site membership—queries and permissions must respect these boundaries or data will leak across tenants
- Middleware pipeline for API concerns — Cross-cutting concerns like locale detection, request logging, and parameter decoding are implemented as Grape middlewares in
app/api/locomotive/api/middlewares/; understanding middleware execution order is crucial for debugging request handling
🔗Related repos
locomotivecms/wagon— Official local development tool for Locomotive sites; developers use Wagon to build sites locally then deploy them to the engine via the REST APIrails/rails— Locomotive is built on Rails 7; understanding Rails conventions and the Rails engine pattern is essential for contributingmongodb/mongoid— Mongoid 7 is the ODM used for all data persistence; model design and query patterns are defined by Mongoid, not ActiveRecordruby-grape/grape— The REST API framework powering app/api/locomotive/api.rb; Grape's DSL and patterns define how endpoints are structuredelabs/pundit— Authorization gem integrated for role-based access control; used to enforce membership and site permissions across API endpoints
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive integration tests for API resources and forms validation
The repo has 16 API resources (account, content_entry, page, site, etc.) and 15 corresponding forms under app/api/locomotive/api/resources/ and app/api/locomotive/api/forms/, but there's no visible spec directory for these critical components. These handle core CMS operations (content creation, site management, asset handling) and lack test coverage that would catch validation or serialization bugs early.
- [ ] Create spec/api/locomotive/api/resources/ directory structure matching app/api/locomotive/api/resources/
- [ ] Add tests for base_form.rb validation patterns used by all 15+ form subclasses
- [ ] Write integration tests for content_entry_resource.rb and content_entry_form.rb (core CMS feature)
- [ ] Add tests for authentication_helper.rb and params_helper.rb used across all resources
- [ ] Test entity serialization in app/api/locomotive/api/entities/ (15 entity classes) to ensure API responses are correct
Refactor monolithic API middleware and helpers into focused modules
The app/api/locomotive/api/helpers/ contains 5 helpers (authentication, locales, pagination, params, persistence) that are likely mixed concerns. Similarly, 3 middlewares in app/api/locomotive/api/middlewares/ may have unclear responsibilities. This creates maintenance overhead and makes testing individual concerns difficult.
- [ ] Extract locale switching logic from locale_middleware.rb into a dedicated Locomotive::LocaleManager service
- [ ] Split params_helper.rb into ParamsValidator and ParamsTransformer modules to separate concerns
- [ ] Create Locomotive::PaginationService to replace pagination_helper.rb and standardize pagination across 16 resources
- [ ] Move persistence_helper.rb logic into model concern (app/models/concerns/) to reduce API-specific coupling
- [ ] Add specs documenting the responsibility of each helper/middleware to prevent feature creep
Add missing CI workflow for API contract/schema validation
The .travis.yml exists but there's no automated API schema validation. With 16 resources, 15 entities, and 15 forms, API contract changes can break client integrations silently. The repo should validate that entity serialization matches documented API contracts before merge.
- [ ] Create GitHub Action workflow (.github/workflows/api-contract-tests.yml) to run on PRs
- [ ] Add OpenAPI/JSON Schema specs for all entities in app/api/locomotive/api/entities/ (currently just .rb classes)
- [ ] Implement schema validation in CI using a tool like json-schema or Swagger/OpenAPI validation
- [ ] Add tests ensuring all 16 resource endpoints respect the schema (e.g., content_entry_resource.rb returns valid ContentEntryEntity)
- [ ] Document breaking API changes in CHANGELOG.md as part of CI checks
🌿Good first issues
- Add missing entity serializers for new content types: the
app/api/locomotive/api/entities/folder shows 14 entities but only covers core models (page, snippet, content_entry); a common task is adding a new entity when models are added to the engine. - Expand test coverage for form validation:
app/api/locomotive/api/forms/defines 14+ form classes but provided data shows no visible spec files for them; writing validation tests for base_form.rb and content_entry_form.rb would be high-impact. - Document API middleware pipeline: The
app/api/locomotive/api/middlewares/folder has 4 middlewares (locale, logger, params_decoder, and one missing from snippet) but no inline documentation; adding clear comments on execution order and side effects would help new contributors.
⭐Top contributors
Click to expand
Top contributors
- @did — 87 commits
- @leio10 — 6 commits
- @vrenaville — 1 commits
- @Arunthogadiya — 1 commits
- @Revathyne — 1 commits
📝Recent commits
Click to expand
Recent commits
87c6636— fix: don't raise an exception if there is no fallback for an unregistered locale (fixed in the new custom fields version (did)417d4a8— fix: set the local fallbacks for the new locales (#1424) (did)a0a3111— feat: add Docker Compose setup for MongoDB 6.0 (#1425) (did)0be4dbc— test: write a spec to prove we can post a JSON request to a Locomotive page (did)e6fb512— add 127.0.0.1 as a localhost address (did)055c488— upgrade Grape which wasn't working with the last version of Rack + File.exists? -> File.exist? + new version of Steam (D (did)07a439c— bump version to 4.2.0.alpha2 (did)6ba85d4— Localized fields don't render correctly in forms (#1414) (did)f4808fd— bump version to 4.2.0.alpha1 (did)e287460— Rails 7 (#1408) (did)
🔒Security observations
- High · Missing Dependency Audit Information —
Gemfile.lock (not provided). The Gemfile.lock content was not provided for analysis. Cannot verify if the Rails engine and its dependencies contain known CVEs or security vulnerabilities. This is critical for a CMS platform that manages user-generated content and handles authentication. Fix: Run 'bundle audit' or 'bundle audit check' regularly. Use tools like Dependabot or Snyk to monitor dependencies. Provide Gemfile.lock for complete security analysis. - High · API Endpoint Exposure Without Visibility of Auth Mechanisms —
app/api/locomotive/api/resources/. Multiple API resources are defined (accounts, content entries, sites, etc.) but without access to the actual resource implementation code, it's unclear if proper authorization checks (ACL, role-based access control) are enforced on sensitive endpoints like membership_resource.rb, site_resource.rb, and content_asset_resource.rb. Fix: Implement and verify proper authorization checks on all API endpoints. Use pundit or similar authorization gem. Ensure role-based access control (RBAC) is enforced. Conduct thorough authorization testing. - High · Form Input Validation Not Visible —
app/api/locomotive/api/forms/. Multiple form classes exist (content_entry_form.rb, page_form.rb, etc.) but their validation logic cannot be verified from the file structure alone. Forms processing user input require strict validation to prevent injection attacks. Fix: Ensure all forms use strong parameter validation. Implement whitelist-based validation for all user inputs. Use Rails strong_parameters. Validate and sanitize content types, especially for rich text fields. - Medium · Potential XSS Risk in Content Rendering —
app/api/locomotive/api/entities/ (content rendering logic not visible). As a CMS platform, LocomotiveCMS likely renders user-generated content (pages, snippets, editable_elements). Without seeing the template rendering code, there's risk of stored XSS if content is not properly escaped. The editable_element_entity.rb and page_entity.rb suggest dynamic content handling. Fix: Ensure all user-generated content is properly escaped when rendered. Use Rails' auto-escaping features. Implement Content Security Policy (CSP) headers. Consider implementing an HTML sanitizer for rich text fields (e.g., sanitize gem). - Medium · Missing Security Configuration Files —
Root configuration files. .env files and other configuration management files are not visible in the provided structure. Secrets management practices cannot be verified. The presence of .tx (Transifex) and .travis.yml suggests CI/CD pipeline, but security configurations are unclear. Fix: Use environment variables or Rails credentials for secrets management. Never commit .env files or secrets to version control. Use Rails' encrypted credentials feature. Implement proper CI/CD secret handling. - Medium · API Authentication Mechanism Unclear —
app/api/locomotive/api/helpers/authentication_helper.rb, app/api/locomotive/api/resources/token_resource.rb. An authentication_helper.rb exists, and token_resource.rb is present, suggesting token-based auth. However, without code visibility, it's unclear if authentication is properly implemented (JWT validation, token expiration, secure token storage). Fix: Implement secure token-based authentication (JWT with proper signing and expiration). Use secure token storage and transmission. Implement rate limiting on authentication endpoints. Ensure tokens are validated on every request. - Medium · Middleware Security Not Verifiable —
app/api/locomotive/api/middlewares/. Middleware files exist (locale_middleware.rb, logger_middleware.rb, params_decoder_middleware.rb) but their implementations cannot be verified. Custom middleware could introduce security issues if not properly implemented. Fix: Review middleware for security best practices. Ensure no sensitive data is logged. Validate all middleware is using Rails security features. Use security middleware like rack-security-headers. - Medium · Database Query Patterns Not Visible —
Database models and queries (not provided). Cannot verify SQL injection protection in models. With entities and resources handling content entries, content types, and assets, vulnerable query patterns could exist. Fix: Use parameterized queries exclusively. Never concatenate user input into SQL. Use Rails O
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.