Skip to main content

Private models and datasets

This page is the supported way to run a model or a dataset that the GPU provider must not see. You rent a Confidential Computing machine, the pod proves from the inside what machine it is on, and only then does a key service you control release a short-lived key that decrypts your data. Nothing secret is stored in the image, the template or your Lium account.

What is guaranteed​

This holds on a Confidential Computing machine, once your own verifier has checked the pod's quote as in step 4.

GuaranteeHolds
The GPU provider cannot read the VM's memory, through the host OS, the hypervisor or physical access to RAMYes. Intel TDX encrypts it.
The GPU provider cannot read the VM's diskYes. The disk is encrypted with a key that only a VM with the same measurements can obtain.
A process in your pod can obtain a hardware-signed quote that binds a value you choose to the machine's measured softwareYes. See Attesting from inside your pod.
The quote identifies your container imageNo. It measures the machine: Lium's OS image and node software. Your image is not measured.
The quote identifies your podNo. Any pod on the same machine can ask for a quote. Rent every GPU of the machine so yours is the only pod on it.
Lium cannot access the podNo. Lium's node software runs inside the VM and manages your pod's container. The VM keeps the GPU provider out, not Lium.
GPU memory and the CPU-to-GPU link are protectedNot stated by the label. The label is based on the CPU's TDX quote and does not say whether the GPUs run in NVIDIA confidential-computing mode. If you need that, check the GPUs with NVIDIA's attestation tools from your pod before you load anything.
Data that leaves the VM is protectedOnly by what you do. Backups, network volumes, logs and network traffic leave the VM. Encrypt them yourself and use TLS.
The machine stays upNo. The provider can still power it off or cut its network. This is confidentiality, not availability.

On an ordinary machine none of the "Yes" rows hold: the provider can read the pod's memory, disk and environment (Pod security). The credential lifecycle below still limits how long a leaked credential works and what it opens. It does not hide anything from the host.

The flow​

1. Prepare outside Lium​

  • Encrypt the weights or the dataset with a data key, and upload only the ciphertext to your object store.
  • Keep the data key in a key service you control: a cloud KMS, Vault, or a small service of your own. This page calls it the key broker. It releases the key only to a pod that passes step 4.
  • Build an image that contains your code and the dstack SDK, and no secrets. Build arguments, ENV lines and image layers are readable by anyone who can pull the image.

2. Rent a Confidential Computing machine​

On Browse Pods, turn on the Confidential Computing filter and pick a machine. Rent all of its GPUs.

From the API, the listing carries the same flag on every node:

curl -s https://lium.io/api/executors \
| jq '[.[] | select(.specs.tdx_attestation_passed == true) | {id, machine_name, gpu_count}]'

Rent the node you picked with its full GPU count, so the rent is refused when another pod already holds part of the machine: Lium.up(executor_id=..., gpu_count=...) in the SDK or lium up <id> -c <gpu_count> in the CLI.

Do not pass secrets in the rent request, the template's environment variables or a startup command. They travel through Lium's control plane.

The label is what Lium last observed for the machine. Your verifier, not the label, decides whether the pod receives a key.

3. Get a quote from inside the pod​

Inside the pod, create a key pair that exists only in memory. Ask the broker for a fresh nonce. Bind both into the quote's report_data:

import hashlib

from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from dstack_sdk import DstackClient

nonce = get_nonce_from_your_broker() # your code
private_key = X25519PrivateKey.generate()
public_key = private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)

quote = DstackClient().get_quote(hashlib.sha256(public_key + nonce).digest())
send_to_your_broker(quote.quote, quote.event_log, public_key, nonce) # your code

4. Verify the quote and release the key​

The broker releases nothing until every check passes:

  1. Signature and TCB. The quote verifies against Intel's certificate chain with an Intel DCAP quote verifier or the dstack verifier, and its TCB status is up to date.
  2. Binding. report_data equals sha256(public_key + nonce), and the nonce is one the broker issued in the last few minutes and has not seen before.
  3. Measurements. The OS image measurements (MRTD, RTMR0 to RTMR2) are the ones of the dstack OS image release named in the CVM guide, and the compose hash in RTMR3 is the one published in the "CVM attestation" section of the current executor release notes. Reject any other value, even when the quote itself is genuine.

When all three pass, the broker encrypts the data key, or a short-lived storage credential, to the pod's public key and returns it. It logs the pod, the quote's hash and the time.

5. Load the data​

The pod decrypts the key, downloads the ciphertext and decrypts it into memory or into /root. Do not write plaintext to a network volume or to a path you back up.

6. End the run​

Terminate the pod. The in-memory key pair goes with it. Revoke any storage credential that has not expired, and rotate the data key when you retire that version of the model or dataset.

Credential lifecycle​

CredentialWhere it livesLifetimeHow it ends
Lium API keyYour laptop or CI, never the podLong; set an expiry and a budgetDelete it under Access → API Keys. A read and rent key is enough for this flow.
SSH private keyYour laptopLongThe pod only receives the public key.
Pod key pairPod memoryThe pod's lifetimeGone when the pod is terminated.
Data keyYour key brokerUntil you rotate itReleased per attested pod, wrapped to that pod's public key.
Storage credentialPod memoryMinutes to an hourA pre-signed URL or a temporary token, read-only, limited to the one object or prefix. It expires on its own, or you revoke it.