Skip to content

Commit b91f1d0

Browse files
author
Kevin Wang
committed
feat(guest-agent): fail EmitEvent with an explicit removal message
`EmitEvent` is gone for good -- runtime RTMR3 events are system-owned in 0.6.0, so an app extending the measurement chain is no longer something this API should offer. Deleting the method outright is the wrong way to say that. prpc answers both "no such method" and "the handler failed" with HTTP 400 and drops the message, so a 0.5.x app that emits events gets a bare 400 that reads identically to a wrong socket path or a broken build, and its author has no way to learn why the events stopped being recorded. Keep the method and the args message on the unversioned service, and have the handler always bail with the reason. Nothing reaches `emit_runtime_event`; this is a deliberate error, not a code path.
1 parent 53a53a2 commit b91f1d0

3 files changed

Lines changed: 47 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4444

4545
### Removed
4646
- sdk: the client-side `verify()` method is gone from the Rust, Python, Go and JavaScript SDKs, replaced by the standalone `verify_signature` above -- it never needed a client connection in the first place. Checking a signature needs no key material and no attestation, and the agent's verdict arrived over the socket unattested, so a caller who believed the TEE was vouching for it was mistaken and one who did not gained nothing over checking the signature locally. Verification also became stricter in one respect: non-canonical high-S secp256k1 signatures are now rejected explicitly everywhere. `k256` accepted only the canonical form, so the Rust agent already behaved this way, but a naive port to Python or Go would have silently accepted both `(r, s)` and `(r, n-s)` for the same message. The server-side `Verify` RPC (`/Verify`, present since v0.5.6) is **retained** on the unversioned guest-agent path so 0.5.x SDKs keep working against a 0.6 agent unchanged; it is deprecated, its semantics are frozen at the v0.5.11 wire surface, and it will not be carried into the v1 API. New code should verify locally
47+
- guest-agent: the `EmitEvent` RPC no longer records anything -- runtime RTMR3 events are system-owned in 0.6.0, so an app can no longer extend the measurement chain. The method itself stays on the unversioned path and always fails with an error naming the removal, rather than being deleted outright: prpc answers both "no such method" and "the handler failed" with HTTP 400 and drops the message, so a deleted method would leave a 0.5.x caller with a generic error indistinguishable from a wrong socket or a version skew. **Breaking:** any app extending RTMR3 at runtime must stop; bind app data through `report_data` instead, which is what most callers wanted anyway
48+
4749

4850
## [0.5.5] - 2025-10-20
4951

dstack/guest-agent/rpc/proto/agent_rpc.proto

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ service DstackGuest {
6060
// Returns a dstack-defined attestation format that supports different attestation modes across platforms.
6161
rpc Attest(AttestArgs) returns (AttestResponse) {}
6262

63+
// Removed in v0.6.0: always fails. Runtime RTMR3 events are system-owned now,
64+
// so an app can no longer extend them.
65+
//
66+
// The method is kept only so a pre-0.6 client gets a self-explanatory error.
67+
// prpc answers both "no such method" and "the handler failed" with HTTP 400,
68+
// so the message text is the only thing that tells the two apart.
69+
rpc EmitEvent(EmitEventArgs) returns (google.protobuf.Empty) {}
70+
6371
// Get app info
6472
rpc Info(google.protobuf.Empty) returns (AppInfo) {}
6573

@@ -300,6 +308,15 @@ message GetQuoteResponse {
300308
string vm_config = 4;
301309
}
302310

311+
// The request of the removed EmitEvent RPC. Kept so a pre-0.6 client's request
312+
// still decodes and reaches the handler that explains the removal.
313+
message EmitEventArgs {
314+
// The event name
315+
string event = 1;
316+
// The event data
317+
bytes payload = 2;
318+
}
319+
303320
// The request to derive a key
304321
message AppInfo {
305322
// App ID

dstack/guest-agent/src/rpc_service.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ use dstack_guest_agent_rpc::{
1616
tappd_server::{TappdRpc, TappdServer},
1717
worker_server::{WorkerRpc, WorkerServer},
1818
AppInfo, AttestAppKeyRequest, AttestArgs, AttestGpuArgs, AttestGpuResponse, AttestResponse,
19-
DeriveK256KeyResponse, DeriveKeyArgs, GetKeyArgs, GetKeyResponse, GetQuoteResponse,
20-
GetTlsKeyArgs, GetTlsKeyResponse, GpuEvidenceBundle, GpuInfoResponse, HealthResponse,
21-
RawQuoteArgs, SignRequest, SignResponse, TdxQuoteArgs, TdxQuoteResponse, VerifyRequest,
22-
VerifyResponse, WorkerVersion,
19+
DeriveK256KeyResponse, DeriveKeyArgs, EmitEventArgs, GetKeyArgs, GetKeyResponse,
20+
GetQuoteResponse, GetTlsKeyArgs, GetTlsKeyResponse, GpuEvidenceBundle, GpuInfoResponse,
21+
HealthResponse, RawQuoteArgs, SignRequest, SignResponse, TdxQuoteArgs, TdxQuoteResponse,
22+
VerifyRequest, VerifyResponse, WorkerVersion,
2323
};
2424
use dstack_types::{AppKeys, SysConfig, GPU_ATTESTATION_OUTPUT};
2525
use ed25519_dalek::ed25519::signature::hazmat::{PrehashSigner, PrehashVerifier};
@@ -389,6 +389,16 @@ impl DstackGuestRpc for InternalRpcHandler {
389389
self.state.quote_response(report_data)
390390
}
391391

392+
/// Always fails. See the RPC's doc comment in agent_rpc.proto: the method
393+
/// exists so a pre-0.6 client learns why its events stopped being recorded
394+
/// instead of getting an unknown-method 400 it cannot tell apart from
395+
/// talking to the wrong socket.
396+
async fn emit_event(self, _request: EmitEventArgs) -> Result<()> {
397+
anyhow::bail!(
398+
"EmitEvent was removed in dstack 0.6.0; runtime RTMR3 events are system-owned and cannot be extended by apps"
399+
)
400+
}
401+
392402
async fn info(self) -> Result<AppInfo> {
393403
get_info(&self.state, false).await
394404
}
@@ -1598,4 +1608,18 @@ pNs85uhOZE8z2jr8Pg==
15981608

15991609
assert_eq!(result.unwrap_err().to_string(), "Unsupported algorithm");
16001610
}
1611+
1612+
#[tokio::test]
1613+
async fn emit_event_reports_its_removal() {
1614+
let (state, _guard) = setup_test_state().await;
1615+
let result = InternalRpcHandler { state }
1616+
.emit_event(EmitEventArgs {
1617+
event: "test-event".to_string(),
1618+
payload: b"payload".to_vec(),
1619+
})
1620+
.await;
1621+
1622+
let err = result.unwrap_err().to_string();
1623+
assert!(err.contains("removed in dstack 0.6.0"), "{err}");
1624+
}
16011625
}

0 commit comments

Comments
 (0)