Skip to content
Customize

Acceptance policies, TPM classes & assurance tiers

You decide which kinds of device you trust and how strongly the user must be authenticated. That decision is a named acceptance policy: pick a built-in, or author your own. The default is conservative: hardware-rooted TPMs only.

TPM classes: what kind of device is this?

Every enrolled device is classified into one root-of-trust class, derived from its hardware key's certificate (the EK cert issuer + the TCG manufacturer identifier) or, for mobile, from the platform attestation. Classes roll up into groups you author policy against:

GroupClassesWhat the cert proves
hardwarehardware-discrete-infineon · -st-micro · -nuvoton · -otherDiscrete silicon, signed by the TPM vendor's PKI. Highest.
firmware-tpmfirmware-tpm-intel-ptt · -amd-ftpm · -plutonFirmware TPM with a manufacturer chain (Intel PTT, AMD fTPM, Microsoft Pluton). High.
cloud-vtpmcloud-vtpm-aws-nitro · -azure · -gcp · -hyper-vA virtual TPM in a VM under that cloud account, not a physical chip. Medium; rejected by default.
emulatedemulated-swtpmA software TPM emulator. No external trust anchor. Rejected by default.
mobile-hardwaremobile-android-strongbox · -tee · mobile-apple-secure-enclave · mobile-apple-app-attestHardware-rooted mobile attestation (StrongBox / TEE / Secure Enclave / App Attest). High.
mobile-softwaremobile-android-softwareSoftware-level attestation only. No hardware backing. Rejected by default.

The class governs the acceptance decision, and it surfaces in the verdict: an accepted hardware or firmware device satisfies the real-device and oem-keyed entries of assuranceClaimsMet, and its root-of-trust tier shows up in the hardware dimension of device.trustworthinessVector. Branch on those in your own code even when the policy already accepted the device.

Note
Why a cloud vTPM isn't "hardware".AWS NitroTPM, Azure vTPM and GCP Shielded VM vTPM are real TPM 2.0 implementations doing real crypto. But the certificate proves "a virtual TPM in an instance under this cloud account": an identity an attacker mints with one API call, for pennies, at scale. If your problem is "one real machine = one identity" (Sybil, smurfing, free-tier farming), you want these rejected. If you're attesting your own cloud workloads, accept them, but require cross-validation (below).

Built-in policies: the easy path

You don't have to author anything. Select a built-in by name from the dashboard, pin it to an API key, or name it per-call as the policy argument to rh.verify(). The appraisal is run against it server→server, so the verdict you get back already reflects it.

NameAcceptsNotes
rootherald:builtin:strict-hardwarehardware, firmware-tpm, mobile-hardwareThe default. Requires device:high and full boot-chain verification. Rejects all virtual/emulated TPMs.
rootherald:builtin:strict-hardware-permissive-mobilesame, incl. Android TEE & macOS Secure EnclaveNo device:high floor. For consumer surfaces where StrongBox isn't universal.
rootherald:builtin:cloud-permissive+ cloud-vtpm (cross-validation required)For B2B customers serving endpoints and cloud workloads.
rootherald:builtin:enterprise-managed-onlyhardware, firmware-tpm only; device:highPair with EK pre-registration: only IT-provisioned devices enrol. Strongest stance against relayed attestations.
rootherald:builtin:deveverything, incl. emulated-swtpmDevelopment only. Always carries at least a warn verdict.

Custom policies: the coarse form

Need something between the built-ins? Register a named policy (you get back a UUID you reference afterwards; the policy lives server-side, you don't ship a rule blob in every request). The coarse form is a few fields:

POST /api/v1/admin/policiests
// Authorization: Bearer rh_sk_live_…   (Owner role)
{
  "name": "My app — accept hardware + Nitro",
  "description": "Hardware and firmware TPMs everywhere; AWS NitroTPM for our CI runners.",
  "acceptedClassGroups": ["hardware", "firmware-tpm", "mobile-hardware"],
  "acceptedSpecificClasses": ["cloud-vtpm-aws-nitro"],
  "unacceptedBehavior": "reject",          // "reject" | "warn"
  "requireCloudCrossValidation": true,
  "minAcr": "urn:rootherald:device:high",
  "minimumEarStatus": "affirming",
  "mode": "strict"
}
// → 201 Created (PolicyDetail):
// {
//   "id": "8f3a…",
//   "name": "My app — accept hardware + Nitro",
//   "isBuiltIn": false,
//   "version": 1,
//   "acceptedClassGroups": ["hardware", "firmware-tpm", "mobile-hardware"],
//   "acceptedSpecificClasses": ["cloud-vtpm-aws-nitro"],
//   "unacceptedBehavior": "reject",
//   "requireCloudCrossValidation": true,
//   "minAcr": "urn:rootherald:device:high",
//   "mode": "strict"
// }

acceptedClassGroups takes group names (hardware, cloud-vtpm, …); acceptedSpecificClasses pins individual vendor classes (e.g. cloud-vtpm-aws-nitro). Anything outside the accepted set is governed by unacceptedBehavior: "warn" lets out-of-set devices through with a warn verdict and a degraded trustworthiness vector (useful for shadow-banning or review queues), while "reject" blocks them outright.

Note
Advanced (Rego). A fine-grained tier is on the roadmap: arbitrary predicates over the full evidence (PCR patterns, event-log shape, EK issuer DN) expressed in OPA/Rego. The coarse fields above are authoritative today; Rego evaluation is deferred and not accepted on the create call yet.

Assurance tiers (ACR): how strongly is the user authenticated?

A separate axis from TPM class. The acr value says what the authenticationachieved. Root Herald treats a policy ACR floor as a hard requirement: a device that can't meet minAcr fails the policy rather than passing at a weaker tier. The acr returned in the verdict is the single tier the device actually satisfied, never a silently-weaker result.

URNMeansNIST AAL
urn:rootherald:device:anyAttested device, no user identity
urn:rootherald:device:highAttested device + verified boot chain + event-log replay
urn:rootherald:user:1faDevice + passwordAAL1
urn:rootherald:user:2faDevice + user + any second factorAAL2
urn:rootherald:user:phrDevice + user + phishing-resistant MFA (passkey)AAL2+
urn:rootherald:user:phrhDevice + user + hardware-bound phishing-resistant MFAAAL3
urn:rootherald:user:phrh:freshphrh + auth within the last 60 sAAL3 + freshness

Higher tiers subsume lower ones. The acr in the verdict is the single tier that was actually satisfied, never a preference list, never a partial match.

User tiers aren't reachable on the Shield verify path

On the server→server Shield verify path the resolved acr is only ever urn:rootherald:device:any or urn:rootherald:device:high — the user:* tiers belong to the (deferred) user-auth track. A policy whose minAcr is set to a user tier can never be satisfied on this path, so it will always reject.

Cloud vTPMs & cross-validation

If your policy accepts a cloud-vtpm class and requireCloudCrossValidationis on (the default), the device must additionally submit cloud-provider instance-identity evidence so the verifier can bind "this virtual TPM" to "this specific instance, in this account": the defence against relaying a vTPM attestation from a different instance.

The cloud evidence is bundled into the opaque blob the keyless client collects. Your server submits it with rh.verify() (naming a cloud-cross-validating policy) and reads the resulting device verdict returned directly.

AWS cross-validation evidence (attestation request)ts
cloudEvidence: {
  provider: "aws",
  nitroAttestationDocument: body.nitroDoc,        // CBOR/COSE, from the Nitro hypervisor
  ec2InstanceIdentityDocument: body.iid,          // from IMDSv2
  ec2InstanceIdentitySignature: body.iidSig,
  getInstanceTpmEkPubResult: body.ekPub,          // from the EC2 GetInstanceTpmEkPub API
}

Azure (provider: "azure", IMDS attested document) and GCP (provider: "gcp", GCE instance identity token) follow the same pattern. The verifier checks the cloud doc chains to that cloud's root, the EK matches what was enrolled, and the instance/account/region are what you expect.

Heads up
Honest status. The wire shape is stable, but the server-side validators for the cloud documents are still landing. Until they do, presenting cloud evidence sets the class to a cloud-vtpm class and (under cloud-permissive / dev) the attestation proceeds with a warn verdict noting cross-validation was not performed. Under any strict policy, cloud classes are rejected regardless.

For server/workload attestation generally, also consider cloud-native primitives (AWS Nitro Enclaves attestation documents, SPIFFE/SPIRE, cloud workload identity). A hardware TPM is the wrong tool for "is this container the workload I expect?". Root Herald is strongest for endpoint devices.