Nostr for peer discovery, instead of DHT

There has been discussion for years about how to make Syncthing’s discovery more resilient. DHT has always been the proposed solution. But this would be complex and heavy.

I think that using Nostr relays is a better solution. There are already p2p projects, like Hashtree, that use ephemeral events to discover peers.

This would be much simpler and would be pretty lightweight, while still decentralizing Syncthing’s discovery.

The real problem goes a bit deeper. Real p2p would also need to cover the initial connection between devices. But almost any kind of DHT requires extra cryptography. The only thing our clients know about each other is the device ID.

Correct. The handshake to form the p2p connection would be straightforward using nostr relays. Devices would just gossip their info to relays using ephemeral events.

A DHT hasn’t been implemented yet because it’s quite complex.

A simple “knock and callback” could be used so that no IP address is ever publicly exposed. And all of this could be done only knowing a device’s ID.

From a quick reading about Nostr and relays, it sounds like they are server instances just like our discovery servers that you’d need to run, and then specifically point your client to. Which is to say, they are conceptually shaped just like the current global discovery servers, just using a slightly different protocol?

Moving Syncthing discovery to a DHT would be primarily for two advantages as I see it:

  • Fewer beefy servers for me to run
  • The community doesn’t have to rely on specific servers or trust me to run them

Does Nostr help with that? (If we’d just rely on someone else to run the beefy servers and have to trust them instead, I’m not sure that’s a win.)

2 Likes

Is there any plans to have DHT in syncthing?

Sure. The question is how to do it without completely breaking how devices are initially connected with each other.

Exchanging the necessary public keys on an existing connection would be easy to do. But we’d still rely on central infrastructure for getting the first connection established.

All nostr relays do is store and broadcast JSON text. The nostr protocol is simply a standardized way of doing it so that an entire open public infrastructure can be built.

There are thousands of relays on the network since anyone can run one. So you technically wouldn’t have to run any infrastructure yourself unless you wanted to.

If syncthing used nostr, then a user could simply add a bunch of public nostr relays to a list in the settings. There could also be a couple hardcoded relays that you operate yourself, if you wanted.

So maybe what we’d actually be doing is learning to “speak nostr” so that we can utilize the huge network of public relays.

Down the road you could be clever and implement a way for devices to gossip and inform each other of other relays that they know about. Something akin to (or precisely) NIP-65.

I think bootstrapping is an issue with any protocol you go with, even DHT. However, at least with nostr there’s thousands of public relays in the network already. You can operate anywhere along the continuum between centralized and decentralized.

Only use a couple “official” syncthing ones: centralized.

Have a list of a hundred public ones: pretty decentralized.

It’s a very lightweight protocol, so it doesn’t stress the system/device or require heavy infrastructure. Current nostr clients on phones typically use around 10 relays at once to pull data and make a social feed.

I should clarify though. A DHT would be ideal. But Nostr would be a simple and lightweight way to tap into a large, distrubuted, public network for peer discovery.

Help me understand how that would work. My understanding (from five minutes on the nostr web, so could be wrong) is relays don’t talk to each other. Is the idea that we have a list of a hundred relays, announce to all of them, and query them all until we get a hit?

Thanks for giving me a nice rabbit hole to investigate!

However, going by the How to Nostr book (especially chapter 4), the entire problem of bootstrapping and redundancy is left as an exercise to the (developers of) the clients. Nostr seems to focus on the contents of the messages (which are trivial for a Syncthing discovery mechanism) and the communication between a client and an already discovered server (the relay).

I’d say bootstrapping and relaying are the important parts of whatever discovery mechanism Syncthing could potentially use, and should already be solved for that mechanism.

1 Like

The goal with this would be to replace Syncthing’s specialized discovery system with a nostr-based one. This would mean no single-purpose servers would be needed, as any generic nostr relay could be used.

@calmh , you said your main goal is to remove yourself as a central point of failure. I think Nostr would achieve this. Any user could point their Syncthing client to any of the thousands of existing, free public Nostr relays. You don’t have to trust the server operator because the server is just a dumb pipe passing encrypted JSON.

As to your question specifically, we wouldn’t have to constantly query hundreds of relays. A client could, however, send a NIP-65 relay list to as many relays as possible. This is a small note that says “I’m device X, and I use the following relays…”. As long as two devices have just a single overlap in that initial list, then they would immediately know the entire list of relays the other device uses. On Nostr there are many popular relays used mainly for just publishing your relay list. So those would be easy places to first look, but it’s of course not necessary, any could be used. So a device posts updates to a handful of relays. It can post this list of relays to as many places as possible. If some of its relays go down, it can just update that list.

This allows the network to “self-heal” in case of outages, censorship, etc. As long as a single relay is known about in common, then two devices can exchange a new list of relays.

I initially said ephemeral knock+callback events could be used, but on further thought I think this would drain battery because it would require always-open websockets. Instead it could be done lazily like it currently is.

A device could have a handful of relays (or any amount) that it posts updates to. This is its relay list. NIP-16 and NIP-40 could be used. NIP-16 tells a relay the event is replaceable. So when the relay receives a new update (device IP info, or whatever) it replaces the previous one in-place. So a relay wouldn’t get bogged down with data, it would only carry the latest update. NIP-40 adds an expiration tag to the event. Say 30 minutes. After that time, the relay deletes the info. This would prevent relay bloat or long-term storage. Also devices wouldn’t try connecting over outdated info or to devices that are offline.

Nostr relays are lightweight enough that there are apps for running a full relay from your phone.

NIP-44 + NIP-59 could be maybe be used for privacy. It would allow these updates to be completely encrypted. I’m not too familiar with how that actually works in practice though.

The main issue I see with this is that Syncthing uses TLS for cryptography, while nostr uses secp256k1 and Schnorr signatures to verify authenticity of update info.

I’m not too familiar with how Syncthing relays work, but maybe it’s even possible to utilize nostr relays for that as well. But that’s something else to investigate.

1 Like

The idea is good: in addition of using syncthing peer discovery infrastructure use nostr one. It adds reliability, and, to some extends, vendor-independence.

Sounds good, so far.

Reading along I think the key would have to be that the user can’t be bothered with these details. Users researching a list of servers and providing a list to their syncthing instance is a support nightmare.

If syncthing could be shipped with a comprehensive enough list with enough longevity (I.e. servers don’t move around so much that a majority of the list is still online 5 years later) then I think it could work.

If there was a mechanism where list maintenance could be shared by the servers once a successful connection is made then that’s also good. (I.e. once you connect to one successfully, you can pull a more recent update list)

I like the independence aspect. But it has to be zero setup for the user.

2 Likes

Yeah. NIP-65 would even make it so that no update needs to be pushed to syncthing to update the default list of relays. An “official” list could simply be published all over the network, and when a client sees that, it could automatically update its own.

Sure but a newly installed client would need to be able to make “first contact” to be able to fetch the list.

If the client is getting support for Nostr, then perhaps it wouldn’t be unreasonable that the global discovery servers become Nostr relays too? And their addresses are already available to a freshly installed client.

Roseen that is close to what I ended up trying, from the other side: I left the discovery servers as they are and put Nostr relays beside them. After reading this thread I built a proof of concept, Nsync: Syncthing with peer discovery over Nostr relays. It is a standalone app (Linux, plus an Android fork of Syncthing-Fork) that bundles the unmodified Syncthing core and adds the Nostr layer next to it. I am not asking Syncthing to change anything; it is a third-party experiment that happens to touch several of the questions raised here.

How it deals with them

  • First contact. It does not try to find a device from its device ID alone. Pairing swaps the device ID and a Nostr public key in one string, shown as a QR code, so there is nothing to bootstrap. The price: both sides need Nsync, and it does not help when all you have is a device ID.
  • Which relays, and zero setup. It ships a list of six public relays (I tried each one: many relays refuse this kind of event) and the user can edit it. Paired devices also publish their relay list (NIP-65) and read each other’s, so changing relays on one device does not strand it, as long as one relay is shared with the last list the other published. A Syncthing-operated relay could simply be one of the defaults, which fits your suggestion.
  • “Announce to all and query them all” (@calmh). Yes, that is what it does, with a handful of relays; at that scale it is cheap. On Android it is a short one-shot connection every few minutes rather than open sockets.
  • Is that really better than our own servers? (@calmh). The relays are still servers someone runs, so availability still depends on operators. What changes: there is no single operator, and nobody has to be trusted for correctness. Announcements are signed and expire (NIP-40); a device only accepts one from the paired key with the paired device ID, and Syncthing’s TLS still authenticates the connection itself. A relay can drop or delay an event, not forge one.
  • Privacy. Announcements are not encrypted. A relay, or anyone reading it, sees the device’s local and public IP and when it is online, under a key that exists only for that device. As far as I can tell that is no more than the discovery servers already learn for any device ID. The UI says so, and there is a switch to announce only the local-network address.

The panel shows each relay and whether it accepted the last announcement, when the address was last published and when the next update is due, and has a manual refresh and a way to add relays.

Status. Early. Two instances on one machine, with Syncthing’s own discovery switched off, found each other through a relay and connected. Against the six public relays, all of them accepted the announcements. The Android app runs on an emulator but has not seen real use. There is no NAT traversal.

The protocol is short and written down in PROTOCOL.md; I would value feedback on it, especially from the maintainers’ side. Code, Linux package and Android APKs: Release Nsync 0.3.0 · alanbimbati/Nsync · GitHub

3 Likes

So from my limited understanding, while some servers do have a limit to what kind of events they keep, (they mostly only store and relay microblogging I think) most “central” servers (big guns that is), will do the message relay for free. The message will have to be a “replacement” event kind with a fixed expiration time so (30078 I think) which is the kind that is not too expensive to keep I think. So effectively there might not be a need for anyone in the maintenaner group to worry about now running a nostr relay. They can slowly wind down their current servers from Syncthing relays to Minecraft …

Very quick suggestion on protocol, in the d-tag NSYNC/v1 should be replaced with NSYNC/peer discovery/v1 should Syncthing later decide to have other messages on nostr.

On Gui, the gui is very beautiful to look at but (speaking as a general user), personally I have enough on my plate to ever want to worry about wth nostr is. Right now in the web Gui there are 3 checkboxes and 1 input box for discovery and relay and that’s the maximum cognitive load a user should face related to nostr. I’ll probably remove nostr name altogether from basic settings and the message shown in your screenshot, basically unless you click advanced settings you shouldn’t see the name nostr

Just putting out there that the aggregated load from the current population on the current discovery servers is about 6000 requests per second, each of them typically a new TLS handshake. That’s a nontrivial amount of traffic to point to an unsuspecting server operator, generally requiring real hardware to handle.