glassprint · Operate

Self-host / Embed

Both options ship today. The primary integration model is embedding the glassprint engine library in your own backend — adding the dependency auto-serves all endpoints and processes identifications in-process, with no separate server to deploy. For polyglot shops or teams that prefer a separate service, the standalone glassprint-server container (Docker / Helm / Terraform) is the alternative. Either way, your fraud signals never leave your perimeter. A managed SaaS is design-partner onboarding, not open self-signup.

Option A — Embed the engine in your backend (primary)

The engine libraries are embeddable, self-contained ports of glassprint-server. Add one dependency and your backend automatically serves the full glassprint product in-process — every HTTP endpoint, storage, and the full processing pipeline — with no separate server to deploy or operate.

The engine libraries are commercial / source-available (UNLICENSED), not MIT. A commercial license is required for production use. Contact us to get access.

Node — Express middleware

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

const engine = await createEngine({
  storage: { driver: 'postgres', url: process.env.DATABASE_URL }, // or 'sqlite' for single-instance
  kids: ['acme'],
  masterSecret: process.env.GLASSPRINT_MASTER_SECRET,
});

express().use(glassprintEngine(engine)).listen(8080);
// Auto-mounted: GET /_fp/pubkey  POST /_fp/i/v1  POST /me  POST /privacy/*  GET /healthz

Node — Fastify plugin

server.ts
import Fastify from 'fastify';
import { createEngine } from '@noidme/glassprint-engine';
import { glassprintFastify } from '@noidme/glassprint-engine/fastify';

const engine = await createEngine({
  storage: { driver: 'postgres', url: process.env.DATABASE_URL },
  kids: ['acme'],
  masterSecret: process.env.GLASSPRINT_MASTER_SECRET,
});
const app = Fastify();
app.register(glassprintFastify, { engine });
await app.listen({ port: 8080 });

Go — net/http handler

shell
go get github.com/noidme/glassprint-engine-go
main.go
import (
    glassprint "github.com/noidme/glassprint-engine-go"
    "net/http"
    "log"
    "os"
)

eng, err := glassprint.New(glassprint.Config{
    MasterSecret: os.Getenv("GLASSPRINT_MASTER_SECRET"),
    Kids:         []string{"acme"},
    // Storage: store,  // nil = in-memory (dev); use SQLite or Postgres adapter in production
})
if err != nil { log.Fatal(err) }

// Mount at root, or under a prefix:
// mux.Handle("/gp/", http.StripPrefix("/gp", eng.Handler()))
http.Handle("/", eng.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))

Python — FastAPI (ASGI)

shell
pip install "glassprint-engine[fastapi]"
main.py
from fastapi import FastAPI
import glassprint_engine as gp

engine = gp.create(
    storage="postgresql://user:pass@localhost/glassprint",  # or "sqlite:///glassprint.db"
    kids=["acme"],
    master_secret=os.environ["GLASSPRINT_MASTER_SECRET"],
)
app = FastAPI()
app.include_router(gp.asgi.router(engine))  # all endpoints auto-mounted
# uvicorn main:app

Java — Spring Boot starter (zero-wiring)

Add the starter dependency and set two properties. Spring Boot auto-configuration registers every bean and controller — no manual wiring required.

pom.xml
<dependency>
  <groupId>com.noidme</groupId>
  <artifactId>glassprint-engine-spring-boot-starter</artifactId>
  <version>0.1.0</version>
</dependency>
application.properties
glassprint.master-secret=your-at-least-32-char-production-secret
glassprint.kids=acme
# optional: glassprint.jdbc-url=jdbc:postgresql://localhost/glassprint

The Java engine also ships a framework-agnostic core (glassprint-engine) for Javalin, Vert.x, Micronaut, or plain HttpServer — mount individual endpoint handlers directly.

Storage options (all engines)

DriverWhen to use it
In-memoryDev and tests only — not durable, lost on restart.
SQLite (Node: node:sqlite; Python: stdlib; Go/Java: stdlib sql)Single-instance durable deployments. Zero extra dependencies for the engine itself.
PostgresMulti-instance (horizontal scale). Nonce claims are atomic via INSERT ... ON CONFLICT; ledger appends serialized with an advisory lock.

Keys and auth (embed)

All engines derive keys the same way as glassprint-server: one master secret (supply from your secret manager) via HKDF-SHA256 derives per-customer seal keys, per-(kid, epoch) visitorId HMAC keys, the ledger key, and the transparency key. Supply masterSecret from a secret manager for multi-instance deployments (or the engine auto-generates and persists one for single-instance dev). The privacy-rights API is fail-closed by default — use createOidcAuthenticator() (Node/Go) or the OIDC JWT authenticator (Python/Java) in production; the dev header authenticator is off unless explicitly opted in.

Option B — Standalone glassprint-server (Docker / Helm / Terraform)

The standalone server is a dependency-light Node 24 container — the same identification engine, run as a separate service. Use this option when your primary backend is not in a supported engine language, or when your team prefers to operate it as a separate tier.

The container

The server is stateless and scales horizontally — all durable state lives in Postgres and Redis. The same image runs everywhere; only the connection strings change.

local — docker compose
# bring up Postgres + Redis, then the server
docker compose --profile server up

# server binds 127.0.0.1:8080 (GLASSPRINT_PORT to override)
curl localhost:8080/healthz   # → {"ok":true}

Backends

Backends are selected from the environment: Postgres + Redis when configured (GLASSPRINT_PG_URL / GLASSPRINT_REDIS_URL), in-memory otherwise (dev / single-instance only). The Postgres schema is standard 14+ and idempotent; Redis provides the atomic replay nonce and the cross-replica rate limiter. The ledger appends are advisory-lock-serialized so replicas stay consistent.

Deploy — Docker, Helm, Terraform

The repo ships infrastructure for all three, cloud-portable by construction:

  • Docker — the Dockerfile + docker-compose.yml for local and single-node.
  • Helmdeploy/helm/glassprint (chart, deployment, values), including the externalTrafficPolicy: Local guidance the JA4 trust boundary needs behind a load balancer.
  • Terraform — a deploy/terraform/gcp skeleton.

The same image runs on GCP (Cloud Run + Cloud SQL + Memorystore, or GKE), Azure (Container Apps / AKS + Flexible Server + Azure Cache), or AWS (ECS/Fargate or EKS + RDS + ElastiCache) with no code change — secrets come from each cloud's secret manager.

The JA4 edge options

The JA4 / HTTP2 / TCP network tier is captured at your edge and forwarded over the CIDR trust boundary. It is vendor-neutral — pick the one that fits your stack, or skip it entirely and let the anti-spoof confidence cap take over:

OptionWhen to use it
Cloudflare WorkerYou already front your traffic with Cloudflare — drop in the worker and forward the authentic JA4 header.
CloudFrontAWS-native edge; use CloudFront's native JA4 fingerprint.
Go JA4 proxySelf-host the reverse proxy: it terminates TLS, computes JA4 per the FoxIO spec, strips client-spoofed x-glassprint-* headers, and forwards the authentic one.
NoneSkip JA4 — the server marks topology untrusted-direct and the anti-spoof confidence simply caps. Everything else still works.
Whichever edge you choose, the server honors forwarding headers only from peers in the configured CIDR allowlist (GLASSPRINT_EDGE_RANGES / _CF_RANGES / _CLOUDFRONT_RANGES). It never derives the client IP from X-Forwarded-For, and an untrusted peer has its headers stripped. (The same CIDR trust boundary applies to the embedded engines via their trust / Trust config field.)

Keys and auth (standalone server)

One root secret (GLASSPRINT_MASTER_SECRET, a KMS-random key) derives, via HKDF-SHA256, the per-customer seal keys, the per-(kid, purpose, epoch) visitorId HMAC keys, the ledger key, and the transparency key. The privacy-rights API is fail-closed by default — production verifies a bearer JWT (ES256/RS256; none/HS* rejected); the trivially-spoofable header authenticator is dev-only and off unless explicitly opted in.

Honest hardening note (both options): the master secret, the HPKE recipient private key (no forward secrecy yet), and the ledger signer are all "move to KMS/HSM in production" seams — the one-line swap exists, but the KMS-backed signer is roadmap, not shipped. Observability is PII-free JSON security events to stdout (12-factor → your SIEM).

Honest limits to plan around

  • Identity resolution is exact-match in both options today. The fuzzy trained-ML linker is roadmap, not wired into the live ingest path.
  • The cross-browser deviceId field is always null today — it is a placeholder for the precise-device-identification roadmap purpose.
  • The Java engine multi-instance ledger uses a JVM-level lock (correct for single-process use). For multi-replica deployments sharing one DB, use the standalone server which adds a Postgres advisory lock.
  • The /me per-signal view returns 404 — the engine retains no raw per-signal values by design; the rich transparency view is client-driven and roadmap.
  • The Marketplace packaging (GCP / Azure managed app) is a roadmap target, not built.