CLI tool
lexmount/moli avatar
lexmount/moli

Moli: a Rust headless browser that skips layout and paint until an agent asks for pixels

Best headless browser for AI agents. Lite, Fast, High-Compatibility. Built in Rust

2,989 stars177 forksRustApache-2.0

At a glance

What is it?
Moli is an Apache-2.0 headless browser built in Rust for AI agents and crawlers. Its on-demand rendering model treats the DOM as the source of truth, so most fetch and extraction requests never build a visual tree.
Who is it for?
Adopt Moli if your workload is DOM-first: crawling, retrieval pipelines, browser-use agents, or evaluation environments where most requests never need pixels. Skip it if you depend on a rendering feature the README does not list, or if you need a long support window, since the README gives no compatibility or deprecation policy.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Moli is for, and what it deliberately is not

Moli targets agent workloads: fetching and extracting pages, searching the web, and automating browser tasks. The README names crawling, browser-use agents, retrieval pipelines, evaluation environments, and reinforcement-learning workloads as the fit. The common thread is that these jobs read page structure far more often than they look at rendered pixels.

That framing is also the boundary. Moli is not positioned as a drop-in replacement for a browser you drive interactively. The README describes a complete runtime, including V8, CSS, layout, text shaping, hit-testing and software paint, but the design assumption is that visual work is the exception. If your pipeline needs a continuously rendered visual state, or a rendering capability the README does not list, Moli is the wrong tool and you should say so before benchmarking it.

The project is Apache-2.0, though the repository also carries a LICENSE-MIT file and a licenses/ directory, so verify which terms apply to the artifacts you ship.

Structure first: how on-demand layout and paint actually work

The mechanism is a deferred rendering pipeline. The README states that Moli treats the native DOM and style state as the single source of truth, and triggers layout or software paint only for operations that genuinely require them.

The README's own table maps requests to work. Extracting HTML or Markdown, querying the DOM, running JavaScript, and inspecting network or storage read browser runtime state directly and do not trigger layout or paint. Reading an element's box, hit-testing coordinates, or sending coordinate input runs one layout calculation and retains only the latest frozen layout tree. A screenshot rebuilds from the current DOM and style state, replaces the frozen tree, renders a fresh frame, and discards its paint state after use. A screencast poll compares generation metadata only: clean state emits no frame, changed state rebuilds and emits one.

Two consequences follow. First, the cost of a request depends on what it asks for, not on page complexity alone; a DOM query against a heavy page and a DOM query against a light one take the same kind of path. Second, visual state is not a cache you can rely on between calls. The frozen layout tree is replaced, and paint state is discarded, so an operation that needs geometry pays for a layout pass again after anything invalidates it.

The workspace layout reflects the split. Cargo.toml lists separate crates for moli-dom, moli-layout, moli-paint, moli-css-parse, moli-renderer-v8, moli-geometry and moli-image, alongside protocol crates for CDP, WebDriver Classic and WebDriver BiDi. Rendering is a set of modules that can be entered on demand, not a loop the browser runs continuously.

Installing Moli and running a first fetch

The README offers two install paths. The direct one is a shell installer on Linux or macOS. It downloads the latest prebuilt binary from the releases page, so the version you get is whatever the latest release is at the time you run it.

bash
curl --proto '=https' --tlsv1.2 -fsSL \
  https://github.com/lexmount/moli/releases/latest/download/moli-installer.sh | sh

On Windows the README gives a PowerShell equivalent:

powershell
powershell -ExecutionPolicy ByPass -c "irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex"

The second path is agent-driven: the README suggests giving your agent a prompt that points it at the skills under https://github.com/lexmount/moli/tree/main/skills, has it follow those instructions to download and install the latest prebuilt binary, and then use moli-webfetch to fetch https://example.com. That is a deliberate choice: the project ships instructions for an agent rather than only instructions for a human.

Once installed, the first useful command renders a page as Markdown using the default completion strategy:

bash
moli fetch \
  --dump markdown \
  --wait-until done \
  https://example.com

You should see the page body as Markdown on stdout. For a structure-only view, the README shows a semantic tree dump gated on a selector:

bash
moli fetch \
  --dump semantic_tree_text \
  --wait-selector body \
  https://example.com

Visual output requires the --layout flag. The README's examples write a viewport PNG, a full-document PNG, or a paginated PDF to stdout:

bash
moli fetch --layout --dump screenshot https://example.com > page.png
moli fetch --layout --dump screenshot_full https://example.com > full-page.png
moli fetch --layout --dump pdf https://example.com > page.pdf

For automation, start the server. The README describes three modes: moli serve for DOM-first workloads, moli serve --layout to enable real geometry, coordinate input, and screenshot or screencast surfaces, and moli serve --layout --resource to also fetch optional image, font, audio, video, media, and text-track resources.

bash
moli serve --layout

The same endpoint serves CDP, WebDriver Classic and WebDriver BiDi. Playwright connects over CDP at port 9222:

js
import { chromium } from "playwright";

const browser = await chromium.connectOverCDP("http://127.0.0.1:9222");
const context = browser.contexts()[0];
const page = context.pages()[0] ?? await context.newPage();

await page.goto("https://example.com");
console.log(await page.locator("body").innerText());

await browser.close();

Run fetch --help for the full option list, which the README says covers output formats, page-load and response waits, profiles, proxy settings, resource policies, and tracing.

The trade-off you accept: no persistent visual state

The cost model is the limitation. Because paint state is discarded after a screenshot and the frozen layout tree is replaced, any workflow that interleaves geometry reads with DOM mutations pays for repeated layout. The README describes a screenshot as rebuilding from current DOM and style, rendering a fresh frame, and discarding its paint state. That is efficient for one-shot capture and less so for a tight loop of measure, mutate, measure.

The server flags encode the same trade-off. Without --layout, the README says geometry, coordinate input and screenshot or screencast surfaces are not enabled. Without --resource, optional image, font, audio, video, media and text-track resources are not fetched. A page whose correctness depends on a web font or a lazily fetched image will behave differently under the default serve mode than under moli serve --layout --resource. Choose the mode per workload rather than globally.

There is also no stated compatibility or deprecation policy. The README does not document rollback, and the releases listed are close together (v1.1.2, v1.1.3, v1.1.4 within a week), which suggests a fast cadence. Pin a version in your own build if you cannot absorb that.

How Moli differs from Playwright and Puppeteer

Playwright and Puppeteer are the obvious comparison points, and the README names both as topics. The difference is not the protocol surface. Moli speaks CDP, WebDriver Classic and WebDriver BiDi, and the README shows Playwright connecting to it over CDP unchanged, so the client code you already have can often stay.

The difference is where rendering sits in the request path. Playwright and Puppeteer drive a browser engine that maintains a rendered page as the normal state of the world; layout and paint are part of loading, not an opt-in step. Moli inverts that. The README describes DOM and style state as the source of truth, with layout and paint entered only for operations that need them, and the frozen layout tree retained only until it is replaced.

That inversion is what makes the resource claim plausible for crawlers and agent loops, and it is also why Moli is a poor fit for anything that treats the page as a visual object by default. If your tests assert on screenshots or your agent reasons over pixels continuously, you are paying for the deferred model without collecting its benefit.

Maintenance, release cadence and licence

The repository is not archived, and the last push was on 2026-09-10. The most recent release listed is v1.1.4 on 2026-09-08, with v1.1.3 on 2026-09-05 and v1.1.2 on 2026-09-04. Three patch releases in five days is a fast cadence, and it cuts both ways: fixes arrive quickly, and the surface you integrate against can move between minor versions.

The repository declares Apache-2.0, and it also ships LICENSE-APACHE, LICENSE-MIT and a license-metadata.json alongside a licenses/ directory. That combination is common in Rust workspaces where individual crates carry their own terms. If you redistribute a binary or vendor crates, read license-metadata.json and the per-crate terms rather than assuming the top-level identifier covers everything. This is a pointer to the files, not legal advice.

Upgrade cost is hard to estimate. The README documents no migration notes, no deprecation window, and no rollback procedure. Budget for reading the release notes between the version you pin and the version you move to.

Skills, protocols and where the documentation stops

The README points at a skills directory in the repository and gives a ready-made agent prompt that installs from it. That is the most distinctive documentation choice here: the primary onboarding path is written for an agent to execute, with the human path (the curl and PowerShell installers) as the alternative. If you run an agent framework, the skills route is worth reading before you script the installer yourself.

Protocol coverage is broad on paper. The README states that one endpoint serves CDP, WebDriver Classic and WebDriver BiDi, and the workspace lists dedicated crates for each. The README does not publish a command-by-command compatibility matrix for the three protocols, so treat protocol support as a claim to verify against the specific commands your client sends, not as a guarantee.

The README is also silent on a few things a production adopter will ask about: rollback, a support window, and the exact set of resources excluded when --resource is omitted. Those gaps are the reason to test against your own pages rather than a demo page before you commit.

Editorial conclusion

Adopt Moli if your workload is DOM-first: crawling, retrieval pipelines, browser-use agents, or evaluation environments where most requests never need pixels. Skip it if you depend on a rendering feature the README does not list, or if you need a long support window, since the README gives no compatibility or deprecation policy. Before committing, run moli serve --layout and connect Playwright over CDP at http://127.0.0.1:9222 against one page from your own site, then compare a moli fetch --dump markdown result against the same page in a full browser.

Frequently asked questions

How do I install Moli on Linux or macOS?

The README gives a shell installer that downloads the latest prebuilt binary from the releases page: curl --proto '=https' --tlsv1.2 -fsSL https://github.com/lexmount/moli/releases/latest/download/moli-installer.sh | sh. On Windows the README gives a PowerShell equivalent using moli-installer.ps1.

Does Moli work with Playwright?

Yes, over CDP. The README shows chromium.connectOverCDP("http://127.0.0.1:9222") against a running moli serve instance, then standard page.goto and locator calls. The same endpoint is documented as serving CDP, WebDriver Classic and WebDriver BiDi.

Why does Moli need the --layout flag to take a screenshot?

Moli treats the DOM and style state as the source of truth and does not maintain a rendered visual state by default. The README states that layout and paint run only on demand, so moli fetch --layout --dump screenshot is what triggers a fresh frame. Without --layout, geometry, coordinate input and screenshot or screencast surfaces are not enabled.

Official sources

  1. lexmount/moli on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/lexmount-moli.svg)](https://hysenlabs.com/projects/lexmount-moli)