Product
One call returns a hardware fact about the device.
Drop a server→server rh.verify() call into your signup, claim, or vote flow. It returns a pass/fail verdict and a stable, anonymous per-tenant device pseudonym, so you stop guessing whether the request came from a real machine.
{
"acr": "urn:rootherald:device:high", // assurance level the device satisfied
"device": {
"ueid": "2f9c8a14…", // stable per-tenant device id (no PII)
"earStatus": "affirming", // affirming | warning | contraindicated
"verdict": "pass",
"attestationType": "tpm20",
"secureBootVerified": true,
"trustworthinessVector": { "hardware": 2, "configuration": 2 }
}
}What you get
A proof you can branch on — not a score you have to trust.
Hardware-rooted pseudonym
A stable, anonymous device id across Windows, Linux, macOS, Android, and iOS, bound to the TPM or Secure Enclave, not the OS install.
VM and emulator detection
Bot farms on NitroTPM, Azure vTPM, GCP Shielded, and swtpm chain to provider PKI, not manufacturer roots, so they're rejected before they cost you anything.
Policy as a string
Pick a built-in (rootherald:builtin:strict-hardware) or author your own. Per-call, per-session, or per-project, with no redeploy to change the rules.
Stateless verify
One call, one verdict, signed against a published JWKS. No state to keep, per-region failover underneath.
The integration
Appraise the evidence. Branch on the verdict. Store the id.
The whole client-side surface is one dumb evidence collect; the whole server-side surface is one server→server appraisal. Everything cryptographic happens on our side of the wire.
import { RootHerald } from "@rootherald/node";
const rh = new RootHerald({ secretKey: process.env.RH_SECRET_KEY }); // rh_sk_…
// Earlier: rh.issueChallenge() -> relay nonce to your dumb client, which
// collects the opaque evidence and posts back { challengeId, evidence }.
const verdict = await rh.verify(req.body.evidence, {
challengeId: req.body.challengeId,
policy: "rootherald:builtin:strict-hardware",
});
if (verdict.device.verdict !== "pass") {
return res.status(403).json({ status: verdict.device.earStatus });
}
// verdict.device.ueid — stable, anonymous, per-tenant device pseudonym
await users.create({ ...req.body, deviceId: verdict.device.ueid });Ship your first verdict this afternoon.
Free up to 10K attestations a month, no card. Node available today; more SDKs rolling out.