| Internet-Draft | FULMEN 1.0 | August 2026 |
| Leopizzi, et al. | Expires 6 February 2027 | [Page] |
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.¶
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.¶
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.¶
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.¶
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.¶
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:¶
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).¶
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.¶
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¶
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].¶
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.¶
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.¶
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 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
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.¶
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.¶
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.¶
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
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.¶
struct {
bool has_extensions : 1;
void reserved : 7;
} WelcomeFlags
struct {
WelcomeFlags flags;
uint8 minor_version;
if (has_extensions) {
data extensions;
}
} WelcomePayload
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
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.¶
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:¶
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.¶
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
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.¶
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
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.¶
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
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
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
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
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.¶
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
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.¶
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.¶
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.¶
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
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.¶
The payload of the extension consists of bytes representing the UTF-8 encoded IANA Media Type [RFC6838] of the payload.¶
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.¶
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.¶
struct {
opaque application_uuid[16];
uint8 ae_count;
uint16 application_extensions[ae_count];
} ApplicationExtensionsListPayload
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".¶
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
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.¶
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
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).¶
This document requests that IANA register the following entry in the "TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs" registry created by [RFC7301]:¶
This document requests that IANA register the following entry in the "WebSocket Subprotocol Name Registry" created by [RFC6455]:¶
FULMEN/1¶
FULMEN version 1¶
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.¶