XSCP Specification
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
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\nas 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:
- Request: A message sent from a client to a server requiring a mandatory response.
- Response: A mandatory reply to a Request.
- 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).
- Prohibited Characters: Must not contain the pipe (
- 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\nterminator. - Constraints:
- Allowed Characters: The pipe (
|) is permitted here, allowing for nested data or complex text. - Prohibited Sequences: Must not contain
\r\nto prevent "PDU Smuggling" (injecting a second command into the same stream).
- Allowed Characters: The pipe (
Validation Logic (Server-Side)
When a server receives a Request, it must validate the fields in this specific order:
- Framing Check: Verify the string ends with
\r\n. If not, discard or wait for more data. - Segmentation: Split the string using the first two pipes.
- OPCODE Validation: Check if the first 4 bytes match a known command. If not, return an error.
- Source Integrity: Check that the second segment is between 3 and 32 bytes and contains no forbidden characters.
- 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:
0–599. - 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).
- Prohibited Characters: Must not contain the pipe (
- 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:
- Framing Check: Verify the string ends with
\r\n. If not, discard or wait for more data. - Segmentation: Split the string using a single pipe. The result must contain exactly two segments.
- Status Code Validation: Verify that the first segment parses as a number and does not exceed
599. - Reason Phrase Integrity: Check that the second segment is at most 32 bytes and contains no forbidden characters.
- 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).
- Prohibited Characters: Must not contain the pipe (
- Usage: Either a hostname or nickname of a client (for
BRDCnotifications), or the reserved stringXSCP_SERVERwhen 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\nterminator. - Constraints:
- Allowed Characters: The pipe (
|) is permitted here, allowing for nested data or complex text. - Prohibited Sequences: Must not contain
\r\nto prevent "PDU Smuggling" (injecting a second notification into the same stream).
- Allowed Characters: The pipe (
Validation Logic (Client-Side)
When a client receives a Notification, it must validate the fields in this specific order:
- Framing Check: Verify the string ends with
\r\n. If not, discard or wait for more data. - Segmentation: Split the string using the first two pipes.
- Notification Type Validation: Check if the first 4 bytes match a known notification type. If not, return an error.
- Source Integrity: Check that the second segment is between 3 and 32 bytes and contains no forbidden characters.
- 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.
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 toCLOSED.
2. Events and Transitions
State transitions are triggered by network events, client inputs, or internal server conditions.
| Source State | Destination State | Trigger Event | Description |
|---|---|---|---|
CLOSED | LISTEN | bind_and_listen | The server initializes and binds to the specified port. |
LISTEN | NEGOTIATING | tcp_connection_accepted | The OS completes the 3-way handshake; a dedicated handler is spawned. |
LISTEN | CLOSED | close | The server shuts down the listener voluntarily. |
NEGOTIATING | ESTABLISHED | credentials_validated | The client provides a unique, valid nickname conforming to XSCP rules. |
NEGOTIATING | NEGOTIATING | invalid_credentials | The nickname is malformed or already in use. The retry counter increments. |
NEGOTIATING | ABORTED | max_retries_exceeded | The client fails to provide valid credentials within the allowed attempts. |
NEGOTIATING | ABORTED | connection_error | The connection is dropped or times out during the negotiation phase. |
ESTABLISHED | ABORTED | connection_error | A protocol violation (e.g., PDU > 512 bytes) or an unexpected network disconnection occurs. |
ABORTED | CLOSED | close | Internal 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:
- Clients are granted a limited number of attempts to submit a valid identity.
- Each
invalid_credentialsevent loops back toNEGOTIATINGand increments an internal counter. - Once the limit is reached, the
max_retries_exceededevent forces the connection intoABORTED, dropping the client immediately without allocating further memory.