Short request path
The normalized Welcome request measured 12 Mojito modules. Sessions, database/DBI, file cache, validation, and command tooling are lazy, optional, or outside that plain request path.
Mojito is a minimal, CGI-first Perl web framework for traditional shared hosting. It gives you structured applications, controllers, routes, stash values, bundled escaped-by-default templates, lazy sessions/cache/database access, development commands, and application-owned scaffolding while keeping the fresh-process request path deliberately small.
If you can run a persistent Perl application, use Mojolicious. Mojito exists for the places where traditional CGI is effectively the deployment ceiling.
Independent project by Digital Desert. Mojito is heavily influenced by the excellent work, ideas, and ergonomics of Mojolicious, and is not affiliated with the Mojolicious project. Mojolicious is not a Mojito production runtime dependency.
Normalized CGI p50
37.3 ms
Mojito in the published fresh-process CGI comparison.
Measured Welcome footprint
12 modules
6.78 MiB peak RSS in the normalized Mojito request.
Mojito is for you if...
Mojito probably isn't for you if...
If you can run persistent workers, need WebSockets or SSE, want the full Mojo toolkit, expect a broad plugin ecosystem, or are building a substantial service, choose Mojolicious.
Mojito is deliberately narrow. That narrowness is the product.
Performance proof
The published normalized comparison uses the same Perl executable and the same 140-byte response body across Raw CGI, Mojito, and structured Mojolicious CGI. It is evidence for one specific claim: Mojito keeps traditional-CGI startup and request overhead small.
| Metric | Raw CGI | Mojito | Mojolicious CGI |
|---|---|---|---|
| p50 | 10.875 ms | 37.298 ms | 245.822 ms |
| Added over raw CGI | — | 26.423 ms | 234.947 ms |
| p95 | 11.702 ms | 39.039 ms | 258.283 ms |
Measured result
84.8% lower
Normalized p50 than structured Mojolicious CGI in this specific fresh-process CGI benchmark.
What Mojito adds
26.423 ms
Framework/application work above the measured 10.875 ms raw CGI baseline.
Peak RSS
6.78 MiB
Mojito versus 41.11 MiB for structured Mojolicious CGI: a 34.33 MiB difference in this workload.
Loaded modules
12 vs ~207
Mojito modules versus Mojolicious modules for the normalized Welcome request.
What Mojito actually costs
Raw CGI baseline 10.875 ms
Mojito framework/app +26.423 ms
────────────────────────────────────
Normalized Mojito p50 37.298 ms
Mojito cannot remove process creation, Perl startup, CGI environment setup, output, or process teardown. Its job is to keep everything above that unavoidable floor small.
The measured request still has structure
Sessions, database access, file caching, and auth scaffolding are lazy or optional and are not being claimed as part of this normalized Welcome request.
Why CGI changes the economics
Mojolicious under persistent server
start Perl
load Mojolicious
load application
↓
request
request
request
request
...
Traditional CGI repeats it
start Perl
load framework
load application
request
exit
start Perl
load framework
load application
request
exit
This is a fresh-process CGI benchmark, not a general framework ranking.
Mojolicious carries vastly more capability, and in a persistent deployment much of its startup work is paid when the worker starts instead of on every request. Mojito's measured advantage is specifically about the economics of traditional CGI.
Benchmark methodology and full context →What you get
The normalized Welcome request measured 12 Mojito modules. Sessions, database/DBI, file cache, validation, and command tooling are lazy, optional, or outside that plain request path.
Perl 5.26+ with no Mojolicious production runtime dependency. DBI and database drivers are optional and loaded only when database access is used.
Mojito ships its own EP-like template compiler. Output is escaped by default; <%== %> is the explicit raw-output form. There is no on-disk compiled-template cache.
File-backed cache and the optional file-session backend can outlive a fresh process. Cookie sessions are HMAC-SHA256 signed and loaded only when used.
mojito scaffold:auth generates application-owned auth structure and its migration under database/migrations/. Auth is scaffolding, not a runtime feature every app carries.
HMAC-SHA256 sessions, minimum secret length, 32-byte generated keys, escaped templates, path validation, symlink refusal, and conservative scaffold replacement are covered by the passing security suite.
database:migrate applies ordered SQL migrations transactionally and tracks them in schema_migrations. database:wipe --force resets the selected default database.
Generate a complete app, a whole resource with optional CRUD/CSRF, or individual controllers, models, templates, routes, commands, plugins, and auth.
Mojito stays comfortable with ordinary HTML responses and fragments. HTMX can sit on top without requiring framework-specific browser runtime support.
Layered and lazy
Mojito's architecture is intentionally modular: application and routing at the center, with request/response, sessions, database, cache, validation, rendering, and logging loaded as needed.
Plain request
Only when used
Security is designed into the small pieces.
Templates escape output by default. Cookie sessions use HMAC-SHA256 and require a minimum 16-character secret; scaffolded apps generate a fresh 32-byte random key. Writable storage is private, and scaffolding validates paths, rejects symlinks, and avoids deleting unknown files.
Developer workflow
Start with a clean CGI application, add application-owned pieces as the project grows, then use Mojito's development and database commands without putting the CLI on the normal CGI request path.
Canonical public commands use colon-delimited leaf names. The executable is presented simply as mojito.
mojito scaffold:app --name="MyApp"
Generate a runnable CGI app with public/, structured lib/, templates, database/migrations/, private storage, and a fresh 32-byte secret key.
mojito scaffold:resource article --crud
Generate controller actions, routes, templates, and CSRF-aware CRUD shape while leaving persistence decisions with the application.
mojito scaffold:auth
Add application-owned login/logout/protected-route scaffolding only when needed. The generated auth migration lands in database/migrations/.
mojito database:migrate
Apply database/migrations/*.sql in order, transactionally, while recording completed migrations in schema_migrations.
mojito scaffold:command import_articles
Put maintenance tasks in application-owned commands instead of adding them to the CGI request path.
mojito storage:cleanup
Clean application-owned writable storage explicitly rather than turning housekeeping into request-time work.
Scaffold the whole thing — or one piece
scaffold:controller
scaffold:model
scaffold:template
scaffold:route
scaffold:command
scaffold:plugin
resource shortcut:
scaffold:resource article --crud
Configuration + database utilities
config:set edits arbitrary safe dotted configuration keys atomically and validates the generated CGI with perl -c. secrets.key is reserved.
key:generate writes a new securely generated application key. It always overwrites the current key, so use it deliberately.
database:wipe --force destructively drops the selected default database. It is intentionally explicit and is not part of normal request handling.
Development tools
mojito serve can use Morbo/Mojolicious as an optional development bridge. Static files under public/ are served directly; dynamic requests execute the real public/mojito.cgi in a fresh Perl subprocess.
Browser
↓
Morbo (development only)
↓
static public file ─────────→ served directly
dynamic request
↓
public/mojito.cgi
↓
fresh Perl process
↓
Mojito response
mojito routes
Inspect the registered application route table.
mojito doctor
Check application and shared-host readiness.
Production stays independent.
Mojolicious/Morbo is optional development tooling. A production shared host does not need Mojolicious for Mojito's CGI runtime.
Development boundary: ordinary static files under public/ can be served directly, but the bridge never serves the front controller mojito.cgi or dotfiles such as .htaccess statically. Dynamic requests still execute the real CGI entry point in a fresh Perl subprocess.
Mojolicious ideas, filtered through CGI
Mojito is heavily influenced by Mojolicious. The familiar ideas are intentional; so are the omissions. Mojito has no Mojolicious production runtime dependency.
Familiar ideas
$c-style controller contextIntentionally absent
Those Mojolicious features aren't omitted because they're bad features. They're omitted because paying to initialize machinery for a process that serves one request and dies works against Mojito's design goal.
Embedded Perl marker
Mojito keeps the Embedded Perl idea but uses .ep. as the template marker, leaving the final extension to describe the representation. It does not claim full Mojo::Template compatibility.
Structured by intent
Mojito is a framework, not an application and not a Mojito::Lite micro-DSL. The app scaffold demonstrates routes, a Welcome controller, stash/render behavior, and a bundled EP-like template while normal static public files remain ordinary static files.
GET /
↓
lib/MyApp/Controller/welcome.pm
↓
stash + render
↓
templates/welcome.ep.html
↓
CGI response
/index.html
↓
normal static public file
Apache serves static resources. Mojito handles dynamic application requests.
Start a Mojito app
A fresh scaffold includes a structured Welcome flow, public front controller, templates, private 0700 runtime storage, a database migration directory, and a fresh 32-byte application secret key. Authentication remains a deliberate second step.
Standard
mojito scaffold:app --name="MyApp"
Portable shared-host deployment
mojito scaffold:app \
--name="MyApp" \
--standalone
Standalone vendors exactly 25 Mojito runtime modules plus mojito-runtime.txt into the application's own lib/ tree. It does not vendor Perl, Mojolicious/Morbo, the development command system, Mojito::Test, or arbitrary optional CPAN database dependencies.
Generated application layout
MyApp/
├── .gitignore
├── database/
│ └── migrations/
│ └── .gitkeep
│
├── lib/
│ ├── MyApp.pm
│ └── MyApp/
│ ├── Command/
│ │ └── .gitkeep
│ ├── Controller/
│ │ ├── .gitkeep
│ │ └── welcome.pm
│ └── Model/
│ └── .gitkeep
│
├── public/
│ ├── .htaccess
│ ├── index.html
│ ├── mojito.cgi # executable front controller
│ └── assets/
│ ├── css/
│ ├── js/
│ ├── fonts/
│ └── images/
│
├── storage/ # private / 0700
│ ├── .htaccess
│ ├── cache/
│ ├── database/
│ ├── logs/
│ └── sessions/
│
└── templates/
└── welcome.ep.html
Empty scaffold directories are retained with .gitkeep files where needed.
Welcome experience
GET /
↓
Welcome controller
↓
response stash
↓
welcome.ep.html
/index.html is the static counterpart: the web server can serve ordinary public files without entering Mojito.
Auth is a deliberate second step
mojito scaffold:app --name="MyApp"
cd MyApp
mojito scaffold:auth
Authentication is application-owned scaffolding. Its SQL migration is written to the same database/migrations/ tree as the application scaffold.
Database lifecycle
mojito database:migrate
# destructive, explicit:
mojito database:wipe --force
Migrations are ordered, transactional, and tracked. Wipe targets the configured default database and requires --force.
Current status: v1.0.0 (beta).
Mojito is currently v1.0.0 (beta). Treat the API as beta and subject to change while the project continues to harden. At repository commit 2ff597b, the full suite passed (111 test files / 2,941 assertions), the 148-test security suite passed, and check-release.pl passed.
When CGI stops being the constraint, graduate.
Mojito has no persistent-process deployment story by design. Move to Mojolicious or another persistent framework when the hosting model allows it.
Explore Mojolicious ↗