Skip to content

guest: enforce app compose version policy#754

Open
kvinwang wants to merge 2 commits into
masterfrom
feat/manifest-version-os-policy
Open

guest: enforce app compose version policy#754
kvinwang wants to merge 2 commits into
masterfrom
feat/manifest-version-os-policy

Conversation

@kvinwang

@kvinwang kvinwang commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Parse app-compose.json manifest_version as a string, while keeping legacy numeric 1/2 compatibility and rejecting numeric 3+.
  • Add guest-side manifest gating:
    • reject manifests above guest MAX_SUPPORTED_MANIFEST_VERSION
    • reject any manifest that contains requirements unless manifest_version == "3"
  • Add requirements support:
    • requirements.os_version uses Rust semver requirement syntax, e.g. ">=0.6.0, <0.7.0"
    • requirements.platforms reuses existing AttestationMode deserialize values, e.g. "dstack-gcp-tdx"
    • omitted platforms means any platform; explicit platforms: [] means no platform is supported
  • Update dstack-vmm to emit string manifest version "3" for OS images >= 0.6.0, while preserving legacy numeric 2 for older images.
  • Update Rust/Go/JS/Python SDK and UI AppCompose types for manifest_version and requirements.

Example

{
  "manifest_version": "3",
  "requirements": {
    "os_version": ">=0.6.0",
    "platforms": ["dstack-gcp-tdx"]
  }
}

Validation

  • cargo fmt --all --check
  • git diff --check
  • cargo test -p dstack-types
  • cargo test -p dstack-util
  • cargo test -p dstack-attest compatibility_tests::attestation_mode_deserializes_canonical_names
  • cargo check -p dstack-vmm -p dstack-guest-agent
  • cargo clippy -- -D warnings -D clippy::expect_used -D clippy::unwrap_used --allow unused_variables
  • cargo test --all-features --no-run
  • cd vmm/ui && npm run build
  • cd sdk/go && go test ./dstack -run '^$'
  • python3 -m py_compile sdk/python/src/dstack_sdk/get_compose_hash.py sdk/python/src/dstack_sdk/__init__.py
  • GitHub CI: all checks passing

Copilot AI review requested due to automatic review settings July 7, 2026 10:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the AppCompose schema to treat manifest_version as a string (while still accepting legacy numeric 1/2 on input) and enforces version-based policies in the guest during system setup, including a new os_version_policy.min constraint.

Changes:

  • Canonicalize manifest_version parsing to a string with legacy numeric 1/2 compatibility and add manifest_version_u32() helper in dstack-types.
  • Enforce a guest-side maximum supported manifest version and enforce os_version_policy.min (before KMS/env/docker setup) in dstack-util system setup.
  • Update SDK/UI AppCompose types to support string manifest_version and the new os_version_policy field.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
vmm/ui/src/composables/useVmManager.ts Extends UI AppCompose typing for string/number manifest_version and optional os_version_policy.min.
sdk/python/src/dstack_sdk/get_compose_hash.py Extends Python AppCompose typing for string/number manifest_version and adds os_version_policy.
sdk/js/src/get-compose-hash.ts Extends JS SDK AppCompose typing for string/number manifest_version and adds os_version_policy.
sdk/go/dstack/compose_hash.go Updates Go SDK AppCompose to accept string/number manifest_version and adds os_version_policy support.
guest-agent/src/rpc_service.rs Updates guest-agent test AppCompose construction for new Rust AppCompose fields/types.
dstack-util/src/system_setup.rs Adds guest enforcement for max supported manifest version + os_version_policy.min, and validates early in system setup.
dstack-types/src/lib.rs Changes Rust AppCompose manifest_version to canonical string form with custom deserialization + adds os_version_policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread sdk/go/dstack/compose_hash.go Outdated
Comment on lines +35 to +41
// AppCompose represents the application composition structure
type AppCompose struct {
ManifestVersion *int `json:"manifest_version,omitempty"`
Name string `json:"name,omitempty"`
Features []string `json:"features,omitempty"` // Deprecated
Runner string `json:"runner"`
DockerComposeFile string `json:"docker_compose_file,omitempty"`
DockerConfig *DockerConfig `json:"docker_config,omitempty"`
PublicLogs *bool `json:"public_logs,omitempty"`
PublicSysinfo *bool `json:"public_sysinfo,omitempty"`
PublicTcbinfo *bool `json:"public_tcbinfo,omitempty"`
KmsEnabled *bool `json:"kms_enabled,omitempty"`
GatewayEnabled *bool `json:"gateway_enabled,omitempty"`
TproxyEnabled *bool `json:"tproxy_enabled,omitempty"` // For backward compatibility
LocalKeyProviderEnabled *bool `json:"local_key_provider_enabled,omitempty"`
KeyProvider KeyProviderKind `json:"key_provider,omitempty"`
KeyProviderID string `json:"key_provider_id,omitempty"` // hex string
AllowedEnvs []string `json:"allowed_envs,omitempty"`
NoInstanceID *bool `json:"no_instance_id,omitempty"`
SecureTime *bool `json:"secure_time,omitempty"`
BashScript string `json:"bash_script,omitempty"` // Legacy
PreLaunchScript string `json:"pre_launch_script,omitempty"` // Legacy
ManifestVersion interface{} `json:"manifest_version,omitempty"`
Name string `json:"name,omitempty"`
Features []string `json:"features,omitempty"` // Deprecated
Runner string `json:"runner"`
DockerComposeFile string `json:"docker_compose_file,omitempty"`
Comment on lines 110 to +123
// GetComposeHash computes the SHA256 hash of the application composition
func GetComposeHash(appCompose AppCompose, normalize ...bool) (string, error) {
shouldNormalize := len(normalize) > 0 && normalize[0]

if shouldNormalize {
appCompose = preprocessAppCompose(appCompose)
}

// Convert to generic map for sorting
jsonBytes, err := json.Marshal(appCompose)
if err != nil {
return "", err
}

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread sdk/go/dstack/compose_hash.go Outdated
Comment on lines +31 to +33
type OsVersionPolicy struct {
Min string `json:"min,omitempty"`
}
Comment on lines +765 to +775
fn verify_manifest_version(app_compose: &AppCompose) -> Result<u32> {
let manifest_version = app_compose
.manifest_version_u32()
.context("Invalid manifest_version")?;
if manifest_version > MAX_SUPPORTED_MANIFEST_VERSION {
bail!(
"Unsupported manifest_version: {manifest_version}, max supported: {MAX_SUPPORTED_MANIFEST_VERSION}"
);
}
Ok(manifest_version)
}
@kvinwang kvinwang force-pushed the feat/manifest-version-os-policy branch 6 times, most recently from 2eff4d4 to 14adc15 Compare July 8, 2026 02:02
@kvinwang kvinwang force-pushed the feat/manifest-version-os-policy branch from 14adc15 to fdf3309 Compare July 8, 2026 02:37
- Allow requirements on any manifest_version >= 3 instead of == 3, so a
  future v4 guest does not reject v4 manifests carrying requirements
- Reject non-canonical manifest_version strings like "03" and "+3"
- Rewrite unquote_os_release_value with single-pass escape handling
  (fixes backslash-before-quote mishandling; single quotes take no
  escapes, matching shell semantics)
- Add tests for the above
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants