> ## Documentation Index
> Fetch the complete documentation index at: https://hayate.shiina.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfer Entire Directories with Hayate

> Hayate streams entire directories as encrypted tar archives with automatic compression and strict path-traversal protection for safe extraction.

Passing a directory to `hayate send` works exactly like passing a single file — Hayate handles the packaging, compression, and extraction transparently on both sides. Under the hood, the directory is streamed as a tar archive in real time, so no temporary file is written to disk before the transfer starts. The receiver extracts the archive directly into your chosen output directory with the same name as the source.

## Basic Directory Transfer

Run `hayate send` with a directory path on the sender and `hayate receive` with `--output` on the receiver. Both sides need to use the same code phrase in Pairing Mode, or the sender needs to target the receiver's IP and port in Direct Mode.

```bash theme={null}
# Sender
hayate send ./my-project --code "shared-code"

# Receiver
hayate receive --code "shared-code" --output ./downloads/
```

After the transfer completes, you'll find `./downloads/my-project/` on the receiver — matching the top-level directory name from the sender.

## How It Works Under the Hood

Understanding the pipeline helps you make good choices about compression and troubleshooting.

<Steps>
  <Step title="Tar streaming">
    Hayate walks the source directory and writes a tar archive directly into an in-memory stream. No temporary `.tar` file is created on the sender's disk — the archive is built on the fly as bytes are consumed.
  </Step>

  <Step title="Compression and encryption">
    The raw tar byte stream is compressed with Zstd (level 1 by default), then split into frames. Each frame is encrypted with AES-256-GCM (hardware-accelerated) or ChaCha20-Poly1305 and sent over a QUIC stream.
  </Step>

  <Step title="Streaming extraction">
    The receiver decrypts and decompresses each frame as it arrives, then feeds the resulting bytes into a streaming tar extractor. Entries are written to the output directory progressively — you start seeing files appear before the transfer has finished.
  </Step>

  <Step title="Integrity verification">
    Once all frames arrive, Hayate compares the stream checksum (Blake3 by default) computed on both sides. A mismatch causes the transfer to fail with an error rather than silently saving corrupt data.
  </Step>
</Steps>

## Security During Extraction

Hayate applies strict validation to every entry in the tar archive before writing it to disk.

| Threat                              | Hayate's response                  |
| ----------------------------------- | ---------------------------------- |
| Path traversal (`..` components)    | Entry is rejected; transfer aborts |
| Absolute paths (e.g. `/etc/passwd`) | Rejected; transfer aborts          |
| Symbolic links                      | Ignored entirely — not extracted   |
| Hard links                          | Ignored entirely — not extracted   |

<Warning>
  Malicious tar archives can contain entries with paths like `../../etc/crontab` designed to escape the output directory and overwrite arbitrary files. Hayate rejects these entries outright. **Never disable or bypass this validation** when accepting archives from untrusted sources.
</Warning>

## Large Directory Transfers

For directories full of already-compressed files (videos, images, ZIP archives), disabling compression avoids wasting CPU cycles recompressing data that won't shrink further.

```bash theme={null}
hayate send ./media-library --code "my-code" --compress=false
```

Use `--hash sha256` alongside `--compress=false` if you want a widely-recognised checksum format for the transfer log.

```bash theme={null}
hayate send ./media-library --code "my-code" --compress=false --hash sha256
```

## Nested Directories

Hayate preserves the full directory structure. A source tree like:

```
my-project/
  src/
    main.rs
    lib.rs
  tests/
    integration.rs
  Cargo.toml
```

is extracted to the output directory with the same layout intact:

```
./downloads/my-project/
  src/
    main.rs
    lib.rs
  tests/
    integration.rs
  Cargo.toml
```

Empty directories are included in the archive and reproduced on the receiver.

<Tip>
  For large transfers where you want maximum predictability and don't need auto-discovery, use **Direct Mode** with an explicit IP and port. It eliminates the mDNS discovery step, removes the pairing timeout, and lets the receiver stay up indefinitely waiting for the sender.

  ```bash theme={null}
  # Receiver
  hayate receive --port 50001 --output ./destination/

  # Sender
  hayate send ./large-dataset 192.168.1.50:50001
  ```
</Tip>
