browser

lightpanda-ioAGPL-3.0活跃维护

用 Zig 从零编写的无头浏览器,专为 AI 和自动化场景设计,兼容 CDP 协议,可替代 Playwright、Puppeteer 驱动网页抓取与 Agent 任务。

Lightpanda: the headless browser designed for AI and automation

1.0.3发布于 2026-11-05 09:04 UTC41,756star +236 / 7 天2,082fork同步于 2026-11-10 11:19 UTC

怎么上手

由 README 提炼,执行前请确认
brew install lightpanda-io/browser/lightpanda
docker run -d --name lightpanda -p 127.0.0.1:9222:9222 lightpanda/browser:latest
硬件要求按 README 说明

README 未说明显存或 GPU 要求;提供 Linux(x86_64、aarch64)和 macOS(x86_64、aarch64)二进制,Windows 需在 WSL2 内运行。

有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

用 Zig 从零写的无头浏览器,专为 AI 代理和自动化设计,比 Chromium 更轻更快

用 Zig 从零编写的无头浏览器,专为 AI 和自动化场景设计,兼容 CDP 协议,可替代 Playwright、Puppeteer 驱动网页抓取与 Agent 任务。

核心能力

  • 用 Zig 从零实现,不是 Chromium 或 WebKit 的分支
  • 内置 CDP 服务,Puppeteer 通过 browserWSEndpoint 直连
  • fetch 支持 dump html、markdown、png、pdf 和 structured_data
  • serve 可同时开启 CDP 与 WebDriver Bidi 协议
  • agent 模式用自然语言或斜杠命令驱动浏览器操作

容易踩的坑

Linux 二进制依赖 glibc,Alpine 等 musl 发行版会报 cannot execute,x86_64 需 glibc 2.35、arm64 需 2.38;没有原生 Windows 和 Android 构建,Windows 走 WSL2、Termux 无法运行;从源码构建需 Zig 0.17.0,并先跑 make download-v8 拉预编译 V8,否则会从源码编译耗时数分钟。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本1.0.3
  1. 1.0.3最新正式版2026-11-05 · 2026-11-05 09:04 UTC

    Behavior changes

    • serve: the --http-max-concurrent default now follows --cdp-max-connections (8× it, between 40 and 256). With the default 16 connections it is 128.
    • fetch --dump markdown now asks the server for markdown (Accept: text/markdown,...). A text/markdown response is dumped as-is. Not sent with --dump-selector, --wait-selector or --wait-script.
    • Accessibility.getPartialAXTree now honors fetchRelatives, as Chrome does: it returns the node and its relatives, no longer the whole subtree. Use getFullAXTree or getChildAXNodes for a subtree.
    • DOM.querySelector with no match now returns nodeId: 0 instead of an error, as Chrome does.
    • Touch event handlers (ontouchstart, …) are removed from document and elements: Lightpanda doesn't emulate a touch device.
    • navigator.sendBeacon now checks its arguments. Beacons are still not sent unless --load-resources beacon is set.
    • The HTTP cache database is deleted and rebuilt when its schema version changes.
    • waitForState now reports a timeout instead of always answering that the page reached the state.
    • MCP over HTTP: a request on a session closed with DELETE now gets a 404 instead of starting a new session.
    • fetch() and new Request() with a body on GET or HEAD now throw a TypeError.
    • Scripts inserted from an isolated world (Playwright setContent, appendChild) now run in the main world.
    • Event listeners now run in their own world's context, so Playwright's focus() and selectOption() fire React's onFocus/onChange.

    New Web APIs

    • AudioContext (WAV only)
    • Payment Request API: PaymentRequest, PaymentResponse and their events, behaving like Chrome with no payment app installed
    • NavigateEvent on navigation (intercept() is not implemented)
    • PerformanceNavigationTiming entry for the document
    • document.execCommand: insertText, delete, forwardDelete, selectAll, insertLineBreak, and the styleWithCSS, useCSS and defaultParagraphSeparator settings. Other commands return false
    • image.decode()
    • HTMLElement.isContentEditable (real value), HTMLElement.spellcheck, document.designMode
    • Touch constructor and TouchEvent touch lists
    • elementFromPoint/elementsFromPoint on ShadowRoot
    • SVG getBBox()
    • HTMLFrameElement, SecurityPolicyViolationEvent, a stub Path2D
    • navigator.mimeTypes (empty MimeTypeArray)
    • img.sizes; link.relList.supports() for dns-prefetch, preconnect and prefetch
    • HTMLFieldSetElement.elements; abbr, mark, small, wbr and other HTMLElement-only tags are now HTMLElement instead of HTMLUnknownElement
    • onmouseenter and onmouseleave handlers
    • Web Crypto: raw-secret and raw-public key formats; CryptoKey is now serializable (structuredClone, postMessage, IndexedDB)
    • MessagePort is now transferable
    • TextDecoderStream decodes every encoding TextDecoder supports, with state kept across chunks
    • XMLSerializer now writes XML instead of HTML
    • WebMCP follows the current spec: document.modelContext, getTools(), toolchange, registerTool returns a promise
    • CSS: native nesting, color parsing, more known properties (CSS.supports, unknown property errors)

    Web API behavior / fixes

    • Layout: flex-shrink, horizontal margins, left/right/inset, and widths from text measurement. Widths are relative to the containing box. Out-of-flow children no longer size their parent, and block boxes start at their parent's left edge
    • position is now read from stylesheets, not only from the inline style (offsetParent, getComputedStyle)
    • An iframe without size is now 300×150, and its box is the frame's viewport, so Playwright can click inside it
    • Range.getClientRects() now returns one rect per selected element or text node
    • elementFromPoint returns null outside the viewport
    • Textarea scrollHeight now counts its text, for auto-sizing textareas
    • Typing
  2. 1.0.22026-10-23 · 2026-10-23 09:01 UTC
    查看更新说明

    Behavior changes

    • JS page errors now log under a new page_error scope at WARN (silence with --log-filter).
    • Each top-level page now has its own session history; a second page no longer shows up in the first page's back/forward.
    • --block-private-networks now blocks more non-routable ranges (CGNAT, benchmarking, multicast, reserved, NAT64).
    • no-cors fetches with a non-safelisted method are now rejected.
    • An unreadable --http-cache-dir database is now discarded instead of exiting.
    • Console values written to the log are now escaped and capped.

    New Web APIs

    • Web Crypto: Ed25519, X25519, ECDSA, RSA-PSS, RSASSA-PKCS1-v1_5, wrapKey/unwrapKey + AES-KW; EC and RSA-OAEP import/export
    • WebCodecs, OfflineAudioContext, WebVTT improvements
    • XML document parsing
    • setRangeText() on <input> and <textarea>; textarea.textLength
    • HTMLProgressElement value/max/position; HTMLOptionElement.index; missing table APIs
    • document.hasFocus(); window.outerWidth/outerHeight/screenX/screenY/screenLeft/screenTop
    • :nth-child(An+B of S) and :nth-last-child(An+B of S)
    • PermissionStatus is now an EventTarget; DOMException is now structured-cloneable; ImageData overload
    • Missing global event handlers added to window, document and body

    Web API behavior / fixes

    • Element widths are now resolved relative to their parent (block, inline, floats, %, min/max-width, basic flex)
    • Client rects (getBoundingClientRect, getClientRects) are now viewport-relative
    • Mouse events now reach into shadow DOM and iframes
    • Escape now closes the topmost modal dialog
    • Iframes inside a shadow root now load when their host connects
    • replaceWith compliance and cross-document adoption; isEqualNode(null) now returns false
    • insertAdjacentHTML now works on SVG elements
    • Trusted key events now report Chrome's legacy keyCode/which/charCode
    • Text selection offsets now count UTF-16 code units
    • TextDecoder now only strips a BOM for UTF-8/UTF-16
    • form.elements now lists fieldset, object and output
    • Sequence arguments now accept any iterable
    • Better getComputedStyle().transform
    • Fixed CSS nested rule parsing; escapes are now decoded in unquoted attribute selectors
    • Cookies are now sent longest path first, then oldest first
    • 401/407 responses are now delivered as-is when no auth challenge header is present

    CDP

    • Fetch.continueRequest interceptResponse is now supported
    • Fetch.continueWithAuth with Default/CancelAuth now delivers the original 401/407, as Chrome does
    • Runtime.exceptionThrown is now emitted for unhandled promise rejections, with the context the exception was thrown in
    • Log.entryAdded is now emitted for resources that fail to load
    • DOM.scrollIntoViewIfNeeded is now supported (previously a no-op)
    • LP.getPageStatus added: page issue summary (JS errors, skipped resources, fetch stats). Also included in fetch output when --json is used
    • Custom ARIA states and focusability are now exposed in the AX tree
    • Non-UTF-8 status text and headers are now reported as strings rather than a byte array
    • Permission overrides are now cleared when a browser context closes

    Serve / CLI

    • serve --unix-socket <path> added: CDP over a Unix domain socket
    • --remote-allow-origins added: allow CDP connections that send an Origin header (rejected by default), matching Chrome
    • serve now honors --cookie-jar: the jar is saved when a connection closes. With multiple connections, last write wins
    • fetch --dump structured_data added
    • fetch --markdown-alternate added: follow a page's text/markdown alternate. text/markdown documents are now dumped as-is

    Tools

    • markdown tool: alternate option added
    • A failed goto now closes the page
    • Tree tool: links kept inside headings, multi-line names kept on one line

    Performance / stability

    • Reworked TaggedOpaque (less memory)
    • The schedul
  3. 1.0.12026-10-15 · 2026-10-15 14:55 UTC
    查看更新说明

    Behavior changes

    • setImmediate and clearImmediate were removed (deprecated, not part of the web platform).
    • MCP and agent modes now load external stylesheets by default.
    • Max HTTP redirects raised from 10 to 20, matching the Fetch spec.
    • Unsupported protocols are no longer navigated to; javascript: URLs are run instead of navigated to.

    New Web APIs

    • Web Locks
    • Compression Streams API
    • RSA-OAEP in Web Crypto (generateKey, importKey, encrypt, decrypt)
    • Custom elements: ElementInternals and form-associated callbacks
    • Request.body and Request.textStream
    • HTMLFormElement.reset, form numeric and named index, form.field
    • HTMLTableElement.rows, HTMLSelectElement.remove(index), select.length setter
    • textarea.type, fieldset.type, output.type; HTMLMeterElement accessors
    • GlobalEventHandlers attributes on Window and Document; document named index
    • Performance secondary buffer; performance.timeOrigin relative to the Unix epoch
    • Console and CSS are now proper namespaces; more missing namespace methods

    Forms / input

    • Implicit submit on Enter; improved form submission; GET submissions replace the action's query
    • Fire change when typed text is committed by blur or Enter
    • Ctrl+A selects the whole value in text fields
    • All text edits go through one gated path; better contenteditable detection
    • Single-contact touch for CDP, testdriver and BiDi
    • One WebDriver key table shared by BiDi, testdriver and press
    • Click press/release sequences shared across input paths and matched to Chrome
    • Keydown default actions run only for trusted events
    • Range and color inputs get their default value; input.value reflects to the attribute in default modes
    • Skip inputs with an empty name; :checked applies to options; option text strips and collapses whitespace

    Web API behavior / fixes

    • fetch resolves after headers are received; an abort signal aborts a fetch
    • XHR response header compliance; improved data-URL parsing
    • Exceptions from timers, animation frames, on* handlers, observers and microtasks go to reportError
    • on* handler attributes behave like any other event listener
    • document.open resets loaded frame state; several document.close crash fixes
    • Fire popstate on a fragment navigation; history traverses within a document by entry
    • Each Window gets its own History; Location URLs resolve against the calling document
    • Dynamic <base> updates the document base URL
    • iframe reflects and reacts to src; sandbox token queries; iframe removal cleanup
    • replaceChildren adopts nodes from another document
    • Classic scripts are decoded with their charset
    • Timers accept any value; negative delays clamp to zero
    • IntersectionObserver only intersects connected targets
    • Fix custom element upgrade ordering during fragment parsing; undefined custom elements use the HTMLElement prototype

    CSS / selectors / rendering

    • Computed style reports initial values and a resolved font-size; padding, margin and border widths default to 0px
    • text-transform applied in innerText and resolved in getComputedStyle
    • revert and revert-layer treated like inherit
    • Decode fetched stylesheets; fix tokenizer exponent and hyphen-escape bugs; external stylesheet cap raised to 4 MiB
    • Hit testing honors pointer-events:none and visibility:hidden
    • Visibility inheritance matches Chromium in styles and the AX tree
    • Hide a [popover] that is not showing
    • Block-level flex and grid containers no longer grow with their items
    • Selectors: :valid/:invalid on every validity constraint, :read-write/:read-only per HTML, case-insensitive attribute values, type selectors on XML and foreign elements, pseudo-elements accepted

    CDP (Chrome DevTools Protocol)

    • Pause requests at the Fetch response stage
    • Start of the Debugger domain
    • Emit Runtime.exceptionThrown for uncaught page e
  4. 1.0.02026-10-02 · 2026-10-02 10:12 UTC
    查看更新说明

    Behavior changes

    • getComputedStyle results are now read-only.
    • The "en" language fallback was removed. Language now comes from --locale.
    • Redirects that switch to GET now drop body headers.

    WebDriver

    • Classic HTTP WebDriver, built out into a usable driver
    • Session management; navigate; back/forward; execute
    • Element endpoints; input (click, clear, sendKeys); cookie and viewport endpoints
    • Higher default/max HTTP limits

    CORS

    • CORS is now enforced by default. Cross-origin fetch/XHR requests that fail a CORS check are blocked. Turn it off with --disable-features cors. The old --experimental-features cors flag is gone.
    • CORS store with an entry limit (--cors-store-entry-limit, default 1000)
    • Fetch referrer policy + CorsGate referrer checking
    • Sec-Fetch-Site and Sec-Fetch-Mode request headers
    • Limit redirects that carry credentials

    New Web APIs

    • WebAssembly.compileStreaming and instantiateStreaming
    • Sanitizer API (initial)
    • TextEncoder.encodeInto
    • element.requestFullscreen (mostly a stub)
    • inert property; iframe sandbox setter
    • img srcset support; report natural dimensions for fetched images
    • new Option() and the legacy constructor API
    • DOMTokenList setters; more accurate window.isSecureContext
    • Experimental ServiceWorker (behind --experimental-features serviceworker) with Cache/CacheStorage

    Web API behavior / fixes

    • <template> content handling
    • Custom element callbacks may move the element during parse
    • request.clone() clones headers; correct fetch/response handling of null bodies
    • XHR/fetch report the status reason from the server; TypeError on XHR send(SharedArrayBuffer)
    • input.select() no longer throws for email/number; element.focus() only on focusable elements
    • Correct error on a wrong method; guard headers
    • Validate the XML declaration
    • Avoid spurious mutations for unchanged CSS declarations

    CSS / rendering

    • Split the style cascade into property groups; honor !important on tracked stylesheet properties
    • Resolve width/height from stylesheet rules; give the document a height and clamp viewport scroll to it
    • Measure wrapped text for an element's content height
    • Resolve scroll containers through the style cascade; unify wheel dispatch; clamp scroll offsets and latch a wheel to one container

    CDP (Chrome DevTools Protocol)

    • Optimize Network.getResponseBody
    • DOM.getFrameOwner returns the owning <iframe>; nodes carry a frameId
    • Answer inspector commands on the session that sent them; echo the sessionId in browser-level replies
    • Notify target closure when disposing browser contexts; report the initial about:blank as loaded
    • window.devicePixelRatio from the viewport scale; char text insertion
    • Move a kept isolated-world context onto the navigated origin

    HTTP / Networking

    • Improve caching; add x-frame-options support
    • Keep connections healthy: don't re-use connections likely in a bad state
    • Lowercase response headers; clamp the response status range and report the server's reason phrase
    • Add headers to the sync-request path
    • Accept Netscape cookie format in --cookie; report invalid same-site over CDP
    • Generate an appropriate error on a bot challenge

    Agent / tools

    • Add OpenRouter provider; lightpanda run - reads a script from stdin
    • Report the HTTP status of a navigation; wait for networkidle after an action-triggered navigation
    • findElement matches names by /regex/
    • MCP carries the navigation outcome as structuredContent
    • REPL output fixes and simpler command dispatch

    CLI / ops

    • Report usage errors once, accept -h, show tips only on a terminal; scale the "did you mean" distance with input length
    • Respect NO_COLOR and disable colors when stderr is not a tty
    • Per-page context logs; many warn/err logs downgraded to debug
    • Log the stalled page and JS stack on watchdog termination
  5. 0.4.12026-09-15 · 2026-09-15 13:09 UTC
    查看更新说明

    CORS (experimental)

    CORS is off by default. Enable it with --experimental-features cors.

    • CORS implementation: obey cross-origin checks on fetch/XHR instead of allowing them unconditionally
    • Origin header + request header validation
    • XHR changes for the CORS WPT tests

    IndexedDB

    • WPT conformance pass
    • setName on IDBObjectStore and IDBIndex
    • nextunique / prevunique cursor directions
    • Keep the upgradeneeded event alive past dispatch; better error handling
    • Fix use-after-free reading txn._db after releaseRef frees it

    New Web APIs

    • CountQueuingStrategy and ByteLengthQueuingStrategy
    • resource-timing; Performance is now an EventTarget
    • window.stop()
    • img.decode()
    • navigator.sendBeacon returns true
    • Headers.getSetCookie, customElements.getName, Element.part
    • HTMLInputElement valueAsNumber, valueAsDate, stepUp/stepDown, showPicker
    • SVGGraphicsElement.getCTM and getScreenCTM
    • canvas measureText and the drawing methods chart libraries probe
    • matchMedia change events and real addListener/removeListener
    • Reflect HTMLElement.contentEditable
    • Basic support for CSS custom properties

    Forms / input

    • Text entry: move the cursor on caret keys and to the end on value set; clamp selections against the current value
    • Trusted beforeinput is cancelable
    • A disabled fieldset disables its controls' activation and validation
    • option.selected = true deselects its siblings in a single select
    • Fix select value assignment with no matching option

    Custom elements

    • Upgrade autonomous custom-element clones in place
    • Don't upgrade custom elements in a windowless document
    • Skip null nodes when a create callback fails

    CSS / rendering / performance

    • Memoize StyleManager visibility per element
    • Append inserted rules to the buckets instead of rebuilding
    • Materialize inline styles where layout reads them
    • --strip-mode gains shell and clutter options
    • Faster nth-* selectors, class matching, attribute lookup, and pseudo-class parsing

    Ad blocker

    • Run /regex/ filters with PCRE2
    • Request engine

    Protocols

    • WebDriver: HTTP WebDriver session management
    • CDP: DOM.focus; honor runImmediately, screenWidth/screenHeight, browserContextId
    • CDP: request interception patterns; a context per isolated world per frame
    • CDP: wheel events scroll the viewport unless over a scroll container
    • CDP: fix message lifetimes and console argument serialization; remove a terminate/disconnect race

    HTTP / Networking

    • Keep connections alive across navigations; better redirect handling; timeout tweaks
    • Enable CURLOPT_PIPEWAIT on every easy handle
    • Update curl to 8.22.0
    • Cookies: accept Secure and __Host- on loopback origins; apply the SameSite=Lax exception only on safe methods; compute site-for-cookies from a frame's ancestor chain
    • Guard WebSocket message-length parsing against integer overflow
    • Fix drive-letter lookalike URL segments on non-file schemes

    CLI / tools

    • --locale and --timezone, with language signals derived from one value
    • Suggest the closest flag or command on a typo
    • note log level can be silenced; log CLI tips
    • Scroll the nearest scroll container from the scroll tool
    • Click dispatches a full pointer/mouse sequence, not a bare click event
    • Agent: route REPL output per tool and align page tables of any size

    Stability / memory

    • Crash fixes: TryCatch copied by value, history.state / Navigation.currentEntry, V8 re-entry on an unhandled promise
    • Fix Notification use-after-free
    • Disconnect runaway intersection observers; raise timer caps
    • Leak fixes: robot store, JS terminated during MutationObserver/IntersectionObserver delivery, cloned HTTP responses, V8 backing store
    • V8: notify isolate shutdown, restore the initial heap limit, avoid rearming heap termination during teardown
    • Disarm the wat

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    License Twitter Follow GitHub stars Discord

     

    Benchmarks

    Requesting 933 real web pages over the network on a AWS EC2 m5.large instance. See benchmark details.

    MetricLightpandaHeadless ChromeDifference
    Memory (peak, 100 pages)123MB2GB~16x less
    Execution time (100 pages)5s46s~9x faster

    Quick start

    Install

    Package Managers

    Latest nightly from Homebrew:

    brew install lightpanda-io/browser/lightpanda
    

    Latest nightly from Arch Linux User Repository:

    yay -S lightpanda-nightly-bin
    

    Download from the nightly builds

    You can download the last binary from the nightly builds for Linux and MacOS for both x86_64 and aarch64.

    For Linux

    curl -L -o lightpanda https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-linux && \
    chmod a+x ./lightpanda
    

    Verify the binary before running anything:

    ./lightpanda version
    

    Linux aarch64 is also available

    If you use Nix, you can run Lightpanda directly from the flake without downloading a binary:

    nix run github:lightpanda-io/browser -- --help
    

    Or pin it as an input in your own flake:

    {
      inputs.lightpanda.url = "github:lightpanda-io/browser";
    }
    

    Note: The Linux release binaries are linked against glibc. On musl-based Linux distributions (Alpine, etc.) the binary fails with cannot execute: required file not found because the glibc dynamic linker is missing. Use a glibc-based base image: the current release binaries need glibc 2.35 (x86_64) or 2.38 (arm64), so debian:bookworm-slim (2.36) is fine on x86_64 but too old for arm64 — use FROM ubuntu:24.04, FROM debian:trixie-slim, or FROM debian:forky-slim there — or build from sources.

    Android / Termux: there is no native Android build. The Linux aarch64 binary needs the glibc loader (/lib/ld-linux-aarch64.so.1), which Android's Bionic libc does not provide, so it fails with the same cannot execute: required file not found error.

    For MacOS

    curl -L -o lightpanda https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-aarch64-macos && \
    chmod a+x ./lightpanda
    

    MacOS x86_64 is also available

    For Windows + WSL2

    Lightpanda has no native Windows binary. Install it inside WSL following the Linux steps above.

    WSL not installed? Run wsl --install from an administrator shell, restart, then open wsl. See Microsoft's WSL install guide for details.

    Your automation client (Puppeteer, Playwright, etc.) can run either inside WSL or on the Windows host. WSL forwards localhost:9222 automatically.

    Install from Docker

    Lightpanda provides official Docker images for both Linux amd64 and arm64 architectures. The following command fetches the Docker image and starts a new container exposing Lightpanda's CDP server on port 9222.

    docker run -d --name lightpanda -p 127.0.0.1:9222:9222 lightpanda/browser:latest
    

    Dump a URL

    ./lightpanda fetch --obey-robots --dump html --log-format pretty  --log-level info https://demo-browser.lightpanda.io/campfire-commerce/
    

    You can use --dump markdown to convert directly into markdown, or --dump png > page.png or --dump pdf > page.pdf for a text-only rendering of the page. --dump structured_data prints the page's JSON-LD, OpenGraph, Twitter card, meta and link tags as JSON, like the MCP structuredData tool. --wait-until, --wait-ms, --wait-selector and --wait-script are available to adjust waiting time before dump.

    Start a CDP server

    ./lightpanda serve --obey-robots --log-format pretty  --log-level info --host 127.0.0.1 --port 9222
    

    To listen on a Unix-domain socket (owner-only, mode 0600) instead of TCP:

    ./lightpanda serve --unix-socket /run/user/1000/lightpanda.sock
    

    Once the CDP server started, you can run a Puppeteer script by configuring the browserWSEndpoint.

    import puppeteer from 'puppeteer-core';
    
    // use browserWSEndpoint to pass the Lightpanda's CDP server address.
    const browser = await puppeteer.connect({
      browserWSEndpoint: "ws://127.0.0.1:9222",
    });
    
    // The rest of your script remains the same.
    const context = await browser.createBrowserContext();
    const frame = await context.newPage();
    
    // Dump all the links from the frame.
    await frame.goto('https://demo-browser.lightpanda.io/amiibo/', {waitUntil: "networkidle0"});
    
    const links = await frame.evaluate(() => {
      return Array.from(document.querySelectorAll('a')).map(row => {
        return row.getAttribute('href');
      });
    });
    
    console.log(links);
    
    await frame.close();
    await context.close();
    await browser.disconnect();
    
    Start a webdriver Bidi server

    Use --protocol webdriver to enable Bidi support. You can start both, CDP and Bidi, with --protocol webdriver --protocol cdp

    ./lightpanda serve --obey-robots --log-format pretty  --log-level info --host 127.0.0.1 --port 9222 --protocol webdriver
    

    Agent mode

    lightpanda agent lets you drive the browser with a native agent. Describe what you want in plain English or with slash commands, and it controls the browser: navigating pages, clicking through flows, filling forms, extracting structured data. Think of it as a robot you're directing to use the web, more than a chatbot you're having a conversation with.

    Because the agent runs inside the same process as the browser, every tool call is a direct operation and you retain Lightpanda's speed and memory advantage.

    The output of an agent session is a PandaScript: vanilla JavaScript with a small set of native browser primitives built directly into Lightpanda. Run /save to export one from your current session, then replay it with lightpanda run <script>.js. Scripts are deterministic and token-free, so you can prototype with the LLM and ship the output to production without a model at runtime.

    It supports Anthropic, OpenAI, Gemini, Google Vertex AI, Mistral, Hugging Face, the multi-lab gateways Vercel AI Gateway and OpenRouter (one key for hundreds of models), any OpenAI-compatible endpoint via OPENAI_BASE_URL, local models via Ollama, llama.cpp or LLMD, and OpenAI Codex with a ChatGPT subscription instead of an API key (run /provider codex in the REPL to log in). You can also run without an LLM using --no-llm, which drops you into the REPL. See the agent documentation for the full reference.

    ./lightpanda agent                                    # auto-detects API key from env
    ./lightpanda agent --task "top story on news.ycombinator.com?"
    ./lightpanda agent --no-llm                           # basic REPL, no LLM
    ./lightpanda run session.js                           # run a recorded script
    cat session.js | ./lightpanda run -                   # ...or pipe one in via stdin
    ./lightpanda agent --provider gemini --task "..."     # force a specific provider
    ./lightpanda agent --list-models                      # models available for the detected provider
    VERTEX_API_KEY=... ./lightpanda agent --provider vertex             # Vertex AI, express mode
    GOOGLE_CLOUD_PROJECT=my-proj ./lightpanda agent --provider vertex   # Vertex AI, token via gcloud auth
    AI_GATEWAY_API_KEY=... ./lightpanda agent --provider vercel --model moonshotai/kimi-k2   # any model behind Vercel AI Gateway
    OPENROUTER_API_KEY=... ./lightpanda agent --provider openrouter --model anthropic/claude-sonnet-5   # any model behind OpenRouter
    OPENAI_BASE_URL=https://my-gateway/v1 OPENAI_API_KEY=... ./lightpanda agent            # any OpenAI-compatible server
    

    Native MCP and skill

    The MCP server communicates via MCP JSON-RPC 2.0 over stdio.

    Add to your MCP configuration:

    {
      "mcpServers": {
        "lightpanda": {
          "command": "/path/to/lightpanda",
          "args": ["mcp"]
        }
      }
    }
    
    HTTP transport and independent sessions

    For serving several agents from one process, start the MCP server over HTTP instead of stdio by giving it a port (add --host x.x.x.x to specify the interface to listen on):

    lightpanda mcp --port 9223
    

    Clients POST JSON-RPC to http://host:9223/mcp. Each connection is routed to its own browsing session — its own page, cookies and memory — so agents no longer clobber each other's page:

    • A client that initializes without an Mcp-Session-Id header is assigned a fresh session; the id comes back in the response's Mcp-Session-Id header. Send it on subsequent requests to stay on that session (isolation).
    • Two agents that send the same Mcp-Session-Id share one browsing context (sharing — e.g. a workflow where several agents work the same page).
    • The session_new, session_list and session_close tools manage sessions explicitly. Sending DELETE /mcp with an Mcp-Session-Id closes that session.

    The run tool runs a PandaScript in the caller's session, as lightpanda run does, and returns its console output and the value it returns. It takes the source as script, or the path of a file that save wrote. Pages the script opens are closed when it ends; cookies and storage stay, so a login carries over to the next run. An optional timeout in milliseconds (default 60000) stops a stuck script. A backend can drive Lightpanda with one POST:

    curl -s http://127.0.0.1:9223/mcp -H 'Content-Type: application/json' -d '{
      "jsonrpc": "2.0", "id": 1, "method": "tools/call",
      "params": { "name": "run", "arguments": { "script":
        "const page = new Page();\nawait page.goto(\"https://example.com\");\nreturn page.extract({ title: \"title\", links: [\"a\"] });" } }
    }'
    

    Read full documentation

    A skill is available in lightpanda-io/agent-skill.

    Shell completion

    lightpanda completion <bash | fish | zsh> prints a completion script for commands, flags and their values:

    lightpanda completion fish > ~/.config/fish/completions/lightpanda.fish
    lightpanda completion bash > ~/.local/share/bash-completion/completions/lightpanda
    lightpanda completion zsh > ~/.zfunc/_lightpanda  # with fpath=(~/.zfunc $fpath) before compinit
    

    Telemetry

    By default, Lightpanda collects and sends usage telemetry. This can be disabled by setting an environment variable LIGHTPANDA_DISABLE_TELEMETRY=true. You can read Lightpanda's privacy policy at: https://lightpanda.io/privacy-policy.

    Core dumps

    Set LIGHTPANDA_DISABLE_CORE_DUMP (to any value) to suppress crash core dumps by zeroing the soft RLIMIT_CORE at startup.

    Status

    Here are the key features we have implemented. For full details, see our Web Platform Tests results.

    • CORS
    • HTTP loader (Libcurl)
    • HTML parser (html5ever)
    • DOM tree
    • Javascript support (v8)
    • DOM APIs
    • Ajax
      • XHR API
      • Fetch API
    • DOM and Markdown dump
    • CDP/websockets server
    • Click
    • Input form
    • Cookies
    • Custom HTTP headers
    • Proxy support
    • Network interception
    • Respect robots.txt with option --obey-robots
    • CDP and Webdriver Bidi
    • Adblocker

    Build from sources

    Prerequisites

    Lightpanda is written with Zig 0.17.0. You have to install it with the right version in order to build the project.

    Lightpanda also depends on v8, Libcurl and html5ever.

    For Debian/Ubuntu based Linux:

    sudo apt install xz-utils ca-certificates \
        pkg-config libglib2.0-dev \
        clang make curl git
    

    pkg-config and libglib2.0-dev are only needed to build V8 from source (see below). You also need to install Rust.

    For systems with Nix, you can use the devShell.

    nix develop
    

    For MacOS, you need cmake and Rust.

    brew install cmake
    

    Build and run

    Run make download-v8 once first to fetch the prebuilt V8 archive; both make and zig build pick it up. Without it, or on a platform with no published archive (the download then fails), the build compiles V8 from source, which takes several minutes. See CONTRIBUTING.md for details.

    Then build the entire browser with make build or make build-dev for debug env.

    But you can directly use the zig command: zig build run.

    Embed v8 snapshot

    Lightpanda uses a v8 snapshot. make build builds and embeds it. With a plain zig build, it is created on startup instead, unless you embed it by using the following commands:

    Generate the snapshot.

    zig build snapshot_creator -- src/snapshot.bin
    

    Build using the snapshot binary.

    zig build -Dsnapshot_path=../../snapshot.bin
    

    See #1279 for more details.

    Test

    Unit Tests

    You can test Lightpanda by running make test.

    make test                                       # Run all tests
    make test F="server"                            # Filter by substring
    TEST_FILTER="WebApi: #selector_all" make test   # Filter main + subtest (separator: #)
    TEST_VERBOSE=true make test
    TEST_FAIL_FIRST=true make test
    METRICS=true make test                          # Capture allocation/duration metrics as JSON
    TEST_JOBS=1 make test                           # Run in one process (default: up to 4, or 1 when filtered)
    

    End to end tests

    To run end to end tests, you need to clone the demo repository into ../demo dir.

    You have to install the demo's node requirements

    You also need to install Go > v1.24.

    make end2end
    

    Web Platform Tests

    Lightpanda is tested against the standardized Web Platform Tests.

    We use a fork including a custom testharnessreport.js. Results are published daily.

    For reference, you can execute a WPT test case with your browser via wpt.live.

    Configure WPT HTTP server

    To run the test, you must clone the repository, configure the custom hosts and generate the MANIFEST.json file.

    Clone the repository with the fork branch.

    git clone -b fork --depth=1 [email protected]:lightpanda-io/wpt.git
    

    Enter into the wpt/ dir.

    Install custom domains in your /etc/hosts

    ./wpt make-hosts-file | sudo tee -a /etc/hosts
    

    Generate MANIFEST.json

    ./wpt manifest
    

    Use the WPT's setup guide for details.

    Run WPT test suite

    An external Go runner is provided by github.com/lightpanda-io/demo/ repository, located into wptrunner/ dir. You need to clone the project first.

    First start the WPT's HTTP server from your wpt/ clone dir.

    ./wpt serve
    

    Run a Lightpanda browser

    zig build -Dwpt_extensions run -- serve \
        --ws-max-concurrent 64 \
        --insecure-disable-tls-host-verification \
        --load-resources iframe \
        --load-resources image \
        --load-resources worker \
        --load-resources stylesheet
    

    Then you can start the wptrunner from the demo's clone dir:

    cd wptrunner && go run .
    

    Or one specific test:

    cd wptrunner && go run . Node-childNodes.html
    

    wptrunner command accepts --summary and --json options modifying output. Also --concurrency define the concurrency limit.

    To check a single test case without the runner, fetch --dump wpt prints its results as JSON: the harness status, a pass/fail summary and the cases that did not pass. It relies on the fork's testharnessreport.js.

    zig build -Dwpt_extensions run -- fetch \
        --insecure-disable-tls-host-verification \
        --load-resources worker \
        --load-resources iframe \
        --dump wpt \
        --wait-script "window.report && report.complete" \
        "http://web-platform.test:8000/dom/nodes/Node-childNodes.html"
    

    :warning: Running the whole test suite will take a long time. In this case, it's useful to build in releaseFast mode to make tests faster.

    zig build -Dwpt_extensions -Doptimize=fast run -- serve ...
    

    Contributing

    See CONTRIBUTING.md for guidelines. You must sign our CLA during the pull request process.

    Why Lightpanda?

    Javascript execution is mandatory for the modern web

    Simple HTTP requests used to be enough for web automation. That's no longer the case. Javascript now drives most of the web:

    • Ajax, Single Page Apps, infinite loading, instant search
    • JS frameworks: React, Vue, Angular, and others

    Chrome is not the right tool

    Running a full desktop browser on a server works, but it does not scale well. Chrome at hundreds or thousands of instances is expensive:

    • Heavy on RAM and CPU
    • Hard to package, deploy, and maintain at scale
    • Many features are not necessary in headless made

    Lightpanda is built for performance

    Supporting Javascript with real performance meant building from scratch rather than forking Chromium:

    • Not based on Chromium, Blink, or WebKit
    • Written in Zig, a low-level language with explicit memory control
    • No graphical rendering engine