feat(guest): built-in launch token requirement#763
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a first-class, guest-enforced “launch token” requirement to bind a deployment to a deployer-known secret, moving the LAUNCH_TOKEN pattern from an application-layer convention into the dstack OS boot policy and corresponding SDK/type surfaces.
Changes:
- Introduces
requirements.launch_token_hash(SHA-256 hex) across Rust types and Go/JS/Python compose-hash SDKs. - Enforces the requirement in the guest during system setup by parsing
user_config(dstack.launch_token) only when the requirement is present and failing closed on any mismatch/error. - Updates security documentation to describe the native launch-token behavior and fail-closed expectations.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| vmm/ui/src/composables/useVmManager.ts | Adds the field to UI-side Requirements typing (but see review comment about schema alignment). |
| sdk/python/src/dstack_sdk/get_compose_hash.py | Adds launch_token_hash to Python compose-hash requirements serialization. |
| sdk/js/src/get-compose-hash.ts | Adds launch_token_hash to JS compose-hash requirements typing. |
| sdk/go/dstack/compose_hash.go | Adds launch_token_hash to Go compose-hash requirements struct. |
| dstack-util/src/system_setup.rs | Implements guest-side verification of requirements.launch_token_hash against user_config.dstack.launch_token, plus unit tests. |
| dstack-types/src/lib.rs | Extends Requirements with launch_token_hash and updates deserialization tests. |
| docs/security/security-best-practices.md | Documents native LAUNCH_TOKEN support via requirements.launch_token_hash. |
| docs/security/cvm-boundaries.md | Clarifies .user-config handling when requirements.launch_token_hash is set. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
6c44489 to
7fca012
Compare
7fca012 to
40ef184
Compare
Add requirements.launch_token_hash so the previously app-layer
LAUNCH_TOKEN pattern is enforced by the OS image itself. When the
field is set, the guest reads the launch token from user_config at
JSON path dstack.launch_token during boot policy verification, before
any keys are provisioned, and fails closed unless its digest matches
the compose-hash-measured value. The digest is domain-separated --
sha256("dstack-launch-token/v1:" || token) -- to stay distinct from
the legacy plain-sha256 convention and generic precomputed tables,
and tokens shorter than 32 bytes are rejected since the hash is
public. When the field is absent, user_config is not parsed and stays
opaque application data.
40ef184 to
e794f2b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #762.
Problem
The LAUNCH_TOKEN pattern is currently a pure app-layer convention (prelaunch script +
APP_LAUNCH_TOKENenv). Every app has to wire the check itself, and there is no OS-enforced way to bind a deployment to a deployer-known secret.Fix
requirements.launch_token_hash: Option<String>to dstack types and SDK compose-hash types.launch_token_hashis present, the guest reads the token fromuser_configat JSON pathdstack/launch_tokenduring boot policy verification — before any keys are provisioned — and fails closed unless its digest matches the compose-hash-measured value.sha256("dstack-launch-token/v1:" || token)(canonical helperdstack_types::launch_token_hash, shell:printf 'dstack-launch-token/v1:%s' "$TOKEN" | sha256sum). This keeps it distinct from the legacy plain-sha256(token)top-level convention and from generic precomputed tables.launch_token_hashis absent,user_configis not parsed at all — it stays opaque application data.launch_token_hashis public (part of app-compose.json), guessable tokens can be recovered offline; guests reject tokens shorter than 32 bytes as a fail-closed guardrail, and docs tell deployers to generate random tokens.user_configin plaintext, so a host that has hosted the deployment learns it; mitigations are fresh token per deployment + pruning stale compose hashes from the on-chain whitelist, or the app-layer encrypted-env pattern when the token must stay secret from the host.user_config, non-JSON content, missing or non-stringdstack.launch_token, too-short token, malformed hash, or token mismatch all abort boot before key provisioning;requirementsstill demands string manifest_version"3", andRequirementsusesdeny_unknown_fields, so older guests reject a compose that carries this field instead of silently ignoring it.launch_token_hashapp-layer convention (UI/vmm-cli +APP_LAUNCH_TOKENenv + prelaunch script) is unchanged and keeps working on all images; migrating tooling to the new requirement is a follow-up.Verification
cargo fmt --check --allcargo clippy -p dstack-types -p dstack-util -- -D warnings --allow unused_variablescargo check --all-featurescargo test -p dstack-types/cargo test -p dstack-utilcd sdk/go && go build ./... && go vet ./...cd sdk/js && npx tsc --noEmitprek run --files <changed files>