# Getting started

> From the source code to a first result in your page.

Source: https://whiskerjs.com/docs/getting-started/ (last updated 2026-10-26)

> **In plain words:**
>
> WhiskerJS is a small script you run in your web page. You call `collect()`, wait a moment, and get back one JSON object describing the browser and device: 46 measurements, two identifiers and a list of warning flags. You then send that object to your own server with a request you already make. The library is not on npm yet, so you build it from source first.

This page takes you from the source code to a first result in a page. It assumes you know how to run a JavaScript build and serve a web page.

## 1. Build the library

WhiskerJS is not on npm yet, and its source repository opens with the first public release. With the source in a folder named `whiskerjs`:

```sh
cd whiskerjs
pnpm install   # dev dependencies only: TypeScript and Node types
pnpm build     # writes dist/: ES2020 modules plus .d.ts type files
pnpm test      # optional: runs the test suite with node:test
```

Requirements: Node 18 or newer (tested on Node 22) and pnpm 10 (`corepack enable` gives you the pinned version).

## 2. Add it to your project

Pick one of these:

- **As a package.** In the WhiskerJS folder run `pnpm pack`. It writes `whiskerjs-0.1.0.tgz` with `dist/`, `LICENSE`, `NOTICE` and `THIRD_PARTY_NOTICES.md`. In your project: `npm install ../whiskerjs/whiskerjs-0.1.0.tgz` (or the pnpm or yarn equivalent). Your bundler then resolves `import { load } from 'whiskerjs'`.
- **As plain files.** Copy `dist/*.js` and `dist/collectors/*.js` (keep the folder layout) to your static files and load `index.js` as a module: `<script type="module">import { load } from '/vendor/whiskerjs/index.js';</script>`. This is how the live demo on whiskerjs.com runs it.

There are no runtime dependencies and no CDN.

## 3. Collect once

```ts
import { load } from 'whiskerjs';

const agent = await load();                 // cheap: no work happens yet
const result = await agent.collect();       // at most 5 s by default (1 s per component)

console.log(result.visitorId);              // '3feb3ead31e6b520459ad33ebc58f2d7'
console.log(result.integrityFlags);         // [] or e.g. ['automation_webdriver']
```

`collect()` never throws and never rejects, whatever the browser does. Collect in the browser, after the page has a `<body>`: several probes add hidden elements to the body for a moment, and without one they report `unavailable`.

The page must be a **secure context** (https, or `http://localhost` while developing). Hashing uses `crypto.subtle`, which browsers only offer there. Without it `visitorId` and `structuredId` are `null` and every large component reports `unavailable` with ruleId `no_digest`.

## 4. Read the result

```ts
for (const [name, c] of Object.entries(result.components)) {
  if (c.status === 'ok') console.log(name, c.value, c.hashed ? '(digest)' : '');
  else console.log(name, c.status, c.ruleId);
}
```

- `visitorId`: one hash over the 37 stable components. Equal ids mean the same browser setup, as far as these signals can tell.
- `structuredId`: seven short hashes, one per signal group, so you can see *which part* of a device changed. See [Identifiers](https://whiskerjs.com/docs/identifiers/).
- `components`: all 46, always present, each with a status. See [Signals](https://whiskerjs.com/docs/signals/) and [How it works](https://whiskerjs.com/docs/how-it-works/).
- `integrityFlags`: names of checks that saw signs of automation or tampering. A hint for your server, not a verdict. See [Integrity checks](https://whiskerjs.com/docs/integrity/).
- `signalsVersion` and `idVersion`: store them with every id; ids are only comparable within the same versions.

## 5. Send it to your own server

WhiskerJS has no network code. Add the result to a request you already make, for example the login form post:

```ts
const result = await agent.collect({
  timeoutMs: 3000,
  binding: await getNonceFromMyServer(),  // { nonce, timestamp }, optional
});
await fetch('/api/login', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ email, password, device: result }),
});
```

The result is plain JSON, about 4 KB. Read [Integration](https://whiskerjs.com/docs/integration/) for what to check on the server, and [Anti-replay binding](https://whiskerjs.com/docs/integration/#anti-replay-binding) for the nonce.

## 6. Tell your users

Device signals can be personal data. Before you deploy, read [Privacy](https://whiskerjs.com/docs/privacy/) and add a notice to your privacy policy.

## Try it without building

The live demo at [whiskerjs.com/demo](https://whiskerjs.com/demo/) runs the real library in your browser and shows every component, both ids and the flags.
