Privacy-first, local-only barcode scanning SDK for Web, Node.js, React, iOS, and Android.
Live demo: qr-decoder-theta.vercel.app
All 11 public SDK packages are published at 2.1.0 with verified provenance and exact frozen artifact hashes. See the release notes and immutable publication record.
Choose the package for your runtime. Browser, Node, and React packages already include their required Scanly workspace dependencies.
npm install @scanly/browser
npm install @scanly/node
npm install @scanly/reactBarcode decoding remains local. Remote URL intelligence is optional and disabled by default; no barcode image or camera frame is sent to AI or reputation providers. Link Intelligence is an independent asynchronous second stage, using the existing structured payload parser.
import { UrlSafetyClient } from "@scanly/url-safety";
const safetyClient = new UrlSafetyClient({ endpoint: "/api/url-safety" });
const outcome = await router.scan(frame);
if (outcome.ok && outcome.primary.structuredPayload?.kind === "url") {
const analysis = await safetyClient.analyze(
outcome.primary.structuredPayload.fields.href as string,
{ mode: "local-only" }, // no network; choose other modes only with explicit consent
);
// Display evidence. Never automatically open the destination.
}@scanly/url-safety/server provides self-hosted SSRF-protected inspection, optional Google Web Risk / VirusTotal lookup adapters, and a vendor-neutral JSON LLM adapter. LLM is advisory evidence, not an authority. No verdict guarantees a URL is safe. See URL safety architecture, integration, privacy and configuration.
Advanced engine composition can use @scanly/core directly:
npm install @scanly/core- iOS: v2.1.0 provides a Swift Package Manager source package under
native/ios. Because the manifest is not at the repository root, check out tagv2.1.0and addnative/iosas a local package. See the iOS guide. - Android: use
scanly-sdk-2.1.0.aarfrom the v2.1.0 GitHub Release. Maven Central is not a v2.1.0 distribution channel. See the Android guide.
import { BrowserCaptureSession } from "@scanly/browser";
const scanner = new BrowserCaptureSession();
scanner.initialize();
scanner.start();
const outcome = await scanner.scanFile(file);
if (outcome.ok) console.log(outcome.results);
await scanner.dispose();Camera access uses BrowserCameraSource, requires HTTPS or localhost, and remains subject to browser permission and hardware capability.
import { createNodeCaptureRouter, loadNormalizedFrameFromPath } from "@scanly/node";
const router = createNodeCaptureRouter({ formats: ["qr_code", "data_matrix"] });
const frame = await loadNormalizedFrameFromPath("label.png");
const outcome = await router.scan(frame);
if (outcome.ok) console.log(outcome.results);
await router.dispose();"use client";
import { useScanly } from "@scanly/react";
export function Scanner() {
const { outcome, scanning, scanFile } = useScanly();
return <>
<input type="file" accept="image/*" onChange={(event) => {
const file = event.currentTarget.files?.[0];
if (file) void scanFile(file);
}} />
<output>{scanning ? "Scanning…" : outcome?.ok ? outcome.primary.rawText : ""}</output>
</>;
}import ScanlySDK
let decoder = try ScanlyDecoder()
let results = try decoder.decode(pixelBuffer, options: ScanlyOptions(
formats: [.qrCode, .dataMatrix],
maxResults: 8
))val decoder = ScanlyDecoder()
val session = ScanlyScannerSession(
decoder,
ScanlyOptions(formats = setOf(ScanlyBarcodeFormat.QR_CODE), maxResults = 8),
)
session.onResults = { outcome -> outcome.onSuccess(::renderResults) }- QR Code Model 2
- Data Matrix ECC 200
- PDF417
- Code 128
- EAN-13 and EAN-8
- UPC-A and UPC-E
The default scenario remains QR-only for compatibility. Select additional formats explicitly. The JavaScript jsQR and ZXing-JS adapters are QR-only; the default Browser and Node composition lazily loads the ZXing-C++ WASM engine for the full public format set. See symbology support.
- Image upload and realtime camera scanning
- Multiple-code results, tracking, and batch identity
- Bounded industrial recovery for difficult symbols
- Browser Worker execution and lazy, self-hosted ZXing-C++ WASM
- Framework-independent routing and engine registration
- Native iOS and Android wrappers over the shared C++ decode core
Industrial recovery is not an industrial, warehouse, or DPM certification. The dpm-experimental profile is opt-in.
Barcode decoding remains local and offline-capable after code and WASM assets are loaded. No scanning image or camera frame is uploaded. The separate URL intelligence module is disabled by default and shares decoded URLs or sanitized page evidence only after explicit opt-in. There is no built-in analytics or remote logging. Host applications remain responsible for their own telemetry, storage, service configuration and privacy disclosures.
v2.1.0 adds opt-in Link Intelligence on top of the v2.0.1 cancellation, React concurrency, camera error handling and terminal ScannerSession lifecycle guarantees. Automated browser, Node, simulator, emulator, API/ABI, security, package and artifact checks are verified. Automated coverage is not physical-device qualification.
Physical Web iOS/Android, Native iOS/Android, 30-minute and 60-minute camera soaks, and the complete hardware matrix remain POST_RELEASE_VALIDATION_PENDING under Issue #13. No all-device-verified claim is made.
Known limitations include hardware-dependent camera capabilities, difficult or occluded symbols, experimental DPM recovery, unsupported Android armeabi-v7a, no Maven Central or CocoaPods distribution for v2.1.0, and the pending physical-device matrix. URL risk verdicts are advisory observations, not safety guarantees.
mainis the Stable release branch; immutable versioned publication records identify completed releases.developis the integration branch for post-v2 maintenance and the next patch/minor line.- Feature and fix branches start from
develop. - Immutable RC/Stable tags and versioned evidence preserve qualification ancestry. Only
mainanddevelopare retained as long-lived branches.
Detailed Alpha, Beta, and RC development evidence is preserved in development history and under release/.
- Documentation index
- SDK usage
- Public API
- Worker and WASM deployment
- Platform compatibility
- Migration from v1
- Security policy
- v2.1.0 release notes
- v2.1.0 qualification manifest
- v2.1.0 publication record
- v2.1.0 additive provenance correction
- v2.1.0 checksums
- v2.1.0 SBOM
- v2.0.1 release notes
- v2.0.1 qualification manifest
- v2.0.1 checksums
- v2.0.1 SBOM
- v2.0.1 publication record
- v2.0.0 release notes
- Changelog
- v2.0.0 publication record
- Third-party notices
Published v2.0.0 artifacts, tags, and historical qualification evidence remain immutable. Each later Stable release uses its own release/stable/vX.Y.Z/ evidence directory and never replaces earlier bytes.
See CONTRIBUTING.md, SECURITY.md, and the maintenance policy.
