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

# Modelos TEE y E2EE

> IA con privacidad mejorada mediante entornos de ejecución confiables y cifrado de extremo a extremo

Venice ofrece modelos con privacidad mejorada que se ejecutan en entornos de ejecución confiables (TEE) y admiten cifrado de extremo a extremo (E2EE). Estos modelos proporcionan garantías criptográficas de que tus datos permanecen privados, incluso frente a Venice.

## Comprendiendo los niveles de privacidad

| Tipo     | Prefijo  | Qué significa                                                                                                                          |
| -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **TEE**  | `tee-*`  | El modelo se ejecuta en un enclave protegido por hardware. Venice no puede acceder al cómputo. Puedes verificarlo mediante atestación. |
| **E2EE** | `e2ee-*` | Cifrado completo de extremo a extremo. Tus prompts se cifran del lado del cliente antes de enviarse. Solo el TEE puede descifrarlos.   |

<Info>
  Los modelos E2EE incluyen protección TEE más cifrado del lado del cliente. Los modelos TEE proporcionan seguridad de enclave sin requerir cifrado del lado del cliente.
</Info>

## Modelos disponibles

<div id="tee-e2ee-models-placeholder">Cargando...</div>

Consulta la [página de Modelos](/models/overview) para ver la lista completa con precios y límites de contexto.

## Modelos TEE

Los modelos TEE se ejecutan dentro de enclaves protegidos por hardware (Intel TDX, NVIDIA Confidential Computing). Los pesos del modelo y tus datos están protegidos frente al sistema anfitrión, incluida la infraestructura de Venice.

### Uso básico

Los modelos TEE funcionan exactamente igual que los modelos normales:

<CodeGroup>
  ```python Python theme={"dark"}
  from openai import OpenAI

  client = OpenAI(
      api_key="your-venice-api-key",
      base_url="https://api.venice.ai/api/v1"
  )

  response = client.chat.completions.create(
      model="tee-qwen3-5-122b-a10b",
      messages=[{"role": "user", "content": "Explain quantum computing"}]
  )

  print(response.choices[0].message.content)
  ```

  ```javascript Node.js theme={"dark"}
  import OpenAI from 'openai';

  const client = new OpenAI({
      apiKey: 'your-venice-api-key',
      baseURL: 'https://api.venice.ai/api/v1'
  });

  const response = await client.chat.completions.create({
      model: 'tee-qwen3-5-122b-a10b',
      messages: [{ role: 'user', content: 'Explain quantum computing' }]
  });

  console.log(response.choices[0].message.content);
  ```

  ```bash cURL theme={"dark"}
  curl https://api.venice.ai/api/v1/chat/completions \
    -H "Authorization: Bearer $API_KEY_VENICE" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "tee-qwen3-5-122b-a10b",
      "messages": [{"role": "user", "content": "Explain quantum computing"}]
    }'
  ```
</CodeGroup>

### Verificación de la atestación TEE

Puedes verificar criptográficamente que un modelo se está ejecutando en un TEE genuino obteniendo su informe de atestación:

<CodeGroup>
  ```bash cURL theme={"dark"}
  # Genera un nonce aleatorio (evita ataques de repetición)
  NONCE=$(openssl rand -hex 16)

  # Obtén la atestación
  curl "https://api.venice.ai/api/v1/tee/attestation?model=tee-qwen3-5-122b-a10b&nonce=$NONCE" \
    -H "Authorization: Bearer $API_KEY_VENICE"
  ```

  ```python Python theme={"dark"}
  import secrets
  import requests

  nonce = secrets.token_hex(16)

  response = requests.get(
      f"https://api.venice.ai/api/v1/tee/attestation",
      params={"model": "tee-qwen3-5-122b-a10b", "nonce": nonce},
      headers={"Authorization": f"Bearer {api_key}"}
  )

  attestation = response.json()
  print(f"Verified: {attestation['verified']}")
  print(f"TEE Provider: {attestation['tee_provider']}")
  print(f"Model: {attestation['model']}")
  ```
</CodeGroup>

La respuesta de atestación incluye:

| Campo             | Descripción                                                                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `verified`        | Si la atestación pasó la verificación del lado del servidor                                                                                |
| `nonce`           | Tu nonce, que confirma la frescura                                                                                                         |
| `model`           | El ID del modelo atestado                                                                                                                  |
| `tee_provider`    | Identificador del proveedor del TEE                                                                                                        |
| `intel_quote`     | Cita Intel TDX en bruto (base64) para verificación del lado del cliente                                                                    |
| `nvidia_payload`  | Datos de atestación de GPU NVIDIA (si aplica)                                                                                              |
| `signing_key`     | Clave pública para verificar las firmas de respuesta (normalmente requerida en flujos E2EE; puede omitirse en algunos modelos TEE simples) |
| `signing_address` | Dirección de Ethereum derivada de la clave de firma                                                                                        |

<Tip>
  En entornos de producción, verifica la atestación del lado del cliente parseando la cita Intel TDX y comprobando la atestación de NVIDIA.
</Tip>

<Note>
  Para la verificación de un modelo TEE simple, `signing_address` y los campos de verificación del lado del servidor son suficientes para las comprobaciones básicas de atestación. Se requiere un `signing_key` cuando necesitas el acuerdo de claves E2EE del lado del cliente y comprobaciones estrictas de vinculación de claves.
</Note>

### Firmas de respuesta

Los modelos TEE pueden firmar sus respuestas, demostrando que la salida provino del enclave atestado:

<CodeGroup>
  ```bash cURL theme={"dark"}
  # Tras obtener una completación, verifica la firma
  curl "https://api.venice.ai/api/v1/tee/signature?model=tee-qwen3-5-122b-a10b&request_id=chatcmpl-abc123" \
    -H "Authorization: Bearer $API_KEY_VENICE"
  ```

  ```python Python theme={"dark"}
  response = requests.get(
      f"https://api.venice.ai/api/v1/tee/signature",
      params={"model": "tee-qwen3-5-122b-a10b", "request_id": completion_id},
      headers={"Authorization": f"Bearer {api_key}"}
  )

  signature = response.json()
  # Verifica que la firma coincida con el signing_address de la atestación
  ```
</CodeGroup>

## Modelos E2EE

Los modelos E2EE añaden cifrado del lado del cliente sobre la protección TEE. Tus prompts se cifran antes de salir de tu dispositivo, y solo el TEE puede descifrarlos.

E2EE de Venice utiliza:

* **ECDH (Elliptic Curve Diffie-Hellman)** sobre secp256k1 para el intercambio de claves
* **HKDF-SHA256** para la derivación de claves
* **AES-256-GCM** para el cifrado simétrico
* **Atestación TEE** para verificar que el modelo se ejecuta en un enclave seguro

<Warning>
  E2EE requiere implementación del lado del cliente. Los ejemplos a continuación muestran el protocolo completo.
</Warning>

### Cómo funciona E2EE

<Steps>
  <Step title="Generar par de claves efímeras">
    El cliente genera un par de claves secp256k1 para esta sesión.
  </Step>

  <Step title="Obtener atestación TEE">
    El cliente solicita `/api/v1/tee/attestation` y recibe la clave pública del modelo, la evidencia de atestación y el nonce.
  </Step>

  <Step title="Verificar la atestación">
    El cliente comprueba la coincidencia del nonce, que el modo de depuración esté deshabilitado y la validez de la atestación.
  </Step>

  <Step title="Cifrar mensajes">
    El cliente cifra los prompts utilizando un secreto compartido ECDH → HKDF → AES-GCM.
  </Step>

  <Step title="Enviar solicitud">
    El cliente envía la solicitud con cabeceras E2EE (`X-Venice-TEE-Client-Pub-Key`, `X-Venice-TEE-Model-Pub-Key`, `X-Venice-TEE-Signing-Algo`).
  </Step>

  <Step title="Procesamiento en el TEE">
    El TEE descifra la solicitud, la procesa y cifra la respuesta.
  </Step>

  <Step title="Descifrar la respuesta">
    El cliente recibe fragmentos cifrados y los descifra con la clave privada.
  </Step>
</Steps>

### Requisitos previos

**JavaScript (Node.js ESM):**

```bash theme={"dark"}
npm install elliptic @noble/ciphers @noble/hashes
```

**Python:**

```bash theme={"dark"}
pip install cryptography ecdsa requests
```

### Paso 1: Verificar el soporte de E2EE del modelo

Primero, verifica que el modelo admite E2EE consultando el endpoint `/models`.

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  async function getE2EEModels(apiKey) {
    const response = await fetch('https://api.venice.ai/api/v1/models', {
      headers: { Authorization: `Bearer ${apiKey}` },
    })
    const { data } = await response.json()

    return data.filter(model => model.model_spec?.capabilities?.supportsE2EE === true)
  }

  // Ejemplo de uso
  const models = await getE2EEModels('your-api-key')
  console.log('E2EE Models:', models.map(m => m.id))
  // Salida: ['e2ee-qwen3-5-122b-a10b', 'e2ee-glm-5', ...]
  ```

  ```python Python theme={"dark"}
  import requests

  def get_e2ee_models(api_key: str) -> list:
      """Get list of models that support E2EE."""
      response = requests.get(
          'https://api.venice.ai/api/v1/models',
          headers={'Authorization': f'Bearer {api_key}'}
      )
      models = response.json()['data']

      return [
          model for model in models
          if model.get('model_spec', {}).get('capabilities', {}).get('supportsE2EE')
      ]

  # Ejemplo de uso
  models = get_e2ee_models('your-api-key')
  print('E2EE Models:', [m['id'] for m in models])
  ```
</CodeGroup>

### Paso 2: Generar par de claves efímeras

Genera un nuevo par de claves para cada sesión. La clave privada debe mantenerse solo en memoria y borrarse de forma segura tras su uso.

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  import { ec as EC } from 'elliptic'

  function generateEphemeralKeyPair() {
    const ec = new EC('secp256k1')
    const keyPair = ec.genKeyPair()

    return {
      privateKey: new Uint8Array(keyPair.getPrivate().toArray('be', 32)),
      publicKeyHex: keyPair.getPublic('hex'), // Formato no comprimido (65 bytes en hex)
    }
  }

  // Seguridad: rellena con ceros la clave privada cuando termines
  function zeroFill(arr) {
    arr.fill(0)
  }
  ```

  ```python Python theme={"dark"}
  from ecdsa import SECP256k1, SigningKey
  import secrets

  def generate_ephemeral_key_pair():
      """Generate ephemeral secp256k1 key pair for E2EE session."""
      private_key = SigningKey.generate(curve=SECP256k1)
      public_key = private_key.get_verifying_key()

      # Get uncompressed public key (04 || x || y)
      public_key_bytes = b'\x04' + public_key.to_string()

      return {
          'private_key': private_key.to_string(),  # 32 bytes
          'public_key_hex': public_key_bytes.hex()  # 130 hex chars
      }
  ```
</CodeGroup>

#### Ayudantes de validación

Usa estas funciones auxiliares para validar las claves y el contenido cifrado antes de enviar las solicitudes.

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  function validateClientPubkey(pubkeyHex) {
    if (pubkeyHex.length !== 130 || !pubkeyHex.startsWith('04')) {
      throw new Error(`Client pubkey must be 130 hex chars starting with '04' (got ${pubkeyHex.length})`)
    }
  }

  function isValidEncrypted(s) {
    // Mínimo: ephemeral_pub (65) + nonce (12) + tag (16) = 93 bytes = 186 caracteres hex
    return s.length >= 186 && /^[0-9a-fA-F]+$/.test(s)
  }
  ```

  ```python Python theme={"dark"}
  def validate_client_pubkey(pubkey_hex: str) -> None:
      """Validate client public key format."""
      if len(pubkey_hex) != 130 or not pubkey_hex.startswith('04'):
          raise ValueError(f"Client pubkey must be 130 hex chars starting with '04' (got {len(pubkey_hex)})")

  def is_valid_encrypted(s: str) -> bool:
      """Check if string is valid hex-encrypted content."""
      # Mínimo: ephemeral_pub (65) + nonce (12) + tag (16) = 93 bytes = 186 caracteres hex
      return len(s) >= 186 and all(c in '0123456789abcdefABCDEF' for c in s)
  ```
</CodeGroup>

### Paso 3: Obtener y verificar la atestación TEE

La atestación demuestra que el modelo se está ejecutando en un TEE genuino. Verifica siempre la atestación antes de confiar en la clave pública del modelo.

<Info>
  **Importante: longitud del nonce** — El nonce del cliente debe ser de **32 bytes (64 caracteres hexadecimales)**. Algunos proveedores de TEE requieren exactamente 32 bytes y rechazarán nonces más cortos.
</Info>

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  import crypto from 'crypto'

  async function fetchAndVerifyAttestation(modelId, apiKey) {
    // Genera un nonce de cliente para protección contra repetición (32 bytes = 64 caracteres hex)
    const clientNonce = crypto.randomBytes(32).toString('hex')

    const response = await fetch(
      `https://api.venice.ai/api/v1/tee/attestation?model=${encodeURIComponent(modelId)}&nonce=${clientNonce}`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    )

    const attestation = await response.json()

    // Verifica la atestación
    if (attestation.verified !== true) {
      throw new Error('TEE attestation verification failed on server')
    }

    if (attestation.nonce !== clientNonce) {
      throw new Error('Attestation nonce mismatch - possible replay attack')
    }

    // Obtén la clave pública del modelo para cifrar
    const modelPublicKey = attestation.signing_key || attestation.signing_public_key
    if (!modelPublicKey) {
      throw new Error('No signing key in attestation response')
    }

    return {
      modelPublicKey,
      signingAddress: attestation.signing_address,
      attestation,
    }
  }
  ```

  ```python Python theme={"dark"}
  import secrets
  import requests

  def fetch_and_verify_attestation(model_id: str, api_key: str) -> dict:
      """Fetch and verify TEE attestation for a model."""
      # Genera un nonce de cliente para protección contra repetición (32 bytes = 64 caracteres hex)
      client_nonce = secrets.token_hex(32)

      response = requests.get(
          f'https://api.venice.ai/api/v1/tee/attestation',
          params={'model': model_id, 'nonce': client_nonce},
          headers={'Authorization': f'Bearer {api_key}'}
      )
      attestation = response.json()

      # Verifica la atestación
      if attestation.get('verified') != True:
          raise ValueError('TEE attestation verification failed on server')

      if attestation.get('nonce') != client_nonce:
          raise ValueError('Attestation nonce mismatch - possible replay attack')

      # Obtén la clave pública del modelo para cifrar
      model_public_key = attestation.get('signing_key') or attestation.get('signing_public_key')
      if not model_public_key:
          raise ValueError('No signing key in attestation response')

      return {
          'model_public_key': model_public_key,
          'signing_address': attestation.get('signing_address'),
          'attestation': attestation
      }
  ```
</CodeGroup>

### Paso 4: Cifrar los mensajes

Cifra los mensajes de usuario y de sistema antes de enviarlos. Solo los mensajes con rol `user` y `system` necesitan cifrarse.

<Warning>
  Cuando hay cabeceras E2EE presentes, **todos** los mensajes con rol `user` y `system` deben estar cifrados. Enviar contenido en texto plano en estos roles dará lugar a un error "Encrypted field is not valid hex".
</Warning>

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  import { gcm } from '@noble/ciphers/aes.js'
  import { hkdf } from '@noble/hashes/hkdf.js'
  import { sha256 } from '@noble/hashes/sha2.js'
  import { ec as EC } from 'elliptic'
  import crypto from 'crypto'

  const HKDF_INFO = new TextEncoder().encode('ecdsa_encryption')

  function encryptMessage(plaintext, modelPublicKeyHex) {
    const ec = new EC('secp256k1')

    // Normaliza la clave pública (añade el prefijo 04 si es necesario)
    let normalizedKey = modelPublicKeyHex
    if (!normalizedKey.startsWith('04') && normalizedKey.length === 128) {
      normalizedKey = '04' + normalizedKey
    }

    const modelPublicKey = ec.keyFromPublic(normalizedKey, 'hex')

    // Genera un par de claves efímeras para este mensaje
    const ephemeralKeyPair = ec.genKeyPair()

    // Secreto compartido ECDH
    const sharedSecret = ephemeralKeyPair.derive(modelPublicKey.getPublic())
    const sharedSecretBytes = new Uint8Array(sharedSecret.toArray('be', 32))

    // Deriva la clave AES usando HKDF
    const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

    // Genera un nonce aleatorio
    const nonce = crypto.randomBytes(12)

    // Cifra con AES-GCM
    const cipher = gcm(aesKey, nonce)
    const encrypted = cipher.encrypt(new TextEncoder().encode(plaintext))

    // Obtén la clave pública efímera (no comprimida)
    const ephemeralPublic = new Uint8Array(ephemeralKeyPair.getPublic(false, 'array'))

    // Combina: ephemeral_public (65 bytes) + nonce (12 bytes) + ciphertext
    const result = new Uint8Array(65 + 12 + encrypted.length)
    result.set(ephemeralPublic, 0)
    result.set(nonce, 65)
    result.set(encrypted, 65 + 12)

    return Buffer.from(result).toString('hex')
  }

  function encryptMessagesForE2EE(messages, modelPublicKey) {
    return messages.map(msg => {
      if (msg.role === 'user' || msg.role === 'system') {
        return {
          ...msg,
          content: encryptMessage(msg.content, modelPublicKey),
        }
      }
      return msg
    })
  }
  ```

  ```python Python theme={"dark"}
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
  from cryptography.hazmat.primitives.kdf.hkdf import HKDF
  from cryptography.hazmat.primitives import hashes
  from ecdsa import SECP256k1, VerifyingKey, SigningKey
  import os

  HKDF_INFO = b'ecdsa_encryption'

  def encrypt_message(plaintext: str, model_public_key_hex: str) -> str:
      """Encrypt a message using ECDH + HKDF + AES-GCM."""
      # Normaliza la clave pública
      key_hex = model_public_key_hex
      if not key_hex.startswith('04') and len(key_hex) == 128:
          key_hex = '04' + key_hex

      model_public_key_bytes = bytes.fromhex(key_hex)

      # Parsea la clave pública del modelo (omite el prefijo 04)
      model_verifying_key = VerifyingKey.from_string(
          model_public_key_bytes[1:],
          curve=SECP256k1
      )

      # Genera un par de claves efímeras para este mensaje
      ephemeral_private = SigningKey.generate(curve=SECP256k1)
      ephemeral_public = ephemeral_private.get_verifying_key()

      # ECDH: calcula el secreto compartido
      shared_point = model_verifying_key.pubkey.point * ephemeral_private.privkey.secret_multiplier
      shared_secret = shared_point.x().to_bytes(32, 'big')

      # Deriva la clave AES usando HKDF
      hkdf = HKDF(
          algorithm=hashes.SHA256(),
          length=32,
          salt=None,
          info=HKDF_INFO,
      )
      aes_key = hkdf.derive(shared_secret)

      # Genera un nonce aleatorio
      nonce = os.urandom(12)

      # Cifra con AES-GCM
      aesgcm = AESGCM(aes_key)
      ciphertext = aesgcm.encrypt(nonce, plaintext.encode('utf-8'), None)

      # Obtén la clave pública efímera (no comprimida: 04 || x || y)
      ephemeral_public_bytes = b'\x04' + ephemeral_public.to_string()

      # Combina: ephemeral_public (65 bytes) + nonce (12 bytes) + ciphertext
      result = ephemeral_public_bytes + nonce + ciphertext

      return result.hex()

  def encrypt_messages_for_e2ee(messages: list, model_public_key: str) -> list:
      """Encrypt user and system messages."""
      encrypted_messages = []
      for msg in messages:
          if msg['role'] in ('user', 'system'):
              encrypted_messages.append({
                  **msg,
                  'content': encrypt_message(msg['content'], model_public_key)
              })
          else:
              encrypted_messages.append(msg)
      return encrypted_messages
  ```
</CodeGroup>

### Paso 5: Enviar la solicitud con cabeceras E2EE

Incluye las cabeceras requeridas para habilitar el procesamiento E2EE.

| Cabecera                      | Descripción                                                  |
| ----------------------------- | ------------------------------------------------------------ |
| `X-Venice-TEE-Client-Pub-Key` | Tu clave pública efímera (hex no comprimido, 130 caracteres) |
| `X-Venice-TEE-Model-Pub-Key`  | Clave pública del modelo obtenida de la atestación           |
| `X-Venice-TEE-Signing-Algo`   | Siempre `ecdsa`                                              |

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  async function sendE2EERequest(messages, model, e2eeContext, apiKey) {
    // Cifra los mensajes
    const encryptedMessages = encryptMessagesForE2EE(messages, e2eeContext.modelPublicKey)

    const response = await fetch('https://api.venice.ai/api/v1/chat/completions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        // Cabeceras E2EE
        'X-Venice-TEE-Client-Pub-Key': e2eeContext.publicKeyHex,
        'X-Venice-TEE-Model-Pub-Key': e2eeContext.modelPublicKey,
        'X-Venice-TEE-Signing-Algo': 'ecdsa',
      },
      body: JSON.stringify({
        model,
        messages: encryptedMessages,
        stream: true, // E2EE requiere streaming
      }),
    })

    return response
  }
  ```

  ```python Python theme={"dark"}
  import requests

  def send_e2ee_request(
      messages: list,
      model: str,
      e2ee_context: dict,
      api_key: str
  ) -> requests.Response:
      """Send an E2EE-encrypted chat completion request."""
      # Cifra los mensajes
      encrypted_messages = encrypt_messages_for_e2ee(
          messages,
          e2ee_context['model_public_key']
      )

      response = requests.post(
          'https://api.venice.ai/api/v1/chat/completions',
          headers={
              'Authorization': f'Bearer {api_key}',
              'Content-Type': 'application/json',
              # Cabeceras E2EE
              'X-Venice-TEE-Client-Pub-Key': e2ee_context['public_key_hex'],
              'X-Venice-TEE-Model-Pub-Key': e2ee_context['model_public_key'],
              'X-Venice-TEE-Signing-Algo': 'ecdsa'
          },
          json={
              'model': model,
              'messages': encrypted_messages,
              'stream': True  # E2EE requiere streaming
          },
          stream=True
      )

      return response
  ```
</CodeGroup>

### Paso 6: Descifrar los fragmentos de respuesta

Las respuestas de los modelos E2EE son fragmentos cifrados codificados en hexadecimal. Descifra cada fragmento con tu clave privada.

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  import { gcm } from '@noble/ciphers/aes.js'
  import { hkdf } from '@noble/hashes/hkdf.js'
  import { sha256 } from '@noble/hashes/sha2.js'
  import { ec as EC } from 'elliptic'

  const HKDF_INFO = new TextEncoder().encode('ecdsa_encryption')

  function hexToBytes(hex) {
    const h = hex.startsWith('0x') ? hex.slice(2) : hex
    const bytes = new Uint8Array(h.length / 2)
    for (let i = 0; i < bytes.length; i++) {
      bytes[i] = parseInt(h.substring(i * 2, i * 2 + 2), 16)
    }
    return bytes
  }

  function isHexEncrypted(s) {
    // Mínimo: ephemeral_pub (65) + nonce (12) + tag (16) = 93 bytes = 186 caracteres hex
    if (s.length < 186) return false
    return /^[0-9a-fA-F]+$/.test(s)
  }

  function decryptChunk(ciphertextHex, clientPrivateKey) {
    const raw = hexToBytes(ciphertextHex)

    // Parsea los componentes
    const serverEphemeralPubKey = raw.slice(0, 65)
    const nonce = raw.slice(65, 65 + 12)
    const ciphertext = raw.slice(65 + 12)

    // ECDH con la clave efímera del servidor
    const ec = new EC('secp256k1')
    const clientKey = ec.keyFromPrivate(Buffer.from(clientPrivateKey))
    const serverKey = ec.keyFromPublic(Buffer.from(serverEphemeralPubKey))
    const sharedSecret = clientKey.derive(serverKey.getPublic())
    const sharedSecretBytes = new Uint8Array(sharedSecret.toArray('be', 32))

    // Deriva la clave AES
    const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

    // Descifra
    const cipher = gcm(aesKey, nonce)
    const plaintext = cipher.decrypt(ciphertext)

    return new TextDecoder().decode(plaintext)
  }

  // Procesa la respuesta en streaming
  async function processE2EEStream(response, clientPrivateKey) {
    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    let fullContent = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      const text = decoder.decode(value)
      const lines = text.split('\n')

      for (const line of lines) {
        if (!line.startsWith('data: ')) continue
        const data = line.slice(6)
        if (data === '[DONE]') continue

        try {
          const chunk = JSON.parse(data)
          const content = chunk.choices?.[0]?.delta?.content

          if (content && isHexEncrypted(content)) {
            const decrypted = decryptChunk(content, clientPrivateKey)
            fullContent += decrypted
            process.stdout.write(decrypted) // Salida en tiempo real
          } else if (content) {
            fullContent += content
            process.stdout.write(content)
          }
        } catch (e) {
          // Omite fragmentos mal formados
        }
      }
    }

    return fullContent
  }
  ```

  ```python Python theme={"dark"}
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
  from cryptography.hazmat.primitives.kdf.hkdf import HKDF
  from cryptography.hazmat.primitives import hashes
  from ecdsa import SECP256k1, VerifyingKey, SigningKey
  import json
  import re

  HKDF_INFO = b'ecdsa_encryption'

  def is_hex_encrypted(s: str) -> bool:
      """Check if string looks like hex-encrypted content."""
      if len(s) < 186:  # Mínimo: 65 + 12 + 16 = 93 bytes = 186 hex
          return False
      return bool(re.match(r'^[0-9a-fA-F]+$', s))

  def decrypt_chunk(ciphertext_hex: str, client_private_key: bytes) -> str:
      """Decrypt an E2EE response chunk."""
      raw = bytes.fromhex(ciphertext_hex)

      # Parsea los componentes
      server_ephemeral_pub = raw[:65]
      nonce = raw[65:77]
      ciphertext = raw[77:]

      # Parsea la clave pública efímera del servidor (omite el prefijo 04)
      server_verifying_key = VerifyingKey.from_string(
          server_ephemeral_pub[1:],
          curve=SECP256k1
      )

      # Reconstruye la clave privada del cliente
      client_signing_key = SigningKey.from_string(client_private_key, curve=SECP256k1)

      # ECDH: calcula el secreto compartido
      shared_point = server_verifying_key.pubkey.point * client_signing_key.privkey.secret_multiplier
      shared_secret = shared_point.x().to_bytes(32, 'big')

      # Deriva la clave AES
      hkdf = HKDF(
          algorithm=hashes.SHA256(),
          length=32,
          salt=None,
          info=HKDF_INFO,
      )
      aes_key = hkdf.derive(shared_secret)

      # Descifra
      aesgcm = AESGCM(aes_key)
      plaintext = aesgcm.decrypt(nonce, ciphertext, None)

      return plaintext.decode('utf-8')

  def process_e2ee_stream(response, client_private_key: bytes) -> str:
      """Process streaming E2EE response."""
      full_content = ''

      for line in response.iter_lines():
          if not line:
              continue

          line_str = line.decode('utf-8')
          if not line_str.startswith('data: '):
              continue

          data = line_str[6:]
          if data == '[DONE]':
              continue

          try:
              chunk = json.loads(data)
              content = chunk.get('choices', [{}])[0].get('delta', {}).get('content', '')

              if content and is_hex_encrypted(content):
                  decrypted = decrypt_chunk(content, client_private_key)
                  full_content += decrypted
                  print(decrypted, end='', flush=True)  # Salida en tiempo real
              elif content:
                  full_content += content
                  print(content, end='', flush=True)
          except json.JSONDecodeError:
              pass

      print()  # Salto de línea final
      return full_content
  ```
</CodeGroup>

### Ejemplo completo en funcionamiento

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={"dark"}
    import elliptic from 'elliptic';
    import { gcm } from '@noble/ciphers/aes.js';
    import { hkdf } from '@noble/hashes/hkdf.js';
    import { sha256 } from '@noble/hashes/sha2.js';
    import crypto from 'crypto';

    const EC = elliptic.ec;

    const API_KEY = process.env.API_KEY_VENICE;
    const BASE_URL = 'https://api.venice.ai/api/v1';
    const MODEL = 'e2ee-qwen3-5-122b-a10b';
    const HKDF_INFO = new TextEncoder().encode('ecdsa_encryption');

    function hexToBytes(hex) {
      const bytes = new Uint8Array(hex.length / 2);
      for (let i = 0; i < bytes.length; i++) {
        bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
      }
      return bytes;
    }

    async function main() {
      // Paso 1: Genera el par de claves efímeras
      console.log('🔑 Generating ephemeral key pair...');
      const ec = new EC('secp256k1');
      const keyPair = ec.genKeyPair();
      const clientPublicKeyHex = keyPair.getPublic('hex');

      // Paso 2: Obtén y verifica la atestación
      console.log('🔍 Fetching TEE attestation...');
      const clientNonce = crypto.randomBytes(32).toString('hex'); // se requieren 32 bytes
      const attestationRes = await fetch(
        `${BASE_URL}/tee/attestation?model=${MODEL}&nonce=${clientNonce}`,
        { headers: { Authorization: `Bearer ${API_KEY}` } }
      );
      const attestation = await attestationRes.json();

      if (attestation.verified !== true || attestation.nonce !== clientNonce) {
        throw new Error('Attestation verification failed');
      }

      const modelPublicKey = attestation.signing_key || attestation.signing_public_key;
      console.log('✅ TEE attestation verified');

      // Paso 3: Cifra el mensaje
      console.log('🔐 Encrypting message...');
      const plaintext = 'What is 2+2? Answer briefly.';

      // Normaliza y parsea la clave pública del modelo
      let normalizedKey = modelPublicKey;
      if (!normalizedKey.startsWith('04') && normalizedKey.length === 128) {
        normalizedKey = '04' + normalizedKey;
      }

      const modelKey = ec.keyFromPublic(normalizedKey, 'hex');
      const ephemeralKeyPair = ec.genKeyPair();
      const sharedSecret = ephemeralKeyPair.derive(modelKey.getPublic());
      const sharedSecretBytes = new Uint8Array(sharedSecret.toArray('be', 32));
      const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32);
      const nonce = crypto.randomBytes(12);
      const cipher = gcm(aesKey, nonce);
      const encrypted = cipher.encrypt(new TextEncoder().encode(plaintext));
      const ephemeralPublic = new Uint8Array(ephemeralKeyPair.getPublic(false, 'array'));

      const result = new Uint8Array(65 + 12 + encrypted.length);
      result.set(ephemeralPublic, 0);
      result.set(nonce, 65);
      result.set(encrypted, 77);

      const encryptedContent = Buffer.from(result).toString('hex');
      const messages = [{ role: 'user', content: encryptedContent }];

      // Paso 4: Envía la solicitud E2EE
      console.log('📤 Sending encrypted request...');
      const response = await fetch(`${BASE_URL}/chat/completions`, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${API_KEY}`,
          'Content-Type': 'application/json',
          'X-Venice-TEE-Client-Pub-Key': clientPublicKeyHex,
          'X-Venice-TEE-Model-Pub-Key': modelPublicKey,
          'X-Venice-TEE-Signing-Algo': 'ecdsa',
        },
        body: JSON.stringify({ model: MODEL, messages, stream: true }),
      });

      // Paso 5: Descifra la respuesta
      console.log('📥 Decrypting response...\n');
      const reader = response.body.getReader();
      const decoder = new TextDecoder();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const text = decoder.decode(value);
        for (const line of text.split('\n')) {
          if (!line.startsWith('data: ') || line.includes('[DONE]')) continue;

          try {
            const chunk = JSON.parse(line.slice(6));
            const content = chunk.choices?.[0]?.delta?.content;
            if (!content) continue;

            if (/^[0-9a-fA-F]+$/.test(content) && content.length >= 186) {
              // Descifra
              const raw = hexToBytes(content);
              const serverEphemeralPub = raw.slice(0, 65);
              const nonce = raw.slice(65, 77);
              const ciphertext = raw.slice(77);

              const serverKey = ec.keyFromPublic(Buffer.from(serverEphemeralPub));
              const sharedSecret = keyPair.derive(serverKey.getPublic());
              const aesKey = hkdf(sha256, new Uint8Array(sharedSecret.toArray('be', 32)), undefined, HKDF_INFO, 32);
              const cipher = gcm(aesKey, nonce);
              const plaintext = new TextDecoder().decode(cipher.decrypt(ciphertext));
              process.stdout.write(plaintext);
            } else {
              process.stdout.write(content);
            }
          } catch {}
        }
      }

      console.log('\n\n🔐 Response decrypted end-to-end');
    }

    main().catch(console.error);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    #!/usr/bin/env python3
    """Complete E2EE implementation example for Venice AI API."""

    import os
    import json
    import secrets
    import requests
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    from cryptography.hazmat.primitives.kdf.hkdf import HKDF
    from cryptography.hazmat.primitives import hashes
    from ecdsa import SECP256k1, VerifyingKey, SigningKey

    API_KEY = os.environ.get('API_KEY_VENICE')
    BASE_URL = 'https://api.venice.ai/api/v1'
    MODEL = 'e2ee-qwen3-5-122b-a10b'
    HKDF_INFO = b'ecdsa_encryption'

    def main():
        # Paso 1: Genera el par de claves efímeras
        print('🔑 Generating ephemeral key pair...')
        private_key = SigningKey.generate(curve=SECP256k1)
        public_key = private_key.get_verifying_key()
        client_public_key_hex = (b'\x04' + public_key.to_string()).hex()

        # Paso 2: Obtén y verifica la atestación
        print('🔍 Fetching TEE attestation...')
        client_nonce = secrets.token_hex(32)  # se requieren 32 bytes
        attestation_res = requests.get(
            f'{BASE_URL}/tee/attestation',
            params={'model': MODEL, 'nonce': client_nonce},
            headers={'Authorization': f'Bearer {API_KEY}'},
            timeout=30
        )
        attestation = attestation_res.json()

        if attestation.get('verified') != True or attestation.get('nonce') != client_nonce:
            raise ValueError('Attestation verification failed')

        model_public_key = attestation.get('signing_key') or attestation.get('signing_public_key')
        print(f'✅ TEE attestation verified (provider: {attestation.get("tee_provider", "unknown")})')

        # Paso 3: Cifra el mensaje
        print('🔐 Encrypting message...')
        plaintext = 'What is 2+2? Answer briefly.'

        # Normaliza la clave pública
        key_hex = model_public_key
        if not key_hex.startswith('04') and len(key_hex) == 128:
            key_hex = '04' + key_hex

        model_key_bytes = bytes.fromhex(key_hex)
        model_verifying_key = VerifyingKey.from_string(model_key_bytes[1:], curve=SECP256k1)

        # ECDH
        ephemeral_private = SigningKey.generate(curve=SECP256k1)
        ephemeral_public = ephemeral_private.get_verifying_key()
        shared_point = model_verifying_key.pubkey.point * ephemeral_private.privkey.secret_multiplier
        shared_secret = shared_point.x().to_bytes(32, 'big')

        # Deriva la clave AES
        hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=HKDF_INFO)
        aes_key = hkdf.derive(shared_secret)

        # Cifra
        nonce = os.urandom(12)
        aesgcm = AESGCM(aes_key)
        ciphertext = aesgcm.encrypt(nonce, plaintext.encode('utf-8'), None)

        ephemeral_public_bytes = b'\x04' + ephemeral_public.to_string()
        result = ephemeral_public_bytes + nonce + ciphertext
        encrypted_content = result.hex()

        messages = [{'role': 'user', 'content': encrypted_content}]

        # Paso 4: Envía la solicitud E2EE
        print('📤 Sending encrypted request...')
        response = requests.post(
            f'{BASE_URL}/chat/completions',
            headers={
                'Authorization': f'Bearer {API_KEY}',
                'Content-Type': 'application/json',
                'X-Venice-TEE-Client-Pub-Key': client_public_key_hex,
                'X-Venice-TEE-Model-Pub-Key': model_public_key,
                'X-Venice-TEE-Signing-Algo': 'ecdsa'
            },
            json={'model': MODEL, 'messages': messages, 'stream': True},
            stream=True,
            timeout=60
        )

        # Paso 5: Descifra la respuesta
        print('📥 Decrypting response...\n')

        for line in response.iter_lines():
            if not line:
                continue
            line_str = line.decode('utf-8')
            if not line_str.startswith('data: ') or '[DONE]' in line_str:
                continue

            try:
                chunk = json.loads(line_str[6:])
                content = chunk.get('choices', [{}])[0].get('delta', {}).get('content', '')
                if not content:
                    continue

                # Comprueba si está cifrado
                if len(content) >= 186 and all(c in '0123456789abcdefABCDEF' for c in content):
                    raw = bytes.fromhex(content)
                    server_ephemeral_pub = raw[:65]
                    nonce = raw[65:77]
                    ciphertext = raw[77:]

                    server_verifying_key = VerifyingKey.from_string(server_ephemeral_pub[1:], curve=SECP256k1)
                    shared_point = server_verifying_key.pubkey.point * private_key.privkey.secret_multiplier
                    shared_secret = shared_point.x().to_bytes(32, 'big')

                    hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=HKDF_INFO)
                    aes_key = hkdf.derive(shared_secret)

                    aesgcm = AESGCM(aes_key)
                    plaintext = aesgcm.decrypt(nonce, ciphertext, None)
                    print(plaintext.decode('utf-8'), end='', flush=True)
                else:
                    print(content, end='', flush=True)
            except Exception:
                pass

        print('\n\n🔐 Response decrypted end-to-end')

    if __name__ == '__main__':
        main()
    ```
  </Tab>
</Tabs>

### Limitaciones de E2EE

<Warning>
  E2EE tiene algunas restricciones debido a los requisitos de cifrado:
</Warning>

| Funcionalidad           | Estado                                                 |
| ----------------------- | ------------------------------------------------------ |
| Streaming               | **Obligatorio** (no se admite el modo sin streaming)   |
| Búsqueda web            | **Deshabilitada** (filtraría contenido)                |
| Subida de archivos      | **No admitida**                                        |
| Llamadas a funciones    | **No admitido**                                        |
| System prompt de Venice | **Deshabilitado** (debe cifrarse del lado del cliente) |

### Buenas prácticas de seguridad

1. **Genera nuevos pares de claves por sesión** — No reutilices claves efímeras.
2. **Rellena con ceros las claves privadas** — Borra los bytes de la clave privada de la memoria cuando termines.
3. **Verifica la atestación** — Comprueba siempre que `verified: true` y que el nonce coincide.
4. **Comprueba el modo de depuración** — Rechaza las atestaciones de enclaves en depuración.
5. **Usa streaming** — E2EE requiere streaming para una correcta fragmentación del cifrado.
6. **Gestiona los errores con cuidado** — No expongas los errores de descifrado a los usuarios.
7. **Usa nonces de 32 bytes** — Los proveedores de TEE requieren exactamente 32 bytes.

## Buenas prácticas

<AccordionGroup>
  <Accordion title="Verifica siempre la atestación en producción">
    No te limites a confiar en la respuesta `verified: true`. Parsea la cita Intel TDX en el cliente y verifica que las mediciones coinciden con los valores esperados. Para GPU de NVIDIA, verifica la atestación mediante el servicio de verificación de NVIDIA.
  </Accordion>

  <Accordion title="Usa nonces nuevos">
    Genera siempre un nuevo nonce aleatorio para cada solicitud de atestación. Esto evita ataques de repetición en los que un atacante podría servir una atestación obsoleta.
  </Accordion>

  <Accordion title="Verifica la vinculación de la clave">
    La clave de firma debe estar vinculada al campo TDX REPORTDATA. Esto demuestra que la clave se generó dentro del enclave.
  </Accordion>

  <Accordion title="Comprueba el modo de depuración">
    Verifica que la atestación TDX no tenga marcas de depuración activadas. Un enclave de depuración puede inspeccionarse y no debe considerarse confiable para producción.
  </Accordion>

  <Accordion title="Usa nuestros SDK para E2EE">
    E2EE requiere una implementación criptográfica cuidadosa. Usa nuestros SDK oficiales en lugar de implementar el protocolo por tu cuenta.
  </Accordion>
</AccordionGroup>

## Comprobación de las capacidades del modelo

Puedes comprobar si un modelo admite TEE o E2EE a través del endpoint de modelos:

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl https://api.venice.ai/api/v1/models \
    -H "Authorization: Bearer $API_KEY_VENICE" | jq '.data[] | select(.model_spec.capabilities.supportsTeeAttestation == true or .model_spec.capabilities.supportsE2EE == true) | {id, tee: .model_spec.capabilities.supportsTeeAttestation, e2ee: .model_spec.capabilities.supportsE2EE}'
  ```

  ```python Python theme={"dark"}
  models = client.models.list()

  for model in models.data:
      caps = getattr(model, 'model_spec', {}).get('capabilities', {})
      if caps.get('supportsTeeAttestation') or caps.get('supportsE2EE'):
          print(f"{model.id}: TEE={caps.get('supportsTeeAttestation')}, E2EE={caps.get('supportsE2EE')}")
  ```
</CodeGroup>

## Manejo de errores

| Error                                 | Causa                                          | Solución                                      |
| ------------------------------------- | ---------------------------------------------- | --------------------------------------------- |
| `TEE attestation verification failed` | La atestación no superó la validación          | Reintenta o contacta con soporte              |
| `Attestation nonce mismatch`          | Posible ataque de repetición                   | Genera un nonce nuevo                         |
| `TDX debug mode detected`             | El enclave está en modo de depuración          | No usar para producción                       |
| `Failed to decrypt field`             | Falló el descifrado E2EE del lado del servidor | Revisa tu implementación de cifrado           |
| `E2EE requires streaming`             | Solicitud sin streaming a un modelo E2EE       | Establece `stream: true`                      |
| `Encrypted field is not valid hex`    | Se envió texto plano con cabeceras E2EE        | Cifra todos los mensajes user/system          |
| `Invalid public key`                  | Formato de clave incorrecto                    | Usa 130 caracteres hex que comiencen con `04` |

## Solución de problemas

<AccordionGroup>
  <Accordion title="502 Bad Gateway o 'Nonce must be exactly 32 bytes'">
    La longitud del nonce es incorrecta. Los proveedores de TEE requieren exactamente **32 bytes (64 caracteres hexadecimales)**.

    * Usa `crypto.randomBytes(32).toString('hex')` (JS) o `secrets.token_hex(32)` (Python).
    * Error común: `secrets.token_hex(16)` produce 32 caracteres hex (16 bytes), no 32 bytes.
  </Accordion>

  <Accordion title="Verificación de la atestación fallida">
    * Comprueba que el modelo admite E2EE (`supportsE2EE: true`).
    * Verifica que tu clave API es válida y tiene acceso al modelo solicitado.
    * Verifica la conectividad de red con la API de Venice.
  </Accordion>

  <Accordion title="Descifrado fallido">
    * Asegúrate de utilizar la misma clave privada que generó la clave pública enviada en las cabeceras.
    * Comprueba que el contenido de la respuesta está realmente codificado en hexadecimal (E2EE activo).
    * Verifica que la clave pública del modelo coincide con la utilizada para cifrar.
  </Accordion>

  <Accordion title="Encrypted field is not valid hex">
    * Todos los mensajes con rol `user` y `system` deben estar cifrados cuando hay cabeceras E2EE presentes.
    * Verifica que tu contenido cifrado pasa la validación `isValidEncrypted()` (mínimo 186 caracteres hex).
    * Comprueba que la salida del cifrado es hex en minúsculas sin ningún prefijo.
  </Accordion>

  <Accordion title="Errores de clave pública no válida">
    * La clave pública del cliente debe tener exactamente **130 caracteres hexadecimales** comenzando con `04`.
    * Usa el ayudante `validateClientPubkey()` para verificar el formato antes de enviar.
    * Asegúrate de utilizar el formato de clave pública no comprimido (65 bytes = 130 caracteres hex).
  </Accordion>

  <Accordion title="Modelo no encontrado">
    * Verifica que el ID del modelo es correcto y que el modelo admite E2EE.
    * Usa el endpoint `/models` para comprobar los modelos E2EE disponibles.
  </Accordion>
</AccordionGroup>

## Recursos

* [Documentación de Intel TDX](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/documentation.html)
* [NVIDIA Confidential Computing](https://developer.nvidia.com/confidential-computing)
