Network Working Group G. Leopizzi Internet-Draft J. Grabis Intended status: Experimental K. Ābelnīca Expires: 6 February 2027 Riga Technical University S. Martorelli IBM 5 August 2026 FULMEN 1.0 draft-leopizzi-fulmen-00 Abstract This document specifies FULMEN version 1.0, an event-based, bi- directional client-server communication protocol with a binary wire format. Within a FULMEN connection, both the client and the server can send events. An event is a message frame identified by a sequential identifier and addressed by a UTF-8 path used for routing and dispatching; its sender can request an acknowledgment, a response correlated to the event that carries a status code describing the outcome of its processing. Events and acknowledgments can carry a binary payload, either inline within the frame or delivered incrementally in chunks through a stream. The protocol version in use is negotiated during the connection handshake. FULMEN also defines an extension mechanism, based on typed data units attached to protocol messages, through which additional functionality can be introduced without changes to the wire format. Status of This Memo This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet- Drafts is at https://datatracker.ietf.org/drafts/current/. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." This Internet-Draft will expire on 6 February 2027. Leopizzi, et al. Expires 6 February 2027 [Page 1] Internet-Draft FULMEN 1.0 August 2026 Copyright Notice Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/ license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License. Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 2. Conventions and Notation . . . . . . . . . . . . . . . . . . 3 3. Transport . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.1. TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.2. WebSocket . . . . . . . . . . . . . . . . . . . . . . . . 5 4. Connection Establishment and Major Version Negotiation . . . 6 5. Frame . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 6. Messages . . . . . . . . . . . . . . . . . . . . . . . . . . 7 6.1. Hello . . . . . . . . . . . . . . . . . . . . . . . . . . 8 6.2. Welcome . . . . . . . . . . . . . . . . . . . . . . . . . 9 6.3. Event . . . . . . . . . . . . . . . . . . . . . . . . . . 10 6.4. Acknowledgment . . . . . . . . . . . . . . . . . . . . . 11 6.5. Stream . . . . . . . . . . . . . . . . . . . . . . . . . 14 6.6. Stream Ack . . . . . . . . . . . . . . . . . . . . . . . 15 6.7. Heartbeat . . . . . . . . . . . . . . . . . . . . . . . . 16 6.8. Heartbeat Ack . . . . . . . . . . . . . . . . . . . . . . 17 6.9. Extensions . . . . . . . . . . . . . . . . . . . . . . . 17 6.10. Alert . . . . . . . . . . . . . . . . . . . . . . . . . . 18 7. Extensions . . . . . . . . . . . . . . . . . . . . . . . . . 19 7.1. Data Transfer Object . . . . . . . . . . . . . . . . . . 20 7.2. Authentication . . . . . . . . . . . . . . . . . . . . . 21 7.3. ContentType . . . . . . . . . . . . . . . . . . . . . . . 21 7.4. Metadata . . . . . . . . . . . . . . . . . . . . . . . . 22 7.5. Application Extensions List . . . . . . . . . . . . . . . 22 7.6. Allowed Event Paths . . . . . . . . . . . . . . . . . . . 23 7.7. Configuration . . . . . . . . . . . . . . . . . . . . . . 24 8. Security Considerations . . . . . . . . . . . . . . . . . . . 26 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 26 9.1. ALPN Protocol Identifier . . . . . . . . . . . . . . . . 26 9.2. WebSocket Subprotocol Name . . . . . . . . . . . . . . . 26 10. Normative References . . . . . . . . . . . . . . . . . . . . 26 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 27 Leopizzi, et al. Expires 6 February 2027 [Page 2] Internet-Draft FULMEN 1.0 August 2026 1. Introduction FULMEN is a client-server protocol. Similarly to HTTP, which is based on the concept of request-response, FULMEN is designed around the concept of event acknowledgment. In HTTP, the client strictly sends a request, and the server is responsible for the responses. In FULMEN, both the client and server can send events and acknowledgments. Before proceeding, it is necessary to provide the reader with definitions of such terms in the context of this protocol: * An event is a semantically significant, application-layer message frame exchanged by the communicating parties, representing a discrete unit of intent, data, or instruction. Each event has an identifier that is unique within the connection context. Each event includes a path, a UTF-8 encoded string for routing, dispatching, or topic identification. The path provides each event with a logical addressing mechanism, enabling the event to be dynamic and context-sensitive. An event MAY include a binary payload. * An acknowledgment is a structured response sent by the receiver of an event upon explicit request of the event's sender. It provides a mechanism to correlate an optional response to a given event whenever necessary for the application layer. Each acknowledgment includes a status code to inform about the result of event processing and MAY carry a payload. Both event and acknowledgment MAY include a payload. The protocol supports the payload as included within the frame, sent later, or in chunks with separate future frames. In the latter case, a "stream" is used, and the payload body delivery can be deferred to a future point. The protocol supports extensions to guarantee extensibility. An extension is a modular, typed data unit that can be added to some frames to add or adapt core protocol functionalities without altering the protocol wire format or breaking compatibility. Extensions are serialized using a TLV format attached to the payload of some protocol messages. 2. Conventions and Notation The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP14] (RFC2119) (RFC8174) when, and only when, they appear in all capitals, as shown here. Leopizzi, et al. Expires 6 February 2027 [Page 3] Internet-Draft FULMEN 1.0 August 2026 The protocol exclusively uses big-endian byte order. This document uses a pseudo-C struct in listings to clarify how fields are serialized and deserialized from the wire format. uint8, uint16, uint32, uint64 represent an unsigned integer serialized respectively using exactly 1, 2, 4, and 8 bytes. string is used to represent an encoded UTF-8 string. The string is serialized as a uint16 representing the length of the string in bytes, followed by the bytes representing the string. For example, the string "IETF" would be serialized as: [0x00, 0x04, 0x49, 0x45, 0x54, 0x46] data represents an encoded buffer of binary bytes. As per the string, it is serialized as a uint16 representing the length of the buffer in bytes, followed by the buffer itself. For example, the buffer [0x01, 0x02, 0x03] would be serialized as: [0x00, 0x03, 0x01, 0x02, 0x03] opaque name[n] represents exactly n bytes, serialized as-is without any length prefix. More generally, the array notation type name[count] represents count elements of the indicated type, serialized back-to-back in order and without any length prefix; when count is the name of another field of the structure, the number of elements is given by the value of that field. When declaring a pseudo-C struct, a field (or multiple fields) might be wrapped within an "if" block. If so, the field (or the fields) are only present whenever the bit flag is set in the if condition. The name of the bit flag might be preceded by a "!" indicating the logical NOT operation. For example, to represent that a field is there only if the bit flag named "hello" is not set, the struct would contain the following: if (!hello) { string example; } A C-like bit-field notation is used in the listings to represent bit flags. Despite its appearance, it is not an actual C bit-field: it is only a notation to describe the meaning of the individual bits of a single byte, and no C compiler layout, padding, or memory alignment rule applies. The bits are listed from the most significant to the least significant: the first element of the bit-field is the most significant bit of the byte. For example, in a structure declared as: Leopizzi, et al. Expires 6 February 2027 [Page 4] Internet-Draft FULMEN 1.0 August 2026 struct { bool a : 1; void reserved : 7; } FlagsExample the flag a is the most significant bit of the byte (0x80), and the remaining seven bits are reserved. The notation uses the void type to reserve space for future flags. Reserved bits MUST be set to zero when a frame is sent and MUST be ignored upon receipt. Unless otherwise specified, enumerated types (enum) and bit-flag structures are serialized as a single byte (uint8). 3. Transport FULMEN is an application-layer protocol and does not define its own mechanisms for reliability, ordering, or congestion control. It therefore requires an underlying transport that provides such guarantees. This document defines the use of FULMEN over two transports, TCP (Section 3.1) and WebSocket (Section 3.2). The lifetime of a FULMEN connection coincides with the lifetime of the underlying connection. 3.1. TCP When FULMEN is carried over TCP [RFC9293], the frames (Section 5) are sent back-to-back over the TCP connection, with no additional framing. A frame can be split across several TCP segments, and several frames can be carried by a single TCP segment; a receiver MUST NOT rely on any relationship between frame boundaries and transport-level boundaries. Implementations SHOULD disable the Nagle algorithm by setting the TCP_NODELAY socket option. When the connection is established over TLS, the ALPN extension [RFC7301] SHOULD be used with the following identifier: fulmen/1 3.2. WebSocket When FULMEN is carried over WebSocket [RFC6455], the client SHOULD include the subprotocol name fulmen/1 in the Sec-WebSocket-Protocol header field of its opening handshake, and a server that accepts the connection echoes it as prescribed by [RFC6455]. Leopizzi, et al. Expires 6 February 2027 [Page 5] Internet-Draft FULMEN 1.0 August 2026 FULMEN is carried exclusively by WebSocket messages of type binary: a party that receives a text message MUST terminate the connection with an Alert message (Section 6.10) with code MALFORMED_FRAME. The FULMEN byte stream is the concatenation, in the order received, of the payloads of the binary messages. A receiver MUST NOT assume that a message boundary coincides with a frame boundary. However, sending each frame as a single binary message is RECOMMENDED. 4. Connection Establishment and Major Version Negotiation A FULMEN connection, regardless of the transport, begins with a handshake. The handshake starts with a fixed sequence of 8 bytes sent by each party, and proceeds with the Hello and Welcome messages (Section 6), in which the minor version is negotiated. The 8 handshake bytes are: * the six octets 0x46 0x55 0x4C 0x4D 0x45 0x4E (the ASCII string "FULMEN"); * one byte, an 8-bit unsigned integer representing the FULMEN major version to be used within the connection. The value 0 MUST NOT be sent by the client; * one reserved byte. It MUST be set to zero when sent and MUST be ignored upon receipt. This 8-byte sequence is invariant: its format MUST remain identical across all FULMEN major versions. The client MUST send the 8 handshake bytes as the first bytes of the connection, carrying the single major version it intends to use within the connection. The client is free to decide whether to send the Hello message (Section 6.1) immediately after its handshake bytes, without waiting for the server: doing so is RECOMMENDED. The server MUST respond with its own 8 handshake bytes. If the server supports the major version indicated by the client, it MUST echo the same major version; it is RECOMMENDED for the server to send the Welcome message (Section 6.2) immediately after its handshake bytes. If the server does not support the major version indicated by the client, it MUST send the handshake bytes carrying the major version 0 and terminate the connection; in this case, the server MUST NOT attempt to interpret any byte received after the client's handshake bytes. A party that receives handshake bytes that do not begin with the six octets "FULMEN" MUST terminate the connection. Leopizzi, et al. Expires 6 February 2027 [Page 6] Internet-Draft FULMEN 1.0 August 2026 Likewise, a client that receives handshake bytes carrying a major version that is neither the one it sent nor 0 MUST terminate the connection. Figure 1 shows the handshake bytes exchanged in a FULMEN 1.0 connection. Client -> Server: 46 55 4C 4D 45 4E 01 00 ("FULMEN", major version 1) Server -> Client: 46 55 4C 4D 45 4E 01 00 ("FULMEN", major version 1) Figure 1: FULMEN 1.0 handshake bytes 5. Frame Figure 2 represents the generic FULMEN frame structure. The frame is the unit sent over the wire. Thus, all the bytes received from the transport (Section 3) after the handshake bytes MUST be decoded as frames and later interpreted as described based on the type field. All the fields of a message are serialized within the frame's payload field. The sizes of the variable-length fields within a message are constrained by the enclosing frame. A party that decodes a frame whose payload does not conform to the structure of the message indicated by the type field MUST terminate the connection with an Alert message (Section 6.10) with code MALFORMED_FRAME. struct { uint8 type; data payload; } FulmenFrame Figure 2: FULMEN frame structure 6. Messages A message is a unit sent using a frame. Each message is represented by a unique type identifier (8-bit unsigned integer). The type identifier 0x00 is reserved and MUST NOT be used. A party that receives a frame whose type is 0x00 or is not supported MUST terminate the connection with an Alert message (Section 6.10) with code UNSUPPORTED_FRAME_TYPE. Leopizzi, et al. Expires 6 February 2027 [Page 7] Internet-Draft FULMEN 1.0 August 2026 The connection follows a strict lifecycle. The first message sent by the client MUST be a Hello message (Section 6.1), and the first message sent by the server MUST be a Welcome message (Section 6.2). Once the Welcome message has been sent, the handshake is complete, and Hello and Welcome messages MUST NOT be sent again within the connection lifetime. With the exception of the Alert message (Section 6.10), which can be sent by either party at any point of the connection, no other message can be sent before the Welcome message has been received. A party that receives a message violating this lifecycle MUST terminate the connection with an Alert message with code UNEXPECTED_MESSAGE. 6.1. Hello Type: 0x01 Sender: client Extensions: supported It is the first message that MUST be sent over the open connection, after the handshake bytes (Section 4). In this message, the client lists all the supported minor versions of the protocol. It contains the following fields: * flags, currently the only flag supported is has_extensions, indicating if the message has some extensions included. * ver_length, an 8-bit unsigned integer representing the length in bytes of the supported_minor_versions array. * supported_minor_versions, an array of 8-bit unsigned integers, each one representing a minor version supported by the client. The array MUST NOT be empty: a receiver of a Hello message with a ver_length of 0 MUST terminate the connection with an Alert message (Section 6.10) with code MALFORMED_FRAME. * extensions, a data array containing the Data Transfer Objects (DTOs) of the extensions carried by this message. Each DTO is encoded as later described in this document. Figure 3 shows the message's payload. Leopizzi, et al. Expires 6 February 2027 [Page 8] Internet-Draft FULMEN 1.0 August 2026 struct { bool has_extensions : 1; void reserved : 7; } HelloFlags struct { HelloFlags flags; uint8 ver_length; opaque supported_minor_versions[ver_length]; if (has_extensions) { data extensions; } } HelloPayload Figure 3: Hello message payload 6.2. Welcome Type: 0x02 Sender: server Extensions: supported It is the message sent by the server in response to the Hello message. As shown by Figure 4, besides the extensions, the only field is the minor_version representing the minor version, among the ones supported by the client, that the server has picked to be used within this connection. The connection is opened after receiving a Welcome message, and the parties can begin sending other appropriate messages. If the server supports none of the minor versions listed in the Hello message, it MUST send an Alert message (Section 6.10) with code UNSUPPORTED_MINOR_VERSIONS and close the connection. Likewise, a client that receives a Welcome message whose minor_version was not listed in its Hello message MUST terminate the connection with an Alert message (Section 6.10) with code UNSUPPORTED_MINOR_VERSIONS. Leopizzi, et al. Expires 6 February 2027 [Page 9] Internet-Draft FULMEN 1.0 August 2026 struct { bool has_extensions : 1; void reserved : 7; } WelcomeFlags struct { WelcomeFlags flags; uint8 minor_version; if (has_extensions) { data extensions; } } WelcomePayload Figure 4: Welcome message payload 6.3. Event Type: 0x03 Sender: client, server Extensions: supported This message delivers an event (Figure 5). struct { bool has_extensions : 1; bool has_inline_payload : 1; bool requires_ack : 1; void reserved : 5; } EventFlags struct { uint64 event_id; EventFlags flags; string path; if (!has_inline_payload) { uint32 stream_id; } if (has_inline_payload) { data payload; } if (has_extensions) { data extensions; } } EventPayload Figure 5: Event message payload Leopizzi, et al. Expires 6 February 2027 [Page 10] Internet-Draft FULMEN 1.0 August 2026 The event payload supports the following fields: * event_id, a 64-bit unsigned integer representing the progressive identifier of the current event. Each client and server has its own sequence that MUST be unique to the FULMEN connection. It starts from zero, and it MUST be incremented by one for each Event message sent. A party that receives an Event message whose event_id is not the expected next value of the sender's sequence MUST terminate the connection with an Alert message (Section 6.10) with code INVALID_EVENT_ID. * flags, the following flags are supported: - has_extensions, as previously described. - has_inline_payload, if set, the payload of the event is written inline. If not set, a stream identifier will be provided to retrieve the payload later. - requires_ack, if set the sender of this event is expecting an Acknowledgment message in response to the event. * path, the path of the event. It MUST be a UTF-8 encoded string and MUST NOT be empty. A path, to be valid, MUST contain exclusively the following characters: A-Z a-z 0-9 - _ . ~ / A path MUST NOT begin or end with the "/" character and MUST NOT contain two consecutive "/" characters. A party that receives an Event message with an invalid path MUST terminate the connection with an Alert message (Section 6.10) with code INVALID_EVENT_PATH. * stream_id, only present if the flag has_inline_payload is not set. If so, the 32-bit unsigned integer identifies the stream that will later carry this event's payload. * payload, only present if the flag has_inline_payload is set. Represents the binary payload of the event. A payload of length 0 is allowed: an event that carries no payload is sent with the has_inline_payload flag set and a zero-length payload. * extensions, same as the Hello message. 6.4. Acknowledgment Type: 0x04 Leopizzi, et al. Expires 6 February 2027 [Page 11] Internet-Draft FULMEN 1.0 August 2026 Sender: client, server Extensions: supported As shown in Figure 6, the message delivers an event acknowledgment to the sender party. It contains the following fields: * event_id, the 64-bit unsigned integer representing the identifier of the event this message is acknowledging. It correlates an event with its Acknowledgment message. * flags, the following are currently supported: - has_extensions, same as the Hello message. - has_inline_payload, same as the Event message. - has_status_description, if set, the payload includes a string to clarify the meaning of the status further. * status, an 8-bit unsigned integer representing the status of the message. It works similarly to the HTTP Response Status Code. Its purpose is to provide the sender with information about how the processing of the Event is completed. The status code 0 is reserved and MUST NOT be used. * status_description, a string present if the flag has_status_description is set. It represents an optional message that can be sent along the status to clarify it further (for example, by providing an error description). * stream_id, same as for the Event message. * payload, same as for the Event message. * extensions, same as for the Hello message. A party that receives an Acknowledgment message whose event_id does not correspond to an event it sent with the requires_ack flag set, or that has already been acknowledged, MUST terminate the connection with an Alert message (Section 6.10) with code UNEXPECTED_EVENT_ACK. Leopizzi, et al. Expires 6 February 2027 [Page 12] Internet-Draft FULMEN 1.0 August 2026 enum Status { OK = 1, INVALID_ARGUMENT = 2, UNAUTHENTICATED = 3, PERMISSION_DENIED = 4, NOT_FOUND = 5, ALREADY_EXISTS = 6, FAILED_PRECONDITION = 7, OUT_OF_RANGE = 8, CONFLICT = 9, RESOURCE_EXHAUSTED = 10, CANCELLED = 11, DEADLINE_EXCEEDED = 12, RATE_LIMITED = 13, INTERNAL_ERROR = 14, UNAVAILABLE = 15, PROTOCOL_VIOLATION = 16 } struct { bool has_extensions : 1; bool has_inline_payload : 1; bool has_status_description : 1; void reserved : 5; } AckFlags struct { uint64 event_id; AckFlags flags; Status status; if (has_status_description) { string status_description; } if (!has_inline_payload) { uint32 stream_id; } if (has_inline_payload) { data payload; } if (has_extensions) { data extensions; } } AcknowledgmentPayload Figure 6: Acknowledgment message payload Leopizzi, et al. Expires 6 February 2027 [Page 13] Internet-Draft FULMEN 1.0 August 2026 6.5. Stream Type: 0x05 Sender: client, server Extensions: supported A message that delivers a chunk of the payload whenever the event or the acknowledgment message declares its intention to use it for payload streaming functionality. Figure 7 shows the message's payload. The stream status 0 is reserved and MUST NOT be used. In a Stream message, the stream_id always refers to a stream initiated by the message's sender. A stream consists of zero or more chunks with status HAS_MORE followed by a final chunk with status ENDED; the complete payload of the stream is the concatenation, in the order received, of the payload fields of all its chunks. The payload of an ENDED chunk can be empty. The initiator can terminate a stream abnormally at any moment by sending a chunk with status ABORTED, whose payload MUST be empty; upon receiving it, the receiver MUST discard all the previously received chunks of the stream. A stream ends when a chunk with status ENDED or ABORTED is received. Each party allocates stream identifiers from its own sequence, which SHOULD start from zero and SHOULD be incremented by one for each new stream the party initiates (by declaring it in an Event or Acknowledgment message). A stream identifier can be reused upon reception of a Stream Ack message with status RECEIVED from the counterpart (Section 6.6). A party that receives an Event, Acknowledgment, or Stream message violating these rules MUST terminate the connection with an Alert message (Section 6.10) with code INVALID_STREAM_ID. FULMEN version 1.0 does not provide per-stream flow control. A receiver that cannot keep up with an incoming stream can cancel it using the Stream Ack message (Section 6.6). Stream messages of different streams, as well as Stream messages and other messages, MAY be freely interleaved; the chunks of a single stream MUST be sent in order. Implementations MAY adopt a prioritization system to decide the order in which frames are sent. Leopizzi, et al. Expires 6 February 2027 [Page 14] Internet-Draft FULMEN 1.0 August 2026 enum StreamStatus { HAS_MORE = 1, ENDED = 2, ABORTED = 3 } struct { bool has_extensions : 1; void reserved : 7; } StreamFlags struct { uint32 stream_id; StreamFlags flags; StreamStatus status; data payload; if (has_extensions) { data extensions; } } StreamPayload Figure 7: Stream message payload 6.6. Stream Ack Type: 0x06 Sender: client, server Extensions: supported A message sent by the receiver of a stream to its initiator (Figure 8). Similarly to the Acknowledgment message, the stream_id of a Stream Ack message MUST always be the same as that of the received Stream message. FULMEN version 1.0 supports two status values: * ABORT, used to request the cancellation of an incoming stream. Upon receiving it, the initiator of the stream MUST NOT send further HAS_MORE chunks and MUST confirm the cancellation by sending a final Stream message with status ABORTED for the stream. The party that requested the cancellation MUST ignore any HAS_MORE chunk of the stream received while waiting; the stream ends when a final chunk is received. Leopizzi, et al. Expires 6 February 2027 [Page 15] Internet-Draft FULMEN 1.0 August 2026 * RECEIVED, used to acknowledge the termination of a stream. A party that receives a final Stream message (status ENDED or ABORTED) MUST respond with a Stream Ack message with status RECEIVED. After receiving a Stream Ack message with status RECEIVED, the initiator can consider that stream identifier as valid for reuse. Once a party considers a stream terminated, it MUST ignore every message referring to that stream identifier, with the exception of the reception of a Stream Ack message with status RECEIVED. Only upon the re-declaration of the stream identifier by a new Event or Acknowledgment message do messages referring to that identifier become valid again, as they refer to the new stream. A Stream Ack message that refers to a stream identifier that its receiver has never allocated MUST be treated as a violation, and the receiving party MUST terminate the connection with an Alert message (Section 6.10) with code INVALID_STREAM_ID. enum StreamAckStatus { ABORT = 1, RECEIVED = 2 } struct { bool has_extensions : 1; void reserved : 7; } StreamAckFlags struct { uint32 stream_id; StreamAckFlags flags; StreamAckStatus status; if (has_extensions) { data extensions; } } StreamAckPayload Figure 8: Stream Ack message payload 6.7. Heartbeat Type: 0x07 Sender: client, server Extensions: not supported Leopizzi, et al. Expires 6 February 2027 [Page 16] Internet-Draft FULMEN 1.0 August 2026 Either party sends it as a "keep-alive" mechanism. As shown in Figure 9, it includes a token; the receiving party MUST respond with a Heartbeat Ack message (Section 6.8) carrying the same token, allowing the sender to correlate the two messages. There is no restriction on token reuse: the token generation strategy is left to the sender. The message does not support extensions. struct { void reserved : 8; } HeartbeatFlags struct { HeartbeatFlags flags; uint8 token; } HeartbeatPayload Figure 9: Heartbeat message payload 6.8. Heartbeat Ack Type: 0x08 Sender: client, server Extensions: not supported Sent upon reception of a Heartbeat message. As shown in Figure 10, it MUST include the same token received in the Heartbeat message. The message does not support extensions. struct { void reserved : 8; } HeartbeatAckFlags struct { HeartbeatAckFlags flags; uint8 token; } HeartbeatAckPayload Figure 10: Heartbeat Ack message payload 6.9. Extensions Type: 0x09 Sender: client, server Extensions: supported Leopizzi, et al. Expires 6 February 2027 [Page 17] Internet-Draft FULMEN 1.0 August 2026 A message that only consists of extensions. Its purpose is to deliver the information needed to allow an additional mechanism to work correctly. As shown by Figure 11, it works as the Hello message, but it does not include a flag since the data are always present due to the nature of the message itself. The extensions field MUST contain at least one DTO (Section 7.1): a receiver of an Extensions message carrying no extensions MUST terminate the connection with an Alert message (Section 6.10) with code MALFORMED_FRAME. struct { data extensions; } ExtensionPayload Figure 11: Extensions message payload 6.10. Alert Type: 0x0B Sender: client, server Extensions: not supported Shown in Figure 12, a message used to deliver an alert code to the other party. The Alert message always terminates the connection; after sending or receiving an Alert message, a party MUST consider the connection closed, and no further message can be sent. Consequently, every orderly connection termination ends with an Alert message; a graceful termination is signaled with the code CONNECTION_CLOSE. The alert code 0 is reserved and MUST NOT be used. Leopizzi, et al. Expires 6 February 2027 [Page 18] Internet-Draft FULMEN 1.0 August 2026 enum AlertCode { CONNECTION_CLOSE = 1, UNSUPPORTED_MINOR_VERSIONS = 2, UNKNOWN_EXTENSION = 3, UNSUPPORTED_FRAME_TYPE = 4, MALFORMED_FRAME = 5, INVALID_EVENT_ID = 6, INVALID_STREAM_ID = 7, INVALID_EVENT_PATH = 8, HEARTBEAT_FAILURE = 9, UNEXPECTED_EVENT_ACK = 10, MISSING_EVENT_ACK = 11, LIMITS_EXCEEDED = 12, UNEXPECTED_MESSAGE = 13, EXTENSION_FAILURE = 14, UNKNOWN_ERROR = 255 } struct { bool has_description : 1; void reserved : 7; } AlertFlags struct { AlertFlags flags; AlertCode code; if (has_description) { string description; } } AlertPayload Figure 12: Alert message payload 7. Extensions The protocol supports extensions to transport additional typed payload. Extensions can be of two categories: * Protocol Extensions (PE). Those are extensions that are defined by the protocol specification. Every Protocol Extension defined by a version of the protocol is mandatory to implement: each client/server that advertises support for a FULMEN version MUST support all the Protocol Extensions defined by that version's specification. For FULMEN version 1.0, these are the extensions defined in this document. Leopizzi, et al. Expires 6 February 2027 [Page 19] Internet-Draft FULMEN 1.0 August 2026 * Application Extensions (AE). Those are extensions defined by the application that uses the FULMEN protocol. The application developers are responsible for ensuring the correct versioning of the extension itself. A 16-bit unsigned integer identifies each extension type. The most significant bit of the extension type determines its category: when it is not set (types 0x0001 to 0x7FFF), the extension is a PE; when it is set (types 0x8000 to 0xFFFF), the extension is an AE. The extension type 0x0000 is reserved and MUST NOT be used. The use of Application Extensions MUST be declared in advance. A connection in which AEs are to be used MUST include the Application Extensions List extension (Section 7.5) in its Hello message. If the Hello message does not carry the Application Extensions List extension, no AE can be used within the connection. A party that receives an AE type that was not declared MUST terminate the connection with an Alert message (Section 6.10) with code UNKNOWN_EXTENSION. A party that receives an extension type (either PE or AE) that it does not support MUST ignore it. When the processing of a supported extension fails in a way that prevents the connection from continuing, the party MAY terminate the connection with an Alert message (Section 6.10) with code EXTENSION_FAILURE and SHOULD include an appropriate description. 7.1. Data Transfer Object Each extension is serialized using the structure shown in Figure 13. When a message carries more than one extension, the DTOs are encoded back-to-back within the extensions field; a receiver MUST parse ExtensionFrame structures until the length of the enclosing data field is exhausted. A message MUST NOT carry more than one extension of the same type; a receiver of a message violating this rule MAY terminate the connection with an Alert message (Section 6.10) with code EXTENSION_FAILURE. When the has_extensions flag of a message supporting it is set, the extensions field MUST contain at least one DTO: a receiver of a message whose has_extensions flag is set and whose extensions field contains no DTOs MUST terminate the connection with an Alert message (Section 6.10) with code MALFORMED_FRAME. Leopizzi, et al. Expires 6 February 2027 [Page 20] Internet-Draft FULMEN 1.0 August 2026 The definition of each extension lists the messages that can carry it. An extension MUST NOT be attached to a message that its definition does not list; a party that receives an extension carried by a message that is not allowed to carry it MUST terminate the connection with an Alert message (Section 6.10) with code EXTENSION_FAILURE. struct { uint16 type; data payload; } ExtensionFrame Figure 13: Extension Frame 7.2. Authentication Extension type: 0x01 Messages: Hello, Event The extension payload begins with a one-byte (unsigned 8-bit integer) value indicating the type of authentication method used. The interpretation of the subsequent payload depends on this value. Version 1.0 defines the following types: * Type 0, basic authentication. The payload consists of two back- to-back encoded strings. The first represents a username and the second its password. * Type 1, token-based authentication. The payload consists of a single string representing the authentication token. When the extension is carried by a Hello message and the authentication fails, the server MUST terminate the connection with an Alert message (Section 6.10) with code EXTENSION_FAILURE. When the extension is carried by an Event message and the authentication fails, the receiver SHOULD reject the event with an Acknowledgment message with status UNAUTHENTICATED (when an acknowledgment is expected) and MAY terminate the connection with an Alert message with code EXTENSION_FAILURE. 7.3. ContentType Extension type: 0x02 Messages: Event, Acknowledgment Leopizzi, et al. Expires 6 February 2027 [Page 21] Internet-Draft FULMEN 1.0 August 2026 The payload of the extension consists of bytes representing the UTF-8 encoded IANA Media Type [RFC6838] of the payload. 7.4. Metadata Extension type: 0x03 Messages: Event, Acknowledgment The extension allows the sender to attach application metadata to an Event or Acknowledgment message, conceptually similar to HTTP header fields, while leveraging the binary wire format of FULMEN. The extension payload consists of a single CBOR [RFC8949] map. Each key of the map MUST be either a CBOR text string or a CBOR unsigned integer, and MUST be unique within the map; the value associated with a key can be of any CBOR type. In FULMEN version 1.0, unsigned integer keys carry no meaning; a receiver MUST ignore every entry whose key is an unsigned integer, and a sender MUST NOT include such entries. 7.5. Application Extensions List Extension type: 0x04 Messages: Hello The extension allows the client to declare, during the handshake, the Application Extensions available to the application (Figure 14). The declared AE types are the only Application Extensions that can be used within the connection, by either party, as described in Section 7. It contains the following fields: * application_uuid, a 16-byte UUID [RFC9562] identifying the application. Since AE types are for private use, the UUID qualifies the namespace in which the listed Application Extensions are defined. * application_extensions, the list of AE types available to the client, preceded by ae_count, the number of its elements. If the server does not recognize the application_uuid, it MUST send an Alert message (Section 6.10) with code UNKNOWN_EXTENSION and close the connection. Leopizzi, et al. Expires 6 February 2027 [Page 22] Internet-Draft FULMEN 1.0 August 2026 struct { opaque application_uuid[16]; uint8 ae_count; uint16 application_extensions[ae_count]; } ApplicationExtensionsListPayload Figure 14: Application Extensions List extension payload 7.6. Allowed Event Paths Extension type: 0x05 Messages: Hello, Welcome, Extensions The extension allows each party to declare the event paths it is willing to receive. As shown in Figure 15, the payload consists of a list of strings, each one representing a path pattern to be matched against event paths. A path pattern is a sequence of segments separated by the "/" character, like the paths it is matched against. Each segment of a pattern is one of the following: * a literal segment, composed of the path characters listed in Section 6.3: it matches only an identical path segment; * the segment "*": it matches exactly one path segment, whatever its content; * the segment "**", which can only appear as the last segment of a pattern: it matches any number of path segments, including zero. A path matches a pattern when its segments can be aligned with the pattern's segments according to the rules above; the alignment MUST always cover the entire path, never only a substring. For example, the pattern "sensors/*/temperature" matches the path "sensors/kitchen/temperature" but neither "sensors/temperature" nor "sensors/kitchen/hvac/temperature"; the pattern "logs/**" matches "logs", "logs/app", and "logs/app/error"; the pattern "config/reload" matches only the path "config/reload". Leopizzi, et al. Expires 6 February 2027 [Page 23] Internet-Draft FULMEN 1.0 August 2026 A pattern is invalid if it is empty, if any of its segments is empty (like a path, a pattern cannot begin or end with "/" or contain two consecutive "/" characters), if any of its segments contains the "*" character without being exactly "*" or "**" (for example, "foo*" and "***" are invalid segments), if a "**" segment appears in a position other than the last, or if it contains any character that is neither a valid path character nor "*". A party that receives a list containing an invalid pattern MUST terminate the connection with an Alert message (Section 6.10) with code EXTENSION_FAILURE. A party MUST NOT send an Event message whose path does not match at least one of the patterns most recently declared by the other party. A party that receives such an Event MAY terminate the connection with an Alert message (Section 6.10) with code INVALID_EVENT_PATH. A newly received list completely replaces the previously received one (for example, a list received within an Extensions message replaces the list received during the handshake). An empty list (path_count equal to 0) means that the other party cannot send any Event message at all. If a party has never sent this extension, no path restriction applies to the events it receives. struct { uint8 path_count; string allowed_paths[path_count]; } AllowedEventPathsPayload Figure 15: Allowed Event Paths extension payload 7.7. Configuration Extension type: 0x06 Messages: Welcome, Extensions The extension allows the server to communicate its connection policy (Figure 16). It contains the following fields: * stale_timeout, the number of seconds after which the server considers the connection stale: if the server does not receive any frame for stale_timeout seconds, it MAY close the connection. The value 0 means that the server does not apply any stale timeout. Leopizzi, et al. Expires 6 February 2027 [Page 24] Internet-Draft FULMEN 1.0 August 2026 * disable_heartbeats, controls whether the client can send Heartbeat messages. The value 0 indicates that the client can send them; any other value indicates that the client MUST NOT send them. If the client sends a Heartbeat message while not allowed to, the server MAY send an Alert message (Section 6.10) with code HEARTBEAT_FAILURE and close the connection. * ack_timeout, the maximum number of seconds a party SHOULD wait for the Acknowledgment of an Event message sent with the requires_ack flag set. Once the timeout has elapsed, the waiting party MAY terminate the connection with an Alert message (Section 6.10) with code MISSING_EVENT_ACK. The value 0 means that no deadline applies. * heartbeat_timeout, the maximum number of seconds a party SHOULD wait for the Heartbeat Ack message after sending a Heartbeat message. Once the timeout has elapsed, the waiting party MAY terminate the connection with an Alert message (Section 6.10) with code HEARTBEAT_FAILURE. The value 0 means that no deadline applies. * max_payload_size, the maximum size in bytes of the payload field of Event, Acknowledgment, and Stream messages sent by the client. The value 0 means that the server does not apply that limit. * max_streams, the maximum number of streams the client can keep simultaneously open. The value 0 means that the server does not apply that limit. If the client exceeds a declared limit, the server MAY send an Alert message (Section 6.10) with code LIMITS_EXCEEDED and close the connection. The extension can also be carried by an Extensions message to update the policy previously communicated. If the server has never sent such an extension, no limits or timeouts apply. struct { uint16 stale_timeout; uint8 disable_heartbeats; uint16 ack_timeout; uint16 heartbeat_timeout; uint16 max_payload_size; uint16 max_streams; } ConfigurationPayload Figure 16: Configuration extension payload Leopizzi, et al. Expires 6 February 2027 [Page 25] Internet-Draft FULMEN 1.0 August 2026 8. Security Considerations FULMEN does not mandate the use of a specific security protocol. However, a FULMEN connection SHOULD be established over a protocol that ensures the confidentiality and integrity of the communication. For the transports defined in this document, this means running the TCP transport (Section 3.1) over TLS [RFC8446] and using the "wss" URI scheme for the WebSocket transport (Section 3.2). 9. IANA Considerations 9.1. ALPN Protocol Identifier This document requests that IANA register the following entry in the "TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs" registry created by [RFC7301]: Protocol: FULMEN/1 Identification Sequence: 0x66 0x75 0x6c 0x6d 0x65 0x6e 0x2f 0x31 ("fulmen/1") Reference: This document 9.2. WebSocket Subprotocol Name This document requests that IANA register the following entry in the "WebSocket Subprotocol Name Registry" created by [RFC6455]: Subprotocol Identifier: FULMEN/1 Subprotocol Common Name: FULMEN version 1 Subprotocol Definition: This document, Section 3.2 The identifier registered in the "Sec-WebSocket-Protocol" header field is "fulmen/1". This document makes no other requests of IANA. 10. Normative References [BCP14] Best Current Practice 14, . At the time of writing, this BCP comprises the following: Leopizzi, et al. Expires 6 February 2027 [Page 26] Internet-Draft FULMEN 1.0 August 2026 Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, . Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, . [RFC6455] Fette, I. and A. Melnikov, "The WebSocket Protocol", RFC 6455, DOI 10.17487/RFC6455, December 2011, . [RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type Specifications and Registration Procedures", BCP 13, RFC 6838, DOI 10.17487/RFC6838, January 2013, . [RFC7301] Friedl, S., Popov, A., Langley, A., and E. Stephan, "Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension", RFC 7301, DOI 10.17487/RFC7301, July 2014, . [RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018, . [RFC8949] Bormann, C. and P. Hoffman, "Concise Binary Object Representation (CBOR)", STD 94, RFC 8949, DOI 10.17487/RFC8949, December 2020, . [RFC9293] Eddy, W., Ed., "Transmission Control Protocol (TCP)", STD 7, RFC 9293, DOI 10.17487/RFC9293, August 2022, . [RFC9562] Davis, K., Peabody, B., and P. Leach, "Universally Unique IDentifiers (UUIDs)", RFC 9562, DOI 10.17487/RFC9562, May 2024, . Authors' Addresses Giacomo Leopizzi Information Technology Institute, Riga Technical University Riga, LV-1048 Latvia Email: giacomo.leopizzi@rtu.lv Leopizzi, et al. Expires 6 February 2027 [Page 27] Internet-Draft FULMEN 1.0 August 2026 Jānis Grabis Information Technology Institute, Riga Technical University Riga, LV-1048 Latvia Email: grabis@rtu.lv Kaspars Ābelnīca Information Technology Institute, Riga Technical University Riga, LV-1048 Latvia Email: kaspars.abelnica@rtu.lv Simone Martorelli IBM 821 09 Bratislava Slovakia Email: simone.martorelli@ibm.com Leopizzi, et al. Expires 6 February 2027 [Page 28]