Why a prompt?
doorman-benny has two entry points with different trade-offs, doesn't run server-side, and shouldn't be trusted in isolation for security decisions. Agents that pick the wrong entry point or skip the SSR guard ship code that almost works and silently misses bot traffic in production.
The prompt below covers all of that. Paste it into a fresh agent session before asking it to add fingerprinting to your project. The agent gets the API surface, the picking rules, the SSR caveat, the server-side-verification reminder, and the current option set in one shot.
Integrate the `doorman-benny` device fingerprinting library into this project. ## What it is doorman-benny is a client-side TypeScript library (current version: 1.25.0) that produces two hashes per device: - A per-browser fingerprint (high entropy, differs across browsers, fused from all 26 signals)
- A cross-browser hardware fingerprint (stable across Chrome, Safari, Firefox, Brave on the same machine) It runs entirely in the browser with zero server dependencies and zero runtime deps. ~39 KB minified. It also ships a thin server-roundtrip helper (`submitToBenny`) for projects that POST the result to a Benny the Doorman server. ## Tasks 1. Install the package: ```bash npm install doorman-benny ``` 2. Pick the right entry point for the use case: - `getDeviceId(options?)`: fast (about 200 to 300 ms), hardware signals only, cross-browser stable. Use for MFA, device-bound auth, cross-browser account linking, license enforcement. - `getFingerprint(options?)`: full pipeline (about 1 to 1.5 s), all 26 signals. Use for fraud detection, bot detection, session tracking, distinguishing two users on the same physical device. - `createCollector(options?)`: background collector. Call `start()` early in page load, `await getResult()` later when you need the hash. Use when latency on the critical path matters. - `submitToBenny(result, options)`: thin POST helper that ships the FingerprintResult or DeviceIdResult to a Benny server's `/v1/identify` endpoint and returns the parsed `IdentifyResponse` (server-assigned `visitorId`, `matchConfidence` bucket, `isNew`, timestamps). Use ONLY when the backend is a Benny server; for any other backend, write your own POST. 3. Guard against SSR or Node-only paths. doorman-benny requires browser globals (navigator, document, OffscreenCanvas, WebGL). In Next.js, Nuxt, Remix, or Angular SSR, only call it from a client-only effect (e.g. `useEffect`, `ngAfterViewInit`, `onMounted`) or check `typeof window !== 'undefined'`. 4. Send the resulting hash(es) to the backend. Never make a security decision purely on the client-side hash. Always verify server-side and combine with other signals. If the backend is a Benny server, use `submitToBenny` — it builds the request body, stamps `issuedAt`, sets the headers, handles timeout and `AbortSignal`, and throws a single structured `SubmitToBennyError` on any failure (10 codes including `INVALID_API_KEY`, `ORIGIN_NOT_ALLOWED`, `STALE_REQUEST`, `RATE_LIMITED`, `NETWORK_ERROR`, `TIMEOUT`). Wire the call yourself rather than auto-calling it from `getFingerprint`, so the POST can be gated on user consent or auth state. 5. Read `result.consistency.spoofLikelihood`, `result.automation.automationLikelihood`, and `result.incognito.incognitoLikelihood` for risk-scoring. Treat `medium` as suggestive and `high` as conclusive. Incognito is informational only, not evidence of fraud. ## Key options (FingerprintOptions) - `tiers: [1, 2]`: opt in to 7 extra signals (media_devices, webrtc_sdp, permissions, etc.)
- `warmup: true`: yield before collection so timing-sensitive signals sample on a quiet main thread
- `performance: true`: attach `_pipeline` timing block for profiling (never in production)
- `bindings: ['hardware']`: restrict to hardware-bound signals only (same as getDeviceId internally)
- `stabilize: ['private']`: drop farbled signals on Brave, Firefox-resist, and Safari 17+ for hash stability ## Key options (SubmitToBennyOptions) - `endpoint` (required): full URL to `/v1/identify`, e.g. `https://api.bennythedoorman.com/v1/identify`
- `apiKey` (required): public key from the dashboard (`pk_live_…` or `pk_test_…`)
- `subjectId` (optional, strongly recommended): hashed end-user identifier — populated so DSARs can resolve the visitor rows for a data subject
- `linkedId` (optional): customer-side correlation key (order id, session id) echoed back in the response
- `timeoutMs` (optional, default 10000): rejects with `TIMEOUT` if the server doesn't respond
- `signal` (optional): `AbortSignal` composed with the timeout — abort signals win
- `origin` (Node only): explicit Origin header for server-side callers; browser callers must NOT set this ## Minimal example ```ts
import { getDeviceId, getFingerprint, createCollector, submitToBenny } from 'doorman-benny'; // Cross-browser device identity
const { id, confidence } = await getDeviceId(); // Full per-browser fingerprint
const result = await getFingerprint();
console.log(result.fingerprint); // per-browser
console.log(result.hardwareFingerprint); // cross-browser, same as id above
console.log(result.consistency.spoofLikelihood);
console.log(result.automation.automationLikelihood); // Background pattern
const collector = createCollector();
collector.start();
//... user interaction...
const final = await collector.getResult(); // Send to a Benny server (only if your backend IS a Benny server).
try { const identity = await submitToBenny(result, { endpoint: 'https://api.bennythedoorman.com/v1/identify', apiKey: 'pk_live_...', subjectId: hashedUserEmail, timeoutMs: 8_000, }); console.log(identity.visitorId); // server-assigned, stable across sessions console.log(identity.matchConfidence); // 'exact' | 'fuzzy' | 'ambiguous' | 'new' | 'refused' console.log(identity.isNew);
} catch (err) { // SubmitToBennyError exposes.status and.errors[0].code for branching. console.error('submit failed:', err);
}
``` ## Consistency flags: notable ones - `canvas_noise_uniform` and `audio_noise_uniform`: uniform ±1 noise across runs; anti-detect tool pattern
- `webgl_ua_mismatch`, `font_ua_mismatch`, `error_stack_ua_mismatch`: UA vs signal cross-check trio
- `brave_shields_active`: Brave detected; spoofLikelihood auto-downgraded for real Brave users ## Automation flags: notable ones - `prototype_tostring_patched`: Function.prototype.toString returns a native-like string for a patched fn
- `prototype_tostring_brand_broken`: toString brand check fails; stealth patching detected ## Report back When you're done, tell me which files you changed, which entry point you picked and why, whether you used `submitToBenny` or hand-rolled the POST (and why), and whether you wired the hash up to any existing telemetry or auth flow. Ask before sending the hash to any external service.Copy this prompt and paste it into your AI coding assistant.
Tuning the prompt
If you only need cross-browser device matching, strip the prompt down to just the install command and the getDeviceId example; agents work better with narrower briefs. If you're integrating into a specific framework (Next.js App Router, Angular SSR, SvelteKit, etc.), add a line at the top of the prompt naming it so the agent picks the right client-only escape hatch.
Last reviewed 2026-06-04

