# How Croc Handles Secure File Transfers Without Port Forwarding: Relay Architecture Explained

> Discover how croc bypasses port forwarding for secure file transfers using a relay architecture with PAKE and AES-GCM encryption for robust end-to-end security.

- Repository: [Zack/croc](https://github.com/schollz/croc)
- Tags: architecture
- Published: 2026-07-26

---

**Croc achieves secure file transfers without port forwarding by routing all traffic through a public relay server that acts as a TCP-level proxy, while Password-Authenticated Key Agreement (PAKE) and AES-GCM encryption ensure end-to-end security that the relay cannot compromise.**

Croc is an open-source command-line tool developed by schollz that enables encrypted file sharing between any two computers without configuring firewalls or routers. Understanding how croc handles secure file transfers without port forwarding requires examining its unique relay-based architecture, which masks NAT complexity through outbound connections and cryptographic handshakes. The implementation leverages specific packages in the `schollz/croc` repository to establish secure channels that completely bypass the need for manual port forwarding.

## The Relay-Only Transport Architecture

Instead of requiring peers to accept inbound connections, croc routes all traffic through a publicly reachable **relay** server. When a sender initiates a transfer with `croc send`, the client establishes an outbound TCP connection to the configured relay address, defaulting to `croc.schollz.com` on ports `9009`, `9010`, and subsequent ports as defined in [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go).

Because both the sender and receiver initiate **outbound connections** to this relay, typical home routers and NAT devices allow the traffic through automatically. The relay acts solely as a dumb TCP pipe, forwarding encrypted bytes between peers without ever holding the decryption keys. This architecture eliminates the need for manual firewall configuration or port forwarding rules on either end of the transfer.

The relay configuration also supports **IPv6-first connectivity** with automatic IPv4 fallback, configured through the address defaults in [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go) (lines 94-101). The repository additionally provides [`src/webrelay/webrelay.go`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go), which exposes an embedded web client that bridges HTTP/WebSocket connections to the same TCP relay infrastructure, enabling browser-based transfers without installing the CLI.

## Password-Authenticated Key Agreement (PAKE)

Once the TCP connection to the relay is established, croc performs a cryptographic handshake to generate a shared secret that the relay cannot access. Inside [`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go), the implementation calls `pake.InitCurve` to initiate the PAKE exchange (lines 78-84).

This handshake creates a **strong session key** (`strongKey`) derived from the user's code phrase without transmitting the key itself over the network. The PAKE protocol guarantees that even if an attacker controls the relay or eavesdrops on the connection, they cannot derive the session key or decrypt the subsequent communication. This step is crucial for establishing trust between sender and receiver without relying on the relay's integrity.

## End-to-End Encryption Implementation

After the PAKE handshake completes, croc feeds the `strongKey` into the `crypt` package to derive an encryption key via PBKDF2 (or Argon2, depending on version). The implementation in [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go) (lines 16-33) provides `crypt.New` to initialize the cipher and `crypt.Encrypt`/`crypt.Decrypt` for payload protection.

All data—including the transfer password, file metadata, and individual file chunks—is encrypted using **AES-GCM** before being transmitted to the relay. Because encryption occurs after the secure key exchange, the relay merely forwards ciphertext without ever learning the file contents, metadata, or authentication credentials. This ensures true end-to-end encryption where the relay has zero knowledge of the transferred data.

## Practical Usage and Code Examples

Using croc requires no configuration files or network setup. The CLI ([`src/cli/cli.go`](https://github.com/schollz/croc/blob/main/src/cli/cli.go)) parses commands and constructs a `croc.Client` via `croc.New` (defined in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go), lines 28-33), automatically handling the relay connection and encryption handshake.

**Sending a file via CLI:**

```bash
croc send myphoto.jpg

# Output:

# Sending 'myphoto.jpg' (2.3 MiB)

# Code is: sunrise-apple-tiger

```

**Receiving on another machine:**

```bash
croc sunrise-apple-tiger

# File automatically saves to current directory

```

**Programmatic usage in Go:**

```go
import (
    "github.com/schollz/croc/v10/src/croc"
    "github.com/schollz/croc/v10/src/models"
)

func main() {
    opts := models.Options{
        RelayAddress: "croc.schollz.com", // default relay
        Debug:        true,
    }
    client, _ := croc.New(opts)

    // Send a file
    client.SendFile("example.txt")

    // Receive (blocks until sender uses matching codephrase)
    client.Receive()
}

```

The `croc.New` function initializes the client with the specified relay address, while the underlying `tcp` and `crypt` packages manage the connection and security layers transparently.

## Summary

- **Relay-based architecture**: Both peers connect outbound to a public relay (default `croc.schollz.com`), eliminating the need for inbound port forwarding or NAT traversal.
- **Zero-knowledge relay**: The relay only forwards TCP traffic and cannot decrypt content because encryption keys are established independently via PAKE.
- **Cryptographic handshake**: `pake.InitCurve` in [`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go) generates a `strongKey` that never traverses the network, preventing man-in-the-middle attacks even if the relay is compromised.
- **AES-GCM encryption**: All payloads are encrypted using keys derived via PBKDF2/Argon2 in [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go), ensuring end-to-end confidentiality.
- **Cross-platform compatibility**: IPv6-first design with IPv4 fallback ensures the tool works across diverse network topologies without configuration.

## Frequently Asked Questions

### Does the croc relay server store any of my file data?

No. The relay server operates as a stateless TCP proxy that only forwards encrypted packets between connected peers. Because files are encrypted using AES-GCM before transmission via the `crypt.Encrypt` function in [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go), the relay possesses no mechanism to decrypt or persist the data it routes.

### What happens if the public relay is offline or blocked?

You can deploy a private relay using the croc relay binary and specify it via the `--relay` flag. The [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go) file documents the expected relay address format, and the `croc.Options` struct allows custom relay configuration when initializing clients programmatically.

### How does croc verify the identity of the receiving party?

Croc uses the PAKE protocol (`pake.InitCurve` in [`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go)) to ensure that only participants possessing the matching code phrase can derive the session key. This prevents unauthorized interception even if an attacker knows the relay room ID or connects to the same TCP socket, as they cannot complete the cryptographic handshake without the correct password.

### Is IPv6 required for croc to work without port forwarding?

No. While croc defaults to IPv6-first connectivity as configured in [`src/models/constants.go`](https://github.com/schollz/croc/blob/main/src/models/constants.go) (lines 94-101), it automatically falls back to IPv4 if IPv6 is unavailable. The relay architecture works identically on both protocols, maintaining NAT traversal capabilities regardless of which IP version connects to the public relay.