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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@atgcentry/safe-capture-verifier

v1.0.1

Published

Identity verification camera component with TF FaceMesh, document ROI, quality gating and remote validation.

Readme

SafeCaptureVerifier React Component

SafeCaptureVerifier is a validation‑first identity capture widget for React applications.
It combines live camera capture, face landmarks + micro‑liveness (MediaPipe FaceMesh), and remote image analysis into a single, easy‑to‑embed component.

The component is designed for KYC / onboarding / identity flows where:

  • A live face is required inside the frame
  • The user holds an identity document in a guide box
  • The app performs local quality checks (exposure, blur, edge density)
  • A remote service performs SafeSearch + document detection
  • Capture is only allowed when the face is detected and “armed”

Features

  • ✔️ Live camera stream with device switching and optional torch toggle
  • ✔️ MediaPipe FaceMesh via @tensorflow-models/face-landmarks-detection
    • Face bounding box and landmarks overlay
    • Sticky face gate: once a good face is seen, capture stays enabled while any face remains
    • Simple micro‑liveness heuristic (movement + size change)
  • ✔️ Static document guide (dotted white frame) for document positioning
  • ✔️ Local image quality gates before any upload:
    • Exposure
    • Blur (Laplacian variance)
    • Edge density
  • ✔️ Remote validation via endpointUrl + X_API_KEY header
  • ✔️ SafeSearch parsing, face & document confidence, and telemetry hook
  • ✔️ EN / FR built‑in translations, optional overrides, and future‑proof for additional locales
  • ✔️ Responsive layout: mobile & desktop
  • ✔️ Optional auto‑capture when face is stable & live
  • ✔️ Optional consent checkbox before enabling capture

Installation

Make sure you have React and TypeScript set up.

# Core dependencies
yarn add react react-dom

# TensorFlow / FaceMesh stack
yarn add @tensorflow-models/face-landmarks-detection @mediapipe/face_mesh

# Icons (used by this component)
yarn add lucide-react

If you use npm:

npm install \
  react react-dom \
  @tensorflow-models/face-landmarks-detection @mediapipe/face_mesh \
  lucide-react

Note: The component itself is just a .tsx file; you can drop it into your project (e.g. src/components/SafeCaptureVerifier.tsx).


Basic Usage

import React, { useCallback } from "react";
import SafeCaptureVerifier, {
  ValidationPayload,
  TelemetryPayload,
} from "./SafeCaptureVerifier";

export const KycPage: React.FC = () => {
  const handleValidated = useCallback(
    (isSafe: boolean, analysis: ValidationPayload) => {
      console.log("Validated?", isSafe, analysis);
      // e.g. update local state or show summary to the user
    },
    []
  );

  const handleConfirm = useCallback(
    (file: File, analysis: ValidationPayload) => {
      // This is called only when:
      // - the image is marked as `safe`
      // - there is a valid analysis payload
      // The parent can then upload `file` to its own backend.
      console.log("Confirmed snapshot:", file, analysis);
    },
    []
  );

  const handleFailure = useCallback((err: unknown) => {
    console.error("Validation error:", err);
  }, []);

  const handleTelemetry = useCallback((payload: TelemetryPayload) => {
    console.debug("Telemetry:", payload);
  }, []);

  return (
    <SafeCaptureVerifier
      apiKey={import.meta.env.VITE_CENTRY_API_KEY}
      endpointUrl="https://api.centry.uk/v1/ocr/api/analyze"
      requestId="signup-kyc-12345"
      locale="en"
      minDocConfidence={89}
      countdownSeconds={5}
      autoCapture
      onValidated={handleValidated}
      onConfirm={handleConfirm}
      onFailure={handleFailure}
      onTelemetry={handleTelemetry}
      brandImageUrl="/assets/brand-logo.svg"
    />
  );
};

At a minimum you must provide:

  • apiKey – the API key sent as X_API_KEY header to your backend / analysis service.

If you don’t pass endpointUrl, it defaults to: https://api.centry.uk/v1/ocr/api/analyze


Component API

SafeCaptureVerifier Props

export type SafeSearchLikelihood =
  | "UNKNOWN"
  | "VERY_UNLIKELY"
  | "UNLIKELY"
  | "POSSIBLE"
  | "LIKELY"
  | "VERY_LIKELY";

export interface SafeSearchAnnotation {
  Adult: SafeSearchLikelihood;
  Medical: SafeSearchLikelihood;
  Racy: SafeSearchLikelihood;
  Spoof: SafeSearchLikelihood;
  Violence: SafeSearchLikelihood;
}

export interface ValidationPayload {
  SafeSearch: string;             // Raw SafeSearch string (parsed internally)
  Faces?: unknown[];              // Face detections from backend (if any)
  Labels: Record<string, number>; // Label -> score map
}

export interface TelemetryPayload {
  requestId?: string | number;
  faceDetected: boolean;
  faceQualityPct: number;         // 0..100
  livenessScore: number;
  localChecks: {
    exposureOk: boolean;
    blurOk: boolean;
    edgesOk: boolean;
  };
  remote?: {
    safe: boolean;
    docConfidence: number;
    safeSearch?: SafeSearchAnnotation | null;
  };
}

Main Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | apiKey | string | required | API key sent as X_API_KEY header to the validation endpoint. | | endpointUrl | string | "https://api.centry.uk/v1/ocr/api/analyze" | Remote image analysis endpoint. Receives a FormData with image and optional title. | | requestId | string \| number | undefined | Optional identifier attached to the validation request as title. Useful for correlating backend logs. | | locale | "en" \| "fr" | "en" | Current UI language. | | labels | Partial<LabelSchema> | undefined | Override or extend the built‑in EN/FR translations. | | brandImageUrl | string | undefined | Optional logo shown above the analysis panel. | | countdownSeconds | number | 10 | Countdown duration before the snapshot is taken once the user clicks the capture button. | | minDocConfidence | number | 89 | Minimum "Identity document" label score (in %) required for the capture to be considered safe. | | acceptedLikelihoods | SafeSearchLikelihood[] | ["VERY_UNLIKELY","UNLIKELY"] | Allowed SafeSearch likelihoods for all categories. Any category outside this set fails the validation. | | autoCapture | boolean | false | If true, starts the countdown automatically when the face is stable and liveness is OK. | | autoCaptureStableFrames | number | 10 | Number of stable frames required before auto‑capture triggers. | | requireConsent | boolean | false | If true, shows a consent checkbox and keeps the capture button disabled until the user agrees. | | onConsentChange | (consented: boolean) => void | undefined | Called whenever the consent checkbox changes. | | enableTfFace | boolean | true | Enables MediaPipe FaceMesh for face detection / landmarks / liveness. | | detectionFps | number | 8 | Target detection FPS for the FaceMesh loop. | | livenessWindow | number | 12 | Number of frames in the liveness history window. | | livenessThreshold | number | 0.015 | Threshold on movement + area change; above this, liveness is considered OK. | | showOverlay | boolean | true | If true, draws the face bounding box and sparse landmarks on a canvas overlay. | | enableTorch | boolean | true | If supported by the device, shows a torch toggle button. | | docRoiInset | number | 0.12 | Creates a white dotted static guide box inset by this fraction from each border. Only visual – no document detection. |

Callback Props

| Prop | Signature | Description | |------|-----------|-------------| | onValidated | (isSafe: boolean, analysis: ValidationPayload) => void | Called after the remote validator responds, whether the snapshot is safe or not. | | onConfirm | (file: File, analysis: ValidationPayload) => void | Called when the user submits the form and the capture is considered safe (meets SafeSearch, face, and document confidence criteria). | | onFailure | (error: unknown) => void | Called when remote validation fails (network or server error). | | onTelemetry | (payload: TelemetryPayload) => void | Optional analytics hook for logging local quality metrics, liveness score, and remote result. |


i18n and Label Overrides

The component ships with a LabelSchema for English (en) and French (fr) and merges your overrides on top.

import { LabelSchema } from "./SafeCaptureVerifier";

const customLabels: Partial<LabelSchema> = {
  title: "Identity Check",
  subtitle: "Please hold your ID and align your face in the frame.",
  errors: {
    // only override what you want
    apiValidation: "Unable to validate at this time. Try again later.",
  },
};
<SafeCaptureVerifier
  apiKey={apiKey}
  locale="fr"
  labels={customLabels}
/>

Any missing field falls back to the internal defaults.


Consent‑Gated Capture

To require explicit consent before capture is possible:

<SafeCaptureVerifier
  apiKey={apiKey}
  requireConsent
  onConsentChange={(consented) => {
    console.log("User consented?", consented);
  }}
/>

Until the user checks “I understand and agree”, the capture button remains disabled.


Auto‑Capture Flow

You can enable auto‑capture so that the user does not need to click the camera button explicitly; the system will start the countdown once the face is stable and liveness is OK:

<SafeCaptureVerifier
  apiKey={apiKey}
  autoCapture
  autoCaptureStableFrames={12} // number of stable frames (~1–2 seconds)
/>

The component still shows the countdown overlay. When the countdown reaches 0, it takes a snapshot, runs local quality checks, and sends it to your backend.


Handling the Confirmed Snapshot

The component wraps everything in a <form>; it calls onConfirm only when the latest snapshot is considered safe:

const handleConfirm = (file: File, analysis: ValidationPayload) => {
  const formData = new FormData();
  formData.append("image", file);
  formData.append("safeScore", String(analysis.Labels["Identity document"] ?? 0));

  fetch("/api/kyc/upload", {
    method: "POST",
    body: formData,
  })
    .then((res) => res.json())
    .then((res) => console.log("Uploaded to our backend:", res))
    .catch((err) => console.error(err));
};

<SafeCaptureVerifier
  apiKey={apiKey}
  onConfirm={handleConfirm}
/>;

Your backend is free to store the original file; the component itself never persists it.


Telemetry and Observability

onTelemetry is useful for dashboards, debugging or offline tuning. Example:

const handleTelemetry = (p: TelemetryPayload) => {
  // Example: send to your logging / metrics service
  console.debug("[SafeCapture] telemetry", p);
};

<SafeCaptureVerifier
  apiKey={apiKey}
  onTelemetry={handleTelemetry}
/>;

You’ll get both local metrics (exposure, blur, edge density) and remote verdicts (SafeSearch, doc confidence) in a single object.


Styling & Layout

The component uses inline styles only, no external CSS. It is:

  • Responsive by design – uses a simple breakpoint hook (useBreakpoint) to switch between single‑column and two‑column layout.
  • Uses only neutral colors and clean visual hierarchy:
    • Left: live camera with face overlay and dotted document guide
    • Right: latest snapshot preview with retake button
    • Below: analysis panel and optional error alert

If you prefer, you can wrap it in your own layout container or place it inside a modal / dialog component. The width scales with its parent node.


Notes & Best Practices

  1. HTTPS & Permissions
    Browsers require HTTPS for camera access in production. Make sure your app runs over HTTPS, or use localhost in development.

  2. Device Support
    Torch support depends on the browser & device. The component fails gracefully if torch is not available.

  3. Backend Contract
    The remote endpoint is expected to return a JSON payload with either:

    • payload: ValidationPayload, or
    • ValidationPayload at the top level.

    The relevant parts are:

    • SafeSearch: a string like "Adult: VERY_UNLIKELY, Violence: UNLIKELY, ..."
    • Faces: an array (non‑empty if faces detected)
    • Labels["Identity document"]: a numeric 0–100 score
  4. Liveness
    Liveness is a lightweight heuristic, not a full anti‑spoof system. For high‑risk use cases, combine it with stronger server‑side checks.


License

This component is provided as part of a private project integration.
Adapt, extend, and integrate it into your own codebase according to your project’s license and compliance requirements.