> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prem.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Attestation

> What the current Prem attestation client verifies, what remains policy work, and how the SDK uses the result.

Attestation is hardware-signed evidence about a confidential-computing environment. A verifier combines that evidence with manufacturer collateral, freshness checks, and an explicit policy before deciding whether to trust a runtime.

> [!WARNING]
>
> Evidence verification and policy verification are different. A valid manufacturer signature proves that evidence came from the relevant hardware trust chain. It does not, by itself, prove that the measured software matches an approved Prem release.

## Current implementation status

This table reflects `@premai/reticle` `0.5.1` and its release source (`aec6337` in [prem-research/reticle](https://github.com/prem-research/reticle)).

| Module | Implemented in the high-level client | Current limitation |
| - | - | - |
| AMD SEV-SNP | Generates a nonce, fetches the report and AMD certificate material, and verifies the report with the nonce | `attest_sev` delegates measured-value appraisal to the configured OPA policy; no reference measurements are pinned by default |
| Intel TDX | Generates a nonce, fetches the quote and Intel collateral, and runs quote verification with the nonce | `attest_tdx` delegates measured-value appraisal to the configured OPA policy; the shipped golden policy leaves image measurements unpinned by default |
| NVIDIA GPU | Generates a nonce; verifies each GPU's token signature against NVIDIA's keys; matches the nonce on every token; requires a passing measurement result for each GPU | The measurement result is enforced. There is no pinning of the exact GPU set, no per-GPU pass or fail exposed, and no proof that the request ran on exactly those GPUs |

The client discovers one CPU module (`SEV-SNP` or `TDX`) and an optional NVIDIA evidence bundle, then calls the corresponding high-level methods. The NVIDIA EAT token can contain multiple detached GPU claims even though module discovery reports only the NVIDIA vendor module.

> [!NOTE]
>
> Prem documentation and deployments target AMD SEV-SNP, Intel TDX, and NVIDIA confidential-computing GPUs. The exact hardware model, firmware, TCB, and policy acceptance decision must come from verified evidence and an explicit policy, not from a model alias.

> [!TIP]
>
> Want to run attestation yourself? See the step-by-step [Run Attestation](https://docs.prem.io/guides/run-attestation.md) guide to verify a confidential runtime directly with `@premai/reticle`.

## What verified attestation policy means

A complete policy decision needs four distinct inputs:

1. **Authenticity:** the report or token verifies against manufacturer collateral.
2. **Freshness:** the evidence contains the verifier's nonce.
3. **Configuration:** security-relevant claims meet required TCB, debug, and confidential-mode rules.
4. **Identity:** measured values match a release-specific approved reference set.

Reticle's high-level methods perform evidence and freshness checks, then pass the measured values to the configured OPA policy. Step 4 happens only if that policy pins approved reference measurements; the shipped golden policy leaves image measurements unpinned by default, so the client does not automatically match a published image fingerprint.

Prem publishes the baseline golden policies in [prem-research/confidential-policies](https://github.com/prem-research/confidential-policies). The repository contains the Rego policies for AMD SEV-SNP, Intel TDX, and NVIDIA GPUs. These golden policies enforce baseline invariants such as debug restrictions and minimum TCB values. They also define reference fields (such as `mrtd` or `rtmr` measurements) so operators can pin approved release images.

## Where Reticle runs

Reticle is the verifier library, not a component that must run on the GPU cluster.

```mermaid theme={"system"}
flowchart LR
    subgraph Client["Client device or local proxy"]
        SDK["Prem SDK / Confidential Proxy"]
        R["@premai/reticle verifier"]
        SDK --> R
    end

    subgraph Prem["Prem services"]
        G["Gateway and model routing"]
        A["Attestation endpoint in the confidential runtime"]
        M["Model backend"]
    end

    R -->|"request evidence with nonce and model"| G
    G --> A
    A -->|"hardware evidence and session header"| R
    SDK -->|"encrypted request with session header"| G
    G --> M
```

The attestation endpoint runs with the confidential runtime to obtain hardware evidence. Reticle runs in the SDK or proxy process and verifies that evidence. The Rust implementation is also published as a WASM package for web, server, and mobile JavaScript environments.

## How the SDK gates a request

With the default `attest: true`, `@premai/api-sdk` performs this sequence:

1. Builds a Reticle client against `PROXY_URL` and authenticates with the Prem API key.
2. Adds the requested model to the attestation query.
3. Calls the high-level `client.attest()` method.
4. Reads an `x-session-id` from the returned attestation headers.
5. Sends the encrypted inference request with that session ID.

The SDK caches a successful session ID for 30 seconds by API key and model and coalesces simultaneous attestation work. Transient attestation transport errors can be retried up to four attempts. A non-transient failure, or a missing session ID, throws before the protected inference request is sent.

> [!WARNING]
>
> `attest: false` and the proxy flag `--no-attest` bypass this gate. Do not use them for a workflow that claims attested confidential inference.

## Attestation error handling

When the attestation gate fails, the SDK throws an `AttestationError` before the protected inference request is sent. The name is a wrapper, not a verdict: an `AttestationError` does **not** always mean attestation itself failed. It can also carry a gateway rejection — for example an invalid API key — that surfaces through the attestation gate. The proper error handling path is to distinguish the two, because they need different responses.

The SDK exports four helpers to inspect and surface these errors:

| Helper | Returns | Purpose |
| - | - | - |
| `isAttestationError(err)` | `boolean` | True when `err` is an `AttestationError` thrown by the attestation gate |
| `isGatewayError(err)` | `boolean` | True when the error is a gateway rejection (a structured object with a non-empty message) |
| `getGatewayErrorMessage(err)` | `string \| null` | The raw gateway rejection message, or `null` when the error is not a gateway error |
| `getAttestationErrorMessage(err)` | `string` | A user-safe message for the error, resolved in a defined order |

### The two cases behind an `AttestationError`

1. **A gateway rejection.** The gateway rejected the request before attestation could complete — for example `Authentication token is invalid` on a `401`. This is not an attestation failure; it is an API or auth problem that happens to surface through the attestation gate. The user can act on it (fix the API key, check the quota).
2. **A genuine attestation failure.** The attestation exchange itself failed internally. There is no user-actionable message; the SDK logs the full cause chain for debugging.

Check `isGatewayError` first. If it is true, surface `getGatewayErrorMessage` — that is the comprehensive, actionable message. Only when it is false should you treat the error as a genuine attestation failure.

`getAttestationErrorMessage` resolves the message in this order:

1. **The gateway rejection body** — for example `Authentication token is invalid` on a `401`. This is the message you want to show the user, because it tells them what to fix.
2. **The deliberately exposed message** — the SDK's own message when it is not the generic `An internal error occurred`.
3. **The outermost cause** — used only for the fully-internal case, where the SDK logs the full cause chain for debugging and returns the first cause as a last resort.

The gateway check is structural, not an `instanceof` check. This matters if you drive Reticle directly (for example, through `useAttestation` in a browser bundle): the error can come from a different Reticle WASM instance than the one the SDK cached, so matching on the error shape keeps the check reliable across instances.

### Handle attestation errors in code

```typescript theme={"system"}
import {
  createRvencClient,
  isAttestationError,
  isGatewayError,
  getGatewayErrorMessage,
  getAttestationErrorMessage,
} from "@premai/api-sdk";

const client = await createRvencClient({
  apiKey: process.env.PREM_API_KEY,
  clientKEK: process.env.CLIENT_KEK,
});

try {
  const chat = await client.chat.completions.create({
    model: "deepseek-v4.1-flash-1017",
    messages: [{ role: "user", content: "Hello" }],
  });
} catch (err) {
  if (isAttestationError(err)) {
    if (isGatewayError(err)) {
      // A gateway rejection (e.g. invalid API key) — not an attestation failure.
      // Surface the real, actionable message.
      console.error("Request rejected:", getGatewayErrorMessage(err));
    } else {
      // A genuine attestation failure — no user-actionable message.
      console.error("Attestation failed:", getAttestationErrorMessage(err));
    }
  } else {
    throw err;
  }
}
```

> [!NOTE]
>
> The attestation gate runs before the inference request is sent. A gateway rejection means the request never reached the model backend, so you can retry safely once the underlying cause (for example, a corrected API key) is resolved.

## Routing and session pinning

Routing has two separate meanings:

* **Commercial/API routing:** the gateway authenticates the API key, applies limits and billing metadata, and selects the service path for the requested alias while handling encrypted content.
* **Attestation-linked routing:** the attestation response supplies a session ID that the SDK attaches to the encrypted inference request so the platform can associate it with the attested backend path.

The client source confirms model-scoped attestation and session-header forwarding. It does not prove every backend scheduling, load-balancing, or billing implementation detail. Billing is associated with the authenticated request; an attestation failure stops the inference request before completion, but final billing semantics must be confirmed from Prem usage records.

## Model aliases are not assurance profiles

Model-list and completion types do not include `assurance`, `attestation`, `tee`, or provider-backed/Reticle-verifiable fields.

* OpenAI-format and direct SDK completions can return a backend-resolved model ID in `response.model`.
* The Anthropic adapter returns the requested alias in its `model` field.
* Neither response is a substitute for an assurance profile.

Do not classify a model as provider-backed or Reticle-verifiable from its name. See [Platform Status](https://docs.prem.io/platform-status.md) for the current contract gap.

## Run attestation with Reticle

Reticle is published as `@premai/reticle` on npm. It runs on Node.js, Bun, Deno, modern browsers, and mobile devices (iOS and Android). It performs module discovery, nonce freshness checks, certificate validation, and OPA policy appraisal.

To run attestation yourself and verify a confidential runtime directly, see the step-by-step [Run Attestation](https://docs.prem.io/guides/run-attestation.md) guide.

> [!WARNING]
>
> Do not use low-level evidence request methods (`request_sev`, `request_tdx`, `request_nvidia`) for verification without cryptographic checks. A successful raw evidence fetch is not an attestation pass. Follow the complete verification flow in the [Run Attestation guide](https://docs.prem.io/guides/run-attestation.md).

## Multi-GPU attestation and load balancing

A single model can be served by several physical GPUs at once, and each GPU produces its own signed attestation token. The client verifies every token in the response: it confirms each signature against NVIDIA's published keys, checks that the request's freshness value matches on every token, and requires each GPU to pass its measurement check — that is, the GPU's measured firmware and configuration match NVIDIA's reference values.

These checks establish that each GPU in the response is genuine, fresh, and running measured firmware. A few things they do not establish are worth calling out:

* **The exact set of GPUs is not pinned.** The client verifies every token it is handed but does not reject an unexpected extra one. Any extra token would still need a valid NVIDIA signature and the current freshness value, which limits the risk, but the response does not prove that these — and only these — GPUs served the request.
* **Results are all-or-nothing.** Verification either succeeds for the whole set of GPUs or fails; the application does not receive a separate pass or fail for each GPU.
* **The serving path is not bound.** Nothing in the result proves the request was actually routed to the GPUs that were attested, rather than to other hardware behind the load balancer.

## CPU technologies and other processors

AMD SEV-SNP and Intel TDX are confidential-VM technologies with remote-attestation formats and manufacturer collateral that Reticle implements. Other CPUs are not automatically insecure; they are unsupported by this verifier unless there is a corresponding confidential-computing architecture, evidence format, collateral path, and policy implementation.

Support for one CPU family is significant because it lets a remote client authenticate hardware evidence and freshness for that family. It does not make all workloads, measurements, or firmware states acceptable without policy checks.

## GPU technologies

NVIDIA confidential computing is hardware- and firmware-dependent. “Hopper” and “Blackwell” are architecture families, not sufficient policy inputs. A verifier must examine the actual signed token and claims for the deployed SKU and software stack.

GPUs without a supported confidential-computing mode and attestation format cannot provide the same remote evidence through Reticle. They may still run inference, but the client cannot transfer NVIDIA confidential-computing claims to them.

## Audit records

An audit record should contain non-sensitive decision metadata, such as:

* request/support ID and time;
* requested alias and resolved model ID when exposed;
* verifier and policy version;
* evidence type, hardware identity fields, and TCB result;
* nonce/freshness result;
* reference-set identifier and measurement result when implemented;
* allow/deny decision and reason.

Do not record prompts, responses, API keys, KEKs, raw decrypted payloads, or secrets in an attestation audit log.

## Known gaps

1. Expected CPU measurement comparison is policy-driven: measured values reach the configured OPA policy, but the shipped golden policy leaves reference measurements unpinned by default, so approving a specific release image requires operator-supplied reference values.
2. The API does not expose a model assurance profile.
3. Each GPU in a multi-GPU response is checked for a valid signature, a matching freshness value, and a passing measurement result. The client does not pin the exact set of GPUs, does not surface a per-GPU pass or fail, and does not prove the request ran on the attested GPUs.
4. An open-source CVM base image, open verification of model weights and serving engine code, and a public release-to-measurement mapping are separate requirements.
5. Package and runtime version reporting currently disagree for the Confidential Proxy.

> [!NOTE]
>
> These findings apply to the reviewed package and source versions. Changes to Reticle, the SDK, endpoints, or policy require new source and live validation.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.