Skip to main content
This guide shows how to verify that TLS certificates are controlled by a TEE using cryptographic attestation. For application code verification, see your app’s attestation documentation. For security concepts, see Security Architecture.

Which Verification Guide to Follow?

The verification method depends on what the application deployer chose:
Domain TypeWhen to UseOperator
Gateway Domains (*.phala.network)Your app uses Phala’s gateway domainPhala Cloud
Custom DomainYour app uses a custom domain with dstack-ingressDeveloper
Both provide cryptographic proof of TEE control over TLS certificates. The difference is who operates the certificate infrastructure.

Gateway Domains

Gateway domains expose attestation endpoints that prove TEE control over TLS certificate private keys.

What You’ll Verify

The gateway TEE publishes cryptographic evidence at https://GATEWAY-DOMAIN/.dstack/ endpoints that proves:
  1. The TLS certificate private keys are generated and controlled by the gateway TEE
  2. TEE hardware backs all certificate operations
  3. Only the TEE-controlled Let’s Encrypt account can issue certificates for gateway domains
  4. All historical certificates are accounted for via TEE remote attestation

3-Step Verification

Example: We’ll use gateway.dstack-pha-prod7.phala.network - replace with your gateway domain.

Step 1: Get Gateway Evidence

The gateway publishes evidence through three endpoints:
# Get ACME certificate information
curl -s "https://gateway.dstack-pha-prod7.phala.network/.dstack/acme-info" | jq > acme-info.json

# Get gateway application information
curl -s "https://gateway.dstack-pha-prod7.phala.network/.dstack/app-info" | jq > app-info.json
Expected output from acme-info.json:
{
  "account_uri": "https://acme-v02.api.letsencrypt.org/acme/acct/2584877296",
  "account_quote": { tdx-quote-with-eventlogs },
  "hist_keys": ["<base64-pubkey-1>"],
  "quoted_hist_keys": [
    {"public_key": "<base64>", "quote": { tdx-quote-with-eventlogs } },
    {"public_key": "<base64>", "quote": { tdx-quote-with-eventlogs } }
  ],
  "active_cert": "-----BEGIN CERTIFICATE-----\n...",
  "base_domain": "dstack-pha-prod7.phala.network"
}
What this means: The gateway provides its Let’s Encrypt account, all certificate public keys (current and historical), and TDX quotes proving the TEE generated these keys.

Step 2: Verify Certificate Matches TDX Quotes

Check that the served certificate matches the TEE-quoted keys:
# Get the public key from the served certificate
openssl s_client -connect gateway.dstack-pha-prod7.phala.network:443 </dev/null 2>/dev/null | \
  openssl x509 -pubkey -noout > served-pubkey.pem

# Get the public key from the evidence certificate
cat acme-info.json | jq -r '.active_cert' | \
  openssl x509 -pubkey -noout > evidence-pubkey.pem

# Compare the public keys
diff served-pubkey.pem evidence-pubkey.pem
Expected: No output (files are identical). Now verify the TEE hardware signed the certificate public key:
# Step 1: Prepare the public key for hashing
cat acme-info.json | jq -r '.quoted_hist_keys[0].public_key' | xxd -r -p > pubkey.bin
(echo -n "zt-cert:" && cat pubkey.bin) | sha512sum
# Output: 68f04e2b77f9ca8324d49ce916a5a7ebc6a715027d68fba4e7d3fb60f0f841a8  -

# Step 2: Search for this hash in the TDX quote's report_data
jq -r '.quoted_hist_keys[0].quote | fromjson | .report_data' acme-info.json | \
  grep -o "68f04e2b77f9ca8324d49ce916a5a7ebc6a715027d68fba4e7d3fb60f0f841a8"
# Output: 68f04e2b77f9ca8324d49ce916a5a7ebc6a715027d68fba4e7d3fb60f0f841a8
What this means: If you see the same hash in both outputs, Intel TDX hardware has cryptographically signed this public key. Copy the hash from step 1 and search for it in step 2 - they must match exactly. Please follow the Attestation Verification tutorial to further verify the remote attestation of the gateway. It ensures the implementation and the execution environment of the gateway is secure.

Step 3: Check Domain Protection and Certificate History

Verify only this TEE can issue certificates:
# Check ALL certificates ever issued for the gateway domain
curl -s "https://crt.sh/?q=%.dstack-pha-prod7.phala.network&output=json" | \
  jq -r '.[] | "\(.not_before) to \(.not_after): \(.issuer_name)"' | head -10

# Get the TEE's ACME account
cat acme-info.json | jq -r '.account_uri'
# Output: https://acme-v02.api.letsencrypt.org/acme/acct/2584877296

# Check DNS CAA records for the base domain
dig +short CAA dstack-pha-prod7.phala.network
Example output:
# Certificate history
2025-01-15T08:23:45 to 2025-04-15T08:23:44: C=US, O=Let's Encrypt, CN=E5
2024-12-01T10:15:22 to 2025-03-01T10:15:21: C=US, O=Let's Encrypt, CN=R3

# ACME account
https://acme-v02.api.letsencrypt.org/acme/acct/2584877296

# CAA record
0 issue "letsencrypt.org;validationmethods=dns-01;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/2584877296"
Verify the following:
  • All certificates are from Let’s Encrypt (no other CAs)
  • All the CAA record matches the account_uri from the TEE
  • Historical keys in hist_keys correspond to certificates in CT logs

Gateway Evidence Endpoints Reference

EndpointContainsProves
/.dstack/indexList of available endpointsGateway discovery
/.dstack/app-infoApp ID, instance ID, device ID, TCB infoTEE application identity
/.dstack/acme-infoACME account, certificate keys, TDX quotesCertificate control and history
The acme-info response structure:
FieldTypePurpose
account_uriStringLet’s Encrypt account URI
account_quoteStringTDX quote proving TEE controls the ACME account
hist_keysArrayAll certificate public keys (current + historical)
quoted_hist_keysArrayPublic keys with TDX quotes proving TEE generated them
active_certStringCurrently active certificate (PEM format)
base_domainStringBase domain for gateway (e.g., dstack-pha-prod7.phala.network)

Troubleshooting

“Connection refused to /.dstack/ endpoints”: These endpoints are only accessible via the gateway subdomain: gateway.dstack-pha-prod7.phala.network “Certificate doesn’t match quoted keys”: The gateway may have recently renewed certificates. Re-fetch the evidence with a fresh acme-info request. “Multiple ACME accounts in history”: Gateway upgrades or migrations may result in new ACME accounts. Check that the CAA record matches the current account_uri, and verify old certificates have expired.

What You’ve Verified

When all checks pass, you’ve cryptographically proven:
  • Zero-trust TLS: The gateway TEE controls certificate private keys, verifiable via TDX attestation
  • Phala-operated infrastructure: Phala Network operates the gateway TEE and maintains CAA records
  • Certificate transparency: All certificates are publicly auditable via CT logs
Key difference from custom domains: With gateway domains, Phala operates the certificate infrastructure. With custom domains, the application deployer operates it. Both provide cryptographic proof of TEE control.

Custom Domains

Custom domains use dstack-ingress running in your application’s TEE to manage certificates. The application deployer operates the certificate infrastructure.

What You’ll Verify

Your TEE publishes cryptographic evidence at https://your-domain.com/evidences/ that proves:
  1. The TLS certificate is controlled exclusively by this TEE
  2. Intel TDX hardware backs the attestation
  3. No one else can issue certificates for this domain
Live example: custom-domain-demo.phala.systems/evidences/

Prerequisites

Before starting, you’ll need:
  • Your domain name (e.g., your-domain.com)
  • curl, jq, openssl, and dig commands installed
  • 3 minutes to complete all verifications

Simple 3-Step Verification

Example: We’ll use custom-domain-demo.phala.systems - just replace with your domain.

Step 1: Get the Evidence Files

Your TEE publishes 4 files that prove it controls the certificate:
# Download the evidence files (4 files total)
curl -sO https://your-domain.com/evidences/quote.json
curl -sO https://your-domain.com/evidences/sha256sum.txt
curl -sO https://your-domain.com/evidences/cert.pem
curl -sO https://your-domain.com/evidences/acme-account.json

# Verify the files haven't been tampered with
sha256sum -c sha256sum.txt
Expected output:
acme-account.json: OK
cert.pem: OK
This means the files are authentic.

Step 2: Verify the Certificate Matches

Check that the evidence matches what’s actually being served:
# Compare the served certificate with the evidence certificate
echo | openssl s_client -connect your-domain.com:443 2>/dev/null | \
  openssl x509 -fingerprint -sha256 -noout
openssl x509 -in cert.pem -fingerprint -sha256 -noout
What to look for: Both fingerprints should be identical. For example:
SHA256 Fingerprint=62:89:C4:19:E6:F6:B6:50:11:F9:7D:E1:CF:FD:EE:5A:CE:2C:A3:38:24:F0:DE:5F:9E:2C:01:A3:72:D3:8B:DE
SHA256 Fingerprint=62:89:C4:19:E6:F6:B6:50:11:F9:7D:E1:CF:FD:EE:5A:CE:2C:A3:38:24:F0:DE:5F:9E:2C:01:A3:72:D3:8B:DE
Now verify the TEE hardware signed these files:
# Step 1: Get the hash of sha256sum.txt
sha256sum sha256sum.txt
# Output: 7cd338a801531e88a5af8478aa185cfc51baf7501319446ce2f94dade56a0988  sha256sum.txt

# Step 2: Search for this complete hash in the TDX quote
cat quote.json | jq -r '.quote' | grep -o "7cd338a801531e88a5af8478aa185cfc51baf7501319446ce2f94dade56a0988"
# Output: 7cd338a801531e88a5af8478aa185cfc51baf7501319446ce2f94dade56a0988
What this means: If you see the same 64-character hash in both outputs, Intel TDX hardware has cryptographically signed your evidence files. Copy the hash from step 1 and search for it in step 2 - they must match exactly.

Step 3: Check Domain Protection and Timeline

Make sure only this TEE can get certificates for your domain:
# Check ALL certificates ever issued for your domain
curl -s "https://crt.sh/?q=your-domain.com&output=json" | \
  jq -r '.[] | "\(.not_before) to \(.not_after): \(.issuer_name)"' | head -5

# Get the TEE's ACME account number
cat acme-account.json | jq -r '.uri'

# Check if your domain is locked to this account
dig +short CAA your-domain.com
Example output:
# Certificate history (note the dates!)
2025-08-04T06:10:56 to 2025-11-02T06:10:55: C=US, O=Let's Encrypt, CN=E5  ← Current
2025-08-04T06:00:18 to 2025-11-02T06:00:17: C=US, O=Let's Encrypt, CN=E5  ← TEE deployment

# ACME account
https://acme-v02.api.letsencrypt.org/acme/acct/2572685661

# CAA record (contains the same account number)
0 issue "letsencrypt.org;validationmethods=dns-01;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/2572685661"
Critical timeline check: If you see certificates issued BEFORE your TEE was deployed (before CAA was set), verify they’re either:
  • Already expired (check the “to” date)
  • Issued by the same ACME account (meaning they were from a previous TEE deployment)
If unexpired certificates exist from before CAA setup, they could potentially intercept traffic!

Understanding the Results

When all verifications pass, you’ve proven: Exclusive TEE Control: Only this specific TEE instance has the private key for your TLS certificate Hardware-Backed Security: Intel TDX hardware cryptographically signed the evidence, proving it’s a genuine TEE Domain Protection: CAA DNS records prevent anyone else (even with domain access) from issuing certificates Complete Chain of Trust: Every link from TDX hardware → evidence files → certificate → your connection is verified

Evidence Files Reference

The four evidence files at /evidences/ work together:
FileContainsProves
quote.jsonTDX attestation with embedded hashHardware generated the evidence
sha256sum.txtSHA256 of cert.pem + acme-account.jsonLinks certificate to ACME account
cert.pemCurrent TLS certificateWhat’s being served to clients
acme-account.jsonACME account URIWhich account controls certificates

Troubleshooting

“Files not found at /evidences/”: Ensure your domain uses custom domain setup with SET_CAA=true “Certificate doesn’t match”: The TEE may have renewed the certificate. Re-download evidence files. “Hash not in quote”: Verify you’re checking a production TEE deployment, not a test instance. “CAA record missing”: The domain owner needs to set CAA records pointing to the TEE’s ACME account.

Going Deeper: Application Trust

While you’ve verified the network connection is secure, the TLS private key is only as trustworthy as the code handling it. The key management software (dstack-ingress) runs inside the TEE and is open-source auditable code. To verify the exact code handling your keys matches what’s deployed, check the compose-hash in the TEE’s event log against the published source code. This ensures no malicious code can access or leak your private keys. Learn more: Application Attestation Documentation

Complete Security Checklist

Network Security (covered above):
  • ✅ Evidence files verify TEE control
  • ✅ TDX attestation proves hardware backing
  • ✅ CAA records prevent unauthorized certificates
  • ✅ Certificate timeline shows no pre-deployment threats
Advanced Verification (for maximum security):
  • ⚠️ Application code trust - Verify compose-hash matches source code
  • ⚠️ Certificate timeline - Ensure no valid pre-CAA certificates exist
  • ⚠️ Continuous monitoring - Watch CT logs for unexpected certificates
For detailed application verification: Attestation Guide

Next Steps

Monitoring

Set up alerts on crt.sh to detect unexpected certificate issuance:
  • Gateway domains: %.dstack-pha-prod7.phala.network
  • Custom domains: your-domain.com

Development

  • Gateway verification: Integrate attestation checks into your security monitoring workflow
  • Custom domains: Enable CAA records with SET_CAA=true in your deployment configuration

Security Auditing

Review the source code to understand certificate management: