Networking in Joint
This document will cover the traits of the networking implementation of Joint, and how you can work with it.
Node Replication
Joint provides built-in support for node replication, allowing you to synchronize the state of Joint nodes properties multiple clients in a networked environment.
It's very simple to make your Joint Node replicated - you just have to enable the Replicates property on the node class, and Joint will take care of the rest for you.

After enabling this property, the properties marked with Replicated or Replicated Using specifiers in your node class will be automatically synchronized across the network.
What you have to do next is simply to implement the logic that utilizes the replicated properties in your node class!
Please check out Joint Native's DF_ServerClient_WaitSkip and DF_ServerClient_Vote BP for the example of a replicated node.
Playback State is NOT Replicated
One of the core concept you have to know is that the playback state of fragments itself is NOT replicated.
This means that the Begin / End / Pending end states of fragments are managed independently on each client - but ONLY THE PROPERTIES marked as Replicated or Replicated Using are synchronized across the network.
This is totally intentional design decision for the Reactive Playback system of Joint, as it allows each client to maintain its own playback state while still being able to synchronize the necessary data across the network.
Working Around It
If you want to control the playback state of fragments across the network, You have to implement some additional logic to do so using the replicated properties of your nodes, For example, you can create a replicated property that indicates whether a fragment should be playing or not, and then use that property to control the playback state on each client on RepNotify function.
RPC Function Execution for Nodes
Joint Nodes also support RPC function execution, allowing you to call functions on nodes across the network.
It's basically the same as the standard Unreal Engine RPC system - you can mark your node functions with Server, Client, or NetMulticast specifiers to indicate how they should be executed across the network.
Joint Nodes CAN'T Execute RPC Functions Directly
But there is one important thing you have to keep in mind when working with RPC functions on Joint Nodes: Node itself CAN'T execute RPC functions directly.
Unfortunately, this is due to the networking system design of the Unreal Engine itself.
Unreal's networking system is designed around with the authority model, where only objects that the host(client, server) has authority over the object can execute RPC functions of the object.
If you need further details about this concept, please refer to Unreal Engine's official documentation about Networking and Multiplayer.
Working Around It
Since the clients don't have authority over the Joint Nodes (as they are owned by the Joint Actor Instance, which is typically owned by the server), they can't execute RPC functions on the nodes directly.
So what you have to do is to use an actor that the client has authority: such as a player controller. This is a common pattern in Unreal Engine networking, where the player controller acts as a bridge between the client and the server.
Here is an example of the best practice of how to execute RPC functions on Joint Nodes:
In the Joint Native's Sample Contents, You can find BP_Joint_NetAuthorityHandleInterface interface that declares abstract functions for executing RPC calls for DF_ServerClient_WaitSkip and DF_ServerClient_Vote. We used this interface as an additional layer for the abstration because we didn't want to tightly couple the Joint Node classes with the sample player controller class.

And in DF_ServerClient_WaitSkip and DF_ServerClient_Vote, we cast the player controller object to BP_Joint_NetAuthorityHandleInterface and call the functions declared in the interface to execute the RPC calls.

And in the actual player controller class, BP_JointSamplePC, we inherit BP_Joint_NetAuthorityHandleInterface and override the interface functions to execute the actual RPC function that will be executed on the server side, and from there, we can call the RPC functions on the Joint Nodes.
(So we're making the client's fragment to let the client's player controller to send request to the server via RPC, and the server's player controller will execute the actual RPC function on the Joint Node instead of the client.)

Automated Per-Client Perfect Playback Timing Controls
Joint provides Reactive Playback for different clients - and here is a video that explains the concept of Reactive Playback for different cultures and clients:
Please refer to Reactive Playback For Different Culture & Per Client and read the document to understand the concept of Reactive Playback for different clients before proceeding.
Key principle of this concept is that each client manages its own playback of the fragments independently, allowing for perfect synchronization of content regardless of the networking situation.
This is why the playback state of fragments is not replicated across the network - because each client needs to manage its own playback state independently to achieve perfect timing for each client.
Authoritative Base Node Playback Control
Even though the playback state of fragments is not replicated across the network, Joint needs and provides a way to sync out and control the overall playback of the Joint Manager.
Joint solve this issue by letting only the host(server or client) that has authority over the Joint Actor Instance to control the playback of the Base Nodes (such as Foundation Node), and the other clients' base nodes' playback state will follow the playback state the base nodes of the authoritative host.
This is absolute rule - if the authoritative host pauses, skips, or stops the base nodes, the clients will do the same.
How To Prevent "Skipping" on Clients
But one problem is, this means that clients with no authority can experience some "skipping" on the playback - for example, if the authoritative host skips a base node while the client didn't finish playing the fragments on the base node, the client will immediately ends all the fragments that were being played and move on to the next base node.
Preventing this "skipping" behavior is actually very simple - you just have to make sure that the authoritative host only skips or moves on to the next base node when all clients are ready to do so.
One of the best practices to achieve this is to implement a "ready check" system using replicated properties on your Joint Nodes - Which is exactly what Joint Native's DF_ServerClient_WaitSkip node does.
This node checks and waits til enough number of players press "skip" button and say "ready" to proceed, and only when the required number of players are ready, this fragment will end and allow the base node to move on to the next one.