Skip to content

Instantly share code, notes, and snippets.

@ryogrid
Last active March 18, 2024 13:48
Show Gist options
  • Save ryogrid/fa2bfa284784c866ad88e3c38445752a to your computer and use it in GitHub Desktop.
Save ryogrid/fa2bfa284784c866ad88e3c38445752a to your computer and use it in GitHub Desktop.
Pure P2P Distributed Microblogging System by All Sser's Contribution (NostrP2p)
  • Concept
    • Microblogging system by all user's contribution
      • Problem NostrP2P focus on: Existing distributed SNS (Mastdon, Nostr, Bluesky, etc.) place a big works on the server operators, but there is no insentive for that
  • Other points
    • A system centered on broadcasting using the gossip protocol (messaging is handled by the weaveworks/mesh library)
    • Focus on ease of implementation and simplicity over performance and data consistency
      • In the end, it may come down to wanting to save man-hours, but if you put a complicated mechanism into this kind of system, it's difficult to make it work stably
      • For the above reasons, we will not adopt structuring mechanisms such as DHT (for the time being)
    • Make the whole thing fuzzy (ex: event data loss is acceptable as long as it is a small amount)
      • Compared to other decentralized SNS, it is a pure P2P architecture that is difficult to operate with decent performance, but it can be managed by making a trade-off with the level of user experience
        • However, I will determine features which are dropped carefully
    • Each server operates on an overlay NW (NAT is overcome by relaying by a server with a global IP)
    • Based on the idea of Nostr
      • Authentication infrastructure using private key and public key, The data structure of event data, the design of various Kinds to realize microblog application's features, etc
      • However, since the architecture is different, there are cases where it becomes impossible and possible, so the protocol should be optimized accordingly
      • Therefore, although we agree on the points that are acceptable, the design will be incompatible in areas where it is difficult to do so
    • Prerequisite for using mesh in go (it would be nice if there were libraries compatible with mesh in other languages, but there are currently none)
      • In mesh, each node has a 64-bit ID
      • Each node knows the IDs of all nodes participating in the overlay NW (there will be a delay until it is updated, but I think it will be fine in 2-3 seconds if there are up to about 100 nodes... )
    • Each user sets up a server on their own machine. The client used by each user (something responsible for the presentation layer) reads and writes to its own client (details will be explained later)
    • Utilizing the fact that each server is mapped to a 64-bit ID space on the overlay NW, data replication and other operations are performed using the same concept as DHT-based KVS (details will be described later)
    • As a result of the Nostr protocol's emphasis on readability and versatility, communication volume is relatively large when used in microblogging systems, so NostrP2P will be optimized specifically for microbloggging application and serializing data into a binary format. Therefore, communication volume is keeped low
    • Push rather than pull (details later)
  • Each person sets up a server on their own machine and send Read and write to it
    • If you set it up on your home machine (that is, a machine that does not have a global IP), it is assumed that you will connect a VPN such as tailscale and you will be able to access to the server from anywhere
    • If there is no server operating with a public IP, an overlay system cannot be established
    • => For operating on a public IP, prepare settings to verify whether writes from clients of server owner or not using signatures
  • Each server has two supoprt servers so that it can receive event data while it was offline when it returns
    • The decision logic for the support server is the same as for the replica server described later
  • Push rather than pull
    • Broadcast it and have it received only by those who follow you (or if the requester of the server acting as your supporter follows you), and have others discard it
    • As a general rule, it is assumed that the recipient is online when broadcasting
    • However, when the server comes online, it receives unobtained data from its own support servers
    • Even if you are not following, when you are in charge of replicas, keep it and return it when data request is received due to absence of master
      • There are two nodes in charge of replicas, and if the master is offline, query both and merge the results
      • The first replica person looks at the ID space as a circle like Chord, adds one-third of the maximum value of the ID space to the master ID, and selects the node with the larger and closest ID
      • The second person in charge of the replica adds two-thirds and does the same
  • Private key
    • Only the client needs to have it
    • Set only the public key on the server. With that information, writes from users who don't have the correct private key can be blocked, and other servers can unicast some messages
    • The private key and public key of Nostr (HEX format) can be used
      • NostrP2P uses ECDSA with sekp256k1 for signing as same as Nostr
  • Basically, data is retained in on-memory
    • However, I only need to make it permanent by periodically writing it out using MessagePack or etc. For now (it's all useless every time, so let's summarize it in a time range? To do that, do we need a data structure that can narrow down the time range?) When the amount of data becomes too large to fit in memory, old data is discarded
      • It's not a bad idea to introduce an embedded DB, but considering the load when searching is required, I thought it would be better to maintain a small amount of target data, so I started with the above specifications
  • Communication between servers is binary: {ID, public key, tag kind, timestamp, content, signature}. Same as Nostr except for replacing it with binary and change the event ID to 64bit (uint64)
    • For the binary format, use MessagePack for now. Might switch to ProtoBuf later
    • Do not perform communication over TLS as it will be more troublesome when setting up the server
      • mesh seems to be able to generate a common key from the password commonly set in the application and perform encryption, but this function is not used due to concerns about performance degradation and poor compatibility with OSS
      • Even if it is done, it will probably involve doing E2EE separately, but there is a problem that it is not compatible with broadcasting
  • Follows
    • Since the list of node IDs (= user ID list) on the NW is known, it is possible in theory to pull the profile from that information and display the user list
  • Replies
    • This can only be done for the person you are following, and is visible only to those involved
    • Since you know the event ID of the target post, include it in the content and throw it. Make nesting possible
  • Likes
    • Unicast to the master server of the original user and the servers in charge of replicas
    • When you go online, you will receive event data from the replica manager
    • Those who liked the post only know that they liked the target post, but the total number is not known. Only author of the post know the total number of Likes
    • Other people cannot know any information about Likes of posts
  • Profiles
    • Broadcast after updating
    • If you follow a new one, get the latest one from the user's server master or replica
    • (As a temporary specification until the replica mechanism is implemented, there is a 20% chance that the time of the last profile update will be included in the post tag, so followers with old profiles will know that they need to update)
  • Joining and leaving nodes
    • It is assumed that leaving will not be done gracefully
    • When joining
      • The server in charge of the replica changes depending on the ID range, so delegate data to appropriate server
    • When leaving
      • When the server in charge of replicas leaves, it is necessary to maintain a state where the number of replicas is 2
      • ( How do we detect withdrawal? Does the master periodically send a heartbeat to the replica node via unicast? )
        • Considering the possibility that a node that left will come back, I would like to increase the number of replicas when the node leaves for a certain period of time, but it seems difficult
  • Clients
    • Communicate with your own server
    • Assuming REST with binary req/resp for now
      • (Before that, I think we will proceed in stages, such as completing the client with server-side rendering or creating it from a text-based REST I/F)
    • As mentioned above, add a signature to the write
    • The response to read should not follow the the event data format of Nostr. At least the signature has been verified on the server, so this is excluded
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment