Croc Reconnect Mechanism: How It Resumes Interrupted File Transfers Automatically
Croc automatically resumes interrupted transfers by creating a new reconnect room, applying exponential back-off delays, and cycling through remembered relay addresses to re-establish the PAKE handshake, supporting up to 10 retry attempts without requiring manual intervention.
The schollz/croc file transfer tool implements a robust croc reconnect mechanism that allows large file transfers to survive network interruptions without restarting from scratch. Located primarily in src/croc/croc.go, this protocol handles disconnection detection, state preservation, and secure re-handshaking automatically when connections drop.
Core Concepts of the Croc Reconnect Protocol
Version Negotiation and Compatibility
Before attempting any reconnection, croc validates that both sender and receiver support the reconnect protocol. The system defines a ReconnectVersion constant (set to 1 in src/croc/croc.go). If either peer reports an older version, the reconnect mechanism disables itself via canRetryTransfer to prevent protocol mismatches.
State Reset and Exponential Back-Off
When a transferDisconnectError occurs, croc initiates a controlled reset via resetForReconnectAttempt. This function clears file pointers, resets cryptographic key state, creates a fresh nextReconnectRoom for the retry attempt, and zeroes internal chunk counters.
The system then applies an exponential back-off strategy through reconnectBackoff(attempt), capping the delay at 5 seconds between retries. The default maxReconnectAttempts is set to 10.
Relay Candidate Selection
Croc maintains a memory of successful connections in reconnectRelayAddresses. During a retry, reconnectRelayCandidates builds an ordered list prioritizing the current control address first, followed by any previously remembered relay addresses. This ensures croc attempts the most likely working endpoints before falling back to alternatives.
PAKE Handshake Re-execution
Reconnection requires re-authentication. The sender invokes senderReconnectRelayAttempt, which internally calls senderWaitForHandshake to re-run the PAKE (Password Authenticated Key Exchange) protocol. The receiver uses receiverReconnectRelayAttempt, sending a single handshakeRequest byte to trigger the exchange. Once authenticated, transferWithReconnect loops back into the standard transfer routine.
Step-by-Step Reconnection Flow
When a network interruption occurs, croc executes the following sequence in src/croc/croc.go:
-
Disconnection Detection: The core
transfer()function returns atransferDisconnectErrorwhen the underlying TCP/relay connection drops. -
Retry Validation:
canRetryTransferchecks three conditions: the reconnect version compatibility, existence ofnextReconnectRoom, and whether the attempt counter remains belowmaxReconnectAttempts. -
Back-off Application: The system sleeps for the duration calculated by
reconnectBackoff(attempt). -
State Reset:
resetForReconnectAttemptclears per-file metadata while preserving the transfer progress (file offsets and chunk maps) stored in the client instance. -
Relay Dialing:
reconnectRelayCandidatesassembles the candidate list, andreconnectRelayAttemptdials each address sequentially until one accepts the connection. -
Secure Re-handshake: For senders,
senderReconnectRelayAttemptruns the full PAKE handshake. Receivers trigger the handshake viareceiverReconnectRelayAttempt. -
Transfer Resumption: Upon successful handshake,
transferWithReconnectresumes thetransfer()loop, continuing from the last acknowledged byte.
Practical Usage and Code Examples
Automatic Recovery via CLI
The reconnect mechanism requires no special flags. If a transfer interrupts, simply rerun the original command:
# Terminal 1 - Sender
croc send large-file.iso
# Terminal 2 - Receiver
croc receive
Croc detects the previous session state, creates the reconnect room automatically, and resumes the transfer.
Debug Output
Enable --debug to observe the reconnect logic:
croc --debug send file.txt
Example output during reconnection:
debug: reconnect attempt 1 after 100ms
debug: resetting transfer state for reconnect attempt 1
debug: could not reconnect to any relay: 127.0.0.1:9009: connection refused
Go API Implementation
Developers using the Go library get automatic reconnect for free:
c, _ := croc.New(croc.Options{
SharedSecret: "secret-code",
IsSender: true,
})
// Transfer automatically handles disconnections internally
err := c.Send(filesInfo, emptyFolders, totalFolders)
if err != nil {
log.Fatal("Transfer failed after retries:", err)
}
The Client struct internally invokes transferWithReconnect, wrapping all retry logic transparently.
Key Source Files Implementing Reconnect
src/croc/croc.go: ContainsReconnectVersion,maxReconnectAttempts,transferWithReconnect,resetForReconnectAttempt, andreconnectBackoff.src/croc/ctx.go: Provides cancellable context (c.stop) for clean aborts during retries.src/tcp/tcp.go: Low-level TCP wrapper (ConnectToTCPServer) used by reconnection attempts.src/comm/comm.go: Connection abstraction allowing the reconnect routine to swap underlyingCommobjects while preserving client state.src/message/message.go: Defines PAKE handshake messages re-exchanged duringsenderReconnectRelayAttemptandreceiverReconnectRelayAttempt.
Summary
- Croc's reconnect mechanism operates entirely within
src/croc/croc.go, requiring no manual user intervention. - The system supports up to 10 retry attempts with exponential back-off (max 5-second delay).
- State preservation ensures file transfers resume from the exact byte offset where interruption occurred.
- Relay candidate caching (
reconnectRelayAddresses) prioritizes known-good endpoints during reconnection sweeps. - Version checking via
ReconnectVersionprevents incompatible peers from attempting reconnection.
Frequently Asked Questions
How many times will croc retry a failed connection?
By default, croc attempts to reconnect 10 times before giving up. This limit is defined by the maxReconnectAttempts constant in src/croc/croc.go. Each retry applies an exponential back-off delay calculated by reconnectBackoff, capped at 5 seconds between attempts.
Do I need special flags to enable reconnect?
No. The croc reconnect mechanism is automatic and transparent. Simply rerun the same croc send or croc receive command after a failure. The tool detects the previous session through the shared secret, creates a nextReconnectRoom, and attempts to resume without additional configuration.
What happens if the protocol version mismatches during reconnect?
If canRetryTransfer detects that either peer reports a ReconnectVersion older than the current protocol version (version 1), reconnection is immediately disabled. This prevents state corruption and forces a fresh transfer rather than attempting incompatible reconnection logic.
How does croc know where to resume the file transfer?
Before executing resetForReconnectAttempt, croc preserves the transfer progress in the Client struct, including file offsets and chunk acknowledgment maps. After successfully re-handshaking via senderReconnectRelayAttempt or receiverReconnectRelayAttempt, the transfer function resumes reading from these preserved offsets, ensuring no data duplication or loss.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →