gregg_protocol/lib.rs
1//! `gregg-protocol` defines the versioned JSON wire contract shared by the
2//! `greggd` daemon and the `gregg` client.
3//!
4//! The crate is intentionally dependency-light (only `serde`, `serde_json`, and
5//! `thiserror`) so it can be consumed by collectors, the HTTP server, the
6//! polling engine, and tests without dragging in larger stacks.
7//!
8//! # Schema versions
9//!
10//! ## Version 1
11//!
12//! Every snapshot carries an explicit
13//! [`SCHEMA_VERSION_V1`](constant.SCHEMA_VERSION_V1) so clients can reject
14//! incompatible payloads per host without terminating the whole TUI.
15//!
16//! Numeric values are transported as raw units — bytes for memory and swap,
17//! percentages in the closed interval `0.0..=100.0` for utilization, and
18//! milliseconds since the Unix epoch for timestamps. No human-formatted
19//! strings cross the wire.
20//!
21//! ## Version 2
22//!
23//! Schema version 2 ([`SCHEMA_VERSION_V2`](constant.SCHEMA_VERSION_V2))
24//! extends v1 with explicit capability flags for load average, swap, and
25//! memory commit. This allows the protocol to truthfully represent Linux,
26//! macOS, and Windows metric differences without fabricating unsupported
27//! values.
28//!
29//! V2 snapshots use `Option` for metrics that are unsupported on some
30//! platforms. Capability flags determine which `Option` values must be
31//! `Some` (supported) vs `None` (unsupported).
32//!
33//! The `/v2/status` response is a flat [`v2::StatusPayloadV2`] wrapper. Its
34//! optional `drives` field is additive and does not change the public Rust
35//! struct-literal compatibility of [`v2::StatusSnapshotV2`].
36//!
37//! # Compatibility policy
38//!
39//! Within each schema version:
40//!
41//! - Unknown additive JSON fields are ignored by default.
42//! - Required version-1 fields remain required unless explicitly changed to
43//! optional under an additive compatibility decision.
44//! - Capability flags control interpretation of optional metrics. A `None`
45//! value paired with a `false` capability is expected; a `None` value
46//! paired with a `true` capability indicates a missing or still-warming
47//! sample.
48//!
49//! # Examples
50//!
51//! ## Version 1
52//!
53//! ```
54//! use gregg_protocol::{StatusSnapshot, HealthResponse, ReadinessState, SCHEMA_VERSION_V1};
55//!
56//! let json = format!(r#"{{
57//! "schema_version": {sv},
58//! "observed_at_unix_ms": 1,
59//! "sample_interval_ms": 1000,
60//! "capabilities": {{ "cpu_iowait": false }},
61//! "system": {{
62//! "name": "mac-mini",
63//! "hostname": "mac-mini.local",
64//! "os_name": "macos",
65//! "os_version": "15.0",
66//! "kernel_name": "Darwin",
67//! "kernel_release": "24.0.0",
68//! "architecture": "arm64"
69//! }},
70//! "cpu": {{ "logical_cores": 8, "usage_pct": 12.5, "iowait_pct": null }},
71//! "load": {{ "one": 1.1, "five": 0.9, "fifteen": 0.6 }},
72//! "memory": {{ "used_bytes": 1, "total_bytes": 2, "usage_pct": 50.0 }},
73//! "swap": {{ "used_bytes": 0, "total_bytes": 0, "usage_pct": 0.0 }}
74//! }}"#, sv = SCHEMA_VERSION_V1);
75//!
76//! let snap: StatusSnapshot = serde_json::from_str(&json).expect("valid snapshot");
77//! snap.validate().expect("snapshot validates");
78//!
79//! let health = HealthResponse::warming();
80//! assert_eq!(health.state, ReadinessState::Warming);
81//! ```
82//!
83//! ## Version 2
84//!
85//! ```
86//! use gregg_protocol::v2::{
87//! StatusSnapshotV2, MetricCapabilitiesV2, CpuMetricsV2, CommitMetrics,
88//! HealthResponseV2, SCHEMA_VERSION_V2,
89//! };
90//! use gregg_protocol::{ReadinessState, HealthCategory};
91//!
92//! let json = r#"{
93//! "schema_version": 2,
94//! "observed_at_unix_ms": 1,
95//! "sample_interval_ms": 1000,
96//! "capabilities": {
97//! "cpu_iowait": false,
98//! "load_average": false,
99//! "swap": false,
100//! "memory_commit": true
101//! },
102//! "system": {
103//! "name": "win-pc",
104//! "hostname": "win-pc.local",
105//! "os_name": "windows",
106//! "os_version": "10.0",
107//! "kernel_name": "Windows",
108//! "kernel_release": "10.0.19045",
109//! "architecture": "x86_64"
110//! },
111//! "cpu": { "logical_cores": 4, "usage_pct": 12.5, "iowait_pct": null },
112//! "memory": { "used_bytes": 2000000000, "total_bytes": 8000000000, "usage_pct": 25.0 },
113//! "commit": { "used_bytes": 3000000000, "limit_bytes": 8000000000, "usage_pct": 37.5 }
114//! }"#;
115//!
116//! let snap: StatusSnapshotV2 = serde_json::from_str(json).expect("valid v2 snapshot");
117//! gregg_protocol::validate_v2(&snap).expect("v2 validates");
118//!
119//! let health = HealthResponseV2::ready(snap);
120//! assert_eq!(health.state, ReadinessState::Ready);
121//! ```
122
123#![forbid(unsafe_code)]
124
125pub mod v2;
126
127mod health;
128mod snapshot;
129mod validate;
130mod validate_v2;
131
132#[cfg(feature = "test_support")]
133pub mod test_support;
134
135pub use health::{HealthCategory, HealthResponse, ReadinessState};
136pub use snapshot::{
137 CpuMetrics, LoadAverage, MemoryMetrics, MetricCapabilities, StatusSnapshot, SwapMetrics,
138 SystemIdentity,
139};
140pub use validate::{ValidationViolation, ViolationKind};
141pub use validate_v2::{validate_payload_v2, validate_v2, ValidationViolationV2, ViolationKindV2};
142
143/// Schema major version implemented by this crate (version 1).
144///
145/// Wire payloads whose `schema_version` does not match this value are
146/// rejected by [`StatusSnapshot::validate`]. Additive changes within version 1
147/// are allowed by the compatibility policy; breaking changes require a new
148/// schema major and explicit migration handling.
149pub const SCHEMA_VERSION_V1: u16 = 1;
150
151/// Maximum sampling cadence accepted by the wire validation rules.
152pub const MAX_SAMPLE_INTERVAL_MS: u64 = 86_400_000;