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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! # Hayate Engine
//!
//! Hayate is a completion-based transfer engine for encrypted file and
//! directory movement across local networks. It is the library behind the
//! Hayate CLI, but it is designed to be embedded directly in Rust applications.
//!
//! The engine combines:
//!
//! * QUIC transport through `compio-quic`.
//! * Completion-based async I/O through `compio`.
//! * Ephemeral X25519 key agreement.
//! * HKDF-SHA256 key derivation.
//! * Blake3 or SHA256 payload integrity.
//! * ChaCha20-Poly1305 or AES-256-GCM frame encryption.
//! * Optional zstd compression.
//! * Safe tar streaming for directory payloads (symlinks rejected; hard links
//! supported by replay after the target file is extracted).
//!
//! Most applications should start with [`HayateSender`] and [`HayateReceiver`].
//! Lower-level modules remain public for custom transports, protocol testing,
//! or UI integrations that need direct control over handshakes and payload
//! streams.
//!
//! ## Direct Transfer
//!
//! Direct mode connects to a known receiver address.
//!
//! ```no_run
//! use std::net::SocketAddr;
//! use hayate::HayateSender;
//!
//! # async fn run() -> Result<(), hayate::EngineError> {
//! let target: SocketAddr = "192.168.1.50:50001".parse().unwrap();
//!
//! let sender = HayateSender::new()
//! .target(target)
//! .compress(true);
//!
//! let checksum = sender.send("photos", |bytes_sent| {
//! println!("sent {bytes_sent} bytes");
//! Ok(())
//! }).await?;
//!
//! println!("checksum {checksum}");
//! # Ok(())
//! # }
//! ```
//!
//! ```no_run
//! use std::net::SocketAddr;
//! use hayate::HayateReceiver;
//!
//! # async fn run() -> Result<(), hayate::EngineError> {
//! let bind_addr: SocketAddr = "0.0.0.0:50001".parse().unwrap();
//!
//! let receiver = HayateReceiver::new()
//! .bind(bind_addr);
//!
//! let (checksum, path) = receiver.receive(
//! "./downloads",
//! |meta| {
//! println!("incoming {} ({} bytes)", meta.filename, meta.total_size);
//! true
//! },
//! |bytes_received| {
//! println!("received {bytes_received} bytes");
//! Ok(())
//! },
//! ).await?;
//!
//! println!("saved to {}", path.display());
//! println!("checksum {checksum}");
//! # Ok(())
//! # }
//! ```
//!
//! ## Pairing-Code Transfer
//!
//! Pairing mode lets a sender and receiver find each other through LAN
//! broadcast using a shared phrase. The same phrase is also used during key
//! derivation, so peers that do not know it cannot derive the application-layer
//! metadata and payload key.
//!
//! ```no_run
//! use hayate::HayateSender;
//!
//! # async fn run() -> Result<(), hayate::EngineError> {
//! let sender = HayateSender::new()
//! .code("apple-bravo-charlie".to_owned());
//!
//! sender.send("report.pdf", |_| Ok(())).await?;
//! # Ok(())
//! # }
//! ```
//!
//! ```no_run
//! use hayate::HayateReceiver;
//!
//! # async fn run() -> Result<(), hayate::EngineError> {
//! let receiver = HayateReceiver::new()
//! .code("apple-bravo-charlie".to_owned())
//! .auto_accept(true);
//!
//! let (_checksum, _path) = receiver.receive("./downloads", |_| true, |_| Ok(())).await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Module Guide
//!
//! * [`runner`] provides the builder-style high-level API, including staged
//! transfers ([`TransferStage`], [`HayateSender::send_with`],
//! [`HayateReceiver::receive_with`], [`ListeningReceiver`]).
//! * [`transfer`] implements handshake, consent, payload send, and payload
//! receive.
//! * [`protocol`] defines wire constants, frame flags, and
//! [`protocol::Metadata`].
//! * [`crypto`] contains key agreement, HKDF, AEAD sealing, and AEAD opening.
//! * [`network`] binds QUIC endpoints and builds ephemeral TLS/transport
//! config.
//! * [`discovery`] handles pairing-code broadcast and discovery.
//! * [`tar`] packages directories and safely extracts directory payloads.
//! * [`local_addr`] exposes local IPv4 helpers for UI and discovery.
//! * [`error`] defines [`EngineError`].
//!
//! ## Protocol Shape
//!
//! A transfer establishes QUIC, opens a bidirectional stream, negotiates
//! protocol version and cipher capability, performs X25519 key agreement,
//! derives a 32-byte AEAD key, encrypts metadata (including chosen hash
//! algorithm), sends receiver consent, and then streams length-prefixed
//! encrypted payload frames. File transfers must finish with the
//! exact announced byte count; directory transfers are tar streams with
//! containment checks during extraction.
//!
//! ## Safety Guarantees
//!
//! Hayate treats peer input as untrusted:
//!
//! * Metadata is encrypted and authenticated before use.
//! * Unknown transfer types are rejected before receive routing.
//! * Payload frames are length-capped and AEAD-authenticated before writes.
//! * Directory extraction rejects absolute paths, `..`, and symlinks; hard
//! links are collected and replayed after their targets are extracted.
//! * Each transfer uses a fresh random HKDF salt and a transcript-bound key
//! derivation. When a passphrase is used, it is mixed into the IKM; this
//! authenticates the session against an eavesdropper but is **not a PAKE** on
//! its own — offline brute-force resistance depends on the word-list entropy
//! (see the CLI word-list hardening in the matching Tier 2 change).
//! * Receive task failures are returned as [`EngineError`] instead of
//! panicking.
//!
//! ## Runtime
//!
//! Public async APIs are intended to run inside a `compio` runtime, commonly
//! via `#[compio::main]`. Low-level callers must respect `compio` buffer
//! ownership: buffers are moved into I/O operations and returned through
//! `compio::BufResult`.
pub use ;
pub use EngineError;
pub use ;
pub use ;
/// Encode bytes as a lowercase hex string. Replaces the external `hex` crate.
pub