I hacked up a prototype multi-player game in just a static HTML/JS
files. The game runs in players' browsers without the need for a centralized
game server. This peer-to-peer model - getting rid of the server - is something
I've been interested in for a long time. I finally discovered a way to make it
work without hosting my own server or relying on a hosted service that requires
API keys, accounts, or payments. That missing piece came in the form of nostr,
a decentralized network protocol that I'll get into later.
Recently p2panda and Veilid were released. They are decentralized
application frameworks. Neither has the exact properties I like, but that
spurred me to work on a prototype game that shows the direction that I find
promising for decentralized applications.
Distributed application models
Most distributed applications today are built on a centralized client-server model.
Applications are not a single program, but two programs. A client application
on the user's device communicates with a server application on the application
owner's machines. The way it works is pretty simple: the server holds the data
and the client sends commands to operate on the data.
The centralized client-server model is kind of a drag because you need to
develop two separate programs and maintain a server so that the application
remains online at all times. Non-technical users can't really host the
application themselves. It costs money to run the server. If the application
owner decides to pull the plug on the server then users cannot use the
application anymore. Bad practices of locking in, hoarding, and selling user
data as well as monitoring and manipulating user behavior are commonplace
because the server controls
access to user data.
Peer-to-peer applications solve many of these issues. The advantages are roughly:
- Eliminating the cost, effort, and skill required to maintain servers.
- Improving user privacy by not involving a server.
- Operating without constant internet connectivity.
- Enabling users to run the application even after the developer has stopped supporting it.
- Reducing application complexity by replacing client/server with a single program.
How to make a peer-to-peer application
This needs to work for web, mobile, and desktop applications because people switch between these three environments all the time. It would be impractical if the solution does not support all environments.
The web is the most restrictive environment,
mostly for security reasons. Many technologies are not available on the web,
including networking APIs that desktop peer-to-peer applications tend to rely
on. But if a solution works on the web, then mobile and desktop applications
are likely to be able to use the same technology and interoperate with web
applications.
Luckily the web environment has one technology that can be used to build
peer-to-peer applications: WebRTC.
Implementations are available for mobile and destkop environments as well.
WebRTC's DataChannels can be thought of as network connections that transfer
messages between two devices. They are the primitive for communicating in a
peer-to-peer application in place of HTTPS, TCP, or UDP connections that most
existing application use today.
Unfortunately WebRTC is not fully peer-to-peer because it relies on a
"signaling server" for connection establishment. The signaling server exchanges
connectivity information so that a peer-to-peer connetion can be negotiated.
This negotiation process does not always succeed, by the way, so
in some cases it is not possible to create a peer-to-peer connection. I have no
solution for that without hosting servers.
The crux of using WebRTC is that a signaling server is needed, but we don't
want to host one for each application. Over the years I've investigated
existing peer-to-peer networks like Tor and WebTorrent to see if they could act
as the signaling server. I didn't find one that is usable from the web
environment (it's too restricted) until now.
It turns out that nostr, originally designed for social network applications
but now being used for a bunch of different applications, is web-friendly and
could act as a WebRTC signaling server quite easily. In my prototype I abused
the encrypted direct message (NIP-04) feature for WebRTC signaling. It
works but has the downside that the nostr relay wastes storage because there
is no need to preserve the messages. That can be fixed by assigning an
"ephemeral kind" so the relay knows it can discard messages after delivery.
(Another option is to build a free public WebRTC signaling service. Its
design would be remarkably close to the nostr protocol, so I decided not to
reinvent the wheel. If anyone wants to create a public service, let me know and
I can share ideas and research.)
Once connectivity has been established via WebRTC, it's up to the
application to decide how to communicate. It could be a custom protocol like
the JSON messages that my prototype uses, it could be the nostr protocol, it
could be HTTP, or literally anything.
The user experience
Here is how my game prototype works:
- Player A opens the game web page (just static files hosted on GitLab Pages) and clicks "Host" game.
- Player A shares the game link with player B.
- Player B opens the game link and uses nostr to exchange WebRTC signaling messages encrypted with the other player's public key.
- A WebRTC DataChannel is negotiated and nostr is no longer used once the peer-to-peer connection is established.
- The game proceeds with player A and B exchanging game messages over the DataChannel.
In order to connect apps, a user must share a public key with the other
user. The public key allows the other user to connect. In my prototype the
player hosting the game gets a URL that can be shared with the other player.
When the other player visits the URL they will join the game because the public
key is embedded in the URL. The role of the public key is similar to the idea
behind INET256's "stable
addresses derived from public keys".
When devices go offline it is no longer possible to connect to them. This is
not a problem for short-lived use cases like playing a game of chess or
synchronizing the state of an RSS reader application between a phone and a
laptop. For long-lived use cases like a discussion forum or a team chat there
are two options: a fully peer-to-peer replicated and eventually consistent data
model or a traditional centralized server hosted on a supernode. Both of these
options are possible.
Try it out
You can try out my prototype in your web browser. It's a 2-player tic-tac-toe game: https://gitlab.com/stefanha/tic-tac-toe-p2p/. If the game does not start, try it again (sorry, I hacked it up in a weekend and it's not perfect).
If you want to discuss or share other peer-to-peer application approaches, see my contact details here.