diff --git a/cloud/private-inference.mdx b/cloud/private-inference.mdx
index 05e01b1..c972e5f 100644
--- a/cloud/private-inference.mdx
+++ b/cloud/private-inference.mdx
@@ -26,7 +26,7 @@ NEAR AI Cloud's private inference provides three core guarantees:
Your prompts, model weights, and outputs are encrypted and isolated in hardware-secured environments. Infrastructure providers, model providers, and NEAR cannot access your data at any point in the process.
- Every computation generates cryptographic proof that it occurred inside a genuine, secure TEE. You can independently verify that your AI requests were processed in a protected environment without trusting any third party.
+ Attestation reports provide cryptographic evidence for a specific runtime and request path. When a response signature is available, you can also verify response integrity.
Hardware-accelerated TEEs with [NVIDIA Confidential Computing](https://www.nvidia.com/en-us/data-center/solutions/confidential-computing/) deliver high-throughput inference with minimal latency overhead, making private inference practical for real-world applications.
@@ -54,101 +54,39 @@ NEAR AI Cloud combines Intel TDX and NVIDIA TEE technologies to create isolated,
If you're using a standard OpenAI SDK or curl, your prompts are automatically protected by TLS encryption—no additional setup required.
-NEAR AI Cloud supports two connection modes — both provide TLS termination inside a TEE:
+For new integrations, use the NEAR AI Cloud Gateway. Its verification flow can establish the evidence required for a particular request path.
**Gateway mode** routes through `cloud-api.near.ai`, which runs in its own TEE before forwarding to the model TEE:
```mermaid
flowchart LR
- A["Your Machine"] -->|"🔒 Encrypted"| B["Gateway TEE"] -->|"🔒 Encrypted"| C["Model TEE: TLS Termination → LLM"]
+ A["Your Machine"] -->|"🔒 HTTPS"| B["NEAR AI Cloud gateway"] -->|"🔒 HTTPS"| C["Model TEE → LLM"]
style A fill:#1a1a2e,stroke:#4fc3f7,stroke-width:2px,color:#fff
style B fill:#1b5e20,stroke:#66bb6a,stroke-width:2px,color:#fff
style C fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff
```
-**Direct completions mode** connects you straight to the model's TEE — one hop, one TEE to verify:
-
-```mermaid
-flowchart LR
- A["Your Machine"] -->|"🔒 Encrypted"| C["Model TEE: TLS Termination → LLM"]
-
- style A fill:#1a1a2e,stroke:#4fc3f7,stroke-width:2px,color:#fff
- style C fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff
-```
-
-**Key insight:** In both modes, TLS termination happens inside the TEE (green boxes), not at an external load balancer. Your prompts remain encrypted until they reach the secure enclave.
+**Key insight:** TLS protects traffic in transit. When the Gateway TLS attestation flow passes for a particular connection, it binds that connection's endpoint to verified attestation evidence.
Here's why this works:
1. **Standard HTTPS = TLS encryption**: When you make API calls using the OpenAI SDK or curl, you're connecting via HTTPS. This means TLS encryption is applied automatically by your client before any data leaves your machine.
-2. **TLS terminates inside the TEE**: Unlike traditional cloud services where TLS terminates at an external load balancer, NEAR AI Cloud terminates TLS connections **inside the Trusted Execution Environment**. Your encrypted data travels from your laptop directly into the secure enclave before being decrypted.
-
-3. **No plaintext exposure**: Because TLS termination happens within the TEE, your prompts are never exposed in plaintext outside the hardware-secured environment—not to network infrastructure, not to cloud providers, not to anyone.
-
-This means you get the same seamless developer experience as any OpenAI-compatible API, while your data remains encrypted from your machine all the way into the secure TEE.
+2. **TLS endpoint verification**: Use Gateway TLS attestation when your application needs to verify the endpoint for a particular connection. The same TLS connection must supply both the peer certificate and attestation report.
-### Direct Completions
+3. **Evidence-driven trust**: Use attestation and the workload policy appropriate for your application to decide which environment properties are required.
-With direct completions, each model is available at its own subdomain under `completions.near.ai`. Your request goes straight to the model's TEE — no gateway hop needed.
-
-**Base URL format:**
-```
-https://{slug}.completions.near.ai/v1
-```
-
-**Available endpoints** (see [completions.near.ai/endpoints](https://completions.near.ai/endpoints) for the full list):
-
-| Subdomain | Model | Input | Output |
-|-----------|-------|-------|--------|
-| `glm-5-1.completions.near.ai` | GLM-5.1 | Text | Text |
-| `glm-5-2.completions.near.ai` | GLM-5.2 (`z-ai/glm-5.2`, `zai-org/GLM-5.2-FP8`) | Text | Text |
-| `qwen35-122b.completions.near.ai` | Qwen3.5-122B | Text | Text |
-| `qwen3-6-35b.completions.near.ai` | Qwen3.6-35B | Text | Text |
-| `qwen3-30b.completions.near.ai` | Qwen3-30B | Text | Text |
-| `gpt-oss-120b.completions.near.ai` | GPT-OSS-120B | Text | Text |
-| `gemma-4-31b.completions.near.ai` | Gemma 4 31B | Text | Text |
-| `qwen3-vl-30b.completions.near.ai` | Qwen3-VL-30B | Text, Image | Text |
-| `flux2-klein.completions.near.ai` | FLUX.2-klein-4B | Text | Image |
-| `whisper-large-v3.completions.near.ai` | Whisper Large V3 | Audio | Text |
-| `qwen3-embedding.completions.near.ai` | Qwen3-Embedding-0.6B | Text | Embedding |
-| `qwen3-reranker.completions.near.ai` | Qwen3-Reranker-0.6B | Text | Score |
-| `privacy-filter.completions.near.ai` | Privacy Filter | Text | Classification |
-
-Each subdomain exposes the served direct route set for that model:
-
-- `POST /v1/chat/completions` — Chat completions (with signing)
-- `POST /v1/completions` — Text completions
-- `POST /v1/tokenize` — Tokenization
-- `POST /v1/embeddings` — Embeddings
-- `POST /v1/rerank` — Reranking
-- `POST /v1/score` — Scoring
-- `POST /v1/images/generations` — Image generation
-- `POST /v1/images/edits` — Image editing
-- `POST /v1/audio/transcriptions` — Audio transcription
-- `POST /v1/privacy/classify` — Privacy classification on privacy-filter deployments
-- `GET /v1/models` — Available models
-- `GET /v1/attestation/report` — TEE attestation (TDX + GPU)
-- `GET /v1/signature/{chat_id}` — Retrieve cached signatures
-
-`/v1/privacy/classify` is also forwarded by direct completions endpoints when the selected privacy-filter backend serves it. `/v1/privacy/redact` remains a gateway privacy endpoint on `https://cloud-api.near.ai/v1`.
-
-Gateway and direct completions responses include an `X-Request-Id` response header. Support may ask you for that opaque value when debugging a request. It is support/debugging metadata, not W3C `traceparent` or distributed trace context. If you send your own `X-Request-Id`, use a non-sensitive UUID value; `X-Request-Id` values must not contain secrets or PII. `X-Org-Id` and `X-Workspace-Id` are internal tenant headers; public clients cannot set or override them.
-
-**Benefits of direct completions:**
-- **Fewer hops** — Your request reaches the model TEE directly, reducing latency
-- **Simpler trust model** — Only the model TEE needs to be verified (no [gateway verification](/cloud/verification/gateway) required)
-- **TLS binds to attestation** — The `include_tls_fingerprint=true` parameter on the attestation endpoint binds the TLS certificate to the attestation report
+This gives you the same OpenAI-compatible development flow while allowing your application to verify the properties it requires.
### The Inference Process
-When you make a request to NEAR AI Cloud, your data flows through a secure pipeline designed to maintain privacy at every step. The exact flow depends on whether you use the gateway or a direct completions endpoint.
+When you make a request to NEAR AI Cloud through the Gateway, your data flows through a secure pipeline designed to maintain privacy at every step.
**Via Gateway (`cloud-api.near.ai`):**
1. **Request Initiation:**
- You send chat completion requests via HTTPS to the LLM Gateway. TLS encryption protects your data in transit, and the TLS connection terminates inside the TEE, ensuring your prompts are decrypted only within the secure environment.
+ You send chat completion requests via HTTPS to the LLM Gateway. TLS protects data in transit; use [NEAR AI Cloud gateway TLS connection binding](/cloud/verification/cloud-api/tls) when your application needs to verify the endpoint for a particular connection.
2. **Secure Request Routing:**
The LLM Gateway routes your request to the appropriate Private LLM Node based on the requested model, availability, and load balancing requirements.
@@ -157,27 +95,13 @@ When you make a request to NEAR AI Cloud, your data flows through a secure pipel
AI inference computations execute inside the Private LLM Node's TEE, where all data and model weights are protected by hardware-enforced isolation.
4. **Attestation Generation**
- The TEE generates CPU and GPU attestation reports that provide cryptographic proof of the environment's integrity and configuration.
+ Attestation reports provide evidence about the environment and its measured configuration.
- 5. **Cryptographic Signing:**
- The TEE cryptographically signs both your original request and the inference results to ensure authenticity and prevent tampering.
+ 5. **Response Signing:**
+ Supported flows can make a response signature available for integrity verification.
6. **Verifiable Response:**
- You receive the AI response along with cryptographic signatures and attestation data for independent verification.
-
-**Via Direct Completions (`{slug}.completions.near.ai`):**
-
- 1. **Direct Request:**
- You send chat completion requests via HTTPS directly to the model's subdomain. TLS terminates inside the model's TEE — there is no intermediate gateway.
-
- 2. **Secure Inference:**
- The model processes your request entirely within its TEE, where all data and model weights are protected by hardware-enforced isolation.
-
- 3. **Attestation & Signing:**
- The TEE generates attestation reports and cryptographically signs both your request and the inference results.
-
- 4. **Verifiable Response:**
- You receive the response with cryptographic signatures. Only [model verification](/cloud/verification/model) is needed — no gateway attestation to check.
+ Use [verification](/cloud/verification) to check the evidence required by your application. A response without an available signature is not response-verified.
---
@@ -215,13 +139,13 @@ NEAR AI Cloud's private inference implements multiple layers of security to prot
TEEs create isolated execution environments enforced at the hardware level, preventing unauthorized access to memory and computation even from privileged system administrators or cloud providers.
- **Secure Communication** :
-All communication between your applications and the LLM infrastructure uses TLS encryption. Critically, TLS termination occurs inside the TEE—not at an external load balancer—so your prompts remain encrypted until they reach the secure enclave.
+All communication between your applications and the LLM infrastructure uses TLS encryption. Use the route-specific [verification guide](/cloud/verification) to verify endpoint binding for a particular connection when that property is required.
- **Cryptographic Attestation** :
Every TEE environment generates cryptographic proofs that verify the integrity of the execution environment, allowing you to independently confirm your computations occurred in a genuine, unmodified TEE.
- **Result Authentication** :
-All AI outputs are cryptographically signed inside the TEE before leaving the secure environment, ensuring the authenticity and integrity of responses.
+When a response signature is available, it can be verified against the attested signer to check response integrity.
### Threat Protection
@@ -247,8 +171,8 @@ Cryptographic signatures generated inside the TEE ensure that responses cannot b
Understand how to verify and validate secure interactions with AI models
-
- Verify that your HTTPS connection terminates inside the TEE using hardware-backed TLS attestation
+
+ Verify gateway, model, TLS, and response evidence for requests sent through the NEAR AI Cloud gateway
Add client-side encryption for defense-in-depth protection of your messages
diff --git a/cloud/quickstart.mdx b/cloud/quickstart.mdx
index 1d84968..a78b940 100644
--- a/cloud/quickstart.mdx
+++ b/cloud/quickstart.mdx
@@ -21,7 +21,7 @@ icon: "rocket"
## Make Your First API Call
-NEAR AI Cloud uses an OpenAI-compatible API, making it easy to integrate with existing tools and libraries. You can connect through the **gateway** (`cloud-api.near.ai`) or **directly** to a model's own endpoint (`{slug}.completions.near.ai`).
+NEAR AI Cloud uses an OpenAI-compatible API, making it easy to integrate with existing tools and libraries. Connect through the NEAR AI Cloud Gateway at `cloud-api.near.ai`.
Replace `YOUR_API_KEY` with the API key you generated in the setup steps above.
@@ -29,10 +29,8 @@ NEAR AI Cloud uses an OpenAI-compatible API, making it easy to integrate with ex
**Request IDs**
- Gateway and direct completions responses include an `X-Request-Id` response header. Support may ask you for it when debugging a request. The value is opaque support/debugging metadata, not W3C `traceparent` or distributed trace context. If you send your own `X-Request-Id`, use a non-sensitive UUID value; `X-Request-Id` values must not contain secrets or PII. `X-Org-Id` and `X-Workspace-Id` are internal tenant headers; public clients cannot set or override them.
+ Gateway responses include an `X-Request-Id` response header. Support may ask you for it when debugging a request. The value is opaque support/debugging metadata, not W3C `traceparent` or distributed trace context. If you send your own `X-Request-Id`, use a non-sensitive UUID value; `X-Request-Id` values must not contain secrets or PII. `X-Org-Id` and `X-Workspace-Id` are internal tenant headers; public clients cannot set or override them.
-
-
The gateway routes your request to the appropriate model TEE:
@@ -97,82 +95,6 @@ console.log(completion.choices[0].message.content);
-
-
-
-Direct completions connect you straight to the model's TEE — TLS terminates inside the model enclave with no intermediate gateway hop.
-
-Each model has its own subdomain. For example, Qwen3.5-122B is available at `qwen35-122b.completions.near.ai`:
-
-
-
-
-```bash
-curl https://qwen35-122b.completions.near.ai/v1/chat/completions \
--H "Content-Type: application/json" \
--H "Authorization: Bearer YOUR_API_KEY" \
--d '{
- "model": "Qwen/Qwen3.5-122B-A10B",
- "messages": [{
- "role": "user",
- "content": "Hello, NEAR AI!"
- }]
-}'
-```
-
-
-
-
-```python
-import openai
-
-client = openai.OpenAI(
- base_url="https://qwen35-122b.completions.near.ai/v1",
- api_key="YOUR_API_KEY"
-)
-
-response = client.chat.completions.create(
- model="Qwen/Qwen3.5-122B-A10B",
- messages=[{
- "role": "user", "content": "Hello, NEAR AI!"
- }]
-)
-
-print(response.choices[0].message.content)
-```
-
-
-
-
-```javascript
-import OpenAI from 'openai';
-
-const openai = new OpenAI({
- baseURL: 'https://qwen35-122b.completions.near.ai/v1',
- apiKey: 'YOUR_API_KEY',
-});
-
-const completion = await openai.chat.completions.create({
- model: 'Qwen/Qwen3.5-122B-A10B',
- messages: [{
- role: 'user', content: 'Hello, NEAR AI!'
- }]
-});
-
-console.log(completion.choices[0].message.content);
-```
-
-
-
-
-
- **Available Endpoints**
-
- See the full list of available direct completions endpoints at [completions.near.ai/endpoints](https://completions.near.ai/endpoints). Examples include `qwen35-122b.completions.near.ai`, `qwen3-30b.completions.near.ai`, `gpt-oss-120b.completions.near.ai`, and more.
-
-
-
-
### Expected Response
The API will return a JSON response containing the model's completion:
diff --git a/cloud/verification/chat.mdx b/cloud/verification/chat.mdx
deleted file mode 100644
index 9e92f47..0000000
--- a/cloud/verification/chat.mdx
+++ /dev/null
@@ -1,334 +0,0 @@
----
-title: "Chat Message Verification"
-sidebarTitle: "Chat Verification"
----
-
-
-You can verify each chat message with NEAR AI Cloud. For this you will need:
-
-1. [Chat Message **REQUEST** Hash](#chat-message-request-hash)
-2. [Chat Message **RESPONSE** Hash](#chat-message-response-hash)
-3. [Chat Message Signature](#chat-message-signature)
-
-
- See an example implementation in the [NEAR AI Cloud Verification Example](https://github.com/near-examples/nearai-cloud-verification-example) repo.
-
-
- **Two signature kinds**
-
- There are two kinds of chat signatures, distinguished by the `signature_kind` field on the gateway's signature response — see [Signature Kinds](#signature-kinds):
-
- - **`provider_tee`** — generated **inside the model TEE** over the exact bytes the TEE received and sent. This is always what you get from a model's [direct completions endpoint](/cloud/private-inference#direct-completions) (`{slug}.completions.near.ai`) — the examples below use `qwen35-122b.completions.near.ai`.
- - **`gateway`** — generated **inside the gateway TEE** (`cloud-api.near.ai`) over the exact bytes the gateway returned to you. Used when the gateway rewrites streamed response bytes (OpenAI-spec usage accounting), so the model TEE's byte-exact signature could no longer match what you received.
-
----
-
-## Chat Message Request Hash
-
-The value is calculated from the **exact JSON request body string** as it was sent over the wire.
-
-**_Example:_**
-
-```json
-{"messages":[{"content":"Respond with only two words.","role":"user"}],"stream":true,"model":"Qwen/Qwen3.5-122B-A10B","chat_template_kwargs":{"enable_thinking":false}}
-```
-
-Which hashes to:
-
-```bash
-2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad
-```
-
-Here is an example of how to get the sha256 hash of your message request body:
-
-
-
-
-```js
-import crypto from 'crypto';
-
-const requestBody = JSON.stringify({
- "messages": [
- {
- "content": "Respond with only two words.",
- "role": "user"
- }
- ],
- "stream": true,
- "model": "Qwen/Qwen3.5-122B-A10B",
- "chat_template_kwargs": {"enable_thinking": false}
-});
-
-const hash = crypto.createHash('sha256').update(requestBody).digest('hex');
-console.log(hash); //2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad
-```
-
-
-
-
-```python
-import hashlib
-import json
-
-request_body = {
- "messages": [
- {
- "content": "Respond with only two words.",
- "role": "user"
- }
- ],
- "stream": True,
- "model": "Qwen/Qwen3.5-122B-A10B",
- "chat_template_kwargs": {"enable_thinking": False}
-}
-
-# Convert to JSON string with same formatting as JavaScript
-request_body_str = json.dumps(request_body, separators=(',', ':'))
-
-# Calculate SHA-256 hash
-hash_obj = hashlib.sha256(request_body_str.encode())
-hash_hex = hash_obj.hexdigest()
-print(hash_hex) #2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad
-```
-
-
-
-
----
-
-## Chat Message Response Hash
-
-This value is calculated from the **exact response payload string**.
-
-
- Please note that the streaming response contains two new lines at the end and should not be omitted when copying the response as the hash value will change.
-
-**_Example Response Body:_**
-
-```bash
-data: {"choices":[{"delta":{"content":"","reasoning_content":null,"role":"assistant"},"finish_reason":null,"index":0,"logprobs":null,"matched_stop":null}],"created":1780404899,"id":"afa7975eaf844b1888776cf41548e230","model":"Qwen/Qwen3.5-122B-A10B","object":"chat.completion.chunk"}
-
-data: {"choices":[{"delta":{"content":"Under","reasoning_content":null},"finish_reason":null,"index":0,"logprobs":null,"matched_stop":null}],"created":1780404899,"id":"afa7975eaf844b1888776cf41548e230","model":"Qwen/Qwen3.5-122B-A10B","object":"chat.completion.chunk"}
-
-data: {"choices":[{"delta":{"content":"stood.","reasoning_content":null},"finish_reason":null,"index":0,"logprobs":null,"matched_stop":null}],"created":1780404899,"id":"afa7975eaf844b1888776cf41548e230","model":"Qwen/Qwen3.5-122B-A10B","object":"chat.completion.chunk"}
-
-data: {"choices":[{"delta":{"reasoning_content":null},"finish_reason":"stop","index":0,"logprobs":null,"matched_stop":248046}],"created":1780404899,"id":"afa7975eaf844b1888776cf41548e230","model":"Qwen/Qwen3.5-122B-A10B","object":"chat.completion.chunk"}
-
-data: {"choices":[],"created":1780404899,"id":"afa7975eaf844b1888776cf41548e230","model":"Qwen/Qwen3.5-122B-A10B","object":"chat.completion.chunk","usage":{"completion_tokens":4,"prompt_tokens":18,"prompt_tokens_details":null,"reasoning_tokens":0,"total_tokens":22}}
-
-data: [DONE]
-
-```
-
-Which hashes to:
-
-```bash
-8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c
-```
-
-Here is an example of how to get the sha256 hash of your message response payload:
-
-
-
-
-
-```js
-const response = await fetch('https://qwen35-122b.completions.near.ai/v1/chat/completions', {
- method: 'POST',
- headers: {
- 'accept': 'application/json',
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${process.env.NEARAI_CLOUD_API_KEY}`
- },
- body: requestBody
-});
-
-const responseBody = await response.text();
-const hash = crypto.createHash('sha256').update(responseBody).digest('hex');
-console.log(hash); // 8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c
-```
-
-
-
-
-```python
-import hashlib
-import requests
-import os
-
-response = requests.post(
- 'https://qwen35-122b.completions.near.ai/v1/chat/completions',
- headers={
- 'accept': 'application/json',
- 'Content-Type': 'application/json',
- 'Authorization': f'Bearer {os.environ["NEARAI_CLOUD_API_KEY"]}'
- },
- data=request_body_str # Uses the exact string from the previous example
-)
-
-response_body = response.text
-hash_obj = hashlib.sha256(response_body.encode())
-hash_hex = hash_obj.hexdigest()
-print(hash_hex) # 8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c
-```
-
-
-
-
----
-
-## Chat Message Signature
-
-From the Chat Message Response you will get a unique chat `id` that is used to fetch the Chat Message Signature from NEAR AI Cloud.
-
-You can query the signature API with the value of `id` from the response after chat completion.
-
-Use one of the following endpoints to get the signature:
-
-**Via Direct Completions:**
-```bash
-GET https://{slug}.completions.near.ai/v1/signature/{chat_id}?signing_algo=ecdsa
-```
-
-**Via Gateway:**
-```bash
-GET https://cloud-api.near.ai/v1/signature/{chat_id}?model={model_id}&signing_algo=ecdsa
-```
-
-For example, the `id` from the response in the previous section is:
-
- `afa7975eaf844b1888776cf41548e230`
-
-```bash
-# Via direct completions:
-curl -X GET 'https://qwen35-122b.completions.near.ai/v1/signature/afa7975eaf844b1888776cf41548e230?signing_algo=ecdsa' \
- -H "Content-Type: application/json" \
- -H "Authorization: Bearer "
-```
-
-
- A model can be served by multiple TEE nodes behind the same domain. The signature is cached on the node that served your chat completion, so a lookup may transiently return `Chat id not found or expired` if it lands on a different node — simply retry until you hit the right one.
-
-***Example Response:***
-
-```json
-{
- "text":"Qwen/Qwen3.5-122B-A10B:2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad:8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c",
- "signature":"0xed381e84d059198d1826e44dbbbac9501caaa8f79f913f27578acafa5be852e6103fe34fabd6446d7fde3f5250c0a16e109fe088a562bae08ac13d081a66d0761b",
- "signing_address":"0x6525e128afcffebf7eed05d485d7be983cdae934",
- "signing_algo":"ecdsa"
-}
-```
-
-The above response gives us all of the crucial information we need to verify that the message was executed in our trusted environment:
-
-- `text` - The signed payload. Its format depends on the [signature kind](#signature-kinds): `{model_id}:{request_hash}:{response_hash}` for a model-TEE signature (as in this example), `{request_hash}:{response_hash}` for a gateway signature
-- `signature` - This is the cryptographic signature of the `text` field, generated using the TEE's private key
-- `signing_address` - Public key of the TEE that signed (the model TEE for `provider_tee`, the gateway TEE for `gateway`)
-- `signing_algo` - Cryptography curve used to sign
-- `signature_kind` - Which key produced the signature: `"provider_tee"` or `"gateway"` (gateway endpoint only — see [Signature Kinds](#signature-kinds))
-
-You can see that `text` is:
-
-`Qwen/Qwen3.5-122B-A10B:2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad:8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c`
-
-This exactly matches the model we requested and the values we calculated in the previous sections:
-
-- Model: `Qwen/Qwen3.5-122B-A10B`
-- Request hash: `2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad`
-- Response hash: `8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c`
-
----
-
-## Signature Kinds
-
-Signatures fetched from the gateway (`GET https://cloud-api.near.ai/v1/signature/{chat_id}`) carry a `signature_kind` field that tells you which key signed and what the `text` payload contains:
-
-### `provider_tee`
-
-- **Who signs:** the model TEE that served your request, with the signing key from its [model attestation](/cloud/verification/model). Verify `signing_address` against that attestation.
-- **Payload:** `text = "{model_id}:{request_hash}:{response_hash}"`.
-- **What it covers:** the exact request bytes the model TEE received and the exact response bytes it sent. When you use a [direct completions endpoint](/cloud/private-inference#direct-completions), the bytes you sent and received are those bytes, so both hashes verify byte-for-byte against your own traffic. (Direct completions signature responses come straight from the model TEE and do not carry a `signature_kind` field — they are always model-TEE signatures.)
-
-### `gateway`
-
-- **Who signs:** the gateway TEE (`cloud-api.near.ai`), with the same signing key whose address is returned by the [gateway attestation report](/cloud/verification/gateway) (`GET /v1/attestation/report`). Verify `signing_address` against that report.
-- **Payload:** `text = "{request_hash}:{response_hash}"` — no model-ID prefix.
-- **What it covers:** the exact request body you sent to the gateway and the exact response bytes the gateway returned to you, so both hashes verify byte-for-byte against your own traffic.
-
-The gateway signs a streamed response itself when it has rewritten the stream bytes for OpenAI compatibility, which makes the model TEE's byte-exact signature unable to match what you received:
-
-- **Usage accounting** — with `stream_options: {"include_usage": true}`, the gateway rewrites intermediate chunks to carry `usage: null` and appends a single final usage chunk.
-- **Usage stripping** — on attested models, default streaming (no `stream_options`) and `"include_usage": false` streams have per-chunk usage nulled out and the upstream's trailing usage-only chunk dropped.
-
-In both cases the gateway stores its signature **before** emitting the final `data: [DONE]` line, so the signature is retrievable the moment the stream ends. The gateway signature also covers attested third-party fallback serving (`x-serving-provider: chutes` response header): those backends protect response integrity with an end-to-end encrypted channel rather than a per-response model signature, so a rewritten stream served by them is signed by the gateway alone.
-
-
- **Legacy signatures**
-
- Signatures stored before `signature_kind` was recorded omit the field — their provenance is unknown rather than guessed. You can still distinguish them structurally: a `text` with three `:`-separated fields (`{model_id}:{request_hash}:{response_hash}`) is a model-TEE payload; one with two (`{request_hash}:{response_hash}`) is a gateway payload.
-
----
-
-## Verify Signature
-
-Signature verification can be easily done with any standard ECDSA verification library such as [ethers](https://www.npmjs.com/package/ethers) or even an online tool such as [etherscan's VerifySignatures](https://etherscan.io/verifiedSignatures).
-
-These tools will require:
-
-- `Address`: What the expected address is for the signature. In our case it will be the one retrieved from your [attestation API query](/cloud/verification/model) (see Model Verification page).
-- `Message`: The original message before signing. In our case it will be the `text` field from [Chat Message Signature](#chat-message-signature) (`{model_id}:{request_hash}:{response_hash}`)
-- `Signature`: The signed message from above
-
-Here is an example of how to verify the Chat Message signature using `ethers`:
-
-
-
-
-```js
-import { ethers } from 'ethers';
-
-const text = "Qwen/Qwen3.5-122B-A10B:2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad:8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c";
-const signature = "0xed381e84d059198d1826e44dbbbac9501caaa8f79f913f27578acafa5be852e6103fe34fabd6446d7fde3f5250c0a16e109fe088a562bae08ac13d081a66d0761b";
-const expectedAddress = "0x6525e128afcffebf7eed05d485d7be983cdae934";
-
-// Recover the address from the signature
-const recoveredAddress = ethers.verifyMessage(text, signature);
-
-// Compare with expected address (case-insensitive)
-const isValid = recoveredAddress.toLowerCase() === expectedAddress.toLowerCase();
-
-console.log("Text:", text);
-console.log("Expected address:", expectedAddress);
-console.log("Recovered address:", recoveredAddress);
-console.log("Signature valid:", isValid);
-```
-
-
-
-
-```python
-from eth_account import Account
-from eth_account.messages import encode_defunct
-
-text = "Qwen/Qwen3.5-122B-A10B:2974f24b2a687856d2a0cf08d813902965c25e6552ba7062e4fa303432b6d2ad:8cb30eef9d133bdc6bfe812772dc4a62336d2827caea843546cbeff3f004c42c"
-signature = "0xed381e84d059198d1826e44dbbbac9501caaa8f79f913f27578acafa5be852e6103fe34fabd6446d7fde3f5250c0a16e109fe088a562bae08ac13d081a66d0761b"
-expected_address = "0x6525e128afcffebf7eed05d485d7be983cdae934"
-
-# Create a message object that can be signed
-message = encode_defunct(text=text)
-
-# Recover the address from the signature
-recovered_address = Account.recover_message(message, signature=signature)
-
-# Compare with expected address (case-insensitive)
-is_valid = recovered_address.lower() == expected_address.lower()
-
-print("Text:", text)
-print("Expected address:", expected_address)
-print("Recovered address:", recovered_address)
-print("Signature valid:", is_valid)
-```
-
-
-
diff --git a/cloud/verification/cloud-api/gateway-attestation.mdx b/cloud/verification/cloud-api/gateway-attestation.mdx
new file mode 100644
index 0000000..604ab5a
--- /dev/null
+++ b/cloud/verification/cloud-api/gateway-attestation.mdx
@@ -0,0 +1,54 @@
+---
+title: "Gateway Attestation"
+description: "Verify the NEAR AI Cloud gateway attestation for requests sent to cloud-api.near.ai."
+---
+
+`gateway_attestation` is evidence for the NEAR AI Cloud gateway. It is not evidence for a model environment. Use [model attestations](/cloud/verification/cloud-api/model-attestations) when your policy also requires model evidence.
+
+## Request gateway evidence
+
+Generate the nonce in your client. It must be 32 random bytes encoded as 64 hexadecimal characters.
+
+```bash
+NONCE="$(openssl rand -hex 32)"
+
+curl --fail-with-body -G 'https://cloud-api.near.ai/v1/attestation/report' \
+ --data-urlencode 'signing_algo=ecdsa' \
+ --data-urlencode "nonce=${NONCE}" \
+ -H 'Accept: application/json' \
+ -H 'Authorization: Bearer '
+```
+
+The response contains the gateway report at `gateway_attestation`. For a deployment-first completion, verify and retain it before sending the completion. Add `model=`, `provider=near`, and the `x-no-aliasing: true` header when you also need [NEAR model evidence](/cloud/verification/cloud-api/model-attestations). That request returns NEAR model reports in `model_attestations[]`; verify every entry before sending the completion. Add `include_tls_fingerprint=true` only when you are following the [NEAR AI Cloud gateway TLS flow](/cloud/verification/cloud-api/tls).
+
+## Verify the gateway report
+
+For `gateway_attestation`:
+
+1. Verify `intel_quote` with an Intel DCAP quote verifier, such as [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl), and apply your TCB and advisory policy.
+2. Read `report_data` and measurements from the **verified quote**, not only from fields echoed in the HTTP response. Reject the report if its echoed `request_nonce` or `report_data` is missing or differs from the nonce and report data you verified.
+3. Check that the verified quote binds the nonce generated by your client and the reported `signing_address`. Use `signing_algo` to interpret that identity and verify response signatures.
+4. Replay `event_log` as described in [Replay the RTMR3 event log](/cloud/verification/reference/quote-nonce-signer#replay-the-rtmr3-event-log) and require the result to equal the RTMR3 in the verified quote.
+5. Obtain the raw `info.tcb_info.app_compose` string. If `tcb_info` is JSON text, decode it first. Hash `app_compose` without parsing or reserializing it, and require the quote's 48-byte MRCONFIGID to equal `01`, then that SHA-256 hash, then 15 zero bytes. See [Check the configuration measurement](/cloud/verification/reference/quote-nonce-signer#check-the-configuration-measurement).
+6. When a TLS fingerprint was requested, verify its binding as part of the [NEAR AI Cloud gateway TLS flow](/cloud/verification/cloud-api/tls).
+7. Apply your accepted measurement and image-provenance policy.
+
+The verified quote is the cryptographic source of truth. The HTTP fields are consistency checks, not a substitute for quote verification.
+
+## Field scope
+
+| Field path | What it describes |
+| --- | --- |
+| `gateway_attestation.intel_quote` | The gateway's Intel TDX quote. |
+| `gateway_attestation.event_log` | Runtime measurement log. Replay it against the quote's RTMR3. |
+| `gateway_attestation.request_nonce` and `.report_data` | HTTP copies to compare with the client nonce and verified quote. |
+| `gateway_attestation.signing_address` and `.signing_algo` | The gateway signing identity and algorithm. |
+| `gateway_attestation.tls_cert_fingerprint` | The gateway TLS binding when requested. |
+| `gateway_attestation.info.tcb_info.app_compose` | Raw measured configuration string. Hash it and match it to the quote's MRCONFIGID. |
+| `model_attestations[]` | Separate NEAR model-evidence candidates when requested with `model` and `provider=near`. Verify every candidate separately; none is a substitute for the gateway quote. |
+
+## Freshness and failure handling
+
+The nonce establishes freshness only when the client generated and retained it, and the verified quote contains that nonce. If the report is unavailable, the quote fails, the nonce does not match, or the identity does not match, treat the gateway property as unverified.
+
+For a deployment-first response-signature flow, retain this verified report and require a later `gateway` signature to match its signer and algorithm. A report fetched after the completion cannot replace that preflight evidence, and a report from another signer is not a substitute.
diff --git a/cloud/verification/cloud-api/image-provenance.mdx b/cloud/verification/cloud-api/image-provenance.mdx
new file mode 100644
index 0000000..5e0eacc
--- /dev/null
+++ b/cloud/verification/cloud-api/image-provenance.mdx
@@ -0,0 +1,36 @@
+---
+title: "Image Provenance"
+description: "Apply an image-provenance policy to configurations measured in NEAR AI Cloud gateway and model attestations."
+---
+
+Image provenance is separate from hardware attestation. A verified quote can bind a configuration measurement to a runtime. Provenance determines whether the image digests in that configuration were built by a source and build process your policy accepts.
+
+For model-level provenance through the gateway, this flow applies to every NEAR model report returned by a request with `provider=near`.
+
+For a NEAR AI Cloud gateway flow, decide separately whether your policy requires provenance for:
+
+- the configuration measured by `gateway_attestation`
+- each NEAR model report in `model_attestations[]`, requested with `provider=near`
+- both
+
+## Verification flow
+
+1. Verify the relevant Intel TDX quote first. For `model_attestations[]`, verify each candidate report separately before treating it as eligible evidence.
+2. For each eligible NEAR model report, use the raw `info.tcb_info.app_compose` string. Calculate its SHA-256 hash without parsing or reserializing it, and compare it with the configuration measurement in that report's verified quote.
+3. Extract every immutable image digest (`@sha256:…`) from the matched configuration.
+4. Cryptographically verify provenance for each required digest and confirm that the statement's subject equals the image digest exactly.
+5. Apply your policy for repository, build identity, workflow, ref, and source revision.
+
+
+ A tag, search result, or HTTP `200` only locates a possible record. It does not cryptographically verify the provenance statement, its signer, or its subject digest.
+
+
+## Missing evidence
+
+An unavailable provenance record, including an HTTP `404`, means the reported digest could not be verified under that policy. It does not by itself identify a security incident.
+
+Fetch a fresh report after a deployment change. If the current evidence still lacks provenance required by your policy, treat that image as unverified.
+
+
+ When requesting help, share only non-sensitive verification metadata: the endpoint, UTC time, requested model, signing address, `info.instance_id` when present, image name, and full digest. Never include API keys, prompts, or response content.
+
diff --git a/cloud/verification/cloud-api/index.mdx b/cloud/verification/cloud-api/index.mdx
new file mode 100644
index 0000000..ecaf93b
--- /dev/null
+++ b/cloud/verification/cloud-api/index.mdx
@@ -0,0 +1,36 @@
+---
+title: "Verify the NEAR AI Cloud Gateway"
+description: "Verify the gateway, model evidence, connection, response, and software evidence for requests sent through cloud-api.near.ai."
+---
+
+Use this guide when your application sends inference requests to `https://cloud-api.near.ai`.
+
+## What you can verify
+
+
+
+ Verify the NEAR AI Cloud gateway deployment before an inference request.
+
+
+ Verify every returned NEAR model-attestation candidate before an inference request.
+
+
+ Verify that your live HTTPS connection terminates at the attested gateway.
+
+
+ Verify exact response bytes against the matching preflight signer.
+
+
+ Check that the software images in the attested environment meet your source and build policy.
+
+
+
+## Verify a completion
+
+1. Before the completion, request and verify fresh gateway evidence. If your policy requires NEAR model evidence, request it with the canonical model ID and `provider=near`, verify every returned candidate, and retain the verified results. For Gateway TLS binding, follow the same-connection [TLS flow](/cloud/verification/cloud-api/tls).
+2. Send the completion with the same canonical model ID and `x-no-aliasing: true`. Retain the exact request and response bytes.
+3. Fetch the completion signature. Its `signature_kind` selects the verified gateway report or the uniquely matching verified model report; it does not decide which deployment checks your policy required.
+
+Use one explicit `signing_algo` for the deployment reports and completion signature. Do not rely on endpoint defaults to choose a matching signer.
+
+Start with [Gateway attestation](/cloud/verification/cloud-api/gateway-attestation). The other guides provide the individual checks.
diff --git a/cloud/verification/cloud-api/model-attestations.mdx b/cloud/verification/cloud-api/model-attestations.mdx
new file mode 100644
index 0000000..8e847cb
--- /dev/null
+++ b/cloud/verification/cloud-api/model-attestations.mdx
@@ -0,0 +1,76 @@
+---
+title: "NEAR Model Attestations"
+description: "Verify the NEAR model-attestation report returned through the NEAR AI Cloud gateway."
+---
+
+Use this page when a request to `cloud-api.near.ai` needs evidence for a NEAR-operated model environment in addition to the gateway. Request that report explicitly with `provider=near`.
+
+## Request model evidence
+
+Include the canonical model ID when requesting the report. Do not use an alias: send `x-no-aliasing: true` so the request fails instead of being rewritten to another model ID.
+
+```bash
+NONCE="$(openssl rand -hex 32)"
+
+curl --fail-with-body -G 'https://cloud-api.near.ai/v1/attestation/report' \
+ --data-urlencode 'model=' \
+ --data-urlencode 'provider=near' \
+ --data-urlencode 'include_tls_fingerprint=false' \
+ --data-urlencode 'signing_algo=ecdsa' \
+ --data-urlencode "nonce=${NONCE}" \
+ -H 'Accept: application/json' \
+ -H 'x-no-aliasing: true' \
+ -H 'Authorization: Bearer '
+```
+
+The response includes gateway evidence and NEAR model-report candidates in `model_attestations[]`. The field is omitted when no candidate is available. If your policy requires NEAR model evidence, reject a missing or empty `model_attestations[]`. Before sending a completion, verify every candidate and retain the verified results; do not choose a raw entry yourself.
+
+For a later `provider_tee` response signature, select exactly one retained verified report whose `signing_address` and `signing_algo` match the signature. If no report or more than one report matches, the response is unverified. A report fetched after the completion cannot replace this preflight evidence.
+
+
+ `provider=near` requests NEAR model reports from this endpoint. It does not select the provider for a later completion. A `provider_tee` response is verified only when its signer and algorithm uniquely match a retained verified report.
+
+
+## NEAR model report fields
+
+The NEAR report uses these fields:
+
+| Field | Purpose |
+| --- | --- |
+| `intel_quote` | Intel TDX evidence. |
+| `event_log` | Runtime measurement log. Replay it and match the result to the quote's RTMR3. |
+| `request_nonce` | Echoed request nonce. Require it to match the client nonce, then confirm the nonce binding from the verified quote. |
+| `report_data` | Not currently returned in model reports. Read report data from the verified quote. |
+| `signing_address` and `signing_algo` | Model signing identity and its algorithm. |
+| `signing_public_key` | Model public key used by the E2EE flow; other flows do not need it. With `signing_algo=ed25519` it is the Ed25519 key for the default E2EE flow; with `ecdsa` it is the secp256k1 key for ECDSA E2EE. |
+| `nvidia_payload` | NVIDIA GPU evidence, when present. |
+| `info.tcb_info.app_compose` | Raw measured configuration string. Hash it and match it to the quote's MRCONFIGID. `tcb_info` can itself be JSON text. |
+
+## Verify NEAR model reports
+
+Perform these checks on every returned candidate before sending a completion:
+
+1. Verify `intel_quote` with an Intel DCAP quote verifier, such as [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl), and apply your TCB and advisory policy.
+2. Read report data from the verified quote. Require it to bind the client-generated nonce and `signing_address`; require `request_nonce` to match the client nonce. When `report_data` is present, require it to match the verified quote. Interpret the address using `signing_algo`.
+3. Replay `event_log` as described in [Replay the RTMR3 event log](/cloud/verification/reference/quote-nonce-signer#replay-the-rtmr3-event-log) and require the resulting RTMR3 to equal the RTMR3 in the verified quote.
+4. If `tcb_info` is JSON text, decode it to obtain `app_compose`. Hash the raw `app_compose` string without parsing or reserializing it. Require the quote's 48-byte MRCONFIGID to equal `01`, then that SHA-256 hash, then 15 zero bytes. See [Check the configuration measurement](/cloud/verification/reference/quote-nonce-signer#check-the-configuration-measurement).
+5. When GPU evidence is present, follow [GPU evidence verification](/cloud/verification/reference/quote-nonce-signer#verify-gpu-evidence-when-present).
+6. Apply your own allowlist and provenance policy to the verified measurements.
+
+After receiving a `provider_tee` response signature, select the one retained report whose signer identity and algorithm match the signature. If the match is absent or ambiguous, reject the response as unverified.
+
+
+ `model_attestations[]` is not an inventory of every model instance behind a URL. Instances of the same model share one signing key, including instances with different measured configurations, so a matching signer does not establish that the report and the completion came from the same serving instance.
+
+
+## Relationship to NEAR AI Cloud gateway TLS
+
+The browser or client connection is to `cloud-api.near.ai`. Its TLS endpoint binding is verified against `gateway_attestation`, not against a TLS fingerprint carried by an upstream model report. See [NEAR AI Cloud gateway TLS connection binding](/cloud/verification/cloud-api/tls).
+
+## Verify a signed response
+
+For a NEAR AI Cloud gateway response with `signature_kind: "provider_tee"`, verify the signature over the endpoint-defined payload and match its signer to exactly one previously verified model report. For a `gateway` signature, match the signer to the previously verified `gateway_attestation` instead. See [NEAR AI Cloud gateway response signatures](/cloud/verification/cloud-api/response-signatures).
+
+## Other provider documentation
+
+Chutes documents its own evidence format in [TEE Evidence Verification](https://github.com/chutesai/chutes-api/blob/main/docs/tee-verification.md).
diff --git a/cloud/verification/cloud-api/response-signatures.mdx b/cloud/verification/cloud-api/response-signatures.mdx
new file mode 100644
index 0000000..cc8881f
--- /dev/null
+++ b/cloud/verification/cloud-api/response-signatures.mdx
@@ -0,0 +1,74 @@
+---
+title: "Response Signatures"
+description: "Verify available response signatures from the NEAR AI Cloud gateway against exact bytes and matching preflight evidence."
+---
+
+Response signatures returned through the NEAR AI Cloud gateway can be produced by either a model-serving TEE or the gateway itself. Before sending a completion, verify the gateway and, when your policy requires model evidence, every returned NEAR model-attestation candidate. When present, `signature_kind` determines which verified preflight result must match the signer.
+
+
+ A response without an available signature is not a successfully verified response. If response verification is required by your policy, treat it as unverified after a short, bounded retry interval.
+
+
+## Preserve exact request and response bytes
+
+Build the request body once and retain the exact bytes sent by the client. Retain the raw response bytes before parsing JSON or Server-Sent Events.
+
+To verify a `provider_tee` signature, send the original completion with the canonical model ID and `x-no-aliasing: true`. Do not send an alias and replace it after receiving the response: the model ID in the original request is part of the signed text.
+
+```text
+request_hash = SHA-256(exact request-body bytes)
+response_hash = SHA-256(exact response-body bytes)
+```
+
+Do not parse and re-serialize JSON, normalize whitespace or line endings, omit stream delimiters, or reconstruct a streamed response from parsed events. The signature covers the uncompressed response body. Request `Accept-Encoding: identity`, or hash the body after your HTTP client decompresses it.
+
+## Fetch and verify the signature
+
+Set `` to the `id` in the completion response whose exact bytes you retained. For the Responses API, use its returned `resp_…` ID.
+
+```bash
+curl --fail-with-body -G 'https://cloud-api.near.ai/v1/signature/' \
+ --data-urlencode 'signing_algo=' \
+ -H 'Accept: application/json' \
+ -H 'Authorization: Bearer '
+```
+
+A successful (`2xx`) response containing `error_code` and `message` is an unavailable result, not a signature. A `404` means no matching signature was found; it can be retried for a short, bounded interval when the signature may still be recorded. Treat either result as unverified if a signature is required.
+
+Use the same explicit `signing_algo` for the attestation request and the signature request. Their defaults differ: `/v1/signature` defaults to `ecdsa`, while `/v1/attestation/report` defaults to `ed25519` for the gateway report and `ecdsa` for model reports.
+
+For `ecdsa`, recover the Ethereum EIP-191 signer from `text` and `signature`, then compare it with `signing_address`. For `ed25519`, verify the UTF-8 bytes of `text` using `signing_address` as the public key. Reject unsupported algorithms.
+
+Then compare `text` with the expected payload:
+
+| `signature_kind` | Expected `text` | Required preflight match |
+| --- | --- | --- |
+| `provider_tee` | `::` | Exactly one verified NEAR model report with the same `signing_address` and `signing_algo`. |
+| `gateway` | `:` | A verified `gateway_attestation` with the same `signing_address` and `signing_algo`. |
+
+Only `provider_tee` binds a response to a model-serving TEE. A `gateway` signature binds the response to the gateway, not to a model report. If a model signature is unavailable, model-level response binding is unavailable. Instances of the same model share one signing key, so a matching `provider_tee` signer does not establish that the verified report and the completion came from the same serving instance.
+
+The Gateway signs a response itself when it changes the bytes it relays: by default on streamed completions (it removes per-chunk usage data), when it resolves a model alias, and on the Responses API. A `gateway` signature covers the exact bytes received from the Gateway, including stream framing and terminal events.
+
+If `signature_kind` is absent or unfamiliar, do not infer it from the shape of `text`. Treat the signer classification as unavailable unless your endpoint contract gives a rule for that response.
+
+For `provider_tee`, `` is the canonical model identifier in the signed payload.
+
+Use the [manual response-signature helpers](/cloud/verification/reference/response-signature-verification) with the exact request and response bytes. For `provider_tee`, pass the canonical model ID from the original request to build the expected text and use the signer from the uniquely matching verified model report. For `gateway`, omit the model ID and use the verified Gateway report's signer.
+
+## Match preflight evidence
+
+Before sending the completion, request and verify gateway evidence with a client-generated nonce. To support `provider_tee`, also request NEAR model evidence with `model=` and `provider=near`. Reject a missing or empty `model_attestations[]`; verify every returned candidate and retain the verified results.
+
+After fetching the signature, use `signature_kind` to select the matching preflight result:
+
+- For `provider_tee`, require the signature signer and algorithm to match exactly one verified NEAR model report. If the match is absent or ambiguous, the response is unverified.
+- For `gateway`, require them to match the verified gateway report.
+
+Do not use a report obtained after the completion as a substitute for preflight evidence. If the signature's signer or algorithm differs from the required preflight evidence, the response is unverified under this flow.
+
+`ecdsa` identities are Ethereum-style signing addresses. `ed25519` identities are Ed25519 public-key bytes encoded as hexadecimal. Always compare the identity together with `signing_algo`.
+
+
+ Preflight Gateway and model evidence plus one response signature do not yet form a complete model-to-Gateway-to-final-response chain. A `gateway` signature proves that the verified Gateway signed the final client-visible bytes, not that the verified model produced an upstream response. A `provider_tee` signature does not bind its model-signed bytes to the preflight Gateway or TLS evidence.
+
diff --git a/cloud/verification/cloud-api/tls.mdx b/cloud/verification/cloud-api/tls.mdx
new file mode 100644
index 0000000..d81bf4e
--- /dev/null
+++ b/cloud/verification/cloud-api/tls.mdx
@@ -0,0 +1,44 @@
+---
+title: "TLS Connection Binding"
+description: "Bind a live NEAR AI Cloud gateway TLS connection to the gateway attestation."
+---
+
+Use this flow to establish that the HTTPS connection to `cloud-api.near.ai` terminates at the endpoint bound to `gateway_attestation`.
+
+
+ The peer certificate and attestation report must be obtained on the **same TLS connection**. Two independent connections can reach different instances or observe different certificates, so they cannot establish this connection-specific conclusion.
+
+
+
+ Browser Fetch APIs do not expose the peer certificate or its SPKI. A browser client can verify a gateway quote without TLS binding, but cannot establish this TLS connection-binding property.
+
+
+## What this checks
+
+When you request `include_tls_fingerprint=true`, `gateway_attestation.tls_cert_fingerprint` is the SHA-256 hash of the gateway certificate's SubjectPublicKeyInfo (SPKI). The verified quote binds that fingerprint, the gateway signing identity, and the nonce.
+
+This is a gateway check. It does not bind the client connection to an upstream model endpoint.
+
+## Verification flow
+
+1. Generate a fresh 32-byte nonce in the client.
+2. Open a TLS connection to `cloud-api.near.ai` using the trust configuration required by your application.
+3. Read the peer certificate from that connection and calculate `SHA-256(SPKI_DER)`.
+4. Reuse the same open connection to request `/v1/attestation/report` with `include_tls_fingerprint=true`, the nonce, and the signing algorithm you require. Include the normal `Authorization: Bearer ` header. NEAR AI Cloud gateway report retrieval requires an API key.
+5. Verify `gateway_attestation.intel_quote`, then check the nonce, gateway signer, and TLS-fingerprint binding in the verified quote.
+6. Compare the calculated peer SPKI hash with `gateway_attestation.tls_cert_fingerprint`.
+
+The optional `tls_certificate` response field is not a substitute for the peer certificate observed on the connection. A command-line report request alone also cannot establish the same-connection property.
+
+This result applies to the connection used for the evidence request. When it must also cover an inference request, send that request over the same open TLS connection. If the client reconnects, repeat the flow.
+
+## Required handling
+
+| Result | Action when TLS endpoint binding is required |
+| --- | --- |
+| `gateway_attestation.tls_cert_fingerprint` is absent | Treat TLS binding as unavailable. Request a new report with `include_tls_fingerprint=true`. |
+| Peer SPKI hash differs | Reject the connection and repeat the flow with one TLS connection. |
+| Quote, nonce, or signer binding fails | Reject the report and fetch fresh evidence. |
+| Certificate and report came from different connections | Treat the result as inconclusive. |
+
+Do not use a cached fingerprint as evidence for a new connection. Fetch new evidence after a certificate, signer, measurement, or accepted-attestation-age change.
diff --git a/cloud/verification/direct/image-provenance.mdx b/cloud/verification/direct/image-provenance.mdx
new file mode 100644
index 0000000..197c7c7
--- /dev/null
+++ b/cloud/verification/direct/image-provenance.mdx
@@ -0,0 +1,36 @@
+---
+title: "Experimental Image Provenance"
+description: "Apply an image-provenance policy to configuration measured by a direct model attestation."
+---
+
+
+ This experimental check applies to the direct report under inspection. It does not establish deployment evidence for a separate inference request to the same hostname.
+
+
+Image provenance is separate from hardware attestation. A verified direct model quote can bind a configuration measurement to a runtime. Provenance determines whether the image digests in that configuration were built by a source and build process your policy accepts.
+
+## Verification flow
+
+1. Verify the direct model's Intel TDX quote first.
+2. Use the raw `info.tcb_info.app_compose` string from the direct report. Calculate its SHA-256 hash without parsing or reserializing it, and compare it with the configuration measurement in the verified quote.
+3. Extract every immutable image digest (`@sha256:…`) from the matched configuration.
+4. Cryptographically verify provenance for each required digest and confirm that the statement's subject equals the image digest exactly.
+5. Apply your policy for repository, build identity, workflow, ref, and source revision.
+
+
+ An image tag, search result, or HTTP `200` only locates a possible record. It does not cryptographically verify the provenance statement, its signer, or its subject digest.
+
+
+## Relationship to direct attestation
+
+The quote and configuration measurement establish that the configuration text you matched is bound to the verified model report. Provenance establishes that a specific digest satisfies your source and build policy. Use both checks when your application requires a source-to-runtime trust chain.
+
+## Missing evidence
+
+An unavailable provenance record, including an HTTP `404`, means the reported digest could not be verified under that policy. It does not by itself identify a security incident.
+
+Fetch a fresh report after a deployment change. If the current evidence still lacks provenance required by your policy, treat that image as unverified.
+
+
+ When requesting help, share only non-sensitive verification metadata: the endpoint, UTC time, model name, signing address, `info.instance_id` when present, image name, and full digest. Never include API keys, prompts, or response content.
+
diff --git a/cloud/verification/direct/index.mdx b/cloud/verification/direct/index.mdx
new file mode 100644
index 0000000..a691d86
--- /dev/null
+++ b/cloud/verification/direct/index.mdx
@@ -0,0 +1,36 @@
+---
+title: "Experimental Direct Model Endpoints"
+description: "Experimental verification notes for direct completions endpoints."
+---
+
+
+ Direct model endpoint verification is experimental and is not recommended for new integrations or production verification workflows. Use the [NEAR AI Cloud Gateway](/cloud/verification/cloud-api) instead.
+
+
+Independent requests to the same direct hostname can reach different serving instances. An attestation report, including `all_attestations[]`, is evidence for the request that returned it; it does not establish deployment evidence for a later completion. A later response-signature request can return `404`, so do not use direct response signatures as a production verification gate.
+
+## Available experimental checks
+
+
+
+ Inspect the model environment reported by a direct endpoint.
+
+
+ Bind one live HTTPS connection to a direct report.
+
+
+ Inspect an available signature for exact response bytes.
+
+
+ Check that the software images in the attested environment meet your source and build policy.
+
+
+
+## Start with model attestation
+
+1. Generate a fresh nonce for this verification attempt.
+2. Request a fresh report from the same direct endpoint your application uses for inference.
+3. Verify the model's hardware attestation and apply your acceptance policy.
+4. Add TLS, response-signature, or image-provenance checks only for the direct request under inspection.
+
+Start with [Model attestation report](/cloud/verification/direct/model-attestation). Do not combine these experimental checks into a production deployment-verification conclusion for later independent requests.
diff --git a/cloud/verification/direct/model-attestation.mdx b/cloud/verification/direct/model-attestation.mdx
new file mode 100644
index 0000000..55dc259
--- /dev/null
+++ b/cloud/verification/direct/model-attestation.mdx
@@ -0,0 +1,58 @@
+---
+title: "Experimental Model Attestation Report"
+description: "Inspect the root model report returned by a direct completions endpoint."
+---
+
+
+ This experimental report is evidence for the request that returned it. It does not establish deployment evidence for a separate inference request to the same hostname.
+
+
+Use this page to inspect the model environment reported by a direct `https://.completions.near.ai` endpoint.
+
+## Request a fresh report
+
+Generate the nonce in your client as 32 random bytes encoded as 64 hexadecimal characters.
+
+```bash
+NONCE="$(openssl rand -hex 32)"
+
+curl --fail-with-body -G 'https://.completions.near.ai/v1/attestation/report' \
+ --data-urlencode 'signing_algo=ecdsa' \
+ --data-urlencode "nonce=${NONCE}" \
+ -H 'Accept: application/json'
+```
+
+To request evidence for a response signer, add `signing_address=` and use the response's `signing_algo`. Add `include_tls_fingerprint=true` only for [direct TLS connection binding](/cloud/verification/direct/tls).
+
+## Direct response shape
+
+The root response is the model report. Relevant fields include:
+
+| Field | Meaning |
+| --- | --- |
+| `model_name` | The model name stated in this report. |
+| `signing_address`, `signing_algo` | The model signing identity and algorithm. |
+| `request_nonce` | The nonce echoed for diagnostics. Verify it inside the quote. |
+| `report_data` | Not currently returned in direct model reports. Read report data from the verified quote. |
+| `intel_quote` | The model's Intel TDX quote. |
+| `event_log` | Runtime measurement log. Replay it and match the result to the quote's RTMR3. |
+| `nvidia_payload` | GPU evidence, when present. |
+| `info.tcb_info.app_compose` | Raw measured configuration string. Hash it and match it to the quote's MRCONFIGID. `tcb_info` can itself be JSON text. |
+| `tls_cert_fingerprint` | The direct endpoint TLS binding, when requested. |
+| `all_attestations[]` | Model-report entries in this direct response envelope. It is not a complete list of instances that can serve later requests. |
+
+## Verify the report
+
+1. Verify `intel_quote` with an Intel DCAP quote verifier, such as [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl), and apply your TCB and advisory policy.
+2. Check the client-generated nonce and signer identity against the verified quote. Read report data from the verified quote; require `request_nonce` to match the client nonce and, when `report_data` is present, require it to match the verified quote. Use `signing_algo` to interpret that identity and verify response signatures.
+3. Replay `event_log` as described in [Replay the RTMR3 event log](/cloud/verification/reference/quote-nonce-signer#replay-the-rtmr3-event-log) and require the resulting RTMR3 to equal the RTMR3 in the verified quote.
+4. If `tcb_info` is JSON text, decode it to obtain `app_compose`. Hash the raw `app_compose` string without parsing or reserializing it. Require the quote's 48-byte MRCONFIGID to equal `01`, then that SHA-256 hash, then 15 zero bytes. See [Check the configuration measurement](/cloud/verification/reference/quote-nonce-signer#check-the-configuration-measurement).
+5. When GPU evidence is present, follow [GPU evidence verification](/cloud/verification/reference/quote-nonce-signer#verify-gpu-evidence-when-present).
+6. Apply your accepted measurement policy to the verified configuration.
+7. If source and build provenance are required, continue with [direct image provenance](/cloud/verification/direct/image-provenance).
+
+When a response signature is being verified, match both `signing_address` and `signing_algo`. A direct report for another signer is not a substitute, even when the hostname and model name are the same.
+
+## Load balancing and freshness
+
+The same direct model hostname can serve more than one instance. Do not assume two independent HTTP requests reached the same instance, or use a report as evidence for a separate completion sent later. Fetch fresh evidence when the signer, certificate, measured configuration, or accepted-attestation-age policy changes.
diff --git a/cloud/verification/direct/response-signatures.mdx b/cloud/verification/direct/response-signatures.mdx
new file mode 100644
index 0000000..e28a5f5
--- /dev/null
+++ b/cloud/verification/direct/response-signatures.mdx
@@ -0,0 +1,54 @@
+---
+title: "Experimental Response Signatures"
+description: "Inspect an available direct-model response signature against exact bytes and a matching model report."
+---
+
+
+ A signature lookup can return `404` after a direct completion. Treat a missing signature as unavailable; direct response signatures are not a production verification gate.
+
+
+Use this experimental flow to inspect the signature over exact bytes for a completion sent directly to a model endpoint.
+
+## Preserve exact request and response bytes
+
+Build the request body once and retain the exact bytes sent by the client. Retain the raw response bytes before parsing JSON or Server-Sent Events.
+
+```text
+request_hash = SHA-256(exact request-body bytes)
+response_hash = SHA-256(exact response-body bytes)
+```
+
+Do not parse and re-serialize JSON, normalize whitespace or line endings, omit stream delimiters, or reconstruct a streamed response from parsed events. The signature covers the uncompressed response body. Request `Accept-Encoding: identity`, or hash the body after your HTTP client decompresses it.
+
+## Fetch and verify the signature
+
+Set `` to the `id` in the completion response whose exact bytes you retained.
+
+```bash
+curl --fail-with-body -G 'https://.completions.near.ai/v1/signature/' \
+ --data-urlencode 'signing_algo=' \
+ -H 'Accept: application/json' \
+ -H 'Authorization: Bearer '
+```
+
+Set `` to the explicit algorithm used for the matching model attestation.
+
+For `ecdsa`, recover the Ethereum EIP-191 signer from `text` and `signature`, then compare it with `signing_address`. For `ed25519`, verify the UTF-8 bytes of `text` using `signing_address` as the public key. Reject unsupported algorithms.
+
+Then require that `text` is exactly:
+
+```text
+::
+```
+
+Use the `model_name` reported by the direct model attestation. A different string, even if it resembles the hostname or a NEAR AI Cloud gateway model ID, does not satisfy this check. If the report has no non-empty `model_name`, this direct response-signature format cannot be verified.
+
+Use the [manual response-signature helpers](/cloud/verification/reference/response-signature-verification) with the exact request and response bytes, this `model_name`, and the signer from the verified direct model attestation.
+
+## Match the model signer
+
+Request or select a direct model attestation whose `signing_address` and `signing_algo` exactly match the signature. Verify its quote, client-generated nonce, signer binding, and required workload policy as described in [direct model attestation](/cloud/verification/direct/model-attestation). A matching signer does not establish that the report and completion came from the same serving instance.
+
+Do not infer a gateway or model classification from the format of the signed text, and do not substitute a report for a different signer.
+
+`ecdsa` identities are Ethereum-style signing addresses. `ed25519` identities are Ed25519 public-key bytes encoded as hexadecimal. Always compare the identity together with `signing_algo`.
diff --git a/cloud/verification/direct/tls.mdx b/cloud/verification/direct/tls.mdx
new file mode 100644
index 0000000..f525692
--- /dev/null
+++ b/cloud/verification/direct/tls.mdx
@@ -0,0 +1,40 @@
+---
+title: "Experimental TLS Connection Binding"
+description: "Bind one live direct-model TLS connection to a direct model attestation report."
+---
+
+Use this experimental flow to bind one HTTPS connection to the direct model attestation report it returns.
+
+
+ Obtain the peer certificate and direct attestation report over the **same TLS connection**. This result does not establish deployment evidence for later independent completion or signature requests. Use the NEAR AI Cloud Gateway for production verification workflows.
+
+
+## What this checks
+
+With `include_tls_fingerprint=true`, the direct report includes `tls_cert_fingerprint`: the SHA-256 hash of the direct endpoint certificate's SubjectPublicKeyInfo (SPKI). The verified model quote binds that fingerprint, the model signing identity, and the nonce.
+
+This check applies to the direct model connection. It does not verify a connection to `cloud-api.near.ai`.
+
+## Verification flow
+
+1. Generate a fresh 32-byte nonce in the client.
+2. Open a TLS connection to `https://.completions.near.ai` using the trust configuration required by your application.
+3. Read the peer certificate from that connection and calculate `SHA-256(SPKI_DER)`.
+4. Reuse the same open connection to request `/v1/attestation/report` with `include_tls_fingerprint=true`, the nonce, and the signing algorithm you require.
+5. Verify the direct report's Intel TDX quote, then check the nonce, model signer, and TLS-fingerprint binding in the verified quote.
+6. Compare the calculated peer SPKI hash with `tls_cert_fingerprint`.
+
+A command-line request can retrieve a report, but cannot by itself demonstrate that the report and peer certificate came from one TLS connection.
+
+This result applies only to that open TLS connection. Send the inference request on it. If the client reconnects, repeat this flow before treating the new connection as bound.
+
+## Required handling
+
+| Result | Action when TLS endpoint binding is required |
+| --- | --- |
+| `tls_cert_fingerprint` is absent | Treat the TLS binding as unavailable. Request a new report with `include_tls_fingerprint=true`. |
+| Peer SPKI hash differs | Reject the connection and repeat the flow with one TLS connection. |
+| Quote, nonce, or signer binding fails | Reject the report and fetch fresh evidence. |
+| Certificate and report came from different connections | Treat the result as inconclusive. |
+
+Do not use a cached fingerprint as evidence for a new connection. Fetch new evidence after a certificate, signer, measurement, or accepted-attestation-age change.
diff --git a/cloud/verification/gateway.mdx b/cloud/verification/gateway.mdx
deleted file mode 100644
index 25404c6..0000000
--- a/cloud/verification/gateway.mdx
+++ /dev/null
@@ -1,166 +0,0 @@
----
-title: "Gateway Verification"
----
-
-
-
- **Direct Completions — No Gateway Verification Needed**
-
- If you are using [direct completions endpoints](/cloud/private-inference#direct-completions) (e.g., `https://qwen35-122b.completions.near.ai`), your requests go straight to the model's TEE with no gateway in the path. In that case, only [Model Verification](/cloud/verification/model) is needed — you can skip gateway verification entirely.
-
-To verify the NEAR AI Cloud private inference gateway is operating in a secure trusted environment, you need to verify the gateway attestation. The gateway attestation proves that the API gateway itself runs in a Trusted Execution Environment (TEE).
-
-- [Request Gateway Attestation](#request-gateway-attestation) report from NEAR AI Cloud
-- [Verify Gateway Attestation](#verifying-gateway-attestation) report using Intel attestation authenticators
-
-
- See an example implementation in the [NEAR AI Cloud Verifier](https://github.com/nearai/nearai-cloud-verifier) repo.
-
----
-
-## Request Gateway Attestation
-
-The gateway attestation can be requested standalone or is included in the response when you request a model attestation. To request gateway attestation standalone, use the following `GET` API endpoint:
-
-```bash
-https://cloud-api.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce={nonce}
-```
-
-The `signing_algo` parameter specifies the signing algorithm used (`ecdsa` or `ed25519`). The `nonce` parameter is optional but recommended. It should be a randomly generated 64 character hexadecimal string (32 bytes) that ensures attestation freshness and prevents replay attacks. If not provided, the server will generate one for you.
-
-The endpoint requires an API key (`Authorization: Bearer `); report retrieval is free and never counts against your usage.
-
-If you want to verify that the HTTPS connection to `cloud-api.near.ai` terminates inside the gateway TEE, add `include_tls_fingerprint=true` and follow [TLS Attestation Verification](/cloud/verification/tls). That opt-in flag binds the gateway's TLS certificate fingerprint into `report_data`; it is disabled by default for compatibility with existing clients.
-
-
-
-
-```bash
-# Generate a random 64-character hex nonce (optional but recommended)
-NONCE=$(openssl rand -hex 32)
-
-curl "https://cloud-api.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce=${NONCE}" \
- -H 'accept: application/json' \
- -H 'Authorization: Bearer '
-```
-
-
-
-
-```js
-import crypto from 'crypto';
-
-// Generate a random 64-character hex nonce (optional but recommended)
-const nonce = crypto.randomBytes(32).toString('hex');
-
-const response = await fetch(
- `https://cloud-api.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce=${nonce}`,
- {
- headers: {
- 'accept': 'application/json',
- 'Authorization': `Bearer ${process.env.NEAR_AI_CLOUD_API_KEY}`,
- },
- }
-);
-```
-
-
-
-
-```python
-import os
-import requests
-import secrets
-
-# Generate a random 64-character hex nonce (optional but recommended)
-nonce = secrets.token_hex(32)
-
-response = requests.get(
- f'https://cloud-api.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce={nonce}',
- headers={
- 'accept': 'application/json',
- 'Authorization': f'Bearer {os.environ["NEAR_AI_CLOUD_API_KEY"]}',
- }
-)
-```
-
-
-
-
-
- ```json
- {
- "gateway_attestation": {
- "request_nonce": "...", \\ The nonce you provided in the request
- "intel_quote": "...", \\ Attestation report used to verify w/ Intel TDX
- "event_log": [...], \\ TDX event log
- "info": { \\ TCB information including Docker compose manifest
- "compose": "...",
- ...
- }
- }
- }
- ```
-
- - `gateway_attestation`: Attestation report for the private inference gateway (API gateway)
- - `request_nonce`: The nonce you provided in the request
- - `intel_quote`: Intel TDX quote for the gateway TEE
- - `event_log`: TDX event log
- - `info`: TCB information including Docker compose manifest
-
-
-
- **Implementation**: This endpoint is defined in the [NEAR AI Cloud API](https://github.com/nearai/cloud-api).
-
----
-
-## Verifying Gateway Attestation
-
-Once you have [requested a gateway attestation](#request-gateway-attestation) from NEAR AI Cloud, you can use the returned payload to verify its authenticity. You can verify:
-
-- **Intel TDX quote**: Verifies TDX quote with [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl) library
-- **TDX report data**: Validates that report data includes the nonce in request
-- **Compose manifest**: Displays Docker compose manifest and verifies it matches the mr_config measurement
-- **Source Code provenance**: Verifies container image provenance
-
-### Verify TDX Quote
-
-You can verify the Intel TDX quote with the value of `intel_quote` from `gateway_attestation` using the [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl) library. This verifies:
-
-- The CPU TEE measurements are valid
-- The quote is authentic and signed by Intel
-- The TEE environment is genuine
-
-Alternatively, you can verify the Intel TDX quote at [TEE Attestation Explorer](https://proof.t16z.com/).
-
-### Verify TDX Report Data
-
-The TDX report data validates that:
-- The report data binds the signing address (ECDSA or Ed25519)
-- The report data embeds the request nonce
-
-This ensures cryptographic binding between the signing address and the hardware, and prevents replay attacks through nonce freshness.
-
-### Verify Compose Manifest
-
-The attestation response includes Docker compose manifest information in the `gateway_attestation.info` field. To verify:
-
-1. Extract the Docker compose manifest from the attestation
-2. Calculate the SHA-256 hash of the compose manifest
-3. Compare it with the `mr_config` measurement from the verified TDX quote
-4. Verify they match, proving the exact container configuration
-
-This ensures the exact Docker compose file is deployed to the TEE environment.
-
-### Verify Source Code Provenance
-
-Extract the `nearaidev/cloud-api` container image digests from the Docker compose manifest (matching `@sha256:xxx` patterns) and fetch the source code provenance from Sigstore for each image. This allows you to:
-
-1. Verify the container images were built from the expected source repository with exact release tag
-2. Review the GitHub Actions workflow that built the images
-3. Audit the build provenance and supply chain metadata
-4. Audit the source code of a given release
-
-The source code of the [cloud-api gateway](https://github.com/nearai/cloud-api) is reproducible, which means you can build from the source code of a given release and verify that the resulting Docker image digest matches the digest from the attestation (`@sha256:xxx`).
-
-This ensures the exact version of source code is built into the `nearaidev/cloud-api` Docker image and makes it easy to audit and validate the source code. You can find one [Sigstore link example](https://search.sigstore.dev/?hash=sha256:f75c2a8f1a3d8a36ed6cd7479e848edf0a0e814381d7b83993a703201594bc14) in the [v0.1.7 release of cloud-api](https://github.com/nearai/cloud-api/releases/tag/v0.1.7).
diff --git a/cloud/verification/index.mdx b/cloud/verification/index.mdx
index 9d3ce6d..8bacedf 100644
--- a/cloud/verification/index.mdx
+++ b/cloud/verification/index.mdx
@@ -1,57 +1,33 @@
---
title: "Verification"
+description: "Verify requests sent through the NEAR AI Cloud Gateway."
---
-[NEAR AI Cloud](https://cloud.near.ai) operates in Trusted Execution Environments (TEEs) which use cryptographic proofs to verify that your private AI conversations actually happened in secure, isolated environments that are not on compromised systems or with unauthorized access.
+Use these guides to verify the evidence returned for requests sent through the NEAR AI Cloud Gateway.
-This section will show you step-by-step processes for checking these proofs, validating digital signatures, and confirming that your AI interactions haven't been tampered with.
-
-
- See an easy-to-follow example implementation in the [NEAR AI Cloud Verification Example](https://github.com/near-examples/nearai-cloud-verification-example) repo.
-
- A more complete verifier implementation is available in the [NEAR AI Cloud Verifier](https://github.com/nearai/nearai-cloud-verifier) repo.
-
- **Simplified Verification with Direct Completions**
-
- When using [direct completions endpoints](/cloud/private-inference#direct-completions) (e.g., `https://qwen35-122b.completions.near.ai`), your requests go straight to the model's TEE. This means only **model verification** is needed — there is no gateway in the path, so [gateway verification](/cloud/verification/gateway) can be skipped entirely.
-
-
- **TLS Termination Inside the TEE**
-
- If you also need to prove that your HTTPS connection terminates inside the same TEE, request attestation with `include_tls_fingerprint=true`. That opt-in flag changes the attested `report_data` from `signing_address || nonce` to `SHA256(signing_address || tls_cert_fingerprint) || nonce`. See [TLS Attestation Verification](/cloud/verification/tls).
+ See an example implementation in the [NEAR AI Cloud Verification Example](https://github.com/near-examples/nearai-cloud-verification-example) repo. A more complete verifier is available in the [NEAR AI Cloud Verifier](https://github.com/nearai/nearai-cloud-verifier) repo.
-## How NEAR AI Cloud Verification Works
-1. **Secure Key Generation:** When NEAR AI Cloud initializes, it generates a unique cryptographic signing key pair inside the Trusted Execution Environment (TEE). The private key never leaves the secure hardware.
+## Verify a NEAR AI Cloud Gateway request
-2. **Hardware Attestation:** The system generates attestation reports that cryptographically prove it's running on genuine NVIDIA H100/H200 hardware in TEE mode within a confidential VM.
-
-3. **Key Binding:** These attestation reports include the public key from step 1, creating a verifiable link between the secure hardware and the signing capability.
-
-4. **Message Signing:** Every AI inference request and response is digitally signed using the private key that remains secured within the TEE.
-
-5. **End-to-End Verification:** You can verify that your AI interactions were genuinely processed in the secure environment by:
+
+
+ Use this when your application sends requests through `cloud-api.near.ai`. Verify the gateway and, when your policy requires it, every returned NEAR model-attestation candidate.
+
+
- - Checking the hardware attestation reports
- - Validating the digital signatures on your messages
- - Confirming the signing key matches the attested hardware
+## What happens next
----
+The Gateway guide walks through verifying fresh deployment evidence before a completion and, when present, a response signature against its reported signer. Depending on your application's policy, you can also verify the TLS connection and software-image provenance.
-## What You Can Verify
+## Common verification concepts
-
- Verify that AI models are running in secure TEE environments using NVIDIA and Intel attestation
-
-
- Verify that the private inference gateway is running in secure TEE environments using Intel attestation
-
-
- Verify that your HTTPS connection terminates inside the attested TEE by binding the TLS certificate into report_data
+
+ Learn how fresh attestation evidence is tied to your request and to a signing identity.
-
- Verify individual chat messages and responses through cryptographic signatures
+
+ Decide which checks, measurements, and source-provenance requirements your application accepts.
diff --git a/cloud/verification/model.mdx b/cloud/verification/model.mdx
deleted file mode 100644
index 9dfd65c..0000000
--- a/cloud/verification/model.mdx
+++ /dev/null
@@ -1,297 +0,0 @@
----
-title: "Model Verification"
----
-
-
-To verify a NEAR AI model is operating in a secure trusted environment, there are two main steps:
-
-- [Request Model Attestation](#request-model-attestation) report from NEAR AI Cloud
-- [Verify Model Attestation](#verifying-model-attestation) report using NVIDIA & Intel attestation authenticators
-
-
- See an easy-to-follow example implementation in the [NEAR AI Cloud Verification Example](https://github.com/near-examples/nearai-cloud-verification-example) repo.
-
- A more complete verifier implementation is available in the [NEAR AI Cloud Verifier](https://github.com/nearai/nearai-cloud-verifier) repo.
-
----
-
-## Request Model Attestation
-
-To request a model attestation from NEAR AI cloud, use one of the following `GET` API endpoints:
-
-**Via Gateway:**
-```bash
-https://cloud-api.near.ai/v1/attestation/report?model={model_name}&signing_algo=ecdsa&nonce={nonce}
-```
-
-**Via Direct Completions:**
-```bash
-https://{slug}.completions.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce={nonce}
-```
-
-The `signing_algo` parameter specifies the signing algorithm used (`ecdsa` or `ed25519`). The `nonce` parameter is optional but recommended. It should be a randomly generated 64 character hexadecimal string (32 bytes) that ensures attestation freshness and prevents replay attacks. If not provided, the server will generate one for you.
-
-The gateway endpoint requires an API key (`Authorization: Bearer `); report retrieval is free and never counts against your usage. The direct completions endpoint is served by the model TEE itself and needs no API key.
-
-With direct completions, you can also pass `include_tls_fingerprint=true` to bind the TLS certificate fingerprint to the attestation report. This is disabled by default for compatibility with existing clients. Without the flag, `report_data` binds only the signing key and nonce. With the flag, `report_data[0:32]` becomes `SHA256(signing_address || tls_cert_fingerprint)`, which lets you verify that the HTTPS connection terminates inside the model TEE. See [TLS Attestation Verification](/cloud/verification/tls).
-
-
-
-
-```bash
-# Generate a random 64-character hex nonce (optional but recommended)
-NONCE=$(openssl rand -hex 32)
-
-curl "https://cloud-api.near.ai/v1/attestation/report?model=zai-org/GLM-5.1-FP8&signing_algo=ecdsa&nonce=${NONCE}" \
- -H 'accept: application/json' \
- -H 'Authorization: Bearer '
-```
-
-
-
-
-```bash
-# Generate a random 64-character hex nonce (optional but recommended)
-NONCE=$(openssl rand -hex 32)
-
-# Direct completions — no model parameter needed
-curl "https://qwen35-122b.completions.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce=${NONCE}&include_tls_fingerprint=true" \
- -H 'accept: application/json'
-```
-
-
-
-
-```js
-import crypto from 'crypto';
-
-const MODEL_NAME = 'zai-org/GLM-5.1-FP8'
-// Generate a random 64-character hex nonce (optional but recommended)
-const nonce = crypto.randomBytes(32).toString('hex');
-
-// Via gateway (requires an API key):
-const response = await fetch(
- `https://cloud-api.near.ai/v1/attestation/report?model=${MODEL_NAME}&signing_algo=ecdsa&nonce=${nonce}`,
- {
- headers: {
- 'accept': 'application/json',
- 'Authorization': `Bearer ${process.env.NEAR_AI_CLOUD_API_KEY}`,
- },
- }
-);
-
-// Or via direct completions:
-// const response = await fetch(
-// `https://qwen35-122b.completions.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce=${nonce}&include_tls_fingerprint=true`,
-// { headers: { 'accept': 'application/json' } }
-// );
-```
-
-
-
-
-```python
-import os
-import requests
-import secrets
-
-MODEL_NAME = 'zai-org/GLM-5.1-FP8'
-# Generate a random 64-character hex nonce (optional but recommended)
-nonce = secrets.token_hex(32)
-
-# Via gateway (requires an API key):
-response = requests.get(
- f'https://cloud-api.near.ai/v1/attestation/report?model={MODEL_NAME}&signing_algo=ecdsa&nonce={nonce}',
- headers={
- 'accept': 'application/json',
- 'Authorization': f'Bearer {os.environ["NEAR_AI_CLOUD_API_KEY"]}',
- }
-)
-
-# Or via direct completions:
-# response = requests.get(
-# f'https://qwen35-122b.completions.near.ai/v1/attestation/report?signing_algo=ecdsa&nonce={nonce}&include_tls_fingerprint=true',
-# headers={'accept': 'application/json'}
-# )
-```
-
-
-
-
-
- ```json
- {
- "model_attestations": [ \\ List of all GPU nodes in the network
- {
- "signing_address": "...", \\ TEE Public Key
- "nvidia_payload": "...", \\ Attestation report used to verify w/ NVIDIA
- "intel_quote": "..." \\ Attestation report used to verify w/ Intel
- }
- ]
- }
- ```
-
- - `model_attestations`: List attestations from all GPU nodes as multiple TEE nodes may be used to serve inference requests.
-
- - `signing_address`: Account address generated inside TEE that will be used to sign the chat response. You can utilize the `signing_address` from `model_attestations` to select the appropriate TEE node for verifying its integrity.
-
- - `nvidia_payload` and `intel_quote`: Attestation report formatted for NVIDIA TEE and Intel TEE respectively. You can use them to verify the integrity of the TEE. See [Verifying Model Attestation](#verifying-model-attestation) for more details.
-
-
-
- **Implementation**: This endpoint is defined in the [NEAR AI Private ML SDK](https://github.com/nearai/private-ml-sdk/blob/a23fa797dfd7e676fba08cba68471b51ac9a13d9/vllm-proxy/src/app/api/v1/openai.py#L170).
-
-## Verifying Model Attestation
-
-Once you have [requested a model attestation](#request-model-attestation) from NEAR AI Cloud, you can use the returned payload to verify its authenticity. You can verify:
-
-- **GPU attestation**: Submit GPU evidence payload to NVIDIA NRAS and verify the nonce matches
-- **Intel TDX quote**: Verifies TDX quote with [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl) library
-- **TDX report data**: Validates that report data binds the signing key (ECDSA or Ed25519) and nonce
-- **Compose manifest**: Displays Docker compose manifest and verifies it matches the mr_config measurement
-
-### Verify GPU Attestation
-
-NVIDIA offers a [Remote Attestation Service](https://docs.nvidia.com/attestation/cloud-services/latest/nras/nras_introduction.html) that allows you to verify that you are using a trusted environment with one of their GPUs. To verify:
-
-1. Submit the GPU evidence payload (`nvidia_payload`) to NVIDIA NRAS
-2. Verify the nonce in the GPU payload matches the request nonce
-3. Validate the NVIDIA attestation verdict is PASS
-
-The `nvidia_payload` contains:
-- `nonce` - The nonce from your attestation request
-- `arch` - Architecture of the GPU _(HOPPER or BLACKWELL)_
-- `evidence_list` - A list of GPU evidence items, each containing an evidence and a corresponding certificate
-
-The `evidence_list` contains Base64 encoded data that lists the GPU's:
-
-- Hardware Identity
-- Firmware & Software measurements
-- Security configuration state
-- Endorsement certificates (Signed measurements from the GPU's unique key)
-
-The private key of this GPU is how we can securely verify the authenticity. NVIDIA burns this unique private key into each GPU during the manufacturing process and only retains the corresponding public key, which is used to verify the signature of attestations provided to them.
-
-All of this data is provided to you from the [Model Attestation response](#request-model-attestation) as `nvidia_payload`.
-
-Simply use this JSON Object with your API call:
-
-```bash
-curl -X POST https://nras.attestation.nvidia.com/v3/attest/gpu \
- -H "accept: application/json" \
- -H "content-type: application/json" \
- -d ""
-```
-
-See official documentation: https://docs.api.nvidia.com/attestation/reference/attestmultigpu_1
-
-
- ```json
- [
- [
- "JWT",
- "eyJraWQiOiJudi1lYXQta2lkLXByb2QtMjAyNTA4MjQxNzI2MzczMzEtMGM4YzM2MzQtY2ZkMC00YmViLWFmNWYtMTE2MzliOWUxOTIyIiwiYWxnIjoiRVMzODQifQ.eyJzdWIiOiJOVklESUEtUExBVEZPUk0tQVRURVNUQVRJT04iLCJ4LW52aWRpYS12ZXIiOiIyLjAiLCJuYmYiOjE3NTYxNjg5MjYsImlzcyI6Imh0dHBzOi8vbnJhcy5hdHRlc3RhdGlvbi5udmlkaWEuY29tIiwieC1udmlkaWEtb3ZlcmFsbC1hdHQtcmVzdWx0Ijp0cnVlLCJzdWJtb2RzIjp7IkdQVS0wIjpbIkRJR0VTVCIsWyJTSEEtMjU2IiwiMDJmYzJmMTg3M2JkZjg5Y2VlNGYzZTQzYzU3ZTE3YzI0ODUxODcwMmQ4ZGZjMzcwNmE3YjdmZTgwMzZlOTNkMCJdXX0sImVhdF9ub25jZSI6IjRkNmUwYzQ5MzIxZDIyZGFhOWJkN2ZjMjIwNWUzODFmOTUwNmMyMGU3N2RkNTA4MmVjZjVlMTI0ZWMwZjQ2MTgiLCJleHAiOjE3NTYxNzI1MjYsImlhdCI6MTc1NjE2ODkyNiwianRpIjoiYzFhM2NkYzktZWUyMi00MmFkLTljZDEtNDRhMTE5OWYyZGVlIn0.199S4bah6SVZpy4lpBvRBc975tmf25gkf_mLDwR9-fwrc_kWYePNxGygTRQUzGbRdbrZOQHXWP0eALUPkJvmwGIV_MVfHRIKaBIRdr1e2_7jEP1-mqkbCmbefimiZN8t"
- ],
- {
- "GPU-0": "eyJraWQiOiJudi1lYXQta2lkLXByb2QtMjAyNTA4MjQxNzI2MzczMzEtMGM4YzM2MzQtY2ZkMC00YmViLWFmNWYtMTE2MzliOWUxOTIyIiwiYWxnIjoiRVMzODQifQ.eyJ4LW52aWRpYS1ncHUtZHJpdmVyLXJpbS1zY2hlbWEtdmFsaWRhdGVkIjp0cnVlLCJpc3MiOiJodHRwczovL25yYXMuYXR0ZXN0YXRpb24ubnZpZGlhLmNvbSIsIngtbnZpZGlhLWdwdS1hdHRlc3RhdGlvbi1yZXBvcnQtY2VydC1jaGFpbi12YWxpZGF0ZWQiOnRydWUsImVhdF9ub25jZSI6IjRkNmUwYzQ5MzIxZDIyZGFhOWJkN2ZjMjIwNWUzODFmOTUwNmMyMGU3N2RkNTA4MmVjZjVlMTI0ZWMwZjQ2MTgiLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLXNpZ25hdHVyZS12ZXJpZmllZCI6dHJ1ZSwieC1udmlkaWEtZ3B1LXZiaW9zLXJpbS1mZXRjaGVkIjp0cnVlLCJleHAiOjE3NTYxNzI1MjYsImlhdCI6MTc1NjE2ODkyNiwidWVpZCI6IjY0Mjk2MDE4OTI5ODAwNzUxMTI1MDk1ODAzMDUwMDc0OTE1MjczMDIyMTE0MjQ2OCIsImp0aSI6IjFhMzhjMTAzLWMyODAtNDQyMi1hZDc1LTRkMTA3OTkyMGI2MyIsIngtbnZpZGlhLWdwdS1hdHRlc3RhdGlvbi1yZXBvcnQtbm9uY2UtbWF0Y2giOnRydWUsIngtbnZpZGlhLWdwdS12Ymlvcy1pbmRleC1uby1jb25mbGljdCI6dHJ1ZSwieC1udmlkaWEtZ3B1LXZiaW9zLXJpbS1jZXJ0LXZhbGlkYXRlZCI6dHJ1ZSwic2VjYm9vdCI6dHJ1ZSwieC1udmlkaWEtZ3B1LWF0dGVzdGF0aW9uLXJlcG9ydC1wYXJzZWQiOnRydWUsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLXNpZ25hdHVyZS12ZXJpZmllZCI6dHJ1ZSwieC1udmlkaWEtZ3B1LWFyY2gtY2hlY2siOnRydWUsIngtbnZpZGlhLWF0dGVzdGF0aW9uLXdhcm5pbmciOm51bGwsIm5iZiI6MTc1NjE2ODkyNiwieC1udmlkaWEtZ3B1LWRyaXZlci12ZXJzaW9uIjoiNTcwLjEzMy4yMCIsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLW1lYXN1cmVtZW50cy1hdmFpbGFibGUiOnRydWUsIngtbnZpZGlhLWdwdS1hdHRlc3RhdGlvbi1yZXBvcnQtc2lnbmF0dXJlLXZlcmlmaWVkIjp0cnVlLCJod21vZGVsIjoiR0gxMDAgQTAxIEdTUCBCUk9NIiwiZGJnc3RhdCI6ImRpc2FibGVkIiwieC1udmlkaWEtZ3B1LWRyaXZlci1yaW0tZmV0Y2hlZCI6dHJ1ZSwib2VtaWQiOiI1NzAzIiwieC1udmlkaWEtZ3B1LXZiaW9zLXJpbS1zY2hlbWEtdmFsaWRhdGVkIjp0cnVlLCJtZWFzcmVzIjoic3VjY2VzcyIsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLWNlcnQtdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtdmVyc2lvbiI6Ijk2LjAwLkNGLjAwLjAyIiwieC1udmlkaWEtZ3B1LXZiaW9zLXJpbS1tZWFzdXJlbWVudHMtYXZhaWxhYmxlIjp0cnVlfQ.Zjac1Al0OsYbrXu7lOKDAH7lLNnRU_G2R1UJBnUpvZKL1EE8mjPyy-4sqRvE_d8uZJ4GuhXoy_EonyuUIXESd3sxjY0Eohe9Rtlzatj14iLOdcVrF_eOq12ZHNIYs4Go"
- }
- ]
- ```
-
-
-
- NVIDIA's attestation verification response returns a "Entity Attestation Token" (EAT) encoded as a JSON Web Token (JWT)
-
- To decode these values, you can use an online tool such as [jwt.io](https://www.jwt.io) or a library such as [Jose](https://www.npmjs.com/package/jose).
-
-
- ```json
-
- "JWT":
- {
- "sub": "NVIDIA-PLATFORM-ATTESTATION",
- "x-nvidia-ver": "2.0",
- "nbf": 1756168926,
- "iss": "https://nras.attestation.nvidia.com",
- "x-nvidia-overall-att-result": true,
- "submods": {
- "GPU-0": [
- "DIGEST",
- [
- "SHA-256",
- "02fc2f1873bdf89cee4f3e43c57e17c248518702d8dfc3706a7b7fe8036e93d0"
- ]
- ]
- },
- "eat_nonce": "4d6e0c49321d22daa9bd7fc2205e381f9506c20e77dd5082ecf5e124ec0f4618",
- "exp": 1756172526,
- "iat": 1756168926,
- "jti": "c1a3cdc9-ee22-42ad-9cd1-44a1199f2dee"
- }
-
- "GPU-0":
- {
- "x-nvidia-gpu-driver-rim-schema-validated": true,
- "iss": "https://nras.attestation.nvidia.com",
- "x-nvidia-gpu-attestation-report-cert-chain-validated": true,
- "eat_nonce": "4d6e0c49321d22daa9bd7fc2205e381f9506c20e77dd5082ecf5e124ec0f4618",
- "x-nvidia-gpu-vbios-rim-signature-verified": true,
- "x-nvidia-gpu-vbios-rim-fetched": true,
- "exp": 1756172526,
- "iat": 1756168926,
- "ueid": "642960189298007511250958030500749152730221142468",
- "jti": "1a38c103-c280-4422-ad75-4d1079920b63",
- "x-nvidia-gpu-attestation-report-nonce-match": true,
- "x-nvidia-gpu-vbios-index-no-conflict": true,
- "x-nvidia-gpu-vbios-rim-cert-validated": true,
- "secboot": true,
- "x-nvidia-gpu-attestation-report-parsed": true,
- "x-nvidia-gpu-driver-rim-signature-verified": true,
- "x-nvidia-gpu-arch-check": true,
- "x-nvidia-attestation-warning": null,
- "nbf": 1756168926,
- "x-nvidia-gpu-driver-version": "570.133.20",
- "x-nvidia-gpu-driver-rim-measurements-available": true,
- "x-nvidia-gpu-attestation-report-signature-verified": true,
- "hwmodel": "GH100 A01 GSP BROM",
- "dbgstat": "disabled",
- "x-nvidia-gpu-driver-rim-fetched": true,
- "oemid": "5703",
- "x-nvidia-gpu-vbios-rim-schema-validated": true,
- "measres": "success",
- "x-nvidia-gpu-driver-rim-cert-validated": true,
- "x-nvidia-gpu-vbios-version": "96.00.CF.00.02",
- "x-nvidia-gpu-vbios-rim-measurements-available": true
- }
-
- ```
-
-
-### Verify TDX Quote
-
-You can verify the Intel TDX quote with the value of `intel_quote` using the [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl) library. This verifies:
-
-- The CPU TEE measurements are valid
-- The quote is authentic and signed by Intel
-- The TEE environment is genuine
-
-Alternatively, you can verify the Intel TDX quote at [TEE Attestation Explorer](https://proof.t16z.com/).
-
-### Verify TDX Report Data
-
-The TDX report data validates that:
-- The report data binds the signing address (ECDSA or Ed25519)
-- The report data embeds the request nonce
-
-This ensures cryptographic binding between the signing address and the hardware, and prevents replay attacks through nonce freshness.
-
-### Verify Compose Manifest
-
-The attestation response includes Docker compose manifest information in the `info` field. To verify:
-
-1. Extract the Docker compose manifest from the attestation
-2. Calculate the SHA-256 hash of the compose manifest
-3. Compare it with the `mr_config` measurement from the verified TDX quote
-4. Verify they match, proving the exact container configuration
-
-This ensures the exact Docker compose file is deployed to the TEE environment.
diff --git a/cloud/verification/reference/quote-nonce-signer.mdx b/cloud/verification/reference/quote-nonce-signer.mdx
new file mode 100644
index 0000000..3bcb3cd
--- /dev/null
+++ b/cloud/verification/reference/quote-nonce-signer.mdx
@@ -0,0 +1,322 @@
+---
+title: "Quote, Nonce, and Signer Bindings"
+description: "Shared rules for reading verified quotes, checking freshness, and matching signing identities."
+---
+
+Every NEAR verification flow starts with a client-generated nonce and a verified quote. HTTP fields are inputs to compare with the quote. They are not a replacement for quote verification.
+
+## Use a client-generated nonce
+
+Generate 32 random bytes in the client and encode them as 64 hexadecimal characters. Retain the value until verification completes.
+
+```bash
+NONCE="$(openssl rand -hex 32)"
+```
+
+Send this value with the attestation request. Reject a report unless the verified quote contains the same nonce in its request-freshness binding.
+
+## Find the quote for your path
+
+| Request path | Quote location |
+| --- | --- |
+| NEAR AI Cloud gateway | `gateway_attestation.intel_quote` |
+| NEAR AI Cloud gateway model evidence (`provider=near`) | Each candidate report in `model_attestations[]` |
+
+## Verify the signer binding
+
+For a NEAR-operated report requested with `include_tls_fingerprint=true`, the verified quote binds the signer identity, TLS certificate fingerprint, and nonce.
+
+`signing_address` is algorithm-specific:
+
+| `signing_algo` | `signing_address` |
+| --- | --- |
+| `ecdsa` | An Ethereum-style address derived from the signing key. |
+| `ed25519` | Ed25519 public-key bytes encoded as hexadecimal. |
+
+Always compare both `signing_address` and `signing_algo`. Do not compare identities across algorithms.
+
+## Read NEAR report data
+
+For NEAR-operated reports, quote report data is 64 bytes. The final 32 bytes are the client nonce. The first 32 bytes bind the signing identity:
+
+- Without TLS binding, they are the decoded signing identity, zero-padded on the right to 32 bytes. Decode `signing_address` from hex after removing any `0x` prefix. An ECDSA address occupies 20 bytes; an Ed25519 public key occupies all 32 bytes.
+- With TLS binding, they are `SHA-256(decoded signing identity || decoded tls_cert_fingerprint)`, where the fingerprint is the raw 32-byte SHA-256 of the certificate's SPKI, not its hex text. Compare the quote-bound fingerprint with the peer certificate observed on the same TLS connection.
+
+This layout applies only to NEAR-operated dstack reports; other providers have their own report-data contracts.
+
+## Check the configuration measurement
+
+`info.tcb_info.app_compose` is the measured configuration. Hash its exact UTF-8 bytes without parsing or reserializing it. The verified quote's MRCONFIGID is 48 bytes: `01`, then that SHA-256 hash, then 15 zero bytes.
+
+```bash
+jq -j '.info.tcb_info.app_compose' report.json | sha256sum
+```
+
+Use `jq -j`: `jq -r` appends a newline and produces a different hash. For a gateway report, read `.gateway_attestation.info.tcb_info.app_compose`.
+
+## Replay the RTMR3 event log
+
+`event_log` is a list of events. The gateway returns it as a JSON string; decode it first. Replay only the events whose `imr` is `3`, in order:
+
+1. Compute each event's digest as `SHA-384(event_type as 4 little-endian bytes || ":" || event as UTF-8 || ":" || hex-decoded event_payload)`.
+2. If the event carries a non-empty `digest`, require it to equal the computed digest. Some reports return an empty `digest`; use the computed one.
+3. Start from 48 zero bytes and extend once per event: `RTMR3 = SHA-384(RTMR3 || digest)`.
+
+Require the result to equal RTMR3 in the verified quote. Recomputing each digest also authenticates the event payloads, such as `app-id` and `compose-hash`.
+
+## Verify GPU evidence when present
+
+`nvidia_payload`, when present, is a JSON string. Decode it and send the resulting JSON object unchanged to NVIDIA NRAS; do not wrap it in another JSON property. For a NEAR AI Cloud response, save each `model_attestations[]` candidate as a separate report file and run this check for every candidate before accepting it.
+
+```bash
+GPU_PAYLOAD="$(jq -er '.nvidia_payload | fromjson' report.json)"
+
+printf '%s' "$GPU_PAYLOAD" | jq -e --arg nonce "$NONCE" \
+ '(.nonce | type == "string") and ((.nonce | ascii_downcase) == ($nonce | ascii_downcase))' >/dev/null &&
+printf '%s' "$GPU_PAYLOAD" | curl --fail-with-body -sS -X POST \
+ 'https://nras.attestation.nvidia.com/v3/attest/gpu' \
+ -H 'Accept: application/json' \
+ -H 'Content-Type: application/json' \
+ --data-binary @-
+```
+
+The request is sent only when the payload nonce matches. The payload goes on standard input because an eight-GPU payload is close to the shell's argument-size limit.
+
+NRAS returns a JSON array whose first entry is `["JWT", ""]`. Decode that JWT's payload and require `x-nvidia-overall-att-result` to be boolean `true`. Reject missing or malformed GPU evidence, a nonce mismatch, or any other verdict. This minimal flow relies on the TLS-authenticated NRAS verdict; apply local JWT/EAT validation separately if your policy requires it. Define in advance whether missing GPU evidence is acceptable for your workload policy.
+
+## Check a report in code
+
+These helpers run the quote, debug-mode, nonce, signer, configuration, and RTMR3 checks described above on one NEAR report. They use [`dcap-qvl`](https://github.com/Phala-Network/dcap-qvl) to verify the quote and read report data and measurements from the verified quote. Run them on `gateway_attestation`, on every `model_attestations[]` candidate, or on a direct model report. Pass `peer_spki_sha256` only when you observed the peer certificate on the same TLS connection that returned the report. GPU evidence is checked separately, as described above.
+
+
+
+
+Requires Python 3.9 or later.
+
+```bash
+pip install dcap-qvl requests
+```
+
+```python
+import hashlib
+import json
+import struct
+
+import dcap_qvl
+
+
+def _json(value):
+ return json.loads(value) if isinstance(value, str) else value
+
+
+def _signing_identity(report):
+ algorithm = report["signing_algo"]
+ identity = bytes.fromhex(report["signing_address"].removeprefix("0x"))
+ if {"ecdsa": 20, "ed25519": 32}.get(algorithm) != len(identity):
+ raise ValueError("unsupported signing identity")
+ return identity
+
+
+def replay_rtmr3(event_log):
+ rtmr3 = bytes(48)
+ for event in _json(event_log):
+ if event.get("imr") != 3:
+ continue
+ digest = hashlib.sha384(
+ struct.pack("
+
+
+Save the code as an ES module (a `.mjs` file, or set `"type": "module"` in `package.json`).
+
+```bash
+npm install @phala/dcap-qvl
+```
+
+```javascript
+import { createHash } from 'node:crypto';
+import { getCollateralAndVerify } from '@phala/dcap-qvl';
+
+const hexBytes = (value) => Buffer.from(value.replace(/^0x/i, ''), 'hex');
+const parse = (value) => (typeof value === 'string' ? JSON.parse(value) : value);
+const hash = (algorithm, ...parts) => {
+ const h = createHash(algorithm);
+ for (const part of parts) h.update(part);
+ return h.digest();
+};
+
+export function replayRtmr3(eventLog) {
+ let rtmr3 = Buffer.alloc(48);
+ for (const event of parse(eventLog)) {
+ if (event.imr !== 3) continue;
+ const eventType = Buffer.alloc(4);
+ eventType.writeUInt32LE(event.event_type);
+ const digest = hash('sha384', eventType, ':', Buffer.from(event.event, 'utf8'), ':',
+ hexBytes(event.event_payload ?? ''));
+ if (event.digest && !hexBytes(event.digest).equals(digest)) {
+ throw new Error(`digest mismatch for event ${event.event}`);
+ }
+ rtmr3 = hash('sha384', rtmr3, digest);
+ }
+ return rtmr3;
+}
+
+export async function checkNearReport(report, nonceHex, acceptedTcbStatuses, peerSpkiSha256) {
+ const verified = await getCollateralAndVerify(hexBytes(report.intel_quote));
+ if (!acceptedTcbStatuses.includes(verified.status)) {
+ throw new Error(`TCB status ${verified.status} not accepted: ${verified.advisory_ids}`);
+ }
+ if (verified.report.type !== 'td10') throw new Error('expected a TDX quote');
+ const quote = verified.report.data;
+
+ if (quote.tdAttributes[0] & 1) throw new Error('TDX debug mode is enabled');
+
+ const identity = hexBytes(report.signing_address);
+ if ({ ecdsa: 20, ed25519: 32 }[report.signing_algo] !== identity.length) {
+ throw new Error('unsupported signing identity');
+ }
+ const fingerprint = report.tls_cert_fingerprint;
+ if (peerSpkiSha256 !== undefined && (fingerprint ?? '').toLowerCase() !== peerSpkiSha256.toLowerCase()) {
+ throw new Error('TLS fingerprint does not match the peer certificate');
+ }
+ const boundIdentity = fingerprint
+ ? hash('sha256', identity, hexBytes(fingerprint))
+ : Buffer.concat([identity, Buffer.alloc(32 - identity.length)]);
+ if (!Buffer.from(quote.reportData).equals(Buffer.concat([boundIdentity, hexBytes(nonceHex)]))) {
+ throw new Error('report data does not bind the signer and nonce');
+ }
+ if (report.request_nonce.toLowerCase() !== nonceHex.toLowerCase()) {
+ throw new Error('echoed nonce does not match');
+ }
+
+ const appCompose = parse(report.info.tcb_info).app_compose;
+ const expectedConfig = Buffer.concat([Buffer.from([1]), hash('sha256', Buffer.from(appCompose, 'utf8')), Buffer.alloc(15)]);
+ if (!Buffer.from(quote.mrConfigId).equals(expectedConfig)) {
+ throw new Error('MRCONFIGID does not match app_compose');
+ }
+
+ if (!replayRtmr3(report.event_log).equals(Buffer.from(quote.rtMr3))) {
+ throw new Error('event log does not match RTMR3');
+ }
+ return verified.status;
+}
+```
+
+```javascript
+import { randomBytes } from 'node:crypto';
+const ACCEPTED_TCB_STATUSES = ['UpToDate']; // Set by your verification policy.
+
+const nonce = randomBytes(32).toString('hex');
+const response = await fetch(
+ 'https://cloud-api.near.ai/v1/attestation/report?' +
+ new URLSearchParams({
+ model: 'z-ai/glm-5.3-flash',
+ provider: 'near',
+ signing_algo: 'ecdsa',
+ nonce,
+ }),
+ {
+ headers: {
+ Authorization: `Bearer ${process.env.NEAR_AI_CLOUD_API_KEY}`,
+ 'x-no-aliasing': 'true',
+ },
+ },
+);
+if (!response.ok) throw new Error(`Attestation request failed: ${response.status}`);
+const body = await response.json();
+
+console.log('gateway:', await checkNearReport(body.gateway_attestation, nonce, ACCEPTED_TCB_STATUSES));
+const candidates = body.model_attestations ?? [];
+if (candidates.length === 0) throw new Error('No NEAR model evidence returned');
+for (const candidate of candidates) {
+ console.log('model:', await checkNearReport(candidate, nonce, ACCEPTED_TCB_STATUSES));
+}
+```
+
+
+
+
+The helpers raise when the quote's TCB status is not in your accepted set; the error lists Intel's advisory IDs. Decide which statuses your [verification policy](/cloud/verification/reference/verification-policy) accepts.
+
+`dcap-qvl` fetches quote collateral from Phala's PCCS by default. To use Intel's PCS instead, pass `INTEL_PCS_URL` from the same package as the second argument to `get_collateral_and_verify` or `getCollateralAndVerify`.
diff --git a/cloud/verification/reference/response-signature-verification.mdx b/cloud/verification/reference/response-signature-verification.mdx
new file mode 100644
index 0000000..7411d54
--- /dev/null
+++ b/cloud/verification/reference/response-signature-verification.mdx
@@ -0,0 +1,213 @@
+---
+title: "Response Signature Verification"
+description: "Verify a response signature over exact request and response bytes."
+---
+
+Use these helpers after you have fetched a response signature and verified the applicable preflight attestation. They verify only the signed response: exact-byte hashes, the signature, and the signer match. They do not verify a quote or choose which preflight evidence applies.
+
+Pass the signature JSON, the exact request and response bytes, the expected signed text for the endpoint, and the signer from the already verified preflight report:
+
+```text
+preflight_signer = {
+ signing_algo: verified report signing_algo,
+ signing_address: verified report signing_address
+}
+```
+
+The endpoint pages define the expected text and which preflight report to use:
+
+- [NEAR AI Cloud Gateway response signatures](/cloud/verification/cloud-api/response-signatures)
+- [Experimental direct model endpoint response signatures](/cloud/verification/direct/response-signatures)
+
+
+
+
+Install [`ethers`](https://www.npmjs.com/package/ethers) v6 and [`tweetnacl`](https://www.npmjs.com/package/tweetnacl), and save the code as an ES module (a `.mjs` file, or set `"type": "module"` in `package.json`):
+
+```bash
+npm install ethers@6 tweetnacl
+```
+
+```javascript
+import { Buffer } from 'node:buffer';
+import { createHash, timingSafeEqual } from 'node:crypto';
+import { ethers } from 'ethers';
+import nacl from 'tweetnacl';
+
+function sha256(bytes) {
+ return createHash('sha256').update(bytes).digest('hex');
+}
+
+// Omit modelName for a Gateway signature.
+export function responseSignatureText(requestBytes, responseBytes, modelName) {
+ const hashes = `${sha256(requestBytes)}:${sha256(responseBytes)}`;
+ if (modelName === undefined) return hashes;
+ if (typeof modelName !== 'string' || !modelName) {
+ throw new Error('modelName must be a non-empty string');
+ }
+ return `${modelName}:${hashes}`;
+}
+
+function requireHex(value, label, expectedBytes) {
+ if (typeof value !== 'string') throw new Error(`${label} must be hex`);
+ const hex = value.replace(/^0x/i, '');
+ if (!hex || !/^[0-9a-f]*$/i.test(hex) || hex.length % 2) {
+ throw new Error(`${label} must be hexadecimal`);
+ }
+ const bytes = Buffer.from(hex, 'hex');
+ if (bytes.length !== expectedBytes) {
+ throw new Error(`${label} must be ${expectedBytes} bytes`);
+ }
+ return bytes;
+}
+
+function signerIdentity(value, label) {
+ const algorithm = value?.signing_algo;
+ if (algorithm !== 'ecdsa' && algorithm !== 'ed25519') {
+ throw new Error(`${label}.signing_algo is unsupported`);
+ }
+ return {
+ algorithm,
+ address: requireHex(
+ value.signing_address,
+ `${label}.signing_address`,
+ algorithm === 'ecdsa' ? 20 : 32,
+ ),
+ };
+}
+
+function sameBytes(left, right) {
+ return left.length === right.length && timingSafeEqual(left, right);
+}
+
+// signature is the JSON returned by GET /v1/signature/.
+// preflightSigner comes from the already verified report selected by the endpoint.
+export function verifyResponseSignature({ signature, expectedText, preflightSigner }) {
+ if (typeof signature?.text !== 'string' || signature.text !== expectedText) {
+ throw new Error('signature text does not match the exact request/response bytes');
+ }
+
+ const signer = signerIdentity(signature, 'signature');
+ const signatureBytes = requireHex(
+ signature.signature,
+ 'signature.signature',
+ signer.algorithm === 'ecdsa' ? 65 : 64,
+ );
+
+ if (signer.algorithm === 'ecdsa') {
+ // ethers.verifyMessage applies the Ethereum EIP-191 personal-sign prefix.
+ const recovered = requireHex(
+ ethers.verifyMessage(Buffer.from(signature.text, 'utf8'), ethers.hexlify(signatureBytes)),
+ 'recovered signer',
+ 20,
+ );
+ if (!sameBytes(recovered, signer.address)) {
+ throw new Error('ECDSA signature does not match signing_address');
+ }
+ } else if (!nacl.sign.detached.verify(
+ Buffer.from(signature.text, 'utf8'), signatureBytes, signer.address,
+ )) {
+ throw new Error('Ed25519 signature does not match signing_address');
+ }
+
+ const preflight = signerIdentity(preflightSigner, 'preflightSigner');
+ if (preflight.algorithm !== signer.algorithm || !sameBytes(preflight.address, signer.address)) {
+ throw new Error('signature signer does not match verified preflight evidence');
+ }
+}
+```
+
+
+
+
+Requires Python 3.9 or later. Install [`eth-account`](https://pypi.org/project/eth-account/) and [`PyNaCl`](https://pypi.org/project/PyNaCl/):
+
+```bash
+pip install eth-account pynacl
+```
+
+```python
+import re
+from hashlib import sha256
+from hmac import compare_digest
+from typing import Optional
+
+from eth_account import Account
+from eth_account.messages import encode_defunct
+from nacl.exceptions import BadSignatureError
+from nacl.signing import VerifyKey
+
+
+def response_signature_text(
+ request_bytes: bytes, response_bytes: bytes, model_name: Optional[str] = None
+) -> str:
+ hashes = f'{sha256(request_bytes).hexdigest()}:{sha256(response_bytes).hexdigest()}'
+ if model_name is None:
+ return hashes
+ if not isinstance(model_name, str) or not model_name:
+ raise ValueError('model_name must be a non-empty string')
+ return f'{model_name}:{hashes}'
+
+
+def _require_hex(value: object, label: str, expected_bytes: int) -> bytes:
+ if not isinstance(value, str):
+ raise ValueError(f'{label} must be hex')
+ value = value.removeprefix('0x').removeprefix('0X')
+ if not value or len(value) % 2 or not re.fullmatch(r'[0-9a-fA-F]*', value):
+ raise ValueError(f'{label} must be hexadecimal')
+ decoded = bytes.fromhex(value)
+ if len(decoded) != expected_bytes:
+ raise ValueError(f'{label} must be {expected_bytes} bytes')
+ return decoded
+
+
+def _signer_identity(value: dict, label: str) -> tuple[str, bytes]:
+ algorithm = value.get('signing_algo')
+ if algorithm not in ('ecdsa', 'ed25519'):
+ raise ValueError(f'{label}.signing_algo is unsupported')
+ address = _require_hex(
+ value.get('signing_address'),
+ f'{label}.signing_address',
+ 20 if algorithm == 'ecdsa' else 32,
+ )
+ return algorithm, address
+
+
+# signature is the JSON returned by GET /v1/signature/.
+# preflight_signer comes from the already verified report selected by the endpoint.
+def verify_response_signature(
+ signature: dict, expected_text: str, preflight_signer: dict
+) -> None:
+ if signature.get('text') != expected_text:
+ raise ValueError('signature text does not match the exact request/response bytes')
+
+ algorithm, address = _signer_identity(signature, 'signature')
+ signature_bytes = _require_hex(
+ signature.get('signature'),
+ 'signature.signature',
+ 65 if algorithm == 'ecdsa' else 64,
+ )
+
+ if algorithm == 'ecdsa':
+ # encode_defunct applies the Ethereum EIP-191 personal-sign prefix.
+ recovered = Account.recover_message(
+ encode_defunct(text=signature['text']),
+ signature=f'0x{signature_bytes.hex()}',
+ )
+ if not compare_digest(_require_hex(recovered, 'recovered signer', 20), address):
+ raise ValueError('ECDSA signature does not match signing_address')
+ else:
+ try:
+ VerifyKey(address).verify(signature['text'].encode('utf-8'), signature_bytes)
+ except (BadSignatureError, ValueError) as error:
+ raise ValueError('Ed25519 signature does not match signing_address') from error
+
+ preflight_algorithm, preflight_address = _signer_identity(
+ preflight_signer, 'preflight_signer'
+ )
+ if algorithm != preflight_algorithm or not compare_digest(address, preflight_address):
+ raise ValueError('signature signer does not match verified preflight evidence')
+```
+
+
+
diff --git a/cloud/verification/reference/verification-policy.mdx b/cloud/verification/reference/verification-policy.mdx
new file mode 100644
index 0000000..9ffbf03
--- /dev/null
+++ b/cloud/verification/reference/verification-policy.mdx
@@ -0,0 +1,44 @@
+---
+title: "Verification Policy"
+description: "Define required evidence, acceptable measurements, provenance requirements, and handling for unavailable verification results."
+---
+
+Attestation verifies particular cryptographic statements. Your application must decide which statements it requires and what software, hardware, and source provenance it accepts.
+
+## Define acceptance before verification
+
+At minimum, specify:
+
+- acceptable Intel TDX verification status and advisory policy
+- rejection of quotes with TDX debug mode enabled (bit 0 of `TD_ATTRIBUTES` in the verified quote)
+- required signer algorithm and identity matching rules
+- whether GPU evidence is required and the accepted verdict
+- accepted workload measurements and configuration rules
+- trusted image repositories, build identities, workflows, refs, and source revisions
+- maximum acceptable evidence age
+- whether TLS endpoint binding and response signatures are required
+
+## Treat required checks as fail-closed
+
+When a required check is missing, unavailable, malformed, mismatched, or fails verification, the result is unverified. Do not replace it with a report for a different signer, certificate, request, or model instance.
+
+| Condition | Recommended result |
+| --- | --- |
+| Quote or nonce mismatch | Reject the evidence and request a fresh report. |
+| Quote has TDX debug mode enabled | Reject the evidence. |
+| TLS report or peer SPKI unavailable | Treat endpoint binding as unverified. |
+| Response signature unavailable after a bounded retry | Treat the response as unverified when signatures are required. |
+| Image-provenance record is missing or returns `404` | Treat that digest as unverified when provenance is required. |
+| A required NEAR model candidate is unavailable, malformed, or fails verification | Treat the model property as unverified. |
+
+## Load balancing and rotations
+
+The same hostname can be served by more than one attested instance. A report is not evidence for a later request merely because it came from the same hostname.
+
+- For TLS, obtain the peer certificate and report on the same connection.
+- For a `provider_tee` response signature, verify and retain every returned model-attestation candidate before the completion. Accept the signature only when its signer identity and algorithm match exactly one verified candidate. A report fetched after the completion cannot replace that preflight check.
+- Refresh preflight evidence before a later request after a signer, certificate, configuration, deployment, or policy-age change.
+
+## What attestation does not establish by itself
+
+Attestation does not validate the quality of a model response, secure a compromised client device, or establish image provenance without a separate cryptographic provenance check. It also does not turn a reachable build record into a verified provenance statement.
diff --git a/cloud/verification/tls.mdx b/cloud/verification/tls.mdx
deleted file mode 100644
index 4bb7aea..0000000
--- a/cloud/verification/tls.mdx
+++ /dev/null
@@ -1,463 +0,0 @@
----
-title: "TLS Attestation Verification"
-description: "How to verify that your HTTPS connection to NEAR AI terminates inside a Trusted Execution Environment using TLS attestation."
----
-
-
-NEAR AI runs inference models inside **Confidential VMs (CVMs)** — virtual machines backed by Intel TDX hardware that provide a Trusted Execution Environment (TEE). The key property: even the cloud operator cannot see or tamper with what runs inside the TEE.
-
-TLS attestation proves that the TLS connection used for your HTTPS requests terminates directly **inside the TEE**, ensuring your messages remain encrypted end-to-end.
-
-
- Check our [example](https://github.com/nearai/nearai-cloud-verifier/tree/main) on how to perform TLS attestation verification.
-
-## What TLS Attestation Proves
-
-TLS attestation binds the server's **TLS certificate** to the **TEE hardware attestation**:
-
-1. You request `/v1/attestation/report?include_tls_fingerprint=true`.
-2. The TEE computes the certificate's **SPKI hash** (SHA-256 of the SubjectPublicKeyInfo) and binds it into the attested `report_data`.
-3. Intel TDX hardware signs this quote — it cannot be forged.
-4. You verify the quote, then confirm the live TLS certificate matches the attested hash.
-
-This proves the TLS private key is held by the TEE. Your HTTPS traffic is end-to-end encrypted all the way to the hardware enclave.
-
-
- **Opt-In Behavior**
-
- `include_tls_fingerprint` is disabled by default so existing attestation clients keep the original `report_data` layout. Without the flag, `report_data[0:32]` contains the signing address bytes (padded or truncated to 32 bytes). With the flag, `report_data[0:32]` becomes `SHA256(signing_address || tls_cert_fingerprint)`.
-
-## Trust Model
-
-You trust:
-- **Intel TDX** hardware (CPU attestation)
-- **NVIDIA GPU** hardware (GPU attestation)
-- **The code** running inside the TEE (verified via compose file hash)
-
-You do **not** need to trust the cloud operator, network infrastructure, or certificate authorities.
-
-```mermaid
-flowchart TD
- A["Intel TDX Hardware"] --> Q["TDX Quote
(signed by CPU)"]
- B["NVIDIA GPU Hardware"] --> G["GPU Attestation
(NVIDIA NRAS)"]
- Q --> C["mr_config_id
↓
Docker Compose file
↓
Container images"]
- Q --> D["report_data[0:32]
↓
SHA256(signing_address || tls_cert_fingerprint)"]
- Q --> E["report_data[32:64]
↓
nonce (freshness)"]
- D --> F["Live TLS cert match
↓
Your HTTPS connection"]
-```
-
----
-
-## Prerequisites
-
-Install the required Python packages:
-
-```bash
-pip install dcap-qvl cryptography requests pyyaml
-```
-
----
-## Verifications
-
-### Discover Available Endpoints
-
-The `completions.near.ai/endpoints` API lists all available model endpoints and their domains.
-
-```python
-import http.client
-import json
-
-conn = http.client.HTTPSConnection("completions.near.ai")
-conn.request("GET", "/endpoints")
-resp = conn.getresponse()
-endpoints_data = json.loads(resp.read())
-conn.close()
-
-# Build a lookup: domain -> models
-domain_models = {}
-for entry in endpoints_data.get("endpoints", []):
- domain = entry["domain"]
- domain_models[domain] = entry.get("models", [])
- print(f"{domain:<45} {', '.join(domain_models[domain])}")
-
-# Pick an endpoint to verify
-TARGET_DOMAIN = "glm-5-1.completions.near.ai"
-print(f"\nVerifying: {TARGET_DOMAIN}")
-```
-
----
-
-### Fetch Attestation + Live SPKI Hash
-
-Multiple backend CVMs may serve the same domain via load balancing. Making two separate connections — one for attestation, one to check the certificate — could hit different backends and produce a false SPKI mismatch.
-
-The solution: extract the live certificate **and** make the attestation request over the **same TLS connection**.
-
-
- CA verification is intentionally skipped. The TEE generates its own TLS key pair — it is not CA-signed. Trust comes from the TEE hardware attestation, not from Certificate Authorities.
-
-```python
-import ssl
-import secrets
-from hashlib import sha256
-from cryptography import x509
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives import serialization
-
-def compute_spki_hash(cert_der: bytes) -> str:
- """Compute SHA-256 of a certificate's SubjectPublicKeyInfo DER encoding.
-
- Hashing only the public key info (not the full certificate) keeps the hash
- stable across certificate renewals that reuse the same key.
- """
- cert = x509.load_der_x509_certificate(cert_der, default_backend())
- spki_der = cert.public_key().public_bytes(
- encoding=serialization.Encoding.DER,
- format=serialization.PublicFormat.SubjectPublicKeyInfo,
- )
- return sha256(spki_der).hexdigest()
-
-def fetch_attestation_and_spki(hostname, port=443, signing_algo="ecdsa"):
- """Fetch attestation report and live TLS cert SPKI hash over one connection."""
- nonce = secrets.token_hex(32)
-
- # Skip CA verification — trust comes from the TEE hardware attestation
- context = ssl.create_default_context()
- context.check_hostname = False
- context.verify_mode = ssl.CERT_NONE
-
- conn = http.client.HTTPSConnection(hostname, port, context=context, timeout=60)
- conn.connect()
-
- # Extract the live certificate from this TLS session
- cert_der = conn.sock.getpeercert(binary_form=True)
- if not cert_der:
- conn.close()
- raise Exception("Failed to get certificate from server")
- live_spki_hash = compute_spki_hash(cert_der)
-
- # Make the attestation request over the same connection
- path = (
- f"/v1/attestation/report"
- f"?include_tls_fingerprint=true&nonce={nonce}&signing_algo={signing_algo}"
- )
- conn.request("GET", path, headers={"Host": hostname})
- resp = conn.getresponse()
- body = resp.read()
- conn.close()
-
- if resp.status != 200:
- raise Exception(f"HTTP {resp.status}: {body.decode()}")
-
- attestation = json.loads(body)
- return attestation, live_spki_hash, nonce
-
-attestation, live_spki_hash, request_nonce = fetch_attestation_and_spki(TARGET_DOMAIN)
-
-print(f"Request nonce: {request_nonce}")
-print(f"Live SPKI hash: {live_spki_hash}")
-print(f"TLS fingerprint: {attestation.get('tls_cert_fingerprint')}")
-print(f"Model name: {attestation.get('model_name')}")
-print(f"Signing address: {attestation['signing_address']}")
-print(f"Intel quote length: {len(attestation.get('intel_quote', ''))} hex chars")
-print(f"NVIDIA payload: {bool(attestation.get('nvidia_payload'))}")
-```
-
-Query parameters used in the attestation request:
-
-| Parameter | Description |
-|-----------|-------------|
-| `include_tls_fingerprint=true` | Tells the TEE to include its TLS certificate's SPKI hash in the report data |
-| `nonce` | A random 32-byte hex string you generate; included in the report to prove freshness |
-| `signing_algo` | `ecdsa` (secp256k1) or `ed25519` — which signing key to bind |
-
----
-
-### Verify the Intel TDX Quote
-
-The attestation report contains an Intel TDX quote — a hardware-signed data structure that proves what code is running and what data the TEE attests to. Verify it using `dcap-qvl`, which validates the quote against Intel's DCAP (Data Center Attestation Primitives) collateral.
-
-```python
-import dcap_qvl
-
-intel_quote_bytes = bytes.fromhex(attestation["intel_quote"])
-print(f"Quote size: {len(intel_quote_bytes)} bytes")
-print("Verifying against Intel DCAP collateral...")
-
-result = await dcap_qvl.get_collateral_and_verify(intel_quote_bytes)
-result_json = json.loads(result.to_json())
-
-print(f"Verification status: {result.status}")
-print(f"Advisory IDs: {result.advisory_ids}")
-
-# Extract key fields from the verified quote
-td10 = result_json["report"]["TD10"]
-report_data_hex = td10["report_data"]
-mr_config_id = td10["mr_config_id"]
-```
-
-| Status | Meaning |
-|--------|---------|
-| `UpToDate` | Quote is valid, TCB firmware is current |
-| `SWHardeningNeeded` | Quote is valid but has advisories — check `advisory_ids` |
-| Any other value | Verification failed |
-
-Key fields extracted from the verified quote:
-
-| Field | What it proves |
-|-------|----------------|
-| `report_data[0:32]` | `SHA256(signing_address ∥ tls_cert_fingerprint)` — binds signing key + TLS cert to this TEE |
-| `report_data[32:64]` | The nonce — proves the attestation is fresh, not a replay |
-| `mr_config_id` | `"01" + SHA256(app_compose)` — binds the Docker Compose file to this TEE |
-
----
-
-### Verify Report Data Binds Signing Key + TLS Cert
-
-Check that the 64-byte `report_data` in the TDX quote contains the expected values. This cryptographically binds the signing key, TLS certificate, and nonce all to the same hardware attestation.
-
-```python
-report_data = bytes.fromhex(report_data_hex.removeprefix("0x"))
-signing_algo = attestation.get("signing_algo", "ecdsa").lower()
-
-if signing_algo == "ecdsa":
- signing_address_bytes = bytes.fromhex(attestation["signing_address"].removeprefix("0x"))
-else:
- signing_address_bytes = bytes.fromhex(attestation["signing_address"])
-
-# report_data[0:32] = SHA256(signing_address || tls_cert_fingerprint)
-cert_fp_bytes = bytes.fromhex(attestation["tls_cert_fingerprint"])
-expected_hash = sha256(signing_address_bytes + cert_fp_bytes).digest()
-
-binds_key_and_tls = report_data[:32] == expected_hash
-print(f"Signing key + TLS cert bound to TEE: {binds_key_and_tls}")
-
-# report_data[32:64] = nonce
-embeds_nonce = report_data[32:].hex() == request_nonce
-print(f"Nonce matches (fresh attestation): {embeds_nonce}")
-```
-
----
-
-### Verify Live TLS Certificate Matches Attested Fingerprint
-
-Compare the SPKI hash extracted from the live TLS session (Step 2) against the `tls_cert_fingerprint` from the attestation report. A match proves the TLS connection terminates inside the TEE.
-
-```python
-attested_fingerprint = attestation["tls_cert_fingerprint"]
-
-print(f"Live SPKI hash: {live_spki_hash}")
-print(f"Attested fingerprint: {attested_fingerprint}")
-
-tls_match = live_spki_hash == attested_fingerprint
-if tls_match:
- print("\nThe TLS connection terminates inside the TEE.")
- print("Your HTTPS traffic is end-to-end encrypted to the hardware enclave.")
-else:
- print("\nWARNING: TLS certificate mismatch! The connection may not terminate in the TEE.")
-```
-
----
-
-### Verify Model Name
-
-The attestation report's `model_name` field is self-reported by the inference proxy running inside the TEE. Since it originates from within the attested enclave, it tells you which model is actually serving your requests. Compare it against the model listed in `/endpoints` for this domain.
-
-```python
-attested_model = attestation.get("model_name")
-expected_models = domain_models.get(TARGET_DOMAIN, [])
-
-print(f"Attested model (from TEE): {attested_model}")
-print(f"Expected models (from /endpoints): {expected_models}")
-
-if attested_model:
- model_match = attested_model in expected_models
- print(f"\nModel verified: {model_match}")
-else:
- print("\nModel name not present — older proxy version.")
-```
-
----
-
-### Verify GPU Attestation
-
-In addition to CPU-level attestation, the inference GPUs provide hardware attestation via NVIDIA's Remote Attestation Service (NRAS). The GPU evidence is collected inside the TEE and included as `nvidia_payload` in the report. It's bound to the same nonce as the TDX quote, ensuring both attestations are fresh and from the same request.
-
-```python
-import base64
-import requests
-
-GPU_VERIFIER_API = "https://nras.attestation.nvidia.com/v3/attest/gpu"
-
-def decode_jwt_payload(jwt_token):
- payload_b64 = jwt_token.split(".")[1]
- padded = payload_b64 + "=" * ((4 - len(payload_b64) % 4) % 4)
- return json.loads(base64.urlsafe_b64decode(padded).decode())
-
-nvidia_payload_str = attestation.get("nvidia_payload")
-if nvidia_payload_str:
- payload = json.loads(nvidia_payload_str)
-
- gpu_nonce_matches = payload["nonce"].lower() == request_nonce.lower()
- print(f"GPU nonce matches request nonce: {gpu_nonce_matches}")
-
- print("Submitting GPU evidence to NVIDIA NRAS...")
- nras_resp = requests.post(GPU_VERIFIER_API, json=payload, timeout=30)
- jwt_token = nras_resp.json()[0][1]
- verdict = decode_jwt_payload(jwt_token)
- overall_result = verdict["x-nvidia-overall-att-result"]
-
- print(f"NVIDIA attestation verdict: {overall_result}")
- if overall_result == "success":
- print("GPU hardware attestation verified.")
- else:
- print(f"WARNING: GPU attestation result is '{overall_result}'")
-else:
- print("No NVIDIA payload — GPU attestation not available for this endpoint.")
-```
-
----
-
-### Verify Docker Compose File
-
-The TDX quote's `mr_config_id` encodes `"01" + SHA256(app_compose_json)`, binding the exact Docker Compose configuration — including pinned container image digests — to the hardware attestation. Verifying this confirms the TEE is running exactly the code defined in that compose file.
-
-```python
-import re
-import yaml # pip install pyyaml
-
-tcb_info = attestation["info"]["tcb_info"]
-if isinstance(tcb_info, str):
- tcb_info = json.loads(tcb_info)
-
-app_compose_str = tcb_info.get("app_compose")
-if app_compose_str:
- app_compose = json.loads(app_compose_str)
- docker_compose_yaml = app_compose["docker_compose_file"]
-
- # List services and their pinned image digests
- compose_parsed = yaml.safe_load(docker_compose_yaml)
- print("Services in this TEE:")
- for svc_name, svc_config in compose_parsed.get("services", {}).items():
- print(f" {svc_name}: {svc_config.get('image', '(no image)')[:80]}")
-
- # Verify hash matches mr_config_id
- compose_hash = sha256(app_compose_str.encode()).hexdigest()
- expected_mr_config = "01" + compose_hash
- compose_matches = mr_config_id.lower().startswith(expected_mr_config.lower())
- print(f"\nCompose hash matches mr_config_id: {compose_matches}")
- if compose_matches:
- print("The TEE is running exactly the code defined in this compose file.")
-
- # Extract pinned image digests for Sigstore checks
- digest_pattern = r'@sha256:([0-9a-f]{64})'
- unique_digests = list(dict.fromkeys(re.findall(digest_pattern, docker_compose_yaml)))
- print(f"\nPinned image digests ({len(unique_digests)}):")
- for digest in unique_digests:
- print(f" sha256:{digest[:24]}...")
-else:
- print("No app_compose in tcb_info — cannot verify compose file.")
-```
-
-
- `mr_config_id` may be all zeros on some endpoints during TEE configuration transitions. In that case, skip this check — the TDX quote verification, report data binding, and TLS fingerprint checks above still provide strong guarantees.
-
-**Cross-reference with GitHub:** The compose files deployed to CVMs come from the public repository [nearai/cvm-compose-files](https://github.com/nearai/cvm-compose-files). You can fetch the file by its tag and compare it byte-for-byte with what the TEE attests to.
-
-**Sigstore provenance:** Container images are pinned by `@sha256:` digest. Check `https://search.sigstore.dev/?hash=sha256:` for each image to confirm it was built by a traceable GitHub Actions pipeline, not pushed manually.
-
----
-
-## Caching Verified Certificates for Production
-
-Running the full attestation flow is thorough but takes several seconds. For production use, cache the verified SPKI hash and check it on every connection as a lightweight trust anchor.
-
-### The key insight: SPKI hash as trust anchor
-
-Once you've verified a particular SPKI hash is bound to a TEE via hardware attestation, you can reuse it:
-1. Connect to the endpoint via TLS
-2. Extract the live certificate's SPKI hash
-3. Compare against your cached, previously-verified hash
-4. If it matches → you're talking to the same TEE — no full attestation needed
-
-### Build the cache
-
-```python
-trusted_certs = {} # domain -> {"spki_hash": ..., "model_name": ..., "signing_address": ...}
-
-for domain in domain_models:
- try:
- att, spki, nonce = fetch_attestation_and_spki(domain)
- tls_fp = att.get("tls_cert_fingerprint")
- if tls_fp and spki == tls_fp:
- trusted_certs[domain] = {
- "spki_hash": spki,
- "model_name": att.get("model_name"),
- "signing_address": att["signing_address"],
- }
- print(f"PASS {domain}")
- else:
- print(f"FAIL {domain}")
- except Exception as e:
- print(f"ERROR {domain}: {e}")
-```
-
-### Verify on every connection
-
-```python
-def verified_request(domain, method, path, body=None, headers=None, port=443):
- """Make an HTTPS request verified against the cached SPKI hash."""
- if domain not in trusted_certs:
- raise Exception(f"No cached SPKI for {domain}. Run full attestation first.")
-
- cached_spki = trusted_certs[domain]["spki_hash"]
-
- context = ssl.create_default_context()
- context.check_hostname = False
- context.verify_mode = ssl.CERT_NONE
-
- conn = http.client.HTTPSConnection(domain, port, context=context, timeout=30)
- conn.connect()
-
- live_spki = compute_spki_hash(conn.sock.getpeercert(binary_form=True))
- if live_spki != cached_spki:
- conn.close()
- raise Exception(
- f"SPKI mismatch for {domain}! "
- f"Certificate may have rotated — re-run full attestation."
- )
-
- # SPKI matches — this connection terminates in the verified TEE
- all_headers = {"Host": domain, **(headers or {})}
- conn.request(method, path, body=body, headers=all_headers)
- resp = conn.getresponse()
- data = resp.read()
- conn.close()
- return resp.status, json.loads(data)
-```
-
-**When to re-verify:** Re-run full attestation when the SPKI hash changes (certificate rotated with a new key), when the TEE is redeployed, or when your cached entry expires (a 24-hour TTL is a reasonable default).
-
----
-
-## Verification Summary
-
-| Check | What it proves |
-|-------|----------------|
-| **Intel TDX quote** | Attestation comes from genuine Intel TDX hardware |
-| **Report data binding** | Signing key + TLS cert are bound to this specific TEE |
-| **Live TLS match** | Your HTTPS connection terminates inside the TEE |
-| **Model name** | The TEE is running the model you expect |
-| **GPU attestation** | NVIDIA GPUs are in verified confidential computing mode |
-| **Compose file hash** | `mr_config_id` matches `SHA256(app_compose)` — exact code verified |
-| **GitHub cross-reference** | Compose file matches the public source at `nearai/cvm-compose-files` |
-| **Sigstore provenance** | Container images were built by a traceable CI pipeline |
-
----
-
-## Resources
-
-- [NEAR AI Cloud Verifier](https://github.com/nearai/nearai-cloud-verifier) — Reference client that performs TLS certificate and attestation verification
-- [Private Inference](/cloud/private-inference) — How TEE isolation protects your data
-- [E2EE Chat Completions](/cloud/guides/e2ee-chat-completions) — Defense-in-depth encryption on top of TLS
-- [Verification](/cloud/verification) — Verify chat signatures and model attestation
diff --git a/docs.json b/docs.json
index 257f4de..dd1f125 100644
--- a/docs.json
+++ b/docs.json
@@ -67,10 +67,25 @@
"icon": "file-check",
"root": "cloud/verification/index",
"pages": [
- "cloud/verification/model",
- "cloud/verification/gateway",
- "cloud/verification/tls",
- "cloud/verification/chat"
+ {
+ "group": "NEAR AI Cloud Gateway",
+ "root": "cloud/verification/cloud-api/index",
+ "pages": [
+ "cloud/verification/cloud-api/gateway-attestation",
+ "cloud/verification/cloud-api/model-attestations",
+ "cloud/verification/cloud-api/tls",
+ "cloud/verification/cloud-api/response-signatures",
+ "cloud/verification/cloud-api/image-provenance"
+ ]
+ },
+ {
+ "group": "Reference",
+ "pages": [
+ "cloud/verification/reference/quote-nonce-signer",
+ "cloud/verification/reference/response-signature-verification",
+ "cloud/verification/reference/verification-policy"
+ ]
+ }
]
}
]
@@ -448,6 +463,26 @@
{
"source": "/api",
"destination": "/api-reference/introduction"
+ },
+ {
+ "source": "/cloud/verification/chat",
+ "destination": "/cloud/verification"
+ },
+ {
+ "source": "/cloud/verification/gateway",
+ "destination": "/cloud/verification/cloud-api/gateway-attestation"
+ },
+ {
+ "source": "/cloud/verification/model",
+ "destination": "/cloud/verification"
+ },
+ {
+ "source": "/cloud/verification/tls",
+ "destination": "/cloud/verification"
+ },
+ {
+ "source": "/cloud/verification/provenance",
+ "destination": "/cloud/verification"
}
]
}