Skip to the content.

RFC 0017: State Durability and Recovery Across CVM Lifecycle

Summary

Make the deployed fleet and its private per-app state survive a CVM being recreated: externalize the daemon’s volumes, provide an exportable manifest set, and a pinned restore that redeploys each app at its recorded commit/digest — refusing (not silently re-cloning) if the pin no longer matches.

Problem

Recovery today is shallow. recover_all() in proxy/runtimes.py restarts runtimes and per-project containers from whatever is on disk under /var/lib/tee-daemon. That disk persists only if DAEMON_VOLUME_NAME / the data volume point at external named volumes; the default daemon_data volume in docker-compose.yaml is local to the CVM. Concretely, on a fresh CVM:

So a CVM swap risks losing apps and private state, and there is no deterministic way to bring a fleet back at known-good versions. This is the “doesn’t feel durable / won’t survive the CVM changing” concern, and it is accurate.

Files to Modify

Implementation

  1. GET /_api/export (authed): a JSON bundle of every project’s manifest with its pins — source, ref, commit_sha, tree_hash, image, image_digest, runtime, entry, port, mode, volumes — plus audit-log references. Raw env secrets are excluded by default; secret continuity is the credential broker’s job (RFC 0018), not a plaintext dump.
  2. POST /_api/import (or a CLI): for each manifest, redeploy pinned. Git projects clone at commit_sha and deploy.py recomputes tree_hash; if it does not match the recorded pin, error and skip — no fallback to re-clone-latest, no masking. Image projects pull by image_digest. This reconstitutes the fleet on a fresh CVM deterministically.
  3. Operational requirement, documented: daemon_data, the audit dir, the tunnel dir, and the per-project data volume must be external named volumes that outlive any single CVM (ties to the DAEMON_VOLUME_NAME / data-volume wiring already in runtimes.py). Update the docker-compose.yaml comments so this is not a silent footgun.
  4. Boot bootstrap: in recover_all(), if the project registry is empty but an import bundle is present, restore from it before starting runtimes — so a fresh CVM with the export attached comes up as the previous fleet.
  5. Sealed dataDir snapshot (stretch, separate follow-up): an export of /daemon-data/<name> encrypted under the dstack-derived key, restorable only inside the same app identity. The manifest export + external volumes is the MVP; the sealed-snapshot crypto is its own RFC and should not block this one.

Testing & Validation Requirements

Report Requirements

Out of Scope