Confidential VM Tier

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.

Tenant-side TDX quotes
Full root via SSH
GPU attached, nvidia-smi works

VM Tier vs Container Tier

The confidential container tier, retired by our provider on 1 October 2026, gave you a hardware-sealed container inside an Intel TDX enclave: the workload was protected, but attestation happened at the infrastructure level and the container did not expose /dev/tdx_guest, so you had to trust the platform's attestation reports. The comparison below is kept for customers who used it.

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.

CapabilityConfidential ContainersConfidential VMs
/dev/tdx_guest exposedNoYes
AttestationInfrastructure-levelTenant-generated TDX quote
Access levelContainer rootFull VM root via sudo
ProvisioningSelf-serve (API, dashboard, CLI)Self-serve (deploy page, since 7 Sept 2026)
Persistent volumesAvailableNot 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.

Deploy 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.

1

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.

2

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.

3

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.

Check the TDX guest device
$ ls -l /dev/tdx_guest
crw------- 1 root root 10, 127 ... /dev/tdx_guest
Check the kernel modules
$ lsmod | grep -E 'tdx_guest|tsm'
tdx_guest              ...
tsm                    ...
Check the GPU
$ 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.

Fresh nonce, hashed to 64 bytes
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.

Expected header
$ xxd -l 6 quote.bin
00000000: 0400 0200 8100                           ......

Header Fields

04 00bytes 0-1Quote format version 4
02 00bytes 2-3Attestation key type: ECDSA-P256
81 00 ...bytes 4-7TEE type 0x81: Intel TDX

Generate a GPU Attestation Report

The TDX quote above never covers the GPU. On a single-GPU H200, H100 or RTX PRO 6000 Blackwell 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.

Step 1: confirm the GPU is in confidential mode
$ 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: OFF
Step 2: pull a report bound to your own nonce
sudo 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())
PY
Step 3: have NVIDIA verify it, not us
python3 - <<'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())
PY

A 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: the container tier gives the tenant no attestation, so the Intel TDX quote is the only hardware evidence available there. RTX PRO 6000 Blackwell VMs were attested on 17 September 2026 (CC State ON, NVIDIA remote attestation successful on a caller nonce, evidence at /blog/two-proofs/evidence/rtx6000b-2026-09-17/). Always run conf-compute -qyourself 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.

OffsetSizeWhat it isChanges when
[4, 36)32 bytesYour nonce, echoed backYou send a different nonce
[3565, 3597)32 bytesThe GPU's own nonceEvery single call
[4033, 4129)96 bytesECDSA signatureEvery 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.

What to pin instead. Compare the parsed measurement blocks, or the claims in the verified EAT token (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.
Reproduce it: same nonce, two reports, diff
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))
PY

Found 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.

FileSizeWhat it isHow to check it
nras_token.json3,055 bytesNVIDIA's verdict: two JWTs signed ES384 by nras.attestation.nvidia.com, nonce 6154dbb2…, issued 2026-09-04 12:31:05 UTCVerify the signatures against NVIDIA's JWKS, then read the claims (measres, driver, VBIOS, secboot, dbgstat)
gpu_report.bin + nonce.txt4,129 bytesA 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.bin5,243 bytesTDX v4 quote (header 04 00 02 00 81 00) with the tenant's 64-byte report_data at byte 568Intel DCAP QVL against Intel's root CA, then compare bytes [568, 632) with report_data.bin
SHA256SUMS, README.txtChecksums and a plain-text description of each file, including what the set does not provesha256sum -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.

What to do. Serialise quote generation inside your process, and retry on 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.
The shape of the retry
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))

The Guest Clock: What It Proves, and Where It Stops

If your workload makes a security decision based on elapsed time, a lease that expires, a token that must be refreshed, a window that must close, then the clock is part of your threat model. Inside a confidential VM it is worth knowing exactly how far the guarantee goes, because the answer differs by platform and it is not absolute on either.

On Intel TDX, which is what this tier runs. The TDX module, not the host, owns TSC virtualisation for a Trust Domain. The virtual TSC frequency is a property of the TD fixed when the TD is built, and an attempt to change it afterwards is rejected. That is why the Linux guest forces X86_FEATURE_TSC_RELIABLE: it also stops a fall back to a tick source whose rate the host could bend. A paused TD that resumes cannot be made to believe no time passed, so a lease driven by rdtsc sees the jump.

On AMD SEV-SNP, unless Secure TSC is enabled. The counter stays malleable by the host at runtime, through scaling and offset, so rdtsc alone is not a trusted time source there. AMD addresses this with Secure TSC, which is opt-in through a bit in the SEV_FEATURES field of the VM Save Area; Intel's equivalent feature is called Virtual TSC. This matters when comparing tiers: the confidential GPU VM sold by Azure is built on AMD SEV-SNP, not on Intel TDX.

Measured, not assumed. On a genuine GCP N2D SEV-SNP guest, with /dev/sev-guest present and sev_guest as the TSM provider, there is no Secure TSC at all and tsc_reliable is set on no CPU. So on the SEV-SNP you can actually rent today, the malleable case is the default and the protected one is the exception. The TDX guest was the only one of the three to clear the bar, with tsc_reliable on all four CPUs.

The available clocksources say the same thing more plainly. On TDX they are tsc and acpi_pm. On both AMD guests they are tsc, kvm-clock and acpi_pm. kvm-clock is the host telling the guest what time it is, and TDX does not leave it on the shelf. All three guests also report tsc_known_freq, meaning the kernel was handed its rate rather than measuring it: 2449.998 MHz on both AMD boxes, 2700.000 MHz on the Intel one. You can watch the handover in dmesg and you cannot check it against anything.

These measurements were run and shared by Mohammed Zoheb Shaik on 15 September 2026, on three GCP guests that were deleted afterwards. He also enumerated the TD report body field by field, 584 bytes with every one accounted for by a documented field, which is what turns the claim below from a reading of a specification into a property of a real capture.

Where it stops, on both platforms. Neither feature prevents the host from choosing the rate once, at launch. On AMD the host passes DESIRED_TSC_FREQ in the launch request to the security processor. On Intel the host sets the TD's frequency before the virtual CPUs exist, after which it is frozen. So freezing the clock mid-flight is closed, but a host that wants a one-hour lease to last longer in real time can set a slow rate once and never touch it again, and everything inside the guest stays perfectly monotonic. We looked for a way for a guest to verify the frequency it was handed against its own attestation evidence and found none documented: the report carries build and boot measurements, not the rate the counter runs at.

What to do about it. Treat the guest counter as a source of monotonicity, which it genuinely is, and not as a source of wall-clock truth. If a deadline has to hold against the operator, let a signed timestamp from the party on the other side of the exchange decide whether the window has closed, and use the guest counter only to order events between those checks. Put another way, and this is the framing we owe to the reporter below: the clock becomes trustworthy at the platform level rather than in your code, so what the bound is worth is reported per deployment rather than assumed.

Credit. The TDX-versus-SEV distinction here was sent to us on 13 September 2026 by Mohammed Zoheb Shaik, who is building Custodian, a key broker for model weights, after he checked the TDX behaviour on real hardware rather than taking our reading on trust. The launch-time frequency caveat is ours, found while verifying his report against the specifications. The published weakness of Secure TSC itself, its use as a co-location detection channel between confidential VMs, was reported to AMD in March 2024 by researchers at TU Graz and placed by AMD outside the current threat model for the feature.

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.

On your own machine, not the VM
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 nine negative tests. Use 0.2.1 or later: 0.2.1 fixes a key-pinning weakness in 0.2.0 (NVIDIA signing keys are now taken only from the bundle and chained to the pinned NRAS intermediate).

Three commands
# your machine
pip install "voltage-verify>=0.2.1"
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

# a bare TDX quote from any provider, no manifest, no NVIDIA (since 0.2.0)
voltage-verify quote quote.bin --report-data <64 bytes hex you expect>

Since 0.2.0 the same tool also verifies a bare Intel TDX quote that did not come from a VoltageGPU VM: voltage-verify quote takes the raw bytes written by /sys/kernel/config/tsm/report (or hex, or base64), checks the signature chain up to the pinned Intel root, then the TCB status, Quoting Enclave identity and revocation from Intel PCS. It is tested against Google's production Sapphire Rapids quote from go-tdx-guest, which it verifies and, correctly, reports as TCB OutOfDate.

Show your proof. Drop the bundle.json on voltagegpu.com/verify/share: the checks run again up to the Intel and NVIDIA roots, and you can publish a page with a badge for your clients or your team. The page is unlisted and never indexed, and you get a private link to remove it. Nothing is stored unless you choose to publish.

Source and issues: github.com/VoltageGPU/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), on the 8x H100 node (September 10, 2026, multi-GPU Protected PCIe mode, all eight GPUs attested; NVSwitch not attestable from the guest), on single-GPU H100 VMs (September 16, 2026) and on RTX PRO 6000 Blackwell VMs (September 17, 2026, CC State ON, verified with voltage-verify). The 8x H200 reads the same mode but is not attested yet, 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.

Halting the guest does not stop the rental. 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.
1

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.

2

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.

3

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

ActionMachineDisk contentsBilling
sudo rebootRestartsKeptKeeps running
sudo poweroffStays offKeptKeeps running
Release VMDestroyedDestroyedStops, 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.

There is no web terminal on this tier, and that is deliberate. The machine carries your public key and nothing else. We hold no key for it, so we cannot open a shell on your VM, and we do not offer a button that pretends otherwise. Connect with your own SSH client, from your own machine.

Next steps