Skip to content

Latest commit

 

History

History
803 lines (697 loc) · 85.8 KB

File metadata and controls

803 lines (697 loc) · 85.8 KB

Daemon API reference

Diataxis category: reference.

This document is the schema-style reference for the Unix-domain wire protocol between the d2b CLI, d2bd, and the privileged broker. The prose in this file is hand-maintained. The schema blocks bounded by AUTO-GENERATED markers are regenerated by cargo xtask gen-daemon-api from packages/d2b-contracts/src/ and are therefore the implementation oracle for the IPC types.

Transport

The daemon uses non-abstract Unix-domain SOCK_SEQPACKET sockets for both public and private traffic:

  • public socket: /run/d2b/public.sock
  • broker socket: /run/d2b/priv.sock

Each application-level frame is:

  1. a 4-byte little-endian unsigned length prefix;
  2. followed immediately by one UTF-8 JSON document.

The maximum accepted frame size is 1 MiB. Oversized frames are rejected before JSON deserialization so callers get a typed failure instead of partial reads or truncated documents.

SOCK_SEQPACKET already preserves message boundaries, but the protocol keeps the explicit length prefix so the wire format stays identical if a future transport ever needs stream framing. There is never more than one request or one response inside a frame.

Transport-neutral in v2. The local Unix socket at /run/d2b/public.sock is one transport binding for the D2b daemon API. A relay-backed or remote daemon-access binding, when configured, carries daemon API requests only — it does not expose the broker socket (/run/d2b/priv.sock) and does not tunnel raw guest ttRPC traffic. Relay credentials used to authenticate a remote daemon-access session are node-management credentials, not realm or provider workload credentials. There is no implicit "remote caller is Admin" mapping: relay identity is never resolved to d2b-admin; the SO_PEERCRED-based admin/launcher gate remains the sole authn mechanism for the local Unix binding, and any non-local binding requires explicit principal mapping against a daemon-access trust anchor. Realm and provider workload credentials are held inside a gateway guest VM and never transit the daemon API or broker paths.

Handshake

Every new connection begins with a Hello message. The client presents:

  • a SemverRange describing the versions it can speak;
  • a list of FeatureFlag values it knows how to use.

The server replies with either:

  • HelloOk — version negotiation succeeded, and the server selected one concrete protocol version plus a capability set; or
  • HelloRejected — the peer is local and authenticated, but version or capability negotiation failed before the first command payload.

Downgrades are allowed only when the selected version stays inside the client's explicit SemverRange. There is no silent downgrade to a server-chosen version outside that range.

Unknown feature flags are ignored for forward compatibility. Unknown fields inside a known request or response type are rejected (deny_unknown_fields) because the documented wire enums are a closed compatibility surface.

Handshake and negotiation types

Type Kind Rust definition Shape
FeatureFlag struct FeatureFlag empty struct
GuestCapability enum GuestCapability Health; Capabilities; ExecAttached; ExecDetached; ExecTty; ExecLogs; TtyResize; Signals; ReadGuestFile; UsbipImport; ShellAttached; ShellManagement; ShellForceAttach; UsbipStatus; SystemActivation; AudioStatus; AudioSet
Hello struct Hello struct { client_version: SemverRange; supported_features: Vec<FeatureFlag> }
HelloOk struct HelloOk struct { server_version: Version; selected_version: Version; capabilities: Vec<FeatureFlag> }
HelloRejected struct HelloRejected struct { reason: HelloRejectedReason }
HelloRejectedReason enum HelloRejectedReason VersionMismatch; CapabilityNegotiationFailed; InternalError
HelloRequest struct HelloRequest struct { client_version: String; supported_features: Vec<String> }
HelloRequest struct HelloRequest struct { metadata: GuestRequestMetadata; host_nonce: GuestNonce; transcript_version: u32 }
HelloResponse struct HelloResponse struct { server_version: String; selected_version: String; capabilities: Vec<String> }
HelloResponse struct HelloResponse struct { guest_nonce: GuestNonce; guest_boot_id: GuestBootId; protocol_version: u32 }
KnownFeatureFlag enum KnownFeatureFlag TypedErrors; ManifestV04; StatusCheckBridges; ExportBrokerAudit; ConfiguredLaunchV1; UnsafeLocalProviderV1; UnsafeLocalShellV1

Public socket

/run/d2b/public.sock is the operator-facing socket.

The feature-negotiated workload frame carries a WorkloadOp body and returns workloadResponse. List and Status expose provider-neutral, argv-free metadata. LauncherExec accepts only canonical target, item id, and operation id. The daemon reloads and hash-verifies the trusted bundle, cross-checks public item metadata against its private configured item, and then routes to guest-control or the exact requester-UID unsafe-local helper. Remote/relay principals, UID fields, argv, environment, cwd, proxy paths, process ids, unit names, and fallback transports are not accepted.

The existing protocol-v3 shell frame is provider-neutral without changing its serde shape: the historical vm field carries either a local VM name or a canonical target. d2bd resolves canonical and bare targets to local VM, unsafe-local, or a typed capability refusal. Unsafe-local policy comes only from the hash-verified private workload artifact. Attach dispatches a private helper v2 request for the exact authenticated peer uid, validates one connected terminal fd, generates a bounded opaque public session handle, and multiplexes terminal-v1 operations over that fd. List/detach/kill use correlated helper management responses. Public requests carry no policy, uid, argv, environment, cwd, path, operation id, or provider override.

All shell operations require the local admin role before target resolution or helper/guest contact. unsafe-local-shell-v1 is negotiated independently of guest shell support; a client that does not negotiate it cannot reach the unsafe-local route. Local VM shell behavior and protocol version 3 remain unchanged.

Local-VM launcher execution uses a deterministic opaque guest exec id derived from the local peer uid and public operation identity. Guestd's durable detached record is the restart-stable idempotency authority; the same id and argv hash replay the existing exec, while a hash mismatch is rejected.

Daemon audit records the authenticated peer_uid and public operation_id on each configured-launch outcome. Local-VM events also carry the opaque guest exec_id and emit the standard detached-create event with that id, allowing the launch request and guest execution to be correlated without recording argv, environment, cwd, or output.

  • transport: non-abstract Unix-domain SOCK_SEQPACKET
  • path: /run/d2b/public.sock
  • mode: 0660
  • owner/group: root:d2b

Authorization uses SO_PEERCRED.

The daemon reads the peer uid/gid from the socket, resolves the caller's configured launcher/admin identity, and then performs a supplementary group lookup through getgrouplist(3) as part of the user/group mapping flow. Wheel membership alone never grants access. The public socket is for configured launcher/admin identities only, and d2bd itself is not treated as a valid public-socket client.

Public status read model

Unfiltered List and Status responses are served from a daemon-maintained read model after the first successful build. The model is published as an atomic frame snapshot and includes readModel metadata with:

  • schemaVersion;
  • kind (list or status);
  • generation;
  • sourceFingerprint;
  • updatedAtUnixMs;
  • freshness;
  • deepRefresh.

The source fingerprint covers the current system generation, public manifest, host/process/bundle artifacts, and the daemon pidfd-table generation. Mutating public operations and runner pidfd registration/deregistration invalidate the snapshot before later fast reads may reuse it. Filtered requests, explicit deep diagnostics, and bridge-check requests may bypass the read model so they can return request-specific data.

USB sysfs driver bind/unbind has no true async Rust wrapper in the current implementation. The broker therefore uses a bounded isolated helper for both USBIP bind and unbind rather than performing those writes on daemon status/read paths or an async reactor thread. Status/read-model paths never trigger sysfs driver mutation.

Broker socket

/run/d2b/priv.sock is the daemon-to-broker control path.

  • transport: non-abstract Unix-domain SOCK_SEQPACKET
  • path: /run/d2b/priv.sock
  • mode: 0660
  • owner/group: root:d2bd

Authorization is stricter than the public socket: the peer uid obtained through SO_PEERCRED must equal the d2bd uid. Root, launchers, and admins are all rejected at the wire layer if they try to speak directly to the broker.

Broker IPC is rate-limited before privileged dispatch. Direct broker peers are bucketed by stable peer UID, while accepted daemon-forwarded requests are bucketed by the daemon UID plus the forwarded caller role class and closed operation name. Rate-limit failures return only the generic Broker.IpcRateLimited envelope to the daemon, and direct non-daemon peers still receive no privileged details. Broker audit appends have a separate bounded write limiter so refused USB/module requests cannot grow audit storage without bound.

Request and response message types

The generated tables below list the concrete request and response shapes captured in d2b-contracts. The table entries link back to the Rust source files so a reviewer can diff the prose against the type definitions directly.

Any field carrying a Linux interface, bridge, TAP, or bridge-port name uses the IfName newtype rather than a raw String. Names at or above IFNAMSIZ (16 bytes including the trailing NUL) and malformed names are rejected during deserialization.

USBIP fields whose stable wire names include DurableClaim, durableClaim, or preserve_durable_claim refer to the broker-owned host-session claim under /run/d2b/locks/usbip. That claim survives VM stop/restart and daemon restart during the current host boot, but not host reboot.

Public socket request types

Type Kind Rust definition Shape
PublicRequest enum PublicRequest Capabilities; AuthStatus; List — (ListRequest); Status — (StatusRequest); Audit — (AuditRequest); HostCheck — (HostCheckRequest); VmStart — (VmLifecycleRequest); VmStop — (VmLifecycleRequest); VmRestart — (VmLifecycleRequest); Switch — (ActivationRequest); Boot — (ActivationRequest); Test — (ActivationRequest); Rollback — (ActivationRequest); Gc — (GcRequest); KeysList; KeysShow — (KeysShowRequest); KeysRotate — (KeysRotateRequest); Trust — (TrustRequest); RotateKnownHost — (RotateKnownHostRequest); UsbipBind — (UsbipBindCliRequest); UsbipUnbind — (UsbipUnbindCliRequest); UsbipProbe; StoreVerify — (StoreVerifyRequest); Migrate — (MigrateRequest); HostPrepare — (HostPrepareRequest); HostDestroy — (HostDestroyRequest); HostInstall — (HostInstallRequest); HostReconcile — (HostReconcileRequest); ReadGuestConfig — (ReadGuestConfigRequest); Exec — (ExecOp); Shell — (ShellOp); Console — (ConsoleOp); Audio — (AudioOp); GatewayDisplay — (GatewayDisplayOp); Workload — (WorkloadOp); UsbSecurityKeyStatus; UsbSecurityKeySessions; UsbSecurityKeyCancel — (crate::security_key::SecurityKeyCancelRequest)
ListRequest struct ListRequest struct { env: Option<String>; vm: Option<String> }
StatusRequest struct StatusRequest struct { check_bridges: bool; vm: Option<String> }
AuditRequest struct AuditRequest struct { filter: Option<AuditSelector>; format: AuditFormat; since: Option<String> }
HostCheckRequest struct HostCheckRequest struct { read_only: bool; strict: bool }
VmLifecycleRequest struct VmLifecycleRequest struct { vm: String; flags: MutationFlags; force: bool; no_wait_api: bool }
ActivationRequest struct ActivationRequest struct { vm: String; flags: MutationFlags }
GcRequest struct GcRequest struct { flags: MutationFlags; keep_generations: Option<u32> }
KeysShowRequest struct KeysShowRequest struct { vm: String }
KeysRotateRequest struct KeysRotateRequest struct { vm: String; flags: MutationFlags }
TrustRequest struct TrustRequest struct { vm: String; flags: MutationFlags }
RotateKnownHostRequest struct RotateKnownHostRequest struct { vm: String; flags: MutationFlags }
UsbipBindCliRequest struct UsbipBindCliRequest struct { vm: String; bus_id: String; flags: MutationFlags }
UsbipUnbindCliRequest struct UsbipUnbindCliRequest struct { vm: String; bus_id: String; flags: MutationFlags }
StoreVerifyRequest struct StoreVerifyRequest struct { vm: String; repair: bool }
ReadGuestConfigRequest struct ReadGuestConfigRequest struct { vm: String }
MigrateRequest struct MigrateRequest struct { flags: MutationFlags }
HostPrepareRequest struct HostPrepareRequest struct { flags: MutationFlags }
HostDestroyRequest struct HostDestroyRequest struct { flags: MutationFlags }
HostInstallRequest struct HostInstallRequest struct { flags: MutationFlags; enable: bool; start: bool; no_start: bool }
HostReconcileRequest struct HostReconcileRequest struct { flags: MutationFlags; network: bool }
UsbSecurityKeyStatusRequest struct UsbSecurityKeyStatusRequest empty struct
UsbSecurityKeySessionsRequest struct UsbSecurityKeySessionsRequest empty struct
UsbSecurityKeyCancelRequest struct UsbSecurityKeyCancelRequest struct { session_id: Option<String>; current: bool }
UsbSecurityKeyTestRequest struct UsbSecurityKeyTestRequest struct { vm: String }

Broker socket request types

Type Kind Rust definition Shape
BrokerRequest enum BrokerRequest ApplyNftables — (ApplyNftablesRequest); ApplyNmUnmanaged — (ApplyNmUnmanagedRequest); ApplyRoute — (ApplyRouteRequest); ApplySysctl — (ApplySysctlRequest); BindUnixSocket — (BindUnixSocketRequest); CreateOrReconcileUsersGroups — (CreateOrReconcileUsersGroupsRequest); CreatePersistentTap — (CreatePersistentTapRequest); CreateTapFd — (CreateTapFdRequest); DelegateCgroupV2 — (DelegateCgroupV2Request); ExportBrokerAudit — (ExportBrokerAuditRequest); Hello — (HelloRequest); GuestControlSign — (GuestControlSignRequest); InjectSecretById — (SecretByIdRequest); LaunchMinijailChild — (LaunchMinijailChildRequest); ModprobeIfAllowed — (ModprobeIfAllowedRequest); OpenCgroupDir — (OpenCgroupDirRequest); OpenDevice — (OpenDeviceRequest); OpenFuse — (OpenFuseRequest); OpenHidrawSecurityKey — (OpenHidrawSecurityKeyRequest); OpenKvm — (OpenKvmRequest); QemuMediaEnroll — (QemuMediaEnrollRequest); QemuMediaRefreshRegistry — (QemuMediaRefreshRegistryRequest); QemuMediaBoot — (QemuMediaBootRequest); QemuMediaSystemPowerdown — (QemuMediaLifecycleRequest); QemuMediaQueryStatus — (QemuMediaQueryStatusRequest); QemuMediaQuit — (QemuMediaLifecycleRequest); QemuMediaAttach — (QemuMediaHotplugRequest); QemuMediaDetach — (QemuMediaHotplugRequest); OpenPidfd — (OpenPidfdRequest); OpenVhostNet — (OpenVhostNetRequest); PauseBroker; PollChildReaped; PrepareRuntimeDir — (PrepareDirRequest); PrepareStateDir — (PrepareDirRequest); ReconcileStorageScope — (ReconcileStorageScopeRequest); ValidateLockSpec — (ValidateLockSpecRequest); PrepareStoreView — (PrepareStoreViewRequest); StoreSync — (StoreSyncRequest); StoreVerify — (StoreVerifyRequest); ReadSecretById — (SecretByIdRequest); ResumeBroker; RotateSecretById — (SecretByIdRequest); RunHostInstall — (RunHostInstallRequest); RunMigrate — (RunMigrateRequest); RunActivation — (RunActivationRequest); RunGc — (RunGcRequest); RunKeysRotate — (RunKeysRotateRequest); RunHostKeyTrust — (RunHostKeyTrustRequest); RunRotateKnownHost — (RunRotateKnownHostRequest); SetBridgePortFlags — (SetBridgePortFlagsRequest); SetSocketAcl — (SetSocketAclRequest); SetupMountNamespace — (SetupMountNamespaceRequest); SignalRunner — (SignalRunnerRequest); DeregisterRunnerPidfd — (DeregisterRunnerPidfdRequest); SpawnRunner — (SpawnRunnerRequest); UpdateHostsFile — (UpdateHostsFileRequest); UsbipBind — (UsbipBindRequest); UsbipBindFirewallRule — (UsbipBindFirewallRuleRequest); UsbipProxyReconcile — (UsbipProxyReconcileRequest); UsbipUnbind — (UsbipUnbindRequest); UsbipExplicitBind — (UsbipExplicitBindRequest); UsbipExplicitFirewallRule — (UsbipExplicitFirewallRuleRequest); ValidateBundle; SeedDnsmasqLease — (SeedDnsmasqLeaseRequest); BindMountFromHardlinkFarm — (BindMountFromHardlinkFarmRequest); OwnershipMatrixCheck — (OwnershipMatrixCheckRequest); SshHostKeyPreflight — (SshHostKeyPreflightRequest); DiskInit — (DiskInitRequest); SecurityKeyOpenDevice — (crate::security_key::SecurityKeyOpenDeviceRequest); SecurityKeyApplyUdevRules — (crate::security_key::SecurityKeyApplyUdevRulesRequest)
RunHostInstallRequest struct RunHostInstallRequest struct { bundle_installer_intent_ref: BundleOpId; enable: bool; start: bool; no_start: bool; tracing_span_id: Option<TracingSpanId> }
RunMigrateRequest struct RunMigrateRequest struct { bundle_migrate_intent_ref: BundleOpId; tracing_span_id: Option<TracingSpanId> }
RunActivationRequest struct RunActivationRequest struct { bundle_activation_intent_ref: BundleOpId; mode: ActivationMode; phase: ActivationPhase; vm: String; tracing_span_id: Option<TracingSpanId> }
RunGcRequest struct RunGcRequest struct { bundle_gc_intent_ref: BundleOpId; keep_generations: Option<u32>; tracing_span_id: Option<TracingSpanId> }
RunKeysRotateRequest struct RunKeysRotateRequest struct { bundle_keys_intent_ref: BundleOpId; vm: String; tracing_span_id: Option<TracingSpanId> }
RunHostKeyTrustRequest struct RunHostKeyTrustRequest struct { bundle_trust_intent_ref: BundleOpId; vm: String; tracing_span_id: Option<TracingSpanId> }
RunRotateKnownHostRequest struct RunRotateKnownHostRequest struct { bundle_rotate_known_host_intent_ref: BundleOpId; vm: String; tracing_span_id: Option<TracingSpanId> }
HelloRequest struct HelloRequest struct { client_version: String; supported_features: Vec<String> }
ApplyNftablesRequest struct ApplyNftablesRequest struct { bundle_nft_intent_ref: BundleOpId; scope_id: ScopeId; desired_hash: Option<String>; destroy: bool; tracing_span_id: Option<TracingSpanId> }
ApplyNmUnmanagedRequest struct ApplyNmUnmanagedRequest struct { bundle_nm_intent_ref: BundleOpId; scope_id: ScopeId; destroy: bool; tracing_span_id: Option<TracingSpanId> }
ApplyRouteRequest struct ApplyRouteRequest struct { bundle_route_intent_ref: BundleOpId; scope_id: ScopeId; destroy: bool; tracing_span_id: Option<TracingSpanId> }
ApplySysctlRequest struct ApplySysctlRequest struct { bundle_sysctl_intent_ref: BundleOpId; scope_id: ScopeId; destroy: bool; tracing_span_id: Option<TracingSpanId> }
BindUnixSocketRequest struct BindUnixSocketRequest struct { bundle_socket_intent_ref: BundleOpId; vm_id: VmId; role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
CreateOrReconcileUsersGroupsRequest struct CreateOrReconcileUsersGroupsRequest struct { subject_ids: Vec<SubjectId>; tracing_span_id: Option<TracingSpanId> }
CreatePersistentTapRequest struct CreatePersistentTapRequest struct { role_id: RoleId; vm_id: VmId; tracing_span_id: Option<TracingSpanId> }
CreateTapFdRequest struct CreateTapFdRequest struct { role_id: RoleId; vm_id: VmId; tracing_span_id: Option<TracingSpanId> }
DelegateCgroupV2Request struct DelegateCgroupV2Request struct { scope_id: ScopeId; tracing_span_id: Option<TracingSpanId> }
ExportBrokerAuditRequest struct ExportBrokerAuditRequest struct { filter: Option<BrokerAuditFilter>; since: Option<String> }
GuestControlSignRequest struct GuestControlSignRequest struct { vm_id: VmId; role: GuestControlProofRole; protocol_version: u32; direction: GuestControlDirection; purpose: GuestControlAuthPurpose; guest_control_port: u32; peer_cid: Option<u32>; host_nonce: Vec<u8>; guest_nonce: Vec<u8>; guest_boot_id: GuestBootIdWire; capabilities_hash: Option<String>; tracing_span_id: Option<TracingSpanId> }
SecretByIdRequest struct SecretByIdRequest struct { opaque_id: String; tracing_span_id: Option<TracingSpanId> }
LaunchMinijailChildRequest struct LaunchMinijailChildRequest struct { vm_id: VmId; role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
ModprobeIfAllowedRequest struct ModprobeIfAllowedRequest struct { module_name: String; tracing_span_id: Option<TracingSpanId> }
OpenCgroupDirRequest struct OpenCgroupDirRequest struct { scope_id: ScopeId; path_class: PathClass; tracing_span_id: Option<TracingSpanId> }
OpenDeviceRequest struct OpenDeviceRequest struct { role_id: RoleId; device_class: String; tracing_span_id: Option<TracingSpanId> }
OpenKvmRequest struct OpenKvmRequest struct { role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
QemuMediaEnrollRequest struct QemuMediaEnrollRequest struct { vm_id: VmId; media_ref: MediaRef; bus_id: String; tracing_span_id: Option<TracingSpanId> }
QemuMediaRefreshRegistryRequest struct QemuMediaRefreshRegistryRequest struct { tracing_span_id: Option<TracingSpanId> }
QemuMediaBootRequest struct QemuMediaBootRequest struct { vm_id: VmId; tracing_span_id: Option<TracingSpanId> }
QemuMediaLifecycleRequest struct QemuMediaLifecycleRequest struct { vm_id: VmId; tracing_span_id: Option<TracingSpanId> }
QemuMediaQueryStatusRequest struct QemuMediaQueryStatusRequest struct { vm_id: VmId; shutdown_context: bool; tracing_span_id: Option<TracingSpanId> }
QemuMediaHotplugRequest struct QemuMediaHotplugRequest struct { vm_id: VmId; bus_id: String; tracing_span_id: Option<TracingSpanId> }
OpenPidfdRequest struct OpenPidfdRequest struct { vm_id: VmId; role_id: RoleId; pid: i32; expected_start_time_ticks: u64; tracing_span_id: Option<TracingSpanId> }
OpenVhostNetRequest struct OpenVhostNetRequest struct { role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
OpenFuseRequest struct OpenFuseRequest struct { role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
OpenHidrawSecurityKeyRequest struct OpenHidrawSecurityKeyRequest struct { vm_id: VmId; selector_id: String; tracing_span_id: Option<TracingSpanId> }
PrepareDirRequest struct PrepareDirRequest struct { vm_id: VmId; path_class: PathClass; tracing_span_id: Option<TracingSpanId> }
PrepareStoreViewRequest struct PrepareStoreViewRequest struct { vm_id: VmId; tracing_span_id: Option<TracingSpanId> }
StoreSyncRequest struct StoreSyncRequest struct { vm_id: VmId; bundle_closure_ref: BundleClosureRef; generation_token: u32; tracing_span_id: Option<TracingSpanId> }
StoreVerifyRequest struct StoreVerifyRequest struct { vm_id: VmId; repair: bool; tracing_span_id: Option<TracingSpanId> }
SetBridgePortFlagsRequest struct SetBridgePortFlagsRequest struct { vm_id: VmId; role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
SetSocketAclRequest struct SetSocketAclRequest struct { bundle_socket_intent_ref: BundleOpId; vm_id: VmId; role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
SetupMountNamespaceRequest struct SetupMountNamespaceRequest struct { vm_id: VmId; role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
UpdateHostsFileRequest struct UpdateHostsFileRequest struct { bundle_hosts_intent_ref: BundleOpId; destroy: bool; tracing_span_id: Option<TracingSpanId> }
UsbipBindRequest struct UsbipBindRequest struct { bundle_usbip_bind_intent_ref: BundleOpId; tracing_span_id: Option<TracingSpanId> }
UsbipBindFirewallRuleRequest struct UsbipBindFirewallRuleRequest struct { bundle_usbip_firewall_intent_ref: BundleOpId; tracing_span_id: Option<TracingSpanId> }
UsbipProxyReconcileRequest struct UsbipProxyReconcileRequest struct { scope_id: ScopeId; tracing_span_id: Option<TracingSpanId> }
UsbipUnbindRequest struct UsbipUnbindRequest struct { bundle_usbip_bind_intent_ref: BundleOpId; preserve_durable_claim: bool; tracing_span_id: Option<TracingSpanId> }
UsbipExplicitBindRequest struct UsbipExplicitBindRequest struct { bus_id: String; vm: String; env: String; tracing_span_id: Option<TracingSpanId> }
UsbipExplicitFirewallRuleRequest struct UsbipExplicitFirewallRuleRequest struct { bus_id: String; env: String; host_uplink_ip: String; net_uplink_ip: String; tracing_span_id: Option<TracingSpanId> }
SignalRunnerRequest struct SignalRunnerRequest struct { vm_id: VmId; role_id: RoleId; signal: RunnerSignal; pid: Option<i32>; expected_start_time_ticks: Option<u64>; tracing_span_id: Option<TracingSpanId> }
DeregisterRunnerPidfdRequest struct DeregisterRunnerPidfdRequest struct { vm_id: VmId; role_id: RoleId; tracing_span_id: Option<TracingSpanId> }
SpawnRunnerRequest struct SpawnRunnerRequest struct { vm_id: VmId; role_id: RoleId; role: RunnerRole; bundle_runner_intent_ref: BundleOpId; runtime_allocations: Vec<RunnerAllocation>; tracing_span_id: Option<TracingSpanId>; workload_identity: Option<WorkloadIdentity> }
SeedDnsmasqLeaseRequest struct SeedDnsmasqLeaseRequest struct { vm_id: VmId; scope_id: ScopeId; tracing_span_id: Option<TracingSpanId> }
BindMountFromHardlinkFarmRequest struct BindMountFromHardlinkFarmRequest struct { vm_id: VmId; bundle_store_view_intent_ref: Option<BundleOpId>; tracing_span_id: Option<TracingSpanId> }
OwnershipMatrixCheckRequest struct OwnershipMatrixCheckRequest struct { vm_id: VmId; tracing_span_id: Option<TracingSpanId> }
SshHostKeyPreflightRequest struct SshHostKeyPreflightRequest struct { vm_id: VmId; tracing_span_id: Option<TracingSpanId> }
ReconcileStorageScopeRequest struct ReconcileStorageScopeRequest struct { storage_ref: BundleOpId; apply: bool; tracing_span_id: Option<TracingSpanId> }
ValidateLockSpecRequest struct ValidateLockSpecRequest struct { lock_ref: BundleOpId; tracing_span_id: Option<TracingSpanId> }
DiskInitRequest struct DiskInitRequest struct { vm_id: VmId; tracing_span_id: Option<TracingSpanId> }

Console and audio wire types

ConsoleOp and AudioOp are staged runtime public wire types committed to d2b-contracts and included in the auto-generated tables above. d2bd dispatches both op families; the design is governed by ADR 0041. See provider capability matrix for the per-provider behavior each op implements.

The implemented surface contracts are:

  • ConsoleOp — Attach, Detach, ReadOutput. ReadOutput responses include ring-buffer cursor metadata so clients can detect dropped output and fast-forward cleanly. Console bytes are never logged, audited, or used as metric labels.
  • AudioOp — GetState, SetMic, SetSpeaker, SetOff, Status. Status returns per-target AudioProviderResult structs so one misconfigured provider does not fail the entire multi-target query. Volume and gain values are bounded 0..=100 domain integers validated at the wire boundary. Host PipeWire enforcement and guestd AudioSet / AudioStatus integration report host-and-guest only after both live enforcement paths complete; qemu-media reports the intentional host-only posture because guest enforcement is unsupported there.

Both ops follow the standard PublicRequest/PublicResponse framing; see the auto-generated tables above for the committed Rust variants.

Public socket response types

Type Kind Rust definition Shape
PublicResponse enum PublicResponse Capabilities — (CapabilitiesResponse); AuthStatus — (AuthStatusResponse); List — (ListResponse); Status — (StatusResponse); Audit — (AuditResponse); HostCheck — (HostCheckResponse); KeysList — (KeysListResponse); KeysShow — (KeysShowResponse); UsbipProbe — (UsbipProbeResponse); StoreVerify — (StoreVerifyResponse); MutatingVerb — (MutatingVerbResponse); ReadGuestConfig — (ReadGuestConfigResponse); Exec — (ExecOpResponse); Shell — (ShellOpResponse); Console — (ConsoleOpResponse); Audio — (AudioOpResponse); GatewayDisplay — (GatewayDisplayOpResponse); Workload — (WorkloadOpResponse); UsbSecurityKeyStatus — (crate::security_key::SecurityKeyStatusResponse); UsbSecurityKeySessions — (crate::security_key::SecurityKeySessionsResponse); UsbSecurityKeyCancel — (crate::security_key::SecurityKeyCancelResponse); Error — (Error)
WorkloadOpResponse enum WorkloadOpResponse List — (WorkloadListResult); Status — (Box); LauncherExec — (LauncherExecResult)
GatewayDisplayOpResponse enum GatewayDisplayOpResponse Start — (GatewayDisplayStartResult); Stop — (GatewayDisplayStopResult); Open — (GatewayDisplayOpenResult); Close — (GatewayDisplayCloseResult); List — (GatewayDisplayListResult); ListDetailed — (GatewayDisplayListDetailedResult)
ReadGuestConfigResponse struct ReadGuestConfigResponse struct { content_base64: String }
ExecOpResponse enum ExecOpResponse Start — (ExecStartResult); DetachedCreate — (ExecDetachedCreateResult); WriteStdin — (ExecWriteStdinResult); ReadOutput — (ExecReadOutputResult); Signal — (ExecControlResult); Resize — (ExecControlResult); Wait — (ExecWaitResult); Close — (ExecCloseResult); List — (ExecDetachedListResult); Logs — (ExecDetachedLogsResult); Status — (ExecDetachedStatusResult); Kill — (ExecDetachedKillResult)
ShellOpResponse enum ShellOpResponse Attach — (ShellAttachResult); WriteStdin — (crate::terminal_wire::TerminalWriteStdinResult); ReadOutput — (crate::terminal_wire::TerminalReadOutputChunk); Resize — (crate::terminal_wire::TerminalControlResult); Wait — (crate::terminal_wire::TerminalWaitResult); CloseStdin — (crate::terminal_wire::TerminalCloseResult); CloseAttach — (ShellDetachResult); List — (ShellListResult); Detach — (ShellDetachResult); Kill — (ShellKillResult)
ConsoleOpResponse enum ConsoleOpResponse Attach — (ConsoleAttachResult); WriteStdin — (ConsoleControlResult); ReadOutput — (ConsoleReadOutputResult); Resize — (ConsoleControlResult); Wait — (ConsoleWaitResult); Close — (ConsoleCloseResult)
AudioOpResponse enum AudioOpResponse Status — (AudioStatusResult); SetVolume — (AudioSetResult); Mute — (AudioSetResult)
MutatingVerbResponse struct MutatingVerbResponse struct { verb: String; outcome: MutatingVerbOutcome; target_wave: Option<String>; summary: Option<String>; remediation: Option<String>; api_ready: Option<String> }
CapabilitiesResponse struct CapabilitiesResponse struct { broker_socket: String; capabilities: Vec<FeatureFlag>; public_socket: String; server_version: Version; selected_version: Version }
AuthStatusResponse struct AuthStatusResponse struct { allowed_subcommands: Vec<String>; denied_subcommands: Vec<DeniedCommandHint>; role: AuthRole; sockets: Vec<SocketReachability> }
ListResponse struct ListResponse struct { vms: Vec<ListEntry>; read_model: Option<PublicReadModelMetadata> }
StatusResponse struct StatusResponse struct { entries: Vec<VmStatus>; read_model: Option<PublicReadModelMetadata> }
AuditResponse struct AuditResponse struct { entries: Vec<AuditEntry> }
HostCheckResponse struct HostCheckResponse struct { exit_code: u8; findings: Vec<HostFinding> }
KeysListResponse struct KeysListResponse struct { entries: Vec<KeyEntry> }
KeysShowResponse struct KeysShowResponse struct { vm: String; env: Option<String>; managed_key_path: String; public_key: String; fingerprint: String; known_hosts_entry: Option<String> }
UsbipProbeResponse struct UsbipProbeResponse struct { entries: Vec<UsbipProbeEntry> }
UsbSecurityKeyStatusResponse struct UsbSecurityKeyStatusResponse struct { host_proxy_enabled: bool; physical_keys: Vec<UsbSkPhysicalKeyStatus>; vm_devices: Vec<UsbSkVirtualDeviceStatus>; lease: UsbSkLeaseStatus }
UsbSecurityKeySessionsResponse struct UsbSecurityKeySessionsResponse struct { sessions: Vec<UsbSkSession> }
UsbSecurityKeyCancelResponse struct UsbSecurityKeyCancelResponse struct { cancelled_session_id: Option<String>; already_idle: bool }
UsbSecurityKeyTestResponse struct UsbSecurityKeyTestResponse struct { vm: String; ok: bool; checks: Vec<UsbSkTestCheck> }

Broker socket response types

Type Kind Rust definition Shape
RunHostInstallResponse struct RunHostInstallResponse struct { installed: bool; enabled: bool; started: bool; artifacts_written: Vec<String> }
RunMigrateResponse struct RunMigrateResponse struct { migrated_vm_count: u32; notes: Vec<String> }
RunActivationResponse struct RunActivationResponse struct { mode: ActivationMode; vm: String; generation_number: Option<u64>; guest_switch_script_path: Option<String>; summary: String }
RunGcResponse struct RunGcResponse struct { keep_generations: Option<u32>; retained_store_path_count: u32; summary: String }
RunKeysRotateResponse struct RunKeysRotateResponse struct { vm: String; key_path: String; public_key_fingerprint: String }
RunHostKeyTrustResponse struct RunHostKeyTrustResponse struct { vm: String; static_ip: String; known_hosts_path: String; updated: bool }
RunRotateKnownHostResponse struct RunRotateKnownHostResponse struct { vm: String; static_ip: String; known_hosts_path: String; removed: bool }
BrokerResponse enum BrokerResponse Ack — (AckResponse); CreatePersistentTap — (TapReadyResponse); CreateTapFd — (TapReadyResponse); Error — (BrokerErrorResponse); ExportBrokerAudit — (ExportBrokerAuditResponse); RunHostInstall — (RunHostInstallResponse); RunMigrate — (RunMigrateResponse); RunActivation — (RunActivationResponse); RunGc — (RunGcResponse); RunKeysRotate — (RunKeysRotateResponse); RunHostKeyTrust — (RunHostKeyTrustResponse); RunRotateKnownHost — (RunRotateKnownHostResponse); Hello — (HelloResponse); GuestControlSign — (GuestControlSignResponse); QemuMediaEnroll — (QemuMediaEnrollResponse); QemuMediaRefreshRegistry — (QemuMediaRefreshRegistryResponse); QemuMediaBoot — (QemuMediaHotplugResponse); QemuMediaSystemPowerdown — (QemuMediaLifecycleResponse); QemuMediaQueryStatus — (QemuMediaQueryStatusResponse); QemuMediaQuit — (QemuMediaLifecycleResponse); QemuMediaAttach — (QemuMediaHotplugResponse); QemuMediaDetach — (QemuMediaHotplugResponse); OpenHidrawSecurityKey — (OpenHidrawSecurityKeyResponse); OpenPidfd — (OpenPidfdResponse); PollChildReaped — (PollChildReapedResponse); ReconcileStorageScope — (ReconcileStorageScopeResponse); SetBridgePortFlags — (BridgePortFlagsResponse); SignalRunner — (SignalRunnerResponse); DeregisterRunnerPidfd — (DeregisterRunnerPidfdResponse); SpawnRunner — (SpawnRunnerResponse); StoreSync — (StoreSyncResponse); StoreVerify — (StoreVerifyResponse); ValidateLockSpec — (ValidateLockSpecResponse); ValidateBundle — (ValidateBundleResponse)
BrokerErrorResponse struct BrokerErrorResponse struct { kind: String; operation: String; target_wave: Option<String>; message: String; action: String }
HelloResponse struct HelloResponse struct { server_version: String; selected_version: String; capabilities: Vec<String> }
GuestControlSignResponse struct GuestControlSignResponse struct { tag: Vec<u8> }
QemuMediaEnrollResponse struct QemuMediaEnrollResponse struct { vm_id: VmId; media_ref: MediaRef; read_only: bool; enrolled: bool; udev_rule_written: bool; udev_reloaded: bool }
QemuMediaRefreshRegistryResponse struct QemuMediaRefreshRegistryResponse struct { record_count: u32; redacted_index_written: bool; udev_rule_written: bool; udev_reloaded: bool }
QemuMediaLifecycleResponse struct QemuMediaLifecycleResponse struct { vm_id: VmId; command: QemuMediaLifecycleAction }
QemuMediaQueryStatusResponse struct QemuMediaQueryStatusResponse struct { vm_id: VmId; status: QemuMediaVmStatus }
QemuMediaHotplugResponse struct QemuMediaHotplugResponse struct { vm_id: VmId; media_ref: MediaRef; slot: String; read_only: bool; qmp_commands: Vec<String>; events: Vec<QemuMediaHotplugEvent> }
OpenPidfdResponse struct OpenPidfdResponse struct { vm_id: VmId; role_id: RoleId; pid: i32; verified_start_time_ticks: u64; pidfd_index: u32 }
OpenHidrawSecurityKeyResponse struct OpenHidrawSecurityKeyResponse struct { selector_resolved: String; device_class: String }
StoreSyncResponse struct StoreSyncResponse struct { vm: String; generation_id: String; generation_token: u32; hardlink_farm_path: String; closure_count: u32; retained_generations: Vec<u32>; swept_count: u32; cleanup_deferred: bool }
StoreVerifyResponse struct StoreVerifyResponse struct { vm: String; status: StoreVerifyStatus; checked: u32; drifted: u32; repaired: u32; unknown_reason: Option<StoreVerifyUnknownReason>; audit_ref: Option<String>; remediation: Option<String> }
AckResponse struct AckResponse struct { accepted: bool; operation: String }
TapReadyResponse struct TapReadyResponse struct { bridge: Option<IfName>; tap: IfName }
ExportBrokerAuditResponse struct ExportBrokerAuditResponse struct { lines: Vec<String> }
BridgePortFlagsResponse struct BridgePortFlagsResponse struct { bridge: IfName; isolated: bool; neigh_suppress: bool; port: IfName }
ValidateBundleResponse struct ValidateBundleResponse struct { valid: bool }
SignalRunnerResponse struct SignalRunnerResponse struct { signaled: bool; vm_id: VmId; role_id: RoleId }
DeregisterRunnerPidfdResponse struct DeregisterRunnerPidfdResponse struct { vm_id: VmId; role_id: RoleId; removed: bool }
SpawnRunnerResponse struct SpawnRunnerResponse struct { vm_id: VmId; role_id: RoleId; role: RunnerRole; pid: i32; start_time_ticks: u64; pidfd_index: u32; console_fd_index: Option<u32> }
ReconcileStorageScopeResponse struct ReconcileStorageScopeResponse struct { storage_ref: BundleOpId; scope: String; kind: String; status: StorageReconcileStatus; applied: bool; path_hash: String }
ValidateLockSpecResponse struct ValidateLockSpecResponse struct { lock_ref: BundleOpId; scope: String; kind: String; cloexec_required: bool; fd_passing_mechanism: String; order_key: String }
PollChildReapedResponse struct PollChildReapedResponse struct { notifications: Vec<ChildReapedNotification> }

Per-VM lifecycle state

The VM lifecycle enum is frozen so supervisor work can reuse it without a wire break. The public state surface distinguishes:

  • Stopped
  • Starting
  • Booted
  • Running
  • Stopping
  • Restarting
  • Failed
  • Unknown

pendingRestart is a derived boolean carried forward from the v0.4.0 bash CLI: it means the VM is running, booted exists, current exists, and the two store paths differ. It is not a separate state-machine node.

lifecycle.degraded and lifecycle.degradedReasons[] surface bounded daemon-degraded conditions that require operator attention without inventing a new lifecycle state. VM activation uses reason activation-pending while a crash-consistent host marker says an activation was in progress, and activation-indeterminate when guest activation may have completed but host metadata commit has not been proven. Remediation text is bounded and never includes guest output, store paths, or activation command arguments.

Stop and restart requests accept a serde-defaulted force flag. When force = false, d2bd applies the VM's manifest lifecycle.gracefulShutdown policy: supported local providers receive a bounded graceful guest-shutdown request before VMM pidfd cleanup. When force = true, d2bd records explicit operator intent and skips only the provider graceful wait; it still uses the standard SIGTERM/SIGKILL cleanup policy rather than jumping directly to SIGKILL. Restart applies force to the stop phase only.

VM activation flow

The public activation requests (Switch, Test, Rollback, and Boot) are daemon-owned operations. The generated request/response tables below describe the currently committed wire shapes; when the activation wire types change, regenerate the AUTO-GENERATED sections with cargo xtask gen-daemon-api rather than editing those blocks by hand.

Live activation (Switch, Test, and live Rollback) is not a broker script-execution surface. d2bd prepares the VM toplevel, asks the broker/store-view path to publish the closure into the per-VM live store pool, opens authenticated guest-control to guestd, starts activation of the prepared toplevel inside the running guest, polls guest activation status, and only then asks the broker to commit host-owned generation metadata. If the VM is stopped/offline or guestd does not advertise the activation capability, the operation fails closed before host-side generation commit.

Boot is the explicit offline staging mode. It publishes and commits the declared toplevel for the next VM start without contacting guestd or running live guest activation.

Lifecycle enum

Type Kind Rust definition Shape
VmLifecycleState enum VmLifecycleState Stopped; Starting; Booted; Running; Stopping; Restarting; Failed; Unknown

Other documented enums

Type Kind Rust definition Shape
ActivationMode enum ActivationMode Switch; Boot; Test; Rollback
ActivationPhase enum ActivationPhase Prepare; Commit; MetadataOnly
GuestControlProofRole enum GuestControlProofRole HostProof; GuestProof
GuestControlDirection enum GuestControlDirection HostToGuest
GuestControlAuthPurpose enum GuestControlAuthPurpose GuestControlAuthV1
QemuMediaLifecycleAction enum QemuMediaLifecycleAction SystemPowerdown; Quit
QemuMediaVmStatus enum QemuMediaVmStatus Running; Paused; Shutdown; Suspended; Watchdog; Debug; Inmigrate; InternalError; IoError; Postmigrate; Prelaunch; FinishMigrate; RestoreVm; SaveVm; GuestPanicked; Colo; Preconfig; Unknown; ConnectionLostDuringShutdown
QemuMediaHotplugStatus enum QemuMediaHotplugStatus IdentityResolved; QmpConnected; QmpCapabilities; FdAdded; BlockdevAdded; DeviceAdded; DeviceDeleted; BlockdevDeleted; FdRemoved; VmContinued
StoreVerifyStatus enum StoreVerifyStatus Ok; Drift; Unknown; Repaired; Failed; NotFound
StoreVerifyUnknownReason enum StoreVerifyUnknownReason MarkerOrManifestMissing; MarkerOrManifestUnreadable; OlderHostGeneration; GenerationIdentityUnavailable
RunnerSignal enum RunnerSignal Term; Kill; Quit
RunnerRole enum RunnerRole CloudHypervisor; QemuMedia; Virtiofsd; Swtpm; SwtpmFlush; Gpu; Audio; Video; VsockRelay; Usbip; OtelHostBridge; WaylandProxy
RunnerAllocationKind enum RunnerAllocationKind VsockCid; TapFdSlot; ApiSocketPath
BrokerCallerRole enum BrokerCallerRole AdminUid — struct { uid: u32 }; LauncherUid — struct { uid: u32 }; RootUid — struct { uid: u32 }; NotAuthorized
StorageReconcileStatus enum StorageReconcileStatus Clean; Created; Reused; CheckedOnly; TemplateUnexpanded; Refused
ChildExitKind enum ChildExitKind Exited; Signaled; Killed
BrokerNotification enum BrokerNotification ChildReaped — (ChildReapedNotification); Unknown
AuthDirection enum AuthDirection HostToGuest
AuthPurpose enum AuthPurpose GuestControlAuthV1
ProofRole enum ProofRole Host; Guest
GuestCapability enum GuestCapability Health; Capabilities; ExecAttached; ExecDetached; ExecTty; ExecLogs; TtyResize; Signals; ReadGuestFile; UsbipImport; ShellAttached; ShellManagement; ShellForceAttach; UsbipStatus; SystemActivation; AudioStatus; AudioSet
GuestFileId enum GuestFileId GuestConfig
GuestSubsystem enum GuestSubsystem Guestd; Userd; Exec; LogStorage; Token; Vsock; Usbip; Shell; Shpool; SystemActivation; Audio
TerminalKind enum TerminalKind Exec; Shell
ShellState enum ShellState Attached; Detached; Killed; PoolUnavailable; FeatureDisabled; OutputGap
ShellCloseCause enum ShellCloseCause ClientDetach; EvictedByForce; EvictedByAdminDetach; KilledByAdmin; PoolUnavailable; OutputGap
HealthOrigin enum HealthOrigin GuestReported; HostSynthesized
GuestVsockDirection enum GuestVsockDirection HostToGuest
GuestIdentityBinding enum GuestIdentityBinding VmIdCidPortAndTokenTranscript
GuestTransportKind enum GuestTransportKind VirtioVsockTtrpc
GuestConnectAckValue enum GuestConnectAckValue OpaqueLocalPort
HealthState enum HealthState Healthy; Degraded; UnavailableOldGeneration; ListenerAbsent; TransportUnreachable; AuthFailed; ProtocolMismatch; StaleSession
HealthReason enum HealthReason None; OldGeneration; ListenerAbsent; ConnectRefused; ConnectTimeout; EofBeforeAck; MalformedAck; AckTooLong; TransportIo; AuthTokenRejected; ProtocolVersionUnsupported; SessionGenerationMismatch; ExecSubsystemUnavailable; LogStorageUnavailable; QuotaExceeded; RateLimited; InternalHealthCheckFailed
HealthRemediation enum HealthRemediation None; Retry; RestartVm; UpgradeGuest; CheckAuthToken; CheckGuestdService; ReduceLoad; InspectGuestLogs
GuestActivationMode enum GuestActivationMode Switch; Boot; Test; DryActivate
GuestActivationState enum GuestActivationState Running; Succeeded; Failed; TimedOut; Lost
UsbipImportAction enum UsbipImportAction Attach; Detach
AudioSetKind enum AudioSetKind Grant; Level
AudioChannel enum AudioChannel Microphone; Speaker
OutputStream enum OutputStream Stdout; Stderr
WriteDisposition enum WriteDisposition Accepted; Duplicate; Rejected
ExecState enum ExecState Created; Running; Exited; Signaled; Cancelled; SlowConsumerCancelled; ProtocolError; LostGuestd; Reaped
StdinState enum StdinState Open; Closing; Closed; ClosedByProcess; RejectedNotInteractive
TerminalStatus enum TerminalStatus ExitCode — struct { exit_code: i32 }; Signal — struct { signal: u32 }; StatusCode — struct { status_code: i32 }; Error — struct { error: GuestControlErrorKind }
SignalTarget enum SignalTarget ForegroundProcessGroup; ProcessTree
ExecCancelReason enum ExecCancelReason ClientDisconnect; UserRequested; SlowConsumer; ProtocolError
KnownFeatureFlag enum KnownFeatureFlag TypedErrors; ManifestV04; StatusCheckBridges; ExportBrokerAudit; ConfiguredLaunchV1; UnsafeLocalProviderV1; UnsafeLocalShellV1
WorkloadOp enum WorkloadOp List — (WorkloadListArgs); Status — (WorkloadStatusArgs); LauncherExec — (LauncherExecArgs)
WorkloadAvailability enum WorkloadAvailability Ready; HelperUnavailable; HelperStale; UserManagerUnavailable; GraphicalSessionInactive; WaylandUnavailable; ProxyUnavailable; Degraded
GraphicalLaunchPosture enum GraphicalLaunchPosture Proxied; NotApplicable; GraphicalSessionInactive; WaylandUnavailable; ProxyUnavailable
LauncherExecDisposition enum LauncherExecDisposition Committed; AlreadyCommitted
GatewayDisplayOp enum GatewayDisplayOp Start — (GatewayDisplayStartArgs); Stop — (GatewayDisplayStopArgs); Open — (GatewayDisplayOpenArgs); Close — (GatewayDisplayCloseArgs); List — (GatewayDisplayListArgs); ListDetailed — (GatewayDisplayListArgs)
ExecStream enum ExecStream Stdout; Stderr
ExecOp enum ExecOp Start — (ExecStartArgs); WriteStdin — (ExecWriteStdinArgs); ReadOutput — (ExecReadOutputArgs); Signal — (ExecSignalArgs); Resize — (ExecResizeArgs); Wait — (ExecWaitArgs); Close — (ExecCloseArgs); List — (ExecDetachedListArgs); Logs — (ExecDetachedLogsArgs); Status — (ExecDetachedStatusArgs); Kill — (ExecDetachedKillArgs)
ExecTerminalStatus enum ExecTerminalStatus Exited — struct { code: i32 }; Signaled — struct { signal: u32 }; Error — struct { slug: String }
ExecDetachedKillOutcome enum ExecDetachedKillOutcome Cancelling; AlreadyTerminal
ShellOp enum ShellOp Attach — (ShellAttachArgs); WriteStdin — (crate::terminal_wire::TerminalWriteStdin); ReadOutput — (crate::terminal_wire::TerminalReadOutput); Resize — (crate::terminal_wire::TerminalResize); Wait — (crate::terminal_wire::TerminalWait); CloseStdin — (crate::terminal_wire::TerminalClose); CloseAttach — (ShellCloseAttachArgs); List — (ShellListArgs); Detach — (ShellDetachArgs); Kill — (ShellKillArgs)
ShellSessionState enum ShellSessionState Attached; Detached; Killed; PoolUnavailable; FeatureDisabled; OutputGap
ShellCloseCause enum ShellCloseCause ClientDetach; EvictedByForce; EvictedByAdminDetach; KilledByAdmin; PoolUnavailable; OutputGap
ConsoleProviderKind enum ConsoleProviderKind LocalHypervisor; QemuMedia; AcaSandbox
ConsoleOp enum ConsoleOp Attach — (ConsoleAttachArgs); WriteStdin — (ConsoleWriteStdinArgs); ReadOutput — (ConsoleReadOutputArgs); Resize — (ConsoleResizeArgs); Wait — (ConsoleWaitArgs); Close — (ConsoleCloseArgs)
AudioChannel enum AudioChannel Speaker; Microphone
AudioEnforcementPosture enum AudioEnforcementPosture HostAndGuest; HostOnly; GuestOnly; Unsupported
AudioProviderKind enum AudioProviderKind LocalHypervisor; QemuMedia; AcaSandbox
AudioOp enum AudioOp Status — (AudioStatusArgs); SetVolume — (AudioSetVolumeArgs); Mute — (AudioMuteArgs)
AudioSetApplied enum AudioSetApplied HostAndGuest; HostOnly; GuestOnly; Unsupported
MutatingVerbOutcome enum MutatingVerbOutcome DryRunPlanned; Applied; ApiReadyTimeout; NotYetImplemented; BrokerError; InvalidRequest
UsbipProbeStatus enum UsbipProbeStatus Bound; Unbound; Degraded; Enrollable; Enrolled; Stale; DirectConfig; Unknown
UsbipDurableClaimState enum UsbipDurableClaimState Missing; HeldByDesiredOwner; HeldByOtherOwner; StaleOwner; Corrupt; NotApplicable; Unknown
UsbipHostBindState enum UsbipHostBindState Unbound; BoundToUsbipHost; BoundToUnexpectedDriver; DeviceMissing; NotApplicable; Unknown
UsbipHostCarrierState enum UsbipHostCarrierState Absent; Unavailable; WithheldForOwner; Ready; DepartedDuringProbe; NotApplicable; Unknown
UsbipProxyState enum UsbipProxyState NotDeclared; Stopped; Starting; Listening; Stale; Failed; NotApplicable; Unknown
UsbipGuestImportState enum UsbipGuestImportState Detached; Imported; Unavailable; NotApplicable; Unknown
UsbipTopologyState enum UsbipTopologyState Match; Mismatch; Incomplete; NotObserved; NotApplicable; Unknown
UsbipPolicyState enum UsbipPolicyState Allowed; Denied; Missing; NotApplicable; Unknown
UsbipProbeDegradedReasonCode enum UsbipProbeDegradedReasonCode PolicyFailed; DeviceDepartedBeforeClaim; DeviceDepartedAfterLock; DeviceDepartedDuringMutation; DeviceReappearedWithDifferentTopology; LockHeldByOtherOwner; InvalidPersistedLockClaim; CarrierUnavailable; HostBindUnavailable; ProxyUnavailable; GuestImportUnavailable; StaleHostState; StaleGuestState; ProbeIncomplete; Unknown
UsbProbeEntryKind enum UsbProbeEntryKind Usbip; QemuMediaSlot
AuditFormat enum AuditFormat Human; Json
AuthRole enum AuthRole None; Launcher; Admin
HostFindingSeverity enum HostFindingSeverity Pass; Warn; Fail
UsbSkLeaseState enum UsbSkLeaseState Idle; Active; Queued; Unknown
UsbSkSessionOutcome enum UsbSkSessionOutcome Success; Timeout; Cancelled; DeviceUnavailable; Active; Unknown
SecurityKeyVmSessionState enum SecurityKeyVmSessionState Idle; AwaitingLease; Active; Completed; Cancelled
SecurityKeySessionResult enum SecurityKeySessionResult InProgress; Success; CtapError; Timeout; Cancelled; InternalError
SecurityKeyEvent enum SecurityKeyEvent SessionStarted — struct { session_id: SecurityKeySessionId; vm: String; device_label: SecurityKeyDeviceLabel; started_at: String }; SessionSucceeded — struct { session_id: SecurityKeySessionId; vm: String; device_label: SecurityKeyDeviceLabel; ended_at: String }; SessionFailed — struct { session_id: SecurityKeySessionId; vm: String; device_label: SecurityKeyDeviceLabel; result: SecurityKeySessionResult; ended_at: String }; SessionCancelled — struct { session_id: SecurityKeySessionId; vm: String; device_label: SecurityKeyDeviceLabel; ended_at: String }; DeviceRemoved — struct { device_label: SecurityKeyDeviceLabel; interrupted_session_id: Option<SecurityKeySessionId> }; DeviceReinserted — struct { device_label: SecurityKeyDeviceLabel }; SessionQueued — struct { session_id: SecurityKeySessionId; vm: String; device_label: SecurityKeyDeviceLabel; queued_at: String; blocking_vm: String }
TerminalStream enum TerminalStream Stdout; Stderr
TerminalStatus enum TerminalStatus Exited — struct { code: i32 }; Signaled — struct { signal: u32 }; Error — struct { slug: String }
PathClass enum PathClass Vm; Runtime
HelperScopeKind enum HelperScopeKind LauncherApp; WaylandProxy; PersistentShell
HelperScopeState enum HelperScopeState Starting; Active; Stopping; Exited; Degraded
HelperFailureCode enum HelperFailureCode InvalidRequest; OperationIdConflict; QueueFull; Timeout; UserManagerUnavailable; EnvironmentInvalid; ExecutableUnavailable; ScopeCreateFailed; ScopeIdentityMismatch; GraphicalSessionInactive; WaylandUnavailable; ProxyUnavailable; FirstClientTimeout; ShellUnavailable; ShellNotFound; ShellAlreadyAttached; TerminalOutputGap; TerminalOffsetMismatch; TerminalClosed; InvalidTerminalSize; Internal
HelperOperationDisposition enum HelperOperationDisposition Committed; AlreadyCommitted; Completed
HelperTerminalTransport enum HelperTerminalTransport ConnectedUnixStream
DaemonToUnsafeLocalHelper enum DaemonToUnsafeLocalHelper HelloAccepted — (HelperHelloAccepted); Heartbeat — (HelperHeartbeat); Launch — (HelperLaunchRequest); Shell — (HelperShellRequest)
UnsafeLocalHelperToDaemon enum UnsafeLocalHelperToDaemon Hello — (HelperHello); Snapshot — (HelperSnapshot); Heartbeat — (HelperHeartbeat); Operation — (HelperOperationResult); TerminalReady — (HelperTerminalReady); Shell — (HelperShellResponse); Rejected — (HelperOperationRejected)
UsbipClaimSource enum UsbipClaimSource Declared — struct { firewall_ref: String; bind_ref: String }; Explicit

Error envelope

Every public daemon-API failure (Hello, host check, audit, status, broker dispatch, etc.) uses the same redacted typed envelope:

{
  "kind": "...",
  "code": 42,
  "message": "...",
  "remediation": "...",
  "docsAnchor": "...",
  "owningCommand": "..."
}

This is the shape emitted by d2bd, the broker, and the wire layer; CLI surfaces that proxy daemon-API responses deserialize and re-render it.

The CLI host-verb skeletons (d2b host prepare, host destroy, host doctor, host install) use a separate 7-field operator-UX envelope documented in error-codes.md. That envelope is emitted by the CLI itself when it intercepts a request before reaching the daemon (Tier-0 legacy refusals, missing-flag usage errors, typed daemon-down / not-yet-implemented envelopes per ADR 0015 v1.0 daemon-only). Its anchors live in the same error-codes.md catalog so both shapes resolve to a single source of truth for docs_anchor/docsAnchor resolution; the two shapes are tracked for future unification.

The redaction policy is strict:

  • no host paths unless the path itself is already part of the stable public contract;
  • no secrets, stack traces, credential material, or raw command output;
  • no terminal payloads, argv/env/cwd, helper stderr/stdout, raw shell session handles, raw shell names, supervisor ids, unit names, or helper diagnostics in daemon metrics or broad debug/audit fields;
  • one human-readable remediation hint per failure;
  • one docs anchor into error-codes.md.

owningCommand names the primary CLI subcommand or API surface that owns the failure class, for example host check, audit, status, or daemon-api/Hello.

Typed error envelope types

Type Kind Rust definition Shape
BrokerErrorResponse struct BrokerErrorResponse struct { kind: String; operation: String; target_wave: Option<String>; message: String; action: String }
GuestControlError struct GuestControlError struct { kind: GuestControlErrorKind; remediation: HealthRemediation; retry_after_ms: Option<u64> }
GuestControlErrorKind enum GuestControlErrorKind ProtocolError; MaxChunkExceeded; StdinBackpressure; StdinClosed; StdinNotOpen; StdinClosedByProcess; StdinOffsetMismatch; StdinByteBudgetExhausted; OffsetExpired; OffsetInFuture; OffsetExhausted; OutputLost; TtyStderrUnavailable; TtyRequired; ExecCapacityExceeded; ExecAttachCapacityExceeded; ExecNotFound; ExecAlreadyExited; GuestExecDisabled; GuestExecRootDenied; GuestExecUserDenied; CwdInvalid; CwdDenied; RetainedLogPathUnsafe; RetainedLogQuotaExceeded; ReadWaitCapacityExceeded; WaitCapacityExceeded; SupersededReadWait; RateLimited; RequestIdConflict; ControlSeqMismatch; SlowConsumerCancelled; StaleSession; GuestControlUnavailableOldGeneration; AuthFailed; TransportUnreachable; ExecExpired; FileNotFound; FileTooLarge; PathUnsafe; ReadDenied; InvalidProgram; UsbipUnavailable; UsbipCommandFailed; UsbipInvalidBusId; UsbipInvalidHost; GuestShellDisabled; ShellInvalidName; ShellCapacityExceeded; ShellAttachCapacityExceeded; ShellNotFound; ShellAlreadyAttached; ShellPoolUnavailable; ShellDaemonEpochMismatch; ShellOutputGap; UsbipCommandTimeout; UsbipInvalidOutput; ActivationInvalidId; ActivationInvalidPath; ActivationInvalidMode; ActivationNotFound; ActivationStatusUnavailable; ActivationTimedOut; ActivationSpawnFailed; AudioPipeWireUnavailable; AudioChannelUnknown; AudioLevelOutOfRange; AudioEnforcementFailed
ShellNameError struct ShellNameError empty struct
AudioErrorKind enum AudioErrorKind ProviderMisconfigured; VmNotFound; EnforcementUnavailable; AudioNotEnabled; InternalError
AudioVmError struct AudioVmError struct { vm: String; kind: AudioErrorKind; remediation: Option<String> }
BusIdError enum BusIdError Empty; Invalid; TooLong — struct { max: usize }

Audit

The broker is the only writer for the append-only audit log. Daily rotation is in effect, and there is no legacy single-file path: every record now lands in exactly one per-date file under /var/lib/d2b/audit/:

  • daily path: /var/lib/d2b/audit/broker-<utc-date>.jsonl (e.g. broker-2026-05-28.jsonl)
  • mode: 0640
  • owner/group: root:d2bd
  • append-only via a pre-opened O_APPEND fd held by the broker
  • retention: 14 days of daily files by default; override via d2b.site.audit.retentionDays (set to 0 to disable pruning entirely). Reserved: the broker prune-on-rotate loop is shipping, but the NixOS module does not yet thread the option value through to the broker invocation (daemon-config.json → d2bd → broker spawn args). Until then, the broker uses its 14-day default regardless of NixOS overrides.

Broker caller-role values such as peer_role = "d2b-launcher" are stable audit/authz class labels, not Unix group names. See naming-conventions.md § "Broker caller-role audit labels".

The audit CLI command does not read those files directly. Instead it sends ExportBrokerAudit { since, filter } to d2bd over the public socket. The daemon authorizes the caller against d2b.site.adminUsers; if the caller is allowed, the daemon forwards that request to the broker over /run/d2b/priv.sock and streams back redacted log entries. The broker enumerates every broker-YYYY-MM-DD.jsonl file in the audit directory in chronological order, applies the since and filter substrings, and returns the concatenated lines.

This keeps the read path narrow:

  • non-root launchers never get direct file access;
  • the daemon can read through the file's group permission as d2bd;
  • the broker remains the only writer because it owns the append-only write fd and enforces write serialization internally.

Host shutdown stop requests are distinguishable from ordinary admin stops at the daemon authorization layer. The systemd ExecStop hook connects as uid 0 and receives the narrow HostShutdown role; the daemon permits only vmStop for that role and forwards the request to the broker with the normal lifecycle audit path. Other admin-only daemon verbs remain denied for HostShutdown.

USBIP bind audit records may include deviceIdentity for privileged forensics. That projection keeps raw serial descriptors out of the log: VID/PID are normalized four-hex strings, serialObserved is boolean, and serial correlation uses HMAC-SHA256 with broker-owned root-only key files under ${d2b.site.stateDir}/secrets/usb-audit-serial-hmac/. When previous.key is present during the 30-day key-rotation grace window, the broker keeps both key slots active and emits both correlations. The companion UsbSerialCorrelationKeyRotate audit/log shape carries only previousKeyId, currentKeyId, activeKeyCount, graceWindowSeconds, and correlationVersion; it never contains raw serials, key material, bus IDs, sysfs paths, or dynamic metric labels. Key reload is per broker request, so non-root observability components do not need systemd credentials or a key-read IPC path.

Retention. The broker now prunes daily-rotated files older than d2b.site.audit.retentionDays (default 14) on every day-boundary rotation in append_to_daily and again on AuditLog::open so a long-stopped daemon catches up. Pruning is best-effort — failures are logged but do not break the audit-write path. Filename is the source of truth (broker-YYYY-MM-DD.jsonl); we never parse JSON to inspect record timestamps. Non-matching artifacts under audit/ (operator notes, export tarballs, etc.) are left alone.

Legacy retirement. The broker kept the historical /var/lib/d2b/broker-audit.log single-file path as a read+write compatibility shim while ExportBrokerAudit consumers and the broker-export-audit.sh / broker-socket-acl.sh Layer-1 gates migrated. The shim is dropped: both write paths (AuditEntry via write_entry and OpAuditRecord via write_op_record) and the read path (export_lines) operate solely against the daily files. The broker serve CLI takes --audit-dir <path> instead of the prior --audit-log-path flag.

Audit boundary. The broker audit log above is the local root-owned record for broker operations on this host. Any future gateway or realm aggregate audit (realm access events, provider operation records) is a separate record that lives inside the per-realm gateway guest VM, not in /var/lib/d2b/audit/; a remote full-host node keeps its own broker/audit records on that node's local audit path (broker-mediated and node-owned), not in the gateway guest. Relay or realm identity never enters this local broker audit or auth path: the record's identity fields (e.g. peer_uid, authz_result) carry only the local SO_PEERCRED-derived classification, and decision is the local broker operation outcome.

d2bd also writes daemon-owned JSONL events for transitions that are not broker operations, such as guest-control exec session establishment and runner readiness failures. Those records live under the daemon state directory as daemon-events-<utc-date>.jsonl. Each daemon record carries bounded event metadata plus prev_hash and record_hash fields so a verifier can detect missing, reordered, or modified lines. The hash-chain helper reports tampering explicitly; the write path remains best-effort so an audit sink outage does not abort an already-authorized local operation.

Persistent shell daemon events are in this daemon-owned stream, not the broker audit log. The provider-neutral ShellLifecycle variant is the sole runtime shell audit event for both guest-control and unsafe-local. It records only a configured canonical target or local VM id, admin peer uid, closed provider/action/result enums, optional force-takeover intent, and optional fixed operation/session correlation digests. It covers create, attach, list, detach, kill, close, and failure boundaries. Raw shell names, public handles, supervisor ids, terminal bytes, helper diagnostics, argv, env, cwd, PIDs, unit names, and paths are never written. Abrupt owner disconnects, close timeouts, stale helper generations, and malformed terminal frames are represented by closed results or typed-error buckets rather than free-form text.

Graceful-shutdown daemon events also use this daemon-owned stream. Before a normal stop sends CH vm.shutdown or broker-mediated QMP system_powerdown, d2bd records bounded intent fields (vm, peer uid/authz class, provider, timeout seconds, trigger). It records a final bounded outcome such as clean_guest_shutdown, clean_vmm_cleanup, api_unavailable, timeout_exceeded, or force_requested; raw provider HTTP/QMP payloads, socket paths, command output, argv, and guest-controlled strings stay out of audit records.

Daemon audit sink health is exposed as an explicit report rather than a silent fallback. The report distinguishes writable, degraded, and unavailable states, including retention-floor degradation. It intentionally records only closed reason codes such as write-probe-failed or retention-below-floor; it does not include filesystem paths, raw IO errors, argv/env content, output bytes, provider credentials, or relay tokens.

Forward-compatibility policy

The protocol deliberately mixes strict shape checking with bounded feature negotiation:

  • unknown feature flags are ignored during handshake negotiation;
  • unknown fields in known message types are rejected;
  • closed enums remain closed until a new negotiated version or explicit feature flag says otherwise;
  • additive message types belong in a new negotiated protocol revision, not as undocumented fields on an old one.

The result is intentionally conservative: callers may discover new capabilities, but they do not get silent success from partially understood payloads.

v2 constellation protocol schemas. The v2 constellation peer-session, operation-routing, and stream protocol shapes are not yet published as reference .md or .json contract files in this repository. Do not treat any draft field shape as a stable contract until the corresponding reference document is published.

Autostart contract

On d2bd.service startup — after the pidfd-table is restored from disk and orphan adoption has reconciled any leftover runners from a previous instance — the daemon runs a single autostart pass to bring per-env net VMs and workload VMs up in a controlled order. See docs/reference/daemon-autostart.md for the full contract (net VMs first, configurable concurrency cap, degraded-mode tolerance, idempotent re-entry). The cap is controlled by d2b.daemon.autostart.parallelism (default 3).

/etc/d2b/daemon-config.json carries both autostartParallelism and gracefulShutdownTimeoutSeconds. The latter is the daemon-wide default used when a v7 manifest entry has lifecycle.gracefulShutdown.timeoutSeconds = null.