FULMEN 1.0: Event-Driven Bidirectional Client-Server Communication Protocol
draft-leopizzi-fulmen-01
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Authors | Giacomo Leopizzi , Jānis Grabis (Janis Grabis) , Kaspars Ābelnīca (Kaspars Abelnica) , Simone Martorelli | ||
| Last updated | 2026-08-08 | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-leopizzi-fulmen-01
Network Working Group G. Leopizzi
Internet-Draft J. Grabis
Intended status: Experimental K. Ābelnīca
Expires: 9 February 2027 Riga Technical University
S. Martorelli
IBM
8 August 2026
FULMEN 1.0: Event-Driven Bidirectional Client-Server Communication
Protocol
draft-leopizzi-fulmen-01
Abstract
This document specifies FULMEN version 1.0, an event-driven,
bidirectional 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 9 February 2027.
Leopizzi, et al. Expires 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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 9 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,
<https://www.rfc-editor.org/info/bcp14>.
At the time of writing, this BCP comprises the following:
Leopizzi, et al. Expires 9 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,
<https://www.rfc-editor.org/info/rfc2119>.
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC6455] Fette, I. and A. Melnikov, "The WebSocket Protocol",
RFC 6455, DOI 10.17487/RFC6455, December 2011,
<https://www.rfc-editor.org/rfc/rfc6455>.
[RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type
Specifications and Registration Procedures", BCP 13,
RFC 6838, DOI 10.17487/RFC6838, January 2013,
<https://www.rfc-editor.org/rfc/rfc6838>.
[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, <https://www.rfc-editor.org/rfc/rfc7301>.
[RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol
Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
<https://www.rfc-editor.org/rfc/rfc8446>.
[RFC8949] Bormann, C. and P. Hoffman, "Concise Binary Object
Representation (CBOR)", STD 94, RFC 8949,
DOI 10.17487/RFC8949, December 2020,
<https://www.rfc-editor.org/rfc/rfc8949>.
[RFC9293] Eddy, W., Ed., "Transmission Control Protocol (TCP)",
STD 7, RFC 9293, DOI 10.17487/RFC9293, August 2022,
<https://www.rfc-editor.org/rfc/rfc9293>.
[RFC9562] Davis, K., Peabody, B., and P. Leach, "Universally Unique
IDentifiers (UUIDs)", RFC 9562, DOI 10.17487/RFC9562, May
2024, <https://www.rfc-editor.org/rfc/rfc9562>.
Authors' Addresses
Giacomo Leopizzi
Information Technology Institute, Riga Technical University
Riga, LV-1048
Latvia
Email: giacomo.leopizzi@rtu.lv
Leopizzi, et al. Expires 9 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 9 February 2027 [Page 28]