Show HN: Lasm – Write formally verified webapps in Lean for Node/serverless/edge

Hacker News by 33 min read 162x views
Show HN: Lasm – Write formally verified webapps in Lean for Node/serverless/edge

Share Post

Lean 4 for the JavaScript and WebAssembly world. Lasm compiles average Lean 4 programs onward of period to WebAssembly. One Lean code basis can rotate into a Node server, a serverless or border function, a WASI/Fastly component or a browser app. Because it is Lean, you can demonstrate properties of the code you deploy. The flagship example, Conduit, is the RealWorld app with 117 proved theorems. Lasm ships on npm and brings Lean, Lake and its build tools alongside it; you lone need Node (or Bun or Deno).

npm create @lasm@latest my-app -- --template hono # then: cd my-app && npm instal && npm run dev

AI agents: this README is complete on its own. llms.txt is the short index, and docs/AI.md lists the rules that preserve you from incorrect turns.

If Lasm is helpful to you, I'd value your sponsorship (more below).

Fact Value
Current version @lasm/cli 0.3.5 (release notes); npm perspective @lasm/cli version
Install npm instal -D @lasm/cli (bin: lasm), or no install: npx @lasm/cli …; new project: npm create @lasm@latest
Lean exactly 4.35.0 (lean-toolchain: leanprover/lean4:v4.35.0); Lasm installs it, and you need no elan to build
Develop on Linux x64/arm64 (glibc 2.34+), macOS x64/arm64, Windows x64
Compiler runs on Node ≥ 24 (tested 24.0.0, 24.22.1, 26.12.1); Bun 1.4.4 and Deno 2.9.6 (tested, as the lone runtime)
Output dist/ alongside program.wasm + a JS launcher (Node); or a phase bundle (--target …)
Node apps run on Node ≥ 24, Linux x64/arm64 (glibc 2.28+ or musl) from any build machine, affirmative the build machine's own OS; ≥ 512 MB memory
JS/npm interop import Lasm.JS; one @[js "pkg" "export"] row per function you call; no premade bindings (guide)
Frameworks in Lean Lasm.Fetch (HTTP, all server target), Lasm.UI (browser, Elm architecture, proved diff), Lasm.RPC (typed client/server API), Lasm.Web (DOM and web APIs), Lasm.KV
Not accessible at run time elaboration, #eval-style evaluation, loading .olean, Lean's kernel (exact list); all of these activity at build time
License MIT (Lasm); templates, examples and README code samples additionally MIT-0
Repository / issues https://github.com/Millillion/lasm · report a bug (include npx lasm --version)

Every row is tested in CI alongside the platform's own local runtime, depiction or emulator. By project guideline CI never deploys to a genuine haze account (how all is tested).

Deploy to lasm build Main.lean --target … Output Since
Node server or program (any host, Docker/Kubernetes, Cloud Run, Fly.io, Railway, Render, Heroku, DigitalOcean) node (default) dist/ → node dist/main.mjs 0.1.0
AWS Lambda (.zip, nodejs24.x) lambda lambda.zip 0.1.0
AWS Lambda (container image) lasm init … --deploy lambda Dockerfile.lambda 0.1.0
Lambda@Edge lambda-edge lambda-edge.zip 0.1.0
Vercel Functions vercel .vercel/output 0.1.0
Netlify Functions netlify netlify/functions/lasm 0.1.0
Azure Functions azure azure-functions/ 0.1.0
Google Cloud Functions (gen2) gcf gcf/ 0.1.0
Cloudflare Workers workers workers/ 0.2.0
Vercel Edge vercel-edge api/lasm.mjs, api/_lasm/ 0.2.0
Netlify Edge netlify-edge netlify/edge-functions/lasm.mjs 0.2.0
Deno Deploy deno-deploy deno/main.mjs 0.2.0
Supabase Edge Functions supabase-edge supabase/functions/<name>/ 0.2.0
Bun (≥ 1.3.14) / Deno (2.x) bun / deno bun/main.mjs / deno/main.mjs 0.2.0
Fastly Compute fastly bin/main.wasm, pkg/<name>.tar.gz 0.3.0
WASI (wasmtime, wasmer, node:wasi) wasi wasi/main.wasm 0.3.0
wasi-http (wasmtime serve, Spin) wasi-http wasi-http/main.wasm 0.3.0
Browsers (Chrome, Edge, Firefox, Safari) browser (or the Vite plugin @lasm/cli/vite) dist/ fixed site 0.3.0

Node builds have Lean's complete runtime (threads, files, processes, sockets). The edge, Bun/Deno, WASI and browser builds run Lean single-threaded, without sockets or processes (and without files, apart from on WASI); the remainder of Lean works.

I desire to build… Run Then appearance at
A command-line program npm create @lasm@latest my-tool examples/hello, examples/wasi-cli
A REST API on Node alongside an npm framework npm create @lasm@latest my-api -- --template hono (or express, fastify) examples/js-crud (Postgres + JWT, Express and Hono)
A server in clean Lean (no npm) npm create @lasm@latest my-api -- --template fetch (any target) or --template std-http (Node, additionally native Lean) examples/fetch; the std-http template
A serverless function (Lambda, Vercel, Netlify, Azure, GCF) npm create @lasm@latest my-fn -- --template hono --deploy lambda-zip (or vercel, netlify, azure, gcf, lambda-edge) docs/deploy
An border function (Workers, Vercel/Netlify Edge, Deno Deploy, Supabase) npm create @lasm@latest my-edge -- --template hono --deploy workers (or fetch) examples/edge-pure, docs/EDGE.md
A Fastly, WASI or Spin service npm create @lasm@latest my-svc -- --template fetch --deploy fastly (or wasi-http, spin, wasi) examples/fetch, docs/WASI.md
A browser app npm create @lasm@latest my-web -- --template web examples/ui (counter, TodoMVC alongside proofs, characteristic demo, benchmark)
A complete real-world app (API, browser frontend, SQL, auth) alongside proofs, on Node, Workers or Bun cd examples/conduit && npm instal && npm run build && npm start examples/conduit, the flagship (below)
A full-stack app alongside a verified API npm create @lasm@latest my-app -- --template fullstack templates/fullstack
To call npm packages or fetch from Lean add import Lasm.JS and one @[js] row per function examples/js-fetch, JS bindings
To run on Bun or Deno bun create @lasm my-app --template hono / deno run -A npm:@lasm/create my-app --template hono docs/BUN_DENO.md

Every example has a README alongside a one-command run; examples/README.md lists them all.

Flagship example: Conduit, alongside 117 proved theorems

examples/conduit is Conduit, the RealWorld app (a Medium clone), written entirely in Lean: the REST API, the server-rendered browser app and the proofs.

  • 117 theorems concerning the code that really runs, checked alongside no apologetic and only Lean's norm axioms (npm run proofs; PROOFS.md lists all one in plain English). They shield authority (only authors change their articles and comments), follows and favorites, slugs that never collide, pagination that never skips or repeats, validation shared by client and server (the app never sends what the server would reject), JSON circular trips and Markdown nexus safety. The endpoints are written against a Store of functions, and the proofs run that identical code on a example of the tables.
  • What is trusted, not proved: that all SQL declaration does what its model says, the JSON content layer, concurrency between requests, and Lean, Lasm and the JavaScript runtime. Conformance to the entire RealWorld spec is tested, not proved.
  • API: the authoritative RealWorld API suite passes unmodified (13 of 13 files, 154 requests) on Node (node:sqlite), Cloudflare Workers (D1) and Bun (bun:sqlite). Each mark has one hand-written @[js] binding module.
  • Frontend: Lasm.UI on RealWorld's authoritative templates, alongside server-side rendering, hydration and Markdown parsed in Lean. The authoritative Playwright end-to-end suite passes alongside no retries on Node alongside Chromium, Firefox and WebKit, and on Workers and Bun alongside Chromium.

All of this runs in CI (conduit.yml, run 40615274657, with @lasm/cli 0.3.4).

cd examples/conduit npm instal # @lasm/cli, Hurl and Playwright (the authoritative suites' runners) npm run build && npm commencement # http://localhost:3000: the app and its API on Node npm run proofs # inspect all 117 theorems npm test # the authoritative API suite (also test:workers, test:bun) npx playwright instal chromium && npm run test:e2e # the authoritative end-to-end suite npm run build:workers && npx wrangler dev # Cloudflare Workers alongside local D1 npm run build:bun && npm run start:bun # Bun alongside bun:sqlite

A sole document needs no project. Save this complete program as Main.lean:

def main : IO Unit := do IO.println s!"Hello from Lean! 2 + 3 = {2 + 3}"
npx @lasm/cli Main.lean # builds and runs: Hello from Lean! 2 + 3 = 5 npx @lasm/cli build Main.lean # writes dist/; run it with: node dist/main.mjs

The archetypal build on a new device fetches Lean and the build tools formerly (about 250–275 MB; 19 s for that archetypal build on the Linux CI runner). Later dev builds of a one-line alter obtain concerning 1–2 s.

Each formula is complete: create, develop, build, deploy. Lasm never installs or starts a platform's own CLI (wrangler, vercel, fastly, …); you run those. npm run dev is lasm dev: it rebuilds concerning 2 s following all preserve and restarts the server, and keeps the final fine build operating whenever there is a category error.

Node server (Hono; additionally express, fastify, std-http, fetch).

npm create @lasm@latest my-api -- --template hono cd my-api && npm install npm run dev # http://localhost:3000/hello/Ada npm run build && npm commencement # dist/ needs lone Node 24+ docker build -t my-api . && docker run -p 8080:8080 my-api # the generated Dockerfile

AWS Lambda / Vercel / Netlify / Azure / Google Cloud Functions.

npm create @lasm@latest my-fn -- --template hono --deploy lambda-zip # or vercel, netlify, azure, gcf, lambda-edge cd my-fn && npm install npm run build:lambda # dist/ and lambda.zip (handler lasm-lambda.handler) # deploy lambda.zip alongside the AWS console, aws CLI or SAM; recipes: docs/deploy/lambda.md

Cloudflare Workers (and Vercel Edge, Netlify Edge, Deno Deploy, Supabase).

npm create @lasm@latest my-edge -- --template hono --deploy workers cd my-edge && npm install npm run build:workers # workers/ (wrangler.toml came from lasm init) npx wrangler dev # local workerd; npx wrangler deploy to publish

Fastly Compute, WASI, Spin.

npm create @lasm@latest my-svc -- --template fetch --deploy fastly --deploy spin cd my-svc && npm install npm run build:fastly && fastly compute assist # Viceroy on http://127.0.0.1:7676 npm run build:wasi-http && rotation up # or: wasmtime assist wasi-http/main.wasm

Browser app.

npm create @lasm@latest my-web -- --template web cd my-web && npm install npm run dev # http://127.0.0.1:5173, live reload npm run build # dist/: a fixed location for any presenter or CDN

Full stack (server + browser + verified API).

npm create @lasm@latest my-app -- --template fullstack cd my-app && npm install npm run dev # one URL: http://127.0.0.1:3000 npm run build && npm commencement # Node; additionally build:workers, build:bun, build:deno

Editor. Run npx lasm publishing company formerly per project; lasm init, lasm dev and lasm build additionally do it. The Lean publishing company (VS Code's Lean 4 extension) afterward resolves import Lasm.* (details).

The entire stack in Lean, verified

The fullstack template is one Lean project alongside a server (Lasm.Fetch), a browser app (Lasm.UI) and the API between them (Lasm.RPC). The regulation a request must fulfill is written and proved once, in Shared.lean, which the two sides import (templates/fullstack):

-- Shared.lean @[irreducible] def checkTitle (title : String) : Except String Unit := let n := (clean title).length if n = 0 then .error "Write item first." else if n > maxTitle then .error s!"Keep it under {maxTitle} characters." else .ok () /-- Proved once, holds in the browser and on the server. -/ theorem checkTitle_ok {title : String} (h : checkTitle heading = .ok ()) : 0 < (clean title).length ∧ (clean title).length ≤ maxTitle := by unfold checkTitle at h by_cases h0 : (clean title).length = 0 · simp [h0] at h · by_cases h1 : (clean title).length > maxTitle · simp [h0, h1] at h · omega def addTodo : Endpoint NewTodo Todo := { name := "addTodo", validate := fun r => checkTitle r.title }
-- Server.lean: storing a todo demands the evidence that its heading passed the check, -- and `addTodo.serve` hands the handler that evidence (`ok`) following re-validating. def Store.add (s : Store) (title : String) (_ : checkTitle heading = .ok ()) : Store × Todo := let t := { id := s.next, heading := spotless title, done := false } ({ todos := s.todos.push t, next := s.next + 1 }, t) addTodo.serve fun req ok => do let (s, t) := (← burden kv).add req.title ok preserve kv s come back .ok t
-- Client.lean: a theorem concerning the browser app: it never sends an invalid title. theorem add_invalid (m : Model) (e : String) (h : checkTitle m.draft = .error e) : (update .add m).2.effects = [.msg (.added (.error (.invalid e)))] := addTodo.call_invalid { heading := m.draft } Msg.added "" e h

Where proofs live and how they are checked.

Proof Where Checked by
Your theorems (like checkTitle_ok, add_invalid) your .lean files every lasm build/lasm dev (Lean elaborates them; a damaged evidence is a build error); clean modules additionally alongside lake build
Lasm.RPC: the handler lone runs on requests that continue validate (Endpoint.decodeRequest_valid); the client never sends one that fails it (Endpoint.call_invalid) library/Lasm/RPC compiled into Lasm's archive on all build
Lasm.UI: the DOM diff is accurate against a DOM model, keyed moves included (diff_correct, mount_correct, session_correct) library/Lasm/UI/Proofs (about 1,260 lines, no sorry, norm axioms only) node scripts/ui/lean-tests.mjs in a checkout (CI: ui.yml)
Conduit: 117 theorems concerning authorization, follows, favorites, slugs, pagination, shared validation, JSON circular trips and Markdown nexus safety examples/conduit/Proofs, listed in PROOFS.md npm run proofs in examples/conduit (no sorry, norm axioms only; CI conduit.yml)
TodoMVC: all reachable example is valid examples/ui/todomvc/Todo.lean lasm build; CI ui.yml

Not proved, tested in CI instead: the JSON/HTTP transport between client and server, the JavaScript that applies patches to the genuine DOM, the Lasm compiler and runtime, and the browsers. Lean's own compiler is trusted, as in any Lean program (UI.md, RPC.md).

Calling JavaScript and npm from Lean

Declare all JavaScript function you call alongside one line; the Lean category says how values convert. A complete program (Node's earth fetch):

import Lasm.JS open Lasm.JS /-- `await fetch(url, init)` (Node's earth fetch) -/ @[js "globalThis" "fetch"] opaque fetch : String → Data → IO Value def main : IO Unit := do let reply ← fetch "https://example.com" (obj []) IO.println s!"{← response.get "status" Nat}: {(← response.callMethodAwait "text" #[] String).length} characters"

The rules for penning bindings:

  • @[js "module" "export"] opaque name : A → B → IO R binds module.export. The module is an npm bundle ("marked"), a Node built-in ("node:fs"), or "globalThis" for globals. "new Hono" calls a constructor, and "" is the module's own value.
  • End the category in IO R for a value returned at formerly and in Async R for a promise. An Async binding is awaited without holding a Lean thread.
  • Use String, Nat, Int, Float, Bool, ByteArray, Array, List, Option, tuples, Data (any JS data) or Value (a JS entity reference: get, callMethod, callMethodAwait). Your own structures change alongside deriving ToJS, FromJS.
  • Function.ofFn / Function.ofFnReply rotate Lean functions into JS callbacks. Framework handlers use them (see the hono, province and fastify templates).
  • npm instal the bundle next to the lakefile. lasm build checks all binding against the installed bundle (Lasm.JS binding inspect failed: … export "x" is missing) and copies manufacturing requirements into dist/node_modules.
  • Each call crosses from a Lean thread to Node's chief thread (about 24 µs), so prefer one coarse call complete many small ones.

Full guide, alongside Express, Hono, pg, jose and fetch examples and an agent checklist: docs/JS_BINDINGS.md.

All numbers are from CI on a liberated GitHub runner (4-CPU AMD EPYC, Linux x64), candidate 0.1.0-experimental.40494203854 (2026-10-27) unless another run is named. Full tables, method and caveats: BENCHMARKS.md.

What Number
Node: Hono + Lean handler, chilly commencement to archetypal HTTP response 159 ms (plain Node http: 4.1× faster)
Node: Hono + Lean handler, throughput / p99 latency 24,911 req/s / 4.2 ms (plain Node http: 103,134 req/s, so 0.24×)
Node: Express + Lean handler, throughput 18,678 req/s
Node: Hono app program.wasm (brotli) / dist/ 166 KiB / 8.5 MiB affirmative 1.4 MiB node_modules
Node: memory, Hono app idle / under load 148 MiB / 329 MiB RSS
Lean → JS call circular trip (p50) 24 µs
Cloudflare Workers: Hono app bundle 1.40 MiB, 245 KiB gzip (limit 64 MiB; workers run 40128600109)
Browser: oppose app total transfer / archetypal example (Chromium) 152 KiB brotli / 30 ms (browser run 40288509500)
Browser: js-framework-benchmark vs hand-written JS within 1.7× on all operation, most inside 1.2× (browser run 40302274897)
WASI: Lasm.Fetch component / per petition on Fastly's Viceroy 0.90 MiB / 1.17 ms alongside a caller case per petition (wasi run 40293231963)
Build: spotless Hono build / dev rebuild following a one-line change 4.7 s / 2.0 s
npm instal @lasm/cli: download / installed; tool cache following the archetypal build 71 MiB / 403 MiB; 2.5 GB

Lean on Node pays for WebAssembly and its threads. A chilly commencement is CPU-bound, so it grows on small serverless CPU shares, and all JavaScript call crosses threads. Pure-Lean Std.Http on Node is dilatory (1,290 req/s), so use Hono, Express, Fastify or Lasm.Fetch for manufacturing HTTP. Arithmetic on numbers alongside thousands of digits is slower than native Lean (no GMP).

  • Ahead of period only. A deployed program cannot elaborate, measure or compile Lean, burden component data or plugins, or use Lean's C++ compiler/kernel at run time (exact list). Proofs, tactics, macros and #eval run during compiling, as usual.
  • Lean 4.35.0 exactly. Other lean-toolchain versions are rejected alongside a message.
  • Node: provision an app at smallest 512 MB; it reserves concerning 22 GB of virtual address space. Prebuilt native libraries (-l, shared libraries) cannot be linked; a package's bundled C origin is compiled for you.
  • Edge, Bun/Deno, browser, WASI: single-threaded Lean; no sockets, processes or signals; no records apart from on WASI. In the browser, blocking waits halt the app unless it is built alongside --suspend jspi for a browser alongside JSPI; use Async.
  • No premade bindings for npm packages; Lasm.Web covers the center web platform.
  • Editor: Lasm.* imports determine in the publishing company following npx lasm publishing company (above).
  • Platforms: no Windows ARM64 and no Alpine/musl build machines (built apps run on Alpine). Hosted platforms are tested alongside their local tools, never alongside a real account.

If Lasm is helpful to you or you desire to see it succeed, I'd value your sponsorship: github.com/sponsors/lastmjs. It supports me and the activity on Lasm and the another Millillion projects. My archetypal goal is $250 a month, adequate for one dedicated Claude Code account for Lasm. You can additionally assistance by leading the repo, trying it, and reporting what breaks.

Everything below is the exact contract: all command and option, supported machines and platforms, and exactly what a deployed Lean program cannot do.

Install in a project alongside npm instal -D @lasm/cli (the bin is lasm), or run without installing: npx @lasm/cli Main.lean. npm create @lasm runs lasm init of the identical @lasm/cli type alongside the arguments unchanged. With the Main.lean from the quick start:

npx lasm Main.lean # build (dev profile) and run: Hello from Lean! 2 + 3 = 5 npx lasm build Main.lean # compose dist/ (release profile) npx lasm --version # version, Lean, runtime and phase (for bug reports) node dist/main.mjs # run the deployment: lone Node is needed npx lasm build Main.lean --target lambda # dist/ and an AWS Lambda zip
Command or option Example Effect
Run (default) npx lasm Main.lean Build (dev profile) and run Lean main. npm run dev in a plain project runs this.
Explicit run npx lasm run Main.lean Same as the default command.
Run a publish build npx lasm Main.lean --release Build and run exactly what lasm build deploys.
Program arguments npx lasm Main.lean -- hello "two words" Pass arguments to chief (args : List String).
Watch, rebuild, restart (0.3.0) npx lasm dev, npx lasm dev Main.lean --target workers -- arg Watch the Lean sources and the Lake/npm configuration, rebuild following all alter (dev profile, one long-lived build worker) and restart the program (--target node, bun, deno; default: the runtime operating Lasm). A build error is printed alongside its file, row and origin line; the final fine program keeps running. Edge targets lone rebuild their output directory, which the platform's own local server serves (Lasm never starts phase tools or browsers). --target browser serves the app alongside live reload (--port, --host, --no-watch); multiple outputs build together: --target node=Server.lean,browser=Client.lean. --release builds the publish profile, --no-run lone rebuilds, rs + Enter rebuilds and restarts. Details.
Build npx lasm build Main.lean Write dist/ (release profile); do not run the application.
Dev build npx lasm build Main.lean --dev Write dist/ faster, without the size optimizations.
Output directory npx lasm build Main.lean --output "app dist" Choose the deployment directory. lasm build Main.lean "app dist" is an alias.
Function platforms npx lasm build Main.lean --target lambda Build dist/, afterward bundle it: node (default; dist/ only), lambda (lambda.zip), lambda-edge (lambda-edge.zip), vercel (.vercel/output), netlify (netlify/functions/lasm), azure (azure-functions/), gcf (gcf/). --target=vercel additionally works. Lasm never installs or starts the platform's CLI. Recipes.
Edge isolates (0.2.0) npx lasm build Main.lean --target workers Relink single-threaded alongside a pure-JS host: workers (workers/), vercel-edge (api/lasm.mjs, api/_lasm/), netlify-edge (netlify/edge-functions/lasm.mjs, netlify/lasm/), deno-deploy (deno/main.mjs), supabase-edge (supabase/functions/<name>/; Asyncify, since Supabase's runtime has no JSPI). The border runtime ships inner @lasm/cli. Guides: Workers, Vercel Edge, Netlify Edge, Deno Deploy, Supabase; design and limits.
Bun and Deno (0.2.0) bunx --bun @lasm/cli Main.lean, deno run -A npm:@lasm/cli Main.lean, bun create @lasm my-app --template hono The compiler runs alongside Bun or Deno as its lone JavaScript runtime and produces the identical bytes. Under Bun or Deno the default mark is that runtime: --target bun writes bun/main.mjs, --target deno writes deno/main.mjs (portable profile). Deno skips npm versions younger than 24 hours: correct following a release, continue --minimum-dependency-age=0. Bun and Deno.
Browser (0.3.0) npx lasm build Main.lean --target browser Write dist/ as a fixed site: app.<hash>.js (the loader: mount(element, { flags, hydrate }), auto-mount of <div data-lasm-app>), app.<hash>.wasm, brotli and gzip copies, app.js, index.html (yours alongside the loader injected, or a generated page), your public/ records and lasm-manifest.json. --suspend jspi lets blocking waits suspend anywhere the browser has JSPI. --bundler leaves npm imports for a bundler; in Vite: import lasm from '@lasm/cli/vite', afterward import app from './Main.lean?lasm'. Web APIs from Lean: import Lasm.Web. Guides: BROWSER.md, UI.md, deploying.
WASI, wasi-http, Fastly (0.3.0) npx lasm build Main.lean --target wasi (or wasi-http, fastly) No JavaScript. wasi writes wasi/main.wasm (wasi_snapshot_preview1: wasmtime run --dir . wasi/main.wasm, wasmer, node:wasi); wasi-http writes wasi-http/main.wasm, a wasi:http/[email protected] component (wasmtime serve, Spin); fastly writes bin/main.wasm and pkg/<name>.tar.gz (fastly compute serve/deploy). No sockets, processes or signals. WASI.md, Fastly.
HTTP in clean Lean (0.3.0) import Lasm.Fetch … def chief := Lasm.Fetch.serve handle One Request → Async Response handler serves Node ($HOST:$PORT), the border targets, Bun, Deno, Fastly, wasi-http and WASI as CGI; Lasm.Fetch.fetch sends requests; Response.streaming streams. FETCH.md.
Force rebuilding npx lasm build Main.lean --rebuild Ignore a cached use build.
Detailed diagnostics npx lasm Main.lean --verbose Show compiler and linker details.
Help npx lasm --help, npx lasm init --help Show usage; -h is an alias.
Version npx lasm --version Print lasm 0.3.5 (@lasm/cli; Lean 4.35.0; Node 26.12.1; linux-x64); -v is an alias.
New project npm create @lasm@latest my-app -- --template hono or npx @lasm/cli init my-app --template hono Create a Lake project in a new or bare directory (Main.lean, App.lean, lakefile, lean-toolchain, package.json, README.md, .gitignore, and for servers a Dockerfile and .dockerignore). Templates: see Start here. --deploy (repeatable) adds a platform's records and npm run build:<target>: lambda (Dockerfile.lambda, a receptacle image), lambda-zip, lambda-edge, vercel, netlify, azure, gcf; workers, vercel-edge, netlify-edge, deno-deploy (templates hono, fetch, fullstack), supabase-edge (hono); fastly, wasi-http, rotation (fetch, fullstack), wasi (fetch, plain). The fetch and fullstack templates and their targets are 0.3.0. Writes records only; run npm instal next. -t is an pseudonym of --template; --template=hono additionally works.
Package an existing build npx lasm bundle --target lambda Package a built server (default dist/) for a function phase without rebuilding: the identical targets and outputs as lasm build --target, apart from node. --output PATH chooses the output. Checks the platform's size limit. Refuses a dev build unless --allow-dev. For a browser build, npx lasm bundle dist --target browser checks all document against lasm-manifest.json and writes _headers.
Editor assistance (0.3.0) npx lasm editor Make the Lean publishing company (VS Code's Lean 4 extension, any Lean tongue server client) determine import Lasm.* in this Lake project: links Lasm's Lean 4.35.0 alongside its archive as the elan toolchain lasm-4.35.0-… and sets it as the directory's elan override (elan override unset undoes it). lasm init (when Lasm's Lean is already installed), lasm dev and lasm build do the same, printing one row per project, apart from on CI or alongside LASM_EDITOR=0. Editor setup.
Cache repair npx lasm cache repair Offline verification and elimination of invalid tools and abandoned staging. Refuses an energetic build.

--rebuild and --verbose activity alongside run and build. --output and --target are build-only; arguments following -- are run-only. The application's exit position is preserved. npx lasm --version (-v) prints the version, Lean release, JavaScript runtime and phase (include it in bug reports). There is no install, spotless or uninstall subcommand.

Build profiles. Both nexus the identical program alongside the identical libraries and are deterministic. release (lasm build, lasm Main.lean --release): the full Binaryen size optimization; byte-reproducible; the one to deploy. dev (lasm Main.lean, lasm dev, lasm build --dev): leaves those passes out and compiles your modules at -O1; it behaves the same, builds multiple seconds faster, and its program.wasm is concerning 15–20 % larger. A dev rebuild starts from the project's former build, so its bytes can depend on that build. build-info.json records "profile". lasm bundle and lasm build --dev --target <platform> refuse a dev build unless you continue --allow-dev.

Environment variables (all optional):

Variable Example Effect
LASM_TOOLCHAIN_CACHE LASM_TOOLCHAIN_CACHE=/data/lasm-cache npx lasm Main.lean Move the managed tool cache (default locations).
LASM_BUILD_JOBS LASM_BUILD_JOBS=2 npx lasm build Main.lean Threads for Binaryen's optimizer (default: the CPUs, at most 8). The output is the identical for any value.
LASM_EDITOR LASM_EDITOR=0 npx lasm build Main.lean 0: lasm init, lasm dev and lasm build depart the publishing company setup solitary (editor setup).
LASM_THREAD_POOL (built apps; 0.3.0) LASM_THREAD_POOL=0 node dist/main.mjs Lean thread Workers started before chief (0–64, default 1). Measurements.
LASM_ARTIFACT_SOURCE LASM_ARTIFACT_SOURCE=npm npx lasm Main.lean auto (default): @lasm/… npm tool packages whenever present, alternatively HTTPS; npm: never download directly; https: disregard the npm packages.
LASM_DOWNLOAD_ATTEMPTS, LASM_DOWNLOAD_CONNECT_MS, LASM_DOWNLOAD_IDLE_MS, LASM_DOWNLOAD_TOTAL_MS LASM_DOWNLOAD_TOTAL_MS=14400000 npx lasm Main.lean Download retries and timeouts (defaults and ranges).
NODE_USE_ENV_PROXY, HTTPS_PROXY, NO_PROXY, NODE_EXTRA_CA_CERTS (Node's own) NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://proxy:8080 npx lasm Main.lean Proxies and personal CAs (corporate networks).

Other LASM_* variables in the origin are maintainer diagnostics, not a supported interface.

Installation. npm instal @lasm/cli brings Lean and the build tools as exact-pinned @lasm/… npm packages for your phase through your own registry, proxy and cache. The Lake/Lean-import data and, lone for Lake projects alongside C code, the C tier (@lasm/lean-c-<platform>) are fetched alongside your npm on archetypal use. The terminal shows progress; afterward builds reuse verified tools and unchanged outputs. On a new machine, without the download, a archetypal Hono build took 25.7 s (Linux x64), 27.8 s (Linux arm64), 44.4 s (Windows), 58.7 s (Apple Silicon) and 141.9 s (Intel Mac) on the CI runners (build time, run 40131930601). Cache locations and recovery: NODE_SUPPORT.md, INSTALLATION_HARDENING.md. Git is needed lone for Lake projects alongside Git or Reservoir dependencies; Lasm uses the git on your PATH and says how to instal it whenever it is missing.

Existing Lake projects build alongside npx lasm build Main.lean from the project directory. Pin leanprover/lean4:v4.35.0 in lean-toolchain; another versions are rejected alongside Unsupported Lean toolchain … Lasm supports Lean v4.35.0. A per-package compatibility list covers Batteries, Cli, LSpec, Plausible, Aesop, Qq, leansqlite, UnicodeBasic, MD4Lean, lean-zip, lean4-parser, lean-redis and others; packages not on it are untested.

Machines that build alongside Lasm (the compiler):

Environment Architectures Status
Ubuntu 24.04 LTS x86-64, ARM64 Installed-package and copied-deployment acceptance in CI; the citation platform.
Other Linux distributions x86-64, ARM64 Unverified as build machines; the tools need glibc 2.34. Alpine/musl is not a build device (built apps run there).
macOS 15 x86-64, ARM64 Installed-package and copied-deployment acceptance in CI. Lake projects alongside C code need the Xcode Command Line Tools SDK.
Windows Server 2025 / Windows x64 x86-64 Installed-package and copied-deployment acceptance in CI.
Windows ARM64 ARM64 Not supported: neither Lean nor Emscripten publishes Windows ARM64 prebuilts.
Bun 1.4.4 and Deno 2.9.6 as the compiler's runtime (0.2.0) all of the above Tested in CI on all five platforms, additionally alongside no Node or npm on PATH (BUN_DENO.md).

Node 24 or newer (tested: 24.0.0, 24.22.1, 26.12.1). No motor flags. Exact tested artifacts: NODE_ACCEPTANCE.md.

A Node dist/ needs Node 24 or newer (Node 22 and older halt alongside This use requires Node 24.0.0 or newer) and at smallest 512 MB of memory. It runs on Linux x64 and arm64 (glibc 2.28+, or musl/Alpine) from a build on any supported platform, and on the build machine's own platform. Copy all of dist/; no Lean source, npm instal or build tools are needed there. Each phase below is tested in CI alongside the local runtime, depiction or emulator named; none alongside a real cloud account. Full matrix: DEPLOYMENT.md.

Platform Tested in CI with
Linux x64 and arm64 (glibc 2.28+), Node 24+ Node 24.0.0, 24.22.1 and 26.12.1 on ubuntu-24.04 and ubuntu-24.04-arm
macOS (x64, arm64) Node 24.0.0 and 24.22.1 on macos-15-intel; Node 24.22.1 on macos-15; installed acceptance alongside Node 26.12.1
Windows x64 Node 24.0.0 and 24.22.1 on windows-2025; installed acceptance alongside Node 26.12.1
Docker / Kubernetes node:24-bookworm-slim, node:26-bookworm-slim, node:24-bullseye-slim, Ubuntu 22.04/24.04; read-only, 512 MB, x64 and arm64 (Kubernetes manifests are not applied in CI)
Google Cloud Run, Fly.io, Railway, Render, Heroku the identical receptacle images and Dockerfiles; the platforms themselves are never deployed to
AWS Lambda (container image) public.ecr.aws/lambda/nodejs:24 + AWS Lambda Web Adapter through the Runtime Interface Emulator, x86_64 and arm64; amazonlinux:2023
AWS Lambda (.zip, nodejs24.x) every server template's --target lambda zip in public.ecr.aws/lambda/nodejs:24 through the Runtime Interface Emulator, x86_64 and arm64, alongside API Gateway REST/HTTP, function URL and ALB events; sam local invoke and sam local start-api
Lambda@Edge (origin request, x86_64) the identical depiction and emulator alongside CloudFront origin-request events
Vercel Functions (Node.js 24) vercel build offline; the function under @vercel/node in public.ecr.aws/lambda/nodejs:24
Netlify Functions (v2) netlify build --offline and netlify dev --offline; the deploy zip in public.ecr.aws/lambda/nodejs:24
Azure Functions (Node.js v4 model, Linux) Azure Functions Core Tools func start
Google Cloud Functions (gen2, nodejs24) Functions Framework on Ubuntu 24.04; Google Cloud's buildpacks image
Alpine/musl containers node:24-alpine, node:26-alpine, x64 and arm64, read-only, 512 MB
DigitalOcean App Platform (gVisor) the receptacle images under gVisor's runsc, x64
Cloudflare Workers (0.2.0) wrangler dev 4.152.0 (workerd), alongside the Free plan's budgets gated
Vercel Edge (0.2.0) vercel build offline and Vercel's edge-runtime
Netlify Edge (0.2.0) netlify build --offline and netlify dev --offline
Deno Deploy (0.2.0) Deno 2.9.6 operating deno/main.mjs
Supabase Edge Functions (0.2.0) Supabase's edge-runtime v1.77.8 depiction in Docker
Bun and Deno programs (0.2.0) Bun 1.4.4 and Deno 2.9.6 on all five platforms
WASI, wasi-http, Spin, Fastly (0.3.0) wasmtime 49.0.4 (run, serve), wasmer 7.4.2, Node's node:wasi, Spin 4.2.2, Viceroy 0.21.1 (wasi.yml)
Browsers (0.3.0) Playwright 1.63.0: Chromium 153 and Firefox 155 on ubuntu-24.04; WebKit 26.6 on ubuntu-24.04 and macos-15 (browser.yml)

A dist/ built on Windows and copied to Linux another than through docker build needs chmod 755 dist/host/native/process/linux-*. Node's --jitless cannot work (it disables WebAssembly). Hosts that cap virtual recollection (ulimit -v, vm.overcommit_memory=2) refuse the Node build, which reserves concerning 22 GB of address space. Node 26's authoritative binary needs libatomic1 on minimal images that install the tarball themselves.

Lean assistance and restrictions

Builds enforce ahead-of-time execution. Ordinary functions, recursion, closures, inductive types, form matching, category classes, reliant types and compiled IO are supported. Imports, macros, elaboration, tactics, deriving, proofs and #eval run during compilation in managed native Lean. Initializers count as runtime dependencies: a broad import Lean can keep the compiler; use meta import Lean whenever lone macros or strategies need it, and narrower imports for runtime data libraries (import Lean alongside Lean.Json, and deriving ToJson, FromJson, build and run). Each rejection is explained: Lasm prints the sequence from your code to the compiler code and, whenever a broad import is the cause, the narrower modules your code uses. Deployed programs regard LEAN_ABORT_ON_NONLINEAR=1 as native Lean does. Per-platform results: language coverage.

The deployed use cannot use:

Unavailable at runtime Examples and exact boundary
Load Lean component data or find a compiler installation Lean.importModules, Lean.withImportModules, module-data readers/writers, Lean.findSysroot, Lean.initSearchPath; deployed .olean/IR loading.
Evaluate or compile new Lean code Lean.Environment.evalConst, translator evaluation, a Lean REPL, runtime elaboration/compilation, evaluation of declarations loaded by name. Calling an average compiled function is allowed.
Use Lean's compiler/kernel runtime state Runtime elaboration, kernel evidence checking, compiler environments, language-server machinery needing Lean's C++ compiler/kernel. Pure Lean data operations are allowed if their reachable requirements continue the check. LeanExport.parseStream builds Lean.Expr values through the kernel and is rejected.
Load executable plugins dynamically Lean/Wasm plugin loading, dlopen/dlsym (C code calling dlopen gets an implementation that continually fails), runtime Lean sign lookup.

The build checks reachable runtime dependencies, including imported libraries and indirect calls; a reachable forbidden call is rejected equal if your input would not obtain that branch, and deceased code removed by the linker does not count. The launcher additionally rejects missing, old or incompatible capability records before loading the use (a consistency check, not a safety sandbox). There is no large compatibility fallback.

Lake packages. Modules imported lone for compilation (tactics, elaborators, for example Aesop, Qq or import Lean for macros) do not keep Lean's compiler in the deployment. Lasm runs a module's initializers lone whenever the program uses something they define or register, or whenever they are the program's own code doing IO or calling C (--verbose shows how many were skipped). C origin that a package bundles (extern_lib, moreLinkObjs, for example leansqlite's SQLite) is compiled with Lean's own clang, fetched formerly as the C tier (39–52 MB); no scheme C compiler is needed (macOS needs the Xcode Command Line Tools SDK for C archive headers). Prebuilt native libraries (shared libraries, -l options: libcurl, OpenSSL, libpq, BLAS) cannot be linked; the build names the archive and the package.

Not publish guarantees. Console output, arguments, exit codes, local Lake imports and selected language/filesystem fixtures have native-versus-Node tests, and before 0.1.0 Lean's runtime-capable upstream suite and 19 ecosystem projects ran on all five platforms alongside zero regressions against the former accepted candidate (0.1.0 gate). Complete IO.FS, concurrency, subprocess/signal/terminal parity, all third-party archive and the complete upstream suite are not guaranteed (IO gaps). Big numbers (Nat/Int beyond 63 bits) use Lean's own arbitrary-precision code alternatively of GNU MP, so apps contain no LGPL code; results equal native Lean's, but at 10,000 digits multiplication is concerning 5× and Nat.gcd concerning 60× slower than alongside GMP in Wasm (numbers). Lake dependencies use your scheme Git (system Git).

0.1.0 was the archetypal npm publish (notes); 0.2.0 adds edge targets and Bun/Deno (notes); 0.3.0 adds the browser, Lasm.UI, full-stack apps, WASI/Fastly and the publishing company support (notes); 0.3.1 puts the starter code under MIT-0 and adds bug-report pointers (notes); 0.3.2 fixes Std.Internal.UV sockets, timers and signals (notes); 0.3.3 was never published, and its changes shipped in 0.3.4 (notes); 0.3.5 updates the README and the Conduit example (notes). Lasm's npm packages (@lasm/cli, @lasm/create and the @lasm/… tool packages) are published lone by the release.yml workflow, through npm trusted publishing alongside provenance: no npm tokens exist. A publish is a pull petition that bumps the package.json type and is published lone following the release-checks position (five-platform applicant checks; edge, Bun/Deno, browser, Lasm.UI, WASI/Fastly, fullstack, component and dev-loop checks; and an npm rehearsal of the exact packages) passed for the released commit. Found a bug? npx lasm --version gives the type row the bug study form asks for. npm perspective @lasm/cli@<version> shows the provenance, and npm audit signatures in a project verifies it. Details: RELEASING.md.

Lasm's own origin is under the MIT License (copyright Jordan Last). The starter code — everything lasm init and npm create @lasm put in a new project, templates/, examples/ and the code samples in this README and docs/ — is additionally under MIT No Attribution (MIT-0), in every version: it is yours, no attribution needed. @lasm/create is MIT, and @lasm/cli's licence site is an SPDX expression of Lasm's MIT, MIT-0 and every component it bundles, alongside their texts in licenses/. The license audit lists all distributed component, its license and how all duty is met.

  • Built dist/. THIRD_PARTY_NOTICES.txt starts alongside Lasm's MIT notice and the catalog of Lean records Lasm changed, afterward the licenses of what the build contains: Lean's runtime and norm archive (Apache-2.0), Emscripten (MIT OR NCSA), musl, LLVM libc++, libc++abi, libunwind and compiler-rt, mimalloc, libuv, koffi and Lasm's presenter (MIT). Apps merge no copyleft code. licenses/<package>/ holds the license records of all Lake dependency compiled into program.wasm; alongside npm interop, node_modules/ holds all bundle alongside its own license. Keep these files whenever you allocate a dist/.
  • Edge, browser, WASI, Fastly and wasi-http outputs get the identical notices, rewritten to say what their records contain, written automatically: a THIRD_PARTY_NOTICES.txt in all output directory, a abbreviated /*! … */ licence banner on the JavaScript a phase bundles (the border glue, the browser loader), the entire notices document inner the Wasm of server-side outputs (a lasm.notices custom section, so it travels alongside all deployment), the document in the Fastly package, and THIRD_PARTY_NOTICES.lasm.txt in Vite builds. What this method for your app: docs/LICENSING.md.
  • Tool packages and publish archives. Each @lasm/… tool bundle has an SPDX license site and the archive's licence files. The exact sources of the (L)GPL pieces the Lean archives merge (GNU MP, the GNU C Library, GCC's runtime libraries, the Linux UAPI headers, MinGW-w64) and Lean's own origin are mirrored as resources of the publish that publishes them (lean-4.35.0-tiers-v2). Lasm ships no Git and no Apple SDK files.
  • The Lean name and logos are trademarks of Lean FRO. Lasm is not affiliated with or endorsed by Lean FRO.
Other Article Hacker News
↑
Close Right Ads
Close Left Ads