XSCP Specification

XSCP Logo

XSCP (XSCP Stream Communication Protocol) is a text-based communication protocol.

This specification is designed and maintained by Iván Amón, an Engineering student at UPM. It focuses on efficiency and the safety principles of the Rust programming language.

Ecosystem

XSCP is fundamentally language-agnostic. Because it relies strictly on standard TCP streams and UTF-8 text, it can easily be implemented in any programming language.

However, its native and reference ecosystem is built in Rust. The protocol's design heavily reflects Rust's core philosophies: memory safety, zero-allocation parsing, and bare-metal performance. By enforcing strict PDU sizes and avoiding complex nested structures, the Rust implementation guarantees safety against common network vulnerabilities (such as buffer overflows or memory leaks) while maintaining an incredibly low footprint.

Primary Use Cases

XSCP is a general-purpose communication protocol. While it does not impose restrictions on the type of data being exchanged (as long as it fits the UTF-8 text format), its design choices make it particularly efficient for:

  • Lightweight Messaging: Such as chat rooms or notification systems, thanks to its native support for asynchronous notifications and line-oriented parsing.
  • Microcontrollers and Embedded Systems: Ideal for devices like the ESP32 due to its low memory footprint and fixed PDU limits, allowing for pre-allocated buffers.
  • General Command & Control: Sending instructions to remote services without the overhead of heavier protocols like HTTP.

Project Resources

  • Source Code: GitHub.
  • Rust Crate: Integrate XSCP into your project via Crates.io.

Transport Layer

XSCP operates directly over TCP (Transmission Control Protocol). It relies entirely on TCP's built-in mechanisms to guarantee the reliable, ordered, and error-checked delivery of the data stream.

The protocol does not implement its own packet reordering or retransmission logic, delegating these responsibilities to the underlying transport layer.

  • Underlying Protocol: TCP
  • Default Port: 7878

Encoding and Data Format

XSCP is a text-based protocol that enforces a strict structure to ensure predictable parsing and high performance.

Encoding

All data exchanged via XSCP must be encoded in UTF-8. This ensures universal compatibility across different operating systems and programming languages while maintaining a simple byte-to-character relationship for ASCII-range characters.

Framing and Delimiters

The protocol is line-oriented, meaning each PDU (Protocol Data Unit) is treated as a single, discrete line of text.

  • PDU Terminator: Every message must end with the CRLF sequence (\r\n). Parsers should treat the arrival of \r\n as the signal to process the preceding buffer.
  • Field Separator: Within a PDU, fields are separated by the pipe character (|).
  • Structure: A typical PDU follows the pattern: FIELD1|FIELD2|PAYLOAD\r\n.

Formatting Constraints

To ensure integrity and prevent protocol smuggling, the following rules apply:

  • Control Fields: The pipe character (|) and CRLF sequence are strictly forbidden within control fields (such as the source or command identifiers).
  • Payload Flexibility: The pipe character (|) is allowed within the message payload section. The parser handles this by only splitting the PDU into a fixed number of structural segments, treating the remainder as the raw payload.
  • Line Breaks: The CRLF sequence remains globally forbidden within any part of the PDU content to avoid premature message termination.

3. PDU Definitions

This chapter defines the structure and semantics of the Protocol Data Units (PDUs) used in XSCP. Each PDU is designed to be parsed with minimal overhead, adhering to strict size constraints and a predictable field-based layout.

PDU Categories

XSCP classifies communication into three distinct types:

  1. Request: A message sent from a client to a server requiring a mandatory response.
  2. Response: A mandatory reply to a Request.
  3. Notification: An asynchronous, one-way message that does not require a reply.

Size Constraints (Network Budget)

To ensure compatibility with low-memory devices and maintain high performance, XSCP enforces strict byte limits:

  • Requests & Notifications: Maximum of 512 bytes.
  • Responses: Maximum of 38 bytes (designed for status codes and brief metadata).

Request PDUs

A Request is a message sent from a Client to a Server that triggers a specific action and requires a mandatory Response.

Wire Format

XSCP requests are structured as a single line of text with a strict field-based layout:

+------------------------------------------------------------------+
|   OPCODE (4 Bytes)   |    Source (Min 3 Bytes, Max 32 Bytes)     |
|------------------------------------------------------------------|
|          Message (Max 472 Bytes) + \r\n (2 Bytes)                |
+------------------------------------------------------------------+

Field Specification

1. OPCODE (Operation Code)

The OPCODE is a fixed-length functional identifier.

  • Type: 4-character ASCII string (Uppercase).
  • Length: Exactly 4 bytes.
  • Function: It acts as the instruction for the server's parser to route the request to the correct logic handler.
  • Valid Values: LOGN (Login), SEND (Message), EXIT (Disconnect).

2. Source (Sender Identity)

The Source field identifies who is making the request.

  • Type: UTF-8 string.
  • Length: Variable (Minimum 3 bytes, Maximum 32 bytes).
  • Constraints:
    • Prohibited Characters: Must not contain the pipe (|) character, as it would break the field segmentation.
    • Prohibited Sequences: Must not contain Carriage Return (\r) or Line Feed (\n).
  • Usage: Typically used for usernames or session IDs.

3. Message (Payload)

The Message field contains the data intended for the command's execution.

  • Type: UTF-8 string.
  • Length: Variable (Maximum 472 bytes).
  • Parsing Logic: The parser reads everything from the second pipe until the \r\n terminator.
  • Constraints:
    • Allowed Characters: The pipe (|) is permitted here, allowing for nested data or complex text.
    • Prohibited Sequences: Must not contain \r\n to prevent "PDU Smuggling" (injecting a second command into the same stream).

Validation Logic (Server-Side)

When a server receives a Request, it must validate the fields in this specific order:

  1. Framing Check: Verify the string ends with \r\n. If not, discard or wait for more data.
  2. Segmentation: Split the string using the first two pipes.
  3. OPCODE Validation: Check if the first 4 bytes match a known command. If not, return an error.
  4. Source Integrity: Check that the second segment is between 3 and 32 bytes and contains no forbidden characters.
  5. Size Compliance: Ensure the total PDU does not exceed 512 bytes.

Response PDUs

A Response is a message sent from a Server to a Client as the mandatory reply to a previously received Request. It reports the outcome of the operation in a compact, two-field format.

Wire Format

XSCP responses are structured as a single line of text with a strict field-based layout:

+-----------------------------------------------------------------------+
|   Status Code (1-3 ASCII digits)   |   Reason Phrase (Max 32 Bytes)   |
+-----------------------------------------------------------------------+

The two fields are delimited by a single pipe (|) and the line is terminated by \r\n. The total PDU size must not exceed 38 bytes (delimiter and CRLF included).

Field Specification

1. Status Code

The Status Code is a numeric identifier indicating the outcome of the request.

  • Type: ASCII numeric string.
  • Length: Variable (1 to 3 digits).
  • Range: 0599.
  • Function: It allows the client to programmatically determine whether the request succeeded, failed, or requires further action, without having to interpret human-readable text.

Available status codes:

  • 200: The XSCP Request was processed successfully.
  • 400: Bad XSCP Request.
  • 401: Invalid Credentials.
  • 402: Host exceeded auth attempts.
  • 500: Internal XSCP server error.

2. Reason Phrase

The Reason Phrase is a short, human-readable description that complements the status code.

  • Type: UTF-8 string.
  • Length: Variable (Maximum 32 bytes).
  • Constraints:
    • Prohibited Characters: Must not contain the pipe (|) character, as it would break the field segmentation.
    • Prohibited Sequences: Must not contain Carriage Return (\r) or Line Feed (\n), to prevent "PDU Smuggling" (injecting a second response into the same stream).
  • Usage: Intended for logging, debugging or display purposes. The reason phrase should be in Title Case. Clients must not rely on its exact content to drive logic — that is the role of the Status Code.

Validation Logic (Client-Side)

When a client receives a Response, it must validate the fields in this specific order:

  1. Framing Check: Verify the string ends with \r\n. If not, discard or wait for more data.
  2. Segmentation: Split the string using a single pipe. The result must contain exactly two segments.
  3. Status Code Validation: Verify that the first segment parses as a number and does not exceed 599.
  4. Reason Phrase Integrity: Check that the second segment is at most 32 bytes and contains no forbidden characters.
  5. Size Compliance: Ensure the total PDU does not exceed 38 bytes.

Notification PDUs

A Notification is an unsolicited message pushed from the Server to a Client (or relayed between Clients via the Server) to inform about an event. Unlike a Request, a Notification does not require a Response.

Wire Format

XSCP notifications are structured as a single line of text with a strict field-based layout:

+---------------------------------------------------------------------------+
|   Notification Type (4 Bytes)   |   Source (Min 3 Bytes, Max 32 Bytes)    |
|---------------------------------------------------------------------------|
|                 Message (Max 472 Bytes) + \r\n (2 Bytes)                  |
+---------------------------------------------------------------------------+

Field Specification

1. Notification Type

The Notification Type is a fixed-length functional identifier.

  • Type: 4-character ASCII string (Uppercase).
  • Length: Exactly 4 bytes.
  • Function: It acts as the instruction for the client's parser to route the notification to the correct logic handler.
  • Valid Values: BRDC (Broadcast).

2. Source (Origin Identity)

The Source field identifies who originated the notification.

  • Type: UTF-8 string.
  • Length: Variable (Minimum 3 bytes, Maximum 32 bytes).
  • Constraints:
    • Prohibited Characters: Must not contain the pipe (|) character, as it would break the field segmentation.
    • Prohibited Sequences: Must not contain Carriage Return (\r) or Line Feed (\n).
  • Usage: Either a hostname or nickname of a client (for BRDC notifications), or the reserved string XSCP_SERVER when the notification is emitted by the server itself.

3. Message (Payload)

The Message field contains the data carried by the notification.

  • Type: UTF-8 string.
  • Length: Variable (Maximum 472 bytes).
  • Parsing Logic: The parser reads everything from the second pipe until the \r\n terminator.
  • Constraints:
    • Allowed Characters: The pipe (|) is permitted here, allowing for nested data or complex text.
    • Prohibited Sequences: Must not contain \r\n to prevent "PDU Smuggling" (injecting a second notification into the same stream).

Validation Logic (Client-Side)

When a client receives a Notification, it must validate the fields in this specific order:

  1. Framing Check: Verify the string ends with \r\n. If not, discard or wait for more data.
  2. Segmentation: Split the string using the first two pipes.
  3. Notification Type Validation: Check if the first 4 bytes match a known notification type. If not, return an error.
  4. Source Integrity: Check that the second segment is between 3 and 32 bytes and contains no forbidden characters.
  5. Size Compliance: Ensure the total PDU does not exceed 512 bytes.

Connection Lifecycle (State Machine)

The following state machine defines the operational lifecycle of a connection within the XSCP protocol from the server's perspective. This architecture ensures a robust handling of resources by strictly separating the transport layer connections from the application layer authentication.

XSCP State Machine Diagram

1. State Definitions

  • CLOSED: The initial and final state. No network resources are allocated, and the connection does not exist or has been fully terminated.
  • LISTEN: The server socket is bound to a port and actively waiting for incoming TCP connections.
  • NEGOTIATING: A temporary phase reached immediately after a successful TCP handshake. The server waits for the client to provide a valid identity (nickname) before allowing full protocol interaction.
  • ESTABLISHED: The active communication state. The client is successfully identified, and the stream is fully open for processing requests and broadcasting notifications.
  • ABORTED: A transitional sink state reached due to protocol violations, network errors, or authentication failures. It guarantees that emergency cleanup (like memory freeing and socket dropping) is performed before releasing the session back to CLOSED.

2. Events and Transitions

State transitions are triggered by network events, client inputs, or internal server conditions.

Source StateDestination StateTrigger EventDescription
CLOSEDLISTENbind_and_listenThe server initializes and binds to the specified port.
LISTENNEGOTIATINGtcp_connection_acceptedThe OS completes the 3-way handshake; a dedicated handler is spawned.
LISTENCLOSEDcloseThe server shuts down the listener voluntarily.
NEGOTIATINGESTABLISHEDcredentials_validatedThe client provides a unique, valid nickname conforming to XSCP rules.
NEGOTIATINGNEGOTIATINGinvalid_credentialsThe nickname is malformed or already in use. The retry counter increments.
NEGOTIATINGABORTEDmax_retries_exceededThe client fails to provide valid credentials within the allowed attempts.
NEGOTIATINGABORTEDconnection_errorThe connection is dropped or times out during the negotiation phase.
ESTABLISHEDABORTEDconnection_errorA protocol violation (e.g., PDU > 512 bytes) or an unexpected network disconnection occurs.
ABORTEDCLOSEDcloseInternal cleanup finishes, and the file descriptor is fully released.

3. Security and Retry Policy

To mitigate brute-force attacks, resource exhaustion, and "phantom" connections, XSCP enforces a strict lifecycle validation during the NEGOTIATING phase:

  1. Clients are granted a limited number of attempts to submit a valid identity.
  2. Each invalid_credentials event loops back to NEGOTIATING and increments an internal counter.
  3. Once the limit is reached, the max_retries_exceeded event forces the connection into ABORTED, dropping the client immediately without allocating further memory.