Message Protocol Definitions in `message.go`: How Croc Structures Peer-to-Peer Communication
The src/message/message.go file defines Croc's core messaging protocol through JSON-serializable structs, string-based message type constants, and utility functions that handle optional compression and encryption.
Croc is a secure file transfer tool that coordinates complex handshakes between sender and receiver over arbitrary network transports. The message protocol definitions in message.go establish the fundamental data structures and serialization logic that power every PAKE exchange, metadata transfer, and termination signal. This file implements a lightweight, extensible protocol that can be secured with Croc's built-in compression and encryption layers.
Message Types and Structure
Protocol Message Types
The protocol defines a set of string-based constants that enumerate every possible message purpose. According to the Croc source code, these types span from TypePAKE (for the PAKE handshake) to TypeFileInfo (for file metadata), covering critical actions such as IP discovery, recipient readiness signals, and connection termination. You can find these definitions between lines 15-24 in src/message/message.go.
Each constant represents a distinct phase in the file transfer lifecycle, allowing peers to route messages to the appropriate handlers without ambiguity.
The Message Struct
The Message struct serves as the universal container for all protocol communication. As implemented in lines 27-33 of src/message/message.go, it contains five fields:
t– The messageType(using the constants discussed above)m– A text string payload for human-readable messages or metadatab– An optional binary blob for file data or cryptographic materialb2– A secondary optional binary field for additional datan– An integer field useful for sequence numbers or chunk indices
This structure is designed to be marshaled to and from JSON, making it transport-agnostic while remaining compact enough for efficient network transmission.
Serialization and Transport Utilities
String Representation
For debugging and logging purposes, the Message struct implements the fmt.Stringer interface. The String() method (lines 35-38) JSON-marshals the entire struct, providing a human-readable representation that excludes sensitive binary data while preserving the type and text fields.
Sending Messages
The Send function (lines 40-48) handles the complete transmission pipeline. It accepts three parameters: a comm.Comm channel (defined in src/comm/comm.go), an optional encryption key, and the Message itself. When invoked, the function encodes the message and writes it to the provided communication channel, returning any transmission errors encountered.
Encoding Pipeline
Before messages hit the wire, the Encode function (lines 50-64) processes them through a three-stage pipeline:
- JSON Serialization – Converts the
Messagestruct to bytes - Compression – Applies compression using utilities from
src/compress/compress.go - Encryption – Optionally encrypts the compressed bytes using the provided key via
src/crypt/crypt.go
The function returns the final byte slice ready for socket transmission, with internal logging that distinguishes between encrypted and plain-text operations.
Decoding Incoming Payloads
The Decode function (lines 66-84) reverses the encoding process. It accepts an optional encryption key and a byte slice payload, then executes:
- Decryption – Decrypts the payload if a key is supplied
- Decompression – Restores the original JSON bytes
- Unmarshaling – Parses the JSON into a
Messagestruct
This function includes logging to track successful reads and returns detailed errors if any stage of the pipeline fails.
Practical Usage Examples
Creating and sending a simple "recipient ready" message requires instantiating the struct and invoking Send:
msg := message.Message{
Type: message.TypeRecipientReady,
Message: "ready",
}
err := message.Send(commChannel, nil, msg) // no encryption key
if err != nil {
log.Fatalf("send failed: %v", err)
}
For scenarios requiring manual transport or storage, use Encode with encryption:
key := []byte("my-secret-key")
msg := message.Message{
Type: message.TypeFileInfo,
Bytes: []byte("filename.txt"),
Num: 12345,
}
payload, err := message.Encode(key, msg)
if err != nil {
log.Fatalf("encode error: %v", err)
}
// `payload` can now be written to a socket or stored.
Decoding a received payload on the receiving end uses the same key:
received, err := message.Decode(key, payload)
if err != nil {
log.Fatalf("decode error: %v", err)
}
fmt.Printf("Got %s message: %+v\n", received.Type, received)
Summary
src/message/message.godefines the core protocol using aMessagestruct with fields for type (t), text (m), binary data (b,b2), and integers (n).- Message types are string constants (e.g.,
TypePAKE,TypeFileInfo) that enumerate all possible protocol actions from handshake to file transfer. - The
Encodefunction serializes messages to JSON, compresses them, and optionally encrypts them usingsrc/crypt/crypt.go. - The
Decodefunction reverses this process, handling decryption, decompression, and JSON unmarshaling. - The
Sendfunction abstracts transport concerns by accepting acomm.Commchannel fromsrc/comm/comm.go, enabling the protocol to work over any network transport.
Frequently Asked Questions
What fields does the Message struct contain?
The Message struct contains five fields: t (the message Type), m (a text string), b and b2 (optional binary blobs), and n (an integer). These fields accommodate various protocol needs from simple text signals to file chunks and cryptographic nonces.
How does Croc secure message transmission?
Croc secures messages through the Encode and Decode functions, which optionally encrypt payloads using AES encryption from src/crypt/crypt.go after compressing them. The encryption is only applied when a key is provided, allowing the same code path for both secure and plaintext modes.
What is the difference between Encode and Send?
Encode prepares a message for transmission by converting it to bytes (JSON, compression, and optional encryption) but does not transmit it. Send is a higher-level function that calls Encode internally and then writes the result to a comm.Comm transport channel, handling the full transmission lifecycle.
Where are the message types defined?
Message types are defined as string constants in the src/message/message.go file, specifically between lines 15-24. These include TypePAKE for handshake initialization, TypeFileInfo for metadata exchange, and types for IP discovery and connection termination.
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 →