ricibrowser
A lightweight two-engine browser automation module built entirely on the Chrome DevTools Protocol (CDP). No Playwright, no Puppeteer, no selenium.
Engines
| Engine | Use case | Technology |
|---|---|---|
| Lightpanda (fast path) | crawl, recon, endpoint discovery, non-JS-heavy targets | Zig-based headless engine, CDP at ws://127.0.0.1:9222 |
| CDP-Chrome (thorough path) | DAST, JS-heavy targets, auth flows, anti-bot | Custom CDP client driving the user's real installed Chrome |
Install
pip install ricibrowser
# For the fast path (optional):
bash scripts/install_lightpanda.sh
lightpanda serve --host 127.0.0.1 --port 9222
# For the thorough path:
# Just have Google Chrome installed on your system.
Quick start
Hybrid page snapshots
CDP-Chrome sessions can expose a bounded accessibility/DOM snapshot with stable references for the current page. Refresh the snapshot after navigation or DOM changes; references from an older snapshot are rejected.
snapshot = await session.accessibility_snapshot(interactive_only=True)
# Use snapshot["nodes"][0]["ref"] with session.act_reference(...)
The snapshot combines the CDP accessibility tree with DOM/ARIA enrichment, including roles, accessible names, disabled/checked state, and selectors where available.
JavaScript exception surfacing
Runtime.evaluate responses now carry CDP exceptionDetails through
session.last_eval_error — a thrown expression returns None with the
actual JS exception text instead of an unexplained null, so callers can
distinguish "expression threw" from "returned undefined".
Trusted clicks and promise-aware evaluation
Session.click now dispatches a trusted CDP Input.dispatchMouseEvent at
the element's center — React/Vue router buttons that ignore synthetic
el.click() dispatches respond to these. A synthetic-click fallback remains
for hidden or zero-size elements.
evaluate/evaluate_value now set awaitPromise, so expressions like
fetch('/api').then(r => r.text()) resolve to the final body instead of an
opaque {} — no store-then-read workaround needed.
Stealth diagnostics
ricibrowser.stealth_benchmark provides a defensive, local consistency
benchmark for an operator-owned fixture page. It checks observable signals such
as navigator.webdriver, user-agent/client-hint consistency, locale/timezone,
WebGL, canvas/audio stability, plugins, CDP artifacts, and TLS consistency when
the fixture supplies them. The score is a debugging heuristic, not a promise of
invisibility or a vendor bot-detector result. It does not probe third-party
anti-bot systems or attempt to evade them.
import asyncio
from ricibrowser import Engine, EngineConfig
async def main():
engine = Engine(EngineConfig())
# Fast path (Lightpanda) — crawl/recon
page = await engine.fast_browse("https://example.com")
print(f"Title: {page.title}")
print(f"Text: {page.text[:200]}")
print(f"Links: {len(page.links)}")
# Thorough path (CDP-Chrome) — DAST/auth flows
session = await engine.create_session()
await session.navigate("https://example.com/login")
await session.fill("#username", "admin")
await session.fill("#password", "pass")
await session.click("#login-btn")
# Cookies persist across sessions via CookieJar
await session.navigate("https://example.com/dashboard") # authenticated!
# JS evaluation in isolated world (never Runtime.enable on main world)
count = await session.evaluate("document.querySelectorAll('script').length")
# Network capture (opt-in, off by default)
engine2 = Engine(EngineConfig(debug_network=True))
session2 = await engine2.create_session()
# ... browse ...
flows = engine2.network.to_dict()
await engine.close()
asyncio.run(main())
Stealth
navigator.webdriversuppressed via--disable-blink-features=AutomationControlled(Blink-level, not JS injection)- Uses the user's real installed Chrome (not bundled Chromium) — TLS/JA3 fingerprint matches a real Chrome release
- Never calls
Runtime.enableon the main world — isolated worlds only Console.enableoff by default — only enabled in explicit debug modeNetwork.enableoff by default — known CDP detection vector
Architecture
See ARCHITECTURE.md for the full design.
License
MIT
Release files for ricibrowser 0.2.24
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ricibrowser-0.2.24.tar.gz | 79.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ricibrowser-0.2.24-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 149.3 kB
Release files / ricibrowser-0.2.24.tar.gz
| Download URL | ricibrowser-0.2.24.tar.gz |
|---|---|
| Size | 79.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
565ccef0cb27c75ae1e98b1c18d32ba3d55bfe6e8be654537738e7246321db73
|
|
BLAKE2b-256 checksum How to use checksums |
723db077291b34b341df0951765a5dfac0f2f72088fabad6c958b5cbc6209ff6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ricibrowser-0.2.24-py3-none-any.whl
| Download URL | ricibrowser-0.2.24-py3-none-any.whl |
|---|---|
| Size | 69.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6b2dc8feacd9a4412de131eab1ccc4d46f80d68d5b942b09e32dea2afe7d721a
|
|
BLAKE2b-256 checksum How to use checksums |
1b51886d4f9d2a471650bb2d00c906d58aba973d70aa34636e8d9055a94c4092
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|