1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Reach an OpenAI-compatible model server from anywhere over p2p.
//!
//! The iroh types stay out of the public surface deliberately: callers
//! hold [`Ticket`]s and handles, so an iroh major upgrade is this crate's
//! problem, not its dependents'. The same rule shapes the error types:
//! failures arrive as [`ServeError`] / [`ConnectError`] variants a caller
//! can match a retry policy against, with the transport's own error
//! reachable only as an opaque [`source`](std::error::Error::source).
//!
//! # Features
//!
//! `serde` (off by default) implements `Serialize` and `Deserialize` for
//! [`Ticket`] — as its canonical string, the same text `Display` prints
//! and `FromStr` reads, never as a struct — and for [`PipeStatus`] and
//! [`PeerView`], as the identifiers [`PipeStatus::as_str`] already
//! freezes. For an embedder that renders a status page from a JSON DTO;
//! the CLI has no use for it.
//!
//! # Diagnostics
//!
//! This crate emits [`tracing`] events and installs no subscriber. A
//! library that installed one would take the choice away from the binary it
//! is linked into, and would silence or duplicate whatever that binary had
//! already set up; `modelpipe-cli` installs one under `-v`, and any other
//! embedder does whatever it already does. Without a subscriber the events
//! cost a global atomic read apiece and go nowhere.
//!
//! What they say is bounded on purpose. The serve side emits one `info`
//! line per exchange — method, path, backend status, outcome, elapsed — and
//! one per peer arriving and leaving. **No event carries a token, a ticket,
//! a header value, or a query string**, which is the same discipline the
//! hand-written [`Debug`](std::fmt::Debug) impls on [`Ticket`] and
//! [`TokenPolicy`] exist for: a credential in a log file is a credential
//! that leaked, and a log file is the easiest place in a system to forget
//! that. The property is asserted rather than intended — the tests drive a
//! request whose credential and query string are distinctive strings, and
//! then look for them in the captured output.
use Duration;
// Modules are declared in dependency order, which for this crate is also
// order of testability: a module names only ones above it, and everything
// above the orchestration line can be exercised without a socket. That is
// what lets the whole authentication edge run over `tokio::io::duplex()`,
// and it is a property to preserve rather than a description of how things
// happened to land.
//
// Every one of them is `mod`, never `pub mod`. The re-export block below
// is the entire public surface, so a module can be renamed, split or
// merged without that being a semver event, and `unreachable_pub` turns
// "added a type, forgot to export it" into a compile error.
// Pure: no I/O, no async.
// Orchestration: the two entry points, and the live pipes they return.
// This block is the public API. Everything above is a private module,
// free to be rearranged at will; every name below is versioned. Adding to
// this block is the one edit in the crate that cannot be walked back.
pub use ;
pub use ConnectHandle;
pub use ;
pub use ServeError;
pub use ServeHandle;
pub use ;
pub use ;
pub use TokenPolicy;
// Auto-trait promises, pinned. The handles and the ticket live inside
// consumers' `select!` arms, spawned tasks and daemon state, and the error
// types ride through `anyhow` — those embeddings need these bounds, and a
// sketch whose types are only *accidentally* `Send + Sync` would let the
// implementation break every consumer after the fact. A regression here is
// a compile error in this crate instead.
const
// The async surface gets the same treatment: a spawned task awaiting one
// of these futures needs them `Send`, and an implementation that held a
// non-Send guard across an await point would compile on its own while
// breaking exactly that embedding. Pinning the futures has to name them,
// which means calling the functions — dead code, type-checked, never run.