Files
mathew/lean/README.md
Aran Roig 6df7950161
Some checks failed
Build and Deploy Nuxt / build (push) Failing after 41s
Ai slop
2026-10-03 14:13:08 +02:00

6.5 KiB

lean/ — the Lean server

This folder builds lean4web — whose server runs a real Lean process (with Mathlib) for every session — as a Docker image serving websocket sessions on port 8080. That is why it does not live in the backend or the frontend: upstream asks for a dedicated machine.

The wiki's /lean page talks to this server itself: frontend/app/utils/lean/ implements a Lean language client (JSON-RPC over the websocket), and the page's Monaco editor, Goals and Messages panes (LeanWorkbench.vue) are the wiki's own — styled by the wiki, themed live. lean4web's bundled client is still built and served at the port's root (dressed in the wiki's skin, see below); the editor's status bar links to it for features the wiki client does not chase — live collaboration and interactive widgets.

Build & run

docker compose build lean            # long: fetches Mathlib and unpacks its cache
docker compose up -d lean            # → http://localhost:8080 (and the wiki's /lean)
docker compose logs -f lean

# or plain docker
docker build -t mathew-lean ./lean
docker run --rm -p 8080:8080 mathew-lean

GET http://localhost:8080/health answers when the server is up — the image carries a HEALTHCHECK for it. The frontend finds the server at NUXT_PUBLIC_LEAN_URL (default http://localhost:8080, dev included), so out of the box there is nothing else to configure.

To build against a fixed lean4web release rather than its main:

docker build --build-arg LEAN4WEB_REF=v0.2.2 -t mathew-lean ./lean

(any branch or tag; the clone is shallow, so commit SHAs are not offered.)

The protocol the wiki's client speaks

The websocket endpoint is /websocket/<project> (project names come from GET /api/projects, answered only same-origin — the REST endpoints carry no CORS headers, so the wiki falls back to MathlibDemo when reaching the server across origins; the websocket itself is CORS-free). One JSON-RPC message per frame — the Lean 4 language server protocol, which the server forwards raw to a per-session lake serve. What frontend/app/utils/lean/lsp.ts + useLeanSession.ts do with it:

  1. initialize — and Lean is strict here: rootUri must be a string (the server's URI rewriter throws on null and the message vanishes silently), window.showDocument must be { support: … }, insertTextFormat must be { valueSet: [...] }; the Lean watchdog exits the session on anything it cannot parse. Positions negotiate as utf-16, which matches Monaco.
  2. initialized, then textDocument/didOpen a single document named <project>/Main.lean; edits go as debounced whole-document didChanges.
  3. Goals: the custom request $/lean/plainGoal at the cursor answers { goals: string[], rendered }. ($/lean/plainMessages no longer exists — Lean puts #check-style output in severity-3 publishDiagnostics, which is what the wiki's Messages pane reads; elaboration busy-state comes from $/lean/fileProgress.)
  4. Server→client requests (client/registerCapability, */refresh, workspace/configuration) get automatic null acknowledgements — a single-document client must still answer, or Lean waits.

The skinned classic client

Reaching the server's own address (the wiki editor's status-bar link, or a direct visit) opens lean4web's bundled client — and the image builds it wearing the wiki. theme/ holds the layer: mathew.css redefines the --vscode-* custom properties lean4web itself paints with (top bar, menus, modals, its Monaco editor) in Mathew's tokens — Computer Modern for the chrome, the paper/slate surfaces, the violet accent — and bundles the CMU fonts (with their SIL license); theme-init.js records the theme on <html data-mathew-theme> before the first paint, taking ?theme=light|dark or the browser's preference. The Dockerfile copies both into the client's public/, links them from index.html, and retitles the tab.

To re-skin or extend: edit theme/mathew.css and rebuild — the patch lands after the cached Mathlib steps, so it is only a client rebuild.

The server's own knobs

lean4web reads these from the environment — pass them with -e / compose environment if you change them:

Variable Default here Meaning
PORT 8080 The one port for the editor client and its websocket
NO_BWRAP true Run Lean without bubblewrap — see Security below
SSL_CRT_FILE / SSL_KEY_FILE unset Serve HTTPS directly (the server also redirects :80 → https)
PROJECTS_BASE_PATH Projects Where the Lean projects live, relative to the repo root
VITE_COLLAB true lean4web's collaboration feature (set at build time)

Security

Upstream wraps each Lean session in Bubblewrap; that needs nested user namespaces, which a plain container does not have — so this image sets NO_BWRAP=true and lets Docker itself be the sandbox. The consequence to keep in mind: whoever can reach port 8080 can run Lean code on this machine, inside the container. Keep the port on the LAN / VPN, or put it behind a proxy with authentication, before exposing it past your network.

TLS matters to the wiki page too: if Mathew is served over HTTPS, the browser will refuse the editor's ws:// connection to an http:// server (mixed content). Serve the Lean server over HTTPS — either its own SSL_CRT_FILE/SSL_KEY_FILE, or a reverse proxy in front (websocket upgrade on the same path) — and point NUXT_PUBLIC_LEAN_URL at the https:// address.

Updating

The image clones lean4web at build time, so a rebuild is an update:

docker compose build --pull lean && docker compose up -d lean

Rebuilding also re-runs Projects/*/leanweb-build.sh — the demo projects are rebuilt and Mathlib's cache re-fetched, so treat this like the heavy build it is (several GB of image; run it on the machine that hosts the editor).

Adding Lean projects

Anything under Projects/ of the image source becomes a selectable project (#project=<name>). To ship one with the image, build a custom image that copies it in before the project build step:

FROM mathew-lean AS base   # or edit lean/Dockerfile directly
COPY MyProject /opt/lean4web/Projects/MyProject
WORKDIR /opt/lean4web/Projects/MyProject
RUN lake build

See upstream's Adding Projects for the leanweb-config.json each project needs.