# Message Protocol Definitions in `message.go`: How Croc Structures Peer-to-Peer Communication

> Explore message protocol definitions in schollz/croc's message.go. Learn how JSON structs and constants structure secure peer-to-peer communication with optional compression and encryption.

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

---

**The [`src/message/message.go`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/src/message/message.go), it contains five fields:

- **`t`** – The message `Type` (using the constants discussed above)
- **`m`** – A text string payload for human-readable messages or metadata
- **`b`** – An optional binary blob for file data or cryptographic material
- **`b2`** – A secondary optional binary field for additional data
- **`n`** – 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`](https://github.com/schollz/croc/blob/main/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:

1. **JSON Serialization** – Converts the `Message` struct to bytes
2. **Compression** – Applies compression using utilities from [`src/compress/compress.go`](https://github.com/schollz/croc/blob/main/src/compress/compress.go)
3. **Encryption** – Optionally encrypts the compressed bytes using the provided key via [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/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:

1. **Decryption** – Decrypts the payload if a key is supplied
2. **Decompression** – Restores the original JSON bytes
3. **Unmarshaling** – Parses the JSON into a `Message` struct

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`:

```go
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:

```go
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:

```go
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.go`](https://github.com/schollz/croc/blob/main/src/message/message.go)** defines the core protocol using a `Message` struct 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 `Encode` function** serializes messages to JSON, compresses them, and optionally encrypts them using [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go).
- **The `Decode` function** reverses this process, handling decryption, decompression, and JSON unmarshaling.
- **The `Send` function** abstracts transport concerns by accepting a `comm.Comm` channel from [`src/comm/comm.go`](https://github.com/schollz/croc/blob/main/src/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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.