Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.teesql.com/llms.txt

Use this file to discover all available pages before exploring further.

Verification is what turns “the database claims to be in a TEE” into a cryptographic fact. Skipping it gives you transport security without any guarantee that the program on the other end is the program you expect — defeating the point of running TeeSQL.

Why verify

The server’s RA-TLS certificate is self-signed and carries a TDX quote in a custom extension. A client that trusts the chain like a normal TLS endpoint trusts no one in particular — it just sees a cert that doesn’t chain to a public CA. Verification means extracting the quote, checking it against Intel’s signing chain, and confirming the measurements match what you expect. Only then is the connection meaningfully attested. Two failure modes that verification catches and standard TLS does not:
  • The CVM image was rotated to one you have not approved (caught by allowedMrTd pinning)
  • The CVM is running with TDX debug attributes set, which means the host can inspect its memory (caught by the debug-mode check)

Prerequisites

Methods

There are three places verification can happen, all using the same primitives. The RA-TLS client libraries fold extraction and verification into the connection path. This is the default and the recommended pattern: every new connection re-checks the quote (with caching), and a verification failure surfaces as a connection error before any SQL runs.
from psycopg_ratls import connect
from ra_tls_verify import IntelApiVerifier, VerifyOptions
import os

verifier = IntelApiVerifier(api_key=os.environ["INTEL_TRUST_AUTHORITY_API_KEY"])
options = VerifyOptions(allowed_mr_td=[os.environ["EXPECTED_MRTD"]])

conn = connect(os.environ["DATABASE_URL"], verifier=verifier, options=options)

Method 2 — verify standalone with SDK primitives

For pre-flight checks, monitoring, or any path that wants to verify without opening a database connection, the same libraries expose the underlying primitives directly.
from ra_tls_verify import extract_tdx_quote, IntelApiVerifier, VerifyOptions
import asyncio, ssl, socket

# Fetch the leaf cert from the cluster's :5433 endpoint
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with socket.create_connection(("your-cluster.teesql.com", 5433)) as s:
    with ctx.wrap_socket(s, server_hostname="your-cluster.teesql.com") as ts:
        der_cert = ts.getpeercert(binary_form=True)

quote = extract_tdx_quote(der_cert)
if quote is None:
    raise RuntimeError("not an RA-TLS certificate")

verifier = IntelApiVerifier(api_key="your-ita-key")
result = asyncio.run(verifier.verify(quote, VerifyOptions(allowed_mr_td=["abc..."])))
print("MRTD:", result.mr_td, "TCB:", result.tcb_status)
The Python extract_tdx_quote and TypeScript extractTdxQuote are the same primitive: they walk the X.509 extensions looking for OID 1.3.6.1.4.1.62397.1.8 (the current SCALE-encoded attestation), with a fallback to OID 1.3.6.1.4.1.62397.1.1 (legacy raw quote), and return the raw quote bytes (or null for a non-RA-TLS cert).

Method 3 — manual inspection via /attestation

The sidecar exposes a JSON attestation report at GET /attestation on port 8080. Useful for dashboards, ad-hoc operator checks, or any monitoring tool that just wants the report without verifying the quote itself.
curl -s http://your-cluster.teesql.com:8080/attestation | jq .
The response includes the raw quote (hex), tcb_info (MRTD plus RTMR0–3), postgres_state (current WAL LSN, pg_controldata hash, server version), app_id, compose_hash, and a unix timestamp. To turn this into a trust decision, feed the quote field into a verifier:
import requests, asyncio
from ra_tls_verify import IntelApiVerifier

report = requests.get("http://your-cluster.teesql.com:8080/attestation").json()
quote = bytes.fromhex(report["quote"])

verifier = IntelApiVerifier(api_key="your-ita-key")
result = asyncio.run(verifier.verify(quote))
assert result.tcb_status in {"OK", "SWHardeningNeeded"}
The /attestation endpoint binds REPORTDATA = SHA-256(wal_lsn || controldata_hash || timestamp), so the quote also commits to the database’s live state at the moment the report was produced — useful when you want to cryptographically pin “what was Postgres doing when you said this.”

Verify-before-connect pattern

For most applications, Method 1 is what you want — the connection path verifies on every handshake, with a 1-hour cache, so there’s no separate “verify” step to forget. Use the explicit verify-before-connect form only when you need to abort early in a code path before any database work is set up:

What to do when verification fails

The client libraries treat any verification failure as a hard refusal: no bytes are forwarded to the underlying Postgres driver, the connection error surfaces upward, and the transient verification cache is not populated. The same set of failure modes appears across all three libraries:
FailureWhat it meansAction
Quote extension missingServer is not a TeeSQL RA-TLS endpoint, or you hit the simulator without allowSimulatorConfirm host and port; for dev set allowSimulator: true
Intel Trust Authority returned non-2xxAPI key invalid, network filtering, or transient ITA outageCheck INTEL_TRUST_AUTHORITY_API_KEY, retry, or switch to local DCAP
tdx_is_debuggable: trueThe CVM was launched with TDX debug attributes — no confidentialityOperator must redeploy without debug; never set allowDebugMode: true in production
TCB status not acceptablePlatform firmware/microcode is out of dateEscalate to the cluster operator; do not bypass
MRTD not in allowedMrTdThe CVM image is not the one you approvedVerify the operator’s release notes; if intentional, update your allowlist
For exact error strings and additional codes, see Connection troubleshooting.

Where to get expected measurements

  • MRTD is published by the cluster operator when they cut a CVM image. Pin it via allowedMrTd (TS) / allowed_mr_td (Python) / allowed_mrtds (Rust).
  • RTMR0–3 are returned in every attestation result for inspection but are not pinned by default. Pin them in custom verifier code if you want to lock to a specific kernel + boot configuration.
  • compose_hash is exposed via /attestation and pins the application layer.
Last modified on May 1, 2026