Files
UDN/README.md
T

225 lines
10 KiB
Markdown
Raw Normal View History

2024-07-04 14:57:26 +02:00
# universal-decentralized-network
2024-07-05 19:46:38 +02:00
2024-07-08 18:40:41 +02:00
The UDN project aims to provide decentralized and localhost-able infrastructure for messaging and communication. This infrastructure can be utilized in any scenario where data needs to be sent between devices, especially when no internet connection is present.
2024-07-05 19:46:38 +02:00
2024-07-08 18:27:30 +02:00
# Contents
- [How it works](#how-it-works)
- [Channels](#channels)
- [Scalability](#scalability)
- [Example](#example)
2024-07-08 18:27:30 +02:00
- [Administrator's Guide](#administrators-guide)
- [Getting Started](#getting-started)
- [Configuration](#configuration)
- [Connecting Multiple Servers](#connecting-multiple-servers)
2024-07-08 18:27:30 +02:00
- [App Developer's Guide](#app-developers-guide)
- [Guidelines](#guidelines)
- [The Recommended Way](#the-recommended-way)
- [The Manual Way](#the-manual-way)
2024-07-08 18:27:30 +02:00
2024-07-05 19:46:38 +02:00
# How it works
## Introduction
2024-07-08 22:02:00 +02:00
Let's assume you want to set up a local network at home. Using this local network, you want to chat with family members. This has two advantages: nothing is sent out to the internet, so no company can observe your messages; and your communication continues to function even if the internet is unavailable. All you need is a local Wi-Fi network and a so-called server. You can set up any computer in your house as a server.
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
Remember that the UDN project provides infrastructure and is utilized by separate apps. While your server does provide basic messaging, these messages are not encrypted and only intended for maintenance purposes. You can find a list of apps in the User's Guide below.
2024-07-05 19:46:38 +02:00
## Channels
2024-07-08 16:32:02 +02:00
Communication via the UDN project is based on so-called channels. Think of channels as radio frequencies. You can subscribe to and send messages on one or multiple channels. When a message is sent on a channel you subscribe to, you will receive the message. Other messages will not be sent to your device.
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
In the standard scenario, we can think of a channel as a group chat that everybody can join, sending a message on this channel will send it to every "member of the group". As always, the name of a group chat should describe what the chat is about. We'll call this channel the "primary channel" and ignore the other channels for now.
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
Subscriptions and messages are handled by a server:
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
```md
**Josh** --(subscribe to "my-channel")-> **Server**
**Josh** <-(subscription confirmed )-- **Server**
2024-07-05 19:46:38 +02:00
**Bob** --(subscribe to "my-channel")-> **Server**
**Bob** <-(subscription confirmed )-- **Server**
2024-07-05 19:46:38 +02:00
**Josh** --("hello" on "my-channel" )-> **Server**
**Josh** <-("hello" on "my-channel" )-- **Server**
**Bob** <-("hello" on "my-channel" )-- **Server**
2024-07-08 16:32:02 +02:00
```
## Scalability
Now, let's assume we have an organization with hundreds of users. If all of these users connected to the same server, that server would have a very bad time. To solve this issue, we can set up multiple different servers and evenly distribute the users. This reduces the stress on each server and allows the network to scale.
However, each individual server is responsible for receiving and forwarding messages. If we want everybody on the network to be able to communicate, even when they are connected to different servers, our servers will need to talk to each other. Servers talk to servers the same way user devices talk to servers: they connect and then subscribe to channels.
It would be highly impractical to make every server subscribe to every primary channel there is. So instead, we utilize the fact that users can send on multiple channels. Let's explore this in an example:
## Example
**Scenario**
2024-07-08 22:02:00 +02:00
John and Alice have set up their own UDN server, called S1. Their neighbors, Jane and Bob, did the same, we'll call their server S2. In this scenario, John can chat with Alice, and Jane can chat with Bob. But what if John wants reach Bob about his car's extended warranty? To achieve this, we need to first connect S1 and S2.
2024-07-08 16:32:02 +02:00
**The bad way**
2024-07-08 22:02:00 +02:00
2024-07-08 16:32:02 +02:00
We could simply tell John, Bob, S1, and S2 to subscribe to "car-warranty", a primary channel (it describes the topic of the chat). What if Alice and Jane want to chat as well? In this setup, they'd have to use the "car-warranty" channel too, because anything else will not be sent to the other server. But that would interrupt John's highly important business. Also, Alice and Jane don't care about Bob's car, so they should create their own primary channel describing their conversation anyway.
They would then have to reconfigure S1 and S2 to also subscribe to this new primary channel, restart the servers to load the new config, and then start messaging. That would be a lot of effort, and it would have to be repeated for every new topic.
**The good way**
2024-07-08 22:02:00 +02:00
2024-07-08 16:32:02 +02:00
As said previously, a message can be sent on multiple channels. One of them is the primary channel describing the topic of the chat, every other channel is called secondary. Instead of adding primary channels to our server configs, we'll decide on a secondary channel, for example "s1-and-s2". This needs to be set up only once.
Then, John will send his messages on both "s1-and-s2" and "car-warranty". As always, his server will forward this message to every device subscribing to either of the channels. So the transfer would look like:
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
```md
# John and Bob subscribe:
**John** --(subscribe to "car-warranty")-> **S1**
**John** <-(subscription confirmed )-- **S1**
2024-07-08 16:32:02 +02:00
**Bob** --(subscribe to "car-warranty")-> **S2**
**Bob** <-(subscription confirmed )-- **S2**
2024-07-08 16:32:02 +02:00
---
# The servers subscribe:
2024-07-08 16:33:05 +02:00
**S1** --(subscribe to "s1-and-s2" )-> **S2**
**S2** --(subscribe to "s1-and-s2" )-> **S1**
2024-07-08 16:32:02 +02:00
---
# Sending the Message:
John sends the message to his server
**John** --("hello" on: )-> **S1**
( - "s1-and-s2" )
( - "car-warranty" )
2024-07-08 16:32:02 +02:00
S2 subscribed to "s1-and-s2", so S1 sends this message to S2
**S1** --("hello" on: )-> **S2**
( - "s1-and-s2" )
( - "car-warranty" )
2024-07-08 16:32:02 +02:00
Bob subscribed to "car-warranty", so S2 sends this message to Bob
**S2** --("hello" on: )-> **Bob**
( - "s1-and-s2" )
( - "car-warranty" )
2024-07-08 16:32:02 +02:00
```
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
This requires John to know of "s1-and-s2" and, obviously, Bob to subscribe to "car-warranty". Jane and Alice could agree on their own primary channel and use "s1-and-s2" just like John did.
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
We could now add a third server, connect it to at least one of S1 and S2, configure it to subscribe to the same secondary channel, and thereby extend the network further. We could also create another secondary channel like "s2-and-s3" and expect John to send his stuff on all thee channels.
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
As you can see, there are many ways to connect multiple servers and communicate across them, all with their own pros and cons.
2024-07-08 11:58:39 +02:00
2024-07-08 16:32:02 +02:00
---
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
# Administrator's Guide
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
## Getting Started
The UDN project is based on [bun](https://bun.sh/), a faster and more streamlined alternative to node.js.
1. Make sure bun is installed on your server.
2. Create a directory for your server and open it in a terminal.
3. Run `bun add udn` to install the UDN server package.
4. Start the server with `bun run start`.
5. A configuration file will be created automatically.
## Configuration
2024-07-05 19:46:38 +02:00
By default, the configuration file looks like this:
```JSON
{
"port": 3000,
"connectedServers": [],
"subscribedChannels": []
}
```
The port and channels are self-explanatory. For servers, you'll need to provide full addresses:
```JSON
{
"port": 3000,
"connectedServers": [
2024-07-08 11:58:39 +02:00
"ws://192.168.0.100:3000",
2024-07-08 16:32:02 +02:00
"wss://192.168.0.200:3000"
2024-07-05 19:46:38 +02:00
],
"subscribedChannels": [
2024-07-08 16:35:06 +02:00
"secondary-channel-1",
"secondary-channel-2"
2024-07-05 19:46:38 +02:00
]
}
```
2024-07-08 23:34:16 +02:00
## Connecting Multiple Servers
2024-07-08 16:32:02 +02:00
1. Add the WebSocket addresses of servers you want to connect to the `connectedServers` array in the configuration file.
2. Add secondary channels to the `subscribedChannels` array in the configuration file.
Use the code example given above for reference.
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
As explained in the _How it works_ section, you may want to use multiple servers on your network. In this case, consider the following:
2024-07-06 14:40:01 +02:00
2024-07-08 16:32:02 +02:00
- Server-to-server-connections are bidirectional, so connecting server A to server B yields the same output as connecting server B to server A.
- Servers will attempt to restore lost connections to other servers periodically.
- You should make sure to evenly distribute your clients to different servers, so give your clients addresses of different servers to avoid everybody using the same one.
- Decide on one secondary channel for your network, configure all servers to subscribe, and make sure your users know this channel.
- If you are using self-signed certificates, your servers may refuse to connect to other TLS-enabled servers.
- In case your clients need to use `http`-served apps, these apps may refuse to connect to TLS-enabled servers.
- Therefore, you should have at least one TLS-enabled server, and at least one server without TLS.
- Configure the TLS-enabled servers to connect to those without TLS; connecting vice-versa may not work.
2024-07-06 14:11:48 +02:00
2024-07-08 16:32:02 +02:00
---
# App Developer's Guide
## Guidelines
- When supporting networks with multiple servers, differentiate between primary and secondary channels according to the explanation above.
- Subscribe only to the primary channel, send on the primary and all secondary channels.
- Make sure to label these channels as primary and secondary (you may translate these along with the rest of your app) as users are instructed on these terms.
2024-07-08 18:27:30 +02:00
## The Recommended Way
2024-07-08 16:32:02 +02:00
**Base**
For TypeScript-based projects, you can use the [udn-frontend](https://github.com/marlon-erler/udn-frontend) package to manage the connection. This project provides methods for connecting/disconnecting, subscribing/unsubscribing, and sending messages. If you want to/need to implement this yourself, follow the manual instructions.
2024-07-08 18:27:30 +02:00
## The Manual Way
2024-07-06 14:40:01 +02:00
2024-07-08 16:32:02 +02:00
UDN is based on WebSockets sending JSON strings.
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
The JSON object can have any of these properties:
2024-07-08 11:58:39 +02:00
2024-07-05 19:46:38 +02:00
```TypeScript
export interface WebSocketMessage {
// subscribing to channel
subscribeChannel?: string;
unsubscribeChannel?: string;
2024-07-08 16:32:02 +02:00
subscribed?: boolean; // sent by the server to confirm subscription
2024-07-05 19:46:38 +02:00
2024-07-08 16:32:02 +02:00
//sending or receiving message
2024-07-05 19:46:38 +02:00
messageChannel?: string;
messageBody?: string;
}
```
2024-07-08 16:32:02 +02:00
**Setup**
1. Create a WebSocket connection to the server
2. Listen to messages, when receiving:
- parse the message
- handle it in whatever way you need
3. Define functions to send whatever you need to send
**Sending messages**
2024-07-08 11:58:39 +02:00
2024-07-05 19:46:38 +02:00
1. Create an empty object
2. Add the properties you want to send
2024-07-08 16:32:02 +02:00
3. Stringify the object via `JSON.stringify()`
2024-07-08 11:58:39 +02:00
4. Send the string via WebSocket