Skip to main content

mkit_rpc/
lib.rs

1#![cfg_attr(not(test), deny(clippy::print_stdout, clippy::print_stderr))]
2#![doc = include_str!("../README.md")]
3//!
4//! Versioned wire protocols for mkit cross-system speech.
5//!
6//! mkit-rpc owns the schemas mkit uses to talk to processes outside
7//! its address space:
8//!
9//! - **External signers** (`signer.proto`): mkit-cli ↔ a subprocess
10//!   signer (file, FIDO2, TPM, future hardware backends).
11//! - **SSH transport** (`ssh.proto`): mkit-cli ↔ a remote
12//!   `mkit-server` over an `ssh(1)` child process.
13//! - **Signature verification** (`verify.proto`, issue #692):
14//!   message-only contract for the post-fetch commit/remix/tag check
15//!   `clone`/`pull`/`fetch` run by default. No bound RPC method yet —
16//!   mkit-cli's local dispatch calls the Rust implementation
17//!   (`mkit_core::sign::verify_commit`/`verify_remix`/`verify_tag`)
18//!   directly; the schema exists so a future ConnectRPC transport can
19//!   bind the identical check instead of reimplementing it.
20//!
21//! Shared vocabulary (`common.proto`) — algorithms, key forms, error
22//! codes, protocol-version negotiation — is re-exported at the crate
23//! root for convenience.
24//!
25//! ## Wire framing
26//!
27//! Both protocols use the same length-prefixed framing:
28//!
29//! ```text
30//! [u32 LE length][N bytes protobuf-encoded Frame]
31//! ```
32//!
33//! [`MAX_FRAME_BYTES`] caps the length at 1 MiB. Larger frames are
34//! a protocol error and the connection MUST be closed.
35//!
36//! ## Stability
37//!
38//! The schemas are frozen at protocol version 1 across the 0.x line.
39//! Wire-compatible additions (new enum values, new optional fields,
40//! new oneof variants) ship as patches. Breaking changes bump the
41//! protocol-version integer in `common.proto` and introduce sibling
42//! `signer2.proto` / `ssh2.proto` files rather than mutating v1.
43
44#![cfg_attr(docsrs, feature(doc_cfg))]
45// This crate (including the generated protobuf modules) contains zero
46// `unsafe` — enforce that it stays that way.
47#![forbid(unsafe_code)]
48
49// Generated protobuf modules. `_includes.rs` is emitted by
50// buffa-build; it sets up the module tree for common.proto,
51// signer.proto, ssh.proto under `mkit::rpc::v1`.
52include!(concat!(env!("OUT_DIR"), "/_includes.rs"));
53
54/// Maximum length of a single framed protobuf message in bytes.
55/// Both directions of both protocols enforce this cap; receivers
56/// MUST close the connection on a longer frame.
57pub const MAX_FRAME_BYTES: u32 = 1024 * 1024;
58
59/// The protocol version mkit v0.1.x speaks. Aliased here so callers
60/// don't need to chase the generated module path.
61pub const PROTOCOL_VERSION: i32 = crate::mkit::rpc::v1::ProtocolVersion::ProtocolVersion1 as i32;
62
63mod framing;
64mod helpers;
65pub use framing::{
66    FRAME_RECURSION_LIMIT, FrameError, frame_decode_options, read_frame, write_frame,
67};
68pub use helpers::{
69    CHUNK_DATA_MAX, MAX_REF_NAME, body_name, cond_to_wire, list_response_refs,
70    map_update_ref_error, ref_entry_to_ref, rpc_error_to_transport, signer_error_frame,
71    ssh_error_frame, unexpected_frame,
72};
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn protocol_version_constant_is_one() {
80        // Wire-load-bearing. v0.1.0 ships PROTOCOL_VERSION = 1.
81        // If this assert fails, common.proto was edited in a way
82        // that breaks the protocol contract — bump the integer
83        // explicitly rather than letting codegen drift.
84        assert_eq!(PROTOCOL_VERSION, 1);
85    }
86
87    #[test]
88    fn pin_response_debug_redacts_pin() {
89        // signer.proto marks `pin` with [debug_redact = true]; a stray
90        // `{:?}` log of a PinResponse must never echo the PIN.
91        let pr = crate::mkit::rpc::v1::signer::PinResponse {
92            pin: Some("123456".into()),
93            ..Default::default()
94        };
95        let dbg = format!("{pr:?}");
96        assert!(dbg.contains("[REDACTED]"), "Debug must redact pin: {dbg}");
97        assert!(!dbg.contains("123456"), "PIN leaked into Debug: {dbg}");
98    }
99}
100
101/// Public server-hook wire types and authentication (feature `hooks`).
102#[cfg(feature = "hooks")]
103pub mod hooks;
104
105/// Shared transport and health wire types and Connect service traits.
106#[cfg(feature = "transport")]
107#[allow(missing_debug_implementations, clippy::all, clippy::pedantic)]
108pub mod transport {
109    include!(concat!(env!("OUT_DIR"), "/_connectrpc.rs"));
110}