Skip to content

Add event log v2 format with JSON canonical digest#646

Open
kvinwang wants to merge 14 commits into
masterfrom
feat/event-log-v2-canonical-json
Open

Add event log v2 format with JSON canonical digest#646
kvinwang wants to merge 14 commits into
masterfrom
feat/event-log-v2-canonical-json

Conversation

@kvinwang

@kvinwang kvinwang commented Apr 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add event_log_version field to app-compose.json (default 1, backward compatible)
  • Introduce v2 event log format using JCS (RFC 8785) canonical JSON as digest input
  • V1 and V2 share the same event_type 0x08000001; version is carried out-of-band via RuntimeEvent::version (scale/serde), not inside the hashed content
  • V2 digest: SHA384({"name":"<event-name>","payload":"<hex>","type":134217729}) (keys are JCS-sorted: name < payload < type)
  • V2 runtime events include their digest preimage (hex-encoded) by default for independent verification; V1 event output remains unchanged
  • GetQuote returns the merged binary CCEL event log by default (fail-open to an empty field if CCEL export is unavailable)
  • Expose the new version via vmm-cli.py compose --event-log-version and a dropdown in the VMM Web UI

This enables relying parties (e.g., Intel ITA) to define fine-grained trust policies on individual event claims (e.g., input.event.name == "compose-hash") instead of matching the full RTMR3 value, which varies across VM instances due to other measured events.

Enabling v2 for an app

Via vmm-cli

vmm-cli.py compose \
  --name my-app \
  --docker-compose docker-compose.yaml \
  --kms --gateway \
  --event-log-version 2 \
  --output app-compose.json

vmm-cli.py deploy --name my-app --image dstack-0.5.8 \
  --compose app-compose.json --vcpu 2 --memory 4G --disk 20G

Omitting --event-log-version keeps the field out of app-compose.json, so existing compose hashes for v1 apps are unchanged.

Via VMM Web UI

"Create VM" dialog → "Event log format" dropdown (under Networking). Defaults to V1; pick V2 to opt in.

Consuming v2 attestation

Once the CVM is booted, call GetQuote / Attest; V2 events include their canonical JSON pre-image by default:

# Inside the CVM (via /var/run/dstack/dstack.sock)
curl --unix-socket /var/run/dstack/dstack.sock \
  -X POST http://localhost/GetQuote?json \
  -d '{"report_data":"0000..."}'

Each V2 runtime event in the returned event_log carries a preimage field whose value is a hex-encoded string. Clients verify digests independently with:

sha384(hex_decode(event.preimage)) == event.digest

Wire format notes

  • v1 digest pre-image: binary concat event_type.to_le_bytes() || ":" || event_name || ":" || payload (little-endian is now pinned; previously to_ne_bytes(), identical on x86)
  • v2 digest pre-image: UTF-8 bytes of JCS canonical JSON {"name":"<event-name>","payload":"<hex>","type":134217729}. Field names are generic (no keyword collisions in Rego / CEL) and match common messaging/event-protocol conventions (CloudEvents, MQTT, JWT).
  • Every V2 runtime event returns its pre-image as a hex-encoded string so clients can independently verify sha384(hex_decode(preimage)) == event.digest; V1 output stays unchanged

Wire-format compatibility safety-net

Attestation::into_versioned() conditionally upgrades to V1 msgpack when any runtime event reports a non-V1 version (SCALE V0 skips the version field for legacy binary compat, which would otherwise drop v2 information on the wire). V1-only attestations keep the V0/SCALE encoding, so existing consumers are unaffected. from_bytes detects the encoding by leading byte, so the receiver side needs no changes.

Test plan

  • cargo check --all-features passes
  • cargo clippy passes (with -D warnings -D clippy::expect_used -D clippy::unwrap_used)
  • cargo fmt --check --all passes
  • cargo test -p cc-eventlog — 25 tests including v1 backward compat, v2 digest, canonical JSON escaping/sorting/idempotency, mixed v1/v2 replay, scale round-trip
  • cargo test -p dstack-attest — 8 tests including msgpack round-trip with new fields and V0/V1 dispatch coverage
  • vmm-cli.py compose --event-log-version 2 writes the field; omitting the flag keeps existing compose hashes stable
  • VMM Web UI TypeScript build is clean with the new dropdown
  • Deployed a CVM with event_log_version: 2; boot completes (done), KMS verifier replays RTMR3 without mismatch, and guest-reported digests match the new canonical JSON bytewise

Comment thread dstack-attest/src/attestation.rs Outdated
Comment thread cc-eventlog/src/runtime_events.rs Outdated
Comment thread dstack-util/src/main.rs Outdated

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

Adds a configurable “v2” runtime event digest format based on RFC 8785 (JCS) canonical JSON, and threads the chosen event-log version plus an RPC opt-in for returning per-event digest preimages (“hash inputs”) through guest-agent, simulator, and callers.

Changes:

  • Introduces EventLogVersion + AppCompose.event_log_version (default v1) and threads it through runtime event emission.
  • Implements v2 runtime event digest preimage as JCS-canonical JSON and adds optional hash_input exposure for relying parties.
  • Extends guest-agent RPC (RawQuoteArgs) with include_hash_inputs and updates downstream clients/simulator accordingly.

Reviewed changes

Copilot reviewed 21 out of 23 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
ra-tls/src/cert.rs Adapts to new quote_with_app_id signature by passing a default event log version.
kms/src/main_service/upgrade_authority.rs Updates RawQuoteArgs construction to include include_hash_inputs.
guest-agent/src/rpc_service.rs Threads include_hash_inputs into quote/attest paths and passes event_log_version into event emission.
guest-agent/src/backend.rs Extends backend trait to support include_hash_inputs and versioned runtime event emission.
guest-agent/rpc/proto/agent_rpc.proto Adds include_hash_inputs to RawQuoteArgs and documents behavior.
guest-agent-simulator/src/simulator.rs Adds include_hash_inputs support for simulated quote/attest responses, populating hash inputs when requested.
guest-agent-simulator/src/main.rs Updates simulator backend implementations/tests for new backend trait signatures.
guest-agent-simulator/Cargo.toml Adds dstack-types dependency for EventLogVersion.
gateway/src/gen_debug_key.rs Updates quote request construction for new RawQuoteArgs field.
gateway/src/distributed_certbot.rs Updates quote/attest request construction for new RawQuoteArgs field.
dstack-util/src/system_setup.rs Threads event_log_version from app-compose through multiple RTMR3 emit_runtime_event calls.
dstack-util/src/main.rs Reads event_log_version from host-shared app-compose.json for extend command emission.
dstack-types/src/lib.rs Defines EventLogVersion and adds event_log_version to AppCompose with defaulting.
dstack-attest/src/v1.rs Adds mutable access to TDX event log to populate per-event hash inputs on demand.
dstack-attest/src/lib.rs Updates emit_runtime_event API to accept an explicit event log version.
dstack-attest/src/attestation.rs Adds optional inclusion of per-event hash inputs in event-log JSON and supports event-log version in quote_with_app_id.
cc-eventlog/src/tdx.rs Extends TdxEvent with version + optional hash_input, plus helpers/tests.
cc-eventlog/src/tcg.rs Ensures new TdxEvent fields are initialized for converted TCG events.
cc-eventlog/src/runtime_events.rs Implements v2 canonical JSON digest input, versioned hashing, and extensive tests.
cc-eventlog/src/lib.rs Re-exports EventLogVersion and canonical JSON helper(s).
cc-eventlog/Cargo.toml Adds dstack-types + serde_jcs dependencies.
Cargo.lock Locks new dependencies (serde_jcs, ryu-js, etc.).
.gitignore Ignores .claude/worktrees/.

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

Comment thread dstack/cc-eventlog/src/runtime_events.rs
Comment thread dstack/cc-eventlog/src/runtime_events.rs
Comment thread cc-eventlog/src/runtime_events.rs Outdated
Comment thread guest-agent/rpc/proto/agent_rpc.proto Outdated
Comment thread dstack-types/src/lib.rs Outdated

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 introduces an event log v2 digest format based on JCS (RFC 8785) canonical JSON, adds event_log_version to app-compose.json (defaulting to v1 for backward compatibility), and threads the selected version through runtime event emission and attestation/quote surfaces. It also adds an RPC opt-in (include_hash_inputs) to include the exact digest pre-image for runtime events in returned event logs/attestations.

Changes:

  • Add EventLogVersion + AppCompose.event_log_version and propagate it to all emit_runtime_event(..., version) call sites.
  • Implement v2 runtime event hashing via canonical JSON and extend event log structures to carry per-event version + optional hash_input.
  • Add include_hash_inputs to RawQuoteArgs and plumb it through guest-agent, simulator, gateway, and kms clients.

Reviewed changes

Copilot reviewed 21 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
ra-tls/src/cert.rs Updates attestation quote call signature to pass an event log version.
kms/src/main_service/upgrade_authority.rs Populates new include_hash_inputs flag in attest RPC call.
guest-agent/src/rpc_service.rs Threads include_hash_inputs through quote/attest RPC handlers; threads event_log_version into event emission; updates tests.
guest-agent/src/backend.rs Extends backend trait to support include_hash_inputs + EventLogVersion; real backend fills optional hash inputs.
guest-agent/rpc/proto/agent_rpc.proto Adds include_hash_inputs to RawQuoteArgs and documents behavior.
guest-agent-simulator/src/simulator.rs Supports include_hash_inputs for simulated quote/attest responses by populating hash_input.
guest-agent-simulator/src/main.rs Updates simulator backend trait implementation and tests for new params.
guest-agent-simulator/Cargo.toml Adds dstack-types dependency for EventLogVersion.
gateway/src/gen_debug_key.rs Updates quote request to include include_hash_inputs.
gateway/src/distributed_certbot.rs Updates quote/attest requests to include include_hash_inputs.
dstack-util/src/system_setup.rs Threads event_log_version through all runtime event emissions during boot/setup.
dstack-util/src/main.rs CLI extend command now reads event_log_version from shared app-compose and uses it for emit_runtime_event.
dstack-types/src/lib.rs Introduces EventLogVersion enum and adds event_log_version field to AppCompose with default.
dstack-attest/src/v1.rs Adds mutable access to TDX event log in platform evidence; updates tests for new fields.
dstack-attest/src/lib.rs Updates emit_runtime_event signature to accept an event log version.
dstack-attest/src/attestation.rs Adds optional hash-input inclusion for event log JSON; adds fill_event_hash_inputs; threads version into quote_with_app_id.
cc-eventlog/src/tdx.rs Extends TdxEvent with version + optional hash_input; implements fill_hash_input; adjusts runtime event conversion.
cc-eventlog/src/tcg.rs Initializes new TdxEvent fields when converting from TCG events.
cc-eventlog/src/runtime_events.rs Adds v2 canonical JSON hash input + EventLogVersion handling for runtime event digests; adds tests.
cc-eventlog/src/lib.rs Re-exports EventLogVersion and v2 helpers/constants.
cc-eventlog/Cargo.toml Adds dependencies for JCS canonicalization (serde_jcs) and shared types.
Cargo.lock Locks new dependencies (serde_jcs, ryu-js, etc.) and updated crate deps.
.gitignore Ignores .claude/worktrees/.

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

Comment thread dstack/cc-eventlog/src/runtime_events.rs
Comment thread dstack/cc-eventlog/src/runtime_events.rs
Comment thread guest-agent/rpc/proto/agent_rpc.proto Outdated

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

Adds an opt-in v2 runtime event-log digest format (JCS/RFC 8785 canonical JSON) and threads an event_log_version setting from compose config through guest event emission and attestation/quote RPCs, including an include_hash_inputs flag so relying parties can independently verify per-event digests.

Changes:

  • Introduce EventLogVersion and AppCompose.event_log_version (default V1) and plumb it into runtime event emission (RTMR3 extends).
  • Add include_hash_inputs to GetQuote/Attest RPCs and populate per-runtime-event hash_input (hex-encoded digest pre-image) when requested.
  • Expose event-log version selection in VMM UI + CLI compose generation.

Reviewed changes

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

Show a summary per file
File Description
vmm/ui/src/composables/useVmManager.ts Add form state + compose serialization for event_log_version (omit when V1).
vmm/ui/src/components/CreateVmDialog.ts Add UI dropdown to select V1 vs V2 event log format.
vmm/src/vmm-cli.py Add --event-log-version and write it into app-compose.json.
ra-tls/src/cert.rs Update RA-TLS quote callsite for new quote_with_app_id signature.
kms/src/main_service/upgrade_authority.rs Update attest request to include include_hash_inputs flag.
guest-agent/src/rpc_service.rs Thread include_hash_inputs into quote/attest; pass event_log_version into emit_event.
guest-agent/src/backend.rs Extend backend trait to support include_hash_inputs + event-log versioned emission.
guest-agent/rpc/proto/agent_rpc.proto Add include_hash_inputs field + clarified semantics in comments.
guest-agent-simulator/src/simulator.rs Support include_hash_inputs in simulated quote/attest responses.
guest-agent-simulator/src/main.rs Update simulator backend trait implementation + tests for new signatures.
guest-agent-simulator/Cargo.toml Add dstack-types dependency for EventLogVersion.
gateway/src/gen_debug_key.rs Populate include_hash_inputs in quote requests.
gateway/src/distributed_certbot.rs Populate include_hash_inputs in quote/attest requests.
dstack-util/src/system_setup.rs Pass event_log_version into all runtime event emissions during boot setup.
dstack-util/src/main.rs Make extend command pick event-log version from shared app-compose.json.
dstack-types/src/lib.rs Introduce EventLogVersion and add event_log_version to AppCompose.
dstack-attest/src/v1.rs Expose mutable access to TDX event log for filling hash_input in simulator/tests.
dstack-attest/src/lib.rs Version-aware emit_runtime_event API.
dstack-attest/src/attestation.rs Add include_hash_inputs plumbing for JSON event log; version-aware wire encoding selection.
cc-eventlog/src/tdx.rs Add version + optional hash_input to TdxEvent; compute digest input on demand.
cc-eventlog/src/tcg.rs Initialize new TdxEvent fields when converting from TCG events.
cc-eventlog/src/runtime_events.rs Add v2 canonical JSON digest input + EventLogVersion in RuntimeEvent.
cc-eventlog/src/lib.rs Re-export EventLogVersion and v2 helpers/constants.
cc-eventlog/Cargo.toml Add deps for JCS + version type.
Cargo.lock Lockfile updates for new crates.
.gitignore Ignore .claude/worktrees/.

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

Comment thread dstack/vmm/src/vmm-cli.py
Comment thread dstack-types/src/lib.rs Outdated

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 introduces a new runtime event-log digest format (v2) based on RFC 8785 JCS canonical JSON, plumbs an event_log_version setting from compose/UI/CLI into runtime event emission, and extends attestation/quote APIs to optionally expose per-event digest pre-images and a merged binary CCEL event log.

Changes:

  • Add event_log_version (default v1) to app compose handling (UI + CLI + guest/system setup) and thread it into RTMR3 runtime event emission.
  • Implement v2 runtime-event hashing (JCS canonical JSON pre-image) and optional hash_input inclusion for relying-party verification.
  • Add event_log_ccel to quote responses and update SDKs to decode it.

Reviewed changes

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

Show a summary per file
File Description
vmm/ui/src/composables/useVmManager.ts Adds event_log_version to form state and compose generation logic.
vmm/ui/src/components/CreateVmDialog.ts Adds UI dropdown to select event log format (V1/V2).
vmm/src/vmm-cli.py Adds --event-log-version flag and writes it into app-compose.json.
sdk/rust/types/src/dstack.rs Adds event_log_ccel to GetQuoteResponse plus decoding helper.
sdk/python/src/dstack_sdk/dstack_client.py Adds event_log_ccel to response model plus decoder.
sdk/js/src/index.ts Extends GetQuoteResponse interface with event_log_ccel.
sdk/go/dstack/client.go Extends GetQuoteResponse with EventLogCCEL + decoder.
ra-tls/src/cert.rs Updates quote_with_app_id call site for new signature.
kms/src/main_service/upgrade_authority.rs Supplies include_hash_inputs: false in attest call.
guest-agent/src/rpc_service.rs Threads include_hash_inputs through quote/attest; passes event_log_version to emit_event.
guest-agent/src/backend.rs Adds include_hash_inputs support and returns merged event_log_ccel.
guest-agent/rpc/proto/agent_rpc.proto Adds include_hash_inputs request field and event_log_ccel in response, with updated docs.
guest-agent-simulator/src/simulator.rs Simulator support for include_hash_inputs and new event_log_ccel field.
guest-agent-simulator/src/main.rs Updates simulator backend trait impl/tests for new signatures.
guest-agent-simulator/Cargo.toml Adds dstack-types dependency for EventLogVersion.
gateway/src/gen_debug_key.rs Updates get_quote call to include include_hash_inputs: false.
gateway/src/distributed_certbot.rs Updates quote/attest calls to include include_hash_inputs: false.
dstack-util/src/system_setup.rs Emits runtime events using event_log_version from compose.
dstack-util/src/main.rs Extender command now reads compose to choose event-log version.
dstack-types/src/lib.rs Introduces EventLogVersion enum and adds it to AppCompose.
dstack-attest/src/v1.rs Adds helpers for mutating TDX event log in tests/serialization paths.
dstack-attest/src/lib.rs Updates emit_runtime_event to accept event-log version.
dstack-attest/src/attestation.rs Adds include_hash_inputs support and versioned wire-format upgrade logic; adds CCEL builder plumbing.
cc-eventlog/src/tdx.rs Adds per-event version + optional hash_input, and helpers to populate it.
cc-eventlog/src/tcg.rs Adds CCEL helpers and encoder for runtime events as TCG_PCR_EVENT2 records.
cc-eventlog/src/runtime_events.rs Implements v1/v2 digest pre-images, v2 canonical JSON, and adds version to runtime events.
cc-eventlog/src/lib.rs Re-exports EventLogVersion, exposes tcg module, and adds CCEL roundtrip test.
cc-eventlog/Cargo.toml Adds serde_jcs + dstack-types + or-panic dependencies.
Cargo.lock Locks new Rust dependencies (serde_jcs, ryu-js, etc.).
.gitignore Ignores .claude/worktrees/.
Comments suppressed due to low confidence (1)

cc-eventlog/src/tdx.rs:116

  • TdxEvent::digest() always recomputes the digest for runtime events from event_payload. After this PR, runtime events encoded into the merged CCEL log use event_payload as the digest pre-image bytes (not the original payload/name tuple), and TryFrom<TcgEvent> currently defaults version to V1. That combination makes digest() return a value that can disagree with the stored digest field for runtime events parsed from the merged CCEL, which can break replay/verification when using the decoded TdxEvent API.

Consider changing digest() to prefer returning the stored self.digest when it is present (non-empty), and only recompute when self.digest is empty (e.g., for synthesized/stripped events).

    pub fn digest(&self) -> Vec<u8> {
        if let Some(runtime_event) = self.to_runtime_event() {
            return runtime_event.sha384_digest().to_vec();
        }
        self.digest.clone()
    }

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

Comment thread guest-agent/src/backend.rs Outdated
Comment thread dstack/vmm/src/vmm-cli.py
@kvinwang

Copy link
Copy Markdown
Collaborator Author

@copilot resolve the merge conflicts in this pull request

Copilot AI commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved. The two conflicts were:

  • dstack-types/src/lib.rs: kept both event_log_version (ours) and port_policy + PortPolicy/PortAttrs structs (master) in AppCompose
  • guest-agent/src/rpc_service.rs: kept both event_log_version and port_policy field initializers in the dummy AppCompose

cargo check passes cleanly. Merge commit: d3fd44d

@kvinwang
kvinwang force-pushed the feat/event-log-v2-canonical-json branch from d3fd44d to b953c66 Compare July 22, 2026 05:46
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.

3 participants