Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

monl-compiler

Describe your app in one short text file; monl builds the backend — database, API, user accounts and permissions.

app MyProjects
entity Project
    title: String
    description: Text
actor Visitor selfRegister
actor Admin
rule Project.Read public
workflow Browse for Visitor
    Read Project
workflow Manage for Admin
    Create Project
seed Project
    title: "First project", description: "Ready to explore."

→ You get: a running API (routes your website can call) with an interactive /docs page, sign-up and login, a SQLite database with demo data, and manage.py to create admins. This example's public /project route returns First project immediately. The seed block supplies those demo records.

The same file and compiler version always give the same backend code, with no AI and no network. Custom business logic is left as empty stubs for you to implement. The website UI is optional: AI can draw it from the frontend contract, a description of the API routes, fields and permissions it must follow.

To change the app, edit the file and recompile; never edit generated code.

monl-compiler

CI Version Python Licence


Contents


Quick start

Allow about 5–10 minutes for installation and your first backend; adding an AI-built interface takes extra time depending on your provider. monl opens a guided dialogue so you can write the spec without knowing its syntax. Its express mode asks for the site type, name and a one-sentence description, then prepares the specification, demo data and a brief (instructions describing the content and intended interface). The dialogue's “Quick creation with AI” label refers to the later interface step: specification and backend generation use no model and no network call. At the end, open http://127.0.0.1:8000/docs to explore the API; if you add an interface, open http://127.0.0.1:8000/site.

pip install monl-compiler
monl
monl frontend MyProject --provider codex
monl run MyProject

From a repository clone, pip install . installs the same thing.

Frontend providers that use an API require the optional extra: pip install 'monl-compiler[ai]'. Local agents and monl import do not need it.

The Detailed customization workflow remains available for choosing every option, role, editorial content item, and visual intention. Without a local agent or API key, open MyProject/docs/FRONTEND_PROMPT.md in the AI of your choice, then install the resulting ZIP or HTML file with monl import.

The full workflow, including the interface, is described in QUICKSTART.md.

Why monl-compiler?

Traditional framework
Django, Rails, FastAPI…
AI generator
v0, Bolt, coding assistants
monl-compiler
Infrastructure code written and maintained by hand produced once, then taken over derived from the spec, never maintained
Two identical compilations not applicable different result each time identical backend sources for identical input and compiler version
Access control checked route by route, relying on diligence what the model understood checked at compilation: a privilege collision prevents compilation
Schema / API / rules consistency three places to synchronize no guarantees one source, propagated on recompilation
Security depends on the author depends on generated code and its review generated safeguards: parameterized queries, role taken from the actual account, secret kept out of code
Role of AI none writes everything, including the backend limited to the frontend, bounded by a contract and a smoke test (a quick automatic test that starts the app and calls its routes)
Schema evolution migrations to write manual rework additive and non-destructive, data preserved

What you write: a one-page specification. What you change afterward: that same page. The generated code is recompiled; it is never a starting point to edit.

These safeguards cover the rules supported by the compiler; they do not guarantee an application's overall security. The suitability of the declared permissions, custom code, the frontend, dependencies, and operations require their own checks. Generated sources are deterministic; secrets created for each project and runtime data are not part of that identity. See the security model and the operations guide.

Architecture

Your project goes into monl-compiler, which produces three deliverables: spec.ml, the backend, and the frontend contract. An AI writes the frontend from the contract; monl run checks the backend and frontend, then launches the application.

The dialogue produces the specification; the compiler derives both the backend and the frontend contract from it; AI writes the interface against this contract; monl run checks that all three remain consistent before launching the application.

Commands

Command What it does
monl Guided dialogue → spec.ml + backend + frontend contract
monl compile <spec.ml> --output <dir> Compiles an existing specification
monl frontend <App> AI writes the interface in frontend/
monl import <zip|html|folder> <App> Installs a frontend obtained without an API key
monl retouche "<what looks wrong>" <App> Fixes a display issue without rebuilding the site
monl run <App> Checks consistency, runs the smoke test, then launches
monl diff <App> Shows the contract delta (changes to routes, fields and permissions) without recompiling or writing anything
monl update <App> Recompiles after spec changes, preserves data
monl migrate <App> --name <name> Applies (or undoes with --down) a named schema migration
monl usage <App> Measures AI usage and the project's declared cost
monl assets add <file> --for "<record>" Installs a photo and declares it in the spec
monl assets list <App> What the spec declares, what is present, and what is left over
monl content export <App> Exports demo records to content/*.csv
monl content import <App> Replaces records from CSV files, then revalidates the entire spec

Each project is compiled in its own directory through --output, so the previous one is not overwritten. Specifications use the .ml extension.

Claude Code plugin

The repository is also a Claude Code plugin marketplace. The monl-compiler plugin teaches Claude to write a spec from examples, compile it, check it against a real server, and then build its interface:

claude plugin marketplace add Bodichane/monl-compiler
claude plugin install monl-compiler@monl-compiler

In a session, use /monl-compiler:monl-spec followed by what the application should do. The skill calls the command line through uvx, without prior installation, using the plugin version. The plugin is published in Anthropic's directory: install it from the directory, or use the two commands above.

Web platform and MCP

The compiler is also available through a web platform: it validates a spec, compiles the backend, exposes its contract, and delivers an archive without secrets:

monl-platform --port 8022

MCP-compatible agents can call the same pipeline with monl-mcp over stdio or the HTTP endpoint /mcp. No second generator is maintained: the CLI, web, and MCP all delegate to compile_project.

See Web platform and MCP server.

In production, compose.platform.yaml launches the application as a non-root user with persistent storage, readiness checks, shared quotas, and isolated compilations. The port remains bound to localhost so it can be published behind an HTTPS reverse proxy. The reproducible procedure (variables, DNS, TLS, backups, and probes) is detailed in the deployment runbook.

The specification

A spec describes entities (tables and fields), actors (roles), and access rules. The compiler derives the schema, CRUD routes, and access control from it. Identifiers are constrained by the grammar, which rules out injection through table or column names.

Access control is expressed at the record level, including reads:

Rule Effect
rule Entite.Action ownedBy Acteur Only the owner (a relation auto-populated on creation) can act — filtering also covers reads, both lists and direct access
rule Entite.Action accessibleBy col1, col2 Restricted to the parties referenced by the record (private messaging: sender and recipient)
rule Entite.Action public Removes authentication from a specific action (public gallery, contact form)
rule Article.Read publicWhen status "published" Conditional public read: filtered list, detail returns 404. A sharedBy on the same reference exempts moderators; the owner can always retrieve their own records
rule Vote.Create oncePer Participant, Entry Composite unique index: an account can perform the action only once per target

Field constraints are enforced, not merely declared:

Rule Effect
rule Produit.prix min 0 Input bound — 422 before any INSERT. Value for number types, length for text types
rule Membre.pseudo unique Unique database index — a duplicate returns 409, on both creation and modification
rule Produit.nom required Checked assertion: the field must exist (schemas already make every field required)
rule Ligne.Create decrements Produit.stock by quantite Decrements the requested quantity, and refuses with 409 to go below the declared min
rule Commande.passeeLe timestamp Creation date written by the server (ISO 8601 UTC), absent from request bodies — on both creation and modification
rule Commande.statut oneOf "panier", "expédiée" Refuses any other value on both creation and modification
rule Commande.statut "annulée" releases Ligne Restores stock once when the order is cancelled
rule Commande.statut writableAfterPayment Admin Restricts this field to a dedicated authenticated route; calculated totals remain inaccessible

Other markers refine fields and behavior: hidden, generated, categorized, derivedFrom / sumOf (amounts calculated by the server), payable (payment collection, below), as well as a seed block (demo records inserted at startup without duplicating them) that pre-populates the database. A rule with no effect is refused at compilation rather than silently ignored — and so is a rule that names a nonexistent field: a constraint that matches nothing gives the impression of protection that does not exist.

Collecting payment: rule Commande.total payable

The rule names the field that carries the amount; the entity that contains it is the one being charged. monl-compiler derives two tracking columns and two routes from it — POST /commande/{id}/paiement, which opens a payment session, and POST /paiement/webhook, which receives the provider's confirmation.

The amount comes from the database, never from the client. The payment route accepts no request body: it rereads the field on every call. A cart that sends its own price is a cart whose price can be negotiated. The webhook, in turn, checks the provider's signature before writing anything — it is the only place in the generated backend where an unauthenticated third party touches the database.

Cases that would make charging questionable — a non-numeric field, an amount the client can write, a hidden amount, two payable fields, public creation — are refused at compile time rather than at the moment of charging.

The keys (STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET) come from the environment, like the JWT secret. If they are missing, the routes respond 503 naming the missing variable and the rest of the server works normally: a freshly compiled project starts and can be tested offline.

Registration: why a role cannot be obtained in a single HTTP call

An actor is not registerable by default. actor Client selfRegister opens POST /register to that role; an actor Admin without a marker can only be obtained through offline provisioning (manage.py, generated alongside the backend). Letting the client choose its role during registration would be a privilege escalation in a single HTTP call.

Five commented reference specifications in exemples/: a one-page .ml file for each application — portfolio, shop, social network, kanban, ranking — from which monl-compiler derives everything else.

Visual direction: it does not come from the compiler

monl-compiler has no opinion about visuals — no palette, typography, or grid. It does not know what a project should look like; it only knows table names. The direction is what the author expresses in the dialogue (visual register, placement of images): it travels in the brief, and the interface AI implements it.

Only two requirements remain, and they are not matters of taste: contrast (WCAG AA), which makes the interface readable, and frontend autonomy, which makes it verifiable by the smoke test.

The generated backend

Accounts and roles. POST /register accepts only roles marked selfRegister; any other role is refused (403). Privileged accounts are created with the generated manage.py, on the machine hosting the database: python3 manage.py adduser <username> <role>. The same command manages roles, passwords, the account list, and global session revocation. With B4 TOTP enabled, python3 manage.py totp-reset <identifier> is the operator recovery command: it clears TOTP and revokes all sessions for that account. Email password reset preserves TOTP. TOTP activation requires the current password and a valid code; activation, password reset, and manage.py passwd invalidate existing access and refresh tokens. Every device must log in again.

Authentication. A user registry dedicated to each application (table _monl_users, passwords in PBKDF2-HMAC-SHA256, a unique salt per account, constant-time comparison). Flow: POST /register → POST /login (JWT token, a signed proof of login sent with later requests) → POST /logout (revocation before expiration). The role and identity carried by the token come from the actual account, never from a client declaration.

JWT secret. Generated randomly on the first compilation, stored in .jwt_secret (never versioned). In production, MONL_JWT_SECRET takes precedence and lets you deliver a project without a secret on disk.

Multi-worker. Token revocation and rate limiting (5 attempts / 60 s / IP on /register and /login) are persisted in the database, so they are shared: uvicorn app:app --workers N does not multiply the quotas. Behind a trusted reverse proxy, MONL_TRUST_PROXY=1 makes the app read the real IP from X-Forwarded-For; without this setting the header is ignored to prevent spoofing.

Migrations. Recompiling in the same directory while keeping app.db adds columns with ALTER TABLE ADD COLUMN without touching the data. Destructive changes are not automated, by design — see docs/MIGRATIONS.md.

Served routes. /docs (Swagger, always available) · / (redirects to /docs) · /site (the interface, if frontend/ exists and the app is started with monl run).

Your files: photos, logo, favicon

A broken image is only visible to the eye, once deployed — the worst place to discover a typo. So the files you provide are declared in the spec, and the compiler refuses to compile if they are not there:

assets
    dir: "assets"
    logo: "logo.svg"

entity Produit
    photo: Image          # a LOCAL file, checked for presence

Image designates a project file: a URL is refused, because monl makes no network calls and could not verify anything about a remote address — String remains available for that case, without verification. The directory lives outside frontend/, which is renamed each time the frontend is rebuilt.

To avoid writing these paths by hand:

monl assets add ~/photos/IMG_4821.jpg --for "Halo RS"   # → assets/halo-rs.jpg
monl assets add ~/logo.svg --logo
monl assets list                                        # present, missing, orphaned

The command copies the file, renames it as a slug, writes the declaration — then has the compiler revalidate the resulting spec before saving it. If it is refused, neither the spec nor the directory is modified. It never deletes a file: replacing a photo marks the old one as orphaned; it does not delete it.

Replace content without opening the spec

The demo data lets you see an interface immediately, but it is not meant to become the real catalog. A person can replace text, prices, and photo names with a spreadsheet:

monl content export MyProject
# edit content/Produit.csv and put the photos in assets/
monl content import MyProject
monl update MyProject

Each CSV preserves the order of fields and records. LISEZMOI.txt explains in French the allowed values, required fields, limits, and expected images. An empty cell is omitted: the actual compiler decides whether it was required. Invalid numbers, missing files, suspicious paths, and ambiguous blocks are refused before any writes. Import replaces the entity's entire content; it never silently merges two sources of truth.

The frontend: contract and specialized AI

The interface is written by an AI, from a business contract and a visual direction prepared before coding. Each compilation produces:

  • frontend_contract.json — a machine-readable description of the routes intended for the interface, authentication, and field rules, derived from the same spec as the backend;
  • in docs/, the material to read before writing the interface:
    • FRONTEND_PROMPT.md — the brief to give an interface AI: structure, roles, content, and declared intent, without visual prescriptions;
    • DESIGN_SYSTEM.md — page pattern, starting tokens, anti-patterns, and UX checklist determined from the contract;
    • DESIGN_SPEC.md — editable visual summary; if the author replaces it, it takes priority and Monl does not overwrite it;
    • ASSET_MANIFEST.json — asset plan and section markers, verifiable after monl frontend or monl import.

The design system also selects a local catalog of Monl patterns — hero, catalog, editorial, reassurance, FAQ, contact, and final CTA — with variants suited to the application type. These patterns are standalone HTML/CSS/JS structures, not React components to install.

The AI writes to frontend/ (entry point index.html), which monl run serves at /site without ever touching the backend. Several paths, the same safeguards:

Path Command Authentication
Manual put the files in frontend/ —
Copy-paste monl import <zip|html|folder> <App> none
Local agent monl frontend <App> --provider claude-code|codex|gemini agent subscription
Any agent monl frontend <App> --agent-command "<command> {instruction}" the agent's
Anthropic API monl frontend <App> --provider claude ANTHROPIC_API_KEY
Third-party API monl frontend <App> --provider groq --model <id> GROQ_API_KEY, etc.

Any key will do. OpenAI-dialect providers — groq, openai, openrouter, deepseek, mistral, together, xai, ollama — are preconfigured, each reading its own environment variable. For an endpoint not in this list, use --provider openai-compatible with MONL_AI_BASE_URL and MONL_AI_API_KEY. Outside the Anthropic path, --model is required: monl hardcodes no model ID, because catalogs change too quickly for a fixed value to stay accurate. The key is always read from the environment, never passed as an argument — the shell would record it.

Shared safeguards: allowlisted extensions, protection against zip-slip, a standalone frontend without a CDN, design direction injected before generation, and systematic rechecking of routes, assets, and required sections.

No API key, no credit card, no network

The compiler never calls the outside world. monl compile produces app.py, schema.sql, manage.py, the contract, and the brief entirely offline: the parser, validator, and generator contain no network calls. The entire backend — routes, database, JWT, access control, payments, back office — is obtained without an account anywhere. AI is involved only in the frontend step, and this step has a path without any key:

monl compile boutique.ml --output ./Boutique   # offline
# paste the contents of Boutique/docs/FRONTEND_PROMPT.md into any browser-accessible
# assistant, retrieve the result…
monl import interface.zip ./Boutique           # same safeguards, same verification
monl run ./Boutique

monl import is not a backdoor: the source comes from a conversation, so it is treated as untrusted input — extension allowlist, zip-slip refusal, CDN refusal, index.html required, then consistency check and smoke test, exactly like an API response.

What remains, depending on what you have available: --provider ollama for a fully local model, command-line agents that authenticate by subscription rather than by key, and OpenAI-dialect providers, several of which offer a free tier. monl does not favor or resell any of them: it consumes no tokens on its own account.

What has been proven, and what has not. The offline path, the copy-paste path, and the Anthropic path have been tested end to end against a real server. The codex and gemini presets are written and covered at the plumbing level, but have not been tested against the real binaries — using them means being the first to try them out.

Before every launch, monl run runs a behavioral smoke test against a fresh ephemeral server: every contract route is tested over real HTTP and, if Node.js is available, frontend/index.html is executed in jsdom against this server. Any exception or out-of-contract call blocks the launch (--skip-smoke to override with full awareness).

Quality and verification

Tests published by CI Unit validations and ephemeral servers for HTTP paths; consult the CI run for the relevant revision
Coverage published by CI Compiler and platform measured separately, with a 90% threshold for each
Offensive audit Role impersonation, forged JWT, privilege escalation
Architecture boundaries Import contracts verified by tests, including the independence of analysis from emitters
Lint ruff check src tests — zero findings, justified exceptions in pyproject.toml
CI Workflow configured for Python 3.10, 3.12, and 3.14 on every push and pull request
python3 -m pytest tests/ -rs --cov=src/monl --cov=src/monl_platform --cov-report=term-missing --cov-fail-under=0
python3 -m coverage report --include='src/monl/*' --fail-under=90
python3 -m coverage report --include='src/monl_platform/*' --fail-under=90

A single run of the whole suite, followed by two gates drawn from the same measurements: that is what CI does (a gate measured against a list of test files would forget those not on the list).

ruff check src tests

monl-compiler does not depend on any AI model and makes no network calls: dialogue, specification, and backend generation are entirely deterministic. custom blocks produce safe empty stubs in sandbox_ai.py, whose business logic is written by hand — no code generation is automated.

Repository structure

Directory Contents
src/monl/ The package: parser, validator, dialogue, design system, frontend contract, CLI
src/monl/generator/ The backend generator, one layer per module
src/monl_platform/ The web platform and MCP server: accounts, compilation, hosting, administration
exemples/ Five one-page .ml specifications, compiled in every test
plugin/, .claude-plugin/ The Claude Code plugin and its catalog: skills (plugin/skills/ — write the spec with monl-spec, then build the interface) and an exact copy of the examples and grammar (plugin/reference/)
demo/ The CodexShop demo, a stationery shop that exercises the entire commerce flow: its specification, frontend, and photos
tests/ Regression tests, offensive audit, architecture boundaries
docs/ Design decisions, security, migrations, operations, publication
deploy/ Runbook and platform production deployment files

Documentation

File Contents
QUICKSTART.md The complete workflow, from installation to launch
docs/design_decisions.md The project journal, point by point, each with its why (in French)
docs/DESIGN_DECISIONS_SUMMARY.md English map of the journal: every point by theme, linked to its entry
docs/SECURITE.md Security model
docs/MIGRATIONS.md Schema evolution without loss
docs/STABILITY.md 1.0 public interfaces, language version and compatibility policy
docs/BETA.md Beta status and roadmap
docs/DEPRECATIONS.md Historical compatibility and removal policy
docs/PUBLICATION.md PyPI publication and GHCR platform image
deploy/README.md Docker deployment runbook, DNS, TLS, and smoke test
CHANGELOG.md Version history
CONTRIBUTING.md Working method, repository rules, pre-PR checklist

License

FSL-1.1-ALv2 — Functional Source License, with automatic conversion to Apache-2.0 two years after the publication of each version (LICENSE).

You may use monl-compiler freely, including professionally, modify it, redistribute it, and use it to deliver applications to your clients. The only restriction is competitive use: making it into a commercial product or service that substitutes for monl-compiler. Applications produced from your own specifications belong to you.

Details in French: LICENSE-FAQ.md.

Bug reports and feedback are welcome in the issues.


monl-compiler 1.0.0-rc.1

Metadata

Release files for monl-compiler 1.0.0rc1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for monl-compiler 1.0.0rc1
File Size Uploaded
monl_compiler-1.0.0rc1.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for monl-compiler 1.0.0rc1
File Interpreter ABI Platform
monl_compiler-1.0.0rc1-py3-none-any.whl Python 3 none any Details

Total release size: 1.9 MB

Release files / monl_compiler-1.0.0rc1.tar.gz

Download URL monl_compiler-1.0.0rc1.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
4f11a31f7b256212a7cbd5e826ae28fd0a1fb1c26492529636177b49af219956
BLAKE2b-256 checksum
How to use checksums
fa1f7988f02615e281ce66cfe1ae5ae11237dbe27e90851bbc3fa7f171cc0e8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release files / monl_compiler-1.0.0rc1-py3-none-any.whl

Download URL monl_compiler-1.0.0rc1-py3-none-any.whl
Size 765.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7b9a4e8a4a52a16b030483af984c69bd308493a05a685cbc25d7d9e0bcaf022d
BLAKE2b-256 checksum
How to use checksums
d0a24dba82c4c5809a8bc9a3e3af9146d219452ed859bc95f87d0edd9262356a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page