npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@certilayer/web

v1.3.6

Published

CertiLayer Web SDK — passive behavioral biometrics < 20KB

Readme

@certilayer/web

Passive behavioral biometrics for the browser — under 20KB, zero PII.

CertiLayer's Web SDK silently observes how a real visitor types, moves the mouse, scrolls, and navigates — then streams a compact behavioral feature vector to your CertiLayer account so you can tell humans from bots without a CAPTCHA, a fingerprinting library, or collecting a single byte of personal data.

npm version License


Why CertiLayer

  • No PII, by design. Typed content is never captured. The keyboard collector records timing only (typing rhythm), never which keys were pressed. Password, email, phone and card fields are ignored entirely. The rest is behavioral patterns (mouse velocity, scroll depth, navigation timing).
  • Passive. No challenges, no puzzles, no friction for real users.
  • Tiny footprint. Under 20KB, pure DOM/browser APIs, no runtime dependencies (tslib only).
  • Framework-agnostic. Drop in via <script> tag or install from npm — works the same either way.

Installation

npm

npm install @certilayer/web
import { init } from '@certilayer/web'

init({ apiKey: 'certilayer_live_pk_xxx' })

Script tag

<script src="https://unpkg.com/@certilayer/web/dist/certilayer.min.js"></script>
<script>
  CertiLayer.init({ apiKey: 'certilayer_live_pk_xxx' })
</script>

The SDK auto-starts capture on init() — there's no separate call needed to begin collecting signals.


⚠️ Public key only — never your secret key

init() must only ever receive your public key (certilayer_live_pk_... or certilayer_test_pk_...). This SDK runs in the visitor's browser, and anyone can view-source it — your secret key (certilayer_live_sk_...) is for server-side use only (webhooks, server-to-server calls) and must never be embedded in frontend code.

The SDK enforces this at runtime: calling init() with a key that starts with certilayer_live_sk_ throws immediately instead of silently leaking the key to the browser.


Next.js (App Router)

Run init() in a client component only, never during SSR. Keep the instance and call getSessionId() on it (the global CertiLayer object exists only with the script tag).

'use client'
import { useEffect } from 'react'
import { init, showBadge } from '@certilayer/web'

export function CertiLayerProvider() {
  useEffect(() => {
    const cl = init({ apiKey: process.env.NEXT_PUBLIC_CERTILAYER_PK! })
    showBadge({ position: 'bottom-right' })
    return () => { void cl.stop() }
  }, [])
  return null
}

Configuration

interface CertiLayerConfig {
  apiKey: string          // required — your public key
  endpoint?: string        // default: 'https://ingest.certilayer.net'
  flushInterval?: number   // default: 5000 (ms between automatic batch sends)
  batchSize?: number       // default: 50 (max signals per batch)
  debug?: boolean          // default: false — enables console logging
}

| Option | Default | Notes | | --------------- | --------------------------------- | ------------------------------------------------------------ | | apiKey | — | Required. Must start with certilayer_. | | endpoint | https://ingest.certilayer.net | Override only for self-hosted or on-prem gateways. | | flushInterval | 5000 | How often buffered signals are sent, in milliseconds. | | batchSize | 50 | Signals are also flushed early if the buffer fills up. | | debug | false | Logs session lifecycle events to the console. |


API reference

init(config): CertiLayerSDK

Initializes the SDK, generates or resumes the session ID, and auto-starts capture. Returns an SDK instance.

const cl = init({ apiKey: 'certilayer_live_pk_xxx' })

sdk.start(): void

Manually (re)starts capture if it isn't already running. Not usually needed — init() already calls this.

sdk.stop(): Promise<void>

Stops all capture listeners, flushes any remaining buffered signals, and removes unload listeners.

await cl.stop()

sdk.flush(): Promise<void>

Forces an immediate send of whatever signals are currently buffered, without waiting for the next flushInterval tick.

sdk.getSessionId(): string

Returns the current session ID (persisted in sessionStorage across page loads within the same tab/session).


Trust badge

An optional "Secured by CertiLayer" badge you can drop onto any page to show visitors their session is protected.

import { init, showBadge, hideBadge } from '@certilayer/web'

init({ apiKey: 'certilayer_live_pk_xxx' })
showBadge({ position: 'bottom-right', theme: 'dark' })

// later, if needed:
hideBadge()
interface BadgeOptions {
  position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' // default: 'bottom-right'
  theme?: 'dark' | 'light'    // default: 'dark'
  showPulse?: boolean          // default: true — animated "live" indicator
  zIndex?: number              // default: 999999
}

The badge is pure DOM (~800 bytes), has no external dependencies, and opens certilayer.net in a new tab when clicked.


What gets captured

Four signal types, all behavioral — never content:

| Signal | What's recorded | | ------------ | ---------------------------------------------------------------------- | | keyboard | Inter-key interval and dwell time per keystroke, plus a boolean for Backspace (error-correction rate). Key identity is never recorded. | | mouse | Position, velocity, and click duration | | scroll | Delta, velocity, and page-depth ratio | | navigation | SPA route transitions and time spent per route |

Signals are batched, converted into a 34-dimension feature vector client-side, and sent to your CertiLayer ingestion endpoint — raw signals are retained only for audit purposes. Keyboard signals contain timing only; see Privacy.


Privacy

  • No key identity. event.key and event.code are never placed in any payload. Each keystroke yields only dwell time, inter-key interval, and a Backspace flag.
  • Sensitive fields are skipped entirely: input[type=password], type=email, type=tel, autocomplete=cc-*, autocomplete=one-time-code.
  • Opt any element out by adding data-certilayer-ignore to it or to an ancestor:
  <form data-certilayer-ignore> ... </form>
  • Upgrade note: versions before 1.3.5 sent key values from regular text fields. Upgrade to 1.3.6 or later.

Browser support

Targets modern evergreen browsers (Chrome, Firefox, Safari, Edge). Uses sessionStorage, fetch/sendBeacon, and standard DOM event listeners — no polyfills are bundled. If sessionStorage is unavailable (e.g. strict incognito settings), the SDK falls back to an in-memory session ID for that page load.


License

© CertiLayer. All rights reserved.

Support

certilayer-web-sdk