Skip to main content

secure_exec_sidecar_protocol/
protocol.rs

1use crate::generated_protocol::v1 as generated_protocol;
2use serde::de::{self, SeqAccess, Visitor};
3use serde::ser::SerializeTuple;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
7use std::error::Error;
8use std::fmt;
9
10pub use crate::wire::{OwnershipRequirement, ProtocolCodecError, RequestDirection};
11
12pub const PROTOCOL_NAME: &str = crate::wire::PROTOCOL_NAME;
13pub const PROTOCOL_VERSION: u16 = crate::wire::PROTOCOL_VERSION;
14pub const DEFAULT_MAX_FRAME_BYTES: usize = crate::wire::DEFAULT_MAX_FRAME_BYTES;
15pub const DEFAULT_COMPLETED_RESPONSE_CAP: usize = 10_000;
16pub type RequestId = crate::wire::RequestId;
17pub type ExtEnvelope = crate::wire::ExtEnvelope;
18
19fn serialize_bare_newtype_tag<S, T>(serializer: S, tag: u64, payload: &T) -> Result<S::Ok, S::Error>
20where
21    S: Serializer,
22    T: Serialize,
23{
24    let mut tuple = serializer.serialize_tuple(2)?;
25    tuple.serialize_element(&serde_bare::Uint(tag))?;
26    tuple.serialize_element(payload)?;
27    tuple.end()
28}
29
30/// Convert the compatibility protocol frame into the generated wire frame.
31pub fn to_generated_protocol_frame(
32    frame: &ProtocolFrame,
33) -> Result<generated_protocol::ProtocolFrame, ProtocolCodecError> {
34    Ok(match frame {
35        ProtocolFrame::Request(frame) => {
36            generated_protocol::ProtocolFrame::RequestFrame(generated_protocol::RequestFrame {
37                schema: to_generated_protocol_schema(&frame.schema),
38                request_id: frame.request_id,
39                ownership: to_generated_ownership_scope(&frame.ownership),
40                payload: to_generated_request_payload(&frame.payload)?,
41            })
42        }
43        ProtocolFrame::Response(frame) => {
44            generated_protocol::ProtocolFrame::ResponseFrame(generated_protocol::ResponseFrame {
45                schema: to_generated_protocol_schema(&frame.schema),
46                request_id: frame.request_id,
47                ownership: to_generated_ownership_scope(&frame.ownership),
48                payload: to_generated_response_payload(&frame.payload)?,
49            })
50        }
51        ProtocolFrame::Event(frame) => {
52            generated_protocol::ProtocolFrame::EventFrame(generated_protocol::EventFrame {
53                schema: to_generated_protocol_schema(&frame.schema),
54                ownership: to_generated_ownership_scope(&frame.ownership),
55                payload: to_generated_event_payload(&frame.payload),
56            })
57        }
58        ProtocolFrame::SidecarRequest(frame) => {
59            generated_protocol::ProtocolFrame::SidecarRequestFrame(
60                generated_protocol::SidecarRequestFrame {
61                    schema: to_generated_protocol_schema(&frame.schema),
62                    request_id: frame.request_id,
63                    ownership: to_generated_ownership_scope(&frame.ownership),
64                    payload: to_generated_sidecar_request_payload(&frame.payload)?,
65                },
66            )
67        }
68        ProtocolFrame::SidecarResponse(frame) => {
69            generated_protocol::ProtocolFrame::SidecarResponseFrame(
70                generated_protocol::SidecarResponseFrame {
71                    schema: to_generated_protocol_schema(&frame.schema),
72                    request_id: frame.request_id,
73                    ownership: to_generated_ownership_scope(&frame.ownership),
74                    payload: to_generated_sidecar_response_payload(&frame.payload)?,
75                },
76            )
77        }
78    })
79}
80
81/// Convert the generated wire frame into the compatibility protocol frame.
82pub fn from_generated_protocol_frame(
83    frame: generated_protocol::ProtocolFrame,
84) -> Result<ProtocolFrame, ProtocolCodecError> {
85    Ok(match frame {
86        generated_protocol::ProtocolFrame::RequestFrame(frame) => {
87            ProtocolFrame::Request(RequestFrame {
88                schema: from_generated_protocol_schema(frame.schema),
89                request_id: frame.request_id,
90                ownership: from_generated_ownership_scope(frame.ownership),
91                payload: from_generated_request_payload(frame.payload)?,
92            })
93        }
94        generated_protocol::ProtocolFrame::ResponseFrame(frame) => {
95            ProtocolFrame::Response(ResponseFrame {
96                schema: from_generated_protocol_schema(frame.schema),
97                request_id: frame.request_id,
98                ownership: from_generated_ownership_scope(frame.ownership),
99                payload: from_generated_response_payload(frame.payload)?,
100            })
101        }
102        generated_protocol::ProtocolFrame::EventFrame(frame) => ProtocolFrame::Event(EventFrame {
103            schema: from_generated_protocol_schema(frame.schema),
104            ownership: from_generated_ownership_scope(frame.ownership),
105            payload: from_generated_event_payload(frame.payload),
106        }),
107        generated_protocol::ProtocolFrame::SidecarRequestFrame(frame) => {
108            ProtocolFrame::SidecarRequest(SidecarRequestFrame {
109                schema: from_generated_protocol_schema(frame.schema),
110                request_id: frame.request_id,
111                ownership: from_generated_ownership_scope(frame.ownership),
112                payload: from_generated_sidecar_request_payload(frame.payload)?,
113            })
114        }
115        generated_protocol::ProtocolFrame::SidecarResponseFrame(frame) => {
116            ProtocolFrame::SidecarResponse(SidecarResponseFrame {
117                schema: from_generated_protocol_schema(frame.schema),
118                request_id: frame.request_id,
119                ownership: from_generated_ownership_scope(frame.ownership),
120                payload: from_generated_sidecar_response_payload(frame.payload)?,
121            })
122        }
123    })
124}
125
126fn to_generated_protocol_schema(schema: &ProtocolSchema) -> generated_protocol::ProtocolSchema {
127    schema.clone()
128}
129
130fn from_generated_protocol_schema(schema: generated_protocol::ProtocolSchema) -> ProtocolSchema {
131    schema
132}
133
134// `OwnershipScope` is now an alias for the generated `crate::wire::OwnershipScope`, so the
135// compat<->generated converters are identity functions. They are retained so the JSON-carrying
136// frame-conversion chain (still hand-written, out of scope for this pass) keeps compiling
137// without site-by-site rewiring.
138pub(crate) fn to_generated_ownership_scope(
139    ownership: &OwnershipScope,
140) -> generated_protocol::OwnershipScope {
141    ownership.clone()
142}
143
144pub(crate) fn from_generated_ownership_scope(
145    ownership: generated_protocol::OwnershipScope,
146) -> OwnershipScope {
147    ownership
148}
149
150fn to_generated_dispose_reason(reason: &DisposeReason) -> generated_protocol::DisposeReason {
151    match reason {
152        DisposeReason::Requested => generated_protocol::DisposeReason::Requested,
153        DisposeReason::ConnectionClosed => generated_protocol::DisposeReason::ConnectionClosed,
154        DisposeReason::HostShutdown => generated_protocol::DisposeReason::HostShutdown,
155    }
156}
157
158fn from_generated_dispose_reason(reason: generated_protocol::DisposeReason) -> DisposeReason {
159    match reason {
160        generated_protocol::DisposeReason::Requested => DisposeReason::Requested,
161        generated_protocol::DisposeReason::ConnectionClosed => DisposeReason::ConnectionClosed,
162        generated_protocol::DisposeReason::HostShutdown => DisposeReason::HostShutdown,
163    }
164}
165
166fn to_generated_filesystem_operation(
167    operation: &FilesystemOperation,
168) -> generated_protocol::FilesystemOperation {
169    match operation {
170        FilesystemOperation::Read => generated_protocol::FilesystemOperation::Read,
171        FilesystemOperation::Write => generated_protocol::FilesystemOperation::Write,
172        FilesystemOperation::Stat => generated_protocol::FilesystemOperation::Stat,
173        FilesystemOperation::ReadDir => generated_protocol::FilesystemOperation::ReadDir,
174        FilesystemOperation::Mkdir => generated_protocol::FilesystemOperation::Mkdir,
175        FilesystemOperation::Remove => generated_protocol::FilesystemOperation::Remove,
176        FilesystemOperation::Rename => generated_protocol::FilesystemOperation::Rename,
177    }
178}
179
180fn from_generated_filesystem_operation(
181    operation: generated_protocol::FilesystemOperation,
182) -> FilesystemOperation {
183    match operation {
184        generated_protocol::FilesystemOperation::Read => FilesystemOperation::Read,
185        generated_protocol::FilesystemOperation::Write => FilesystemOperation::Write,
186        generated_protocol::FilesystemOperation::Stat => FilesystemOperation::Stat,
187        generated_protocol::FilesystemOperation::ReadDir => FilesystemOperation::ReadDir,
188        generated_protocol::FilesystemOperation::Mkdir => FilesystemOperation::Mkdir,
189        generated_protocol::FilesystemOperation::Remove => FilesystemOperation::Remove,
190        generated_protocol::FilesystemOperation::Rename => FilesystemOperation::Rename,
191    }
192}
193
194fn to_generated_guest_filesystem_operation(
195    operation: &GuestFilesystemOperation,
196) -> generated_protocol::GuestFilesystemOperation {
197    match operation {
198        GuestFilesystemOperation::ReadFile => {
199            generated_protocol::GuestFilesystemOperation::ReadFile
200        }
201        GuestFilesystemOperation::WriteFile => {
202            generated_protocol::GuestFilesystemOperation::WriteFile
203        }
204        GuestFilesystemOperation::CreateDir => {
205            generated_protocol::GuestFilesystemOperation::CreateDir
206        }
207        GuestFilesystemOperation::Mkdir => generated_protocol::GuestFilesystemOperation::Mkdir,
208        GuestFilesystemOperation::Exists => generated_protocol::GuestFilesystemOperation::Exists,
209        GuestFilesystemOperation::Stat => generated_protocol::GuestFilesystemOperation::Stat,
210        GuestFilesystemOperation::Lstat => generated_protocol::GuestFilesystemOperation::Lstat,
211        GuestFilesystemOperation::ReadDir => generated_protocol::GuestFilesystemOperation::ReadDir,
212        GuestFilesystemOperation::RemoveFile => {
213            generated_protocol::GuestFilesystemOperation::RemoveFile
214        }
215        GuestFilesystemOperation::RemoveDir => {
216            generated_protocol::GuestFilesystemOperation::RemoveDir
217        }
218        GuestFilesystemOperation::Rename => generated_protocol::GuestFilesystemOperation::Rename,
219        GuestFilesystemOperation::Realpath => {
220            generated_protocol::GuestFilesystemOperation::Realpath
221        }
222        GuestFilesystemOperation::Symlink => generated_protocol::GuestFilesystemOperation::Symlink,
223        GuestFilesystemOperation::ReadLink => {
224            generated_protocol::GuestFilesystemOperation::ReadLink
225        }
226        GuestFilesystemOperation::Link => generated_protocol::GuestFilesystemOperation::Link,
227        GuestFilesystemOperation::Chmod => generated_protocol::GuestFilesystemOperation::Chmod,
228        GuestFilesystemOperation::Chown => generated_protocol::GuestFilesystemOperation::Chown,
229        GuestFilesystemOperation::Utimes => generated_protocol::GuestFilesystemOperation::Utimes,
230        GuestFilesystemOperation::Truncate => {
231            generated_protocol::GuestFilesystemOperation::Truncate
232        }
233        GuestFilesystemOperation::Pread => generated_protocol::GuestFilesystemOperation::Pread,
234        GuestFilesystemOperation::Pwrite => generated_protocol::GuestFilesystemOperation::Pwrite,
235    }
236}
237
238fn from_generated_guest_filesystem_operation(
239    operation: generated_protocol::GuestFilesystemOperation,
240) -> GuestFilesystemOperation {
241    match operation {
242        generated_protocol::GuestFilesystemOperation::ReadFile => {
243            GuestFilesystemOperation::ReadFile
244        }
245        generated_protocol::GuestFilesystemOperation::WriteFile => {
246            GuestFilesystemOperation::WriteFile
247        }
248        generated_protocol::GuestFilesystemOperation::CreateDir => {
249            GuestFilesystemOperation::CreateDir
250        }
251        generated_protocol::GuestFilesystemOperation::Mkdir => GuestFilesystemOperation::Mkdir,
252        generated_protocol::GuestFilesystemOperation::Exists => GuestFilesystemOperation::Exists,
253        generated_protocol::GuestFilesystemOperation::Stat => GuestFilesystemOperation::Stat,
254        generated_protocol::GuestFilesystemOperation::Lstat => GuestFilesystemOperation::Lstat,
255        generated_protocol::GuestFilesystemOperation::ReadDir => GuestFilesystemOperation::ReadDir,
256        generated_protocol::GuestFilesystemOperation::RemoveFile => {
257            GuestFilesystemOperation::RemoveFile
258        }
259        generated_protocol::GuestFilesystemOperation::RemoveDir => {
260            GuestFilesystemOperation::RemoveDir
261        }
262        generated_protocol::GuestFilesystemOperation::Rename => GuestFilesystemOperation::Rename,
263        generated_protocol::GuestFilesystemOperation::Realpath => {
264            GuestFilesystemOperation::Realpath
265        }
266        generated_protocol::GuestFilesystemOperation::Symlink => GuestFilesystemOperation::Symlink,
267        generated_protocol::GuestFilesystemOperation::ReadLink => {
268            GuestFilesystemOperation::ReadLink
269        }
270        generated_protocol::GuestFilesystemOperation::Link => GuestFilesystemOperation::Link,
271        generated_protocol::GuestFilesystemOperation::Chmod => GuestFilesystemOperation::Chmod,
272        generated_protocol::GuestFilesystemOperation::Chown => GuestFilesystemOperation::Chown,
273        generated_protocol::GuestFilesystemOperation::Utimes => GuestFilesystemOperation::Utimes,
274        generated_protocol::GuestFilesystemOperation::Truncate => {
275            GuestFilesystemOperation::Truncate
276        }
277        generated_protocol::GuestFilesystemOperation::Pread => GuestFilesystemOperation::Pread,
278        generated_protocol::GuestFilesystemOperation::Pwrite => GuestFilesystemOperation::Pwrite,
279    }
280}
281
282fn to_generated_permission_mode(mode: &PermissionMode) -> generated_protocol::PermissionMode {
283    mode.clone()
284}
285
286fn from_generated_permission_mode(mode: generated_protocol::PermissionMode) -> PermissionMode {
287    mode
288}
289
290fn to_generated_root_filesystem_entry_encoding(
291    encoding: &RootFilesystemEntryEncoding,
292) -> generated_protocol::RootFilesystemEntryEncoding {
293    match encoding {
294        RootFilesystemEntryEncoding::Utf8 => generated_protocol::RootFilesystemEntryEncoding::Utf8,
295        RootFilesystemEntryEncoding::Base64 => {
296            generated_protocol::RootFilesystemEntryEncoding::Base64
297        }
298    }
299}
300
301fn from_generated_root_filesystem_entry_encoding(
302    encoding: generated_protocol::RootFilesystemEntryEncoding,
303) -> RootFilesystemEntryEncoding {
304    match encoding {
305        generated_protocol::RootFilesystemEntryEncoding::Utf8 => RootFilesystemEntryEncoding::Utf8,
306        generated_protocol::RootFilesystemEntryEncoding::Base64 => {
307            RootFilesystemEntryEncoding::Base64
308        }
309    }
310}
311
312fn to_generated_stream_channel(channel: &StreamChannel) -> generated_protocol::StreamChannel {
313    match channel {
314        StreamChannel::Stdout => generated_protocol::StreamChannel::Stdout,
315        StreamChannel::Stderr => generated_protocol::StreamChannel::Stderr,
316    }
317}
318
319fn from_generated_stream_channel(channel: generated_protocol::StreamChannel) -> StreamChannel {
320    match channel {
321        generated_protocol::StreamChannel::Stdout => StreamChannel::Stdout,
322        generated_protocol::StreamChannel::Stderr => StreamChannel::Stderr,
323    }
324}
325
326fn to_generated_vm_lifecycle_state(
327    state: &VmLifecycleState,
328) -> generated_protocol::VmLifecycleState {
329    match state {
330        VmLifecycleState::Creating => generated_protocol::VmLifecycleState::Creating,
331        VmLifecycleState::Ready => generated_protocol::VmLifecycleState::Ready,
332        VmLifecycleState::Disposing => generated_protocol::VmLifecycleState::Disposing,
333        VmLifecycleState::Disposed => generated_protocol::VmLifecycleState::Disposed,
334        VmLifecycleState::Failed => generated_protocol::VmLifecycleState::Failed,
335    }
336}
337
338fn from_generated_vm_lifecycle_state(
339    state: generated_protocol::VmLifecycleState,
340) -> VmLifecycleState {
341    match state {
342        generated_protocol::VmLifecycleState::Creating => VmLifecycleState::Creating,
343        generated_protocol::VmLifecycleState::Ready => VmLifecycleState::Ready,
344        generated_protocol::VmLifecycleState::Disposing => VmLifecycleState::Disposing,
345        generated_protocol::VmLifecycleState::Disposed => VmLifecycleState::Disposed,
346        generated_protocol::VmLifecycleState::Failed => VmLifecycleState::Failed,
347    }
348}
349
350fn to_generated_guest_filesystem_stat(
351    stat: &GuestFilesystemStat,
352) -> generated_protocol::GuestFilesystemStat {
353    stat.clone()
354}
355
356fn from_generated_guest_filesystem_stat(
357    stat: generated_protocol::GuestFilesystemStat,
358) -> GuestFilesystemStat {
359    stat
360}
361
362fn to_generated_process_snapshot_entry(
363    entry: &ProcessSnapshotEntry,
364) -> generated_protocol::ProcessSnapshotEntry {
365    entry.clone()
366}
367
368fn from_generated_process_snapshot_entry(
369    entry: generated_protocol::ProcessSnapshotEntry,
370) -> ProcessSnapshotEntry {
371    entry
372}
373
374fn to_generated_ext_envelope(envelope: &ExtEnvelope) -> generated_protocol::ExtEnvelope {
375    envelope.clone()
376}
377
378fn from_generated_ext_envelope(envelope: generated_protocol::ExtEnvelope) -> ExtEnvelope {
379    envelope
380}
381
382fn to_generated_request_payload(
383    payload: &RequestPayload,
384) -> Result<generated_protocol::RequestPayload, ProtocolCodecError> {
385    Ok(match payload {
386        RequestPayload::Authenticate(inner) => {
387            generated_protocol::RequestPayload::AuthenticateRequest(inner.clone())
388        }
389        RequestPayload::OpenSession(inner) => {
390            generated_protocol::RequestPayload::OpenSessionRequest(inner.clone())
391        }
392        RequestPayload::CreateVm(inner) => {
393            generated_protocol::RequestPayload::CreateVmRequest(inner.clone())
394        }
395        RequestPayload::DisposeVm(inner) => generated_protocol::RequestPayload::DisposeVmRequest(
396            generated_protocol::DisposeVmRequest {
397                reason: to_generated_dispose_reason(&inner.reason),
398            },
399        ),
400        RequestPayload::BootstrapRootFilesystem(inner) => {
401            generated_protocol::RequestPayload::BootstrapRootFilesystemRequest(inner.clone())
402        }
403        RequestPayload::ConfigureVm(inner) => {
404            generated_protocol::RequestPayload::ConfigureVmRequest(inner.clone())
405        }
406        RequestPayload::RegisterHostCallbacks(inner) => {
407            generated_protocol::RequestPayload::RegisterHostCallbacksRequest(inner.clone())
408        }
409        RequestPayload::CreateLayer(_) => generated_protocol::RequestPayload::CreateLayerRequest,
410        RequestPayload::SealLayer(inner) => {
411            generated_protocol::RequestPayload::SealLayerRequest(inner.clone())
412        }
413        RequestPayload::ImportSnapshot(inner) => {
414            generated_protocol::RequestPayload::ImportSnapshotRequest(inner.clone())
415        }
416        RequestPayload::ExportSnapshot(inner) => {
417            generated_protocol::RequestPayload::ExportSnapshotRequest(inner.clone())
418        }
419        RequestPayload::CreateOverlay(inner) => {
420            generated_protocol::RequestPayload::CreateOverlayRequest(inner.clone())
421        }
422        RequestPayload::GuestFilesystemCall(inner) => {
423            generated_protocol::RequestPayload::GuestFilesystemCallRequest(inner.clone())
424        }
425        RequestPayload::SnapshotRootFilesystem(_) => {
426            generated_protocol::RequestPayload::SnapshotRootFilesystemRequest
427        }
428        RequestPayload::Execute(inner) => {
429            generated_protocol::RequestPayload::ExecuteRequest(inner.clone())
430        }
431        RequestPayload::WriteStdin(inner) => {
432            generated_protocol::RequestPayload::WriteStdinRequest(inner.clone())
433        }
434        RequestPayload::CloseStdin(inner) => {
435            generated_protocol::RequestPayload::CloseStdinRequest(inner.clone())
436        }
437        RequestPayload::KillProcess(inner) => {
438            generated_protocol::RequestPayload::KillProcessRequest(inner.clone())
439        }
440        RequestPayload::GetProcessSnapshot(_) => {
441            generated_protocol::RequestPayload::GetProcessSnapshotRequest
442        }
443        RequestPayload::GetResourceSnapshot(_) => {
444            generated_protocol::RequestPayload::GetResourceSnapshotRequest
445        }
446        RequestPayload::FindListener(inner) => {
447            generated_protocol::RequestPayload::FindListenerRequest(inner.clone())
448        }
449        RequestPayload::FindBoundUdp(inner) => {
450            generated_protocol::RequestPayload::FindBoundUdpRequest(inner.clone())
451        }
452        RequestPayload::VmFetch(inner) => {
453            generated_protocol::RequestPayload::VmFetchRequest(inner.clone())
454        }
455        RequestPayload::GetSignalState(inner) => {
456            generated_protocol::RequestPayload::GetSignalStateRequest(inner.clone())
457        }
458        RequestPayload::GetZombieTimerCount(_) => {
459            generated_protocol::RequestPayload::GetZombieTimerCountRequest
460        }
461        RequestPayload::HostFilesystemCall(inner) => {
462            generated_protocol::RequestPayload::HostFilesystemCallRequest(
463                generated_protocol::HostFilesystemCallRequest {
464                    operation: to_generated_filesystem_operation(&inner.operation),
465                    path: inner.path.clone(),
466                    payload_size_bytes: inner.payload_size_bytes,
467                },
468            )
469        }
470        RequestPayload::PersistenceLoad(inner) => {
471            generated_protocol::RequestPayload::PersistenceLoadRequest(inner.clone())
472        }
473        RequestPayload::PersistenceFlush(inner) => {
474            generated_protocol::RequestPayload::PersistenceFlushRequest(inner.clone())
475        }
476        RequestPayload::Ext(inner) => {
477            generated_protocol::RequestPayload::ExtEnvelope(to_generated_ext_envelope(inner))
478        }
479        RequestPayload::GuestKernelCall(inner) => {
480            generated_protocol::RequestPayload::GuestKernelCallRequest(inner.clone())
481        }
482        RequestPayload::ResizePty(inner) => {
483            generated_protocol::RequestPayload::ResizePtyRequest(inner.clone())
484        }
485        RequestPayload::LinkPackage(inner) => {
486            generated_protocol::RequestPayload::LinkPackageRequest(inner.clone())
487        }
488    })
489}
490
491fn from_generated_request_payload(
492    payload: generated_protocol::RequestPayload,
493) -> Result<RequestPayload, ProtocolCodecError> {
494    Ok(match payload {
495        generated_protocol::RequestPayload::AuthenticateRequest(inner) => {
496            RequestPayload::Authenticate(inner)
497        }
498        generated_protocol::RequestPayload::OpenSessionRequest(inner) => {
499            RequestPayload::OpenSession(inner)
500        }
501        generated_protocol::RequestPayload::CreateVmRequest(inner) => {
502            RequestPayload::CreateVm(inner)
503        }
504        generated_protocol::RequestPayload::DisposeVmRequest(inner) => {
505            RequestPayload::DisposeVm(DisposeVmRequest {
506                reason: from_generated_dispose_reason(inner.reason),
507            })
508        }
509        generated_protocol::RequestPayload::BootstrapRootFilesystemRequest(inner) => {
510            RequestPayload::BootstrapRootFilesystem(inner)
511        }
512        generated_protocol::RequestPayload::ConfigureVmRequest(inner) => {
513            RequestPayload::ConfigureVm(inner)
514        }
515        generated_protocol::RequestPayload::RegisterHostCallbacksRequest(inner) => {
516            RequestPayload::RegisterHostCallbacks(inner)
517        }
518        generated_protocol::RequestPayload::CreateLayerRequest => {
519            RequestPayload::CreateLayer(CreateLayerRequest {})
520        }
521        generated_protocol::RequestPayload::SealLayerRequest(inner) => {
522            RequestPayload::SealLayer(inner)
523        }
524        generated_protocol::RequestPayload::ImportSnapshotRequest(inner) => {
525            RequestPayload::ImportSnapshot(inner)
526        }
527        generated_protocol::RequestPayload::ExportSnapshotRequest(inner) => {
528            RequestPayload::ExportSnapshot(inner)
529        }
530        generated_protocol::RequestPayload::CreateOverlayRequest(inner) => {
531            RequestPayload::CreateOverlay(inner)
532        }
533        generated_protocol::RequestPayload::GuestFilesystemCallRequest(inner) => {
534            RequestPayload::GuestFilesystemCall(inner)
535        }
536        generated_protocol::RequestPayload::SnapshotRootFilesystemRequest => {
537            RequestPayload::SnapshotRootFilesystem(SnapshotRootFilesystemRequest {})
538        }
539        generated_protocol::RequestPayload::ExecuteRequest(inner) => RequestPayload::Execute(inner),
540        generated_protocol::RequestPayload::WriteStdinRequest(inner) => {
541            RequestPayload::WriteStdin(inner)
542        }
543        generated_protocol::RequestPayload::CloseStdinRequest(inner) => {
544            RequestPayload::CloseStdin(inner)
545        }
546        generated_protocol::RequestPayload::KillProcessRequest(inner) => {
547            RequestPayload::KillProcess(inner)
548        }
549        generated_protocol::RequestPayload::GetProcessSnapshotRequest => {
550            RequestPayload::GetProcessSnapshot(GetProcessSnapshotRequest {})
551        }
552        generated_protocol::RequestPayload::GetResourceSnapshotRequest => {
553            RequestPayload::GetResourceSnapshot(GetResourceSnapshotRequest {})
554        }
555        generated_protocol::RequestPayload::FindListenerRequest(inner) => {
556            RequestPayload::FindListener(inner)
557        }
558        generated_protocol::RequestPayload::FindBoundUdpRequest(inner) => {
559            RequestPayload::FindBoundUdp(inner)
560        }
561        generated_protocol::RequestPayload::VmFetchRequest(inner) => RequestPayload::VmFetch(inner),
562        generated_protocol::RequestPayload::GetSignalStateRequest(inner) => {
563            RequestPayload::GetSignalState(inner)
564        }
565        generated_protocol::RequestPayload::GetZombieTimerCountRequest => {
566            RequestPayload::GetZombieTimerCount(GetZombieTimerCountRequest {})
567        }
568        generated_protocol::RequestPayload::HostFilesystemCallRequest(inner) => {
569            RequestPayload::HostFilesystemCall(HostFilesystemCallRequest {
570                operation: from_generated_filesystem_operation(inner.operation),
571                path: inner.path,
572                payload_size_bytes: inner.payload_size_bytes,
573            })
574        }
575        generated_protocol::RequestPayload::PersistenceLoadRequest(inner) => {
576            RequestPayload::PersistenceLoad(inner)
577        }
578        generated_protocol::RequestPayload::PersistenceFlushRequest(inner) => {
579            RequestPayload::PersistenceFlush(inner)
580        }
581        generated_protocol::RequestPayload::ExtEnvelope(inner) => {
582            RequestPayload::Ext(from_generated_ext_envelope(inner))
583        }
584        generated_protocol::RequestPayload::GuestKernelCallRequest(inner) => {
585            RequestPayload::GuestKernelCall(inner)
586        }
587        generated_protocol::RequestPayload::ResizePtyRequest(inner) => {
588            RequestPayload::ResizePty(inner)
589        }
590        generated_protocol::RequestPayload::LinkPackageRequest(inner) => {
591            RequestPayload::LinkPackage(inner)
592        }
593    })
594}
595
596fn to_generated_response_payload(
597    payload: &ResponsePayload,
598) -> Result<generated_protocol::ResponsePayload, ProtocolCodecError> {
599    Ok(match payload {
600        ResponsePayload::Authenticated(inner) => {
601            generated_protocol::ResponsePayload::AuthenticatedResponse(inner.clone())
602        }
603        ResponsePayload::SessionOpened(inner) => {
604            generated_protocol::ResponsePayload::SessionOpenedResponse(inner.clone())
605        }
606        ResponsePayload::VmCreated(inner) => {
607            generated_protocol::ResponsePayload::VmCreatedResponse(inner.clone())
608        }
609        ResponsePayload::VmDisposed(inner) => {
610            generated_protocol::ResponsePayload::VmDisposedResponse(inner.clone())
611        }
612        ResponsePayload::RootFilesystemBootstrapped(inner) => {
613            generated_protocol::ResponsePayload::RootFilesystemBootstrappedResponse(inner.clone())
614        }
615        ResponsePayload::VmConfigured(inner) => {
616            generated_protocol::ResponsePayload::VmConfiguredResponse(inner.clone())
617        }
618        ResponsePayload::HostCallbacksRegistered(inner) => {
619            generated_protocol::ResponsePayload::HostCallbacksRegisteredResponse(inner.clone())
620        }
621        ResponsePayload::LayerCreated(inner) => {
622            generated_protocol::ResponsePayload::LayerCreatedResponse(inner.clone())
623        }
624        ResponsePayload::LayerSealed(inner) => {
625            generated_protocol::ResponsePayload::LayerSealedResponse(inner.clone())
626        }
627        ResponsePayload::SnapshotImported(inner) => {
628            generated_protocol::ResponsePayload::SnapshotImportedResponse(inner.clone())
629        }
630        ResponsePayload::SnapshotExported(inner) => {
631            generated_protocol::ResponsePayload::SnapshotExportedResponse(
632                generated_protocol::SnapshotExportedResponse {
633                    layer_id: inner.layer_id.clone(),
634                    entries: inner.entries.clone(),
635                },
636            )
637        }
638        ResponsePayload::OverlayCreated(inner) => {
639            generated_protocol::ResponsePayload::OverlayCreatedResponse(inner.clone())
640        }
641        ResponsePayload::GuestFilesystemResult(inner) => {
642            generated_protocol::ResponsePayload::GuestFilesystemResultResponse(
643                generated_protocol::GuestFilesystemResultResponse {
644                    operation: to_generated_guest_filesystem_operation(&inner.operation),
645                    path: inner.path.clone(),
646                    content: inner.content.clone(),
647                    encoding: inner
648                        .encoding
649                        .as_ref()
650                        .map(to_generated_root_filesystem_entry_encoding),
651                    entries: inner.entries.clone(),
652                    stat: inner.stat.as_ref().map(to_generated_guest_filesystem_stat),
653                    exists: inner.exists,
654                    target: inner.target.clone(),
655                },
656            )
657        }
658        ResponsePayload::RootFilesystemSnapshot(inner) => {
659            generated_protocol::ResponsePayload::RootFilesystemSnapshotResponse(
660                generated_protocol::RootFilesystemSnapshotResponse {
661                    entries: inner.entries.clone(),
662                },
663            )
664        }
665        ResponsePayload::ProcessStarted(inner) => {
666            generated_protocol::ResponsePayload::ProcessStartedResponse(inner.clone())
667        }
668        ResponsePayload::StdinWritten(inner) => {
669            generated_protocol::ResponsePayload::StdinWrittenResponse(inner.clone())
670        }
671        ResponsePayload::StdinClosed(inner) => {
672            generated_protocol::ResponsePayload::StdinClosedResponse(inner.clone())
673        }
674        ResponsePayload::ProcessKilled(inner) => {
675            generated_protocol::ResponsePayload::ProcessKilledResponse(inner.clone())
676        }
677        ResponsePayload::ProcessSnapshot(inner) => {
678            generated_protocol::ResponsePayload::ProcessSnapshotResponse(
679                generated_protocol::ProcessSnapshotResponse {
680                    processes: inner
681                        .processes
682                        .iter()
683                        .map(to_generated_process_snapshot_entry)
684                        .collect(),
685                },
686            )
687        }
688        ResponsePayload::ResourceSnapshot(inner) => {
689            generated_protocol::ResponsePayload::ResourceSnapshotResponse(inner.clone())
690        }
691        ResponsePayload::ListenerSnapshot(inner) => {
692            generated_protocol::ResponsePayload::ListenerSnapshotResponse(inner.clone())
693        }
694        ResponsePayload::BoundUdpSnapshot(inner) => {
695            generated_protocol::ResponsePayload::BoundUdpSnapshotResponse(inner.clone())
696        }
697        ResponsePayload::VmFetchResult(inner) => {
698            generated_protocol::ResponsePayload::VmFetchResponse(inner.clone())
699        }
700        ResponsePayload::SignalState(inner) => {
701            generated_protocol::ResponsePayload::SignalStateResponse(inner.clone())
702        }
703        ResponsePayload::ZombieTimerCount(inner) => {
704            generated_protocol::ResponsePayload::ZombieTimerCountResponse(inner.clone())
705        }
706        ResponsePayload::FilesystemResult(inner) => {
707            generated_protocol::ResponsePayload::FilesystemResultResponse(
708                generated_protocol::FilesystemResultResponse {
709                    operation: to_generated_filesystem_operation(&inner.operation),
710                    status: inner.status.clone(),
711                    payload_size_bytes: inner.payload_size_bytes,
712                },
713            )
714        }
715        ResponsePayload::PermissionDecision(inner) => {
716            generated_protocol::ResponsePayload::PermissionDecisionResponse(
717                generated_protocol::PermissionDecisionResponse {
718                    capability: inner.capability.clone(),
719                    decision: to_generated_permission_mode(&inner.decision),
720                },
721            )
722        }
723        ResponsePayload::PersistenceState(inner) => {
724            generated_protocol::ResponsePayload::PersistenceStateResponse(inner.clone())
725        }
726        ResponsePayload::PersistenceFlushed(inner) => {
727            generated_protocol::ResponsePayload::PersistenceFlushedResponse(inner.clone())
728        }
729        ResponsePayload::Rejected(inner) => {
730            generated_protocol::ResponsePayload::RejectedResponse(inner.clone())
731        }
732        ResponsePayload::ExtResult(inner) => {
733            generated_protocol::ResponsePayload::ExtEnvelope(to_generated_ext_envelope(inner))
734        }
735        ResponsePayload::GuestKernelResult(inner) => {
736            generated_protocol::ResponsePayload::GuestKernelResultResponse(inner.clone())
737        }
738        ResponsePayload::PtyResized(inner) => {
739            generated_protocol::ResponsePayload::PtyResizedResponse(inner.clone())
740        }
741        ResponsePayload::PackageLinked(inner) => {
742            generated_protocol::ResponsePayload::PackageLinkedResponse(inner.clone())
743        }
744    })
745}
746
747fn from_generated_response_payload(
748    payload: generated_protocol::ResponsePayload,
749) -> Result<ResponsePayload, ProtocolCodecError> {
750    Ok(match payload {
751        generated_protocol::ResponsePayload::AuthenticatedResponse(inner) => {
752            ResponsePayload::Authenticated(inner)
753        }
754        generated_protocol::ResponsePayload::SessionOpenedResponse(inner) => {
755            ResponsePayload::SessionOpened(inner)
756        }
757        generated_protocol::ResponsePayload::VmCreatedResponse(inner) => {
758            ResponsePayload::VmCreated(inner)
759        }
760        generated_protocol::ResponsePayload::VmDisposedResponse(inner) => {
761            ResponsePayload::VmDisposed(inner)
762        }
763        generated_protocol::ResponsePayload::RootFilesystemBootstrappedResponse(inner) => {
764            ResponsePayload::RootFilesystemBootstrapped(inner)
765        }
766        generated_protocol::ResponsePayload::VmConfiguredResponse(inner) => {
767            ResponsePayload::VmConfigured(inner)
768        }
769        generated_protocol::ResponsePayload::HostCallbacksRegisteredResponse(inner) => {
770            ResponsePayload::HostCallbacksRegistered(inner)
771        }
772        generated_protocol::ResponsePayload::LayerCreatedResponse(inner) => {
773            ResponsePayload::LayerCreated(inner)
774        }
775        generated_protocol::ResponsePayload::LayerSealedResponse(inner) => {
776            ResponsePayload::LayerSealed(inner)
777        }
778        generated_protocol::ResponsePayload::SnapshotImportedResponse(inner) => {
779            ResponsePayload::SnapshotImported(inner)
780        }
781        generated_protocol::ResponsePayload::SnapshotExportedResponse(inner) => {
782            ResponsePayload::SnapshotExported(SnapshotExportedResponse {
783                layer_id: inner.layer_id,
784                entries: inner.entries,
785            })
786        }
787        generated_protocol::ResponsePayload::OverlayCreatedResponse(inner) => {
788            ResponsePayload::OverlayCreated(inner)
789        }
790        generated_protocol::ResponsePayload::GuestFilesystemResultResponse(inner) => {
791            ResponsePayload::GuestFilesystemResult(GuestFilesystemResultResponse {
792                operation: from_generated_guest_filesystem_operation(inner.operation),
793                path: inner.path,
794                content: inner.content,
795                encoding: inner
796                    .encoding
797                    .map(from_generated_root_filesystem_entry_encoding),
798                entries: inner.entries,
799                stat: inner.stat.map(from_generated_guest_filesystem_stat),
800                exists: inner.exists,
801                target: inner.target,
802            })
803        }
804        generated_protocol::ResponsePayload::RootFilesystemSnapshotResponse(inner) => {
805            ResponsePayload::RootFilesystemSnapshot(RootFilesystemSnapshotResponse {
806                entries: inner.entries,
807            })
808        }
809        generated_protocol::ResponsePayload::ProcessStartedResponse(inner) => {
810            ResponsePayload::ProcessStarted(inner)
811        }
812        generated_protocol::ResponsePayload::StdinWrittenResponse(inner) => {
813            ResponsePayload::StdinWritten(inner)
814        }
815        generated_protocol::ResponsePayload::StdinClosedResponse(inner) => {
816            ResponsePayload::StdinClosed(inner)
817        }
818        generated_protocol::ResponsePayload::ProcessKilledResponse(inner) => {
819            ResponsePayload::ProcessKilled(inner)
820        }
821        generated_protocol::ResponsePayload::ProcessSnapshotResponse(inner) => {
822            ResponsePayload::ProcessSnapshot(ProcessSnapshotResponse {
823                processes: inner
824                    .processes
825                    .into_iter()
826                    .map(from_generated_process_snapshot_entry)
827                    .collect(),
828            })
829        }
830        generated_protocol::ResponsePayload::ResourceSnapshotResponse(inner) => {
831            ResponsePayload::ResourceSnapshot(inner)
832        }
833        generated_protocol::ResponsePayload::ListenerSnapshotResponse(inner) => {
834            ResponsePayload::ListenerSnapshot(inner)
835        }
836        generated_protocol::ResponsePayload::BoundUdpSnapshotResponse(inner) => {
837            ResponsePayload::BoundUdpSnapshot(inner)
838        }
839        generated_protocol::ResponsePayload::VmFetchResponse(inner) => {
840            ResponsePayload::VmFetchResult(inner)
841        }
842        generated_protocol::ResponsePayload::SignalStateResponse(inner) => {
843            ResponsePayload::SignalState(inner)
844        }
845        generated_protocol::ResponsePayload::ZombieTimerCountResponse(inner) => {
846            ResponsePayload::ZombieTimerCount(inner)
847        }
848        generated_protocol::ResponsePayload::FilesystemResultResponse(inner) => {
849            ResponsePayload::FilesystemResult(FilesystemResultResponse {
850                operation: from_generated_filesystem_operation(inner.operation),
851                status: inner.status,
852                payload_size_bytes: inner.payload_size_bytes,
853            })
854        }
855        generated_protocol::ResponsePayload::PermissionDecisionResponse(inner) => {
856            ResponsePayload::PermissionDecision(PermissionDecisionResponse {
857                capability: inner.capability,
858                decision: from_generated_permission_mode(inner.decision),
859            })
860        }
861        generated_protocol::ResponsePayload::PersistenceStateResponse(inner) => {
862            ResponsePayload::PersistenceState(inner)
863        }
864        generated_protocol::ResponsePayload::PersistenceFlushedResponse(inner) => {
865            ResponsePayload::PersistenceFlushed(inner)
866        }
867        generated_protocol::ResponsePayload::RejectedResponse(inner) => {
868            ResponsePayload::Rejected(inner)
869        }
870        generated_protocol::ResponsePayload::ExtEnvelope(inner) => {
871            ResponsePayload::ExtResult(from_generated_ext_envelope(inner))
872        }
873        generated_protocol::ResponsePayload::GuestKernelResultResponse(inner) => {
874            ResponsePayload::GuestKernelResult(inner)
875        }
876        generated_protocol::ResponsePayload::PtyResizedResponse(inner) => {
877            ResponsePayload::PtyResized(inner)
878        }
879        generated_protocol::ResponsePayload::PackageLinkedResponse(inner) => {
880            ResponsePayload::PackageLinked(inner)
881        }
882    })
883}
884
885fn to_generated_event_payload(payload: &EventPayload) -> generated_protocol::EventPayload {
886    match payload {
887        EventPayload::VmLifecycle(inner) => generated_protocol::EventPayload::VmLifecycleEvent(
888            generated_protocol::VmLifecycleEvent {
889                state: to_generated_vm_lifecycle_state(&inner.state),
890            },
891        ),
892        EventPayload::ProcessOutput(inner) => generated_protocol::EventPayload::ProcessOutputEvent(
893            generated_protocol::ProcessOutputEvent {
894                process_id: inner.process_id.clone(),
895                channel: to_generated_stream_channel(&inner.channel),
896                chunk: inner.chunk.clone(),
897            },
898        ),
899        EventPayload::ProcessExited(inner) => {
900            generated_protocol::EventPayload::ProcessExitedEvent(inner.clone())
901        }
902        EventPayload::Structured(inner) => {
903            generated_protocol::EventPayload::StructuredEvent(inner.clone())
904        }
905        EventPayload::Ext(inner) => {
906            generated_protocol::EventPayload::ExtEnvelope(to_generated_ext_envelope(inner))
907        }
908    }
909}
910
911fn from_generated_event_payload(payload: generated_protocol::EventPayload) -> EventPayload {
912    match payload {
913        generated_protocol::EventPayload::VmLifecycleEvent(inner) => {
914            EventPayload::VmLifecycle(VmLifecycleEvent {
915                state: from_generated_vm_lifecycle_state(inner.state),
916            })
917        }
918        generated_protocol::EventPayload::ProcessOutputEvent(inner) => {
919            EventPayload::ProcessOutput(ProcessOutputEvent {
920                process_id: inner.process_id,
921                channel: from_generated_stream_channel(inner.channel),
922                chunk: inner.chunk,
923            })
924        }
925        generated_protocol::EventPayload::ProcessExitedEvent(inner) => {
926            EventPayload::ProcessExited(inner)
927        }
928        generated_protocol::EventPayload::StructuredEvent(inner) => EventPayload::Structured(inner),
929        generated_protocol::EventPayload::ExtEnvelope(inner) => {
930            EventPayload::Ext(from_generated_ext_envelope(inner))
931        }
932    }
933}
934
935fn to_generated_sidecar_request_payload(
936    payload: &SidecarRequestPayload,
937) -> Result<generated_protocol::SidecarRequestPayload, ProtocolCodecError> {
938    Ok(match payload {
939        SidecarRequestPayload::HostCallback(inner) => {
940            generated_protocol::SidecarRequestPayload::HostCallbackRequest(inner.clone())
941        }
942        SidecarRequestPayload::JsBridgeCall(inner) => {
943            generated_protocol::SidecarRequestPayload::JsBridgeCallRequest(inner.clone())
944        }
945        SidecarRequestPayload::Ext(inner) => {
946            generated_protocol::SidecarRequestPayload::ExtEnvelope(to_generated_ext_envelope(inner))
947        }
948    })
949}
950
951fn from_generated_sidecar_request_payload(
952    payload: generated_protocol::SidecarRequestPayload,
953) -> Result<SidecarRequestPayload, ProtocolCodecError> {
954    Ok(match payload {
955        generated_protocol::SidecarRequestPayload::HostCallbackRequest(inner) => {
956            SidecarRequestPayload::HostCallback(inner)
957        }
958        generated_protocol::SidecarRequestPayload::JsBridgeCallRequest(inner) => {
959            SidecarRequestPayload::JsBridgeCall(inner)
960        }
961        generated_protocol::SidecarRequestPayload::ExtEnvelope(inner) => {
962            SidecarRequestPayload::Ext(from_generated_ext_envelope(inner))
963        }
964    })
965}
966
967fn to_generated_sidecar_response_payload(
968    payload: &SidecarResponsePayload,
969) -> Result<generated_protocol::SidecarResponsePayload, ProtocolCodecError> {
970    Ok(match payload {
971        SidecarResponsePayload::HostCallbackResult(inner) => {
972            generated_protocol::SidecarResponsePayload::HostCallbackResultResponse(inner.clone())
973        }
974        SidecarResponsePayload::JsBridgeResult(inner) => {
975            generated_protocol::SidecarResponsePayload::JsBridgeResultResponse(inner.clone())
976        }
977        SidecarResponsePayload::ExtResult(inner) => {
978            generated_protocol::SidecarResponsePayload::ExtEnvelope(to_generated_ext_envelope(
979                inner,
980            ))
981        }
982    })
983}
984
985fn from_generated_sidecar_response_payload(
986    payload: generated_protocol::SidecarResponsePayload,
987) -> Result<SidecarResponsePayload, ProtocolCodecError> {
988    Ok(match payload {
989        generated_protocol::SidecarResponsePayload::HostCallbackResultResponse(inner) => {
990            SidecarResponsePayload::HostCallbackResult(inner)
991        }
992        generated_protocol::SidecarResponsePayload::JsBridgeResultResponse(inner) => {
993            SidecarResponsePayload::JsBridgeResult(inner)
994        }
995        generated_protocol::SidecarResponsePayload::ExtEnvelope(inner) => {
996            SidecarResponsePayload::ExtResult(from_generated_ext_envelope(inner))
997        }
998    })
999}
1000
1001macro_rules! impl_bare_newtype_union_enum {
1002    (
1003        $name:ident,
1004        $json_name:ident,
1005        $(#[$json_attr:meta])*
1006        {
1007            $($variant:ident($ty:ty) = $tag:literal),+ $(,)?
1008        }
1009    ) => {
1010        #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1011        $(#[$json_attr])*
1012        enum $json_name {
1013            $($variant($ty)),+
1014        }
1015
1016        impl From<&$name> for $json_name {
1017            fn from(value: &$name) -> Self {
1018                match value {
1019                    $($name::$variant(inner) => Self::$variant(inner.clone()),)+
1020                }
1021            }
1022        }
1023
1024        impl From<$json_name> for $name {
1025            fn from(value: $json_name) -> Self {
1026                match value {
1027                    $($json_name::$variant(inner) => Self::$variant(inner),)+
1028                }
1029            }
1030        }
1031
1032        impl Serialize for $name {
1033            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1034            where
1035                S: Serializer,
1036            {
1037                if serializer.is_human_readable() {
1038                    $json_name::from(self).serialize(serializer)
1039                } else {
1040                    match self {
1041                        $(Self::$variant(inner) => serialize_bare_newtype_tag(serializer, $tag, inner),)+
1042                    }
1043                }
1044            }
1045        }
1046
1047        impl<'de> Deserialize<'de> for $name {
1048            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1049            where
1050                D: Deserializer<'de>,
1051            {
1052                if deserializer.is_human_readable() {
1053                    Ok($json_name::deserialize(deserializer)?.into())
1054                } else {
1055                    struct UnionVisitor;
1056
1057                    impl<'de> Visitor<'de> for UnionVisitor {
1058                        type Value = $name;
1059
1060                        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1061                            write!(formatter, "a {} BARE union", stringify!($name))
1062                        }
1063
1064                        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1065                        where
1066                            A: SeqAccess<'de>,
1067                        {
1068                            let serde_bare::Uint(tag) = seq
1069                                .next_element()?
1070                                .ok_or_else(|| de::Error::custom(concat!("missing ", stringify!($name), " tag")))?;
1071                            match tag {
1072                                $(
1073                                    $tag => {
1074                                        let payload = seq.next_element::<$ty>()?.ok_or_else(|| {
1075                                            de::Error::custom(format!(
1076                                                "missing {} payload for tag {}",
1077                                                stringify!($variant),
1078                                                $tag
1079                                            ))
1080                                        })?;
1081                                        Ok($name::$variant(payload))
1082                                    }
1083                                )+
1084                                _ => Err(de::Error::custom(format!(
1085                                    "unknown {} tag: {}",
1086                                    stringify!($name),
1087                                    tag
1088                                ))),
1089                            }
1090                        }
1091                    }
1092
1093                    deserializer.deserialize_tuple(2, UnionVisitor)
1094                }
1095            }
1096        }
1097    };
1098}
1099
1100pub type ProtocolSchema = crate::wire::ProtocolSchema;
1101
1102pub type OwnershipScope = crate::wire::OwnershipScope;
1103
1104#[derive(Debug, Clone, PartialEq, Eq)]
1105pub enum ProtocolFrame {
1106    Request(RequestFrame),
1107    Response(ResponseFrame),
1108    Event(EventFrame),
1109    SidecarRequest(SidecarRequestFrame),
1110    SidecarResponse(SidecarResponseFrame),
1111}
1112
1113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1114pub struct RequestFrame {
1115    pub schema: ProtocolSchema,
1116    pub request_id: RequestId,
1117    pub ownership: OwnershipScope,
1118    pub payload: RequestPayload,
1119}
1120
1121impl RequestFrame {
1122    pub fn new(request_id: RequestId, ownership: OwnershipScope, payload: RequestPayload) -> Self {
1123        Self {
1124            schema: ProtocolSchema::current(),
1125            request_id,
1126            ownership,
1127            payload,
1128        }
1129    }
1130}
1131
1132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1133pub struct ResponseFrame {
1134    pub schema: ProtocolSchema,
1135    pub request_id: RequestId,
1136    pub ownership: OwnershipScope,
1137    pub payload: ResponsePayload,
1138}
1139
1140impl ResponseFrame {
1141    pub fn new(request_id: RequestId, ownership: OwnershipScope, payload: ResponsePayload) -> Self {
1142        Self {
1143            schema: ProtocolSchema::current(),
1144            request_id,
1145            ownership,
1146            payload,
1147        }
1148    }
1149}
1150
1151#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1152pub struct SidecarRequestFrame {
1153    pub schema: ProtocolSchema,
1154    pub request_id: RequestId,
1155    pub ownership: OwnershipScope,
1156    pub payload: SidecarRequestPayload,
1157}
1158
1159impl SidecarRequestFrame {
1160    pub fn new(
1161        request_id: RequestId,
1162        ownership: OwnershipScope,
1163        payload: SidecarRequestPayload,
1164    ) -> Self {
1165        Self {
1166            schema: ProtocolSchema::current(),
1167            request_id,
1168            ownership,
1169            payload,
1170        }
1171    }
1172}
1173
1174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1175pub struct SidecarResponseFrame {
1176    pub schema: ProtocolSchema,
1177    pub request_id: RequestId,
1178    pub ownership: OwnershipScope,
1179    pub payload: SidecarResponsePayload,
1180}
1181
1182impl SidecarResponseFrame {
1183    pub fn new(
1184        request_id: RequestId,
1185        ownership: OwnershipScope,
1186        payload: SidecarResponsePayload,
1187    ) -> Self {
1188        Self {
1189            schema: ProtocolSchema::current(),
1190            request_id,
1191            ownership,
1192            payload,
1193        }
1194    }
1195}
1196
1197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1198pub struct EventFrame {
1199    pub schema: ProtocolSchema,
1200    pub ownership: OwnershipScope,
1201    pub payload: EventPayload,
1202}
1203
1204impl EventFrame {
1205    pub fn new(ownership: OwnershipScope, payload: EventPayload) -> Self {
1206        Self {
1207            schema: ProtocolSchema::current(),
1208            ownership,
1209            payload,
1210        }
1211    }
1212}
1213
1214#[derive(Debug, Clone, PartialEq, Eq)]
1215pub enum RequestPayload {
1216    Authenticate(AuthenticateRequest),
1217    OpenSession(OpenSessionRequest),
1218    CreateVm(CreateVmRequest),
1219    DisposeVm(DisposeVmRequest),
1220    BootstrapRootFilesystem(BootstrapRootFilesystemRequest),
1221    ConfigureVm(ConfigureVmRequest),
1222    RegisterHostCallbacks(RegisterHostCallbacksRequest),
1223    CreateLayer(CreateLayerRequest),
1224    SealLayer(SealLayerRequest),
1225    ImportSnapshot(ImportSnapshotRequest),
1226    ExportSnapshot(ExportSnapshotRequest),
1227    CreateOverlay(CreateOverlayRequest),
1228    GuestFilesystemCall(GuestFilesystemCallRequest),
1229    SnapshotRootFilesystem(SnapshotRootFilesystemRequest),
1230    Execute(ExecuteRequest),
1231    WriteStdin(WriteStdinRequest),
1232    CloseStdin(CloseStdinRequest),
1233    KillProcess(KillProcessRequest),
1234    GetProcessSnapshot(GetProcessSnapshotRequest),
1235    FindListener(FindListenerRequest),
1236    FindBoundUdp(FindBoundUdpRequest),
1237    VmFetch(VmFetchRequest),
1238    GetSignalState(GetSignalStateRequest),
1239    GetZombieTimerCount(GetZombieTimerCountRequest),
1240    HostFilesystemCall(HostFilesystemCallRequest),
1241    PersistenceLoad(PersistenceLoadRequest),
1242    PersistenceFlush(PersistenceFlushRequest),
1243    Ext(ExtEnvelope),
1244    GuestKernelCall(GuestKernelCallRequest),
1245    ResizePty(ResizePtyRequest),
1246    GetResourceSnapshot(GetResourceSnapshotRequest),
1247    LinkPackage(LinkPackageRequest),
1248}
1249
1250#[derive(Debug, Clone, PartialEq, Eq)]
1251pub enum ResponsePayload {
1252    Authenticated(AuthenticatedResponse),
1253    SessionOpened(SessionOpenedResponse),
1254    VmCreated(VmCreatedResponse),
1255    VmDisposed(VmDisposedResponse),
1256    RootFilesystemBootstrapped(RootFilesystemBootstrappedResponse),
1257    VmConfigured(VmConfiguredResponse),
1258    HostCallbacksRegistered(HostCallbacksRegisteredResponse),
1259    LayerCreated(LayerCreatedResponse),
1260    LayerSealed(LayerSealedResponse),
1261    SnapshotImported(SnapshotImportedResponse),
1262    SnapshotExported(SnapshotExportedResponse),
1263    OverlayCreated(OverlayCreatedResponse),
1264    GuestFilesystemResult(GuestFilesystemResultResponse),
1265    RootFilesystemSnapshot(RootFilesystemSnapshotResponse),
1266    ProcessStarted(ProcessStartedResponse),
1267    StdinWritten(StdinWrittenResponse),
1268    StdinClosed(StdinClosedResponse),
1269    ProcessKilled(ProcessKilledResponse),
1270    ProcessSnapshot(ProcessSnapshotResponse),
1271    ListenerSnapshot(ListenerSnapshotResponse),
1272    BoundUdpSnapshot(BoundUdpSnapshotResponse),
1273    VmFetchResult(VmFetchResponse),
1274    SignalState(SignalStateResponse),
1275    ZombieTimerCount(ZombieTimerCountResponse),
1276    FilesystemResult(FilesystemResultResponse),
1277    PermissionDecision(PermissionDecisionResponse),
1278    PersistenceState(PersistenceStateResponse),
1279    PersistenceFlushed(PersistenceFlushedResponse),
1280    Rejected(RejectedResponse),
1281    ExtResult(ExtEnvelope),
1282    GuestKernelResult(GuestKernelResultResponse),
1283    PtyResized(PtyResizedResponse),
1284    ResourceSnapshot(ResourceSnapshotResponse),
1285    PackageLinked(PackageLinkedResponse),
1286}
1287
1288#[derive(Debug, Clone, PartialEq, Eq)]
1289pub enum SidecarRequestPayload {
1290    HostCallback(HostCallbackRequest),
1291    JsBridgeCall(JsBridgeCallRequest),
1292    Ext(ExtEnvelope),
1293}
1294
1295#[derive(Debug, Clone, PartialEq, Eq)]
1296pub enum SidecarResponsePayload {
1297    HostCallbackResult(HostCallbackResultResponse),
1298    JsBridgeResult(JsBridgeResultResponse),
1299    ExtResult(ExtEnvelope),
1300}
1301
1302#[derive(Debug, Clone, PartialEq, Eq)]
1303pub enum EventPayload {
1304    VmLifecycle(VmLifecycleEvent),
1305    ProcessOutput(ProcessOutputEvent),
1306    ProcessExited(ProcessExitedEvent),
1307    Structured(StructuredEvent),
1308    Ext(ExtEnvelope),
1309}
1310
1311pub type SidecarPlacement = crate::wire::SidecarPlacement;
1312
1313pub type SidecarPlacementShared = crate::wire::SidecarPlacementShared;
1314
1315pub type SidecarPlacementExplicit = crate::wire::SidecarPlacementExplicit;
1316
1317pub type GuestRuntimeKind = crate::wire::GuestRuntimeKind;
1318
1319pub type DisposeReason = crate::wire::DisposeReason;
1320
1321pub type FilesystemOperation = crate::wire::FilesystemOperation;
1322
1323pub type GuestFilesystemOperation = crate::wire::GuestFilesystemOperation;
1324
1325pub type PermissionMode = crate::wire::PermissionMode;
1326
1327pub type FsPermissionRule = crate::wire::FsPermissionRule;
1328
1329pub type PatternPermissionRule = crate::wire::PatternPermissionRule;
1330
1331pub type FsPermissionRuleSet = crate::wire::FsPermissionRuleSet;
1332
1333pub type PatternPermissionRuleSet = crate::wire::PatternPermissionRuleSet;
1334
1335pub type FsPermissionScope = crate::wire::FsPermissionScope;
1336
1337pub type PatternPermissionScope = crate::wire::PatternPermissionScope;
1338
1339pub type PermissionsPolicy = crate::wire::PermissionsPolicy;
1340
1341pub type RootFilesystemEntryKind = crate::wire::RootFilesystemEntryKind;
1342
1343pub type RootFilesystemMode = crate::wire::RootFilesystemMode;
1344
1345pub type RootFilesystemLowerDescriptor = crate::wire::RootFilesystemLowerDescriptor;
1346
1347pub type SnapshotRootFilesystemLower = crate::wire::SnapshotRootFilesystemLower;
1348
1349pub type StreamChannel = crate::wire::StreamChannel;
1350
1351pub type VmLifecycleState = crate::wire::VmLifecycleState;
1352
1353pub type AuthenticateRequest = crate::wire::AuthenticateRequest;
1354
1355pub type OpenSessionRequest = crate::wire::OpenSessionRequest;
1356
1357pub type CreateVmRequest = crate::wire::CreateVmRequest;
1358
1359#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1360pub struct DisposeVmRequest {
1361    pub reason: DisposeReason,
1362}
1363
1364pub type BootstrapRootFilesystemRequest = crate::wire::BootstrapRootFilesystemRequest;
1365
1366pub type RootFilesystemDescriptor = crate::wire::RootFilesystemDescriptor;
1367
1368pub type RootFilesystemEntryEncoding = crate::wire::RootFilesystemEntryEncoding;
1369
1370pub type RootFilesystemEntry = crate::wire::RootFilesystemEntry;
1371
1372pub type ConfigureVmRequest = crate::wire::ConfigureVmRequest;
1373
1374#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
1375pub struct CreateLayerRequest {}
1376
1377pub type SealLayerRequest = crate::wire::SealLayerRequest;
1378
1379pub type ImportSnapshotRequest = crate::wire::ImportSnapshotRequest;
1380
1381pub type ExportSnapshotRequest = crate::wire::ExportSnapshotRequest;
1382
1383pub type CreateOverlayRequest = crate::wire::CreateOverlayRequest;
1384
1385pub type GuestFilesystemCallRequest = crate::wire::GuestFilesystemCallRequest;
1386
1387pub type GuestKernelCallRequest = crate::wire::GuestKernelCallRequest;
1388pub type ResizePtyRequest = crate::wire::ResizePtyRequest;
1389pub type PackageDescriptor = crate::wire::PackageDescriptor;
1390pub type LinkPackageRequest = crate::wire::LinkPackageRequest;
1391
1392pub type GuestKernelResultResponse = crate::wire::GuestKernelResultResponse;
1393pub type PtyResizedResponse = crate::wire::PtyResizedResponse;
1394pub type PackageLinkedResponse = crate::wire::PackageLinkedResponse;
1395
1396#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
1397pub struct SnapshotRootFilesystemRequest {}
1398
1399pub type MountDescriptor = crate::wire::MountDescriptor;
1400
1401pub type MountPluginDescriptor = crate::wire::MountPluginDescriptor;
1402
1403pub type SoftwareDescriptor = crate::wire::SoftwareDescriptor;
1404
1405pub type ProjectedModuleDescriptor = crate::wire::ProjectedModuleDescriptor;
1406
1407pub type WasmPermissionTier = crate::wire::WasmPermissionTier;
1408
1409pub type ExecuteRequest = crate::wire::ExecuteRequest;
1410
1411pub type WriteStdinRequest = crate::wire::WriteStdinRequest;
1412
1413pub type CloseStdinRequest = crate::wire::CloseStdinRequest;
1414
1415pub type KillProcessRequest = crate::wire::KillProcessRequest;
1416
1417#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
1418pub struct GetProcessSnapshotRequest {}
1419
1420#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
1421pub struct GetResourceSnapshotRequest {}
1422
1423pub type FindListenerRequest = crate::wire::FindListenerRequest;
1424
1425pub type FindBoundUdpRequest = crate::wire::FindBoundUdpRequest;
1426
1427pub type VmFetchRequest = crate::wire::VmFetchRequest;
1428
1429pub type GetSignalStateRequest = crate::wire::GetSignalStateRequest;
1430
1431#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
1432pub struct GetZombieTimerCountRequest {}
1433
1434pub type HostFilesystemCallRequest = crate::wire::HostFilesystemCallRequest;
1435
1436pub type PersistenceLoadRequest = crate::wire::PersistenceLoadRequest;
1437
1438pub type PersistenceFlushRequest = crate::wire::PersistenceFlushRequest;
1439
1440pub type RegisterHostCallbacksRequest = crate::wire::RegisterHostCallbacksRequest;
1441
1442pub type RegisteredHostCallbackDefinition = crate::wire::RegisteredHostCallbackDefinition;
1443
1444pub type RegisteredHostCallbackExample = crate::wire::RegisteredHostCallbackExample;
1445
1446pub type HostCallbackRequest = crate::wire::HostCallbackRequest;
1447
1448pub type JsBridgeCallRequest = crate::wire::JsBridgeCallRequest;
1449
1450pub type AuthenticatedResponse = crate::wire::AuthenticatedResponse;
1451
1452pub type SessionOpenedResponse = crate::wire::SessionOpenedResponse;
1453
1454pub type VmCreatedResponse = crate::wire::VmCreatedResponse;
1455
1456pub type VmDisposedResponse = crate::wire::VmDisposedResponse;
1457
1458pub type RootFilesystemBootstrappedResponse = crate::wire::RootFilesystemBootstrappedResponse;
1459
1460pub type VmConfiguredResponse = crate::wire::VmConfiguredResponse;
1461
1462pub type HostCallbacksRegisteredResponse = crate::wire::HostCallbacksRegisteredResponse;
1463
1464pub type GuestFilesystemStat = crate::wire::GuestFilesystemStat;
1465
1466pub type GuestDirEntry = crate::wire::GuestDirEntry;
1467
1468pub type GuestFilesystemResultResponse = crate::wire::GuestFilesystemResultResponse;
1469
1470pub type RootFilesystemSnapshotResponse = crate::wire::RootFilesystemSnapshotResponse;
1471
1472pub type LayerCreatedResponse = crate::wire::LayerCreatedResponse;
1473
1474pub type LayerSealedResponse = crate::wire::LayerSealedResponse;
1475
1476pub type SnapshotImportedResponse = crate::wire::SnapshotImportedResponse;
1477
1478pub type SnapshotExportedResponse = crate::wire::SnapshotExportedResponse;
1479
1480pub type OverlayCreatedResponse = crate::wire::OverlayCreatedResponse;
1481
1482pub type ProcessStartedResponse = crate::wire::ProcessStartedResponse;
1483
1484pub type StdinWrittenResponse = crate::wire::StdinWrittenResponse;
1485
1486pub type StdinClosedResponse = crate::wire::StdinClosedResponse;
1487
1488pub type ProcessKilledResponse = crate::wire::ProcessKilledResponse;
1489
1490pub type ProcessSnapshotStatus = crate::wire::ProcessSnapshotStatus;
1491
1492pub type ProcessSnapshotEntry = crate::wire::ProcessSnapshotEntry;
1493
1494pub type ProcessSnapshotResponse = crate::wire::ProcessSnapshotResponse;
1495
1496pub type QueueSnapshotEntry = crate::wire::QueueSnapshotEntry;
1497
1498pub type ResourceSnapshotResponse = crate::wire::ResourceSnapshotResponse;
1499
1500pub type SocketStateEntry = crate::wire::SocketStateEntry;
1501
1502pub type ListenerSnapshotResponse = crate::wire::ListenerSnapshotResponse;
1503
1504pub type BoundUdpSnapshotResponse = crate::wire::BoundUdpSnapshotResponse;
1505
1506pub type VmFetchResponse = crate::wire::VmFetchResponse;
1507
1508pub type SignalDispositionAction = crate::wire::SignalDispositionAction;
1509
1510pub type SignalHandlerRegistration = crate::wire::SignalHandlerRegistration;
1511
1512pub type SignalStateResponse = crate::wire::SignalStateResponse;
1513
1514pub type ZombieTimerCountResponse = crate::wire::ZombieTimerCountResponse;
1515
1516pub type FilesystemResultResponse = crate::wire::FilesystemResultResponse;
1517
1518pub type PermissionDecisionResponse = crate::wire::PermissionDecisionResponse;
1519
1520pub type PersistenceStateResponse = crate::wire::PersistenceStateResponse;
1521
1522pub type PersistenceFlushedResponse = crate::wire::PersistenceFlushedResponse;
1523
1524pub type HostCallbackResultResponse = crate::wire::HostCallbackResultResponse;
1525
1526pub type JsBridgeResultResponse = crate::wire::JsBridgeResultResponse;
1527
1528pub type RejectedResponse = crate::wire::RejectedResponse;
1529
1530pub type VmLifecycleEvent = crate::wire::VmLifecycleEvent;
1531
1532pub type ProcessOutputEvent = crate::wire::ProcessOutputEvent;
1533
1534pub type ProcessExitedEvent = crate::wire::ProcessExitedEvent;
1535
1536pub type StructuredEvent = crate::wire::StructuredEvent;
1537
1538impl_bare_newtype_union_enum!(
1539    ProtocolFrame,
1540    JsonProtocolFrame,
1541    #[serde(tag = "frame_type", rename_all = "snake_case")]
1542    {
1543        Request(RequestFrame) = 0,
1544        Response(ResponseFrame) = 1,
1545        Event(EventFrame) = 2,
1546        SidecarRequest(SidecarRequestFrame) = 3,
1547        SidecarResponse(SidecarResponseFrame) = 4,
1548    }
1549);
1550
1551impl_bare_newtype_union_enum!(
1552    RequestPayload,
1553    JsonRequestPayload,
1554    #[serde(tag = "type", rename_all = "snake_case")]
1555    {
1556        Authenticate(AuthenticateRequest) = 0,
1557        OpenSession(OpenSessionRequest) = 1,
1558        CreateVm(CreateVmRequest) = 2,
1559        DisposeVm(DisposeVmRequest) = 3,
1560        BootstrapRootFilesystem(BootstrapRootFilesystemRequest) = 4,
1561        ConfigureVm(ConfigureVmRequest) = 5,
1562        RegisterHostCallbacks(RegisterHostCallbacksRequest) = 6,
1563        CreateLayer(CreateLayerRequest) = 7,
1564        SealLayer(SealLayerRequest) = 8,
1565        ImportSnapshot(ImportSnapshotRequest) = 9,
1566        ExportSnapshot(ExportSnapshotRequest) = 10,
1567        CreateOverlay(CreateOverlayRequest) = 11,
1568        GuestFilesystemCall(GuestFilesystemCallRequest) = 12,
1569        SnapshotRootFilesystem(SnapshotRootFilesystemRequest) = 13,
1570        Execute(ExecuteRequest) = 14,
1571        WriteStdin(WriteStdinRequest) = 15,
1572        CloseStdin(CloseStdinRequest) = 16,
1573        KillProcess(KillProcessRequest) = 17,
1574        GetProcessSnapshot(GetProcessSnapshotRequest) = 18,
1575        FindListener(FindListenerRequest) = 19,
1576        FindBoundUdp(FindBoundUdpRequest) = 20,
1577        GetSignalState(GetSignalStateRequest) = 21,
1578        GetZombieTimerCount(GetZombieTimerCountRequest) = 22,
1579        HostFilesystemCall(HostFilesystemCallRequest) = 23,
1580        PersistenceLoad(PersistenceLoadRequest) = 24,
1581        PersistenceFlush(PersistenceFlushRequest) = 25,
1582        VmFetch(VmFetchRequest) = 26,
1583        Ext(ExtEnvelope) = 27,
1584        GuestKernelCall(GuestKernelCallRequest) = 28,
1585        ResizePty(ResizePtyRequest) = 29,
1586        GetResourceSnapshot(GetResourceSnapshotRequest) = 30,
1587        LinkPackage(LinkPackageRequest) = 31,
1588    }
1589);
1590
1591impl_bare_newtype_union_enum!(
1592    ResponsePayload,
1593    JsonResponsePayload,
1594    #[serde(tag = "type", rename_all = "snake_case")]
1595    {
1596        Authenticated(AuthenticatedResponse) = 0,
1597        SessionOpened(SessionOpenedResponse) = 1,
1598        VmCreated(VmCreatedResponse) = 2,
1599        VmDisposed(VmDisposedResponse) = 3,
1600        RootFilesystemBootstrapped(RootFilesystemBootstrappedResponse) = 4,
1601        VmConfigured(VmConfiguredResponse) = 5,
1602        HostCallbacksRegistered(HostCallbacksRegisteredResponse) = 6,
1603        LayerCreated(LayerCreatedResponse) = 7,
1604        LayerSealed(LayerSealedResponse) = 8,
1605        SnapshotImported(SnapshotImportedResponse) = 9,
1606        SnapshotExported(SnapshotExportedResponse) = 10,
1607        OverlayCreated(OverlayCreatedResponse) = 11,
1608        GuestFilesystemResult(GuestFilesystemResultResponse) = 12,
1609        RootFilesystemSnapshot(RootFilesystemSnapshotResponse) = 13,
1610        ProcessStarted(ProcessStartedResponse) = 14,
1611        StdinWritten(StdinWrittenResponse) = 15,
1612        StdinClosed(StdinClosedResponse) = 16,
1613        ProcessKilled(ProcessKilledResponse) = 17,
1614        ProcessSnapshot(ProcessSnapshotResponse) = 18,
1615        ListenerSnapshot(ListenerSnapshotResponse) = 19,
1616        BoundUdpSnapshot(BoundUdpSnapshotResponse) = 20,
1617        SignalState(SignalStateResponse) = 21,
1618        ZombieTimerCount(ZombieTimerCountResponse) = 22,
1619        FilesystemResult(FilesystemResultResponse) = 23,
1620        PermissionDecision(PermissionDecisionResponse) = 24,
1621        PersistenceState(PersistenceStateResponse) = 25,
1622        PersistenceFlushed(PersistenceFlushedResponse) = 26,
1623        Rejected(RejectedResponse) = 27,
1624        VmFetchResult(VmFetchResponse) = 28,
1625        ExtResult(ExtEnvelope) = 29,
1626        GuestKernelResult(GuestKernelResultResponse) = 30,
1627        PtyResized(PtyResizedResponse) = 31,
1628        ResourceSnapshot(ResourceSnapshotResponse) = 32,
1629        PackageLinked(PackageLinkedResponse) = 33,
1630    }
1631);
1632
1633impl_bare_newtype_union_enum!(
1634    SidecarRequestPayload,
1635    JsonSidecarRequestPayload,
1636    #[serde(tag = "type", rename_all = "snake_case")]
1637    {
1638        HostCallback(HostCallbackRequest) = 0,
1639        JsBridgeCall(JsBridgeCallRequest) = 1,
1640        Ext(ExtEnvelope) = 2,
1641    }
1642);
1643
1644impl_bare_newtype_union_enum!(
1645    SidecarResponsePayload,
1646    JsonSidecarResponsePayload,
1647    #[allow(clippy::enum_variant_names)]
1648    #[serde(tag = "type", rename_all = "snake_case")]
1649    {
1650        HostCallbackResult(HostCallbackResultResponse) = 0,
1651        JsBridgeResult(JsBridgeResultResponse) = 1,
1652        ExtResult(ExtEnvelope) = 2,
1653    }
1654);
1655
1656impl_bare_newtype_union_enum!(
1657    EventPayload,
1658    JsonEventPayload,
1659    #[serde(tag = "type", rename_all = "snake_case")]
1660    {
1661        VmLifecycle(VmLifecycleEvent) = 0,
1662        ProcessOutput(ProcessOutputEvent) = 1,
1663        ProcessExited(ProcessExitedEvent) = 2,
1664        Structured(StructuredEvent) = 3,
1665        Ext(ExtEnvelope) = 4,
1666    }
1667);
1668
1669fn serialize_payload(
1670    frame: &ProtocolFrame,
1671    payload_codec: NativePayloadCodec,
1672) -> Result<Vec<u8>, ProtocolCodecError> {
1673    match payload_codec {
1674        NativePayloadCodec::Json => serde_json::to_vec(frame)
1675            .map_err(|error| ProtocolCodecError::SerializeFailure(error.to_string())),
1676        NativePayloadCodec::Bare => serde_bare::to_vec(&to_generated_protocol_frame(frame)?)
1677            .map_err(|error| ProtocolCodecError::SerializeFailure(error.to_string())),
1678    }
1679}
1680
1681fn deserialize_payload(
1682    payload: &[u8],
1683    payload_codec: NativePayloadCodec,
1684) -> Result<ProtocolFrame, ProtocolCodecError> {
1685    match payload_codec {
1686        NativePayloadCodec::Json => serde_json::from_slice(payload)
1687            .map_err(|error| ProtocolCodecError::DeserializeFailure(error.to_string())),
1688        NativePayloadCodec::Bare => {
1689            let frame: generated_protocol::ProtocolFrame = serde_bare::from_slice(payload)
1690                .map_err(|error| ProtocolCodecError::DeserializeFailure(error.to_string()))?;
1691            from_generated_protocol_frame(frame)
1692        }
1693    }
1694}
1695
1696#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1697pub enum NativePayloadCodec {
1698    Json,
1699    Bare,
1700}
1701
1702impl NativePayloadCodec {
1703    pub fn sniff(payload: &[u8]) -> Self {
1704        match payload.first() {
1705            Some(b'{') => Self::Json,
1706            _ => Self::Bare,
1707        }
1708    }
1709
1710    pub fn alternate(self) -> Self {
1711        match self {
1712            Self::Json => Self::Bare,
1713            Self::Bare => Self::Json,
1714        }
1715    }
1716}
1717
1718#[derive(Debug, Clone)]
1719pub struct NativeFrameCodec {
1720    max_frame_bytes: usize,
1721    payload_codec: NativePayloadCodec,
1722}
1723
1724impl NativeFrameCodec {
1725    pub fn new(max_frame_bytes: usize) -> Self {
1726        Self::with_payload_codec(max_frame_bytes, NativePayloadCodec::Json)
1727    }
1728
1729    pub fn with_payload_codec(max_frame_bytes: usize, payload_codec: NativePayloadCodec) -> Self {
1730        Self {
1731            max_frame_bytes,
1732            payload_codec,
1733        }
1734    }
1735
1736    pub fn max_frame_bytes(&self) -> usize {
1737        self.max_frame_bytes
1738    }
1739
1740    pub fn payload_codec(&self) -> NativePayloadCodec {
1741        self.payload_codec
1742    }
1743
1744    pub fn encode(&self, frame: &ProtocolFrame) -> Result<Vec<u8>, ProtocolCodecError> {
1745        self.encode_with_codec(frame, self.payload_codec)
1746    }
1747
1748    pub fn encode_with_codec(
1749        &self,
1750        frame: &ProtocolFrame,
1751        payload_codec: NativePayloadCodec,
1752    ) -> Result<Vec<u8>, ProtocolCodecError> {
1753        validate_frame(frame)?;
1754
1755        let payload = serialize_payload(frame, payload_codec)?;
1756        if payload.len() > self.max_frame_bytes {
1757            return Err(ProtocolCodecError::FrameTooLarge {
1758                size: payload.len(),
1759                max: self.max_frame_bytes,
1760            });
1761        }
1762
1763        let length =
1764            u32::try_from(payload.len()).map_err(|_| ProtocolCodecError::FrameTooLarge {
1765                size: payload.len(),
1766                max: u32::MAX as usize,
1767            })?;
1768
1769        let mut encoded = Vec::with_capacity(4 + payload.len());
1770        encoded.extend_from_slice(&length.to_be_bytes());
1771        encoded.extend_from_slice(&payload);
1772        Ok(encoded)
1773    }
1774
1775    pub fn decode(&self, bytes: &[u8]) -> Result<ProtocolFrame, ProtocolCodecError> {
1776        self.decode_detected(bytes).map(|(frame, _)| frame)
1777    }
1778
1779    pub fn decode_with_codec(
1780        &self,
1781        bytes: &[u8],
1782        payload_codec: NativePayloadCodec,
1783    ) -> Result<ProtocolFrame, ProtocolCodecError> {
1784        let payload = self.checked_payload(bytes)?;
1785        let frame = deserialize_payload(payload, payload_codec)?;
1786        validate_frame(&frame)?;
1787        Ok(frame)
1788    }
1789
1790    pub fn decode_detected(
1791        &self,
1792        bytes: &[u8],
1793    ) -> Result<(ProtocolFrame, NativePayloadCodec), ProtocolCodecError> {
1794        let payload = self.checked_payload(bytes)?;
1795        let primary = NativePayloadCodec::sniff(payload);
1796
1797        match deserialize_payload(payload, primary) {
1798            Ok(frame) => {
1799                validate_frame(&frame)?;
1800                Ok((frame, primary))
1801            }
1802            Err(primary_error) => {
1803                let alternate = primary.alternate();
1804                let frame = deserialize_payload(payload, alternate).map_err(|_| primary_error)?;
1805                validate_frame(&frame)?;
1806                Ok((frame, alternate))
1807            }
1808        }
1809    }
1810
1811    fn checked_payload<'a>(&self, bytes: &'a [u8]) -> Result<&'a [u8], ProtocolCodecError> {
1812        if bytes.len() < 4 {
1813            return Err(ProtocolCodecError::TruncatedFrame {
1814                actual: bytes.len(),
1815            });
1816        }
1817
1818        let declared =
1819            u32::from_be_bytes(bytes[..4].try_into().expect("length prefix is four bytes"))
1820                as usize;
1821        if declared > self.max_frame_bytes {
1822            return Err(ProtocolCodecError::FrameTooLarge {
1823                size: declared,
1824                max: self.max_frame_bytes,
1825            });
1826        }
1827
1828        let actual = bytes.len() - 4;
1829        if declared != actual {
1830            return Err(ProtocolCodecError::LengthPrefixMismatch { declared, actual });
1831        }
1832        Ok(&bytes[4..])
1833    }
1834}
1835
1836impl Default for NativeFrameCodec {
1837    fn default() -> Self {
1838        Self::new(DEFAULT_MAX_FRAME_BYTES)
1839    }
1840}
1841
1842#[derive(Debug)]
1843pub struct ResponseTracker {
1844    pending: HashMap<RequestId, PendingRequest>,
1845    completed: HashSet<RequestId>,
1846    completed_order: VecDeque<RequestId>,
1847    completed_cap: usize,
1848}
1849
1850#[derive(Debug)]
1851pub struct SidecarResponseTracker {
1852    pending: HashMap<RequestId, PendingSidecarRequest>,
1853    completed: HashSet<RequestId>,
1854    completed_order: VecDeque<RequestId>,
1855    completed_cap: usize,
1856}
1857
1858impl ResponseTracker {
1859    pub fn with_completed_cap(completed_cap: usize) -> Self {
1860        Self {
1861            pending: HashMap::new(),
1862            completed: HashSet::new(),
1863            completed_order: VecDeque::new(),
1864            completed_cap: completed_cap.max(1),
1865        }
1866    }
1867
1868    pub fn completed_count(&self) -> usize {
1869        self.completed.len()
1870    }
1871
1872    pub fn register_request(&mut self, request: &RequestFrame) -> Result<(), ResponseTrackerError> {
1873        if self.pending.contains_key(&request.request_id)
1874            || self.completed.contains(&request.request_id)
1875        {
1876            return Err(ResponseTrackerError::DuplicateRequestId {
1877                request_id: request.request_id,
1878            });
1879        }
1880
1881        self.pending.insert(
1882            request.request_id,
1883            PendingRequest {
1884                ownership: request.ownership.clone(),
1885                expected_response: request.payload.expected_response(),
1886            },
1887        );
1888        Ok(())
1889    }
1890
1891    pub fn accept_response(
1892        &mut self,
1893        response: &ResponseFrame,
1894    ) -> Result<(), ResponseTrackerError> {
1895        if self.completed.contains(&response.request_id) {
1896            return Err(ResponseTrackerError::DuplicateResponse {
1897                request_id: response.request_id,
1898            });
1899        }
1900
1901        let pending = self.pending.get(&response.request_id).ok_or(
1902            ResponseTrackerError::UnmatchedResponse {
1903                request_id: response.request_id,
1904            },
1905        )?;
1906
1907        if pending.ownership != response.ownership {
1908            return Err(ResponseTrackerError::OwnershipMismatch {
1909                request_id: response.request_id,
1910                expected: Box::new(pending.ownership.clone()),
1911                actual: Box::new(response.ownership.clone()),
1912            });
1913        }
1914
1915        if !pending.expected_response.matches(&response.payload) {
1916            return Err(ResponseTrackerError::ResponseKindMismatch {
1917                request_id: response.request_id,
1918                expected: pending.expected_response.as_str().to_string(),
1919                actual: response.payload.kind_name().to_string(),
1920            });
1921        }
1922
1923        self.pending
1924            .remove(&response.request_id)
1925            .expect("pending response should still exist after validation");
1926        self.completed.insert(response.request_id);
1927        self.completed_order.push_back(response.request_id);
1928        while self.completed.len() > self.completed_cap {
1929            if let Some(evicted) = self.completed_order.pop_front() {
1930                self.completed.remove(&evicted);
1931            }
1932        }
1933        Ok(())
1934    }
1935}
1936
1937impl Default for ResponseTracker {
1938    fn default() -> Self {
1939        Self::with_completed_cap(DEFAULT_COMPLETED_RESPONSE_CAP)
1940    }
1941}
1942
1943impl SidecarResponseTracker {
1944    pub fn with_completed_cap(completed_cap: usize) -> Self {
1945        Self {
1946            pending: HashMap::new(),
1947            completed: HashSet::new(),
1948            completed_order: VecDeque::new(),
1949            completed_cap: completed_cap.max(1),
1950        }
1951    }
1952
1953    pub fn pending_count(&self) -> usize {
1954        self.pending.len()
1955    }
1956
1957    pub fn completed_count(&self) -> usize {
1958        self.completed.len()
1959    }
1960
1961    pub fn register_request(
1962        &mut self,
1963        request: &SidecarRequestFrame,
1964    ) -> Result<(), SidecarResponseTrackerError> {
1965        if self.pending.contains_key(&request.request_id)
1966            || self.completed.contains(&request.request_id)
1967        {
1968            return Err(SidecarResponseTrackerError::DuplicateRequestId {
1969                request_id: request.request_id,
1970            });
1971        }
1972
1973        self.pending.insert(
1974            request.request_id,
1975            PendingSidecarRequest {
1976                ownership: request.ownership.clone(),
1977                expected_response: request.payload.expected_response(),
1978            },
1979        );
1980        Ok(())
1981    }
1982
1983    pub fn accept_response(
1984        &mut self,
1985        response: &SidecarResponseFrame,
1986    ) -> Result<(), SidecarResponseTrackerError> {
1987        if self.completed.contains(&response.request_id) {
1988            return Err(SidecarResponseTrackerError::DuplicateResponse {
1989                request_id: response.request_id,
1990            });
1991        }
1992
1993        let pending = self.pending.get(&response.request_id).ok_or(
1994            SidecarResponseTrackerError::UnmatchedResponse {
1995                request_id: response.request_id,
1996            },
1997        )?;
1998
1999        if pending.ownership != response.ownership {
2000            return Err(SidecarResponseTrackerError::OwnershipMismatch {
2001                request_id: response.request_id,
2002                expected: Box::new(pending.ownership.clone()),
2003                actual: Box::new(response.ownership.clone()),
2004            });
2005        }
2006
2007        if !pending.expected_response.matches(&response.payload) {
2008            return Err(SidecarResponseTrackerError::ResponseKindMismatch {
2009                request_id: response.request_id,
2010                expected: pending.expected_response.as_str().to_string(),
2011                actual: response.payload.kind_name().to_string(),
2012            });
2013        }
2014
2015        self.pending
2016            .remove(&response.request_id)
2017            .expect("pending sidecar response should still exist after validation");
2018        self.completed.insert(response.request_id);
2019        self.completed_order.push_back(response.request_id);
2020        while self.completed.len() > self.completed_cap {
2021            if let Some(evicted) = self.completed_order.pop_front() {
2022                self.completed.remove(&evicted);
2023            }
2024        }
2025        Ok(())
2026    }
2027}
2028
2029impl Default for SidecarResponseTracker {
2030    fn default() -> Self {
2031        Self::with_completed_cap(DEFAULT_COMPLETED_RESPONSE_CAP)
2032    }
2033}
2034
2035#[derive(Debug, Clone, PartialEq, Eq)]
2036pub enum ResponseTrackerError {
2037    DuplicateRequestId {
2038        request_id: RequestId,
2039    },
2040    UnmatchedResponse {
2041        request_id: RequestId,
2042    },
2043    DuplicateResponse {
2044        request_id: RequestId,
2045    },
2046    OwnershipMismatch {
2047        request_id: RequestId,
2048        expected: Box<OwnershipScope>,
2049        actual: Box<OwnershipScope>,
2050    },
2051    ResponseKindMismatch {
2052        request_id: RequestId,
2053        expected: String,
2054        actual: String,
2055    },
2056}
2057
2058impl fmt::Display for ResponseTrackerError {
2059    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2060        match self {
2061            Self::DuplicateRequestId { request_id } => {
2062                write!(f, "request id {request_id} is already tracked")
2063            }
2064            Self::UnmatchedResponse { request_id } => {
2065                write!(
2066                    f,
2067                    "response id {request_id} does not match any pending request"
2068                )
2069            }
2070            Self::DuplicateResponse { request_id } => {
2071                write!(f, "response id {request_id} has already been completed")
2072            }
2073            Self::OwnershipMismatch {
2074                request_id,
2075                expected,
2076                actual,
2077            } => write!(
2078                f,
2079                "response id {request_id} used ownership {:?}, expected {:?}",
2080                actual, expected
2081            ),
2082            Self::ResponseKindMismatch {
2083                request_id,
2084                expected,
2085                actual,
2086            } => write!(
2087                f,
2088                "response id {request_id} carried {actual}, expected {expected}",
2089            ),
2090        }
2091    }
2092}
2093
2094impl Error for ResponseTrackerError {}
2095
2096#[derive(Debug, Clone, PartialEq, Eq)]
2097pub enum SidecarResponseTrackerError {
2098    DuplicateRequestId {
2099        request_id: RequestId,
2100    },
2101    UnmatchedResponse {
2102        request_id: RequestId,
2103    },
2104    DuplicateResponse {
2105        request_id: RequestId,
2106    },
2107    OwnershipMismatch {
2108        request_id: RequestId,
2109        expected: Box<OwnershipScope>,
2110        actual: Box<OwnershipScope>,
2111    },
2112    ResponseKindMismatch {
2113        request_id: RequestId,
2114        expected: String,
2115        actual: String,
2116    },
2117}
2118
2119impl fmt::Display for SidecarResponseTrackerError {
2120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2121        match self {
2122            Self::DuplicateRequestId { request_id } => {
2123                write!(f, "sidecar request id {request_id} is already tracked")
2124            }
2125            Self::UnmatchedResponse { request_id } => {
2126                write!(
2127                    f,
2128                    "sidecar response id {request_id} does not match any pending request"
2129                )
2130            }
2131            Self::DuplicateResponse { request_id } => {
2132                write!(
2133                    f,
2134                    "sidecar response id {request_id} has already been completed"
2135                )
2136            }
2137            Self::OwnershipMismatch {
2138                request_id,
2139                expected,
2140                actual,
2141            } => write!(
2142                f,
2143                "sidecar response id {request_id} used ownership {:?}, expected {:?}",
2144                actual, expected
2145            ),
2146            Self::ResponseKindMismatch {
2147                request_id,
2148                expected,
2149                actual,
2150            } => write!(
2151                f,
2152                "sidecar response id {request_id} carried {actual}, expected {expected}",
2153            ),
2154        }
2155    }
2156}
2157
2158impl Error for SidecarResponseTrackerError {}
2159
2160#[derive(Debug, Clone, PartialEq, Eq)]
2161struct PendingRequest {
2162    ownership: OwnershipScope,
2163    expected_response: ExpectedResponseKind,
2164}
2165
2166#[derive(Debug, Clone, PartialEq, Eq)]
2167struct PendingSidecarRequest {
2168    ownership: OwnershipScope,
2169    expected_response: ExpectedSidecarResponseKind,
2170}
2171
2172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2173enum ExpectedResponseKind {
2174    Authenticated,
2175    SessionOpened,
2176    VmCreated,
2177    VmDisposed,
2178    RootFilesystemBootstrapped,
2179    VmConfigured,
2180    HostCallbacksRegistered,
2181    LayerCreated,
2182    LayerSealed,
2183    SnapshotImported,
2184    SnapshotExported,
2185    OverlayCreated,
2186    GuestFilesystemResult,
2187    RootFilesystemSnapshot,
2188    ProcessStarted,
2189    StdinWritten,
2190    StdinClosed,
2191    ProcessKilled,
2192    ProcessSnapshot,
2193    ResourceSnapshot,
2194    ListenerSnapshot,
2195    BoundUdpSnapshot,
2196    VmFetchResult,
2197    SignalState,
2198    ZombieTimerCount,
2199    FilesystemResult,
2200    // `PermissionDecision` is a sidecar-initiated callback response, so no host
2201    // request maps to it in `expected_response_kind`; kept for protocol symmetry.
2202    #[allow(dead_code)]
2203    PermissionDecision,
2204    PersistenceState,
2205    PersistenceFlushed,
2206    ExtResult,
2207    GuestKernelResult,
2208    PtyResized,
2209    PackageLinked,
2210}
2211
2212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2213enum ExpectedSidecarResponseKind {
2214    HostCallback,
2215    JsBridge,
2216    Ext,
2217}
2218
2219impl ExpectedResponseKind {
2220    fn as_str(self) -> &'static str {
2221        match self {
2222            Self::Authenticated => "authenticated",
2223            Self::SessionOpened => "session_opened",
2224            Self::VmCreated => "vm_created",
2225            Self::VmDisposed => "vm_disposed",
2226            Self::RootFilesystemBootstrapped => "root_filesystem_bootstrapped",
2227            Self::VmConfigured => "vm_configured",
2228            Self::HostCallbacksRegistered => "host_callbacks_registered",
2229            Self::LayerCreated => "layer_created",
2230            Self::LayerSealed => "layer_sealed",
2231            Self::SnapshotImported => "snapshot_imported",
2232            Self::SnapshotExported => "snapshot_exported",
2233            Self::OverlayCreated => "overlay_created",
2234            Self::GuestFilesystemResult => "guest_filesystem_result",
2235            Self::RootFilesystemSnapshot => "root_filesystem_snapshot",
2236            Self::ProcessStarted => "process_started",
2237            Self::StdinWritten => "stdin_written",
2238            Self::StdinClosed => "stdin_closed",
2239            Self::ProcessKilled => "process_killed",
2240            Self::ProcessSnapshot => "process_snapshot",
2241            Self::ResourceSnapshot => "resource_snapshot",
2242            Self::ListenerSnapshot => "listener_snapshot",
2243            Self::BoundUdpSnapshot => "bound_udp_snapshot",
2244            Self::VmFetchResult => "vm_fetch_result",
2245            Self::SignalState => "signal_state",
2246            Self::ZombieTimerCount => "zombie_timer_count",
2247            Self::FilesystemResult => "filesystem_result",
2248            Self::PermissionDecision => "permission_decision",
2249            Self::PersistenceState => "persistence_state",
2250            Self::PersistenceFlushed => "persistence_flushed",
2251            Self::ExtResult => "ext_result",
2252            Self::GuestKernelResult => "guest_kernel_result",
2253            Self::PtyResized => "pty_resized",
2254            Self::PackageLinked => "package_linked",
2255        }
2256    }
2257
2258    fn matches(self, payload: &ResponsePayload) -> bool {
2259        match payload {
2260            ResponsePayload::Rejected(_) => true,
2261            _ => payload.kind_name() == self.as_str(),
2262        }
2263    }
2264}
2265
2266impl ExpectedSidecarResponseKind {
2267    fn as_str(self) -> &'static str {
2268        match self {
2269            Self::HostCallback => "host_callback_result",
2270            Self::JsBridge => "js_bridge_result",
2271            Self::Ext => "ext_result",
2272        }
2273    }
2274
2275    fn matches(self, payload: &SidecarResponsePayload) -> bool {
2276        payload.kind_name() == self.as_str()
2277    }
2278}
2279
2280impl RequestPayload {
2281    fn ownership_requirement(&self) -> OwnershipRequirement {
2282        match self {
2283            Self::Authenticate(_) | Self::OpenSession(_) => OwnershipRequirement::Connection,
2284            Self::CreateVm(_) | Self::PersistenceLoad(_) | Self::PersistenceFlush(_) => {
2285                OwnershipRequirement::Session
2286            }
2287            Self::DisposeVm(_)
2288            | Self::BootstrapRootFilesystem(_)
2289            | Self::ConfigureVm(_)
2290            | Self::RegisterHostCallbacks(_)
2291            | Self::CreateLayer(_)
2292            | Self::SealLayer(_)
2293            | Self::ImportSnapshot(_)
2294            | Self::ExportSnapshot(_)
2295            | Self::CreateOverlay(_)
2296            | Self::GuestFilesystemCall(_)
2297            | Self::SnapshotRootFilesystem(_)
2298            | Self::Execute(_)
2299            | Self::WriteStdin(_)
2300            | Self::CloseStdin(_)
2301            | Self::KillProcess(_)
2302            | Self::GetProcessSnapshot(_)
2303            | Self::GetResourceSnapshot(_)
2304            | Self::FindListener(_)
2305            | Self::FindBoundUdp(_)
2306            | Self::VmFetch(_)
2307            | Self::GetSignalState(_)
2308            | Self::GetZombieTimerCount(_)
2309            | Self::GuestKernelCall(_)
2310            | Self::ResizePty(_)
2311            | Self::LinkPackage(_)
2312            | Self::HostFilesystemCall(_) => OwnershipRequirement::Vm,
2313            Self::Ext(_) => OwnershipRequirement::Any,
2314        }
2315    }
2316
2317    fn expected_response(&self) -> ExpectedResponseKind {
2318        match self {
2319            Self::Authenticate(_) => ExpectedResponseKind::Authenticated,
2320            Self::OpenSession(_) => ExpectedResponseKind::SessionOpened,
2321            Self::CreateVm(_) => ExpectedResponseKind::VmCreated,
2322            Self::DisposeVm(_) => ExpectedResponseKind::VmDisposed,
2323            Self::BootstrapRootFilesystem(_) => ExpectedResponseKind::RootFilesystemBootstrapped,
2324            Self::ConfigureVm(_) => ExpectedResponseKind::VmConfigured,
2325            Self::RegisterHostCallbacks(_) => ExpectedResponseKind::HostCallbacksRegistered,
2326            Self::CreateLayer(_) => ExpectedResponseKind::LayerCreated,
2327            Self::SealLayer(_) => ExpectedResponseKind::LayerSealed,
2328            Self::ImportSnapshot(_) => ExpectedResponseKind::SnapshotImported,
2329            Self::ExportSnapshot(_) => ExpectedResponseKind::SnapshotExported,
2330            Self::CreateOverlay(_) => ExpectedResponseKind::OverlayCreated,
2331            Self::GuestFilesystemCall(_) => ExpectedResponseKind::GuestFilesystemResult,
2332            Self::SnapshotRootFilesystem(_) => ExpectedResponseKind::RootFilesystemSnapshot,
2333            Self::Execute(_) => ExpectedResponseKind::ProcessStarted,
2334            Self::WriteStdin(_) => ExpectedResponseKind::StdinWritten,
2335            Self::CloseStdin(_) => ExpectedResponseKind::StdinClosed,
2336            Self::KillProcess(_) => ExpectedResponseKind::ProcessKilled,
2337            Self::GetProcessSnapshot(_) => ExpectedResponseKind::ProcessSnapshot,
2338            Self::GetResourceSnapshot(_) => ExpectedResponseKind::ResourceSnapshot,
2339            Self::FindListener(_) => ExpectedResponseKind::ListenerSnapshot,
2340            Self::FindBoundUdp(_) => ExpectedResponseKind::BoundUdpSnapshot,
2341            Self::VmFetch(_) => ExpectedResponseKind::VmFetchResult,
2342            Self::GetSignalState(_) => ExpectedResponseKind::SignalState,
2343            Self::GetZombieTimerCount(_) => ExpectedResponseKind::ZombieTimerCount,
2344            Self::HostFilesystemCall(_) => ExpectedResponseKind::FilesystemResult,
2345            Self::PersistenceLoad(_) => ExpectedResponseKind::PersistenceState,
2346            Self::PersistenceFlush(_) => ExpectedResponseKind::PersistenceFlushed,
2347            Self::Ext(_) => ExpectedResponseKind::ExtResult,
2348            Self::GuestKernelCall(_) => ExpectedResponseKind::GuestKernelResult,
2349            Self::ResizePty(_) => ExpectedResponseKind::PtyResized,
2350            Self::LinkPackage(_) => ExpectedResponseKind::PackageLinked,
2351        }
2352    }
2353}
2354
2355impl SidecarRequestPayload {
2356    fn ownership_requirement(&self) -> OwnershipRequirement {
2357        OwnershipRequirement::Vm
2358    }
2359
2360    fn expected_response(&self) -> ExpectedSidecarResponseKind {
2361        match self {
2362            Self::HostCallback(_) => ExpectedSidecarResponseKind::HostCallback,
2363            Self::JsBridgeCall(_) => ExpectedSidecarResponseKind::JsBridge,
2364            Self::Ext(_) => ExpectedSidecarResponseKind::Ext,
2365        }
2366    }
2367}
2368
2369impl ResponsePayload {
2370    fn ownership_requirement(&self) -> OwnershipRequirement {
2371        match self {
2372            Self::Authenticated(_) | Self::SessionOpened(_) => OwnershipRequirement::Connection,
2373            Self::VmCreated(_) | Self::PersistenceState(_) | Self::PersistenceFlushed(_) => {
2374                OwnershipRequirement::Session
2375            }
2376            Self::Rejected(_) => OwnershipRequirement::Any,
2377            Self::VmDisposed(_)
2378            | Self::RootFilesystemBootstrapped(_)
2379            | Self::VmConfigured(_)
2380            | Self::HostCallbacksRegistered(_)
2381            | Self::LayerCreated(_)
2382            | Self::LayerSealed(_)
2383            | Self::SnapshotImported(_)
2384            | Self::SnapshotExported(_)
2385            | Self::OverlayCreated(_)
2386            | Self::GuestFilesystemResult(_)
2387            | Self::RootFilesystemSnapshot(_)
2388            | Self::ProcessStarted(_)
2389            | Self::StdinWritten(_)
2390            | Self::StdinClosed(_)
2391            | Self::ProcessKilled(_)
2392            | Self::ProcessSnapshot(_)
2393            | Self::ResourceSnapshot(_)
2394            | Self::ListenerSnapshot(_)
2395            | Self::BoundUdpSnapshot(_)
2396            | Self::VmFetchResult(_)
2397            | Self::SignalState(_)
2398            | Self::ZombieTimerCount(_)
2399            | Self::FilesystemResult(_)
2400            | Self::PermissionDecision(_)
2401            | Self::GuestKernelResult(_)
2402            | Self::PtyResized(_)
2403            | Self::PackageLinked(_) => OwnershipRequirement::Vm,
2404            Self::ExtResult(_) => OwnershipRequirement::Any,
2405        }
2406    }
2407
2408    fn kind_name(&self) -> &'static str {
2409        match self {
2410            Self::Authenticated(_) => "authenticated",
2411            Self::SessionOpened(_) => "session_opened",
2412            Self::VmCreated(_) => "vm_created",
2413            Self::VmDisposed(_) => "vm_disposed",
2414            Self::RootFilesystemBootstrapped(_) => "root_filesystem_bootstrapped",
2415            Self::VmConfigured(_) => "vm_configured",
2416            Self::HostCallbacksRegistered(_) => "host_callbacks_registered",
2417            Self::LayerCreated(_) => "layer_created",
2418            Self::LayerSealed(_) => "layer_sealed",
2419            Self::SnapshotImported(_) => "snapshot_imported",
2420            Self::SnapshotExported(_) => "snapshot_exported",
2421            Self::OverlayCreated(_) => "overlay_created",
2422            Self::GuestFilesystemResult(_) => "guest_filesystem_result",
2423            Self::RootFilesystemSnapshot(_) => "root_filesystem_snapshot",
2424            Self::ProcessStarted(_) => "process_started",
2425            Self::StdinWritten(_) => "stdin_written",
2426            Self::StdinClosed(_) => "stdin_closed",
2427            Self::ProcessKilled(_) => "process_killed",
2428            Self::ProcessSnapshot(_) => "process_snapshot",
2429            Self::ResourceSnapshot(_) => "resource_snapshot",
2430            Self::ListenerSnapshot(_) => "listener_snapshot",
2431            Self::BoundUdpSnapshot(_) => "bound_udp_snapshot",
2432            Self::VmFetchResult(_) => "vm_fetch_result",
2433            Self::SignalState(_) => "signal_state",
2434            Self::ZombieTimerCount(_) => "zombie_timer_count",
2435            Self::FilesystemResult(_) => "filesystem_result",
2436            Self::PermissionDecision(_) => "permission_decision",
2437            Self::PersistenceState(_) => "persistence_state",
2438            Self::PersistenceFlushed(_) => "persistence_flushed",
2439            Self::Rejected(_) => "rejected",
2440            Self::ExtResult(_) => "ext_result",
2441            Self::GuestKernelResult(_) => "guest_kernel_result",
2442            Self::PtyResized(_) => "pty_resized",
2443            Self::PackageLinked(_) => "package_linked",
2444        }
2445    }
2446}
2447
2448impl SidecarResponsePayload {
2449    fn ownership_requirement(&self) -> OwnershipRequirement {
2450        OwnershipRequirement::Vm
2451    }
2452
2453    fn kind_name(&self) -> &'static str {
2454        match self {
2455            Self::HostCallbackResult(_) => "host_callback_result",
2456            Self::JsBridgeResult(_) => "js_bridge_result",
2457            Self::ExtResult(_) => "ext_result",
2458        }
2459    }
2460}
2461
2462impl EventPayload {
2463    fn ownership_requirement(&self) -> OwnershipRequirement {
2464        match self {
2465            Self::Structured(_) => OwnershipRequirement::SessionOrVm,
2466            Self::VmLifecycle(_) | Self::ProcessOutput(_) | Self::ProcessExited(_) => {
2467                OwnershipRequirement::Vm
2468            }
2469            Self::Ext(_) => OwnershipRequirement::Any,
2470        }
2471    }
2472}
2473
2474pub fn validate_frame(frame: &ProtocolFrame) -> Result<(), ProtocolCodecError> {
2475    match frame {
2476        ProtocolFrame::Request(request) => validate_request(request),
2477        ProtocolFrame::Response(response) => validate_response(response),
2478        ProtocolFrame::Event(event) => validate_event(event),
2479        ProtocolFrame::SidecarRequest(request) => validate_sidecar_request(request),
2480        ProtocolFrame::SidecarResponse(response) => validate_sidecar_response(response),
2481    }
2482}
2483
2484fn validate_request(request: &RequestFrame) -> Result<(), ProtocolCodecError> {
2485    validate_schema(&request.schema)?;
2486    validate_request_id_direction(request.request_id, RequestDirection::Host)?;
2487
2488    validate_ownership(&request.ownership)?;
2489    validate_requirement(request.payload.ownership_requirement(), &request.ownership)?;
2490    if let RequestPayload::Authenticate(authenticate) = &request.payload {
2491        if authenticate.auth_token.is_empty() {
2492            return Err(ProtocolCodecError::EmptyAuthToken);
2493        }
2494    }
2495
2496    Ok(())
2497}
2498
2499fn validate_response(response: &ResponseFrame) -> Result<(), ProtocolCodecError> {
2500    validate_schema(&response.schema)?;
2501    validate_request_id_direction(response.request_id, RequestDirection::Host)?;
2502
2503    validate_ownership(&response.ownership)?;
2504    validate_requirement(
2505        response.payload.ownership_requirement(),
2506        &response.ownership,
2507    )?;
2508    Ok(())
2509}
2510
2511fn validate_sidecar_request(request: &SidecarRequestFrame) -> Result<(), ProtocolCodecError> {
2512    validate_schema(&request.schema)?;
2513    validate_request_id_direction(request.request_id, RequestDirection::Sidecar)?;
2514    validate_ownership(&request.ownership)?;
2515    validate_requirement(request.payload.ownership_requirement(), &request.ownership)?;
2516    Ok(())
2517}
2518
2519fn validate_sidecar_response(response: &SidecarResponseFrame) -> Result<(), ProtocolCodecError> {
2520    validate_schema(&response.schema)?;
2521    validate_request_id_direction(response.request_id, RequestDirection::Sidecar)?;
2522    validate_ownership(&response.ownership)?;
2523    validate_requirement(
2524        response.payload.ownership_requirement(),
2525        &response.ownership,
2526    )?;
2527    Ok(())
2528}
2529
2530fn validate_event(event: &EventFrame) -> Result<(), ProtocolCodecError> {
2531    validate_schema(&event.schema)?;
2532    validate_ownership(&event.ownership)?;
2533    validate_requirement(event.payload.ownership_requirement(), &event.ownership)?;
2534    Ok(())
2535}
2536
2537fn validate_schema(schema: &ProtocolSchema) -> Result<(), ProtocolCodecError> {
2538    if schema.name != PROTOCOL_NAME || schema.version != PROTOCOL_VERSION {
2539        return Err(ProtocolCodecError::UnsupportedSchema {
2540            name: schema.name.clone(),
2541            version: schema.version,
2542        });
2543    }
2544
2545    Ok(())
2546}
2547
2548fn validate_ownership(ownership: &OwnershipScope) -> Result<(), ProtocolCodecError> {
2549    match ownership {
2550        OwnershipScope::ConnectionOwnership(inner) => {
2551            validate_non_empty("connection_id", &inner.connection_id)
2552        }
2553        OwnershipScope::SessionOwnership(inner) => {
2554            validate_non_empty("connection_id", &inner.connection_id)?;
2555            validate_non_empty("session_id", &inner.session_id)
2556        }
2557        OwnershipScope::VmOwnership(inner) => {
2558            validate_non_empty("connection_id", &inner.connection_id)?;
2559            validate_non_empty("session_id", &inner.session_id)?;
2560            validate_non_empty("vm_id", &inner.vm_id)
2561        }
2562    }
2563}
2564
2565fn validate_non_empty(field: &'static str, value: &str) -> Result<(), ProtocolCodecError> {
2566    if value.is_empty() {
2567        return Err(ProtocolCodecError::EmptyOwnershipField { field });
2568    }
2569
2570    Ok(())
2571}
2572
2573fn validate_request_id_direction(
2574    request_id: RequestId,
2575    direction: RequestDirection,
2576) -> Result<(), ProtocolCodecError> {
2577    if request_id == 0 {
2578        return Err(ProtocolCodecError::InvalidRequestId);
2579    }
2580
2581    let matches_direction = match direction {
2582        RequestDirection::Host => request_id > 0,
2583        RequestDirection::Sidecar => request_id < 0,
2584    };
2585    if matches_direction {
2586        Ok(())
2587    } else {
2588        Err(ProtocolCodecError::InvalidRequestDirection {
2589            request_id,
2590            expected: direction,
2591        })
2592    }
2593}
2594
2595fn validate_requirement(
2596    required: OwnershipRequirement,
2597    ownership: &OwnershipScope,
2598) -> Result<(), ProtocolCodecError> {
2599    let actual = match ownership {
2600        OwnershipScope::ConnectionOwnership(..) => OwnershipRequirement::Connection,
2601        OwnershipScope::SessionOwnership(..) => OwnershipRequirement::Session,
2602        OwnershipScope::VmOwnership(..) => OwnershipRequirement::Vm,
2603    };
2604
2605    let valid = match required {
2606        OwnershipRequirement::Any => true,
2607        OwnershipRequirement::Connection => {
2608            matches!(ownership, OwnershipScope::ConnectionOwnership(..))
2609        }
2610        OwnershipRequirement::Session => matches!(ownership, OwnershipScope::SessionOwnership(..)),
2611        OwnershipRequirement::Vm => matches!(ownership, OwnershipScope::VmOwnership(..)),
2612        OwnershipRequirement::SessionOrVm => {
2613            matches!(
2614                ownership,
2615                OwnershipScope::SessionOwnership(..) | OwnershipScope::VmOwnership(..)
2616            )
2617        }
2618    };
2619
2620    if valid {
2621        Ok(())
2622    } else {
2623        Err(ProtocolCodecError::InvalidOwnershipScope { required, actual })
2624    }
2625}
2626
2627// ---------------------------------------------------------------------------
2628// JavaScript sync-RPC request types (deserialized from guest Node.js processes)
2629// ---------------------------------------------------------------------------
2630
2631#[derive(Debug, Deserialize, Default)]
2632pub struct JavascriptChildProcessSpawnOptions {
2633    #[serde(default)]
2634    pub cwd: Option<String>,
2635    #[serde(default)]
2636    pub env: BTreeMap<String, String>,
2637    #[serde(rename = "internalBootstrapEnv", default)]
2638    pub internal_bootstrap_env: BTreeMap<String, String>,
2639    #[serde(default)]
2640    pub input: Option<Value>,
2641    #[serde(default)]
2642    pub shell: bool,
2643    #[serde(default)]
2644    pub detached: bool,
2645    #[serde(default)]
2646    pub stdio: Vec<String>,
2647    #[serde(default)]
2648    pub timeout: Option<u64>,
2649    #[serde(rename = "killSignal", default)]
2650    pub kill_signal: Option<String>,
2651}
2652
2653#[derive(Debug, Deserialize)]
2654pub struct JavascriptChildProcessSpawnRequest {
2655    pub command: String,
2656    #[serde(default)]
2657    pub args: Vec<String>,
2658    #[serde(default)]
2659    pub options: JavascriptChildProcessSpawnOptions,
2660}
2661
2662#[derive(Debug, Deserialize)]
2663pub struct JavascriptNetConnectRequest {
2664    #[serde(default)]
2665    pub host: Option<String>,
2666    #[serde(default)]
2667    pub port: Option<u16>,
2668    #[serde(default)]
2669    pub path: Option<String>,
2670    #[serde(rename = "localAddress", default)]
2671    pub local_address: Option<String>,
2672    #[serde(rename = "localPort", default)]
2673    pub local_port: Option<u16>,
2674    #[serde(rename = "localReservation", default)]
2675    pub local_reservation: Option<String>,
2676}
2677
2678#[derive(Debug, Deserialize)]
2679pub struct JavascriptNetReserveTcpPortRequest {
2680    #[serde(default)]
2681    pub host: Option<String>,
2682    #[serde(default)]
2683    pub port: Option<u16>,
2684}
2685
2686#[derive(Debug, Deserialize)]
2687pub struct JavascriptNetListenRequest {
2688    #[serde(default)]
2689    pub host: Option<String>,
2690    #[serde(default)]
2691    pub port: Option<u16>,
2692    #[serde(default)]
2693    pub path: Option<String>,
2694    #[serde(default)]
2695    pub backlog: Option<u32>,
2696    #[serde(rename = "localReservation", default)]
2697    pub local_reservation: Option<String>,
2698}
2699
2700#[derive(Debug, Deserialize)]
2701pub struct JavascriptDgramCreateSocketRequest {
2702    #[serde(rename = "type")]
2703    pub socket_type: String,
2704}
2705
2706#[derive(Debug, Deserialize)]
2707pub struct JavascriptDgramBindRequest {
2708    #[serde(default)]
2709    pub address: Option<String>,
2710    #[serde(default)]
2711    pub port: u16,
2712}
2713
2714#[derive(Debug, Deserialize)]
2715pub struct JavascriptDgramSendRequest {
2716    #[serde(default)]
2717    pub address: Option<String>,
2718    pub port: u16,
2719}
2720
2721#[derive(Debug, Deserialize)]
2722pub struct JavascriptDnsLookupRequest {
2723    pub hostname: String,
2724    #[serde(default)]
2725    pub family: Option<u8>,
2726}
2727
2728#[derive(Debug, Deserialize)]
2729pub struct JavascriptDnsResolveRequest {
2730    pub hostname: String,
2731    #[serde(default)]
2732    pub rrtype: Option<String>,
2733}