Skip to main content

Crate leyline

Crate leyline 

Source
Expand description

§leyline-http

Leyline is an async Rust HTTP client that impersonates browsers on the wire. The TLS ClientHello, HTTP/2 SETTINGS and priorities, HTTP/3 transport parameters, and request header order all come from captures of shipped browsers, so a server that reads JA3, JA4, or HTTP/2 fingerprints sees Chrome, Firefox, or Safari, not a Rust library. It does for Rust what curl-impersonate does for curl.

use leyline::{Browser, Session};

#[tokio::main]
async fn main() -> leyline::Result<()> {
    let session = Session::builder()
        .browser(Browser::Chrome154)
        .audit(true)
        .build()?;

    let resp = session.get("https://example.com/").await?;
    if let Some(audit) = resp.audit() {
        // t13d1517h2_8daaf6152771_cb7bf5808d99, the JA4 of Chrome 154
        println!("{}", audit.ja4);
    }
    Ok(())
}

For the profiles captured from shipped browsers, the live test suite checks that tls.peet.ws sees the same JA4 and HTTP/2 fingerprint from Leyline as from the browser capture.

§Plain client

Session::new() is a plain session:

use std::time::Duration;

use leyline::{RetryPolicy, Session};

let page = leyline::get("https://example.com/").await?.text().await?;

let api = Session::builder()
    .base_url("https://api.example/v1/")
    .bearer_auth("my-token")
    .user_agent("my-tool/1.0")
    .timeout(Duration::from_secs(30))
    .retry(RetryPolicy::transient())
    .build()?;
let status = api.get("status").error_for_status().await?;

No browser headers. A 4xx or 5xx is not an error unless you call error_for_status(). See API clients.

§What it matches

A user agent is one header. A fingerprinting server also reads the ClientHello, the HTTP/2 frames, and the header order. Leyline matches every layer a capture shows:

LayerWhat Leyline sends
TLSCipher suites, extensions and their order, GREASE, key shares, and ALPN, from BoringSSL at the revision Chrome 155 ships.
HTTP/2SETTINGS, window update, pseudo-header order, and stream priority.
HTTP/3QUIC ClientHello, transport parameters, connection ID lengths, HTTP/3 SETTINGS, and the size of the first datagram.
HeadersThe browser’s header set and order for each kind of request: navigation, form, script, image, XHR, frame, and reload.
Fetch metadatasec-fetch-site, Origin, and Referer follow the Fetch rules across redirect chains.
CookiesSameSite follows each browser: Chrome judges the final target, Firefox the whole redirect chain.

Of the 31 bundled profiles, 25 are captures of the shipped browser. Each profile names the build it came from in captured_against, and the profile reference lists the source of every one.

§Profiles

BrowserVersionsNotes
Chrome145 to 155Android is captured for Chrome 145 only.
Brave146, 154, 155
Firefox148 to 158Firefox 155 to 157 offer QUIC v2.
Safari18, 26, 27macOS.
Safari on iOS17, 18, 27
OkHttp4.12Android.
CFNetworkiOS 18, iOS 27, macOS 26Native app traffic, not Safari.

Edge and Opera are brand overlays on the Chrome profiles for desktop platforms. The Opera overlay covers Chrome 145 to 153. To send a profile that is not bundled, load it at runtime with SessionBuilder::profile.

§Install

Add Leyline and Tokio to Cargo.toml:

[dependencies]
leyline-http = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

The package is leyline-http. The library is leyline.

§Requirements

  • Rust 1.96 or later, and Tokio.
  • One of these targets: aarch64-apple-darwin, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-unknown-linux-musl, aarch64-unknown-linux-musl, or x86_64-pc-windows-msvc. Intel macOS is not supported.
  • A C and C++ compiler and git, to build BoringSSL from source: Xcode Command Line Tools on macOS, GCC or Clang on Linux, the MSVC build tools on Windows. On musl, a musl C and C++ toolchain. No CMake, NASM, Go, or libclang is needed.

The first build compiles BoringSSL, so it takes longer than a pure Rust dependency. Later builds reuse it.

§Choose a browser

Session::browser(Browser::default()) sends the newest bundled Chrome with a Windows identity. A patch release can move that default to a newer capture. To keep one fingerprint, pin the browser and the platform:

use leyline::{Browser, Platform, ProtocolPolicy, Session};

let session = Session::builder()
    .browser(Browser::Chrome150)
    .platform(Platform::MacOS)
    .protocol(ProtocolPolicy::Race)
    .build()?;

Reuse one session to share connections and cookies.

§Also included

  • Crawling: HostLimits, ProxyPool, and Response::block().
  • Accounts: Tab for pages and forms, Device to save an account to a file.
  • Atomic downloads and Link pagination.
  • Proxies: HTTP and HTTPS, and SOCKS5 with the socks feature. The pool keeps connections per proxy, and HTTP/3 runs through SOCKS5 UDP ASSOCIATE.
  • Cookies: a jar with RFC 6265bis limits and Secure rules that you can save and restore with serde.
  • WebSocket, streaming bodies, multipart forms, and gzip, deflate, Brotli, and zstd decoding.
  • TLS trust: custom roots, certificate pins, client certificates, and a TLS version floor through TlsTrustConfig.
  • Typed errors: Error::category() names the failure, and is_retryable() says whether to try again. Retries are off until you set a RetryPolicy.
  • Debug output masks passwords, tokens, cookies, and query values.
  • The bundled BoringSSL prefixes its symbols with LEYLINE_, so it links beside OpenSSL and Cloudflare’s boring.

§Limits

  • A profile covers the TLS, HTTP/2, HTTP/3, and header properties its capture shows. It does not reproduce every byte a browser sends, and it runs no JavaScript.
  • Firefox on Android sends the desktop Firefox ClientHello.
  • The kernel picks the TCP option order and window scale. Leyline sets TTL, TCP_NODELAY, and, where the OS allows, MSS, don’t-fragment, and a window clamp.
  • HTTP/3 sends no 0-RTT data, and it can’t run through an HTTP or HTTPS proxy. Safari on iOS 17, OkHttp, and the CFNetwork profiles have no HTTP/3 data.
  • Response::audit() reports what the configured profile sends. It is not a packet capture.
  • Detection by a remote site is not a security defect. See SECURITY.md.

§Documentation

PageContents
Quick startFrom an empty project to a parsed response.
User guideSessions, requests, streaming, proxies, cookies, crawling, accounts, HTTP/3, and TLS trust.
API referenceEvery public item, and each task mapped to its one call.
Profile referenceBundled profiles and their capture status.
BenchmarksPaired runs against wreq, reqwest, and tls-client, losses included.
ChangelogRelease notes and the version policy.

§Security

To report a vulnerability, follow SECURITY.md.

§Contributing

CONTRIBUTING.md describes the build and the checks.

§License

Leyline is licensed under the MIT license. It depends on forks of BoringSSL (MIT, Apache-2.0, and BSD-3-Clause) and quiche (BSD-2-Clause), published as separate crates. NOTICE lists the forks and their licenses.

Copyright 2026 Manaforge Technologies, LLC.

Re-exports§

pub use http;

Modules§

audit
cookie
html
multipart
profile
tls
trace

Structs§

BlockRules
BlockSignal
Body
BodyStream
BrowserProfile
CloseFrame
CompressionConfig
Crawl
CrawlPage
CrawlStats
Device
DeviceAutosave
DeviceAutosaveOptions
DigestAuth
DnsConfig
Error
HostLimits
HostStats
Identity
LeylineService
Link
NoProxy
Pages
PoolConfig
PoolStats
PrefixRead
ProxyConfig
ProxyHealth
ProxyPool
ProxyRule
ProxyUrl
RedirectAttempt
RedirectPolicy
RequestBuilder
Response
ResponseTiming
RetryPolicy
Robots
Session
SessionBuilder
SessionIdentity
SessionState
SocketConfig
Tab
TcpProfile
TimeoutConfig
TlsInfo
TlsTrustConfig
Url
A parsed URL record.
WebSocketBuilder
WebSocketConfig
WsConnection
WsSink
WsStream

Enums§

BlockKind
Browser
ChromiumBrand
ContentEncoding
ErrorCategory
ErrorCode
Family
FetchSite
H2Error
HeaderAnchor
HttpVersion
Kind
Platform
Preset
ProtocolPolicy
ProxyReply
Purpose
RedirectAction
RelayBody
Respect
RetryTrigger
RobotsSource
Scope
SkipReason
StopReason
TlsError
TlsMinVersion
WaitFormat
WsMessage

Traits§

IntoParamPair
IntoUrl

Functions§

get
redact_url
relay_headers

Type Aliases§

Result