Skip to content

Latest commit

 

History

History
373 lines (302 loc) · 17.8 KB

File metadata and controls

373 lines (302 loc) · 17.8 KB

API reference

For task-oriented snippets, see Examples.

Imports and runtime boundary

libcreep is ESM-only. It provides named exports and a frozen default export:

import LibCreep, { collect, getBrowserCapabilities, load } from 'libcreep';

LibCreep.load === load; // true

Named exports are recommended because they make the API and bundle usage explicit. Importing the module does not read browser globals, so it is safe in SSR and Node-based build tools. Fingerprint collection itself requires a browser window.

The default object contains load, collect, hashComponents, componentsToDebugString, getBrowserCapabilities, isBrowserEnvironment, isFingerprintingSupported, algorithmVersion, and libraryVersion.

load(options?)

function load(options?: LoadOptions): Promise<FingerprintCollector>;

load() verifies the minimum browser APIs, waits until document.body exists, dynamically loads the detector runtime, and returns a reusable collector. Its load-specific signal and timeoutMs options cover that readiness/import phase; they do not apply to later calls on the returned collector.

import { load } from 'libcreep';

const collector = await load();
const result = await collector.collect();

The collector exposes algorithmVersion, libraryVersion, and collect(options?). Reuse a collector when an application collects more than once.

All collections in one loaded module instance are serialized, including calls made through different collectors and the one-shot collect() function. Some detectors share temporary DOM and module state, so only one collection runs at a time. A result's durationMs measures its active collection, not time spent waiting in this queue.

Load options

interface LoadOptions {
  debug?: boolean;
  signal?: AbortSignal;
  timeoutMs?: number;
  worker?: {
    strategy?: 'auto' | 'dedicated-only' | 'service-first';
    url?: string | URL;
  };
}
Option Default Behavior
debug false After each successful collection, logs package/algorithm versions and formatted core component results. Development and test transforms also print detector timing tables; the production package excludes that profiler. Failed collections are not logged.
signal none Cancels document readiness and dynamic runtime loading. It does not govern later collector.collect() calls.
timeoutMs none Applies a 0 to 2,147,483,647 millisecond deadline to load() only. Invalid values reject with TypeError.
worker.url ./worker.js beside the package entry Overrides the module-worker asset location. Use this when a bundler or CDN relocates package assets.
worker.strategy 'auto' Controls the worker-scope fallback order described below.

Worker strategies are:

  • 'auto': try a shared module worker, then a dedicated module worker.
  • 'dedicated-only': use only a dedicated module worker.
  • 'service-first': try a temporary service worker, then shared and dedicated module workers.

Service-worker-first mode is opt-in because registration changes origin state temporarily. It creates a unique scope and unregisters only the registration it created. An unknown strategy causes load() to reject with a TypeError.

A worker is not a minimum requirement for the rest of the fingerprint. If all selected worker transports fail, components.workerScope is unsupported and collection continues. See Browser runtime and workers for hosting, CSP, secure-context, and fallback details.

collect(options?)

function collect(options?: CollectOptions): Promise<FingerprintResult>;

interface CollectOptions {
  debug?: boolean;
  includeWebRtc?: boolean;
  signal?: AbortSignal;
  timeoutMs?: number;
  worker?: WorkerOptions;
}

collect() is the one-shot convenience API. It separates the load options, creates a collector, and performs one collection:

import { collect } from 'libcreep';

const result = await collect({
  debug: true,
  timeoutMs: 10_000,
  worker: { strategy: 'auto' },
});

collector.collect(options?)

interface CollectionOptions {
  includeWebRtc?: boolean;
  signal?: AbortSignal;
  timeoutMs?: number;
}
Option Default Behavior
includeWebRtc false Enables SDP, STUN, ICE-candidate, and address collection. This can disclose network information and contact external STUN servers.
signal none Rejects a queued or active call when aborted. An active abort also triggers cleanup for worker, WebRTC, audio, and temporary DOM resources.
timeoutMs none Aborts the entire operation after 0 to 2,147,483,647 milliseconds. Invalid values reject immediately with TypeError.

For collector.collect(), the deadline includes its queue wait and active detectors. For the one-shot collect() function, it additionally includes waiting for document.body and dynamically loading the runtime. A timeout rejects with a DOMException whose name is TimeoutError. A signal rejects with signal.reason, or an AbortError DOM exception when no explicit reason is available.

const controller = new AbortController();

try {
  const result = await collector.collect({
    signal: controller.signal,
    timeoutMs: 5_000,
  });
  console.log(result.visitorId);
} catch (error) {
  if (error instanceof DOMException && error.name === 'TimeoutError') {
    console.warn('Fingerprint collection timed out');
  } else {
    throw error;
  }
}

Failure model

Environment and orchestration failures reject the collection promise. Examples include calling load() outside a browser, missing Web Crypto or TextEncoder, cancellation, timeout, or a failure while calculating final hashes.

Detector-level failures are isolated. Every core and auxiliary entry uses the same discriminated union:

type ComponentResult<T> =
  | { status: 'fulfilled'; value: T; durationMs: number }
  | { status: 'unsupported'; durationMs: number }
  | { status: 'rejected'; error: ComponentError; durationMs: number }
  | { status: 'skipped'; durationMs: number };

interface ComponentError {
  message: string;
  name: string;
  stack?: string;
}
  • fulfilled contains a successful value.
  • unsupported means the detector returned no usable value, commonly because its browser API or worker was unavailable.
  • rejected captures the detector's error without discarding other results.
  • skipped means policy intentionally did not run an opt-in detector. WebRTC has this status by default.

Check status before accessing value or error:

const navigatorResult = result.components.navigator;

switch (navigatorResult.status) {
  case 'fulfilled':
    console.log(navigatorResult.value.platform);
    break;
  case 'rejected':
    console.warn(navigatorResult.error.name, navigatorResult.error.message);
    break;
  case 'unsupported':
  case 'skipped':
    console.log(`Navigator result: ${navigatorResult.status}`);
}

FingerprintResult

Field Meaning
visitorId SHA-256 of an envelope containing algorithmVersion and the hardened stable component set. This is the preferred whole-fingerprint identifier.
rawVisitorId Canonical SHA-256 of all core component statuses and fulfilled values. It intentionally includes unstable, high-entropy signals.
fuzzyHash A 64-character Creep.js similarity signature. It is not a cryptographic identifier.
bot Client-side bot-rule bit mask and the first matching rule, when any.
components A result entry for every name in CORE_COMPONENT_NAMES.
values Successful core values only, keyed by component name. Each value includes its component $hash.
stableComponents A detached snapshot of the exact trust-filtered values hashed into visitorId; untrusted or unavailable groups may be absent or undefined.
focusedHashes SHA-256 hashes for available narrower signal groups, such as Canvas image/text, WebGL, DOM rectangles, CSS, media types, and device/timezone data.
auxiliary Results for mediaCapabilities, mediaDevices, status, and webRtc. Auxiliary values are not included in visitorId or rawVisitorId.
durationMs Active collection duration in milliseconds. Detector durations can overlap because many probes run concurrently.
algorithmVersion Version of the stable-ID inputs and canonical fingerprint algorithm.
libraryVersion Installed npm package version.

For fulfilled core entries, components[name].value and values[name] may reference the same per-result snapshot; treat result data as immutable. stableComponents is independently snapshotted, so mutating raw values cannot change the data represented by the already-computed visitorId. Stable signal groups omit the per-component $hash; those enrichment hashes are outputs, not inputs to the stable identifier.

visitorId is hardened against known unstable or untrusted signals, but it is not guaranteed to remain unchanged. Browser upgrades, privacy modes, extensions, hardware changes, and detector availability can all affect it. Persist algorithmVersion with identifiers so an application can make upgrade decisions explicitly. Neither identifier should be used as authentication or identity proof.

BOT_RULE_NAMES defines the eight-character bot.botMask order. bot.firstMatchedRule is the first set bit in that order. The excessiveLooseFingerprints and crowdBlendingScoreIsLow rules require server-side history or crowd data and therefore remain unset in this client-only package.

Bit Rule Client-side meaning
0 liedWorkerScope The worker scope reported a detected lie.
1 liedPlatformVersion Worker UA platform data conflicts with font-derived OS information.
2 functionToStringHasProxy Function.prototype.toString exposes invalid or proxy-like behavior.
3 outsideFeaturesVersion Engine features fall outside the detected version range.
4 extremeLieCount Severe stealth signals, an unusual font/OS pairing, or more than 100 recorded lies were found.
5 excessiveLooseFingerprints Reserved for server-side history; always 0 here.
6 workerScopeIsBlocked No usable worker scope was collected.
7 crowdBlendingScoreIsLow Reserved for server-side crowd analysis; always 0 here.

The stable component groups are navigator, screen, workerScope, media, canvas2d, canvasWebgl, cssMedia, css, timezone, offlineAudioContext, and fonts. Each is independently filtered, so do not assume that all keys hold usable values.

Focused hash fields are:

interface FocusedHashes {
  canvas2dEmoji?: string;
  canvas2dImage?: string;
  canvas2dPaint?: string;
  canvas2dText?: string;
  canvasWebglImage?: string;
  canvasWebglParameters?: string;
  clientRects?: string;
  cssComputedStyle?: string;
  cssSystem?: string;
  deviceAndTimezone?: string;
  mediaMimeTypes?: string;
}

A focused hash is present only when its underlying signal is non-null and usable. A fulfilled but partial component does not receive hashes for optional fields it did not actually report.

hashComponents(components)

function hashComponents(components: HashableComponents): Promise<string>;

Hashes a custom component selection with the same canonical format used by rawVisitorId:

import { hashComponents } from 'libcreep';

const customId = await hashComponents({
  canvas2d: result.components.canvas2d,
  canvasWebgl: result.components.canvasWebgl,
  navigator: result.components.navigator,
});

Component names and plain-object keys are sorted by UTF-16 code units, without locale-sensitive collation. Fulfilled values are hashed; non-fulfilled statuses are hashed by status. durationMs, rejected error text, and stacks are ignored, so timing and browser-specific wording do not destabilize the hash.

Fulfilled values may contain plain objects (including objects from another window realm and objects whose keys use libcreep's reserved canonical $type tag), arrays (including sparse arrays), JSON primitives, undefined, bigint, non-finite numbers, negative zero, and genuine cross-realm Error objects. Functions, symbols, cycles, symbol-keyed properties, accessors, non-enumerable or extra array properties, and non-plain objects such as Date are rejected instead of being silently converted to lossy JSON. SHA-256 requires Web Crypto.

componentsToDebugString(components)

Returns indented JSON for diagnostics. Unlike hashComponents(), this output retains statuses, durations, and captured error details. It also serializes Error, bigint, negative zero, and non-finite numbers without the usual JSON loss. It represents undefined object fields and array entries as { "$type": "undefined" } instead of dropping them or converting them to null.

import { componentsToDebugString } from 'libcreep';

console.debug(componentsToDebugString(result.components));

Debug output can contain detailed fingerprint and environment information. Redact it before sharing and do not log it by default in production.

Environment helpers

interface BrowserCapabilities {
  hasDedicatedWorker: boolean;
  hasServiceWorker: boolean;
  hasSharedWorker: boolean;
  hasTextEncoder: boolean;
  hasWebCrypto: boolean;
  isBrowser: boolean;
  isSecureContext: boolean;
}
  • isBrowserEnvironment() checks for a browser window and document only.
  • isFingerprintingSupported() checks the minimum collection requirements: a browser window, TextEncoder, and Web Crypto SHA-256.
  • getBrowserCapabilities() reports those requirements plus secure-context and worker API availability. Capability flags report API presence, not a guarantee that CSP, permissions, origin rules, or asset loading will allow a particular detector to succeed.

A secure context is reported but is not itself a minimum check; it matters for service workers and several optional browser APIs.

Versions, manifests, and types

  • LIBRARY_VERSION is the installed package version.
  • ALGORITHM_VERSION is the fingerprint/canonical-hash version.
  • CORE_COMPONENT_NAMES lists the 25 always-attempted core components.
  • DEFAULT_AUXILIARY_COMPONENT_NAMES lists the three auxiliary probes run by default.
  • OPT_IN_AUXILIARY_COMPONENT_NAMES lists webRtc.
  • AUXILIARY_COMPONENT_NAMES combines both auxiliary manifests.
  • BOT_RULE_NAMES defines the bot-mask bit order.

The package exports its public TypeScript interfaces and type aliases, including FingerprintResult, FingerprintCollector, component value types, component result maps, options, capability types, bot types, and HashableComponents. The generated declarations shipped in dist are the authoritative field-level reference for detector values.