Skip to main content
Prove your exact Docker containers run unmodified in the TEE. Verify no one substituted different code.

What Should You Verify?

Basic verification: Confirm your application code (reportData and compose-hash) runs in genuine TEE hardware. Suitable for most use cases. Advanced verification: Everything from Basic, plus RTMR3 event log replay, on-chain governance checks, and source code provenance. Required for high-assurance applications (DeFi, confidential AI, sensitive data).

Basic Verification

Verify your application code runs in genuine TEE hardware. This verification confirms:
  • Your specific data (reportData) is present in the quote
  • Your application configuration (compose-hash) matches what’s deployed
  • The quote comes from genuine Intel TDX hardware
This code runs on the verifier side (end users or auditors checking your CVM), not inside the CVM itself.

Prerequisites

Your application must expose attestation endpoints. See Get Attestation for how to set this up inside your CVM. Fetch all required data from your CVM:
// Fetch attestation quote
const attestResponse = await fetch('https://your-app.example.com/attestation');
const attestData = await attestResponse.json();

const quote = attestData.quote;
const eventLog = attestData.event_log;
const reportData = attestData.report_data;

// Fetch application configuration
const infoResponse = await fetch('https://your-app.example.com/info');
const appInfo = await infoResponse.json();
const appComposeConfig = appInfo.tcb_info.app_compose;

Step 1: Verify reportData

Check that reportData contains the expected challenge or public key:
import assert from 'assert';

const expectedChallenge = 'your-expected-challenge-hex';
assert(reportData.startsWith(expectedChallenge), 'reportData mismatch');

Step 2: Verify compose-hash

The compose-hash is a SHA256 hash of your app-compose.json configuration file (which includes your docker-compose.yaml plus metadata). This hash proves which exact Docker images are running. Verify the configuration you fetch from the CVM matches what’s attested in RTMR3:
import assert from 'assert';
import { createHash } from 'crypto';

// Calculate SHA-256 hash of app-compose
const calculatedHash = createHash('sha256')
  .update(appComposeConfig)
  .digest('hex');

// Extract attested hash from RTMR3 event log
const events = JSON.parse(eventLog);
const composeEvent = events.find(e => e.event === 'compose-hash');
const attestedHash = composeEvent.event_payload;

// Verify hashes match
assert.strictEqual(calculatedHash, attestedHash, 'compose-hash mismatch');

Step 3: Verify quote signature

Verify the TDX quote was signed by genuine Intel hardware. This uses Phala Cloud’s verification API (alternatively, you can use dcap-qvl locally for trustless verification):
import assert from 'assert';

const verifyResponse = await fetch('https://cloud-api.phala.network/api/v1/attestations/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ hex: quote })
});

const result = await verifyResponse.json();
assert(result.quote.verified, 'Hardware verification failed');
You’ve verified your application code runs in genuine TEE hardware.

Advanced Verification

For high-assurance applications, verify additional security properties beyond Basic Verification:
  • RTMR3 event log replay: Cryptographically prove the boot sequence
  • Docker image digests: Ensure images are pinned to specific versions
  • On-chain governance: Verify only authorized configs can run
  • Source code provenance: Link images back to audited source code
All verification steps must run outside the CVM. Only verification that happens outside the TEE provides security guarantees.

Prerequisites

Complete Basic Verification first. You’ll need the same data (quote, event_log, app_compose, calculated hash).

Step 1: Verify RTMR3 event log replay

RTMR3 uses a hash chain where each event extends the previous value. The event log records three events during boot:
  1. compose-hash - Your application configuration
  2. instance-id - Unique CVM instance identifier
  3. key-provider - KMS that distributed encryption keys
The hash chain mechanism ensures no one can modify the event log without breaking the chain. Each new value is computed as: RTMR3_new = SHA384(RTMR3_old || SHA384(event)). To verify this:
  1. Parse the event log and extract all RTMR3 events (where imr === 3)
  2. Start with initial RTMR3 value (48 zero bytes)
  3. For each event digest in order:
    • Pad digest to 48 bytes if needed
    • Compute: RTMR3 = SHA384(RTMR3 || digest)
  4. Compare final replayed RTMR3 with the rtmr3 value from the quote
TODO: The dstack SDK does not currently export a replayRtmrs() utility function for external verifiers. For production use, refer to the Rust verification implementation or use trust-center for complete RTMR3 replay verification.

Step 2: Verify Docker image digests

Docker images must be pinned by SHA256 digest, not mutable tags. Tags like nginx:latest can point to different images over time, breaking verification. Verify all images in your app-compose use immutable digests:
import assert from 'assert';

// Parse app-compose and extract docker-compose
const appCompose = JSON.parse(appComposeConfig);
const dockerComposeYaml = appCompose.docker_compose_file;

// Check all image lines use @sha256 digests
const imageLines = dockerComposeYaml.split('\n').filter(line =>
  line.trim().startsWith('image:')
);

for (const line of imageLines) {
  assert(
    line.includes('@sha256:'),
    `Image not pinned by digest: ${line.trim()}`
  );
}
Example of proper image pinning:
services:
  app:
    # BAD: Mutable tag
    image: nginx:latest

    # GOOD: Immutable digest
    image: nginx@sha256:eee5eae48e79b2e75178328c7c585b89d676eaae616f03f9a1813aaed820745a
Get the digest for any image:
docker pull nginx:latest
docker inspect nginx:latest | grep -A 1 RepoDigests

Step 3: Verify on-chain governance (optional)

If your application uses on-chain governance, verify the compose-hash is whitelisted. This ensures only authorized application versions can boot:
# Check if compose-hash is authorized in DstackApp contract
cast call <DstackApp_ADDRESS> "allowedComposeHashes(bytes32)" <COMPOSE_HASH>
# Returns true if whitelisted, false otherwise

# Monitor for new authorized versions
cast logs <DstackApp_ADDRESS> --event "ComposeHashAdded(bytes32,address)"

Step 4: Verify source code provenance

Link Docker image digests back to audited source code using cryptographic build provenance. Recommended: Sigstore for GitHub builds Sigstore cryptographically links container images to specific GitHub commits and workflows. When you build images via GitHub Actions, Sigstore automatically signs the build with GitHub’s identity, creating unforgeable proof. Platform components use Sigstore: Set up Sigstore signing in your GitHub Actions workflow to provide the same cryptographic build provenance for your applications. Alternative: Reproducible builds For maximum verifiability, use reproducible builds where anyone can rebuild from source and get identical digests. Publish your Dockerfile and build instructions. This requires careful environment control but removes the need to trust any build service.

Attack Scenarios Prevented

Compromised Docker registry: The TEE verifies image digests against compose-hash before pulling. Even if the registry serves malicious images, digest verification fails. Code substitution: The compose-hash in RTMR3 is recorded by TEE hardware during boot. No one can modify it without invalidating the attestation quote signature. Unauthorized updates: On-chain governance (if enabled) ensures only whitelisted compose hashes can boot. Malicious developers cannot deploy unapproved versions. Event log tampering: RTMR3 event log replay cryptographically proves the events are authentic. Modification causes replay to produce different RTMR3 that won’t match the quote.

Verification Checklist

Complete application verification requires checking:
  • All Docker images use SHA256 digests (not tags)
  • Calculated compose-hash matches expected value
  • RTMR3 event log replays to quoted RTMR3 value
  • Attested compose-hash matches calculated hash
  • compose-hash is whitelisted on-chain (if using governance)
  • reportData contains expected challenge/public key
  • Image digests link to audited source code (Sigstore or reproducible builds)

Tools and Resources

Next Steps