Skip to main content

Heartbeat

Struct Heartbeat 

Source
pub struct Heartbeat {
Show 13 fields pub pc_id: String, pub at: DateTime<Utc>, pub agent_version: String, pub hostname: Option<String>, pub os_family: Option<String>, pub agent_cpu_pct: Option<f64>, pub agent_rss_bytes: Option<i64>, pub agent_disk_read_bytes: Option<i64>, pub agent_disk_written_bytes: Option<i64>, pub quarantined_versions: Vec<String>, pub last_logon_user: Option<String>, pub last_logon_display_name: Option<String>, pub command_keys: Option<Vec<String>>,
}
Expand description

Liveness ping every agent sends on a 30 s cadence (see inventory_interval / heartbeat_interval in agent_config).

hostname and os_family are enriched baseline facts so the SPA agents page has something to show as soon as the agent boots — even when the full WMI-driven HwInventory hasn’t been (or can’t be) collected. Both stay Option<String> so older agents that don’t send them still deserialize cleanly.

Fields§

§pc_id: String§at: DateTime<Utc>§agent_version: String§hostname: Option<String>§os_family: Option<String>

Coarse OS bucket from std::env::consts::OS"windows", "linux", "macos". Rich OS metadata still flows through the inventory path; this is just the “agent is alive on a ” signal.

§agent_cpu_pct: Option<f64>

Agent process CPU usage, in percent-of-one-core (a process fully pinning one core reports 100; one pinning two cores reports 200). This is sysinfo’s convention — closer to top than to Windows Task Manager (which normalises by total cores, so a 1-core peg on an 8-core box shows up as ~12.5 % in TM). Divide by host core count if you want a host-normalised view. None is published on the very first heartbeat after process start, because sysinfo’s CPU% needs two consecutive samples to diff — populating it would always report 0.0 there and risk an operator misreading “agent isn’t doing anything”.

§agent_rss_bytes: Option<i64>

Agent process resident set size in bytes — sysinfo’s Process::memory(), which on Windows is PROCESS_MEMORY_COUNTERS_EX::WorkingSetSize (full working set, shared + private). Closest Task Manager column is “Working set (memory)”, NOT “Memory (private working set)” which would be PrivateUsage and sysinfo exposes separately as virtual_memory().

§agent_disk_read_bytes: Option<i64>

Absolute bytes the agent process has read from disk since it started. Wire format is cumulative (not delta) so dropped / out-of-order heartbeats don’t poison rate math for any client that wants to derive a rate by diffing successive snapshots. Today neither the backend projector nor the SPA does that diff — they just store and render the cumulative value. Future SPA work or an exporter can compute rate without a schema change.

§agent_disk_written_bytes: Option<i64>

Absolute bytes the agent process has written to disk since it started. Same shape as agent_disk_read_bytes.

§quarantined_versions: Vec<String>

#582 Phase 2: versions this agent’s boot sentinel rolled back after they crash-looped on boot. The self-update path refuses to (re-)deploy any version listed here, so the SPA’s rollout view can flag “PC-X failed to adopt target 0.43.51” — the fleet-wide signal that a rollout is bad. Empty (the common case) is skipped on the wire; older agents simply omit it and #[serde(default)] leaves it empty.

§last_logon_user: Option<String>

Most-recently signed-in account on this host, read from the Windows LogonUI registry key (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\LastLoggedOnUser). This is the DOMAIN\sam (or .\user) login name the sign-in screen last used; it survives logoff, so it’s populated even when no one is currently signed in. None on a never-signed-in host and on non-Windows agents (read_hklm_value returns None off-Windows) — see #655 for the cross-platform follow-up — so older agents keep sending valid heartbeats either way.

§last_logon_display_name: Option<String>

Display name paired with Self::last_logon_user, from LogonUI\LastLoggedOnDisplayName (e.g. "Yamada Taro"). None when unavailable.

§command_keys: Option<Vec<String>>

#1165: the command-signing key ids this agent currently trusts.

Reported so “which machines still trust the old key” is answerable. Without it, retiring a key is a guess: an agent that never received the replacement rejects every command at stage 3, and there is no way to know it was going to before it does.

Option<Vec<_>> rather than a plain Vec with skip_serializing_if = "Vec::is_empty" — the shape Self::quarantined_versions uses — because empty is the state this exists to surface. Skipping an empty list would put “this agent holds no keys” and “this agent is too old to say” on the wire as the same thing, and they need opposite responses: provision the first one, and upgrade the second before you can even ask. So:

  • None — the agent predates this field. Unknown, not empty.
  • Some([]) — reporting, and holds nothing. This is the work queue.
  • Some([kid, ..]) — what it will actually accept right now.

It reports the in-memory ring, not the registry. Those differ between a key landing on disk and the reload that picks it up (#1186), and the useful answer is what this agent would accept if a command arrived now — reporting the file would describe a machine that does not exist yet.

Trait Implementations§

Source§

impl Clone for Heartbeat

Source§

fn clone(&self) -> Heartbeat

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Heartbeat

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Heartbeat

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for Heartbeat

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more