What Is tcp.go in the Croc Relay Server? A Deep Dive into the TCP Implementation

tcp.go implements the entire TCP-based relay server for croc, handling connection acceptance, PAKE authentication, room pairing, and bidirectional data piping between peers.

In the schollz/croc secure file transfer tool, tcp.go serves as the central nervous system of the relay server component. This single file contains the complete implementation of the TCP listener, cryptographic handshake logic, and room management system that enables secure peer-to-peer transfers without requiring port forwarding. Understanding the role of tcp.go in the croc relay server reveals how the tool establishes encrypted bridges between machines behind NAT firewalls.

Core Responsibilities of tcp.go in the Croc Relay Server

The src/tcp/tcp.go file defines a server struct that encapsulates all relay functionality, including configuration (host, port, password, banner), room state management, and a stop handler for graceful shutdown.

Listening for Inbound TCP Connections

The run() method creates the network backbone of the relay. According to the source at src/tcp/tcp.go#L19-L27, this function initializes a net.ListenConfig, binds to the configured address, and enters an acceptance loop that hands off new connections to handler goroutines. This design allows the relay to manage thousands of concurrent connection attempts.

Secure Handshake Using PAKE

Every incoming connection undergoes password-authenticated key exchange (PAKE) within clientCommunication(). As implemented in src/tcp/tcp.go#L78-L87, this function derives a strong encryption key from the shared password before any file data traverses the network. This ensures that even if the relay server is compromised, it cannot decrypt the transferred content.

Room Creation and Peer Pairing

The server implements a virtual "room" abstraction to pair participants. When the first client connects, tcp.go creates a room identified by a random string. As shown in src/tcp/tcp.go#L64-L71 and src/tcp/tcp.go#L84-L92, the second client joining the same room triggers the server to staple the two connections together, establishing the peer-to-peer bridge.

Bidirectional Data Piping

Once paired, the pipe() function (lines 87-95) spawns two goroutines that continuously copy bytes between sockets. This full-duplex channel remains active for the file transfer duration, with each goroutine handling one direction of traffic to maximize throughput.

Lifecycle Management and Health Checks

A background goroutine named deleteOldRooms (defined at src/tcp/tcp.go#L30-L38) enforces TTL policies to reclaim resources from abandoned transfers. Additionally, the pingRoom constant and PingServer function (lines 48-51 and 14-22) provide external monitoring tools a lightweight way to verify server health without interfering with active transfers.

Client-Side Connection Helpers

Beyond server logic, tcp.go exposes utilities that croc clients use to connect to relays.

Connecting to the Relay

The ConnectToTCPServer function (lines 37-46) abstracts the client-side PAKE exchange, banner retrieval, and room joining. Clients call this function to establish an encrypted channel to the other peer through the relay.

Health Monitoring

PingServer allows clients or external monitors to check relay availability. This function connects to the special pingRoom endpoint, validates the server banner, and returns the remote IP address, as documented in src/tcp/tcp.go#L48-L51.

Code Example: Starting the Croc Relay Server

To launch a relay instance programmatically, invoke the Run() function. This chains through RunWithOptionsAsync() → newDefaultServer() → server.start() → server.run():

// Host, port, and password are supplied via CLI flags
err := tcp.Run(debugLevel, host, port, password, banner)
if err != nil {
    log.Fatal(err)
}

This call initializes the TCP listener at src/tcp/tcp.go#L19-L27 and begins the acceptance loop that waits for peer connections.

Code Example: Connecting a Client to the Relay

Clients use ConnectToTCPServer to perform the PAKE handshake and join a room:

c, banner, ip, err := tcp.ConnectToTCPServer(
    "relay.example.com:4000",
    "myPassword",
    "randomRoomID",
)
if err != nil {
    log.Fatal(err)
}
defer c.Close()
// c now carries an encrypted channel to the other peer

This call executes the PAKE protocol defined at lines 78-87, transmits the password, receives the server banner and remote address, and finally joins the specified room to await the paired connection.

How tcp.go Integrates with the Croc Architecture

The relay implementation in src/tcp/tcp.go does not operate in isolation. It relies on:

  • src/comm/comm.go: Provides the Send and Receive wrappers around net.Conn used during the PAKE handshake and room negotiation.
  • src/crypt/crypt.go: Supplies symmetric encryption functions (Encrypt, Decrypt) that protect the data streams established by pipe().
  • src/models/models.go: Defines constants such as TCP_BUFFER_SIZE referenced in channel creation routines.
  • src/croc/croc.go: Serves as the CLI entry point that parses user flags and ultimately invokes tcp.Run to start the relay service.

Together, these files form a complete relay-server stack where tcp.go manages the transport layer while delegating cryptography and message framing to specialized modules.

Summary

  • tcp.go implements the complete TCP relay server in schollz/croc, from socket creation to data forwarding.
  • The file handles PAKE-based authentication to derive encryption keys without transmitting passwords over the wire.
  • It manages room-based peer pairing, connecting two clients who share the same room ID through a temporary virtual channel.
  • Bidirectional piping via the pipe() function creates full-duplex data streams between paired sockets.
  • Automatic cleanup via deleteOldRooms prevents resource leaks from abandoned transfers.
  • Client helpers like ConnectToTCPServer and PingServer provide convenient APIs for establishing connections and monitoring relay health.

Frequently Asked Questions

What is the exact role of tcp.go in the croc relay server implementation?

tcp.go serves as the core engine that accepts inbound TCP connections, authenticates them using PAKE, pairs two participants in a temporary room, and pipes encrypted data between them. It implements both the server-side listener and client-side connection utilities required for the relay to function.

How does tcp.go secure connections between peers who have never met?

The file implements password-authenticated key exchange (PAKE) in the clientCommunication() function (lines 78-87). Both sides derive the same strong encryption key from a shared password without actually transmitting that password over the network, preventing man-in-the-middle attacks even against a malicious relay operator.

What happens if a client disconnects before pairing with another peer?

The deleteOldRooms goroutine (lines 30-38) periodically scans active rooms and removes those that exceed their time-to-live (TTL). This cleanup process reclaims memory and socket resources when a transfer abandons or fails before the second peer connects.

Can tcp.go handle multiple simultaneous file transfers?

Yes. The run() function spawns a new goroutine for every incoming connection (lines 19-27), and room state is maintained in a thread-safe map structure. This architecture allows the relay to manage hundreds of concurrent rooms, each isolated from the others, while the pipe() function handles bidirectional copying for each paired connection independently.

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 →