Mojito logo Mojito
Perl 5.26+ · CGI-first · dependency-light

Modern Perl for the CGI server you actually have.

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.

Mojito logo showing a terminal and fresh green leaves

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...

CGI isn't nostalgia. It's the deployment environment.

  • cgi-bin
    Your host gives you Perl CGI and little else.
  • No daemon
    Persistent workers are unavailable.
  • Tight resources
    RAM is limited and CPU is shared.
  • Server-rendered HTML
    HTMX and traditional HTML fit the app well.
  • Mojolicious conventions
    You like the structure and Perl ergonomics.
  • More than scripts
    You want structure without a large runtime.

Mojito probably isn't for you if...

You control the server and can run a proper persistent app.

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

Keep the repeated startup bill close to the unavoidable CGI floor.

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

routing controller dispatch request / response stash values template resolution EP-like rendering UTF-8 handling structured app flow

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

Persistent applications amortize startup.

Mojolicious under persistent server

start Perl
load Mojolicious
load application
        ↓
request
request
request
request
...

Traditional CGI repeats it

Every request pays startup again.

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

A small, auditable framework that keeps optional machinery out of the request path until you ask for it.

⚡

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.

🪶

Dependency-light runtime

Perl 5.26+ with no Mojolicious production runtime dependency. DBI and database drivers are optional and loaded only when database access is used.

🧾

Bundled templates

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.

🧊

CGI-realistic persistence

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.

🔐

Authentication when you need it

mojito scaffold:auth generates application-owned auth structure and its migration under database/migrations/. Auth is scaffolding, not a runtime feature every app carries.

🛡️

Reviewed security defaults

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 lifecycle tooling

database:migrate applies ordered SQL migrations transactionally and tracks them in schema_migrations. database:wipe --force resets the selected default database.

🧰

Granular scaffolding

Generate a complete app, a whole resource with optional CRUD/CSRF, or individual controllers, models, templates, routes, commands, plugins, and auth.

↔️

Server-rendered HTML

Mojito stays comfortable with ordinary HTML responses and fragments. HTMX can sit on top without requiring framework-specific browser runtime support.

Layered and lazy

Keep optional machinery off the plain request path.

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

  • • application + router
  • • lazy request / response
  • • bundled renderer/template path
  • • explicit application home

Only when used

  • • signed sessions
  • • DBI / database driver
  • • file cache
  • • validator + auth helpers

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

Scaffold what you need. Maintain it with explicit commands.

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.

$ mojito scaffold:app --name="MyApp"
$ mojito scaffold:resource article --crud
$ mojito scaffold:auth
$ mojito database:migrate
$ mojito routes
$ mojito doctor

Canonical public commands use colon-delimited leaf names. The executable is presented simply as mojito.

mojito scaffold:app --name="MyApp"

Application

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

CRUD-shaped resource

Generate controller actions, routes, templates, and CSRF-aware CRUD shape while leaving persistence decisions with the application.

mojito scaffold:auth

Authentication

Add application-owned login/logout/protected-route scaffolding only when needed. The generated auth migration lands in database/migrations/.

mojito database:migrate

Migrations

Apply database/migrations/*.sql in order, transactionally, while recording completed migrations in schema_migrations.

mojito scaffold:command import_articles

Application command

Put maintenance tasks in application-owned commands instead of adding them to the CGI request path.

mojito storage:cleanup

Runtime storage

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

A local development server without changing the production model.

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

Borrow the great cooking. Leave behind the runtime bill CGI would repay every request.

Mojito is heavily influenced by Mojolicious. The familiar ideas are intentional; so are the omissions. Mojito has no Mojolicious production runtime dependency.

Familiar ideas

  • • application/startup organization
  • • controller/action model
  • • routes
  • • $c-style controller context
  • • stash + rendering
  • • Embedded Perl-style templates
  • • commands and scaffolding
  • • sessions
  • • developer-friendly Perl APIs

Intentionally absent

  • • persistent application servers
  • • large async/event-loop infrastructure
  • • WebSockets/SSE runtime
  • • full Mojo toolkit surface
  • • broad plugin/hook ecosystem
  • • UserAgent / DOM / Promise ecosystem

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

Mojolicious
welcome.html.ep
Mojito
welcome.ep.html

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

A generated app demonstrates the real request flow.

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

Generate a runnable CGI application, then add only what you need.

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 ↗