How the Croc Relay System Works as a Message Broker

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, 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, 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 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, 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. 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 (runtimeConfig), while the CLI accepts them as --relay and --pass arguments.

Practical Examples

Start a custom relay (TCP only):

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

Send a file using a specific relay:

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

Receive the file:

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 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 establishes end-to-end encryption before data transmission.
  • The WebSocket-to-TCP bridge in src/webrelay/webrelay.go enables browser clients via the copyStream function.
  • Fail-over logic in src/croc/croc.go maintains connectivity through automatic relay discovery.
  • Configuration options in 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →