From 8c813fe1bdfcf86450e35d6a5ee628c38df3ab4d Mon Sep 17 00:00:00 2001 From: Adam Reif Date: Sat, 28 Feb 2026 00:48:31 -0600 Subject: [PATCH] docs(arch): add mermaid diagrams for DeRoT key issuance, VM upgrade, and trust verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three new architecture docs in architectureDocs/deployment/: - derot-key-issuance.md — sequence diagram of how a CVM obtains its keys from Onchain KMS (DstackApp + DstackKms contracts on Base) - vm-code-upgrade.md — flowchart of the full upgrade lifecycle: build, governance whitelist approval, deploy, and key issuance for the new code - trust-stack-verification.md — four-layer trust model (Application, Platform, Network, Governance) with both a layer overview diagram and a detailed verifier sequence diagram; includes the full VR-1–VR-4 checklist Co-Authored-By: Claude Sonnet 4.6 --- .../deployment/derot-key-issuance.md | 56 +++++++ .../deployment/trust-stack-verification.md | 156 ++++++++++++++++++ .../deployment/vm-code-upgrade.md | 86 ++++++++++ 3 files changed, 298 insertions(+) create mode 100644 architectureDocs/deployment/derot-key-issuance.md create mode 100644 architectureDocs/deployment/trust-stack-verification.md create mode 100644 architectureDocs/deployment/vm-code-upgrade.md diff --git a/architectureDocs/deployment/derot-key-issuance.md b/architectureDocs/deployment/derot-key-issuance.md new file mode 100644 index 00000000..1f44a0f1 --- /dev/null +++ b/architectureDocs/deployment/derot-key-issuance.md @@ -0,0 +1,56 @@ +# Key Issuance from DeRoT (Onchain KMS) + +How a CVM obtains its application keys from the Decentralized Root of Trust on Base. + +DeRoT uses an **Onchain KMS** where a DstackKms contract on Base holds the KMS root CA, and a +per-application DstackApp contract holds the compose-hash whitelist. The KMS will only issue keys +to CVMs whose attested code is whitelisted by the app owner. + +## Flow + +```mermaid +sequenceDiagram + autonumber + participant CVM as CVM
(TDX hardware) + participant KMS as Onchain KMS
(dstack-kms) + participant DstackApp as DstackApp contract
(Base) + participant DstackKms as DstackKms contract
(Base) + participant Intel as Intel DCAP
(quote verification) + + Note over CVM: Boot — OS + containers start.
dstack measures OS, compose, images into RTMRs. + + CVM->>KMS: Key request + TDX attestation quote
(contains RTMR3 with compose-hash) + KMS->>Intel: Verify TDX quote signature + Intel-->>KMS: Quote valid / TCB status + KMS->>DstackKms: Is OS image hash whitelisted? + DstackKms-->>KMS: Yes (MRTD/RTMR0-2 check passes) + KMS->>DstackApp: Is compose-hash whitelisted? + DstackApp-->>KMS: Yes (owner approved this version) + KMS-->>CVM: App keys (derived from KMS root secret) + + Note over CVM: CVM now holds its secrets.
TLS cert generated inside TEE.
Application starts serving requests. +``` + +## Components + +| Component | Role | +|-----------|------| +| **TDX hardware** | Hardware root of trust; measures everything into RTMR registers at boot | +| **dstack OS** | Hashes OS + kernel + boot params into MRTD/RTMR0-2; hashes docker-compose (with pinned image digests) into RTMR3 | +| **Onchain KMS** | dstack-kms service; verifies attestation quote; checks on-chain whitelists before issuing keys | +| **DstackKms (Base)** | Contract holding whitelisted OS image hashes and KMS aggregated MR | +| **DstackApp (Base)** | Per-application contract; owner controls which compose-hashes are authorized; `0x2f83172A49584C017F2B256F0FB2Dca14126Ba9C` | + +## Key Points + +- **No whitelist → no keys.** If the compose-hash is not in DstackApp, the KMS refuses key issuance, and the CVM cannot start serving. +- **Image digests must be pinned.** `docker-compose` must use `@sha256:` digests; mutable tags like `:latest` produce a non-deterministic compose-hash that won't match any whitelist entry. +- **Key derivation is deterministic.** The same attested code always gets the same derived keys, so data encrypted by one CVM instance is readable by a later instance running the same code. +- **Owner controls governance.** The DstackApp `owner` (wallet, multisig, timelock, or DAO) is the sole entity that can whitelist new compose-hashes. + +## References + +- [Phala: Cloud vs Onchain KMS](https://docs.phala.com/phala-cloud/key-management/cloud-vs-onchain-kms) +- [Phala: DeRoT design](https://docs.phala.com/dstack/design-documents/decentralized-root-of-trust) +- [requirements.md FR-2, FR-2.6](planning/requirements.md) +- [PLAN.md — Onchain KMS on Base](planning/PLAN.md) diff --git a/architectureDocs/deployment/trust-stack-verification.md b/architectureDocs/deployment/trust-stack-verification.md new file mode 100644 index 00000000..78bc0b47 --- /dev/null +++ b/architectureDocs/deployment/trust-stack-verification.md @@ -0,0 +1,156 @@ +# Platform Verification with dstack-verifier + +How to verify that a running CVM is executing on genuine Intel TDX hardware with a trusted OS — +before trusting it with API requests. + +[dstack-verifier](https://github.com/Dstack-TEE/dstack/tree/main/verifier) is the tool Phala +provides for this. It automates cryptographic validation of the TDX quote, OS image, and event log +integrity. Governance checks (compose-hash and OS-image whitelists on Base) are separate and +performed with `cast` against the DstackApp / DstackKms contracts. + +Reference: [Phala Cloud — Verify the Platform](https://docs.phala.com/phala-cloud/attestation/verify-the-platform) + +## What dstack-verifier Checks Automatically + +| Check | What it does | +|-------|-------------| +| **TDX quote signature** | Verifies the quote against Intel root certs via `dcap-qvl` | +| **TCB SVN** | Confirms the hardware is running the latest security patches; debug mode is off | +| **OS image hash** | Downloads the OS image from `download.dstack.org` and compares against the hash in the quote (MRTD/RTMR0-2) | +| **RTMR3 event log replay** | Replays the event log and asserts the result matches the quoted RTMR3 | +| **KMS identity** | Extracts the KMS ID from the `key-provider` event in RTMR3 | + +When all pass, `is_valid: true`. A real TDX deployment also produces `os_image_hash_verified: true` +(the simulator uses a synthetic OS image hash not published on `download.dstack.org`, so that field +is always `false` in local testing — see CI notes below). + +## What dstack-verifier Does NOT Check + +These require separate governance verification (see below): + +- Whether the **compose-hash is whitelisted** in the DstackApp contract +- Whether the **OS image hash is whitelisted** in the DstackKms contract +- Whether the **KMS aggregated MR** is whitelisted +- TLS certificate binding / CAA DNS records + +## Verification Workflow + +```mermaid +sequenceDiagram + autonumber + participant Op as Operator + participant CVM as CVM
(/attestation) + participant DV as dstack-verifier
(localhost:8080) + participant Intel as Intel DCAP + participant Phala as download.dstack.org + participant Base as Base chain
(DstackApp / DstackKms) + + Note over Op,CVM: Step 1 — Collect attestation data + + Op->>CVM: GET /attestation + CVM-->>Op: { quote, event_log, vm_config } + + Note over Op,DV: Step 2 — Platform verification (automated by dstack-verifier) + + Op->>DV: POST /verify { quote, event_log, vm_config, attestation: null } + DV->>Intel: Verify TDX quote signature (dcap-qvl) + Intel-->>DV: Quote valid; TCB SVN + DV->>Phala: Download OS image (os_image_hash from vm_config) + Phala-->>DV: OS image bytes + DV->>DV: Compute MRTD/RTMR0-2 from OS image;
replay event log → assert RTMR3 matches;
extract KMS ID from key-provider event + DV-->>Op: { is_valid, quote_verified, os_image_hash_verified,
details: { kms_id, ... } } + + Note over Op,Base: Step 3 — Governance verification (manual) + + Op->>Base: DstackApp.allowedComposeHashes(composeHash)? + Base-->>Op: true / false + Op->>Base: DstackKms.allowedOsImages(osImageHash)? + Base-->>Op: true / false +``` + +## Running dstack-verifier + +**Start as an HTTP server:** + +```bash +# Docker +docker run -p 8080:8080 dstacktee/dstack-verifier:latest + +# Or build from source (used in CI) +cargo build --manifest-path=path/to/dstack/verifier/Cargo.toml --bin dstack-verifier +./dstack-verifier -c dstack-verifier.toml +``` + +**Collect attestation data and verify:** + +```bash +# 1. Get attestation data from the CVM +curl https://api.litprotocol.com/attestation -o attestation.json + +# 2. Strip 0x prefix from quote, set attestation: null (required by dstack-verifier) +python3 -c ' +import sys, json +d = json.load(open("attestation.json")) +q = d["quote"] +q = q[2:] if q.startswith("0x") else q +print(json.dumps({"quote": q, "event_log": d["event_log"], "vm_config": d["vm_config"], "attestation": None})) +' > verify-request.json + +# 3. POST to dstack-verifier +curl -s -X POST localhost:8080/verify \ + -H 'Content-Type: application/json' \ + -d @verify-request.json | jq +``` + +**Expected output (real TDX hardware):** + +```json +{ + "is_valid": true, + "quote_verified": true, + "os_image_hash_verified": true, + "details": { + "kms_id": "...", + "kms_name": "phala-mainnet" + } +} +``` + +## Governance Verification + +After `is_valid: true`, verify the application and OS image are authorized on-chain: + +```bash +# Is the current compose-hash whitelisted in DstackApp? +cast call "allowedComposeHashes(bytes32)" --rpc-url + +# Is the OS image hash whitelisted in DstackKms? +cast call "allowedOsImages(bytes32)" --rpc-url +``` + +DstackApp address for this deployment: `0x2f83172A49584C017F2B256F0FB2Dca14126Ba9C` + +The compose-hash can be extracted from the RTMR3 event log (the `app-compose` event) or computed +locally: `sha384(docker-compose.yml contents)` with all image references pinned to `@sha256:` +digests. + +## CI Usage Notes + +The phala-simulator CI job (`phala-simulator.yml`) runs dstack-verifier in **oneshot mode** +(`--verify FILE`) against the dstack simulator. Because the simulator's `attestation.bin` contains +a synthetic OS image hash that has never been published on `download.dstack.org`, the result is: + +- `quote_verified: true` — the quote signature pipeline is correct ✓ +- `os_image_hash_verified: false` — expected; the hash is synthetic, not a real published image +- `is_valid: false` — expected; a real deployment against actual Phala Cloud returns `true` + +The CI assertion is `quote_verified=true`, which is the meaningful signal: it confirms that +`get_quote()` → dstack-verifier is wired end-to-end correctly. + +## References + +- [Phala Cloud: Verify the Platform](https://docs.phala.com/phala-cloud/attestation/verify-the-platform) +- [Phala Cloud: Chain of Trust](https://docs.phala.com/phala-cloud/attestation/chain-of-trust) +- [dstack-verifier source](https://github.com/Dstack-TEE/dstack/tree/main/verifier) +- [derot-key-issuance.md](derot-key-issuance.md) +- [phala-simulator.yml](../../.github/workflows/phala-simulator.yml) diff --git a/architectureDocs/deployment/vm-code-upgrade.md b/architectureDocs/deployment/vm-code-upgrade.md new file mode 100644 index 00000000..ad7f5b83 --- /dev/null +++ b/architectureDocs/deployment/vm-code-upgrade.md @@ -0,0 +1,86 @@ +# Upgrading VM Code + +How to safely roll out a new version of the CVM application — from code change through +on-chain governance approval to live traffic. + +Because the Onchain KMS only issues keys to whitelisted code, every upgrade requires two +independent actions: **a governance tx** (whitelist the new compose-hash) and **a deployment** (push +the new image and restart the CVM). Neither alone is sufficient. + +## Flow + +```mermaid +flowchart TB + subgraph Dev["Developer"] + A["Edit code"] --> B["Build & push Docker image\n(tagged with SHA256 digest)"] + B --> C["Compute new compose-hash\nfrom docker-compose + image digests"] + end + + subgraph Governance["On-chain Governance (Base)"] + D["Owner submits tx:\nDstackApp.addAllowedComposeHash(newHash)"] + E{Multisig / timelock\napproval?} + D --> E + E -->|approved| F["New compose-hash\nwhitelisted on-chain"] + E -->|rejected| X["Upgrade blocked"] + end + + subgraph CI["CI/CD (GitHub Actions / just deploy)"] + G["Substitute @sha256: digest\nin docker-compose"] --> H["phala deploy\n(or DeRoT deploy)"] + H --> I["New CVM starts with new image"] + end + + subgraph Boot["New CVM Boot"] + I --> J["dstack measures compose into RTMR3"] + J --> K["CVM requests keys from Onchain KMS"] + K --> L{compose-hash\nwhitelisted?} + L -->|yes| M["Keys issued\nCVM starts serving"] + L -->|no| N["Key request denied\nCVM cannot start"] + end + + C --> D + C --> G + F -.->|"whitelist in place\nbefore deploy"| L + + style X fill:#fee2e2,stroke:#dc2626 + style N fill:#fee2e2,stroke:#dc2626 + style M fill:#dcfce7,stroke:#16a34a +``` + +## Steps + +1. **Build & push** — CI builds the new Docker image and pushes it to the registry. The registry + returns a `@sha256:` digest for the image. + +2. **Compute compose-hash** — dstack hashes the full `docker-compose.yml` (with pinned image + digests) to produce the compose-hash that will appear in RTMR3 at boot. + +3. **Governance approval** — The DstackApp owner (wallet, multisig, or timelock) submits an + on-chain transaction to whitelist the new compose-hash. A timelock or multisig introduces a + mandatory delay, giving stakeholders time to review before the new code can receive keys. + +4. **Deploy** — After the whitelist tx is confirmed, CI redeploys (`phala deploy` or DeRoT + equivalent) with the new image digest substituted into docker-compose. + +5. **Boot & key issuance** — The new CVM boots, dstack measures the compose into RTMR3, and the + CVM requests keys from the Onchain KMS. The KMS verifies the attestation and checks the + DstackApp whitelist (step 3). If the compose-hash matches, keys are issued and the CVM starts. + +## Ordering Requirement + +The governance tx **must be confirmed on-chain before the new CVM boots** and requests keys. +If the CVM boots before the whitelist tx is finalized, the KMS will reject the key request and +the CVM cannot serve traffic. The diagram shows the whitelist flowing into the key issuance check +to reflect this dependency. + +## Rollback + +To roll back, redeploy the previous image (whose compose-hash is already whitelisted). No +governance action is required if the old compose-hash was not removed from the whitelist. +To block a rolled-back version, the owner removes its compose-hash from DstackApp. + +## References + +- [derot-key-issuance.md](derot-key-issuance.md) — how keys are issued after deploy +- [deployment.md](deployment.md) — deploy workflow (`just deploy`, GitHub Actions) +- [requirements.md DR-1, FR-2.6](planning/requirements.md) +- [Phala: Cloud vs Onchain KMS](https://docs.phala.com/phala-cloud/key-management/cloud-vs-onchain-kms)