Skip to content
HN On Hacker News ↗

GitHub - justrach/kuri: Browser automation and web crawling for AI agents. Zig-native, token-efficient CDP snapshots, HAR recording, and a standalone fetcher.

▲ 57 points 7 comments by sorcercode 4w ago HN discussion ↗

Pangram verdict · v3.3

We believe that this document is a mix of AI-generated, and human-written content

58 %

AI likelihood · overall

Mixed
46% human-written 54% AI-generated
SEGMENTS · HUMAN 0 of 7
SEGMENTS · AI 4 of 7
WORD COUNT 1,475
PEAK AI % 100% · §1
Analyzed
Apr 22
backend: pangram/v3.3
Segments scanned
7 windows
avg 211 words each
Distribution
46 / 54%
human / AI fraction
Verdict
Mixed
Pangram v3.3

Article text · 1,475 words · 7 segments analyzed

Human AI-generated
§1 AI · 100%

Browser automation & web crawling for AI agents. Written in Zig. Zero Node.js. CDP automation · A11y snapshots · HAR recording · Standalone fetcher · Interactive terminal browser · Agentic CLI · Security testing Quick Start · Benchmarks · kuri-agent · Security Testing · API · Changelog

Why teams switch to Kuri: 464 KB binary, ~3 ms cold start. On Google Flights, a full agent loop (go→snap→click→snap→eval) costs 4,110 tokens vs 4,880 for agent-browser — 16% less per cycle, compounding across multi-step tasks.

Why Kuri Wins for Agents Most browser tooling was built for QA engineers. Kuri is built for agent loops: read the page, keep token cost low, act on stable refs, and move on.

The product story is not "most commands." It is "useful state from real pages at the lowest model cost." A tiny output only counts if the page actually rendered. Empty-shell output is a failure mode, not a win. The best proof is same-page, same-session, same-tokenizer comparisons.

Snapshot tokens: Google Flights SIN → TPE Same Chrome session, measured with tiktoken cl100k_base. Run ./bench/token_benchmark.sh to reproduce.

Tool / Mode Bytes Tokens vs kuri Note

kuri snap (compact) 13,479 4,328 baseline

kuri snap --interactive 7,024 1,927 0.4x Best for agent loops

kuri snap --json 102,124 31,280 7.2x Old default

agent-browser snapshot 17,103 4,641 1.1x

agent-browser snapshot -i 8,704 2,425 0.6x

lightpanda semantic_tree

§2 Mixed · 57%

67,830 26,244 6.1x ⚠ no JS — raw DOM

lightpanda semantic_tree_text 1,909 507 0.1x ⚠ no JS — empty shell

Full workflow cost: go → snap → click → snap → eval

Tool Tokens per cycle

kuri-agent 4,110

agent-browser 4,880

kuri saves 16% tokens per workflow cycle — compounding across multi-step tasks. Action responses are flat JSON ({"ok":true}) instead of nested CDP, which adds up: click = 9 tokens, back = 5 tokens, scroll = 5 tokens.

Why lightpanda scores low: Lightpanda can't execute JS-heavy SPAs. Google Flights renders via client-side fetch() — lightpanda returns a 507-token empty nav shell with zero flight data. The low token count is a failed render, not efficiency.

Small binary, fast start Measured on Apple M3 Pro, macOS 15.3. kuri built with -Doptimize=ReleaseFast. agent-browser v0.20.0. agent-browser kuri delta (v0.20) (v0.2) ───────────────────────────────────────────────────────────────────── CLI binary 6.0 MB 464 KB 13× smaller Cold start (--version) 3.4 ms 3.0 ms ~same Install (npm) 33 MB 3.3 MB (3 bins) 10× smaller Commands 140+ 40+ endpoints different focus Standalone fetcher ❌ ✅ kuri-fetch no Chrome needed Terminal browser ❌ ✅ kuri-browse interactive REPL JS engine (no Chrome) ❌ ✅ QuickJS SSR-style DOM HTTP API server ❌ (CLI only) ✅ kuri thread-per-conn

agent-browser exposes a broader browser-control surface. Kuri is intentionally narrower: a lightweight HTTP API and CLI stack optimized for agent integration, token economy, and deployment simplicity.

§3 AI · 100%

The Problem Every browser automation tool drags in Playwright (~300 MB), a Node.js runtime, and a cascade of npm dependencies. Your AI agent just wants to read a page, click a button, and move on. Kuri is a single Zig binary. Four modes, zero runtime: kuri → CDP server (Chrome automation, a11y snapshots, HAR) kuri-fetch → standalone fetcher (no Chrome, QuickJS for JS, ~2 MB) kuri-browse → interactive terminal browser (navigate, follow links, search) kuri-agent → agentic CLI (scriptable Chrome automation + security testing)

📦 Installation One-line install (macOS / Linux) curl -fsSL https://raw.githubusercontent.com/justrach/kuri/main/install.sh | sh Detects your platform, downloads the right binary, installs to ~/.local/bin. macOS binaries are notarized — no Gatekeeper prompt. bun / npm bun install -g kuri-agent # or: npm install -g kuri-agent Downloads the correct native binary for your platform at install time. Manual Download the tarball for your platform from GitHub Releases and unpack it to your $PATH. Build from source Requires Zig ≥ 0.15.0. git clone https://github.com/justrach/kuri.git cd kuri zig build -Doptimize=ReleaseFast # Binaries in zig-out/bin/: kuri kuri-agent kuri-fetch kuri-browse

⚡ Quick Start Requirements: Zig ≥ 0.15.1 · Chrome/Chromium (for CDP mode) git clone https://github.com/justrach/kuri.git cd kuri

zig build # build everything zig build test # run 230+ tests

# CDP mode — launches Chrome automatically ./zig-out/bin/kuri

# Standalone mode — no Chrome needed ./zig-out/bin/kuri-fetch https://example.com

# Interactive browser — browse from your terminal

§4 Mixed · 48%

./zig-out/bin/kuri-browse https://example.com First run, shortest path # start the server; if CDP_URL is unset, kuri launches managed Chrome for you ./zig-out/bin/kuri

# discover tabs from that managed browser curl -s http://127.0.0.1:8080/discover

# inspect the discovered tab list curl -s http://127.0.0.1:8080/tabs If you already have Chrome running with remote debugging, set CDP_URL to either the WebSocket or HTTP endpoint: CDP_URL=ws://127.0.0.1:9222/devtools/browser/... ./zig-out/bin/kuri # or CDP_URL=http://127.0.0.1:9222 ./zig-out/bin/kuri Browse vercel.com in 4 commands # 1. Discover Chrome tabs curl -s http://localhost:8080/discover # → {"discovered":1,"total_tabs":1}

# 2. Get tab ID curl -s http://localhost:8080/tabs # → [{"id":"ABC123","url":"chrome://newtab/","title":"New Tab"}]

# 3. Navigate curl -s "http://localhost:8080/navigate?tab_id=ABC123&url=https://vercel.com"

# 4. Get accessibility snapshot (token-optimized for LLMs) curl -s "http://localhost:8080/snapshot?tab_id=ABC123&filter=interactive" # → [{"ref":"e0","role":"link","name":"VercelLogotype"}, # {"ref":"e1","role":"button","name":"Ask AI"}, ...]

🌐 HTTP API All endpoints return JSON. Optional auth via KURI_SECRET env var. Core

Path Description

GET /health Server status, tab count, version

GET /tabs List all registered tabs

GET /discover Auto-discover Chrome tabs via CDP

GET /browdie 🌰 (easter egg)

Browser Control

Path Params Description

GET /navigate

§5 AI · 100%

tab_id, url Navigate tab to URL

GET /tab/new url Create a new tab

GET /window/new url Create a new window/tab target

GET /snapshot tab_id, filter, format A11y tree snapshot with @eN refs

GET /text tab_id Extract page text

GET /screenshot tab_id, format, quality Capture screenshot (base64)

GET /action tab_id, ref, kind Click/type/scroll by ref

GET /evaluate tab_id, expression Execute JavaScript

GET /close tab_id Close tab + cleanup

Content Extraction

Path Description

GET /markdown Convert page to Markdown

GET /links Extract all links

GET /dom/query CSS selector query

GET /dom/html Get element HTML

GET /pdf Print page to PDF

HAR Recording & API Replay

Path Description

GET /har/start?tab_id= Start recording network traffic

GET /har/stop?tab_id= Stop + return HAR 1.2 JSON

GET /har/status?tab_id= Recording state + entry count

GET /har/replay?tab_id=&filter=api&format=all API map with curl/fetch/python code snippets

Navigation & State

Path Description

GET /back Browser back

GET /forward Browser forward

GET /reload Reload page

GET /cookies Get cookies

GET /cookies/delete Delete cookies

GET /cookies/clear Clear all cookies

GET /storage/local Get localStorage

GET /storage/session Get sessionStorage

GET /storage/local/clear Clear localStorage

GET /storage/session/clear Clear sessionStorage

GET /session/save Save browser session

GET /session/load Restore browser session

GET

§6 Mixed · 36%

/session/list List saved browser sessions

GET /auth/profile/save Save cookies + storage as a named auth profile

GET /auth/profile/load Restore a named auth profile into a tab

GET /auth/profile/list List saved auth profiles

GET /auth/profile/delete Delete a saved auth profile

GET /debug/enable Enable in-page debug HUD and optional freeze mode

GET /debug/disable Disable in-page debug HUD

GET /headers Set custom request headers

GET /perf/lcp Capture Largest Contentful Paint timing, optionally after navigation

On macOS, auth profile secrets are stored in the user Keychain. On other platforms, Kuri falls back to .kuri/auth-profiles/. url and expression query params are percent-decoded by the server, so encoded values like https%3A%2F%2Fexample.com are accepted. Advanced

Path Description

GET /diff/snapshot Delta diff between snapshots

GET /emulate Device emulation

GET /geolocation Set geolocation

POST /upload File upload

GET /script/inject Inject JavaScript

GET /intercept/start Start request interception

GET /intercept/stop Stop interception

GET /screenshot/annotated Screenshot with element annotations

GET /screenshot/diff Visual diff between screenshots

GET /screencast/start Start screencast

GET /screencast/stop Stop screencast

GET /video/start Start video recording

GET /video/stop Stop video recording

GET /console Get console messages

GET /stop Stop page loading

GET /get Direct HTTP fetch (server-side)

GET /scrollintoview Scroll a referenced element into view

GET /drag Drag from one ref to another

GET /keyboard/type Type text with key events

GET /keyboard/inserttext Insert text directly

GET /keydown Dispatch a keydown event

GET /keyup

§7 AI · 100%

Dispatch a keyup event

GET /wait Wait for ready state or element conditions

GET /tab/close Close a tab

GET /highlight Highlight an element by ref or selector

GET /errors Get page/runtime errors

GET /set/offline Toggle offline network emulation

GET /set/media Set emulated media features

GET /set/credentials Set HTTP basic auth credentials

GET /find Find text matches in the current page

GET /trace/start Start Chrome tracing

GET /trace/stop Stop tracing and return trace data

GET /profiler/start Start JS profiler

GET /profiler/stop Stop JS profiler

GET /inspect Inspect an element or page state

GET /set/viewport Set viewport size

GET /set/useragent Override user agent

GET /dom/attributes Get element attributes

GET /frames List frame tree

GET /network Inspect network state/requests

🛡️ Stealth & Bot Evasion Kuri applies anti-detection patches automatically on startup — no manual config needed. What's applied

Page.addScriptToEvaluateOnNewDocument — stealth patches run before any page JS navigator.webdriver = false — hides automation flag at Chromium level (--disable-blink-features=AutomationControlled) WebGL/Canvas/AudioContext spoofing — defeats fingerprint-based detection UA rotation — 5 realistic Chrome/Safari/Firefox user agents chrome.csi/chrome.loadTimes — stubs for Akamai-specific checks

Bot block detection Navigate auto-detects blocks and returns structured fallback: curl -s "http://localhost:8080/navigate?tab_id=ABC&url=https://protected-site.com" # If blocked: # {"blocked":true,"blocker":"akamai","ref_code":"0.7d...", # "fallback":{"suggestions":["Open URL directly in browser","Use KURI_PROXY"]}} # If ok: normal CDP response Detects: Akamai, Cloudflare, PerimeterX, DataDome, generic captcha.