# TEE verification and encrypted inference

Verify enclave evidence and per-request receipts, understand verification levels, and use the opaque E2EE transports.

AnonRouter exposes provider-neutral enclave attestation and request-signature
services. Provider evidence is fetched by a credential-isolated worker,
verified fail-closed, and returned to you so your client can independently
repeat the verification.

> **TEE is not automatically hardware-verified**
>
> A route's privacy modality and its verification level are separate claims. This
> API never turns a provider boolean, model suffix, or confidential-compute flag
> into silicon proof. Most provider routes (hop 2) stop below
> `hardware-verified`: several run GPU enclaves whose NVIDIA chain is not
> available to verify, and chaining only the CPU quote would claim more than was
> checked. Where a provider route does reach `hardware-verified`, as Phala AI
> routes do, each check says which machine it covers. See
> [Phala AI](#phala-ai).
> 
> Hop 1, AnonRouter's own routing plane, is different, and *is* reachable at
> `hardware_verified` with a DCAP engine. See
> [Verify AnonRouter itself](#verify-anonrouter-itself-hop-1).

> **Building an E2EE client?**
>
> Follow the dedicated [E2EE client guide](/e2ee) for the ticket sequence,
> provider wire formats, required headers, retry rules, and production
> checklist. This page remains the attestation and receipt reference.

## Two hops, verified separately

A request passes through two parties, and verifying one tells you nothing about
the other.

|                                           | Question it answers                                                                                                                   | How to check it                                 |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| **Hop 1**: AnonRouter's own routing plane | Is the router I am talking to the exact reviewed build, running inside an Intel TDX confidential VM, bound to my nonce and my origin? | `GET /v1/gateway/attestation` (credential-free) |
| **Hop 2**: the downstream provider route  | Did the model provider terminate my request inside a verified enclave running measurements I pinned?                                  | the provider attestation and receipt APIs below |

A verified hop 2 says nothing about who routed the request there. A verified
hop 1 says nothing about where inference actually ran. Most of this page is
about hop 2; the section immediately below is hop 1.

## Verify AnonRouter itself (hop 1)

AnonRouter's content plane runs inside an Intel TDX confidential VM. The relay,
the compatibility broker, and **every provider worker** run inside that TD, and
TLS terminates *in-enclave* rather than at a platform gateway in front of it.

This is why the privacy properties on [the privacy page](/privacy) are not just
policy. Prompts do not reach ordinary AnonRouter infrastructure, and no operator
can read them off a running host, and you do not have to take that on trust,
because the plane will attest to itself on demand.

### The endpoint

```bash
NONCE=$(openssl rand -hex 32)
curl -s "https://api.anonrouter.ai/v1/gateway/attestation?nonce=$NONCE"
```

`GET /v1/gateway/attestation` is **credential-free**: a client should be able to
check the plane *before* trusting it with anything, including before it has an
account. The `nonce` is required and must be exactly 64 hex characters (32
bytes); a request without one is a `400`.

The response is a JSON document of roughly 95 KB:

| Field                      | What it carries                                                                                                                                                        |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `binding`                  | the claims being attested: your `nonce`, `app_id`, `instance_id`, `compose_hash`, `release_id`, `origin`, the plane's `public_key`, `transport`, and `tls_spki_sha256` |
| `binding_hash`             | the digest committed into the quote's `report_data`                                                                                                                    |
| `quote`                    | the raw Intel TDX quote                                                                                                                                                |
| `event_log`                | the boot event log, which replays to the quote's RTMRs                                                                                                                 |
| `app_compose`, `vm_config` | the measured deployment definition                                                                                                                                     |
| `issued_at_ms`             | freshness                                                                                                                                                              |

### What a verdict establishes

Verifying that document is what turns "AnonRouter says it runs in an enclave"
into something you checked:

* **the quote is fresh and yours**: your nonce appears inside the quote's
  `report_data`, as the digest of the canonical binding, so a recorded response
  cannot be replayed at you;
* **TLS terminates inside the TD**: `binding.transport` is `in-tee-tls`, not
  `gateway-tls`. There is no platform proxy in front of the enclave holding your
  plaintext;
* **the certificate belongs to the attested TD**: the leaf SPKI you observed on
  your own connection equals `binding.tls_spki_sha256`. This is the check that
  turns "somebody named a certificate" into "the enclave holds the key for *this*
  connection";
* **the build is the reviewed one**: `compose_hash`, `app_id`, `release_id` and
  the platform measurements match pins you hold locally, not values the server
  chose;
* **the event log is consistent**: it replays to the quote's RTMRs.

> **The pins must not come from the server being checked**
>
> A server that could hand you the list of builds you accept could always name
> itself. Hold the plane to measurements you obtained independently. The SDK ships
> reviewed pins inside the package for exactly this reason, and fails rather than
> falling back to whatever the origin claims.

### Checking it without writing a verifier

Both SDK packages install `anonrouter-verify`, which does the whole document and
exits nonzero unless the assurance you asked for held:

```bash
anonrouter-verify gateway --origin https://api.anonrouter.ai \
  --dcap --require hardware_verified
echo $?   # 0 met, 1 not met, 2 the command was wrong
```

Hop 1 is the one place a `hardware_verified` result is reachable, and only with a
DCAP engine that actually chained the quote's ECDSA signature to Intel's roots
with an accepted TCB status. Without an engine the honest ceiling is
`cryptographically_checked`, and a policy demanding hardware verification fails
closed rather than quietly settling for less. See the
[SDK verification guide](/sdk/confidential) and the
[command reference](/sdk/verify-cli).

> **The pin SDK v0.1.0 ships is older than the running plane**
>
> A shipped pin is a snapshot of a reviewed release, and the content plane has
> been released since `v0.1.0` was tagged. The pin in that version names the
> previous build, so the command above **fails closed** against production today
> on `compose_hash_pinned` and `release_pinned` rather than passing. That is the
> design working (the alternative would be an SDK that accepts whatever the
> origin currently claims), but until a refreshed pin ships, gate on a policy you
> reviewed yourself with `--policy`. [Check the running build's `compose_hash`
> and `release_id`](#the-endpoint) against the release you reviewed.

### What hop 1 does not cover

* **It says nothing about the provider.** That is hop 2, below.
* **It does not make a TEE route end-to-end encrypted.** Inside the enclave, the
  relay handles your plaintext to route and meter it. What that buys you is that
  a build changed to exfiltrate it would change the measurements and stop
  verifying: cheating is *detectable*, not impossible. Only an
  [E2EE route](/e2ee) keeps content opaque to AnonRouter's build whatever code it
  is running.
* **It says nothing about what the provider does with the prompt.** On an
  ordinary route the worker forwards plaintext to the upstream provider, which
  sees it under that provider's own policy. Check the
  [privacy label on the route](/models).
* **Attestation names the build; it does not review it.** Reading the source
  behind a pinned compose hash is a separate act.

## Support matrix

| Provider                 | Maximum verification                | AnonRouter modality                    | Per-request receipt                                                                       |
| ------------------------ | ----------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------- |
| Chutes confidential text | `provider-attested`                 | Non-streaming ML-KEM whole-body `e2ee` | Not exposed by the provider; returns `tee_signature_not_supported`                        |
| Tinfoil                  | `sdk-verified`                      | `tee` over TLS/JSON                    | Not exposed by the provider; returns `tee_signature_not_supported`                        |
| Venice                   | `provider-attested`                 | `e2ee`                                 | EIP-191 enclave receipt for completed non-stream and streaming requests                   |
| NEAR AI direct           | `provider-attested`                 | Opaque `e2ee`, version 2               | EIP-191 receipt with exact AnonRouter wire-hash binding for completed non-stream requests |
| NEAR AI attested-3p      | No direct model-enclave attestation | `tee`                                  | No receipt through the direct flow                                                        |

> **NEAR AI has no callable route in the current catalog**
>
> The NEAR verifier and the version 2 opaque protocol below are implemented and
> documented, but the live catalog publishes **no callable NEAR AI route**. Every
> NEAR example on this page is therefore a protocol reference rather than a
> request you can run today. Read
> [`GET /v1/catalog/models`](/models#public-catalog) for what is actually
> callable before pinning a provider.

The confidential routes the catalog publishes as callable today are Phala AI
`tee`, Tinfoil `tee` and Venice `e2ee`. Chutes has no callable route; its rows
below are a protocol reference. The one callable confidential embedding route is
Tinfoil's `nomic-ai/nomic-embed-text`, which is `tee` rather than `e2ee`.

Tinfoil documents EHBP/HPKE transport, but its model selector is encrypted and
the documented outer headers do not bind that model to an AnonRouter ticket or
reservation. AnonRouter fails closed rather than expose a proxy through which a
caller could ticket one model and encrypt a request for another. Tinfoil routes
are therefore not advertised as E2EE.

## Verification levels

| Level               | What the result proves                                                                                                                                                                                                                                                                                                                       |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hardware-verified` | Quote signatures, certificate and revocation chains, acceptable TCB, GPU evidence, nonce and key bindings, debug state, and pinned measurements all passed to silicon-vendor trust roots. AnonRouter's own plane (hop 1) reaches this level with a DCAP engine. On a provider route, read each check's scope to see which machine it covers. |
| `sdk-verified`      | The official provider SDK completed its required cryptographic verification and the result matched AnonRouter's pinned release policy.                                                                                                                                                                                                       |
| `provider-attested` | Required structure, freshness, nonce/key, route, certificate-possession, and pinned-measurement checks passed, but the complete silicon-vendor chain was not independently established.                                                                                                                                                      |
| `unverified`        | A required check, policy value, or evidence item was absent or failed.                                                                                                                                                                                                                                                                       |
| `unsupported`       | The selected provider or route does not expose the requested capability.                                                                                                                                                                                                                                                                     |

The Intel/NVIDIA chain port remains intentionally unwired. The currently
available path does not provide all collateral, trust-root, revocation, and TCB
handling needed to make the stronger claim. Incomplete evidence fails closed at
`provider-attested` or `unverified`.

## Fetch fresh attestation evidence

Both public TEE endpoints require an API key with the `inference` scope. Use a
fresh random nonce containing 32 to 64 bytes, encoded as hexadecimal:

```bash
curl "https://control.anonrouter.ai/v1/tee/attestation?model=openai/gpt-oss-120b&provider=tinfoil&nonce=$NONCE" \
  -H "Authorization: Bearer $ANONROUTER_API_KEY"
```

The same evidence is available from the confidential origin without presenting a
key to it, using an [attestation ticket](/e2ee#attestation-tickets) instead.

The response identifies the exact provider route and privacy modality and
contains:

* `verification_level` and every individual check;
* normalized measurements and public signing identities;
* the measurement-policy version and expiry;
* the exact raw provider evidence for independent client verification.

Attestation results are cached for five minutes without prompts, responses,
private keys, or raw request bodies. A hostname or model name alone never earns
a verification level.

## Retrieve a request receipt

After a supported completion, use the provider's request ID returned with that
completion:

```bash
curl "https://control.anonrouter.ai/v1/tee/signature/$PROVIDER_REQUEST_ID?model=qwen/qwen-2.5-7b&provider=venice" \
  -H "Authorization: Bearer $ANONROUTER_API_KEY"
```

Optional `request_hash` and `response_hash` query parameters are additional
client cross-checks. They never replace the content-free hashes calculated and
stored by AnonRouter.

### NEAR exact-wire receipts

For a non-stream request, the credential worker hashes the exact JSON sent to
the direct enclave and the exact raw response before parsing. Receipt lookup
requires an unexpired route binding, a fresh matching attestation, exact hash
equality, and successful EIP-191 recovery to the attested signing address.

For a verified NEAR receipt, `hash_binding_checked: true` means the signed
request and response hashes equal the exact bytes observed by AnonRouter. NEAR
streaming does not yet provide the completed-response binding needed for this
claim and fails with `tee_signature_binding_unavailable`.

### Venice enclave receipts

Venice receipts are recovered with EIP-191 against the secp256k1 key committed
to the earlier TDX attestation. The signed statement, completion ID, model, and
`request.received`/`response.returned` event hashes must agree.

Venice signs an enclave-internal ingress/egress representation rather than the
exact public API wire bytes. A valid receipt therefore normally returns
`verified: true`, `verification_level: "provider-attested"`, and
`hash_binding_checked: false`. The response exposes both `bound_*` provider
hashes and `gateway_*` wire hashes instead of claiming equality Venice did not
sign.

Completed Venice streams are supported. Receipt metadata crosses the worker
boundary in a private sideband that is never emitted as a customer SSE event.
Aborted or incomplete streams produce no signature binding.

> **Unsupported is not a broken TEE route**
>
> Tinfoil inference and public attestation work normally. Tinfoil, like Chutes,
> does not expose a compatible per-completion receipt, so signature lookup returns the
> structured `501 tee_signature_not_supported` response. AnonRouter never
> fabricates a substitute gateway signature.

## NEAR v2 opaque E2EE

NEAR direct routes expose the provider-neutral v2 field-encryption modality.
Request a nonce-bound attestation ticket for the exact direct route, then send
ciphertext with this validated header set:

```text
X-AnonRouter-E2EE-Provider: near-ai
X-Signing-Algo: ed25519
X-Client-Pub-Key: <32-byte Ed25519 public key as 64 lowercase hex chars>
X-Encryption-Version: 2
```

Do not send `X-Model-Pub-Key`. That field belongs to NEAR's cloud gateway;
AnonRouter obtains the model key from the direct enclave's attestation.

For every encrypted field, the client uses ephemeral X25519 ECDH and
HKDF-SHA256 with `info = "ed25519_encryption"`, then encodes this lowercase-hex
wire value:

```text
[ephemeral X25519 public key: 32 bytes]
[XChaCha20-Poly1305 nonce: 24 bytes]
[ciphertext + authentication tag]
```

Encryption and decryption stay client-side. AnonRouter forwards only the
validated headers and opaque ciphertext, does not parse message fields as
plaintext, and never receives either private key.

Endpoint discovery must exactly match the reviewed direct enclave domain.
Missing or mismatched discovery fails with
`e2ee_enclave_endpoint_unavailable`; an E2EE request never falls back to the
cloud gateway, a plaintext route, or another provider.

## Chutes ML-KEM whole-body E2EE

Chutes has no callable route in the current catalog, so this section is a
protocol reference.

Confidential Chutes text routes are ciphertext-only through AnonRouter. First,
use the normal model-bound attestation-ticket flow. The returned raw evidence
contains `e2e_instances`: instance UUIDs, ML-KEM-768 public keys, and short-lived
single-use nonces. Accept a key only after verifying the response and its
`SHA256(caller nonce || ML-KEM key)` TDX report-data commitment.

Encrypt with Chutes' official transport format, then submit the exact bytes:

```bash
curl "https://api.anonrouter.ai/v1/e2ee/chat/completions" \
  -X POST \
  -H "Content-Type: application/octet-stream" \
  -H "X-AnonRouter-Ticket: $INFERENCE_TICKET" \
  -H "X-AnonRouter-E2EE-Provider: chutes" \
  -H "X-Chutes-Instance-Id: $ATTESTED_INSTANCE_ID" \
  -H "X-Chutes-E2E-Nonce: $SINGLE_USE_NONCE" \
  --data-binary @encrypted-request.bin \
  --output encrypted-response.bin
```

The credential worker resolves the ticket-bound model to its live `chute_id`,
requires `confidential_compute: true`, overwrites the chute, path, and streaming
headers, and adds the Chutes credential. Only the two validated instance/nonce
values cross from the client. Request and response bodies remain byte-for-byte
opaque to AnonRouter, and there is no provider fallback.

This first endpoint is non-streaming. Chutes keeps its non-stream token usage
inside the encrypted response, so the ticket must reserve the model's full output
ceiling. AnonRouter reserves the full context at the most expensive token rate
and conservatively captures it after success; it never estimates plaintext usage
from compressed ciphertext length. The ticket-mint request must therefore set
`max_tokens` to the model's published maximum.

The opt-in live suite performs a real ML-KEM-768 request/response round trip and
decrypts only in the test client; the relay receives only the encrypted blobs.

## What each provider check establishes

### Chutes

AnonRouter checks every returned instance: TDX v4 structure and debug state,
pinned MRTD/RTMR0-3 measurements, the nonce plus ML-KEM key commitment, the
certificate SPKI commitment, RSA possession of the decoded attested body,
inner/outer quote and GPU equality, certificate validity, and complete instance
key discovery. The missing complete Intel/NVIDIA vendor chain keeps the result
at `provider-attested`. For an `e2ee` result, the verified ML-KEM key commitment
also has to pass; the route never claims `hardware-verified`.

### Tinfoil

The credential worker runs the official `tinfoil@1.2.1` verifier. An
`sdk-verified` result requires `securityVerified === true`, all five SDK
verification steps, the selected endpoint, and exact pinned release digest,
code fingerprint, enclave fingerprint, and TLS key. A missing SDK or document
never defaults to success.

### NEAR direct

AnonRouter checks TDX v4 structure and debug state, NVIDIA evidence, the
nonce/key/TLS commitment, exact model and direct endpoint, `mr_config_id`, the
serialized `app_compose` hash, and pinned MRTD/RTMR0-3 measurements for that
model. Current evidence does not independently prove the model-weight digest or
complete silicon chain, so the maximum remains `provider-attested`.

### Venice

AnonRouter checks TDX v4 structure and debug state, caller nonce and model,
NVIDIA evidence, nested report-data equality, and a secp256k1 public key whose
derived address matches the signer committed into TDX report data. Venice's own
`verified` boolean is not accepted as proof.

> **Where a Venice E2EE route actually decrypts**
>
> E2EE guarantees that only the enclave you verified and encrypted to can read
> the prompt. On some Venice routes **that enclave is a verified gateway rather
> than the machine running the model**: it decrypts your request and forwards the
> plaintext onward inside Venice's own infrastructure. The attestation is real
> and AnonRouter still never sees plaintext, but "only the model host can read
> it" is a stronger claim than the evidence supports. The catalog states this per
> route in `privacyNotes`; read it before treating an E2EE label as a
> model-host guarantee.

### Phala AI

Phala AI routes are classified `tee`. Each request passes through Phala's
gateway, which forwards it to a confidential model session. Before any content
is sent, AnonRouter:

* verifies the gateway's Intel TDX quote to Intel's roots, with a fresh nonce,
  the gateway's TLS key, its measured deployment and its published forwarding
  code (`hardware-verified` is required);
* verifies the identity of one model session and that session's NVIDIA GPU
  attestation, then pins the request to that session.

The model machine's CPU attestation and production profile are enforced by the
verified gateway, not checked by AnonRouter directly. Those checks carry the
`model-machine` scope; AnonRouter's own checks carry `provider-gateway`. On some
routes the model machine is run by a separate compute provider.

Prompts are decrypted inside the verified gateway and model boundaries, so these
routes are not client-opaque E2EE. Phala AI exposes no per-request receipt from
the serving enclave, so signature lookup reports it as unsupported. A failed
verification refuses the request before any content is sent.

## What the API provides

The TEE API returns fresh authenticated evidence, the raw evidence for your own
verification, measurements and signing identities, and provider receipt lookup.
On top of that, AnonRouter enforces explicit verification levels, pinned
measurement policies, route bindings, server-side EIP-191 recovery, and exact
wire-hash checks where the provider signs those bytes.

What you can check depends on the provider: NEAR direct and Venice expose
cryptographically verifiable request receipts, while Chutes, Tinfoil and Phala
AI expose attestation without a compatible per-request receipt.

## Trust boundaries

* The relay, the compatibility broker and every provider worker run **inside the
  Intel TDX confidential VM**, which terminates TLS in-enclave. Prompts do not
  reach ordinary AnonRouter infrastructure, and the plane attests to that on
  demand ([hop 1](#verify-anonrouter-itself-hop-1)).
* Provider credentials stay on their provider-specific worker.
* Control receives only content-free metadata; it serves no request content at
  all, and answers `404` for `/v1/chat/completions`.
* Raw prompts, responses, API keys, decrypted E2EE content, and unsafe upstream
  bodies are never written to verification caches or logs.
* The dispatch fence still runs immediately before billable provider work.
* Measurement-policy changes require reviewed immutable pins rather than
  trusting a live provider assertion in place.

See the [E2EE client guide](/e2ee), [models and privacy labels](/models),
[intelligent routing](/routing), and [the complete privacy architecture](/privacy)
for how these capabilities affect route selection and the rest of the request
lifecycle.
