Frontend SDK

Capture the encrypted fingerprint envelope in the browser — with a plain script tag, or the @signalgate/nextjs package for Next.js and React apps.

Quick Start

Protect your backend endpoints in minutes with the SignalGate plugin for Claude Code.

The plugin analyzes your repo, confirms placement with you, and writes the backend half of the integration — the SDK client wiring, two log() calls and a commented-out check() gate. Review-first: it shows every diff, never commits, and never reads your key. Detection runs on the events your backend forwards; the browser capture documented on this page is the other half, which you add by hand.

/plugin marketplace add SignalGate/signalgate-claude-plugin
/plugin install signalgate@signalgate

Then, inside your backend repo:

/signalgate:integrate

Supported backend stacks: Python 3.10+, Node.js 18+, Go 1.22+, Java 17+. Plugin source & README on GitHub

Once the backend half is in, add the browser capture below — start with Install.

Install

Add a single script tag to your page. The SDK loads once per session and is cached by the browser.

Browser-side library that collects device and browser signals and returns an encrypted four-field envelope. Zero runtime dependencies. Forward the envelope to your backend exactly as you received it.

<script src="https://sdk.signalgate.ai/v0.3.3/index.global.js"></script>

Loaded once per session, cached by the browser. The IIFE bundle assigns to window.SignalGate for use by inline scripts.

Quick example

The canonical pattern: instantiate, start the SDK, and collect a payload on form submit.

<script src="https://sdk.signalgate.ai/v0.3.3/index.global.js"></script>

<script type="module">
  const fp = new SignalGate.Fingerprint({
    key: "YOUR_TENANT_KEY"
  });
  await fp.start();   // warm detectors once

  const form = document.getElementById("login-form");
  form.addEventListener("submit", async (e) => {
    e.preventDefault();
    const { payload } = await fp.get();
    // payload = { encrypted, timestamp, nonce, v: 2 }

    await fetch("/api/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email: form.email.value,
        password: form.password.value,
        signalgate: payload,   // ← four-field envelope, treat as opaque
      }),
    });
  });
</script>
Performance: first get() completes in ~200-500ms (detector warm-up). Subsequent calls within the cache window resolve in under a millisecond. Gated actions: for an action you gate with check() on your backend, capture TWO envelopes — call get() twice, one for the check and a fresh one for the post-success log — as shown in the Backend SDK live examples.

Configuration

Options passed to the Fingerprint constructor.

OptionTypeDefaultPurpose
keystringrequiredYour tenant's key, shown once in Public Keys settings.
cacheEnabledbooleantrueCache collected signals in sessionStorage so repeat calls are sub-millisecond.
cacheTTLMsnumber1800000Cache lifetime (30 min by default).

Payload shape

What fp.get() returns. The four fields are opaque to your code — pass them through to your backend unchanged.

interface EncryptedPayload {
  encrypted: string;   // base64 envelope, opaque
  timestamp: number;   // ms since epoch
  nonce: string;       // unique per call, server-deduplicated
  v: 2;                // envelope format version (the SDK emits v=2)
}

// fp.get() returns
interface FingerprintResult {
  payload: EncryptedPayload;
  raw?: FingerprintData;     // only present when debug: true
}

Your frontend code shouldn't parse, modify, or inspect the payload. The fields are designed for the backend to decrypt and evaluate.

Privacy and lifecycle

How the SDK behaves in your users' browsers.

  • End-to-end encrypted.The fingerprint data is encrypted on the client before it leaves the browser. Your app server forwards an opaque blob; only SignalGate's backend can decrypt it.
  • No third-party requests by default.The SDK doesn't phone home from the user's browser. The envelope leaves only when your code POSTs it to your own backend.
  • Cache lives in sessionStorage (when cacheEnabled) — cleared when the user closes the tab. No long-lived browser storage.
  • Degrades gracefully. The SDK degrades gracefully — if a browser blocks a specific capability, the rest of the signal payload still collects.

Continue reading