O O
\ /
O —— Cr —— O
/ \
O O
Chromium-based browser that runs in a terminal — 60 FPS, 0% idle CPU, SSH-friendly
pip install carbonyl-agent && carbonyl-agent installGet Started · Fork Status · Build from Source · Comparisons · Blog
Carbonyl is a Chromium-based browser that renders into terminal text. It supports pretty much all Web APIs — WebGL, WebGPU, audio and video playback, animations — and starts in less than a second, runs at 60 FPS, and idles at 0% CPU. It does not require a window server (works in a safe-mode console) and runs comfortably over SSH.
This repository (jmagly/carbonyl) is the maintained fork of the original fathyb/carbonyl, which has been inactive since early 2023. It tracks upstream Chromium stable (currently M147) and publishes runtime tarballs as release assets.
Most users want carbonyl-agent — the Python SDK that drives Carbonyl for scripted browsing, scraping, and agent-based testing. It handles binary discovery, session persistence, daemon reconnection, and bot-detection evasion out of the box.
pip install carbonyl-agent
carbonyl-agent install # downloads the verified runtime binaryfrom carbonyl_agent import CarbonylBrowser
b = CarbonylBrowser()
b.open("https://example.com")
b.drain(8.0)
print(b.page_text())
b.close()For multi-instance orchestration (N concurrent browsers over PTY + Unix socket, with gRPC + REST), see carbonyl-fleet.
This repo (jmagly/carbonyl) is the Chromium fork and the source of the runtime tarballs. Most users do not need to build it.
pip install carbonyl-agent
carbonyl-agent installThe installer downloads a verified-by-SHA256 runtime tarball from the public GitHub release page, with documented manual/offline fallbacks. No compilation required.
Maintained linux/amd64 images are published to
ghcr.io/jmagly/carbonyl
on every v* release, built from the verified runtime tarballs (no compile).
docker run --rm -ti ghcr.io/jmagly/carbonyl https://example.com| Tag | Contents |
|---|---|
ghcr.io/jmagly/carbonyl:latest |
headless runtime, newest release |
ghcr.io/jmagly/carbonyl:<version> |
headless, pinned (e.g. 0.2.0-alpha.9) |
ghcr.io/jmagly/carbonyl:<version>-x11 |
x11 ozone, pinned (automation / trusted input) |
The image runs non-root under tini via build/docker-entrypoint.sh
with container-safe defaults (--no-sandbox, --disable-dev-shm-usage,
--disable-gpu). Arguments after the image name are forwarded to the carbonyl
CLI. Current runtimes apply an internal 1.5× zoom, so the entrypoint defaults to
--zoom=67 (≈100%); override with -e CARBONYL_ZOOM=50 or --zoom=50 after the
image name.
# Interactive terminal browsing (needs a TTY)
docker run --rm -ti ghcr.io/jmagly/carbonyl:latest https://example.com
# Version / help (no URL)
docker run --rm ghcr.io/jmagly/carbonyl:latest --version
# Persist cookies / profile
docker volume create carbonyl-profile
docker run --rm -ti -v carbonyl-profile:/home/carbonyl \
ghcr.io/jmagly/carbonyl:latest https://app.example.com
# x11 variant (needs a host/sidecar X server + uinput)
docker run --rm -ti -e DISPLAY=:99 --device=/dev/uinput --group-add input \
ghcr.io/jmagly/carbonyl:<version>-x11 --ozone-platform=x11 https://example.com| Flag | Why |
|---|---|
--rm |
Remove the container on exit |
-ti |
Interactive terminal browsing (omit for --version/non-TTY use) |
-v name:/home/carbonyl |
Persist profile / cookies |
-e CARBONYL_ZOOM=N |
Override zoom (lower = zoom out) |
-e DISPLAY=:99 --device=/dev/uinput |
x11 ozone / trusted input |
# Upstream image (M111-era, dated but works)
docker run --rm -ti fathyb/carbonyl https://youtube.com
# npm (upstream package — M111-era)
npm install --global carbonyl && carbonyl https://github.com
# Pre-built runtime tarball from release assets (M147, current)
# https://github.com/jmagly/carbonyl/releasesReleases also ship native install packages — .deb / .rpm / .AppImage
(Linux x86_64) and a .pkg / .dmg (macOS arm64, not Apple-notarized):
sudo apt install ./carbonyl_<version>_amd64.deb # Debian / Ubuntu
sudo dnf install ./carbonyl-<version>-1.x86_64.rpm # Fedora / RHEL
./carbonyl-<version>-x86_64.AppImage https://github.com # any Linux, no installFull instructions (incl. the macOS Gatekeeper step for the unsigned installer): docs/install.md.
Every release asset ships a SHA-256 + MD5 checksum and a detached GPG signature
(<asset>.asc). The checksums prove the download is intact; the signature proves it
genuinely came from us. Import the Carbonyl release key once, then verify:
curl -fsSL https://magly.net/keys/carbonyl-release.asc | gpg --import
gpg --verify carbonyl-<version>-<asset>.asc carbonyl-<version>-<asset>
# → Good signature from "Carbonyl Release Signing <release@magly.net>"Key fingerprint 96B5 DCE9 275E 218C BAB9 CB28 2DE7 DD0D 3A89 07C0 — published at
https://magly.net/keys/carbonyl-release.asc. Full details, the key block, and rotation
policy: docs/SIGNING.md.
One binary, three deployment shapes:
| Mode | Invocation | When to use |
|---|---|---|
| Terminal-only | carbonyl <url> |
Read in a terminal; smallest surface |
| x11 + trusted input | DISPLAY=:99 carbonyl --ozone-platform=x11 <url> |
Automation against bot-detecting sites (kernel uinput → Xorg → isTrusted=true events) |
| x11 + visual capture | CARBONYL_X_MIRROR=1 DISPLAY=:99 carbonyl --ozone-platform=x11 <url> |
Same, plus blits compositor frames into an X window so scrot/ffmpeg/x11vnc can capture alongside the terminal render |
Full operator reference, session-portability rules, and CLI/env-var matrix: docs/runtime-modes.md.
Carbonyl forwards any flag it does not recognize straight to the underlying Chromium, so you can set the proxy, user-agent, language, and most other Chromium command-line switches alongside Carbonyl's own options:
# Route traffic through a SOCKS proxy
carbonyl --proxy-server=socks5://127.0.0.1:9050 https://example.com
# Override the user-agent string
carbonyl --user-agent="MyAgent/1.0" https://example.com
# Set the UI / Accept-Language locale, with a Carbonyl flag in the same line
carbonyl --lang=fr-FR --zoom=80 https://example.comCarbonyl-specific flags (--fps, --zoom, --viewport, …) are consumed by
Carbonyl and still passed to Chromium, which ignores switches it doesn't know.
Chromium flags Carbonyl doesn't recognize are passed through untouched — see
carbonyl --help and Chromium's flag list.
The original repository (fathyb/carbonyl) has been inactive since early 2023. This fork is actively maintained for use in headless browser automation and agentic pipelines.
- Chromium M147 (147.0.7727.94) — current upstream stable. Upgraded from M111 across six phases (M111 → M120 → M132 → M135 → M140 → M147). 24 patches applied. Runtime tarballs published as release assets.
- Python automation layer (
carbonyl-agent) — extracted into a standalone installable package.CarbonylBrowserclass with persistent sessions, daemon reconnect, mouse movement, click-by-text, and screen extraction. - Fleet server (
carbonyl-fleet) — Rust server for N concurrent browsers with gRPC + REST + Python SDK. - Bot-detection mitigations — Firefox UA spoof,
--disable-http2,AutomationControlledsuppressed, organic mouse movement API. - Session management — named persistent profiles, fork/snapshot,
SessionManagerCLI. - CI infrastructure — automated workflows for fast checks and full Chromium runtime builds, pinned to dedicated build hosts.
--carbonyl-b64-text restored in M135: the experimental text-capture mode was temporarily disabled during the initial M135 ship and has been re-enabled via a structural refactor (Path A, issue #28). Both bitmap rendering (default) and b64 text capture are functional on M147.
Maintenance commitment: Security-relevant Chromium versions are tracked on a best-effort basis. The automation API is under active development. Issues and PRs welcome.
| Repo | Purpose | Build tech |
|---|---|---|
carbonyl |
Chromium fork + runtime tarballs (this repo) | Chromium, GN, ninja, Rust |
carbonyl-agent |
Python automation SDK (single-instance) | Python 3.11+, pyte, pexpect |
carbonyl-fleet |
Fleet server (N concurrent browsers, gRPC + REST) | Rust, tonic, axum |
- Fullscreen mode not supported yet
Lynx is the original terminal web browser, and the oldest one still maintained.
Pros
- When it understands a page, Lynx has the best layout, fully optimized for the terminal
Cons (some might sound like pluses, but Browsh and Carbonyl let you disable most of those if you'd like)
- Does not support a lot of modern web standards
- Cannot run JavaScript/WebAssembly
- Cannot view or play media (audio, video, DOOM)
Browsh is the original "normal browser in a terminal" project. It starts Firefox in headless mode and connects to it through an automation protocol.
Pros
- Easier to update the underlying browser: just update Firefox
- As of today, Browsh supports extensions while Carbonyl doesn't (on our roadmap)
Cons
- Runs slower and requires more resources than Carbonyl. 50× more CPU for the same content on average, because Carbonyl does not downscale or copy the window framebuffer — it natively renders to the terminal resolution.
- Uses custom stylesheets to fix the layout, which is less reliable than Carbonyl's changes to its HTML engine (Blink).
| OS | Status |
|---|---|
| Linux (Debian, Ubuntu, Arch) | ✅ Tested |
| macOS | ✅ Tested (upstream; M147 fork not yet rebuilt on macOS) |
| Windows 11 / WSL | 🟡 Reported working (upstream) |
Wikipedia.mp4 |
Doom.mp4 |
YouTube.mp4 |
|
You almost certainly do not need to do this — use
carbonyl-agent installto pull a pre-built runtime.
Carbonyl is split in two parts:
- Core (
libcarbonyl.so) — written in Rust, builds in seconds viacargo - Runtime (
headless_shell) — a modified Chromium build with 24 patches, requires the full Chromium toolchain
If you're just changing the Rust code, build libcarbonyl and drop it into a release version of Carbonyl. You do not need to rebuild Chromium.
cargo build
⚠️ Building Chromium takes considerable wall time, disk space, and memory. Expect ~100 GB of disk and a heavy compile workload.
Notes:
- Building the runtime is essentially the Chromium build flow with extra steps to patch and bundle the Rust library.
- Scripts in
scripts/are thin wrappers aroundgn,ninja, etc. - Cross-compiling Chromium for arm64 on Linux requires an amd64 processor.
- Tested on Linux and macOS.
./scripts/gclient.sh syncAny existing changes in
chromium/src/will be stashed. Save your work first.
./scripts/patches.sh apply./scripts/gn.sh args out/DefaultWhen prompted, enter:
import("//carbonyl/src/browser/args.gn")
# uncomment to build for arm64
# target_cpu = "arm64"
# comment to disable ccache
cc_wrapper = "env CCACHE_SLOPPINESS=time_macros ccache"
# comment for a debug build
is_debug = false
symbol_level = 0
is_official_build = true./scripts/build.sh DefaultProduces:
out/Default/headless_shell— browser binaryout/Default/icudtl.datout/Default/libEGL.soout/Default/libGLESv2.soout/Default/v8_context_snapshot.bin
./scripts/docker-build.sh Default arm64
./scripts/docker-build.sh Default amd64./scripts/run.sh Default https://wikipedia.orgSee maintenance.md for detailed upgrade procedures, patch rebasing guidance, and the rebase SOP used to move M111 → M147.
- maintenance.md — upgrade procedure, patch reference commits, GN args notes
- changelog.md — full rebase history (M111 → M147)
- AIWG.md — AIWG framework integration
- docs/architecture.md — cross-layer architecture notes
- docs/rust-chromium-boundary.md — Rust/C++ layer map, FFI boundary overview, rebuild recipes by change type, verification patterns
- docs/chromium-integration.md — catalog of every Carbonyl modification to Chromium (patches, injected sources, FFI, build flags)
- chromium/patches/chromium/ — the 24 tracked patches
PRs and issues welcome at github.com/jmagly/carbonyl.
Most meaningful changes to the Python automation path belong in carbonyl-agent. This repo is the Chromium side — patches, build scripts, runtime infrastructure.
- Issues: github.com/jmagly/carbonyl/issues
- GitHub Discussions: github.com/jmagly/carbonyl/discussions
BSD-3-Clause License — see LICENSE.
Carbonyl includes Chromium, which is BSD-licensed. See chromium/src/LICENSE after a checkout for upstream terms.
|
The Temporal Layer for Web3 Enterprise-grade timing infrastructure for blockchain applications. |
No-Code Smart Contracts for Everyone Making blockchain-based agreements accessible to all. |
AI-Powered Automation Solutions Custom AI and blockchain solutions for the digital age. |
Interested in sponsoring? Open a GitHub Discussion.
Built on top of Carbonyl by Fathy Boundjadj, which in turn sits on Chromium and Skia. The M111→M147 rebase path was informed by CEF's blink_glue.cc pattern for the Path A structural fix. Thanks to the Chromium cppgc / Oilpan maintainers for the underlying template machinery (see issue #27).
