Where this fits in the trust chain
Morpheus TEE trust is a two-hop chain:-tee binary that implements Phase 2 faithfully. This is why a v6.0.0+ consumer paired with a v7.0.0+ provider gains all Phase 2 guarantees without any client-side change.
Overview
The Phase 2 backend verification system provides cryptographic proof that AI inference requests forwarded by this P-Node are processed inside authentic, unmodified SecretVM instances running on genuine Intel TDX (or AMD SEV-SNP — see SEV verification) and NVIDIA GPU hardware. It operates in two modes:- Full attestation (
AttestBackend) runs at startup and whenever the fast verify detects a backend change. It performs end-to-end cryptographic verification of the backend’s CPU TEE quote, GPU attestation evidence, workload integrity, and TLS binding. - Fast verification (
FastVerifyBackend) runs on every inference prompt. It always re-fetches the CPU quote and compares its hash and TLS binding against the cached attestation snapshot. If the quote changes, it triggers full re-attestation. There is no TTL — the fast check runs on every request unconditionally.
Security guarantees
The verification system proves the following properties:- Hardware authenticity — The backend runs inside a genuine Intel TDX confidential VM with a hardware-signed attestation quote that chains back to Intel’s root of trust.
- Firmware and OS integrity — The measured boot chain (MRTD, RTMR0-2) matches entries in the SecretVM artifact registry, confirming the VM runs an authentic, unmodified SecretVM image.
- Workload integrity — RTMR3 is recalculated from the backend’s
docker-compose.yamland root filesystem data, proving the exact workload configuration running inside the TEE has not been altered. - Model identity — The
docker-compose.yamldeclares which AI models are loaded. Because RTMR3 covers this file byte-for-byte, swapping, adding, or removing any model changes the hash and fails verification. - TLS channel binding — A digest of the TLS certificate is embedded in the CPU quote’s
reportData, binding the attested environment to a specific TLS identity. Current SecretVMs bind SHA-256 of the certificate’s SPKI (public key); legacy VMs bind SHA-256 of the full certificate. Either is accepted so a mixed fleet keeps verifying. This prevents man-in-the-middle attacks: inference data can only reach the attested endpoint. - CPU-GPU binding — The GPU attestation nonce is embedded in the second half of the CPU
reportData, cryptographically linking the GPU evidence to the same CPU TEE quote and preventing GPU attestation replay. - GPU hardware authenticity — GPU attestation evidence is independently verified by NVIDIA’s Remote Attestation Service (NRAS), confirming the GPU hardware is genuine and uncompromised.
- Continuous verification — Every inference prompt triggers a fast re-check of the backend’s identity. TLS binding or quote changes between attestations are detected immediately.
Verification checks
Full attestation flow
TheAttestBackend method orchestrates the complete verification sequence at startup and whenever the fast verify detects a change in the backend’s CPU quote.
Step by step
- Fetch CPU quote — The proxy-router requests the raw TDX attestation quote from the backend’s attestation port (
:21434/cpu). - Verify CPU quote — The quote is sent to the SecretAI Portal’s
quote-parseAPI, which performs cryptographic verification against Intel’s root of trust. - TLS binding — The proxy-router computes both the SHA-256 of the connection certificate’s SPKI (current binding) and of the full certificate DER (legacy binding) and compares the first 32 bytes of the quote’s
reportDataagainst either. A match proves the TLS endpoint is inside the attested TEE. - Workload verification (if artifact registry is loaded):
- Fetch
docker-compose.yamlfrom the backend. Old attest-rest serves it wrapped in an HTML<pre>block, newer attest-rest serves the raw bytes; both the raw response and the HTML-extracted content are tried against the measurement. - Parse the TDX quote to extract measurement registers (MRTD, RTMR0-3).
- Look up MRTD + RTMR0-2 in the artifact registry to confirm this is a recognized SecretVM build.
- Recalculate the expected RTMR3 from
SHA-256(docker-compose.yaml)combined withrootfs_datausing a SHA-384 extend chain. - Compare the calculated RTMR3 against the quote’s RTMR3 to prove workload integrity.
- Fetch
- Fetch GPU attestation — Retrieve GPU attestation data (JSON containing nonce, architecture, and evidence list) from
:21434/gpu. - CPU-GPU binding — Verify that the second 32 bytes of the CPU
reportDatamatch the GPU nonce, proving both attestations originate from the same machine and session. - NRAS verification — Submit GPU evidence to NVIDIA’s Remote Attestation Service (
/v4/attest/gpu) for independent hardware validation. NRAS returns a signed JWT (Entity Attestation Token); the proxy-router requires the request to succeed, thex-nvidia-overall-att-resultclaim to be true, and the attestedeat_nonceto equal the CPU-bound nonce. NRAS failure is fatal by design: attestation fails and the model’s snapshot is stored asfailed. The attesting node therefore needs network access tonras.attestation.nvidia.com(NVIDIA’s edge blocks some IPs/regions with a 403). - Cache snapshot — Store the attestation result (quote hash, TLS binding, workload status) for use by the fast verification path. The cache has no TTL — it remains valid as long as the backend’s CPU quote and TLS certificate haven’t changed.
Trust chain
The TDX trust chain starts at the hardware and extends through each layer of the boot process up to the running workload.Measurement registers
reportData layout
Per-prompt fast verify
Every inference prompt for a TEE-marked model triggersFastVerifyBackend before the request is forwarded. This keeps verification latency low (approximately 50 ms) while maintaining continuous assurance.
Fast verify logic
There is no TTL or cache expiry. The fast verify runs the same check on every prompt unconditionally:- Cache check — If no cached attestation exists (model was never attested), reject the request.
- Status check — If the cached status is
failed, run a full re-attestation before proceeding. - Re-fetch CPU quote — Always request the current CPU quote from
:21434/cpu(~50 ms TLS handshake). - Hash comparison — Compute
SHA-256of the fetched quote and compare against the cached hash. A mismatch indicates the backend has changed (restart, redeployment) and triggers full re-attestation. - TLS binding comparison — Verify the current connection’s certificate digest matches the cached value. If the full-certificate digest differs but the SPKI digest matches (certificate renewed with the same TEE-resident key), full re-attestation runs instead. If neither matches, it is treated as a critical error (possible MITM attack) and the request is rejected immediately.
- Pass — If both checks succeed, the inference request proceeds to the backend.
Model identity guarantee
Thedocker-compose.yaml inside a SecretVM backend declares the AI models to be loaded via the MODELS environment variable:
SHA-256(docker-compose.yaml). This means:
- Changing any byte of
docker-compose.yaml— swappingqwen3:8bfor a different model, altering a port binding, modifying an environment variable — produces a different RTMR3 value. - The TDX hardware enforces that measurement registers reflect the actual loaded software. Values cannot be faked or overridden from within the VM.
- The proxy-router independently recalculates the expected RTMR3 and compares it against the hardware-reported value. Any discrepancy fails verification.
Configuration reference
Model tagging (on-chain)
TEE verification is enabled per-model on the blockchain, not in the localmodels-config.json. When a model is registered in the Diamond contract with the tee tag (case-insensitive) in its tags array, the proxy-router automatically:
- On the provider side: performs full
AttestBackendagainst that model’sapiUrlhost at startup, and runsFastVerifyBackendon every inference prompt (see Per-prompt fast verify). - On the consumer side: runs
VerifyProviderQuickagainst the provider’s P-Node at session open and on every prompt, refusing to forward inference if attestation fails.
internal/blockchainapi/model_tags.go:
isTee field is required (or accepted) in models-config.json — the models-config-schema.json does not declare one. The only model-config values that matter for TEE are modelId (which the proxy-router uses to look up the on-chain tags) and apiUrl (the backend LLM endpoint).
Backend attestation endpoint derivation
Attestation endpoints are derived from the model’sapiUrl host by probing GET /cpu on port 21434 first (host-net Caddy topology used by current SecretAI backends, where the evidence endpoints are proxied and attest-rest is loopback-only), then falling back to port 29343 (attest-rest bound directly, standard SecretVMs). If neither port answers, the primary :21434 URL is used so the subsequent attestation surfaces a clear error. Phase 1 P-Node host attestation remains fixed on 29343.
Example: a model registered on-chain with the tee tag and apiUrl set to https://backend.example.com:8080/v1 will (assuming :21434 answers the probe) be attested against:
https://backend.example.com:21434/cpu— TDX CPU quotehttps://backend.example.com:21434/gpu— GPU attestation evidencehttps://backend.example.com:21434/docker-compose— backend’s compose file for RTMR3 replay
Environment variables
Health endpoint
- Attestation state (verified, pending, failed)
- Timestamp of last successful attestation
- Workload verification result (
authentic_match,authentic_mismatch,not_authentic) - TLS binding kind that matched (
spkiorcertificate) - Error details if verification failed
Architecture
All TEE verification code resides inproxy-router/internal/attestation/:
Integration points
ProxyReceiver.SessionPrompt— CallsFastVerifyBackendbefore forwarding each inference request for TEE-marked models. This is the per-prompt hot path. The fast verify re-fetches the CPU quote from the resolved attestation port and compares its hash and TLS binding against the cached snapshot. If the backend has changed, full re-attestation runs before the request proceeds.
/cpu, /gpu, /docker-compose) and the inference API share the same TLS endpoint on :21434, whose certificate digest (SPKI or full cert) is embedded in reportData. On standard SecretVMs, attest-rest is bound directly on :29343 with a certificate generated inside the TEE, and the inference API uses a separate certificate whose integrity is covered by workload verification (Caddy and its TLS configuration are part of the attested docker-compose).
Security gaps
The current verification system has several gaps between what is cryptographically proven and what the consumer assumes when opening a session for a specific model.Gap 1: No binding between blockchain model name and models-config
The consumer creates a session using amodelName registered on the blockchain (e.g. qwen3:8b). The provider’s models-config.json maps model IDs to backend API URLs. However, the provider proxy-router does not enforce that the modelName in models-config matches the model name registered on-chain. A provider could register qwen3:8b on the blockchain but configure models-config to route requests to a completely different model.
Gap 2: No binding between models-config and docker-compose.yaml
The workload verification proves that the backend runs a specificdocker-compose.yaml (via RTMR3). However, it does not verify that the model declared in models-config is actually present in that docker-compose.yaml. A docker-compose might declare MODELS='gemma3:4b' while the provider’s models-config claims to serve qwen3:8b for that same backend URL. The RTMR3 check only proves the docker-compose hasn’t been tampered with — not that it contains the model the consumer expects.
Gap 3: Multi-model docker-compose ambiguity
A singledocker-compose.yaml typically loads multiple models (e.g. MODELS='deepseek-r1:70b gemma3:4b qwen3:8b'). The workload verification confirms the backend runs this exact set of models, but provides no guarantee about which model actually serves a given request. The backend’s internal routing (e.g. Ollama model selection) is not covered by attestation.
What is actually proven today
Possible directions
- Docker-compose model registry (off-chain) — maintain a curated registry of
(docker-compose hash → model list)mappings, similar to the existing TDX artifact registry. When workload verification succeeds, look up the verified compose hash to get the models it contains, then check the consumer’s requested model against that list. No compose parsing needed, but every compose change needs a registry update. - Parse docker-compose content — after fetching and verifying the compose, extract the
MODELSenvironment variable and compare against themodelNamefrommodels-config. No external registry, but fragile: different backends (Ollama, vLLM, TGI) declare models differently. - Backend model enumeration endpoint — use the LLM server’s
/modelsendpoint (served from within the TEE over the attested TLS channel) and compare the returned list against the consumer’s requested model. Simple and backend-agnostic, but the list is self-reported by the backend software rather than measured into attestation registers.