π Identity & Profile β Hardware Fingerprint & Browser Settings β
Piggy generates a persistent hardware fingerprint and browser profile on first run. This ensures consistent identity across restarts and bypasses fingerprint-based bot detection.
Overview β
Two files control Piggy's identity:
| File | Purpose | Auto-Generated? | Edit Safely? |
|---|---|---|---|
identity.json | Hardware fingerprint (CPU, RAM, GPU, timezone, noise seeds) | β Yes (first run) | β No |
profile.json | Browser settings (UA, headers, GPU strings) | β Yes (from identity) | β Yes |
β οΈ Version Required: Binary v0.1.12+ | Library v0.0.18+
Quick Start β
import piggy from "nothing-browser";
await piggy.connect({
host: "http://localhost:2005",
key: "peaseernest..."
});
// Get file paths
const paths = await piggy.sessionPaths();
console.log("Identity:", paths.identity); // identity.json
console.log("Profile:", paths.profile); // profile.json
// Hot reload profile after editing
await piggy.sessionReload();
// Check current User-Agent
const currentUA = await piggy.evaluate(() => navigator.userAgent);
console.log("Browser reports:", currentUA);File Locations β
Both files are saved in the same directory as the binary:
/home/user/my-scraper/
βββ nothing-browser-headless
βββ identity.json β DO NOT EDIT (hardware fingerprint)
βββ profile.json β SAFE TO EDIT (browser settings)
βββ cookies.json β SAFE TO EDIT
βββ ws.json
βββ pings.jsonidentity.json β Hardware Fingerprint β
Generated On First Run β
When you first run the binary, Piggy reads real hardware values from your system:
{
"cpu_cores": 8,
"ram_gb": 16,
"screen_resolution": "1920x1080",
"gpu_vendor": "Intel",
"gpu_renderer": "ANGLE (Intel, Mesa Intel(R) HD Graphics 3000 (SNB GT2), OpenGL 4.6)",
"timezone": "Africa/Nairobi",
"canvas_seed": 123456789,
"audio_seed": 987654321,
"webgl_seed": 555555555,
"font_seed": 111111111
}What Each Field Means β
| Field | Source | Purpose |
|---|---|---|
cpu_cores | std::thread::hardware_concurrency() | Browser fingerprint |
ram_gb | /proc/meminfo (Linux) | Browser fingerprint |
screen_resolution | QGuiApplication::primaryScreen() | Viewport spoofing |
gpu_vendor | /sys/class/drm/card0/device/vendor | WebGL spoofing |
gpu_renderer | OpenGL query | WebGL spoofing |
timezone | QTimeZone::systemTimeZoneId() | Timezone spoofing |
canvas_seed | Random | Canvas fingerprint noise |
audio_seed | Random | Audio fingerprint noise |
webgl_seed | Random | WebGL fingerprint noise |
font_seed | Random | Font fingerprint noise |
β οΈ DO NOT EDIT THIS FILE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β WARNING: Do NOT modify identity.json manually β
β β
β - Breaks fingerprint consistency β
β - May make browser detectable β
β - Invalid values cause crashes β
β β
β Delete it β Piggy regenerates fresh identity β
β Keep it β Consistent fingerprint across restarts β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββReset Identity β
# Stop Piggy
pkill nothing-browser-headless
# Delete identity.json
rm identity.json
# Restart β new identity generated
./nothing-browser-headlessprofile.json β Browser Settings β
Generated From identity.json β
Piggy builds profile.json using the hardware fingerprint:
{
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"sec_ch_ua": "\"Google Chrome\";v=\"124\", \"Chromium\";v=\"124\", \"Not-A.Brand\";v=\"99\"",
"platform": "Linux x86_64",
"chrome_version": 124,
"language": "en-US",
"gpu_renderer": "ANGLE (Intel, Mesa Intel(R) HD Graphics 3000 (SNB GT2), OpenGL 4.6)",
"gpu_vendor": "Google Inc. (Intel)",
"timezone": "Africa/Nairobi"
}What Piggy Does With These Values β
| Field | Used For |
|---|---|
user_agent | HTTP User-Agent header + navigator.userAgent |
sec_ch_ua | Sec-Ch-Ua HTTP header (Chrome's User-Agent Client Hints) |
platform | navigator.platform |
chrome_version | Version in Sec-Ch-Ua and JS APIs |
language | Accept-Language header + navigator.language |
gpu_renderer | WebGL UNMASKED_RENDERER_WEBGL |
gpu_vendor | WebGL UNMASKED_VENDOR_WEBGL |
timezone | Intl.DateTimeFormat().resolvedOptions().timeZone |
β SAFE TO EDIT β
You can modify profile.json while Piggy is running:
# 1. Edit profile.json
nano profile.json
# 2. Change user_agent to something else
{
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0"
}
# 3. Hot reload from TypeScript
await piggy.sessionReload();
# 4. All new requests use the new User-AgentCommon Profile Tweaks β
Change User-Agent to Windows β
{
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
"platform": "Win32",
"sec_ch_ua_platform": "\"Windows\""
}Change Language β
{
"language": "ja-JP",
"accept_language": "ja-JP,ja;q=0.9,en;q=0.8"
}Change Timezone β
{
"timezone": "Asia/Tokyo"
}How Fingerprint Spoofing Works β
DocumentCreation Injection β
Unlike Puppeteer/Playwright that inject scripts after page load, Piggy injects at DocumentCreation β before any page JavaScript runs.
Timeline:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΊ
Page starts DocumentCreation Page JavaScript runs
β β β
βΌ βΌ βΌ
βββββββββββ ββββββββββββ βββββββββββ
β HTML β β Piggy β β Website β
β parsed β βββββββΊ β injects β βββββββΊ β runs β
β β β spoofed β β its β
β β β values β β scripts β
βββββββββββ ββββββββββββ βββββββββββ
Spoofed values are already present when website scripts run.
No "before/after" detection possible.What Gets Spoofed β
| API | Spoofed Value |
|---|---|
navigator.webdriver | undefined (not present) |
navigator.plugins | Chrome PDF Plugin, PDF Viewer, Native Client |
navigator.languages | ["en-US", "en"] |
navigator.platform | From profile.json |
navigator.userAgent | From profile.json |
navigator.userAgentData | { brands: [...], mobile: false } |
WebGLRenderingContext.getParameter(37445) | GPU vendor from profile.json |
WebGLRenderingContext.getParameter(37446) | GPU renderer from profile.json |
CanvasRenderingContext2D.getImageData() | Β±1 per-pixel noise (xorshift PRNG) |
AudioContext | Β±0.00000005 per-sample noise |
Intl.DateTimeFormat().resolvedOptions().timeZone | From profile.json |
navigator.getBattery() | Random level between 0.6-1.0 |
RTCPeerConnection | STUN servers stripped (no WebRTC IP leak) |
performance.now() | Reduced precision (100ΞΌs) |
Real-World Examples β
1. Persistent Identity Across Restarts β
// First run β identity.json generated
// Second run β same identity loaded automatically
await piggy.connect({ host, key });
// Browser fingerprint is identical to previous run
const fingerprint = await piggy.evaluate(() => ({
userAgent: navigator.userAgent,
platform: navigator.platform,
webglVendor: document.createElement('canvas').getContext('webgl')?.getParameter(37445)
}));
console.log("Same fingerprint every run:", fingerprint);2. Multiple Profiles (Different Identities) β
# Create separate directories for different identities
mkdir ./profile-us
cp nothing-browser-headless ./profile-us/
cd ./profile-us
./nothing-browser-headless # Generates US identity (timezone America/New_York)
mkdir ./profile-jp
cp nothing-browser-headless ./profile-jp/
cd ./profile-jp
./nothing-browser-headless # Generates JP identity (timezone Asia/Tokyo)// Connect to US profile
await piggy.connect({ host: "http://localhost:2005", key: "us-key" });
// Connect to JP profile (different binary instance)
await piggy.connect({ host: "http://localhost:2006", key: "jp-key" });3. Rotating User-Agent β
import { readFileSync, writeFileSync } from "fs";
const userAgents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/124.0.0.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/124.0.0.0",
"Mozilla/5.0 (X11; Linux x86_64) Chrome/124.0.0.0"
];
for (const ua of userAgents) {
// Update profile.json
const profile = JSON.parse(readFileSync("./profile.json", "utf-8"));
profile.user_agent = ua;
writeFileSync("./profile.json", JSON.stringify(profile, null, 2));
// Hot reload
await piggy.sessionReload();
// Check new UA
const currentUA = await piggy.evaluate(() => navigator.userAgent);
console.log("Now using:", currentUA);
// Scrape with this UA
await piggy.site.navigate("https://example.com");
}4. Verify Fingerprint is Working β
// Test what websites see
const fingerprint = await piggy.evaluate(() => ({
// Navigator properties
webdriver: (navigator as any).webdriver,
plugins: navigator.plugins.length,
languages: navigator.languages,
platform: navigator.platform,
userAgent: navigator.userAgent,
// WebGL
webglVendor: (() => {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
return gl?.getParameter(37445); // UNMASKED_VENDOR_WEBGL
})(),
// Canvas (should have noise, but not detectable as spoofed)
canvasHash: (() => {
const canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 100;
const ctx = canvas.getContext('2d');
ctx?.fillRect(0, 0, 100, 100);
return canvas.toDataURL().slice(0, 100);
})(),
// Timezone
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
}));
console.log("Fingerprint:", fingerprint);
// webdriver: undefined β
// plugins: 3 β
// webglVendor: "Google Inc. (Intel)" β
// timezone: matches profile.json β
File Format Specifications β
identity.json β
interface Identity {
cpu_cores: number; // 1-128
ram_gb: number; // 0.5-512
screen_resolution: string; // "1920x1080"
gpu_vendor: string; // "Intel", "NVIDIA", "AMD"
gpu_renderer: string; // Full renderer string
timezone: string; // IANA timezone (e.g., "Africa/Nairobi")
canvas_seed: number; // 0-2^31-1
audio_seed: number; // 0-2^31-1
webgl_seed: number; // 0-2^31-1
font_seed: number; // 0-2^31-1
}profile.json β
interface Profile {
user_agent: string; // Full browser UA
sec_ch_ua: string; // Chrome's Sec-Ch-Ua header
platform: string; // "Win32", "MacIntel", "Linux x86_64"
chrome_version: number; // 124
language: string; // "en-US"
gpu_renderer: string; // WebGL renderer string
gpu_vendor: string; // WebGL vendor string
timezone: string; // IANA timezone
}Troubleshooting β
"identity.json not being generated" β
Cause: Binary version too old or permissions issue
Fix:
# Check binary version
./nothing-browser-headless --version
# Ensure write permissions in current directory
chmod 755 ."profile.json values not applying" β
Cause: Need to reload after editing
Fix:
await piggy.sessionReload();"Website still detects automation" β
Cause: Some bot detection looks for other signals
Check:
// Enable human mode for behavioral patterns
piggy.actHuman(true);
// Use headful mode (some sites detect headless)
await piggy.launch({ binary: "headful" });"Different fingerprint on each run" β
Cause: identity.json deleted or not readable
Fix: Keep identity.json β don't delete it between runs
Next Steps β
- Cookies Hot Reload β Edit cookies while browser runs
- Session Persistence β Save WebSocket frames and pings
- Anti-Detection β Complete anti-detection guide
Nothing Ecosystem Β· Ernest Tech House Β· Kenya Β· 2026