Skip to main content

darkbio_wire/protocol/
mod.rs

1// wire-rs: encrypted protocol between Ark and host
2// Copyright 2026 Dark Bio AG. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7//! Bidirectional requests over the transport, with pipelining and explicit sessions.
8//!
9//! A reader receives messages from each connection. Each session also has a writer
10//! that sends queued messages and a deadline worker that times out operations.
11//! Incoming requests and unread responses stay encoded until [`Session::recv`]
12//! or [`Promise::wait`] takes them. Invalid payloads close their original
13//! session when decoded.
14//!
15//! Each session limits accepted peer requests and buffered incoming bytes. Set
16//! both with [`Session::set_inbound_limits`] or [`Server::set_inbound_limits`].
17//! Exceeding a limit closes the session. The reader never waits for the application
18//! to make room. These limits are local; no flow control is negotiated with the
19//! peer. The outgoing queue has no capacity limit.
20//!
21//! The application opens a [`crate::transport::Stream`]; this layer constructs and
22//! owns its transport. [`connect`] establishes one client session. [`Server`] owns
23//! a persistent stream and accepts successive server sessions. Each [`Session`]
24//! owns its receive queue and closes when dropped. Its [`Requester`] and
25//! [`Responder`] handles always target that session, even after it closes and
26//! another session connects.
27//!
28//! Both sides use the same handle types. Sessions exchange [`Message`], the union
29//! of every body in the [`schema`]. The [`schema::host_to_ark::Content`] and
30//! [`schema::ark_to_host::Content`] oneofs hold what each side may send. Convert
31//! a received [`Message`] into the peer's oneof to dispatch on it exhaustively.
32//! Callers select response types when waiting on [`Promise<Message>`]. A request
33//! is refused with a [`schema::Error`], an application's own error type converting
34//! into one through [`CodedError`]. A request whose content this build does not
35//! know is refused as `UNKNOWN` by the session itself, so a newer peer learns what
36//! an older one serves. A response of unknown content is malformed, no request
37//! having asked for it.
38//!
39//! Requests and replies return promises without waiting for I/O. Their deadlines
40//! include time in the outgoing queue, and waiting does not restart the timeout.
41//! Transport write timeouts are independent, so a request can still reach the
42//! peer after its promise expires. The reader and writer run independently of the
43//! application, but the application must keep receiving and answering requests
44//! while its own requests wait for replies. All waiting is blocking; no async
45//! runtime is required. Every request expects a reply, including notifications.
46//!
47//! Closing a session fails its pending promises and discards queued messages.
48//! Completed promises keep their results. A write already in progress may still
49//! reach the peer.
50
51mod closer;
52mod envelope;
53mod error;
54mod message;
55mod operation;
56mod promise;
57mod requester;
58mod responder;
59mod server;
60mod session;
61mod worker;
62
63#[cfg(any(test, feature = "fuzz"))]
64#[cfg_attr(coverage_nightly, coverage(off))]
65#[doc(hidden)]
66pub mod mock;
67
68pub use closer::Closer;
69pub use error::{CodedError, Error};
70pub use message::Message;
71pub use promise::Promise;
72pub use requester::Requester;
73pub use responder::Responder;
74pub use server::Server;
75pub use session::{Session, connect};
76
77use std::time::Duration;
78
79/// Default timeout of 5 s for sending an automatic reply.
80///
81/// An automatic reply is `UNANSWERED` when a responder is dropped, or `UNKNOWN`
82/// to a request this build does not know. The timeout starts when the responder
83/// is dropped or the request arrives, and includes time in the outgoing queue.
84/// Configure it with [`Session::set_autoreply_timeout`] or
85/// [`Server::set_autoreply_timeout`]. Transport write timeouts are independent.
86pub const DEFAULT_AUTOREPLY_TIMEOUT: Duration = Duration::from_secs(5);
87
88/// Default limit of 1,024 accepted peer requests per session.
89///
90/// A request counts while queued, held by a responder, or waiting to send its
91/// reply. The slot is freed when the writer takes the reply or the reply is
92/// discarded. Configure it with [`Session::set_inbound_limits`] or
93/// [`Server::set_inbound_limits`]. Zero admits no peer requests.
94pub const DEFAULT_MAX_INBOUND_REQUESTS: usize = 1024;
95
96/// Default limit of 16 MiB for queued requests and unread response promises.
97///
98/// The limit counts their full encoded envelopes, and taking or dropping an
99/// envelope releases its bytes. Decoded application data, outgoing messages
100/// and transport buffers are excluded. Configure it with
101/// [`Session::set_inbound_limits`] or [`Server::set_inbound_limits`]. Zero
102/// permits no retained envelope bytes.
103pub const DEFAULT_MAX_INBOUND_BYTES: usize = 16 * 1024 * 1024;
104
105/// Protobuf bindings of the protocol, generated from `proto/wire.proto`.
106///
107/// The bindings are excluded from the lints of handwritten code. Every message
108/// implements [`prost::Message`] for raw encoding. Applications exchange the
109/// bodies through [`Message`], converting to the
110/// [`host_to_ark::Content`](schema::host_to_ark::Content) and
111/// [`ark_to_host::Content`](schema::ark_to_host::Content) oneofs to dispatch on
112/// one direction. The [`HostToArk`](schema::HostToArk) and
113/// [`ArkToHost`](schema::ArkToHost) envelopes are the wire form, handled by the
114/// session internally.
115#[allow(clippy::all)]
116#[allow(rustdoc::broken_intra_doc_links)]
117pub mod schema {
118    include!("generated/darkbio.wire.rs");
119}