"RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://www.ietf.org/rfc/rfc2119.txt).
## API design
### IDL
This specification uses the same custom Interface Definition Language (IDL) in YAML as defined in [WAKU-API](/standards/application/waku-api.md).
### Primitive types and general guidelines
The primitive types and general guidelines are the same as defined in [WAKU-API](/standards/application/waku-api.md#primitive-types-and-general-guidelines).
## Architecture
The Reliable Channel is a layered architecture that combines multiple components:
```
┌─────────────────────────────────────────┐
│ Reliable Channel API │ ← Application-facing event-driven API
description: "Reliable Channel: an event-driven API for eventual message consistency in Waku channels."
```
### Create Reliable Channel
#### Type definitions
```yaml
types:
ReliableChannel:
type: object
description: "A Reliable Channel instance that provides eventual consistency guarantees."
ReliableChannelOptions:
type: object
fields:
sync_min_interval_ms:
type: uint
default: 30000
description: "The minimum interval between 2 sync messages in the channel (in milliseconds). This is shared responsibility between channel participants. Set to 0 to disable automatic sync messages."
retry_interval_ms:
type: uint
default: 30000
description: "How long to wait before re-sending a message that has not been acknowledged (in milliseconds)."
max_retry_attempts:
type: uint
default: 10
description: "How many times to attempt resending messages that were not acknowledged."
retrieve_frequency_ms:
type: uint
default: 10000
description: "How often store queries are done to retrieve missing messages (in milliseconds)."
sweep_in_buf_interval_ms:
type: uint
default: 5000
description: "How often the SDS message channel incoming buffer is swept (in milliseconds)."
description: "Whether to automatically start the message channel."
process_task_min_elapse_ms:
type: uint
default: 1000
description: "The minimum elapsed time between calling the underlying channel process task for incoming messages. This prevents overload when processing many messages."
causal_history_size:
type: uint
description: "The number of recent messages to include in causal history. Passed to the underlying SDS MessageChannel."
description: "The time in milliseconds after which a message dependency that could not be resolved is marked as irretrievable. Disabled if undefined or 0. Passed to the underlying SDS MessageChannel."
possible_acks_threshold:
type: uint
description: "How many possible acknowledgments (bloom filter hits) does it take to consider it a definitive acknowledgment. Passed to the underlying SDS MessageChannel."
description: "An identifier for the channel. All participants of the channel MUST use the same id."
SenderId:
type: string
description: "An identifier for the sender. SHOULD be unique per participant and persisted between sessions to ensure acknowledgements are only valid when originating from different senders."
description: "Create a new Reliable Channel instance. All participants in the channel MUST be able to decrypt messages and MUST subscribe to the same content topic(s)."
parameters:
- name: waku_node
type: WakuNode
description: "The Waku node instance to use for sending and receiving messages."
- name: channel_id
type: ChannelId
description: "An identifier for the channel. All participants MUST use the same id."
- name: sender_id
type: SenderId
description: "An identifier for this sender. SHOULD be unique and persisted between sessions."
**Default configuration values**: See the [SDS Implementation Suggestions](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/sds.md#sdk-usage-reliablechannel) section for recommended default values for `ReliableChannelOptions`.
description: "Send an ephemeral message in the channel. Ephemeral messages are not tracked for acknowledgment, not included in causal history, and are dropped when rate limits are approached or reached."
description: "Emitted when a message chunk is being sent over the wire. MAY be emitted multiple times if retry mechanism kicks in. For segmented messages, emitted once per chunk."
description: "Emitted when a message chunk has been sent over the wire but has not been acknowledged yet. MAY be emitted multiple times if retry mechanism kicks in. For segmented messages, emitted once per chunk."
description: "Emitted when a bloom filter indicates a message chunk was possibly received by another party. This is probabilistic. For segmented messages, emitted per chunk."
description: "Emitted when a message chunk was fully acknowledged by other members of the channel (present in their causal history). For segmented messages, emitted per chunk as each is acknowledged."
description: "Emitted when a new complete message has been received and reassembled from another participant. The payload is the unwrapped user content (SDS content field). Only emitted once all chunks are received."
description: "Emitted when an ephemeral message was dropped due to rate limit constraints."
SendingMessageEvent:
type: object
fields:
message_id:
type: MessageId
description: "The logical message ID."
chunk_info:
type: ChunkInfo
description: "Information about which chunk is being sent. For non-segmented messages, chunk_index=0 and total_chunks=1."
MessageSentEvent:
type: object
fields:
message_id:
type: MessageId
description: "The logical message ID."
chunk_info:
type: ChunkInfo
description: "Information about which chunk was sent. For non-segmented messages, chunk_index=0 and total_chunks=1."
MessageAcknowledgedEvent:
type: object
fields:
message_id:
type: MessageId
description: "The logical message ID."
chunks_acknowledged:
type: array<uint>
description: "Array of chunk indices that have been acknowledged so far (e.g., [0, 2, 4] means chunks 0, 2, and 4 are acknowledged). Use `.length` to get total count. For non-segmented messages, this is [0]."
total_chunks:
type: uint
description: "Total number of chunks for this message. For non-segmented messages, this is 1."
description: "Start the Reliable Channel. Sets up event listeners, begins sync loop, starts missing message retrieval, and subscribes to messages via the Waku node."
description: "Stop the Reliable Channel. Stops sync loop, missing message retrieval, and clears intervals."
returns:
type: void
isStarted:
description: "Check if the Reliable Channel is currently started."
returns:
type: bool
description: "True if the channel is started, false otherwise."
```
## Implementation Suggestions
This section provides practical implementation guidance based on the [js-waku](https://github.com/waku-org/js-waku) implementation.
### SDS MessageChannel integration
The Reliable Channel MUST use the [SDS](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/sds.md) `MessageChannel` as its core reliability mechanism.
Ephemeral messages are defined in the [SDS specification](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/sds.md#ephemeral-messages) as short-lived messages for which no synchronization or reliability is required.
**Implementation notes**:
1.**SDS integration**:
- Send ephemeral messages with `lamport_timestamp`, `causal_history`, and `bloom_filter` unset
- Do NOT add to unacknowledged outgoing buffer after broadcast
- Do NOT include in causal history or bloom filters
- Do NOT add to local log
2.**Rate limit awareness**:
- Before sending an ephemeral message, check rate limit utilization
- If utilization >= threshold (default: 90%), drop the message and emit `ephemeral-message-dropped`
- This ensures reliable messages are never blocked by ephemeral traffic
3.**Use cases**:
- Typing indicators
- Presence updates
- Real-time status updates
- Other transient UI state that doesn't require guaranteed delivery
4.**Receiving ephemeral messages**:
- Deliver immediately without buffering for causal dependencies
- Emit `message-received` event (same as regular messages)
- Do NOT add to local log or acknowledge
**Reference**: SDS specification section on [Ephemeral Messages](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/sds.md#ephemeral-messages)
### Message segmentation
To handle large messages while accounting for protocol overhead, implementations SHOULD:
1.**Segment size calculation**:
- Target segment size: ~100 KB (102,400 bytes) of user payload
- This accounts for overhead that will be added:
- Encryption: Variable depending on encryption scheme (e.g., ~48 bytes for ECIES)
- SDS metadata: ~12.8 KB with default causal history (200 × 64 bytes)
- WAKU2-RLN-RELAY proof: ~128 bytes
- Protobuf encoding overhead: ~few hundred bytes
- Final Waku message stays well under 150 KB routing layer limit
2.**Message ID and chunk tracking**:
- Compute a single logical `message_id` from the **complete** user payload (before segmentation)
- All chunks of the same message share this `message_id`
- Each chunk has its own SDS message ID for tracking in causal history
- Chunk info (`chunk_index`, `total_chunks`) is included in all events
3.**Segmentation strategy**:
- Split large user payloads into ~100 KB chunks
- Wrap each chunk in a separate SDS `ContentMessage`
- Include segmentation metadata in each SDS message (chunk index, total chunks, logical message ID)
- Each chunk is sent and tracked independently through SDS
4.**Event emission**:
-`sending-message`: Emitted once per chunk with `chunk_info` indicating which chunk
-`message-sent`: Emitted once per chunk with `chunk_info` indicating which chunk
-`message-acknowledged`: Emitted each time a new chunk is acknowledged
-`chunks_acknowledged` contains ALL acknowledged chunks so far (cumulative)