# How the Croc Relay System Works as a Message Broker

> Discover how the croc relay system functions as a message broker forwarding encrypted byte streams securely via PAKE authentication. It acts as a bidirectional pipe for TCP sockets.

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

---

**The croc relay system acts as a lightweight message broker that forwards opaque encrypted byte streams between clients without interpreting the payload, establishing secure channels via PAKE authentication and functioning as a bidirectional pipe for TCP sockets.**

The schollz/croc repository implements a secure file transfer tool where a central relay facilitates connections between sender and receiver. Understanding how the croc relay system works as a message broker reveals why it can forward arbitrary binary data without compromising end-to-end encryption or requiring payload inspection.

## TCP Connection and PAKE Authentication

Both the sender and receiver initiate TCP connections to the configured relay host, defaulting to `croc.schollz.com` on allow-listed ports such as `9009`. In [`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go), the client establishes this connection before performing cryptographic handshakes.

### Password-Authenticated Key Exchange

Upon connection, croc executes a **Password-Authenticated Key Exchange (PAKE)** using the user-supplied relay password. As implemented in [`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go), this handshake establishes a shared secret that encrypts all subsequent traffic. The relay itself does not participate in or verify this exchange; it merely transports the opaque bytes between endpoints.

## Bidirectional Data Flow as a Message Broker

After authentication, the croc relay operates as a pure **message broker** by copying data between two TCP sockets without parsing content. The sender streams encrypted file chunks into its connection, while the receiver reads identical bytes from its connection.

This design means the relay never touches the plaintext payload, enabling it to forward any binary data—files, streams, or protocol messages—while maintaining zero knowledge of the content.

## WebSocket-to-TCP Bridge Architecture

For web browser clients, croc implements a bridge that translates WebSocket connections into raw TCP streams. The implementation in [`src/webrelay/webrelay.go`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go) handles this translation through goroutine-based stream copying.

### The copyStream Implementation

When a web client connects via `/ws?port=9009`, the server validates the request against `allowedPorts` (lines 31-38). It then dials the upstream TCP relay using `net.Dialer.DialContext` (lines 42-48). Two goroutines execute the `copyStream` function (lines 71-89) to forward data bidirectionally between the WebSocket and TCP socket, effectively treating the relay as a transparent byte conduit.

## Relay Discovery and Fail-Over Logic

Croc implements resilient connection handling through candidate relay lists. In [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go), the logic handles `c.relayControlAddress` to gather multiple relay candidates. If the primary relay becomes unreachable, the client automatically retries alternative addresses until establishing a working connection, ensuring the message broker remains accessible even during individual node failures.

## Configuring the Relay via CLI

Users control relay connection parameters through flags defined in [`src/cli/cli.go`](https://github.com/schollz/croc/blob/main/src/cli/cli.go). The parser exposes `RelayAddress` and `RelayPassword` options that populate the runtime configuration used by both CLI and web interfaces.

The web client retrieves these settings via [`/config.js`](https://github.com/schollz/croc/blob/main//config.js) (`runtimeConfig`), while the CLI accepts them as `--relay` and `--pass` arguments.

## Practical Examples

*Start a custom relay (TCP only):*

```bash
croc relay \
  --host myrelay.example.com \
  --ports 9009,9010,9011 \
  --relay-password secret-pass

```

*Send a file using a specific relay:*

```bash
croc send \
  --relay myrelay.example.com \
  --pass secret-pass \
  myfile.txt

```

*Receive the file:*

```bash
croc receive \
  --relay myrelay.example.com \
  --pass secret-pass

```

*Web client endpoint:*

Browser clients connect via `wss://relay-host/ws?port=9009`, triggering the `copyStream` logic in [`src/webrelay/webrelay.go`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go) to bridge the browser WebSocket to the underlying TCP relay.

## Summary

- The croc relay functions as a **lightweight message broker** that forwards opaque byte streams without payload inspection.
- **PAKE authentication** in [`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go) establishes end-to-end encryption before data transmission.
- The **WebSocket-to-TCP bridge** in [`src/webrelay/webrelay.go`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go) enables browser clients via the `copyStream` function.
- **Fail-over logic** in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go) maintains connectivity through automatic relay discovery.
- Configuration options in [`src/cli/cli.go`](https://github.com/schollz/croc/blob/main/src/cli/cli.go) provide flexible relay addressing for both CLI and web interfaces.

## Frequently Asked Questions

### Does the croc relay store transferred files?

No. The relay acts as a transient message broker that only forwards byte streams between connected sockets. It does not buffer, cache, or persist any part of the encrypted payload, ensuring the server operator cannot access transferred content.

### How does the relay handle NAT traversal?

The relay facilitates NAT traversal by providing a publicly accessible meeting point where both sender and receiver can initiate outbound TCP connections. Since both clients connect outward to the relay, they bypass firewall restrictions that typically block inbound connections, with the relay brokering the bidirectional data flow between these established sockets.

### What is the difference between the public relay and a private relay?

The public relay (`croc.schollz.com`) is a shared infrastructure available to all users, while a private relay is an instance you host yourself using the `croc relay` command. Private relays offer control over the `--relay-password` and `--ports` configuration, isolation from public traffic, and the ability to operate within closed networks.

### How does the WebSocket bridge maintain security?

The WebSocket bridge in [`src/webrelay/webrelay.go`](https://github.com/schollz/croc/blob/main/src/webrelay/webrelay.go) does not terminate encryption; it merely transports the opaque encrypted byte stream between the browser's WebSocket connection and the TCP relay. The PAKE handshake and subsequent encryption occur end-to-end between the sender and receiver clients, meaning the bridge cannot inspect traffic and maintains the same security guarantees as direct TCP connections.