New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

koonjs

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

koonjs

Browser-impersonating HTTP client — TLS, HTTP/2, HTTP/3 fingerprint spoofing. Passes Akamai & Cloudflare. 175 browser profiles.

latest
Source
npmnpm
Version
0.7.0
Version published
Maintainers
1
Created
Source

koon

npm PyPI License: MIT CI

An HTTP client that impersonates real browsers at the TLS, HTTP/2, and HTTP/3 fingerprint level.

Built in Rust on top of BoringSSL with native bindings for Node.js, Python, R, and a CLI. Passes Akamai, Cloudflare, and other bot detection systems by reproducing exact browser fingerprints — verified against real browser captures.

Install

Node.js

npm install koonjs

Python

pip install koon

R

# Install from source (requires Rust toolchain)
remotes::install_github("scrape-hub/koon", subdir = "crates/r")

CLI — download from Releases, or:

cargo install koon-cli

Quick start

Node.js

import { Koon } from 'koonjs';

const client = new Koon({ browser: 'chrome145' });
const resp = await client.get('https://httpbin.org/json');
console.log(resp.ok);      // true
console.log(resp.text());  // body as string
console.log(resp.json());  // parsed JSON

Python

from koon import KoonSync

client = KoonSync("chrome145")
resp = client.get("https://httpbin.org/json")
print(resp.ok)      # True
print(resp.json())  # parsed JSON

R

library(koon)

client <- Koon$new("chrome145")
resp <- client$get("https://httpbin.org/json")
resp$ok      # TRUE
resp$text    # body as string

CLI

koon -b chrome145 https://example.com

Rust

use koon_core::{Client, Chrome};

let client = Client::new(Chrome::v145_windows())?;
let r = client.get("https://example.com").await?;

What it does

koon reproduces three fingerprint layers that bot detection systems check:

LayerWhat's fingerprintedHow koon matches it
TLSCipher suites, curves, extensions, ALPN, GREASE, ALPSBoringSSL with per-browser config (JA3/JA4 verified)
HTTP/2SETTINGS order, pseudo-header order, WINDOW_UPDATE, PRIORITY framesForked h2 crate with header ordering API (Akamai hash verified)
HTTP/3QUIC transport params, H3 settingsQuinn + h3 with browser-matching config

All fingerprints are tested against hashes captured from real browsers. 10 integration tests verify JA3N, JA4, and Akamai hashes for Chrome, Firefox, Safari, Edge, and Opera.

Supported browsers

BrowserVersionsPlatformsProfiles
Chrome131 – 145Windows, macOS, Linux, Android60
Firefox135 – 148Windows, macOS, Linux, Android56
Safari15.6 – 18.3macOS, iOS15
Edge131 – 145Windows, macOS30
Opera124 – 127Windows, macOS, Linux12
OkHttp4, 5Android2

175 profiles total. Use koon --list-browsers (CLI) to see all profiles.

Profile naming

Format: {browser}{version}{-os} — all parts except the browser name are optional. Both chrome145-macos and chrome145macos work (dash is optional).

Desktop browsers with OS variants:

BrowserDefault (macOS)WindowsmacOSLinux
Chrome 145chrome145chrome145-windowschrome145-macoschrome145-linux
Firefox 148firefox148firefox148-windowsfirefox148-macosfirefox148-linux
Edge 145edge145edge145-windowsedge145-macos
Opera 127opera127opera127-windowsopera127-macosopera127-linux
Safari 18.3safari183safari183-macos

Mobile browsers:

BrowserExample
Chrome Mobile (Android)chrome-mobile145
Firefox Mobile (Android)firefox-mobile148
Safari Mobile (iOS)safari-mobile183

OkHttp (Android apps):

VersionName
OkHttp 4okhttp4
OkHttp 5okhttp5

Shorthand — omit the version to get the latest:

ShorthandResolves to
chromeChrome 145 macOS
firefoxFirefox 148 macOS
safariSafari 18.3 macOS
edgeEdge 145 macOS
operaOpera 127 macOS
chrome-mobileChrome Mobile 145 Android
firefox-mobileFirefox Mobile 148 Android
safari-mobileSafari Mobile 18.3 iOS
okhttpOkHttp 5

Features

  • TLS fingerprint — cipher list, curves, sigalgs, extension order, GREASE, ALPS, cert compression, delegated credentials
  • HTTP/2 fingerprint — SETTINGS order, pseudo-header order, stream dependencies, priority frames, window sizes
  • HTTP/3 (QUIC) — Alt-Svc discovery, QUIC transport parameter fingerprinting, H3 connection pooling
  • Header order preservation — HTTP/2 (via forked h2) and HTTP/1.1
  • Encrypted Client Hello — real ECH from DNS HTTPS records, with GREASE fallback
  • DNS-over-HTTPS — Cloudflare and Google resolvers with ECH config discovery
  • TLS session resumption — session ticket caching across requests
  • Cookie jar — automatic persistence with domain/path/expiry/Secure/HttpOnly/SameSite
  • Proxy — HTTP CONNECT and SOCKS5, with H3 fallback to H2 through proxies
  • MITM proxy server — local proxy that re-sends all traffic through koon's fingerprinted stack
  • WebSocketwss:// connections with browser-matching TLS handshake
  • Streaming responses — chunked body streaming with async iterator support
  • Multipart form-data — file uploads with custom content types
  • Per-request headers, timeout, and proxy — override defaults per request without affecting the client
  • Ergonomic response APIok, text(), json(), header() on every response
  • Session persistence — save/load cookies and TLS session tickets to JSON
  • Fingerprint randomization — slight jitter on UA build number, accept-language q-values, H2 window sizes
  • Response decompression — gzip, brotli, deflate, zstd (automatic)
  • Local address binding — bind outgoing connections to a specific local IP (multi-IP servers, IP rotation)
  • Connection pooling — H3 multiplexed + H2 multiplexed + H1.1 keep-alive
  • Custom redirect hookonRedirect(status, url, headers) → bool to intercept and stop redirects (captcha detection, geo-block handling)
  • Automatic retry — retry on transport errors (connection, TLS, timeout) with automatic proxy rotation
  • Request hooksonRequest/onResponse observe-only callbacks for logging and debugging
  • Proxy rotation — round-robin over multiple proxy URLs, proxy-aware connection pool
  • Bandwidth tracking — per-request bytesSent/bytesReceived + cumulative counters on the client
  • String bodypost(), put(), patch() accept strings directly (no Buffer.from() needed)
  • User-Agent propertyclient.userAgent exposes the profile UA for Puppeteer/Playwright sync
  • Geo-locale matchinglocale: 'fr-FR' generates Accept-Language matching proxy geography
  • Structured errors — machine-readable [CODE] prefix on all errors (TIMEOUT, TLS_ERROR, PROXY_ERROR, etc.)
  • Connection inforesp.tlsResumed and resp.connectionReused for debugging connection behavior
  • CONNECT proxy headers — custom headers in the HTTP CONNECT tunnel (session IDs, geo-targeting for Bright Data, Oxylabs)
  • IPv4/IPv6 toggle — restrict DNS resolution to a specific IP version
  • Mobile browser profiles — Chrome Mobile (Android), Firefox Mobile (Android), Safari Mobile (iOS) with platform-specific TLS/H2 fingerprints
  • OkHttp profiles — Android app impersonation (OkHttp 4.x, 5.x) with Conscrypt TLS stack fingerprint
  • Sync Python APIKoonSync wrapper for all HTTP methods without asyncio (WebSocket and streaming remain async-only)

Usage

Node.js

import { Koon } from 'koonjs';

// Browser profile + options
const client = new Koon({
  browser: 'chrome145',
  headers: { 'X-Custom': 'value' },
  proxy: 'socks5://127.0.0.1:1080',  // optional
  localAddress: '192.168.1.100',      // optional: bind to specific IP
  randomize: true,                     // optional: slight fingerprint jitter
  retries: 3,                          // optional: retry on transport errors
  locale: 'fr-FR',                     // optional: Accept-Language for proxy geo
  ipVersion: 4,                        // optional: force IPv4 DNS resolution
  proxyHeaders: {                      // optional: CONNECT tunnel headers
    'X-Session-Id': 'abc123',
  },
  onRedirect: (status, url, headers) => {
    return !url.includes('captcha');   // stop if redirect goes to captcha
  },
});

// HTTP methods
const r1 = await client.get('https://httpbin.org/get');
const r2 = await client.post('https://httpbin.org/post', 'data');
const r3 = await client.put('https://httpbin.org/put', 'data');
const r4 = await client.delete('https://httpbin.org/delete');
const r5 = await client.patch('https://httpbin.org/patch', 'data');
const r6 = await client.head('https://httpbin.org/get');

// User-Agent (useful for Puppeteer/Playwright sync)
console.log(client.userAgent);  // "Mozilla/5.0 ... Chrome/145..."

// Response
console.log(r1.ok);                             // true (status 2xx)
console.log(r1.status);                         // 200
console.log(r1.text());                         // body as string (charset-aware)
console.log(r1.json());                         // parsed JSON
console.log(r1.contentType);                    // e.g. "text/html; charset=utf-8"
console.log(r1.header('content-type'));          // case-insensitive header lookup
console.log(r1.body);                           // raw Buffer
console.log(r1.tlsResumed);                     // TLS session was reused
console.log(r1.connectionReused);               // pooled connection was reused
console.log(r1.remoteAddress);                   // remote peer IP, or null for H3
console.log(r1.bytesSent, r1.bytesReceived);    // bandwidth per request

// Per-request headers, timeout, and proxy
const r7 = await client.get('https://httpbin.org/get', {
  headers: { 'Authorization': 'Bearer token' },
  timeout: 5,                                 // 5s timeout for this request only
  proxy: 'http://user:pass@other-proxy:8080', // override proxy for this request
});

// Cookies persist automatically
await client.get('https://httpbin.org/cookies/set/name/value');
const r = await client.get('https://httpbin.org/cookies');

// Clear cookies (keeps TLS sessions and connection pool)
client.clearCookies();

// Session save/load
const session = client.saveSession();           // JSON string
const client2 = new Koon({ browser: 'chrome145' });
client2.loadSession(session);

// File: save/load to disk
client.saveSessionToFile('session.json');
client2.loadSessionFromFile('session.json');

// WebSocket
const ws = await client.websocket('wss://echo.websocket.org');
await ws.send('hello');
const msg = await ws.receive();  // { isText: true, data: Buffer }
await ws.close();

// Streaming
const stream = await client.requestStreaming('GET', 'https://example.com/large');
console.log(stream.status);
const body = await stream.collect();  // or iterate with nextChunk()

// Multipart upload
await client.postMultipart('https://httpbin.org/post', [
  { name: 'field', value: 'text' },
  { name: 'file', fileData: Buffer.from('...'), filename: 'upload.txt', contentType: 'text/plain' },
]);

// MITM proxy
import { KoonProxy } from 'koonjs';
const proxy = await KoonProxy.start({ browser: 'chrome145', listenAddr: '127.0.0.1:8080' });
console.log(proxy.url);         // http://127.0.0.1:8080
console.log(proxy.caCertPath);  // path to CA cert for trust
await proxy.shutdown();

Python

KoonSync provides a blocking API — no asyncio needed:

from koon import KoonSync

# Browser profile + options
client = KoonSync("chrome145",
    headers={"X-Custom": "value"},
    retries=3,                                   # retry on transport errors
    locale="fr-FR",                              # Accept-Language for proxy geo
    ip_version=4,                                # force IPv4 DNS resolution
    proxy_headers={"X-Session-Id": "abc123"},    # CONNECT tunnel headers
    on_redirect=lambda s, u, h: "captcha" not in u,
)

# HTTP methods
r = client.get("https://httpbin.org/get")
r = client.post("https://httpbin.org/post", "data")
r = client.put("https://httpbin.org/put", "data")
r = client.delete("https://httpbin.org/delete")
r = client.patch("https://httpbin.org/patch", "data")
r = client.head("https://httpbin.org/get")

# Response
print(r.ok)                 # True (status 2xx)
print(r.status)             # 200
print(r.text)               # body as string (charset-aware)
print(r.json())             # parsed JSON
print(r.content_type)       # e.g. "text/html; charset=utf-8"
print(r.header("content-type"))  # case-insensitive header lookup
print(r.tls_resumed)        # TLS session was reused
print(r.connection_reused)  # pooled connection was reused
print(r.bytes_sent, r.bytes_received)  # bandwidth per request

# Per-request headers, timeout, and proxy
r = client.get("https://httpbin.org/get",
    headers={"Authorization": "Bearer token"},
    timeout=5,                                   # 5s timeout for this request only
    proxy="http://user:pass@other-proxy:8080",   # override proxy for this request
)

# Cookies persist automatically
client.get("https://httpbin.org/cookies/set/name/value")
r = client.get("https://httpbin.org/cookies")

# Clear cookies (keeps TLS sessions and connection pool)
client.clear_cookies()

# Session save/load
session = client.save_session()
client2 = KoonSync("chrome145")
client2.load_session(session)

# User-Agent (useful for Puppeteer/Playwright sync)
print(client.user_agent)  # "Mozilla/5.0 ... Chrome/145..."

For async code, use Koon instead — same API, but all request methods are coroutines:

from koon import Koon

client = Koon("chrome145")
resp = await client.get("https://httpbin.org/get")

# WebSocket (async only)
ws = await client.websocket("wss://echo.websocket.org")
await ws.send("hello")
msg = await ws.receive()
await ws.close()

# Streaming (async only)
stream = await client.request_streaming("GET", "https://example.com/large")
body = await stream.collect()

R

library(koon)

# Browser profile + options
client <- Koon$new("chrome145", proxy = "socks5://127.0.0.1:1080", randomize = TRUE,
                    local_address = "192.168.1.100", retries = 3L,
                    locale = "fr-FR", ip_version = 4L,
                    proxy_headers = c(`X-Session-Id` = "abc123"),
                    on_redirect = function(status, url, headers) !grepl("captcha", url))

# HTTP methods (synchronous)
resp <- client$get("https://httpbin.org/get")
resp <- client$post("https://httpbin.org/post", "data")
resp <- client$put("https://httpbin.org/put", "data")
resp <- client$delete("https://httpbin.org/delete")
resp <- client$patch("https://httpbin.org/patch", "data")
resp <- client$head("https://httpbin.org/get")

# Response
resp$ok         # TRUE (status 2xx)
resp$status     # 200
resp$version    # "HTTP/2.0"
resp$text           # body as string (charset-aware)
resp$content_type   # e.g. "text/html; charset=utf-8"
resp$body           # raw vector
resp$headers        # data.frame with name + value columns

# Parse JSON (via jsonlite)
data <- jsonlite::fromJSON(resp$text)

# Per-request headers
resp <- client$get("https://httpbin.org/get",
  headers = c(Authorization = "Bearer token")
)

# Cookies persist automatically
client$get("https://httpbin.org/cookies/set/name/value")
resp <- client$get("https://httpbin.org/cookies")

# Clear cookies (keeps TLS sessions and connection pool)
client$clear_cookies()

# Session save/load
json <- client$save_session()
client2 <- Koon$new("chrome145")
client2$load_session(json)

# Export profile as JSON
client$export_profile()

# List all browsers
koon_browsers()

CLI

# GET with browser profile
koon -b chrome145 https://example.com

# POST with body
koon -b firefox147 -X POST -d '{"key":"value"}' https://httpbin.org/post

# Custom headers
koon -b safari183 -H "Authorization: Bearer token" https://api.example.com

# Verbose output (request/response headers)
koon -b chrome145 -v https://httpbin.org/get

# JSON output
koon -b chrome145 --json https://httpbin.org/get

# Save response to file
koon -b chrome145 -o page.html https://example.com

# Proxy
koon -b chrome145 --proxy socks5://127.0.0.1:1080 https://example.com

# Session persistence
koon -b chrome145 --save-session session.json https://example.com/login
koon -b chrome145 --load-session session.json https://example.com/dashboard

# DNS-over-HTTPS
koon -b chrome145 --doh cloudflare https://example.com

# OS-specific user-agent
koon -b chrome145-macos https://example.com

# Fingerprint randomization
koon -b chrome145 --randomize https://example.com

# List all browser profiles
koon --list-browsers

# Export profile as JSON
koon --export-profile chrome145

# Start MITM proxy
koon proxy --browser chrome145 --listen 127.0.0.1:8080

Rust

[dependencies]
koon-core = { git = "https://github.com/scrape-hub/koon.git" }
use koon_core::{BrowserProfile, Client};
use koon_core::profile::Chrome;

#[tokio::main]
async fn main() -> Result<(), koon_core::Error> {
    // From a specific profile constructor
    let client = Client::new(Chrome::v145_windows())?;

    // Or with builder for full control
    let profile = BrowserProfile::resolve("chrome145")?;
    let client = Client::builder(profile)
        .max_retries(3)
        .locale("fr-FR")
        .ip_version(koon_core::IpVersion::V4)
        .on_redirect(|status, url, _headers| {
            !url.contains("captcha")
        })
        .build()?;

    let r = client.get("https://example.com").await?;
    println!("{} {} ({} bytes)", r.status, r.version, r.body.len());

    // Clear cookies without resetting TLS/pool
    client.clear_cookies();

    Ok(())
}

Architecture

koon-core         Rust library — TLS, HTTP/2, HTTP/3, profiles, proxy
koon-node         Node.js native addon via napi-rs
koon-python       Python extension via PyO3 + maturin
koon-r            R package via extendr
koon-cli          Command-line interface via clap

Key dependencies:

  • boring2 — BoringSSL Rust bindings
  • http2 (fork) — HTTP/2 with header field ordering
  • quinn + h3 — QUIC / HTTP/3
  • napi-rs — Rust to Node.js bridge
  • PyO3 + maturin — Rust to Python bridge
  • extendr — Rust to R bridge

Building from source

Only needed if you want to build koon yourself instead of using the published packages.

Requirements:

  • Rust 1.85+
  • CMake
  • NASM (Windows only, for BoringSSL assembly)
  • C compiler — MSVC (Windows), GCC or Clang (Linux/macOS)
# Core library
cargo build --release -p koon-core

# Node.js addon
cargo build --release -p koon-node

# Python package
cd crates/python && pip install -e .

# R package
cd crates/r && Rscript -e "rextendr::document(); devtools::install()"

# CLI binary
cargo build --release -p koon-cli

License

MIT

Keywords

tls-fingerprint

FAQs

Package last updated on 24 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts