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

# End-to-End Encryption

> Add field-level encryption to requests, on top of TLS, using a key from the attested keyset.

E2EE encrypts selected request and response fields between your client and the attested gateway.
TLS still protects the connection; E2EE adds field-level encryption whose public key comes from the
gateway's [attested keyset](/confidential-ai/attestation-report). This guide shows how to send an
E2EE request. For the trust model and what E2EE does and does not protect, read
[End-to-end encryption](/confidential-ai/e2e-encryption).

<Note>
  E2EE is decrypted inside the attested gateway so it can process your request. It proves the
  decryption key belongs to the attested workload; it does not hide the request from that workload. For
  the upstream to be attested too, use a [confidential model](/confidential-ai/confidential-models).
</Note>

## When to use it

Use E2EE when you want field-level encryption bound to a key you can trace to the attestation
report, in addition to TLS. If TLS is sufficient for your threat model, you can call the API normally
without these headers.

## 1. Get the gateway E2EE key

Fetch the attestation report and read the E2EE public key from the attested keyset. Verify the report
first (see [Verify a response](/guides/verify-a-response)) so you know the key is bound to a genuine
TEE.

```bash theme={null}
curl -s "https://api.redpill.ai/v1/aci/attestation?nonce=$(openssl rand -hex 16)" \
  -H "Authorization: Bearer $API_KEY" \
  | jq -r '.attestation.workload_keyset.e2ee_public_keys[] | select(.key_id=="dstack-kms-e2ee-v1") | .public_key'
```

The matching key uses the `secp256k1-aes-256-gcm-hkdf-sha256` algorithm.

## 2. Send E2EE headers

Include these headers on your request. Do not send `X-Signing-Algo`; that selects the legacy path.

| Header             | Value                                    |
| ------------------ | ---------------------------------------- |
| `X-E2EE-Version`   | `2`                                      |
| `X-Client-Pub-Key` | Your secp256k1 public key, hex encoded.  |
| `X-Model-Pub-Key`  | The gateway E2EE public key from step 1. |
| `X-E2EE-Nonce`     | A unique per-request nonce.              |
| `X-E2EE-Timestamp` | Unix seconds, close to gateway time.     |

## 3. Encrypt the selected fields

Each encrypted field is the lowercase hex encoding of:

```text theme={null}
ephemeral_uncompressed_secp256k1_public_key || aes_gcm_nonce || ciphertext_tag
```

The AEAD associated data binds the ciphertext to the protocol version, direction, algorithm, model,
field path, nonce, and timestamp. Any mismatch fails decryption.

<Warning>
  Assembling the ciphertext and AAD by hand is error-prone. Use the official client or verifier
  helpers, which implement the key exchange, AAD construction, and field selection for you.
</Warning>

## Receipts with E2EE

The response still carries an `x-receipt-id`. For an E2EE request, `request.received.body_hash` is the
hash of the gateway-observed **decrypted** body. When you verify, compare request hashes against the
decrypted body your client holds, not the encrypted bytes you sent. See
[Receipts](/confidential-ai/receipts).

## Related

<CardGroup cols={2}>
  <Card title="E2EE concept" icon="key" href="/confidential-ai/e2e-encryption" />

  <Card title="Verify a response" icon="circle-check" href="/guides/verify-a-response" />
</CardGroup>
