Skip to content
Guide · Tutorial

Real & secure hardware

Some features should run onlyon genuine physical hardware that booted securely: not a cloud VM’s virtual TPM, not a software emulator. This tutorial builds that gate end to end. Require a real hardware/firmware TPM, a verified Secure Boot chain, and a quote-bound measured-boot event log, and reject everything else. Every check is server-validated and bound to a signed TPM quote, so a client can’t forge its way past it.

One policy does the enforcement

You don’t hand-roll the boot checks. The built-in rootherald:builtin:strict-hardware policy (the conservative default) accepts only hardware and firmware-tpm TPM classes (so cloud vTPMs and emulators are rejected), requires a signed quote, requires a quote-bound event log, requires Secure Boot to be enabled, and floors the verdict at affirming. You pass the policy name to verify(); the server returns an affirming verdict only when all of that holds.

1. Set up the server client

rh.tsts
import { RootHerald } from "@rootherald/node";

export const rh = new RootHerald({ secretKey: process.env.RH_SECRET_KEY! });

Enrollment is a prerequisite (a device must have an attestation key before it can quote) — see the hardware-ID tracking tutorial for the relay wiring. This guide is about the appraisal.

2. Attest against the strict-hardware policy

Issue a challenge, let the page collect a fresh quote over the nonce, then verify with the policy. The policy is what turns “a valid quote” into “a valid quote from real, secure hardware.”

server: challenge -> verifyts
// 1) mint a single-use challenge
const { challengeId, nonce } = await rh.issueChallenge();

// 2) hand `nonce` to the page; it calls attest(nonce) and posts `evidence` back

// 3) verify under the hardware-only, secure-boot policy
const verdict = await rh.verify(evidence, {
  challengeId,
  policy: "rootherald:builtin:strict-hardware", // (also the default if omitted)
});
browser: collect the quotets
import { attest, enroll, NotEnrolledError } from "@rootherald/browser";

async function collect(nonce, relay) {
  try {
    return await attest(nonce);            // unprivileged quote, no prompt
  } catch (e) {
    if (e instanceof NotEnrolledError) {   // first run on this device
      await enroll(relay);                 // the one-time elevation
      return await attest(nonce);
    }
    throw e;
  }
}

3. Read the verdict

For real-and-secure, an affirming verdict is your green light: the policy already guaranteed hardware class + Secure Boot + measured boot. The device booleans let you display or log the specifics.

server: gate the featurets
if (verdict.device.earStatus !== "affirming") {
  // Contraindicated / warning: not genuine secure hardware under this policy.
  return res.status(403).json({ error: "requires_secure_hardware" });
}

// Green light. What you now KNOW (all server-validated, quote-bound):
const d = verdict.device;
d.attestationType;      // "tpm20" — a TPM device (vs apple-se / android-ka)
d.quoteVerified;        // true — the quote signature checked against the enrolled AK
d.secureBootVerified;   // true — Secure Boot established from the quote-bound event log
d.eventLogVerified;     // true — the event log replayed and bound to the signed quote
d.platform;             // "windows" | "linux" | ...

grantSecureHardwareFeature(verdict.device.ueid);
Why you can trust these booleans

secureBootVerified and eventLogVerified are computed on the server from an event log that had to reconstruct the exact PCR values the TPM signed. A client can’t set them: an event log that doesn’t match the signed quote is rejected before Secure Boot is even read. This is the difference between “the client told us Secure Boot is on” and “the hardware proved it.”

4. What gets rejected, and why

Under strict-hardware, each of these returns a fail (contraindicated) verdict:

Device / conditionTPM classWhy it fails
AWS / Azure / GCP / Hyper-V VMcloud-vtpmClass not in the policy’s accepted set (hardware + firmware-tpm only).
swtpm / software emulatoremulatedNo hardware root of trust; class rejected.
Real TPM, Secure Boot OFFhardware / firmware-tpmQuote-bound event log shows Secure Boot disabled → contraindicated.
Revoked boot component (dbx)hardware / firmware-tpmA forbidden signature in the boot chain → contraindicated.
No event log / PCR-onlyanyPolicy requires a quote-bound event log; a bare quote can’t prove boot posture.

An unrecognized EK (no vendor chain) classifies as Unknown and is rejected too. See the TPM-class taxonomy for how the class is derived.

5. Tune it (optional): a custom policy

If strict-hardware is close but not exact (say you also accept mobile hardware, require a specific ACR, or want to reject rare boot configurations), author a custom policy in the dashboard, or via the admin API, and pass its name to verify(). Configurable knobs include the accepted TPM-class groups, whether an event log and signed quote are required, the required PCR set, a minimum ACR, and an opt-in cohort-prevalence floor (reject boot configs that are novel / too rare).

admin API (sketch)http
POST /api/v1/admin/policies
{
  "name": "my-secure-hw",
  "acceptedClassGroups": ["hardware", "firmware-tpm"],  // no cloud-vtpm / emulated
  "requireEventLog": true,
  "requireSignedQuote": true,
  "minAcr": "urn:rootherald:device:high"
}

Then: rh.verify(evidence, { challengeId, policy: "my-secure-hw" }). Built-in policies are immutable; custom ones are tenant-scoped and versioned.

6. Gate a whole route (token path)

If you issue signed attestation tokens (returnToken: true) and want to gate an API route on device assurance, the requireAttestation middleware verifies the token and enforces an ACR — its device track additionally demands quoteVerified && secureBootVerified && eventLogVerified for device:high:

express: gate a routets
import { requireAttestation } from "@rootherald/node";

app.post(
  "/api/secure-action",
  requireAttestation({
    issuer: "https://api.rootherald.io",
    audience: "your-app",
    acrValues: ["urn:rootherald:device:high"], // real + secure-boot + measured boot
    maxAgeSeconds: 300,                          // require a fresh attestation
  }),
  (req, res) => {
    // reaches here only for genuine, securely-booted hardware
    res.json({ ok: true });
  },
);
The guarantee is server-side

Nothing here trusts the client. The TPM class comes from a vendor-chained EK; Secure Boot and the boot chain come from an event log that had to reproduce the signed PCRs; the policy decision runs on our side. The client collects evidence; it never renders the verdict.