The product
One interface. Every device.
A TPM, a Secure Enclave and an Android TEE prove different things, in different formats, against different roots of trust. Root Herald puts all of them behind one call — and returns one verdict shape, whatever the device turned out to be.
The integration
This is the whole thing.
const result = await rh.verify({
challengeId: req.body.challengeId,
evidence: req.body.evidence,
});
if (result.verdict.device.verdict !== "pass") {
return res.status(403).json({ error: "device_check_failed" });
}You never parse a quote, walk a certificate chain, or learn what a PCR is. The evidence arrives from the device as an opaque blob, your server relays it, and the answer comes back.
The same call handles a Windows laptop with a discrete TPM and an iPhone using App Attest. Nothing in your code says which.
Coverage
Five attestation technologies, one contract.
| Platform | Root of trust | What it proves |
|---|---|---|
| Windows | TPM 2.0 | Quote over a nonce + EK certificate chain to the chip vendor |
| Linux | TPM 2.0 | Quote over a nonce + EK certificate chain to the chip vendor |
| Android | TEE / StrongBox | Key Attestation chain to Google's hardware root |
| iOS | Secure Enclave | App Attest chain to Apple's attestation root |
| macOS | Secure Enclave | Possession of a non-exportable key |
The desktop SDKs expose a byte-identical C ABI, so the same integration code links on all three. Where a platform genuinely proves less — macOS cannot currently prove its own silicon — the verdict says so rather than papering over it. What each platform can prove →
Why one call is enough
Ask for a capability, not an operating system.
You declare what the action requires. The verdict lists what the device actually proved. Allowed when the first is a subset of the second.
This is what keeps the integration from rotting: add a platform, or a platform gains a capability it lacked, and the claim simply starts appearing. No branch to update, no version check, no redeploy.
const REQUIRED = ["rootherald:assurance:real-device"];
const met = new Set(result.assuranceClaimsMet);
const allowed = REQUIRED.every((c) => met.has(c));What you don't build
The part that takes months.
Signature and freshness
TPM2_Quote parsed and verified against the enrolled attestation key, with the nonce read from inside the signed structure — not from a field alongside it.
Certificate chains
Endorsement-key chains validated to seeded chip-vendor roots, with revocation and vendor-specific intermediates handled per manufacturer.
Measured boot
The TCG event log replayed and bound to the signed quote, so boot state is only trusted when the replayed PCRs match the ones the chip signed.
Mobile attestation
Android Key Attestation and Apple App Attest validated against their own roots, with the security level read from the certificate rather than claimed by the app.
Cloud and emulator classing
Provider vTPMs and software emulators identified as what they are, so a rented server can't present itself as a laptop.
Policy resolution
Your acceptance rules applied server-side and changed without a redeploy, so tightening a policy doesn't mean shipping code.
Getting it
Where the contract reaches today.
The API
One HTTPS endpoint, one contract, one set of error codes. Usable directly from any language that can post JSON — an SDK is convenience, never a requirement.
SDKs
@rootherald/node is available today; the rest are rolling out. Because the wire contract is published and stable, a binding landing later doesn't change code you've already written.
Status for every language →Dashboard
Usage, API keys, the policy editor, a live event tail, and billing.