Skip to main content

shep_core/protocol/
mod.rs

1//! The client<->daemon wire protocol (version 7).
2//!
3//! Typed request/response enums plus bus events. Framing lives in
4//! [`wire`]; a serialized shape change bumps [`PROTOCOL_VERSION`].
5//! Version 4 bumped on an addition. Version 5 bumped on a new `AppConfig`
6//! field: that struct is `deny_unknown_fields`, so the additive rule below
7//! does not cover it and an older daemon cannot decode `depends_on`.
8//! Version 6 bumped on a retype: [`Response::Reloading`] became a struct
9//! variant to carry the apps a staged reload refused, so it serializes as
10//! an object where an older peer reads an array. Version 7 bumped on the
11//! same retype applied to [`Response::Restarted`], which a staged restart
12//! refuses apps of for the same reason and had nowhere to name them.
13//!
14//! A `*_wire_v7` test pins today's shape. A
15//! `v1_*_fixture_still_deserializes` test pins an old peer's payload and
16//! never renames.
17
18pub mod events;
19pub mod frame;
20pub mod request;
21/// Frame encoding shared by daemon and client
22pub mod wire;
23
24pub use events::{BusEvent, ProcessEventKind};
25pub use frame::ServerFrame;
26pub use request::{
27    ActionOutcome, ActionReply, DogSectionToml, DogSource, EnvValue, Envelope, ExitInfo, Hello,
28    HelloAck, HelloReply, Lamb, LineOutcome, LineReply, ProcessInfo, ProcessInfoBuilder, Reply,
29    Request, Response, RpcError, RpcErrorCode, SelectorSpec, SheepApplied, SheepConfigView,
30    SheepDrift, SheepRefusal, SignalOutcome, SignalReply, Smit, SmitError, sort_flock,
31};
32pub use shep_channel::{CHANNEL_VERSION, ChildMessage, ShepherdMessage};
33pub use wire::{MAX_FRAME_BYTES, WireError, codec, decode_frame, encode_frame, reply_id};
34
35/// The shepherd channel's wire types. Moved to the `shep-channel` crate;
36/// this path is kept so consumers of 0.1.x do not break. Use
37/// `shep_core::protocol` directly instead.
38#[deprecated(note = "use `shep_core::protocol` directly")]
39pub mod channel {
40    pub use shep_channel::{CHANNEL_VERSION, ChildMessage, ShepherdMessage};
41}
42
43/// Wire protocol version.
44///
45/// Additive optional fields (new serde-defaulted `Option<T>` fields, new
46/// variants behind `#[non_exhaustive]`) keep the version. Removing,
47/// renaming, or retyping anything serialized bumps it, recorded in the
48/// CHANGELOG. Byte fixtures in each protocol module pin the deserialize
49/// direction.
50pub const PROTOCOL_VERSION: u32 = 7;
51
52/// The oldest protocol this build accepts from a peer.
53///
54/// The handshake compares against this rather than [`PROTOCOL_VERSION`],
55/// so a change that only adds does not refuse anyone. This rises only
56/// when a message shape changes such that an older peer cannot read it,
57/// and raising it refuses every peer built below it, which is why the
58/// rules in the spec exist to make that rare.
59pub const MIN_SUPPORTED: u32 = 7;
60
61#[cfg(test)]
62mod tests {
63    use super::{MIN_SUPPORTED, PROTOCOL_VERSION};
64
65    #[test]
66    fn a_retyped_restarted_forced_the_protocol_version_up() {
67        // fails if `Response::Restarted` becomes a struct variant without
68        // the bump, the same way `Response::Reloading` forced 6. The
69        // variant serializes as an object now where it used to serialize
70        // as an array, so an older peer decodes neither, and the handshake
71        // is the only place that can say so.
72        assert_eq!(PROTOCOL_VERSION, 7);
73    }
74
75    #[test]
76    fn the_floor_never_outruns_the_ceiling() {
77        // A floor above the current version would refuse every peer,
78        // including one built from this exact commit. Both sides are
79        // `const`, so clippy wants the check itself const-evaluated.
80        const { assert!(MIN_SUPPORTED <= PROTOCOL_VERSION) };
81    }
82}