A high-performance Rust library for generating statistically realistic browser fingerprints and HTTP headers — built on Bayesian networks trained on real-world browser data.
Drop-in Rust alternative to Python's browserforge with full feature parity: navigator, screen, UA Client Hints, WebGL, codecs, battery, fonts, plugins, multimedia devices, and WebRTC flag.
| Feature | Description |
|---|---|
| Statistically realistic | Sampled from Bayesian networks trained on millions of real browser profiles via the Apify dataset |
| Full fingerprint coverage | Navigator, screen, UA Client Hints (high-entropy), WebGL videoCard, audio/video codecs, battery, fonts, plugins, multimedia devices |
| HTTP headers | Ordered, realistic HTTP headers (User-Agent, Accept, sec-ch-ua, etc.) |
| Deterministic | Seed-based generation for reproducible sessions across restarts |
| Constrained | Filter by browser family (Chrome, Firefox, Safari, Edge), OS (Windows, macOS, Linux, Android, iOS), device type (Desktop, Mobile, Tablet), and locale |
| Blazing fast | ~127µs per fingerprint (warm), 7,800+ generations/sec — faster than browserforge |
| Zero unsafe | #![deny(unsafe_code)] enforced across all crates |
| Tiny binary | Bayesian networks embedded at compile time via include_bytes! — no runtime IO |
Add to your Cargo.toml:
[dependencies]
veilus-fingerprint = "0.1"use veilus_fingerprint::{BrowserFamily, FingerprintGenerator, OsFamily};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Random fingerprint — one line
let profile = FingerprintGenerator::random()?;
println!("{}", profile.fingerprint.navigator.user_agent);
println!("{:?}", profile.headers);
// Constrained to Chrome on Windows
let profile = FingerprintGenerator::new()
.browser(BrowserFamily::Chrome)
.os(OsFamily::Windows)
.generate()?;
// Deterministic — same seed → identical fingerprint
let p1 = FingerprintGenerator::new().seeded(42).generate()?;
let p2 = FingerprintGenerator::new().seeded(42).generate()?;
assert_eq!(p1.fingerprint.navigator.user_agent, p2.fingerprint.navigator.user_agent);
Ok(())
}FingerprintGenerator::new() // unconstrained builder
.browser(BrowserFamily::Chrome) // constrain browser
.os(OsFamily::Windows) // constrain OS
.device(DeviceType::Desktop) // constrain device type
.locale("en-US") // constrain locale
.seeded(42_u64) // deterministic mode
.strict() // error on unsatisfiable constraints
.generate()?; // → Result<BrowserProfile>
FingerprintGenerator::random()?; // shorthand for new().generate()// Browsers // Operating Systems
BrowserFamily::Chrome OsFamily::Windows
BrowserFamily::Firefox OsFamily::MacOs
BrowserFamily::Safari OsFamily::Linux
BrowserFamily::Edge OsFamily::Android
BrowserFamily::Other(String) OsFamily::Ios
OsFamily::Other(String)
// Device Types
DeviceType::Desktop
DeviceType::Mobile
DeviceType::Tabletpub struct BrowserProfile {
pub id: [u8; 16], // unique per call (always random)
pub generated_at: u64, // unix timestamp
pub dataset_version: String, // Apify dataset version
pub browser: BrowserInfo, // { name, version, family }
pub operating_system: OperatingSystem,// { name, version, family }
pub device: DeviceType, // Desktop | Mobile | Tablet
pub headers: HttpHeaders, // ordered HTTP headers (IndexMap)
pub fingerprint: BrowserFingerprint, // full fingerprint data ↓
}All fields match browserforge output 1:1 with camelCase JSON serialization:
pub struct BrowserFingerprint {
pub navigator: NavigatorFingerprint, // UA, platform, vendor, languages, ...
pub screen: ScreenFingerprint, // resolution, DPR, avail*, outer*, ...
pub video_card: Option<VideoCard>, // WebGL renderer & vendor
pub audio_codecs: Option<AudioCodecs>, // ogg, mp3, wav, m4a, aac
pub video_codecs: Option<VideoCodecs>, // ogg, h264, webm
pub battery: Option<Battery>, // charging, level, timing
pub fonts: Option<Vec<String>>, // detected font families
pub plugins_data: Option<PluginsData>, // browser plugins & MIME types
pub multimedia_devices: Option<MultimediaDevices>, // speakers, micros, webcams
pub mock_web_rtc: Option<bool>, // true=Chrome/Edge, false=Firefox/Safari
pub slim: Option<bool>, // always false
}pub struct NavigatorFingerprint {
pub user_agent: String, // navigator.userAgent
pub hardware_concurrency: u8, // navigator.hardwareConcurrency
pub device_memory: Option<f32>, // navigator.deviceMemory
pub platform: String, // navigator.platform
pub language: String, // navigator.language
pub languages: Vec<String>, // navigator.languages
pub webdriver: bool, // always false
pub vendor: String, // navigator.vendor
pub product_sub: String, // navigator.productSub
pub user_agent_data: Option<UserAgentData>, // UA Client Hints (Chromium only)
pub do_not_track: Option<String>, // navigator.doNotTrack
pub app_code_name: Option<String>, // navigator.appCodeName
pub app_name: Option<String>, // navigator.appName
pub app_version: Option<String>, // navigator.appVersion
pub max_touch_points: Option<u8>, // navigator.maxTouchPoints
pub extra_properties: Option<ExtraProperties>, // vendorFlavors, pdfViewerEnabled
}Chrome and Edge profiles include full UA Client Hints data:
pub struct UserAgentData {
pub brands: Vec<BrandVersion>, // [{brand, version}, ...]
pub mobile: bool,
pub platform: String, // e.g., "Windows"
pub architecture: Option<String>, // e.g., "x86"
pub bitness: Option<String>, // e.g., "64"
pub model: Option<String>, // device model (mobile only)
pub platform_version: Option<String>, // e.g., "10.0.0"
pub ua_full_version: Option<String>, // e.g., "125.0.6422.141"
pub full_version_list: Option<Vec<BrandVersion>>,
}cargo run --example full_output -p veilus-fingerprintClick to expand sample JSON
{
"id": [38, 14, 201, 106, 73, 133, 8, 240, 187, 103, 131, 126, 67, 211, 73, 97],
"generatedAt": 1775385049,
"datasetVersion": "2024-01",
"browser": {
"name": "chrome",
"version": "105.0.0.0",
"family": "chrome"
},
"operatingSystem": {
"name": "windows",
"version": "unknown",
"family": "windows"
},
"device": "desktop",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"Accept": "text/html,application/xhtml+xml,...",
"Accept-Encoding": "gzip",
"Upgrade-Insecure-Requests": "1"
},
"fingerprint": {
"navigator": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...",
"hardwareConcurrency": 32,
"deviceMemory": 8.0,
"platform": "Linux x86_64",
"language": "en-US",
"languages": ["en-US"],
"webdriver": false,
"vendor": "Google Inc.",
"productSub": "20030107",
"userAgentData": {
"brands": [
{ "brand": "Google Chrome", "version": "131" },
{ "brand": "Chromium", "version": "131" },
{ "brand": "Not_A Brand", "version": "24" }
],
"mobile": false,
"platform": "Windows",
"architecture": "x86",
"bitness": "64",
"platformVersion": "19.0.0",
"uaFullVersion": "131.0.6778.267",
"fullVersionList": [
{ "brand": "Google Chrome", "version": "131.0.6778.267" },
{ "brand": "Chromium", "version": "131.0.6778.267" },
{ "brand": "Not_A Brand", "version": "24.0.0.0" }
]
},
"appCodeName": "Mozilla",
"appName": "Netscape",
"appVersion": "5.0 (Windows NT 10.0; Win64; x64) ...",
"maxTouchPoints": 0,
"product": "Gecko",
"extraProperties": {
"vendorFlavors": ["chrome"],
"installedApps": []
}
},
"screen": {
"width": 1280,
"height": 1200,
"availWidth": 1280,
"availHeight": 1200,
"colorDepth": 24,
"pixelDepth": 24,
"devicePixelRatio": 1.0,
"innerWidth": 0,
"innerHeight": 0,
"availTop": 0,
"availLeft": 0,
"outerWidth": 1280,
"outerHeight": 1200
},
"videoCard": {
"renderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device ...), SwiftShader driver)",
"vendor": "Google Inc. (Google)"
},
"audioCodecs": {
"ogg": "probably",
"mp3": "probably",
"wav": "probably",
"m4a": "",
"aac": ""
},
"videoCodecs": {
"ogg": "",
"h264": "",
"webm": "probably"
},
"battery": {
"charging": true,
"chargingTime": 0.0,
"dischargingTime": null,
"level": 1.0
},
"fonts": [],
"mockWebRtc": true,
"slim": false
}
}use veilus_fingerprint::{BrowserFamily, FingerprintGenerator};
let profile = FingerprintGenerator::new()
.browser(BrowserFamily::Chrome)
.generate()?;
// Use with reqwest, hyper, or any HTTP client
for (key, value) in &profile.headers {
println!("{key}: {value}");
}
// User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
// Accept: text/html,application/xhtml+xml,...
// sec-ch-ua: "Google Chrome";v="125", "Chromium";v="125", ...let profile = FingerprintGenerator::new().seeded(777).generate()?;
let fp = &profile.fingerprint;
// WebGL Video Card
if let Some(vc) = &fp.video_card {
println!("GPU: {} — {}", vc.vendor, vc.renderer);
}
// Battery Status
if let Some(bat) = &fp.battery {
println!("Battery: {:.0}% (charging: {})", bat.level * 100.0, bat.charging);
}
// UA Client Hints (Chromium only)
if let Some(uad) = &fp.navigator.user_agent_data {
println!("Architecture: {:?}, Bitness: {:?}", uad.architecture, uad.bitness);
}
// Detected Fonts
if let Some(fonts) = &fp.fonts {
println!("Fonts: {:?}", fonts);
}
// WebRTC Flag
println!("mockWebRTC: {:?}", fp.mock_web_rtc); // Some(true) for Chrome/Edgeuse std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn session_seed(id: &str) -> u64 {
let mut h = DefaultHasher::new();
id.hash(&mut h);
h.finish()
}
// Same session ID → identical fingerprint every time
let profile = FingerprintGenerator::new()
.seeded(session_seed("user-alice-session-1"))
.browser(BrowserFamily::Chrome)
.os(OsFamily::Windows)
.generate()?;use veilus_fingerprint::FingerprintError;
match FingerprintGenerator::new()
.browser(BrowserFamily::Safari)
.os(OsFamily::Windows)
.strict()
.generate()
{
Err(FingerprintError::ConstraintConflict { browser, os }) => {
eprintln!("{browser} is not available on {os}");
}
Err(FingerprintError::ConstraintsTooRestrictive(msg)) => {
eprintln!("Constraints cannot be satisfied: {msg}");
}
Ok(profile) => { /* use profile */ }
Err(e) => eprintln!("Unexpected: {e}"),
}| Error Variant | When |
|---|---|
ConstraintConflict |
Known impossible combo (e.g., Safari + Windows) |
ConstraintsTooRestrictive |
Sampler exhausted retry budget (only with .strict()) |
NetworkParseError |
Embedded Bayesian network is corrupt (should never happen) |
SamplingFailed |
Internal sampler error |
Run any example with:
cargo run --example <name> -p veilus-fingerprint| Example | Description | browserforge equivalent |
|---|---|---|
basic |
Core API usage patterns | FingerprintGenerator().generate() |
full_output |
Complete JSON output for comparison | json.dumps(fg.generate().__dict__) |
headers |
HTTP header generation & integration | HeaderGenerator(browser='chrome').generate() |
extended |
All extended fields: GPU, codecs, battery, fonts, plugins | Full fingerprint inspection |
all_browsers |
Side-by-side browser comparison | Multi-browser generation |
seeded_batch |
Session-based deterministic generation | Reproducible sessions |
benchmark |
Performance measurement | Throughput comparison |
Benchmarked on Apple M-series (release build):
| Metric | Result |
|---|---|
| Cold start (first call) | ~70ms (includes network decompression) |
| Warm (unconstrained) | ~127µs/call → 7,800+ fps |
| Warm (constrained Chrome+Windows) | ~181µs/call → 5,500+ fps |
cargo run --release --example benchmark -p veilus-fingerprintComparison: browserforge (Python) reports 0.1–0.2ms per generation. veilus-fingerprint matches or exceeds this performance while running in pure Rust with zero FFI overhead.
fingerprint-generator/ ← Cargo workspace root
├── fingerprint-core/ ← Shared types & errors
│ └── src/types/fingerprint.rs ← BrowserProfile, NavigatorFingerprint, etc.
├── fingerprint-data/ ← Embedded Bayesian network ZIPs
│ ├── data/ ← header-network.zip, fingerprint-network.zip
│ └── src/loader.rs ← Lazy decompression via OnceLock
└── fingerprint-rs/ ← Public API crate
├── src/
│ ├── generator.rs ← FingerprintGenerator builder
│ ├── assembler.rs ← Raw network → BrowserProfile mapping
│ └── engine/ ← Bayesian sampler (ancestral + constrained)
└── examples/ ← 7 runnable examples
veilus-fingerprint uses two independent Bayesian networks:
- Header Network — generates realistic HTTP headers, browser family, OS, and device type. Constraints (browser, OS) are applied here via rejection sampling.
- Fingerprint Network — generates JavaScript API values (navigator, screen, WebGL, codecs, etc.). Sampled independently from the header network.
The assembler then merges outputs from both networks into a coherent BrowserProfile, deriving computed fields like mockWebRTC from the header network's browser family.
Access the Bayesian network and sampler directly for custom use cases:
use veilus_fingerprint::{sample_ancestral, sample_constrained, Constraints};
use fingerprint_data::loader::get_header_network;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
let network = get_header_network()?;
let mut rng = ChaCha8Rng::seed_from_u64(42);
// Unconstrained sample — raw node assignments
let assignment = sample_ancestral(network, &mut rng)?;
// Constrained sample — rejection sampling with target values
let mut constraints = Constraints::new();
constraints.insert("*OPERATING_SYSTEM".to_string(), vec!["windows".to_string()]);
let assignment = sample_constrained(network, &constraints, &mut rng)?;| Feature | browserforge (Python) | veilus-fingerprint (Rust) |
|---|---|---|
| Navigator (UA, platform, vendor) | ✅ | ✅ |
| HTTP Headers (ordered) | ✅ | ✅ |
| Screen (resolution, DPR) | ✅ | ✅ |
| Screen extended (outer*, avail*, client*) | ✅ | ✅ |
| UA Client Hints (high-entropy) | ✅ | ✅ |
| WebGL VideoCard (renderer, vendor) | ✅ | ✅ |
| Audio Codecs (ogg, mp3, wav, m4a, aac) | ✅ | ✅ |
| Video Codecs (ogg, h264, webm) | ✅ | ✅ |
| Battery Status (charging, level, time) | ✅ | ✅ |
| Fonts (detected font families) | ✅ | ✅ |
| Plugins & MIME types | ✅ | ✅ |
| Multimedia Devices (speakers, micros, webcams) | ✅ | ✅ |
| mockWebRTC flag | ✅ | ✅ |
| Browser constraint | ✅ | ✅ |
| OS constraint | ✅ | ✅ |
| Device constraint (mobile/desktop) | ✅ | ✅ |
| Seeded/deterministic generation | ❌ | ✅ |
| Strict mode (fail on impossible combos) | ❌ | ✅ |
| Playwright/Puppeteer injector | ✅ | 🔜 |
# Run the full test suite (58 tests across 3 crates)
cargo test --workspace
# Run with clippy (zero warnings enforced)
cargo clippy --workspace --all-targets -- -D warningsThe test suite includes:
- Structural tests — profile shape, field presence, JSON serialization
- Constraint tests — browser/OS filtering, conflict detection, strict mode
- Extended field tests — videoCard, codecs, battery, fonts, plugins, UAD, mockWebRTC
- Determinism tests — seed reproducibility, different seeds → different outputs
- Header sanitization — no internal meta-keys (
*BROWSER,*DEVICE) leak to output
Browser fingerprint distributions are derived from empirical data collected by Apify:
@apify/fingerprint-generator— fingerprint Bayesian network@apify/header-generator— header Bayesian network
The Bayesian network ZIP files are embedded at compile time via include_bytes! and validated by build.rs. No runtime file IO is required.