@ianmenethil/zp-devicefp 0.1.0-alpha.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Browser Fingerprint Library
2
2
 
3
- Async-first TypeScript browser and device fingerprinting library built from the supplied design brief. It collects a tiered set of browser signals, computes per-component hashes, returns a composite thumbprint, and includes a lightweight anomaly report instead of pretending a client-only fingerprint is unforgeable.
3
+ Async-first TypeScript browser and device fingerprinting library. Collects 19 tiered browser signals, computes per-component SHA-256 hashes, returns a composite thumbprint, and includes an anti-spoof anomaly report with confidence scoring.
4
+
5
+ - **Zero runtime dependencies** — no CDN loads, no npm deps at runtime.
6
+ - **ESM, CJS, and IIFE** — single TypeScript source tree, three distribution formats.
7
+ - **84 tests, 95% line coverage.**
8
+
9
+ ---
4
10
 
5
11
  ## Install
6
12
 
@@ -12,97 +18,236 @@ bun add @ianmenethil/zp-devicefp
12
18
  import { createFingerprintClient } from '@ianmenethil/zp-devicefp';
13
19
  ```
14
20
 
15
- ## Package contents
21
+ ## Quick start
16
22
 
17
- ```text
18
- @ianmenethil/zp-devicefp/
19
- ├── dist/
20
- │ ├── npm/
21
- │ │ ├── index.mjs ← ESM entry (browser, es2022)
22
- │ │ ├── index.cjs ← CJS entry (Node 22+)
23
- │ │ └── index.d.ts ← public types
24
- │ └── cdn/
25
- │ ├── zp.dfp.js ← IIFE (unminified)
26
- │ ├── zp.dfp.min.js ← IIFE (minified)
27
- │ ├── zp.dfp.obf.js ← IIFE (minified + obfuscated)
28
- │ ├── zp.dfp.esm.js ← ESM for import()
29
- │ └── zp.dfp.manifest.json ← SRI integrity hashes
30
- ├── README.md
31
- ├── LICENSE
32
- └── docs/
23
+ ```ts
24
+ import { createFingerprintClient } from '@ianmenethil/zp-devicefp';
25
+
26
+ const client = await createFingerprintClient({ timeoutMs: 1500 });
27
+
28
+ // Async collection all signals, parallel with timeouts
29
+ const result = await client.collect();
30
+
31
+ console.log(result.thumbprint); // "a1b2c3d4..." (64-char SHA-256 hex)
32
+ console.log(result.confidence); // 0.87
33
+ console.log(result.antiSpoof.score); // 0.94
33
34
  ```
34
35
 
35
- ## CDN
36
+ ---
36
37
 
37
- CDN files are available from two sources — jsDelivr (automatic from each npm publish) and the self-hosted CDN at `cdn.zenithpayments.support` (deployed on every build).
38
+ ## API overview
38
39
 
39
- | File | jsDelivr | Self-hosted |
40
- |------|----------|-------------|
41
- | IIFE (minified) | `@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.min.js` | `devicefp/latest/zp.dfp.min.js` |
42
- | IIFE (unminified) | `@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.js` | `devicefp/latest/zp.dfp.js` |
43
- | IIFE (obfuscated) | `@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.obf.js` | `devicefp/latest/zp.dfp.obf.js` |
44
- | ESM | `@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.esm.js` | `devicefp/latest/zp.dfp.esm.js` |
45
- | Manifest | `@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.manifest.json` | `devicefp/latest/zp.dfp.manifest.json` |
40
+ | Method | Returns | Description |
41
+ |--------|---------|-------------|
42
+ | `client.collect(options?)` | `Promise<FingerprintResult>` | Async collection. All signals run in parallel with per-signal timeouts. Emits events. |
43
+ | `client.collectSync(options?)` | `FingerprintResult` | Sync collection. Only 13 of 19 signals support sync mode. No timeout/abort. |
44
+ | `client.upload(result, options)` | `Promise<Response>` | POST the fingerprint to a backend endpoint. |
45
+ | `client.on(event, callback)` | `() => void` | Subscribe to `progress`, `warning`, or `complete` events during async collection. |
46
46
 
47
- - **jsDelivr base:** `https://cdn.jsdelivr.net/npm/` — drop the version tag for latest, or pin with `@2.1` or `@2`.
48
- - **Self-hosted base:** `https://cdn.zenithpayments.support/` — `latest/` always points to the most recent build; versioned folders (`2.1.1/`) are created on `bun run release`.
47
+ ### Events
49
48
 
50
- ### CDN usage
49
+ ```ts
50
+ client.on('progress', (e) => {
51
+ // { completed: 3, total: 10, signal: 'canvas', result: SignalResult }
52
+ console.log(`${e.completed}/${e.total}: ${e.signal}`);
53
+ });
51
54
 
52
- ```html
53
- <!-- jsDelivr -->
54
- <script src="https://cdn.jsdelivr.net/npm/@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.min.js"></script>
55
+ client.on('warning', (e) => {
56
+ // { signal?: 'webrtc', message: 'RTCPeerConnection not available' }
57
+ console.warn(e.message);
58
+ });
55
59
 
56
- <!-- Self-hosted -->
57
- <script src="https://cdn.zenithpayments.support/devicefp/latest/zp.dfp.min.js"></script>
60
+ client.on('complete', (e) => {
61
+ // { result: FingerprintResult }
62
+ console.log('Done', e.result.thumbprint);
63
+ });
64
+ ```
58
65
 
59
- <script>
60
- const client = await window.DeviceFP.createFingerprintClient({ timeoutMs: 1500 });
61
- const result = await client.collect();
62
- </script>
66
+ ### Upload
67
+
68
+ ```ts
69
+ await client.upload(result, {
70
+ endpoint: 'https://api.example.com/fingerprint',
71
+ headers: { 'Authorization': 'Bearer token' },
72
+ bodyExtras: { sessionId: 'abc123' },
73
+ });
63
74
  ```
64
75
 
65
- ## Public API
76
+ ### Aborting
66
77
 
67
78
  ```ts
68
- import { createFingerprintClient } from '@ianmenethil/zp-devicefp';
79
+ const controller = new AbortController();
69
80
 
70
- const client = await createFingerprintClient({
71
- timeoutMs: 1500,
72
- extended: false,
73
- });
81
+ const resultPromise = client.collect({ abortSignal: controller.signal });
74
82
 
75
- const result = await client.collect();
76
- console.log(result.thumbprint, result.antiSpoof);
77
- console.log(result.diagnostics);
83
+ // Cancel mid-flight
84
+ controller.abort();
85
+
86
+ const result = await resultPromise;
87
+ // Aborted signals appear as status: 'error' with error: 'Collection aborted.'
78
88
  ```
79
89
 
80
- `collectSync()` intentionally returns a partial fingerprint that only uses sync-safe collectors.
90
+ ---
81
91
 
82
- Each result also includes:
92
+ ## Options
83
93
 
84
- - `componentHashes`: deterministic per-signal digests
85
- - `antiSpoof`: anomaly and automation hints
86
- - `diagnostics`: requested signals, completed signals, and elapsed runtime
87
- - `warnings`: collection-time warnings that are scoped to that run only
94
+ ```ts
95
+ interface CollectOptions {
96
+ include?: SignalName[]; // whitelist: only collect these signals
97
+ exclude?: SignalName[]; // blacklist: skip these signals
98
+ timeoutMs?: number; // per-signal timeout (default: 1500)
99
+ abortSignal?: AbortSignal; // cancel in-flight collection
100
+ extended?: boolean; // include extended-tier signals (default: false)
101
+ debug?: boolean; // enable debug logging (default: false)
102
+ }
103
+ ```
88
104
 
89
- ## Signals
105
+ ---
90
106
 
91
- Core tier:
107
+ ## Result shape
92
108
 
93
- `ua` · `uaHints` · `locale` · `screen` · `hardware` · `storage` · `fonts` · `canvas` · `webgl` · `audio`
109
+ ```ts
110
+ interface FingerprintResult {
111
+ schemaVersion: number; // schema version (integer, bumped on breaking changes)
112
+ libraryVersion: string; // library semver string
113
+ thumbprint: string; // SHA-256 of all component hashes combined
114
+ confidence: number; // 0–1 quality score
115
+ componentHashes: Partial<Record<SignalName, string>>; // per-signal SHA-256 digests
116
+ signals: Partial<Record<SignalName, SignalResult>>; // raw signal results
117
+ antiSpoof: AntiSpoofReport; // coherence analysis and automation hints
118
+ warnings: string[]; // non-fatal collection warnings
119
+ diagnostics: CollectorDiagnostics; // run metadata
120
+ }
121
+ ```
94
122
 
95
- Extended tier:
123
+ ### `AntiSpoofReport`
96
124
 
97
- `mediaDevices` · `permissions` · `webrtc` · `frameInfo` · `networkInfo` · `paymentSupport` · `referrerInfo` · `navigationInfo` · `riskSignals`
125
+ ```ts
126
+ {
127
+ score: number; // 0 = automated/bot, 1 = genuine user
128
+ anomalies: string[]; // signal inconsistencies (6 anomaly types)
129
+ automationHints: string[]; // headless/bot indicators (2 hint types)
130
+ }
131
+ ```
98
132
 
99
- ## Design choices
133
+ ### `SignalResult`
134
+
135
+ ```ts
136
+ {
137
+ status: 'ok' | 'unsupported' | 'blocked' | 'timeout' | 'error';
138
+ value?: unknown; // signal-specific data (only when status === 'ok')
139
+ durationMs: number; // wall-clock collection time
140
+ error?: string; // error description (when status !== 'ok')
141
+ }
142
+ ```
143
+
144
+ Full API reference with all types, methods, events, options, confidence scoring, anti-spoof heuristics, and constants: **[docs/api-reference.md](docs/api-reference.md)**
145
+
146
+ ---
147
+
148
+ ## Signals (19 total)
149
+
150
+ ### Core tier (10 signals, enabled by default)
100
151
 
101
- - Async first: high-value collectors like audio, UA hints, media devices, permissions, and WebRTC run in `collect()`.
102
- - Explainable output: every signal includes a status and duration.
103
- - No dangerous defaults: no battery, sensors, arbitrary installed-app probes, or local IP harvesting.
104
- - Anti-spoofing focuses on coherence checks and automation hints.
105
- - Upload is backend-neutral and optional.
152
+ | Signal | Sync | Description |
153
+ |--------|------|-------------|
154
+ | `ua` | yes | `navigator.userAgent`, platform, webdriver, maxTouchPoints, cookieEnabled, vendor |
155
+ | `uaHints` | no | User-Agent Client Hints: brands, platform, high-entropy values (architecture, bitness, model, etc.) |
156
+ | `locale` | yes | Language, languages, locale, calendar, numbering system, timeZone, formatted samples |
157
+ | `screen` | yes | Screen width/height, colorDepth, orientation, DPR, media queries (color-gamut, reduced-motion, contrast, forced-colors) |
158
+ | `hardware` | yes | `navigator.hardwareConcurrency`, `deviceMemory`, platform, maxTouchPoints |
159
+ | `storage` | yes | localStorage, sessionStorage, IndexedDB, Web SQL availability (write-read-delete probe) |
160
+ | `fonts` | yes | Installed font detection via hidden DOM span measurement (10 fonts, 3 generic families) |
161
+ | `canvas` | yes | Canvas 2D rendering pattern → `toDataURL()` (GPU+driver fingerprint) |
162
+ | `webgl` | yes | WebGL renderer/vendor (unmasked), version, extensions, max texture/viewport |
163
+ | `audio` | no | OfflineAudioContext oscillator+compressor numeric signature |
164
+
165
+ ### Extended tier (9 signals, requires `extended: true`)
166
+
167
+ | Signal | Sync | Description |
168
+ |--------|------|-------------|
169
+ | `mediaDevices` | no | Media device counts by kind (audioinput, audiooutput, videoinput) — no labels/IDs |
170
+ | `permissions` | no | Permissions API state for geolocation, notifications, camera, microphone |
171
+ | `webrtc` | no | RTCPeerConnection SDP codec/extmap capability hash |
172
+ | `frameInfo` | yes | Iframe count, source domains, top-level window check |
173
+ | `networkInfo` | yes | NetworkInformation API: effectiveType, downlink, RTT, saveData |
174
+ | `paymentSupport` | yes | `window.PaymentRequest` availability |
175
+ | `referrerInfo` | yes | `document.referrer` |
176
+ | `navigationInfo` | yes | PerformanceNavigationTiming.type (navigate/reload/back_forward/prerender) |
177
+ | `riskSignals` | no | Bot/headless detection: webdriver, headless UA, missing chrome object, zero dimensions, empty plugins, UA-CH consistency, notification permission, time to capture |
178
+
179
+ Complete per-signal documentation with value shapes, APIs used, and edge cases: **[docs/signals.md](docs/signals.md)**
180
+
181
+ ---
182
+
183
+ ## Anti-spoof heuristics (6 anomalies + 2 automation hints)
184
+
185
+ | Code | What it detects |
186
+ |------|----------------|
187
+ | `navigator_webdriver` | `navigator.webdriver` is truthy |
188
+ | `headless_user_agent` | UA contains `"headless"` |
189
+ | `ua_platform_mismatch` | `navigator.platform` contradicts UA-CH platform |
190
+ | `touch_claim_without_touch_points` | iPhone UA but `maxTouchPoints` is 0 |
191
+ | `mobile_ua_desktop_screen` | Android UA with screen width >= 1600px |
192
+ | `windows_ua_mac_platform` | Windows UA but platform reports macOS |
193
+ | `implausible_device_memory` | `deviceMemory` < 0.25 or > 64 GB |
194
+ | `tiny_screen_geometry` | Screen width and height both < 200px |
195
+
196
+ ---
197
+
198
+ ## Confidence scoring
199
+
200
+ Each signal has a fixed weight (0.2–1.0). Canvas, WebGL, fonts, and audio carry the highest weight. Status affects points earned: `ok` = full weight, `unsupported`/`blocked` = 35%, `timeout` = 15%, `error` = 0.
201
+
202
+ The base ratio is blended with the anti-spoof score (65% base / 35% anti-spoof). Full details in [docs/api-reference.md](docs/api-reference.md).
203
+
204
+ ---
205
+
206
+ ## CDN
207
+
208
+ CDN files are available from jsDelivr (automatic from npm publish) and self-hosted.
209
+
210
+ | File | Format |
211
+ |------|--------|
212
+ | `zp.dfp.js` | IIFE (unminified) |
213
+ | `zp.dfp.min.js` | IIFE (minified) |
214
+ | `zp.dfp.obf.js` | IIFE (minified + obfuscated) |
215
+ | `zp.dfp.esm.js` | ESM for `import()` |
216
+ | `zp.dfp.manifest.json` | SRI integrity hashes |
217
+
218
+ ```html
219
+ <!-- jsDelivr -->
220
+ <script src="https://cdn.jsdelivr.net/npm/@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.min.js"></script>
221
+
222
+ <!-- Self-hosted -->
223
+ <script src="https://cdn.zenithpayments.support/devicefp/latest/zp.dfp.min.js"></script>
224
+
225
+ <script>
226
+ const client = await window.DeviceFP.createFingerprintClient({ timeoutMs: 1500 });
227
+ const result = await client.collect();
228
+ console.log(window.DeviceFP.LIBRARY_VERSION); // "0.1.0"
229
+ </script>
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Package layout
235
+
236
+ ```text
237
+ dist/
238
+ ├── npm/
239
+ │ ├── index.mjs # ESM entry (browser, ES2022)
240
+ │ ├── index.cjs # CJS entry (Node 22+)
241
+ │ └── index.d.ts # public types
242
+ └── cdn/
243
+ ├── zp.dfp.js # IIFE (unminified)
244
+ ├── zp.dfp.min.js # IIFE (minified)
245
+ ├── zp.dfp.obf.js # IIFE (minified + obfuscated)
246
+ ├── zp.dfp.esm.js # ESM for import()
247
+ └── zp.dfp.manifest.json
248
+ ```
249
+
250
+ ---
106
251
 
107
252
  ## Build and test
108
253
 
@@ -113,22 +258,32 @@ bun test # 84 tests, 95% line coverage
113
258
  bun run smoke # validates all dist artifacts
114
259
  ```
115
260
 
116
- The build pipeline runs esbuild to produce `dist/npm/` and `dist/cdn/`, then copies CDN files to the local CDN server directory for self-hosting.
261
+ ---
117
262
 
118
- ## Limitations
263
+ ## Docs
119
264
 
120
- - Browser-side fingerprints are probabilistic, not identity proofs.
121
- - The default font probe is intentionally bounded and conservative.
122
- - The anti-spoof report is heuristic and should be combined with server-side context for real fraud or login-risk decisions.
265
+ | Document | Covers |
266
+ |----------|--------|
267
+ | [docs/api-reference.md](docs/api-reference.md) | Complete API: types, methods, events, options, constants, confidence, anti-spoof, upload, CDN |
268
+ | [docs/signals.md](docs/signals.md) | All 19 signals: value shapes, APIs used, sync support, edge cases |
269
+ | [docs/architecture.md](docs/architecture.md) | High-level flow and module map |
270
+ | [docs/privacy.md](docs/privacy.md) | Privacy-by-design notes |
271
+ | [docs/testing.md](docs/testing.md) | Test strategy and coverage intent |
123
272
 
124
- ## Layout
273
+ ---
125
274
 
126
- ```text
127
- zp-devicefp/
128
- ├── src/ # 19 signal collectors, core modules
129
- ├── tests/ # bun test (unit + integration)
130
- ├── docs/ # architecture, privacy, testing docs
131
- ├── .scripts/ # esbuild build pipeline
132
- ├── scripts/ # smoke tests
133
- └── dist/ # generated (npm/ + cdn/)
134
- ```
275
+ ## Design choices
276
+
277
+ - **Async first** high-value collectors (audio, UA hints, media devices, permissions, WebRTC, risk signals) run in `collect()`.
278
+ - **Explainable output** every signal includes a status and duration. No silent failures.
279
+ - **No dangerous defaults** — no battery, sensors, arbitrary installed-app probes, or local IP harvesting.
280
+ - **Anti-spoofing** focuses on coherence checks and automation hints, not "secret" client tricks.
281
+ - **Upload is backend-neutral and optional** — you control where (if anywhere) fingerprints are sent.
282
+
283
+ ---
284
+
285
+ ## Limitations
286
+
287
+ - Browser-side fingerprints are probabilistic, not identity proofs.
288
+ - Font detection is bounded to 10 common fonts and is intentionally conservative.
289
+ - The anti-spoof report is heuristic — combine with server-side context for fraud or login-risk decisions.
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@ianmenethil/zp-devicefp",
3
- "version": "2.2.0",
4
- "tag": "@ianmenethil/zp-devicefp@2.2.0",
5
- "npmIdentifier": "ianmenethil-zp-devicefp-2.2.0",
6
- "manifestPath": "ianmenethil-zp-devicefp-2.2.0-manifest.json",
7
- "buildTimestamp": "2026-05-14T12:23:02.107Z",
3
+ "version": "0.1.0-alpha.0",
4
+ "tag": "@ianmenethil/zp-devicefp@0.1.0-alpha.0",
5
+ "npmIdentifier": "ianmenethil-zp-devicefp-0.1.0-alpha.0",
6
+ "manifestPath": "ianmenethil-zp-devicefp-0.1.0-alpha.0-manifest.json",
7
+ "buildTimestamp": "2026-05-14T13:29:16.204Z",
8
8
  "files": [
9
9
  {
10
10
  "file": "zp.dfp.js",
@@ -20,9 +20,9 @@
20
20
  },
21
21
  {
22
22
  "file": "zp.dfp.obf.js",
23
- "hash": "1fba7385337c7f16743fbdd87310b904bd7646d49bc69a2a3e31613bbc74a874d7a6cf3483330bb1d9f49f798fd44c8f",
24
- "hashSri": "sha384-H7pzhTN8fxZ0P73YcxC5BL12RtSbxpoqPjFhO7x0qHTXps80gzMLsdn0n3mP1EyP",
25
- "size": 217962
23
+ "hash": "f4307cffbeaafa52f46a56c3ad63d07789468181d80350d2e585520f04d4b456da4c913d809f6155de79bcb00260e90e",
24
+ "hashSri": "sha384-9DB8/76q+lL0albDrWPQd4lGgYHYA1DS5YVSDwTUtFbaTJE9gJ9hVd55vLACYOkO",
25
+ "size": 229236
26
26
  },
27
27
  {
28
28
  "file": "zp.dfp.esm.js",