hayate 5.0.0

High-performance completion-based QUIC transfer engine.
Documentation

hayate is the high-performance transfer engine powering the Hayate CLI. It provides high-level HayateSender and HayateReceiver builders for encrypted file and directory transfers over QUIC, plus low-level modules for apps needing custom protocol control.

Driven by compio (an io_uring/IOCP runtime) and compio-quic, Hayate achieves blazing fast throughput utilizing a multi-threaded AEAD and Zstd pipeline, ephemeral X25519 key agreements, dynamic payload hashing (blake3, rapidhash, sha256), and safe tar streaming.

Installation

[dependencies]
hayate = "4.0"
compio = { version = "0.19", features = ["macros", "runtime", "fs", "net", "time"] }

Receive API

use std::net::SocketAddr;
use hayate::HayateReceiver;

#[compio::main]
async fn main() -> 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 // Accept the transfer
        },
        |bytes| println!("Received {bytes} bytes"),
    ).await?;

    // Saved payload info
    println!("Saved to {}", path.display());
    println!("Checksum: {checksum}");
    Ok(())
}

Send API

use std::net::SocketAddr;
use hayate::HayateSender;

#[compio::main]
async fn main() -> Result<(), hayate::EngineError> {
    let target: SocketAddr = "192.168.1.50:50001".parse().unwrap();

    let checksum = HayateSender::new()
        .target(target)
        .compress(true)
        .hash_algo("blake3".to_string())
        .send("photos", |bytes| println!("Sent {bytes} bytes"))
        .await?;

    println!("Checksum: {checksum}");
    Ok(())
}

Pairing Mode

Discover peers across the LAN via UDP broadcasts. The code phrase doubles as the HKDF salt.

use hayate::{HayateReceiver, HayateSender};

# async fn sender() -> Result<(), hayate::EngineError> {
HayateSender::new()
    .code("alpha-bravo-charlie".to_owned())
    .send("report.pdf", |_| {})
    .await?;
# Ok(())
# }

# async fn receiver() -> Result<(), hayate::EngineError> {
let (_checksum, _path) = HayateReceiver::new()
    .code("alpha-bravo-charlie".to_owned())
    .auto_accept(true)
    .receive("./downloads", |_| true, |_| {})
    .await?;
# Ok(())
# }

[!NOTE] Android devices and VPNs often block broadcasts. Fallback to direct IP mode when necessary.

Module Architecture

Module Purpose
runner Builder-style APIs: HayateSender and HayateReceiver.
transfer Pipeline orchestration: handshake, consent, multithreaded receive/send pool.
protocol Wire constants, frame flags, metadata structure, and limits.
crypto Ephemeral X25519, HKDF, AEAD wrappers, and cipher selection.
network QUIC bindings and ephemeral rustls configurations.
discovery Pairing-code UDP broadcast and listener logic.
tar Streaming directory packing and extraction.
local_addr Network interface and subnet utilities.
error Defines EngineError.

Runtime Notes

compio uses completion-based I/O. Buffers must be owned and passed by value to I/O operations (returning via compio::BufResult).

Hayate isolates blocking logic (tar extraction) and heavy CPU tasks (zstd / AEAD) off the executor using dedicated worker threads (std::thread::spawn) and flume channels. A BTreeMap handles chunk reordering on the receiver to guarantee sequential disk writes.

Safety Guarantees

Hayate treats network data as fundamentally hostile:

  • Encrypted Metadata: Filename, size, and hashing choices are authenticated before prompting the user.
  • Strict Framing: Frames are length-capped and verified via AEAD before decompression.
  • Size Verification: Completed file sizes must match the exact announced byte count.
  • Path Sanitization: Directory unpacking rigorously prevents absolute paths, parent directory traversals (..), symlinks, and hard links.