Skip to content

πŸ†” 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:

FilePurposeAuto-Generated?Edit Safely?
identity.jsonHardware fingerprint (CPU, RAM, GPU, timezone, noise seeds)βœ… Yes (first run)❌ No
profile.jsonBrowser settings (UA, headers, GPU strings)βœ… Yes (from identity)βœ… Yes

⚠️ Version Required: Binary v0.1.12+ | Library v0.0.18+


Quick Start ​

typescript
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.json

identity.json β€” Hardware Fingerprint ​

Generated On First Run ​

When you first run the binary, Piggy reads real hardware values from your system:

json
{
  "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 ​

FieldSourcePurpose
cpu_coresstd::thread::hardware_concurrency()Browser fingerprint
ram_gb/proc/meminfo (Linux)Browser fingerprint
screen_resolutionQGuiApplication::primaryScreen()Viewport spoofing
gpu_vendor/sys/class/drm/card0/device/vendorWebGL spoofing
gpu_rendererOpenGL queryWebGL spoofing
timezoneQTimeZone::systemTimeZoneId()Timezone spoofing
canvas_seedRandomCanvas fingerprint noise
audio_seedRandomAudio fingerprint noise
webgl_seedRandomWebGL fingerprint noise
font_seedRandomFont 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 ​

bash
# Stop Piggy
pkill nothing-browser-headless

# Delete identity.json
rm identity.json

# Restart β€” new identity generated
./nothing-browser-headless

profile.json β€” Browser Settings ​

Generated From identity.json ​

Piggy builds profile.json using the hardware fingerprint:

json
{
  "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 ​

FieldUsed For
user_agentHTTP User-Agent header + navigator.userAgent
sec_ch_uaSec-Ch-Ua HTTP header (Chrome's User-Agent Client Hints)
platformnavigator.platform
chrome_versionVersion in Sec-Ch-Ua and JS APIs
languageAccept-Language header + navigator.language
gpu_rendererWebGL UNMASKED_RENDERER_WEBGL
gpu_vendorWebGL UNMASKED_VENDOR_WEBGL
timezoneIntl.DateTimeFormat().resolvedOptions().timeZone

βœ… SAFE TO EDIT ​

You can modify profile.json while Piggy is running:

bash
# 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-Agent

Common Profile Tweaks ​

Change User-Agent to Windows ​

json
{
  "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 ​

json
{
  "language": "ja-JP",
  "accept_language": "ja-JP,ja;q=0.9,en;q=0.8"
}

Change Timezone ​

json
{
  "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 ​

APISpoofed Value
navigator.webdriverundefined (not present)
navigator.pluginsChrome PDF Plugin, PDF Viewer, Native Client
navigator.languages["en-US", "en"]
navigator.platformFrom profile.json
navigator.userAgentFrom 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().timeZoneFrom profile.json
navigator.getBattery()Random level between 0.6-1.0
RTCPeerConnectionSTUN servers stripped (no WebRTC IP leak)
performance.now()Reduced precision (100ΞΌs)

Real-World Examples ​

1. Persistent Identity Across Restarts ​

typescript
// 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) ​

bash
# 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)
typescript
// 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 ​

typescript
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 ​

typescript
// 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 ​

typescript
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 ​

typescript
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:

bash
# 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:

typescript
await piggy.sessionReload();

"Website still detects automation" ​

Cause: Some bot detection looks for other signals

Check:

typescript
// 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 ​


Nothing Ecosystem Β· Ernest Tech House Β· Kenya Β· 2026

MIT Licensed | Built by Ernest Tech House Β· Kenya Β· 2026