Confidential Compute (TEE)

Experimental feature. Confidential Compute (TEE) is experimental and under active development. Its SDL syntax, attestation API, and runtime behavior may change without notice. We encourage you to try it and share feedback, but plan for breaking changes.

Deploy workloads inside hardware-isolated Trusted Execution Environments (TEEs) where neither the provider nor any other party can access your data or code in memory.

Standard cloud deployments require trusting the infrastructure operator. Confidential Compute eliminates that requirement. Containers run inside encrypted virtual machines where the CPU hardware enforces isolation, so the provider’s OS, hypervisor, and administrators cannot inspect the workload’s memory.

Akash supports AMD SEV-SNP and Intel TDX. Tenants specify a TEE capability (cpu or cpu-gpu) in their SDL, and the provider resolves the actual hardware platform (snp or tdx) at deployment time based on its cluster nodes. NVIDIA GPU Confidential Computing is available with the cpu-gpu capability.

Important: Private container registries are not supported yet. Confidential Compute workloads can only pull images from public registries. If your SDL references an image that requires registry credentials to pull, the deployment will fail. See Limitations and Considerations for details.


Why Confidential Compute?

Hardware-Enforced Isolation

In a standard deployment, the provider’s operating system has full access to container memory. With Confidential Compute:

  • Memory is encrypted by the CPU, this means the provider’s OS, hypervisor and administrators cannot read it
  • Workloads run in a Trusted Execution Environment (TEE), which is an hardware-level isolation, not software sandboxing
  • Attestation provides cryptographic proof that the workload is running in a genuine TEE with the expected configuration
  • GPU memory can also be protected via NVIDIA Confidential Computing

Use Cases

AI & Machine Learning:

  • Private model inference (protect proprietary models)
  • Confidential fine-tuning on sensitive data

Healthcare:

  • Processing protected health information (PHI)
  • Drug discovery on confidential compounds

General Privacy:

  • Any workload handling secrets, PII or proprietary algorithms
  • Zero-trust deployments where you cannot trust the infrastructure

Supported TEE Types

Akash uses capability-based TEE types. You choose the workload type; the provider resolves the hardware platform.

CapabilitySDL ValueDescription
CPU-only TEEcpuConfidential VM with CPU memory encryption
CPU + GPU TEEcpu-gpuConfidential VM with CPU memory encryption plus NVIDIA GPU Confidential Computing

The provider selects the actual runtime class based on its detected hardware platform:

CapabilityPlatformRuntime Class
cpuIntel TDX (tdx)kata-qemu-tdx
cpuAMD SEV-SNP (snp)kata-qemu-snp
cpu-gpuIntel TDX (tdx)kata-qemu-nvidia-gpu-tdx
cpu-gpuAMD SEV-SNP (snp)kata-qemu-nvidia-gpu-snp

Both Intel TDX and AMD SEV-SNP provide equivalent security guarantees. The actual platform used depends on the provider’s hardware. Use cpu-gpu when your workload requires GPU acceleration (e.g., AI inference or training).


How It Works

Deploying a confidential workload requires only a params.tee value in your SDL. The platform handles the rest:

  1. Your SDL specifies the TEE capability via params.tee (cpu or cpu-gpu)
  2. The chain-SDK projects tee/type=<value> as a placement requirement so only capable providers can bid
  3. The provider matches the bid using its advertised tee/type attribute and resolves the RuntimeClass from the requested capability plus its detected hardware platform (tdx or snp)
  4. A Kata Container VM launches inside the hardware TEE (AMD SEV-SNP or Intel TDX)
  5. Your container runs inside the encrypted VM and all memory is hardware-encrypted
  6. The Akash attestation sidecar is injected by default (unless disabled by the tenant)
  7. You can verify the TEE at any time by requesting a hardware-signed attestation report from the provider

Everything inside the VM boundary is encrypted. The provider’s OS and administrators cannot access it.


SDL Configuration

Set params.tee to the desired capability in your service definition. The rest of the SDL remains unchanged.

Important: Images must come from a public registry. Private container registries are not supported yet for Confidential Compute. Every image referenced by a TEE service must be publicly pullable, images requiring pull credentials will cause the deployment to fail.

Basic Example — CPU-only TEE

---
version: "2.1"
services:
web:
image: nginx
expose:
- port: 80
as: 80
to:
- global: true
params:
tee: cpu
profiles:
compute:
web:
resources:
cpu:
units: 0.5
memory:
size: 256Mi
storage:
size: 128Mi
placement:
westcoast:
pricing:
web:
denom: uact
amount: 1000
deployment:
web:
westcoast:
profile: web
count: 1

GPU + TEE Example

To combine GPU workloads with Confidential Compute, use cpu-gpu and add GPU resources:

---
version: "2.1"
services:
inference:
image: my-private-model:latest
expose:
- port: 8080
as: 80
to:
- global: true
params:
tee: cpu-gpu
profiles:
compute:
inference:
resources:
cpu:
units: 0.5
memory:
size: 256Mi
storage:
size: 128Mi
gpu:
units: 1
attributes:
vendor:
nvidia:
placement:
westcoast:
pricing:
inference:
denom: uact
amount: 10000
deployment:
inference:
westcoast:
profile: inference
count: 1

TEE Type Reference

The params.tee field accepts the following values:

ValueRuntime Class (Intel TDX)Runtime Class (AMD SEV-SNP)Description
cpukata-qemu-tdxkata-qemu-snpCPU-only confidential VM
cpu-gpukata-qemu-nvidia-gpu-tdxkata-qemu-nvidia-gpu-snpConfidential VM with NVIDIA GPU CC

cpu-gpu must be paired with GPU resources in the compute profile. GPU CC workloads request the nvidia.com/pgpu Kubernetes resource for VFIO passthrough.


Preparing Images for Confidential Compute

A TEE workload runs inside a Kata VM, and its image is pulled and unpacked inside the encrypted guest (“guest pull”) rather than on the host. A few image properties that don’t matter for a normal deployment become important here. Following these keeps you on the happy path.

Run as a numeric user, not a named one

The image must run as a numeric UID/GID, not a username. Because the image is mounted inside the guest, the host cannot read the image’s /etc/passwd / /etc/group to translate a name into an ID. An image whose config sets, for example, USER appuser:appgroup fails at container creation with:

failed to create containerd container: mount callback failed on ...: openat etc/passwd: no such file or directory

Fix it in the image by using numeric IDs (or root):

# Instead of: USER appuser:appgroup
USER 1000:1000

If you don’t control the image, re-publish it with only a numeric-user change — the image contents are unchanged, so this is fast and safe:

Terminal window
# Find the numeric IDs the named user maps to
docker run --rm <image> id <username>
# Re-tag with a numeric user (config-only change) to a PUBLIC registry
crane mutate <image> --user "1000:1000" -t <public-registry>/<image>:<tag>

(Publish to a public registry — private registries are not supported yet, see below.)

Keep images small and size memory accordingly

The image is decompressed and unpacked into the guest’s memory (shared_fs is disabled, so there is no host-shared filesystem). This has two consequences:

  • The extracted image must fit in the guest. Request enough memory to hold the extracted image plus your application’s working set, with headroom. A ~10 GB compressed image can expand to 20-30 GB; if it doesn’t fit, container creation fails with Failed to unpack layer to destination.
  • Very large images can time out. The image is fetched during container creation inside the guest rather than by the normal host image pull, so very large images may exceed the request timeout and fail with context deadline exceeded. You cannot change this from the SDL; providers can raise the timeout, but the reliable fix is a smaller image.

Prefer smaller images. For AI inference, a lightweight server that downloads the model at runtime (for example Ollama) is far easier to run confidentially than a multi-gigabyte all-in-one CUDA image.

Known-good example — confidential GPU inference with Ollama

This small, root-user, public image serves a Llama model under cpu-gpu with none of the pitfalls above:

---
version: "2.1"
services:
ollama:
image: ollama/ollama:latest
expose:
- port: 11434
as: 11434
to:
- global: true
command:
- bash
- "-lc"
args:
- |
/bin/ollama serve &
until /bin/ollama ps >/dev/null 2>&1; do sleep 1; done
/bin/ollama pull llama3.2:3b
wait
params:
tee: cpu-gpu
profiles:
compute:
ollama:
resources:
cpu:
units: 4
memory:
size: 16Gi
storage:
size: 20Gi
gpu:
units: 1
attributes:
vendor:
nvidia:
placement:
dcloud:
pricing:
ollama:
denom: uact
amount: 100000
deployment:
ollama:
dcloud:
profile: ollama
count: 1

Once the lease is running, test inference against the forwarded port:

Terminal window
curl http://<lease-host>:<port>/api/generate \
-d '{"model":"llama3.2:3b","prompt":"Hello from a confidential GPU","stream":false}'

Swap llama3.2:3b for llama3.2:1b for an even lighter demo.


Attestation

Attestation is how you verify that your workload is genuinely running inside a hardware TEE. The attestation report is signed by the CPU hardware itself and the provider cannot forge or tamper with it.

Overview

The attestation flow has two stages:

  1. Challenge: Send a random 64-byte nonce (your challenge) to the provider’s attestation quote endpoint. The nonce ensures the report is fresh and was generated for your specific request
  2. Verification: The provider proxies your request to the in-pod attestation sidecar, which returns a hardware-signed report. Verify it against AMD’s, Intel’s, or NVIDIA’s published root-of-trust certificates to confirm the TEE is genuine

Using the CLI

The simplest way to request attestation:

Terminal window
provider-services lease-attestation \
--dseq <deployment-sequence> \
--gseq <group-sequence> \
--oseq <order-sequence> \
--provider <provider-address> \
--from <your-key>

API Reference

The attestation API exposes a single quote endpoint. The provider forwards your nonce to the attestation sidecar running inside the TEE and returns the hardware-signed response verbatim.

Quote (Challenge-Response)

Send your nonce to receive a hardware-signed attestation report:

POST /lease/{dseq}/{gseq}/{oseq}/attestation/quote

Request:

{
"nonce": "<base64-encoded-64-bytes>",
"bind_tls": false
}

Response:

{
"report": "<base64-raw-attestation-report>",
"cert_chain": "<base64-certificate-chain>",
"tee_type": "snp",
"auxblob": "<base64-auxiliary-blob>",
"gpu_reports": [
{
"device_index": 0,
"report": "<base64-gpu-attestation>"
}
],
"tls_bound": false
}

The report field contains the raw hardware-signed attestation evidence (an SNP report or TDX quote). For GPU TEE types, gpu_reports contains a per-device entry for every GPU in the workload.

GPU Report Format

Each gpu_reports[].report value is a base64-encoded blob that contains two concatenated parts:

[SPDM evidence (variable length)][PEM certificate chain (variable length)]

Split on the first -----BEGIN CERTIFICATE----- marker to separate them:

  • Before the marker — SPDM measurement records and signature (the GPU attestation evidence)
  • From the marker onward — PEM-encoded certificate chain, 5 certificates in order:
    1. Device certificate (leaf)
    2. GSP BROM certificate
    3. Provisioner ICA
    4. Identity CA
    5. NVIDIA Device Identity CA (self-signed root)

TLS Channel Binding

Setting bind_tls: true binds the attestation report to the current TLS session. The sidecar computes SHA-512(tls_public_key || nonce)[:64] and places the result in the report’s REPORT_DATA field. This proves the attestation came from the same endpoint you’re connected to, preventing relay attacks.

Security Model

The attestation design enforces these properties:

  • Provider cannot modify evidence — the nonce and hardware report are passed through verbatim
  • Nonce proves freshness — the hardware includes your nonce in REPORT_DATA, proving the report was generated for your request
  • Channel binding is optional but recommended — for sensitive workloads, use bind_tls: true to prevent attestation relay

Limitations and Considerations

  • Private container registries are not supported yet. Confidential Compute workloads can only pull images from public registries. Images that require authentication (pull credentials/imagePullSecrets) cannot be used with a TEE deployment, and the deployment will fail if it references one. Support for private registries is planned but not yet available. For now, ensure any image used in a confidential workload is publicly pullable.
  • Provider availability: Only providers with TEE-capable hardware can accept confidential workloads. Look for the tee/type attribute when selecting a provider.
  • Performance: Memory encryption adds a small overhead (~1-5%). GPU Confidential Computing may add further overhead depending on the workload.
  • Sidecar resources: The attestation sidecar consumes modest resources (10m CPU, 32-64Mi memory) which are automatically included in resource accounting.
  • Runtime environment: TEE workloads run inside Kata VMs rather than standard containers. Most workloads are unaffected, but features that depend on direct host kernel access may behave differently.
  • Distroless and scratch-based images are not supported. Kata Containers uses a guest agent inside the VM to set up and manage the container filesystem. Images built FROM scratch or from gcr.io/distroless/... lack the minimal filesystem structure (e.g. /dev, /proc, /sys) that the guest agent requires to initialize the container. Use a minimal but complete base image such as alpine or ubuntu instead.
  • Images must run as a numeric user, not a named one. A named USER in the image (e.g. USER appuser) fails at container creation (openat etc/passwd: no such file or directory) because the host cannot resolve the name against the in-guest filesystem. Use a numeric UID:GID (or root). See Preparing Images for Confidential Compute.
  • Image size is bounded by guest memory. The image is unpacked into guest RAM (there is no host-shared filesystem), so large images need a correspondingly large memory request and can otherwise fail to unpack (Failed to unpack layer to destination) or time out during creation (context deadline exceeded). Prefer small images and size memory for the extracted image plus your working set.
  • Ephemeral storage is not a real disk (and not extra RAM). With shared_fs disabled, the container’s writable layer lives in the guest’s RAM. A storage request is neither turned into a disk of that size nor into that much RAM (RAM comes from memory); usable writable space is bounded by the VM’s memory, and writing past it fails with an out-of-space error. Size memory for what your workload writes. See Preparing Images for Confidential Compute.