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}