RLN + ChatSDK: Separated Concerns

There has been lots of discussion around how RLN will integrate with the ChatSDK. Here is a Pitch on how to Approach this…

TLDR;

The ChatSDK is agnostic of the underlying transport used, and uses an interface to maintain separation of concerns as the project grows.

WakuSDK is responsible for helping developers coordinate RLN memberships in a low friction way.

ChatSDK is responsible to make initialization of the ChatSDK easy, simple, and without headaches

ChatSDK & RLN interaction

Besides helping developers configure it, What does the ChatSDK need to know about RLN?

The Protocol has exposure to RLN and needs to consider its impact. It needs to ensure that it’s compatible with RLN by; Optimizing for less messages, considering protocol operation when a client is rate limited. In the end the protocol is responsible for making sure that it can function efficiently with an RLN rate limited network.

From the SDK side it doesn’t particularly care about RLN at runtime. It cares about wether a message was successfully “sent”, and then handle failure cases. The reason sending failed (e.g. NetworkDisconnect or Ratelimiting) is irrelevant.

Developers will need to know about transport errors, so they can inform users and set expectations. The ChatSDK will need to be able to pass transport errors to developers regardless; so there is no real requirement here.

While both Waku and Applications need to be RLN aware, the ChatSDK does not provided the protocol is designed to work over a network protected by it.

If the ChatSDK doesn’t need to know about RLN, I would suggest it shouldn’t - Separation of concerns helps keep both projects lean.

Abstraction

The ChatSDK Interface described previously shows the ChatSDK constructor accepting a DeliveryService object. This abstraction was intentional. One of the problems that surfaced in Status-Go was that the internal transport became tightly coupled with the application as separation boundaries were not in place. The Intention is to install an interface from Day 1 to stop this - An example of the abstraction is shown here.

The Advantages of Abstracting a Delivery Service has the following benefits:

Self Documenting

It becomes very clear what the ChatSDK needs from Waku and its relationship is documented perpetually via the DeliveryService interface.

This makes understanding and estimating the impact of waku changes easier to reason about. It also forces projects to focus on what they care about.

Resilience

The ChatSDK is insulated from changes to the WakuSDK and Future upgrades pains are minimized. Waku is free to Upgrade to a future V3, add/remove/change RLN, or add new new features without impacting the ChatSDK. Changes to the core API would constitute a change to the wrapper code, but those changes are isolated to a single location.

Clear Separation of Concerns

The DeliveryService abstraction enforces a clear division of responsibilities between Waku and the Chat.

WakuSDK: owns concepts like Networks, Peers, rate limiting and passing of messages.
It concerns it self with distributing individual messages across a network.

ChatSDK: owns concepts like ContentTopic usage, Authenticity, and Identity.
It concerns it self with combining streams of messages into Conversations and defining the associated Privacy, Security and DevEx that go along with them

Apps: owns Message Contents, and UX.
They concern themselves with how to best solve problems for their users

This also has the benefit of providing guidance of for where new solutions should live.

  • Is feature X related to a single WakuMessages → WakuSDK
  • Is feature X related to a stream of WakuMessages aka Conversations → ChatSDK
  • Everything else → App

Developer Ex

In this model using a ChatClient consists of 3 steps from the developer experience:

  1. Developers get a Configured Waku Instance. Either by carefully constructing one that fits ones own needs, or using a helper function which creates a sensible default.
  2. Developers pass a reference to that object to the SDK constructor. This uses the DeliveryService abstraction
  3. Developers then send messages via ChatSDK which then uses the supplied DeliveryService

Taking a “Developer choice + Usable defaults” approach the ChatSDK provides lots of power, while maintaining ease and simplicity.

  • Advanced developers can supply whichever waku configuration they want. They can choose a particular version of the WakuSDK if needed( for Interop or Dependency management), or leverage newly released features in their projects.
  • %99 of developers can use one of the helper configurations
    • These are prepackaged configurations for common uses cases
    • These are chosen based on demand but could include known good setups for Sending Messages to Status, Mobile, TWN.

Proposed Changes

What is needed to actually implement this?

  • WakuSDK takes complete ownership for streamlining RLN management
    • It’s need by every developer using Waku, and so it should be available at the source.
    • Developers expose what is needed to Users.
  • ChatSDK notifies developers why their messages failed, which allows developers to handle it as necessary.
  • ChatSDK is clear about what it expects from Waku and manages wrappers for the DeliveryService.
  • [WakuSDK | ChatSDK] creates an “Retry Policies” abstraction (name to be improved).
    • As a convenience messages should be tagged with what to do if they fail.
    • e.g. Presence Updates should be dropped, Critical path messages should be Queued
    • The abstraction manages a queue of critical messages which will be re-attempted when possible. This adds an async relationship between message sending but, is easier than managing state at a protocol level.
    • Why only two policies?
      • It’s easy for now, and covers the usecases without complexity.
        • Either this message is important, or its not.
      • It can easily be extended once we have a better understanding of needs.
    • Who owns this?
      • Does it have utility for all WakuDevelopers then put it in WakuSDk
      • Else ChatSDK will implement it for their own needs.

Appendix A: Mock Delivery Service Interface

Here is an example of the Delivery service abstraction. This is by no means complete, but provides an example of how it could work.

enum RetryPolicy {
	DropOnFail,           // Used for low priority messages
	QueueOnFail         // Used for  
}

trait DeliveryService {
    // Register to listen for messages, no concept of. 
    fn subscribe(&self, content_topic: &str) -> Result<(), WakuError>
	
    // Inbound messages are based to ChatSDK by callback.
    fn on_message(&self, handler: NewMessageHandler) -> Result<(), WakuError>
    
    // Send encoded message to a content topic. 
    fn sendMessages( &self, 
                     bytes: &[u8], 
                     content_topic: &str, 
                     policy: RetryPolicy 
    ) -> Result<(), WakuError>;
		
    // Needed if Segmentation is handled by ChatSDK; Don't want to send a 
    // "message" if not all segments can be sent.
    fn has_send_capacity(&self, num_messages: u16) -> Result<bool, WakuError>
		    
}
 
2 Likes

Thank you @jazzz for that.

The draft roadmap does take in account of the proposed structure. See Create Chat SDK milestone and deliverables, and also the Integrate RLN with Waku API milestone.

Few things I’d like to propose:

In terms of “code ownership”, at this point in time, I believe it best if core team focused on message routing, discovery, spam protection. In short, code that needs to be run by all/most nodes in the networks.

While having application level code such as segmentation, rate limit management (that you call “Retry Policies”), end-to-end reliability being own by app/chat teams.


In terms of code location, I would tend to agree that those functionality are probably best part of the “Waku SDK”. It is part of the architecture topic of the virtual off-site, let’s see what the teams come up with.