Skip to the content.

RFC 0020: Machine-Verifiable Attestation Evidence for App Consumers

Summary

Make a hosted app’s attestation evidence verifiable by the app’s consumers — agents, counterparty services, contracts — not by a human looking at a badge. Serve a stable evidence bundle per app and ship a verify() library that returns structured facts: a verified binding from a live endpoint to the exact source tree running in a TEE. The library renders no verdict and shows no green/red; the accept-or-reject policy lives entirely in the consumer.

Problem

Attested promotion already binds a project’s source-tree hash and exposes a verification endpoint (RFC 0015, proxy/ingress.py), and verify.md renders a human “is it verified” view. Three things are wrong with stopping there:

The four layers to assemble:

  1. Channel — reached over an attested gateway (the gateway’s zt-cert quote, pinned).
  2. Platform — the webhost app_id’s on-chain DstackApp allowlist on base-prod.
  3. Per-app binding — the source-tree hash, vouched by the daemon’s promotion quote.
  4. Source — inspectable at that hash.

Note on (3): in a shared instance the TDX quote attests the webhost daemon, not each app. The daemon vouches “project P = tree_hash H.” A consumer’s trust is therefore hardware quote → approved webhost code → its signed binding → source. A consumer that needs a hardware quote of the app itself needs a per-app CVM — a deployment-granularity choice (RFC 0019), out of scope here.

Files to Modify

Implementation

  1. Evidence bundle, served at the verification endpoint: { schema_version, platform_quote, webhost_app_id, onchain { chain_id, kms_contract, dstackapp, allowed_compose_hash, allowed_os_image }, gateway { domain, app_id, zt_cert_ref }, app { project, source { repo, ref, commit_sha, tree_hash }, image_digest, binding_quote } }. Every value is evidence plus a repro pointer (the on-chain call, the quote field) so the consumer re-checks it against Base / Phala / Intel PCS directly. Authority comes from the evidence, not from us — the same posture as the AttestMesh indexer’s repro stubs.
  2. verify(endpoint, opts) -> Facts. Fetches the bundle; verifies the TDX quote (DCAP/QVL + collateral); confirms the gateway zt-cert binding; confirms webhost_app_id is on-chain-approved on base-prod; confirms the project’s tree_hash is bound by the daemon’s quote. Returns { quote_valid, kms_root, webhost_app_id, onchain_approved, gateway_attested, source: { repo, commit_sha, tree_hash }, errors[] }. No accept/reject.
  3. Policy lives in the consumer. Ship two reference policies as examples, not as the library’s job: (a) allowlist match on source.tree_hash; (b) a minimum of quote_valid && onchain_approved && gateway_attested && chain_id == <base-prod>. Document the third, strongest consumer step the binding makes possible: pull the source at tree_hash and reason about it — an agent inspecting the exact code it is about to trust.
  4. Channel prerequisite. verify() either performs the gateway zt-cert verification itself or consumes a pinned base-prod gateway reference (app_id + compose_hash) so layer (1) is checked, not assumed.
  5. verify.md becomes a Facts renderer for developers, behind the “not an end-user trust mechanism” banner.

Testing & Validation Requirements

Report Requirements

Out of Scope