glassprint · SDKs

Two layers: clients collect, engines serve

Client SDKs (browser, iOS, Android, React Native) run in your app — they read device signals behind a fail-closed consent gate, seal the bundle, POST it to your endpoint, and return the server's typed verdict. Embeddable engines (Node, Go, Python, Java) mount inside your own backend and run the entire identification pipeline in-process — unseal, consent gate, signal processing, typed verdict, visitorId, consent ledger, and the GDPR/DPDP rights API — with no separate server to deploy.

Group 1 — MIT, open-source

Client SDKs

The client SDKs collect device signals under a fail-closed consent gate, seal the payload (ECDH-P256 + HKDF-SHA256 + AES-256-GCM), POST the sealed bundle to your ingest endpoint, and return the server's authoritative typed result. identify() always resolves and never throws — denied consent, a missing engine, or a transport failure all surface as a typed reason, not an exception. The visitorId is server-generated; the client never fabricates one. All client SDKs are MIT-licensed.

Browser · JS / TSMIT

@noidme/glassprint

ESM + CommonJS + TypeScript types. Ships a sub-30 KB gzipped bundle. Collects nine signal axes (canvas, WebGL, AudioContext, UA-CH, display, fonts, MEMS, math-FPU, WebGPU). The server adds the network tier (JA4 / TLS / HTTP2) invisible to JS. Requires a createSealedTransport to deliver to your ingest endpoint — without one, identify() returns reason: 'transport-error' and visitorId: null.

shell
npm i @noidme/glassprint
client.ts
import { createGlassprint, createSealedTransport } from '@noidme/glassprint';

const transport = createSealedTransport({
  key,                                  // AES-256-GCM CryptoKey (or fetch /_fp/pubkey for HPKE)
  deliver: async (sealed) =>
    (await fetch('https://id.acme.com/_fp/i/v1', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(sealed),
    })).json(),
});

const gp = createGlassprint(
  { kid: 'acme', endpoint: 'https://id.acme.com/_fp/i/v1' },
  transport,
);

// identify() always resolves — never throws.
const r = await gp.identify({ tag: 'checkout' });
if (r.reason === 'ok') {
  r.class;          // 'human' | 'declared_agent' | 'undeclared_automation' | 'unknown'
  r.visitorId;      // server-generated pseudonymous id (or null)
  r.suspectScore;   // additive risk score with security-fraud consent
}

Source: github.com/noidme-git/glassprint · MIT

iOS · SwiftMIT

Glassprint (SwiftPM)

Requires iOS 15+ / macOS 12+. Zero third-party dependencies — Foundation (URLSession) + CryptoKit only. Collects OS-exposed signals (screen, hardware model, OS version, locale, CPU/memory). Canvas/WebGL/AudioContext signals are not collectable from native — the bundle is lower-entropy than the browser's by design; the server may return needsAttestation: true on a too-common fingerprint. identify() is async and never throws.

Package.swift
// Package.swift
.package(url: "https://github.com/noidme-git/glassprint", from: "1.0.0")
// target dependency: .product(name: "Glassprint", package: "glassprint")
// Or: Xcode → File → Add Package Dependencies…
YourApp.swift
import Glassprint

let gp = Glassprint(
    config: GlassprintConfig(
        kid: "acme",
        endpoint: URL(string: "https://id.acme.com/_fp/i/v1")!,
        purposes: [.deviceIdentification]
    ),
    consent: StaticConsentGate(granted: [.deviceIdentification])
)

let result = await gp.identify(IdentifyOptions(tag: "checkout"))

switch result.class {
case .human:                print("human:", result.visitorId ?? "—")
case .declaredAgent:        print("declared agent:", result.declaredAgentId ?? "—")
case .undeclaredAutomation: print("automation, suspect:", result.suspectScore ?? 0)
case .unknown:              print("unknown:", result.error ?? "below confidence floor")
}

Source: github.com/noidme-git/glassprint (ios/) · MIT

Android · KotlinMIT

com.noidme:glassprint

minSdk 24 (Android 7.0). No third-party crypto or HTTP — javax.crypto / java.security + HttpURLConnection. Only runtime dep: kotlinx-coroutines. Collects Build.* signals, screen/display, locale — zero runtime permissions, no IMEI/ANDROID_ID/Advertising ID. Canvas/WebGL/AudioContext are not reachable from native; the bundle is lower-entropy by design. identify() is a suspend function and never throws.

app/build.gradle.kts
// app/build.gradle.kts
dependencies {
    implementation("com.noidme:glassprint:1.0.0")
}
MainActivity.kt
import com.noidme.glassprint.*

val gp = Glassprint.create(
    context = applicationContext,
    config = GlassprintConfig(
        kid = "acme",
        endpoint = "https://id.acme.com",  // SDK appends /_fp/…
        purposes = listOf(FpPurpose.DEVICE_IDENTIFICATION),
    ),
    consent = ConsentProvider { purpose -> myConsentStore.isGranted(purpose) },
)

lifecycleScope.launch {
    val result = gp.identify(IdentifyOptions(tag = "checkout"))
    when (result.visitorClass) {
        VisitorClass.HUMAN              -> println("human: ${result.visitorId ?: "—"}")
        VisitorClass.DECLARED_AGENT     -> println("declared agent")
        VisitorClass.UNDECLARED_AUTOMATION -> println("automation, suspect: ${result.suspectScore ?: 0.0}")
        VisitorClass.UNKNOWN            -> println("unknown: ${result.error ?: "below confidence floor"}")
    }
}

Source: github.com/noidme-git/glassprint (android/) · MIT

React Native · iOS + AndroidMIT

@noidme/glassprint-react-native

Same createGlassprint API and sealed-bundle wire contract as the browser client. Peer dependency: react-native-quick-crypto (bridges WebCrypto to iOS CryptoKit / Android javax.crypto). Without host-supplied deviceFacts, only Intl timezone and navigator.language are auto-detected — pass model, OS version, and screen geometry (e.g. from react-native-device-info) for full signal depth. Jailbreak/root/emulator detection is host-supplied; this SDK does not bundle a detector.

shell
npm i @noidme/glassprint-react-native
npm i react-native-quick-crypto   # peer: native crypto bridge (CryptoKit / javax.crypto)
cd ios && pod install             # link the native crypto pod
App.tsx
// index.js — before any glassprint call
import { install } from 'react-native-quick-crypto';
install();

// ---
import { createGlassprint } from '@noidme/glassprint-react-native';
import { Platform, Dimensions, PixelRatio } from 'react-native';

const gp = createGlassprint(
  { kid: 'acme', endpoint: 'https://id.acme.com' },
  {
    deviceFacts: {
      os: Platform.OS,
      osVersion: String(Platform.Version),
      screenWidth: Dimensions.get('window').width,
      screenHeight: Dimensions.get('window').height,
      pixelRatio: PixelRatio.get(),
    },
  },
);

const r = await gp.identify({ tag: 'checkout' });
r.class;       // 'human' | 'declared_agent' | 'undeclared_automation' | 'unknown'
r.visitorId;   // server-generated pseudonymous id (or null)

Source: github.com/noidme-git/glassprint (react-native/) · MIT

Shared wire typesMIT

@noidme/glassprint-protocol

The single source of truth for the sealed-bundle wire contract — the types that the client seals and the server opens. Node-native TypeScript, no build step. Exports OpenedBundle, SealedBundle, IngestResponse, vocabulary types (FpPurpose, VisitorClass, FpMode, IngestReason, SmartSignals), and a canonicalBytes(value) helper that produces byte-identical serialization on both client and server (the precondition for the GCM seal to round-trip). Import this package when you are building your own client or server adapter and need the exact wire types.

shell
npm i @noidme/glassprint-protocol
my-adapter.ts
import { canonicalBytes, type OpenedBundle } from '@noidme/glassprint-protocol';

// Shared wire types: OpenedBundle, SealedBundle, IngestResponse,
// FpPurpose, VisitorClass, FpMode, IngestReason, SmartSignals, …
const payload: OpenedBundle = { /* signals, manifest, receipt, nonce, clientTs */ };
const plaintext = canonicalBytes(payload); // byte-identical on client and server

Source: github.com/noidme-git/glassprint (protocol/) · MIT

Group 2 — source-available, self-host

Embeddable engines

The embeddable engines run the entire glassprint product in-process inside your own backend — no separate glassprint-server to deploy. Add one dependency, supply a master secret and a storage backend, and your backend immediately serves every glassprint endpoint: /_fp/pubkey, /_fp/i/v1 (ingest + full pipeline), /me (transparency / self-service erasure), and the full GDPR/DPDP privacy-rights API (/privacy/dsar, /privacy/erase, /privacy/contest). The pipeline: unseal → consent gate (GPC enforced) → replay/staleness → 9-probe + JA4 anti-spoof → 4-class verdict + confidence floor → salted-HMAC epoch-rotated tenant-bound visitorId → HMAC-hash-chained tamper-evident ledger.

License: source-available, not MIT. The engines are the commercial core of glassprint — you can read the source, self-host today, and run them inside your own perimeter. They are not open-source. Contact sales@noidme.com for a license.
Node / TypeScript

@noidme/glassprint-engine

Express middleware or Fastify plugin. SQLite (Node built-in node:sqlite, zero npm deps) or Postgres (npm i pg) for durable storage. In-memory for dev/tests. OIDC/JWT authenticator included for the /privacy/* routes (createOidcAuthenticator()). Web Bot Auth (RFC 9421) verifier wired. Full programmatic Art-17 erasure and TTL retention GC via engine.erase() and engine.retentionGC().

shell
npm i @noidme/glassprint-engine
npm i express   # or: npm i fastify
server.ts
import express from 'express';
import { createEngine } from '@noidme/glassprint-engine';
import { glassprintEngine } from '@noidme/glassprint-engine/express';

// All endpoints auto-mounted — /_fp/pubkey, /_fp/i/v1, /me, /privacy/*, /healthz
const engine = await createEngine({ storage: { driver: 'sqlite', path: './glassprint.db' } });
express().use(glassprintEngine(engine)).listen(8080);
Go

github.com/noidme/glassprint-engine-go

Pure stdlib — net/http, crypto, database/sql. Zero third-party imports in the engine itself; bring your own SQL driver (pure-Go SQLite via modernc.org/sqlite or lib/pq for Postgres). Handler() returns a plain http.Handler mountable at root or under any prefix. The zero-value Config{} is a working dev setup — keys auto-generated and persisted.

shell
go get github.com/noidme/glassprint-engine-go
main.go
import glassprint "github.com/noidme/glassprint-engine-go"

// Zero-config: in-memory storage, auto-generated keys, kid "default".
eng, err := glassprint.New(glassprint.Config{})
if err != nil {
    log.Fatal(err)
}
// Handler() returns a plain http.Handler — mount at root or under a prefix.
http.Handle("/", eng.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))
Python

glassprint-engine

Runtime dependency: cryptography>=41 (ECDH-P256 + HKDF + AES-GCM). Everything else — HMAC, SHA-256, canonical JSON, SQLite, HTTP — uses the Python stdlib. FastAPI (ASGI) and Flask (WSGI) adapters ship as optional extras. SQLite with WAL mode for single-process durable storage; implement the Storage protocol to bring Postgres or Redis. Env-var configuration: GLASSPRINT_MASTER_SECRET, GLASSPRINT_KIDS, GLASSPRINT_STORAGE. Python 3.11+; 3.9/3.10 with optional typing_extensions.

shell
pip install "glassprint-engine[fastapi]"   # + FastAPI/Starlette ASGI adapter
# pip install "glassprint-engine[flask]"  # + Flask WSGI blueprint
main.py
from fastapi import FastAPI
import glassprint_engine as gp

# Keys auto-generated and persisted; all endpoints auto-mounted.
engine = gp.create(storage="sqlite:///glassprint.db")
app = FastAPI()
app.include_router(gp.asgi.router(engine))
# Run: uvicorn main:app
Java / JVM

com.noidme:glassprint-engine

Two artifacts: the framework-agnostic glassprint-engine core (Javalin, Vert.x, Micronaut, plain HttpServer) and a glassprint-engine-spring-boot-starter that auto-registers every bean and controller via Spring Boot 3 @AutoConfiguration. Storage: in-memory (zero config) + JDBC adapter (H2 / Postgres, portable ANSI-SQL schema). Add two application.properties lines and every glassprint endpoint is live. Framework-agnostic programmatic use also supported via GlassprintEngine.create(config) + GlassprintEndpoints.

pom.xml
<!-- Spring Boot 3 starter — zero-wiring auto-configuration -->
<dependency>
  <groupId>com.noidme</groupId>
  <artifactId>glassprint-engine-spring-boot-starter</artifactId>
  <version>0.1.0</version>
</dependency>
application.properties
# application.properties
# Add the starter + these two lines — every glassprint endpoint is live.
glassprint.master-secret=your-at-least-32-char-production-secret
glassprint.kids=tenant-a
# optional: glassprint.jdbc-url=jdbc:postgresql://localhost/glassprint
Honest status across all engines. The exact-match identity resolver is the production deployable today — the trained-ML fuzzy linker (survives signal drift across visits) is a roadmap item. The deviceId (cross-browser stable device ID) field is reserved and returns null in all engines today. smartSignals.incognito is a placeholder (false) pending additional probes. Self-host ships today; the managed-SaaS option is design-partner onboarding, not open self-signup.

Self-host the engine, own your signals

Mount an embeddable engine in your own backend so no device intelligence leaves your perimeter, then point any client SDK at it.