Skip to main content

FULMEN 1.0: Event-Driven Bidirectional Client-Server Communication Protocol
draft-leopizzi-fulmen-01

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]