Confidential VMs & Tenant Attestation
A full virtual machine running as an Intel TDX Trust Domain, with root access and a GPU attached. You generate your own attestation quote from inside the VM and verify it offline against Intel's root CA. VoltageGPU is not in the trust path.
VM Tier vs Container Tier
The confidential container tier gives you a hardware-sealed container inside an Intel TDX enclave: your workload is protected, but attestation happens at the infrastructure level and the container does not expose /dev/tdx_guest. You have to trust the platform's attestation reports.
The Confidential VM tier removes that trust requirement. You get a full Ubuntu VM that itself runs as a TDX Trust Domain, with the TDX guest device exposed inside the VM. You generate the signed quote yourself, bind it to your own nonce, and verify the signature chain offline with Intel's open-source tooling. Nothing in the verification path is operated by VoltageGPU.
| Capability | Confidential Containers | Confidential VMs |
|---|---|---|
/dev/tdx_guest exposed | No | Yes |
| Attestation | Infrastructure-level | Tenant-generated TDX quote |
| Access level | Container root | Full VM root via sudo |
| Provisioning | Self-serve (API, dashboard, CLI) | Self-serve (deploy page, since 7 Sept 2026) |
| Persistent volumes | Available | Not yet, disk is ephemeral |
What You Get Inside the VM
Ubuntu 24.04 LTS
Ubuntu 24.04 LTS with kernel 6.8, running as an Intel TDX Trust Domain. Full root via sudo and direct SSH access, install anything you need.
GPU Attached
The GPU is attached to the VM and visible from inside it: nvidia-smi works out of the box for your training and inference workloads.
TDX Guest Interface
The VM exposes /dev/tdx_guest plus the tdx_guest and tsm kernel modules, everything you need to generate signed quotes via configfs TSM.
Request a Confidential VM
Deploy it yourself from your dashboard: pick a tier, give it a name, and it boots in about two minutes with your SSH key already on it. Add an SSH key to your account first, because a Confidential VM binds its keys at creation and can never be given one afterwards. The VM then appears under Your Pods with its SSH command, its live cost and a Release button, so you never wait on us to start or stop it.
Add Your SSH Key
Add a public SSH key to your account first. A Confidential VM binds its keys at creation and can never be given one afterwards.
Deploy It Yourself
Pick a size on the deploy page and the VM boots in about two and a half minutes as an Intel TDX Trust Domain, with your key on it. Email contact@voltagegpu.com only for a size we do not list.
SSH In
Connect with your key, verify the TDX environment, and run your workload. You have full root via sudo.
Fully self-serve. Deploying, managing and releasing a Confidential VM all happen from your own dashboard, with no email and no waiting. Container-tier pods are self-serve too.
Verify the Environment
Once you are in, confirm you are actually inside a TDX guest: the TDX guest device must exist, the kernel modules must be loaded, and the GPU must be visible.
$ ls -l /dev/tdx_guest crw------- 1 root root 10, 127 ... /dev/tdx_guest
$ lsmod | grep -E 'tdx_guest|tsm' tdx_guest ... tsm ...
$ nvidia-smi
Generate a TDX Quote
Step 1: Create a Report Entry
Quotes are generated through the kernel's configfs TSM interface. Creating a directory under /sys/kernel/config/tsm/report opens a new report request.
sudo mkdir /sys/kernel/config/tsm/report/r1
Step 2: Write Custom report_data
Write exactly 64 bytes of custom report_data into inblob. Binding a hash of a fresh nonce is the standard way to prove the quote was generated now, for you, and is not a replay. SHA-512 output is exactly 64 bytes, so it fits inblob perfectly.
head -c 32 /dev/urandom > nonce.bin openssl dgst -sha512 -binary nonce.bin | \ sudo tee /sys/kernel/config/tsm/report/r1/inblob > /dev/null
Keep the nonce. The verifier needs nonce.bin later to confirm the quote embeds the SHA-512 of that exact nonce in its report_data field.
Step 3: Read the Signed Quote
Reading outblob triggers quote generation inside the Trust Domain and returns the signed quote. On this tier the output is a TDX quote v4 of 5247 bytes.
sudo cat /sys/kernel/config/tsm/report/r1/outblob > quote.bin ls -l quote.bin # 5247 bytes
Step 4: Sanity-Check the Header
Before running full verification, eyeball the first header bytes. A valid quote from this tier starts with 04 00 02 00 81 00.
$ xxd -l 6 quote.bin 00000000: 0400 0200 8100 ......
Header Fields
04 00 | bytes 0-1 | Quote format version 4 |
02 00 | bytes 2-3 | Attestation key type: ECDSA-P256 |
81 00 ... | bytes 4-7 | TEE type 0x81: Intel TDX |
Generate a GPU Attestation Report
The TDX quote above never covers the GPU. On a single-GPU H200 Confidential VM, the GPU produces its own attestation report, signed by NVIDIA rather than by Intel, and bound to a nonce you choose. That gives you two independent proofs from one session, with two separate roots of trust and VoltageGPU in neither. On the 8x H100 node the same proof exists for all eight GPUs, in NVIDIA's multi-GPU Protected PCIe mode: see the note at the end of this section, the reading differs and the SDK needs one option.
Read the state before anything else. If CC State is not ON, or DevTools Mode is not OFF, stop: a DevTools part produces a report whose measurements will not match NVIDIA reference values.
$ nvidia-smi conf-compute -q
CC State : ON
Multi-GPU Mode : None
CPU CC Capabilities : INTEL TDX
GPU CC Capabilities : CC Capable
CC GPUs Ready State : Ready
$ nvidia-smi conf-compute -d
DevTools Mode: OFFsudo apt-get install -y python3-pip python3-venv
python3 -m venv ~/att && . ~/att/bin/activate # a venv, not --break-system-packages
pip install nvidia-ml-py nv-attestation-sdk cryptography==43.0.1
python3 - <<'PY'
import os, pynvml as n
n.nvmlInit()
h = n.nvmlDeviceGetHandleByIndex(0)
nonce = os.urandom(32) # yours, generated here, now
rep = n.nvmlDeviceGetConfComputeGpuAttestationReport(h, nonce)
open("gpu_report.bin","wb").write(bytes(rep.attestationReport[:rep.attestationReportSize]))
print("report", rep.attestationReportSize, "bytes, nonce", nonce.hex())
PYpython3 - <<'PY'
import secrets, json, base64
from nv_attestation_sdk import attestation
nonce = secrets.token_hex(32)
c = attestation.Attestation()
c.set_name("my-vm"); c.set_nonce(nonce); c.set_claims_version("2.0")
c.add_verifier(attestation.Devices.GPU, attestation.Environment.REMOTE,
"https://nras.attestation.nvidia.com/v3/attest/gpu", "")
ev = c.get_evidence() # attest() without evidence raises in 2.7.3
print("attest ->", c.attest(ev))
open("nras_token.json","w").write(c.get_token())
PYA passing run prints Attestation Successful and writes a signed EAT token. The claims that matter are measres: success, meaning the runtime driver and VBIOS measurements matched NVIDIA's published golden values, and x-nvidia-gpu-attestation-report-nonce-match: true, which is what makes the report impossible to replay from an earlier session. Verified on this SKU on 4 September 2026: driver 595.71.05, VBIOS 96.00.CF.00.02, hwmodel GH100, secboot true, dbgstat disabled.
The deprecated local verifier (python3 -m verifier.cc_admin) currently fails its OCSP request from inside the guest and reaches end of support on 15 September 2026. Use the remote service above, or NVIDIA's C++ SDK.
Multi-GPU nodes read differently. On an 8-GPU node in NVIDIA's Protected PCIe mode, nvidia-smi conf-compute -q reports CC State OFF together with Multi-GPU Mode Protected PCIe: the second line is the one that matters. If CC GPUs Ready State reads Not Ready, run sudo nvidia-smi conf-compute -srs 1. The attestation SDK then needs get_evidence(options={"ppcie_mode": False}) (the option means "standalone mode"; with the default it refuses to run on a PPCIe system). Verified on 10 September 2026 on an 8x H100 VM: NVIDIA's remote service returned Attestation Successful for all eight GPUs, token published at /blog/two-proofs/evidence/ppcie-8x-h100/. NVSwitch attestation is not verified yet. The 8x H200 node reads the same mode (Ready State Not Ready on 4 and 9 September) and its attestation has not been run.
Where this does not work: RTX 6000B VMs read CC OFF, and the container tier gives the tenant no attestation. On those, the Intel TDX quote is the only hardware evidence available. Always run conf-compute -q yourself at first login rather than trusting any page, including this one.
Two Reports Are Never Byte-Identical
If you hash a report today and compare that hash against a fresh report tomorrow, the comparison will fail even on an untouched machine. The report is 4,129 bytes and three regions of it change between calls. Only 3,969 bytes are stable. This catches people on their first day, because the natural way to pin an approved state is to fingerprint the whole blob.
| Offset | Size | What it is | Changes when |
|---|---|---|---|
[4, 36) | 32 bytes | Your nonce, echoed back | You send a different nonce |
[3565, 3597) | 32 bytes | The GPU's own nonce | Every single call |
[4033, 4129) | 96 bytes | ECDSA signature | Every single call |
The first and the third surprise nobody. The middle one does: it changes even when you send the same nonce twice, so you only find it by fetching twice with an identical nonce and diffing the results. The report is a DMTF SPDM 1.1 MEASUREMENTS response, and SPDM has the responder contribute 32 random bytes of its own alongside yours. The signature covers both, which is why the signature moves with it. Neither field is a defect, and neither weakens the proof: the nonce you chose is still in there, still signed, still yours to check.
measres, driver version, VBIOS version, secboot, dbgstat). Those are the values that must not drift. If you insist on a raw-byte fingerprint, exclude the three ranges above, and be aware that the offsets are specific to this report version rather than guaranteed by NVIDIA.python3 - <<'PY'
import pynvml as n
n.nvmlInit()
h = n.nvmlDeviceGetHandleByIndex(0)
nonce = bytes(range(32)) # deliberately fixed, not random
def pull():
r = n.nvmlDeviceGetConfComputeGpuAttestationReport(h, nonce)
return bytes(r.attestationReport[:r.attestationReportSize])
a, b = pull(), pull()
runs, start = [], None
for i, (x, y) in enumerate(zip(a, b)):
if x != y and start is None: start = i
if x == y and start is not None: runs.append((start, i)); start = None
if start is not None: runs.append((start, len(a)))
print("size", len(a), "differing ranges", runs)
print("stable bytes", len(a) - sum(e - s for s, e in runs))
PYFound and reported by Mohammed Zoheb Shaik, who paid for the machine that produced it, ran the diff nobody else had run, and sent us the result. Published with his permission.
Artifacts You Can Verify Without a VM
The raw files from the 4 September 2026 run on a single-GPU H200 VM are published, unchanged since capture, with SHA-256 checksums. They let an auditor check NVIDIA's signature and Intel's quote format before spending anything on a machine. They do not replace a run on your own VM: only your own nonce proves your own session.
| File | Size | What it is | How to check it |
|---|---|---|---|
| nras_token.json | 3,055 bytes | NVIDIA's verdict: two JWTs signed ES384 by nras.attestation.nvidia.com, nonce 6154dbb2…, issued 2026-09-04 12:31:05 UTC | Verify the signatures against NVIDIA's JWKS, then read the claims (measres, driver, VBIOS, secboot, dbgstat) |
| gpu_report.bin + nonce.txt | 4,129 bytes | A raw SPDM measurement report from a second round with a different nonce (4206…) | Confirm the nonce at bytes [4, 36); use it to read the layout above. Not submitted to NRAS, cert chain not included |
| quote.bin + report_data.bin | 5,243 bytes | TDX v4 quote (header 04 00 02 00 81 00) with the tenant's 64-byte report_data at byte 568 | Intel DCAP QVL against Intel's root CA, then compare bytes [568, 632) with report_data.bin |
| SHA256SUMS, README.txt | Checksums and a plain-text description of each file, including what the set does not prove | sha256sum -c SHA256SUMS |
Concurrent Quotes: Retry on EINVAL, and Only on EINVAL
One quote at a time works every time. Several at once do not. Mohammed Zoheb Shaik, who is building Custodian, a key broker for model weights, reported on 9 September 2026 that with six quote requests in flight, roughly one in six came back with [Errno 22] Invalid argument from the kernel's configfs TSM interface (Ubuntu 24.04, kernel 6.8). The failure is in the kernel's TSM object as one entry is torn down while the next is created, not in the hardware and not in the quote itself. It fails closed: you get an error, never a quote bound to someone else's report_data.
EINVAL only. A permission error or a missing /dev/tdx_guest means something else is wrong and must fail immediately; a blanket retry would hide it. With that rule the tenant's broker ran twelve unattended renewals and two batches of eight parallel releases without a single crossed quote.import errno, time
def get_quote(report_data, attempts=5):
for i in range(attempts):
try:
return tsm_quote(report_data) # your configfs TSM call
except OSError as e:
if e.errno != errno.EINVAL or i == attempts - 1:
raise # EACCES, ENOENT and the rest: stop here
time.sleep(0.05 * (i + 1))Verify Offline with Intel DCAP
Verification is fully offline and uses Intel's open-source DCAP stack (SGXDataCenterAttestationPrimitives). The quote's ECDSA signature chains through the platform's PCK certificate up to Intel's root CA. VoltageGPU operates nothing in that chain: if the quote verifies, the guarantee comes from Intel silicon, not from us.
git clone https://github.com/intel/SGXDataCenterAttestationPrimitives # Build the Quote Verification Library (QVL) sample and run it # against quote.bin. It validates the signature chain up to the # Intel root CA, entirely offline.
Check report_data Freshness
A valid signature is not enough: also confirm the quote embeds your report_data. Recompute sha512(nonce.bin) and compare it with the 64-byte report_data field inside the quote body. A match proves the quote was generated after you wrote your nonce, inside that specific Trust Domain.
Full walkthrough. The complete step-by-step guide, including the exact DCAP build commands and the report_data offset inside the quote body, is published at voltagegpu.com/blog/tenant-tdx-attestation-guide.
Or bind both proofs to your workload with voltage-verify
voltage-verify is a small open-source tool (MIT, Python 3.10+) that folds the five steps above and the NVIDIA attestation into one flow, bound to a manifest you write: the image digest, the digests of your model or files, and a random challenge. The TDX quote carries SHA-512 of that manifest as report_data, NVIDIA's service signs the GPU claims over SHA-256 of it, and the verifier on your machine checks Intel's chain to the pinned root, TCB status, Quoting Enclave identity, revocation, NVIDIA's ES384 signatures and claims, then runs six negative tests.
# your machine pip install voltage-verify voltage-verify manifest --image ghcr.io/you/app:1.4.2 --artifact model.safetensors -o manifest.json # inside the VM, as root sudo -E python -m voltage_verify attest --manifest manifest.json -o bundle.json # your machine voltage-verify verify bundle.json --challenge <challenge printed by manifest> voltage-verify selftest bundle.json
Source and issues: github.com/Jabsama/voltage-verify; package on PyPI; wheel, reference bundle and documentation mirrored with SHA-256 sums at voltagegpu.com/blog/two-proofs/voltage-verify. It proves that both hardware proofs were issued for your manifest after your challenge. It does not prove that the GPU executed the named image; that needs a measured launcher, and the tool says so.
Honest Limits
Confidential computing claims are only worth something when the boundaries are stated plainly. Here is exactly what this tier does and does not attest.
The container tier has no tenant attestation
Confidential container pods do not expose /dev/tdx_guest. On that tier, attestation is infrastructure-level only: you cannot generate your own quote from inside a container. If tenant-side attestation is a requirement, use the VM tier documented on this page.
The quote attests the CPU TEE boundary, not the GPU
NVIDIA GPU attestation is verified on single-GPU H200 VMs (September 4, 2026, CC State ON, remote attestation successful) and on the 8x H100 node (September 10, 2026, multi-GPU Protected PCIe mode, all eight GPUs attested; NVSwitch not yet). The 8x H200 reads the same mode but is not attested yet, RTX 6000B VMs read CC OFF, and the container tier gives no tenant-side attestation. The TDX quote never attests the GPU: for GPU evidence, fetch the NVIDIA attestation report (nvmlDeviceGetConfComputeGpuAttestationReport or the NVIDIA attestation SDK) and verify it with NVIDIA's service.
No persistent volumes
The VM disk is ephemeral: when the VM is released, the disk is gone. Persistent volumes are not available on the VM tier yet, so copy models, checkpoints, and results off the box (e.g. via scp or rsync) before tearing it down. Deploying and releasing are both self-serve from Your Pods.
Running It, Stopping It, Paying For It
The single most expensive misunderstanding on this tier is what stops the meter. It is worth two minutes now.
sudo poweroff and sudo shutdown stop the operating system, but the machine stays rented and keeps costing. A powered-off VM is the worst of both: it does no work and still bills. The only thing that stops billing is releasing the VM.Release it yourself, from your dashboard
Your VM appears under Your Pods like any other machine. Open it and press Release VM. Billing stops at that moment, and the unused part of the prepaid hour is credited back to your balance automatically.
Copy your work off first
Releasing destroys the disk. There is no snapshot and no resume. Pull results down with scp -P <port> ubuntu@<ip>:~/results ./ before you press it.
Ask for an automatic stop if you want a hard limit
If you are running a fixed window, ask us to arm an automatic release at the end of it. It runs whether or not anyone is awake, so an overnight finish cannot bill you until morning. It is optional, and you can have it cancelled at any time.
What survives a reboot, and what does not
| Action | Machine | Disk contents | Billing |
|---|---|---|---|
sudo reboot | Restarts | Kept | Keeps running |
sudo poweroff | Stays off | Kept | Keeps running |
| Release VM | Destroyed | Destroyed | Stops, unused prepay refunded |
One consequence worth planning for: a reboot re-runs attestation from scratch. Your TDX quote and GPU attestation report are per boot, so a report you captured before a reboot no longer describes the machine you are on. Take a fresh one after any restart, the same way you took the first.