It is wrong to describe these P2P products as server-less. In order to connect two peers over WAN it needs a form of coordination server. Since Rayfish appears to be a Claude coded wrapper over Iroh it should at-least give credit to use of Iroh's discovery and relay nodes.
Having an install script that you paste into the terminal and all it does is download a binary and stick it in a folder is wild.
If your users are savvy enough to be running random scripts they shouldn't need a script to do this and if they're not savvy enough to understand how to do that then the last thing they should be doing on earth is running a random terminal command off a website.
I still have no comprehension of how curl piped into a shell command has become the default installation method for many projects (looking at you, Rust...). It breaks my brain as to how potentially unsafe it is.
Everyone’s eventually going to run a binary they downloaded from the same place, if you’ve already decided to do that, why is a curled install script worse?
Because it normalizes a practice that, while acceptable in context of a well known project with numerous dedicated eyeballs such as Rust language, is not a generally acceptable method of installing software.
The correct way is to have M of N signatures on specific package manager pinned versions. And you trust the auditors to look at each new version, of a well-known package.
We should start a project and get it funded, to do just that. The money can go to LLM tokens for audits, at least, and hosting the multisigs and the package managers.
Anyone want to partner on this? See my profile on HN and email me.
The issue does not have to do with whether the download is a binary or source code. It has to deal with verifying the integrity of the download before installation.
Curl piped into a shell command provides no means to verify that the download is uncorrupted and unmodified before running it. For example, whenever I download software manually I check the downloaded file against the verified checksums to ensure that I have an unmodified version. Ideally I check this with gpg --verify on the signed checksum file (against the source's public key). This is a standard procedure for many organizations [1]. If you just download something and immediately run it without this step, you could potentially run a hacked version of the installation script.
Curl does verify certificates [1]. That does confirm that your connection is to the right server, but it does not confirm that the files were unmodified.
SSL/TLS/HTTPS is more about encrypting the traffic and ensuring that there was no tampering with the file between you and the server. The steps that I describe are more about ensuring that there was no tampering between you and the original source. Those are two separate problems. If you just rely on HTTPS, somebody can replace the file on the server with a modified version, and you would not know.
But where do you get the checksum from? I realize in some cases you are downloading from a mirror (thus as long as you trust the source of the checksum, that is quite useful) - but if it is from the same host - then you are just comparing against the same webserver.
You raise a good point. This is why people sign the checksums. The signature confirms that authenticity of the checksums. That somewhat moves the goalpost, though, since it then depends on where you got the source's public key, but it is still a more secure practice overall. The advantage of having the public key is that you only need to get it once and you can check many downloads later.
It is also possible to have a signed file that you can use to check the authenticity of a downloaded file directly without having to use checksums. Rust [1] does it that way for its other installation methods.
It's because people are too obsessed with providing complete instructions to incorporate any package manager into their instructions.
What we are really missing is an explicit progression from new software to maintained packages across distribution. As it is, each distro expects each package to have a maintainer, and very few people actually want to do that across several distros just to release their software. Generally, the expectation is to instead just wait around for people to make and maintain those packages by virtue of their own interest in your software, but it takes a while, and discoverability isn't automatic.
Yeah, if the script only downloads something from GH releases and doesn’t even put it in a bin dir… why not just make it a normal download from the website
Look, you are going to run an executable. There is no way around it. At some point you are going to fork over inscrutable, opaque sets of bits to your CPU and loudly proclaim them to be executable. The CPU does not know, cannot know and does not care. At some point this will be done. No matter how many hashes, digests and public keys you verify, the bits will be interpreted as instructions and energy will be expended to explore a state space you were told is or leads to the promised land. If deception is involved in any step in this process, the end result will not be what you expect it to be. The peculiarities of the transport mechanism by which these bits were transported to your particular device of computation is very nearly the absolute least interesting thing to worry about in this whole shit-show.
It's completely insane our desktop OSes are holding highly private data like banking details with zero meaningful support for sand-boxing.
This whole problem would be a non-issue if we got proper auditing and management tools. If we could properly inspect our system's resources and see what sandbox has access to what and when and how and at what time, etc. I could draw a line around a "file" or "directory" and proclaim it to be off-limits to everything but "banking app" or whatever.
All the signature verification in the world won't protect my sensitive data from being raw-dogged by this Verified(TM) binary blob. I understand it solves a different problem, but to me all this "proper package management" is theater if the other side of the equation is not being handled with the same amount of attention.
tinc (https://tinc-vpn.org/), a OSS mesh vpn that has existed for a long, long time, is another great solution with no central server. You can manage the public key distribution yourself, or just keep them checked into a git repo (my preferred solution), and it's been solid for years.
thank you for the tip here! would you say tinc can work more or less like tailscale?
I saw this: "As long as one node in the VPN allows incoming connections on a public IP address (even if it is a dynamic IP address), tinc will be able to do NAT traversal, allowing direct communication between peers."
And wondered if tailscale was doing a bit more magic than tinc is here?
> And wondered if tailscale was doing a bit more magic than tinc is here?
Yes, tailscale, rayfish, zerotier and all use an existing network of relays to do nat traversal. Tinc doesn't provide that, but allows you to be completely independent if you get (or already have) a $5 VPS.
Interesting project but can't find anything useful about the author's background on GitHub.
Commit history shows the project is a couple weeks old and the commit velocity only seems possible with heavy LLM involvement. Not unexpected but worth noting.
The repo's CLAUDE.md is huge which conflicts with published best practices around agent instructions and makes me wonder how much experience the author has using LLMs.
All that said, I'd like to use something like this for my personal devices since my personal and work Tailscale networks still can't run at the same time. But there aren't enough trust signals for me for this project yet.
1. foss, p2p-only, no server or intermediate nodes to trust (rayfish)
2. foss, brokered if necessary with all nodes self-hosted (openziti, nebula, some wireguard variants)
3. non-foss, mix of p2p and brokered, host some of the nodes yourself (openvpn, myriad of wireguard variants/wrappers like tailscale, headscale, netbird, netmaker)
One thing I seem to struggle to understand is, a simple invite code system is showcased, but how does host Alice in one country know how to contact host Bob in another country with just the invite code? This seems to require a coordination server at least right, or does the invite embed some sort of information that'd allow Bob to directly reach Alice with just the invite code?
Seems like nothing can really get around this without a server/relay/TUN/STUN server. Peer to Peer messaging just doesn't work otherwise.
Saw Iroh post on HN. Just wonder how it differ from Nostr, Scuttlebutt or Yggdrasil or DHT etc? Many from Nostr claim that they are successor of scuttlebutt, but many devs from Scuttlebutt highly dispute that.
Be good to get a comparison between these protocols for devs who want to use them.
Stable IP addresses solve it as well, but these kinds of things are not generally aimed at contexts where those are an option. Even IPv6 isn't generally stable - the prefix is ISP-defined and tends to vary similarly to IPv4 with CGNAT.
There's also "dynamic DNS", which is basically just caching one side of that server/relay/TUN/STUN handshake, and relying on DNS for global discovery.
For Iroh vs Scuttlebutt / DHT, I'll break that into two parts:
1) Iroh uses DHT for host discovery: https://docs.iroh.computer/about/faq#how-is-iroh-different-f... , and Iroh is more about "use that DHT to get a usually-direct connection globally and then you can do whatever you want", while Scuttlebutt is strictly "... and use that connection to exchange append-only logs via gossip, to implement the Scuttlebutt protocol". (Iroh does have some first-party protocols you can use, but it's lower level in general)
2) Scuttlebutt isn't DHT-based, it's "connect to a known IP to get its data and discover connections" -> "connect to them and repeat..." -> "connect further..." -> etc, plus limited-hop feed replication by default. There isn't a global lookup to connect to any member or retrieve any data, it's all friend-of-a-friend connections and you can (and do) lose connection to someone if they get a new IP address and there's no F-o-a-F(-o-a-F(...)) replication route from them that reaches you. This is also part of the reason that it works instantly when you're on the same network as someone - it's less "it can work locally if you don't have internet access" and more "local is just a discovery method, the internet isn't special at all because it's all just direct connections".
And as far as I understand Nostr, it's conceptually similar to Scuttlebutt, but with direct support for centralizing for performance (relays) and some degree of mutability / forgetfulness / etc. Scuttlebutt is a bit extreme about its logs being immutable and the only way to exchange data, and it's part of the reason it can have rather major perf issues (like needing to pull gigabytes of data before you can discover a feed's display name). (I say this as a fan of Scuttlebutt in principle, but not in practice - there are lots of practical issues with existing implementations that could be solved, but haven't, and it's a large part of why the ecosystem split into other protocols)
It may also be worth pointing out that DHTs also need stable hosts to serve as initial bootstrappers, and apps that use them tend to hard-code a web URL where they can get a small list of those nodes. They just use them to discover other nodes, and save them for next time so the bootstrappers aren't constantly needed.
Iroh doesn't use DHT as its main discovery mechanism. It uses DNS/pkarr and relays as the primary method. DHT is a separate feature if folks like to do that, we don't have it on by default and don't have it on the public relays.
The whole idea is p2p but trying to be realistic about what network environments actually look like.
There has definitely been a lot of blind-idealism in a lot of P2P systems, yea. Though sometimes that's because it was/is just a PoC to see if it works at all, and then it grows too large to change / needs to be forked.
Iroh is definitely near the top of my "play with it some time" list, that pragmatism is very important for building normal things that normal people will be using :)
I think for this kind of system to work, there has to be SOME kind of public/shared server to do the coordination. If the inviting node is behind a firewall then no amount of information can enable a guest node to connect to it without a node reachable by both.
This is very cool - I will likely see if I can use it in place of tailscale for my local LLM hosting. I feel like not having that required login would be great. Also the direct connect feature seems pretty cool, since that’s usually all I need for my use case.
Nowadays I question the necessity of vpn overlay networks. Why not just serve QUIC/HTTP3/Iroh over the internet directly in your application? And use oidc/client cert for authn/authz
I believe the main usecases for this is not requiring changing code, not requiring adding a reverse proxy in front of code I can't change, and "OSI"ish protocols (as in, not really TCP protocols - Remote Database Access, Oracle Net Services/Java Message Queues/X.500, LDAP/smb/ncacn_np).
Those days, rather that actual "vpn overlay", I use Tailscale myself mostly for the Tailscale Funnel - a somewhat stable, yet free arbitrary DNS and free reverse proxy termination of incoming data for anonymous users.
Going from starting the project two weeks ago to already having a flashy marketing site is another tell, unfortunately. As much as I would love to see a trustworthy version of this idea.
Partly. Partly because using EUF-CMA pins the record to the CA which makes membership deniability non-trivial which I don't love. It's not dumb, it's what Signal uses AFAIK and in transit message deniability is different than the signer. But still..
Hi HN, we built Rayfish, a peer-to-peer mesh VPN written in Rust on top of iroh.
The core idea: every node has a keypair, and its identity on the network is that public key. From the key we derive a stable IPv4 in 100.64.0.0/10 and a stable IPv6 in 200::/7, similar in spirit to yggdrasil. Those addresses are yours for as long as you hold the key, and they don't change when you move networks or your physical IP changes. You still reach peers by IP or by a name.ray DNS name, the difference is that the address comes from the identity rather than from where you happen to be.
"No server to trust" is the part we care about most. There is no central control plane that brokers your traffic or holds the keys to your network. Peers find each other and connect directly over iroh's QUIC stack, with NAT traversal, hole punching, and relay fallback handled underneath. Relays, when used, only forward encrypted packets and never see your keys or decide who is in your network. Membership and trust live with the peers, not with us.
How it works in practice:
- Networks are closed by default. You join with a one-time invite, a reusable key for fleets of servers, or live approval from a member already inside. The room id is only for discovery, it is never an admission credential.
- Any member can be granted the network key and act as a coordinator, so admitting new peers keeps working even if the original creator is offline.
- There is a per-device firewall, directional and scoped by port and protocol, plus Magic DNS so you can reach nodes at name.ray (or just name, no need for the .ray suffix).
- A "ray connect" flow links two people directly with no shared room, like a friend request between keys.
- No ACLs. Networks are logical partitions. Firewall is per-host. You can combine both to have custom ACLs.
It is a single binary with a daemon and a CLI. `ray up`, then `ray create` or `ray join <invite>`, and you have a private network.
Honest limitations: it is early. The mesh protocol is gated at the transport layer, so we break compatibility between releases when we need to. There has been no third-party security audit yet. Mobile is not there. It runs on Linux and macOS today.
Please disclose your use of AI. It's rather telling 2 weeks from repository commencement to release. Why should anyone use this over other products, and what testing have you done to PROVE it works as explained by your LLM?
Hey, thanks for sharing this, this is a very cool project and one that is the obvious next step with iroh. I'm curious if you plan to make it into a library to be used, or you intend to keep it solely as an application?
Great work. I'm currently using tailscale and would love to have another option!
Hosting my own iroh-relay makes it truely independent then.
Only missing the mobile app now! Keep up the good work!
If your users are savvy enough to be running random scripts they shouldn't need a script to do this and if they're not savvy enough to understand how to do that then the last thing they should be doing on earth is running a random terminal command off a website.
The correct way is to have M of N signatures on specific package manager pinned versions. And you trust the auditors to look at each new version, of a well-known package.
We should start a project and get it funded, to do just that. The money can go to LLM tokens for audits, at least, and hosting the multisigs and the package managers.
Anyone want to partner on this? See my profile on HN and email me.
Curl piped into a shell command provides no means to verify that the download is uncorrupted and unmodified before running it. For example, whenever I download software manually I check the downloaded file against the verified checksums to ensure that I have an unmodified version. Ideally I check this with gpg --verify on the signed checksum file (against the source's public key). This is a standard procedure for many organizations [1]. If you just download something and immediately run it without this step, you could potentially run a hacked version of the installation script.
[1] https://www.debian.org/CD/verify
SSL/TLS/HTTPS is more about encrypting the traffic and ensuring that there was no tampering with the file between you and the server. The steps that I describe are more about ensuring that there was no tampering between you and the original source. Those are two separate problems. If you just rely on HTTPS, somebody can replace the file on the server with a modified version, and you would not know.
[1] https://curl.se/docs/sslcerts.html
It is also possible to have a signed file that you can use to check the authenticity of a downloaded file directly without having to use checksums. Rust [1] does it that way for its other installation methods.
[1] https://forge.rust-lang.org/infra/other-installation-methods...
But this is new software from someone no one trusts yet. Verifying the binary was not maliciously replaced by someone else doesn’t matter.
What we need here is a reproducible build made and published by an independent third-party.
Would you feel safer if they offered a .deb? Do you unpack and inspect every .deb you install?
Depending on third party packaging (distribution-validated install) is much higher friction.
What we are really missing is an explicit progression from new software to maintained packages across distribution. As it is, each distro expects each package to have a maintainer, and very few people actually want to do that across several distros just to release their software. Generally, the expectation is to instead just wait around for people to make and maintain those packages by virtue of their own interest in your software, but it takes a while, and discoverability isn't automatic.
It's completely insane our desktop OSes are holding highly private data like banking details with zero meaningful support for sand-boxing.
This whole problem would be a non-issue if we got proper auditing and management tools. If we could properly inspect our system's resources and see what sandbox has access to what and when and how and at what time, etc. I could draw a line around a "file" or "directory" and proclaim it to be off-limits to everything but "banking app" or whatever.
All the signature verification in the world won't protect my sensitive data from being raw-dogged by this Verified(TM) binary blob. I understand it solves a different problem, but to me all this "proper package management" is theater if the other side of the equation is not being handled with the same amount of attention.
I saw this: "As long as one node in the VPN allows incoming connections on a public IP address (even if it is a dynamic IP address), tinc will be able to do NAT traversal, allowing direct communication between peers."
And wondered if tailscale was doing a bit more magic than tinc is here?
Yes, tailscale, rayfish, zerotier and all use an existing network of relays to do nat traversal. Tinc doesn't provide that, but allows you to be completely independent if you get (or already have) a $5 VPS.
Commit history shows the project is a couple weeks old and the commit velocity only seems possible with heavy LLM involvement. Not unexpected but worth noting.
The repo's CLAUDE.md is huge which conflicts with published best practices around agent instructions and makes me wonder how much experience the author has using LLMs.
All that said, I'd like to use something like this for my personal devices since my personal and work Tailscale networks still can't run at the same time. But there aren't enough trust signals for me for this project yet.
1. foss, p2p-only, no server or intermediate nodes to trust (rayfish)
2. foss, brokered if necessary with all nodes self-hosted (openziti, nebula, some wireguard variants)
3. non-foss, mix of p2p and brokered, host some of the nodes yourself (openvpn, myriad of wireguard variants/wrappers like tailscale, headscale, netbird, netmaker)
why is #3 so much more popular?
However you could self host one of these on a public server you own. Then you're independent.
Saw Iroh post on HN. Just wonder how it differ from Nostr, Scuttlebutt or Yggdrasil or DHT etc? Many from Nostr claim that they are successor of scuttlebutt, but many devs from Scuttlebutt highly dispute that.
Be good to get a comparison between these protocols for devs who want to use them.
There's also "dynamic DNS", which is basically just caching one side of that server/relay/TUN/STUN handshake, and relying on DNS for global discovery.
For Iroh vs Scuttlebutt / DHT, I'll break that into two parts:
1) Iroh uses DHT for host discovery: https://docs.iroh.computer/about/faq#how-is-iroh-different-f... , and Iroh is more about "use that DHT to get a usually-direct connection globally and then you can do whatever you want", while Scuttlebutt is strictly "... and use that connection to exchange append-only logs via gossip, to implement the Scuttlebutt protocol". (Iroh does have some first-party protocols you can use, but it's lower level in general)
2) Scuttlebutt isn't DHT-based, it's "connect to a known IP to get its data and discover connections" -> "connect to them and repeat..." -> "connect further..." -> etc, plus limited-hop feed replication by default. There isn't a global lookup to connect to any member or retrieve any data, it's all friend-of-a-friend connections and you can (and do) lose connection to someone if they get a new IP address and there's no F-o-a-F(-o-a-F(...)) replication route from them that reaches you. This is also part of the reason that it works instantly when you're on the same network as someone - it's less "it can work locally if you don't have internet access" and more "local is just a discovery method, the internet isn't special at all because it's all just direct connections".
And as far as I understand Nostr, it's conceptually similar to Scuttlebutt, but with direct support for centralizing for performance (relays) and some degree of mutability / forgetfulness / etc. Scuttlebutt is a bit extreme about its logs being immutable and the only way to exchange data, and it's part of the reason it can have rather major perf issues (like needing to pull gigabytes of data before you can discover a feed's display name). (I say this as a fan of Scuttlebutt in principle, but not in practice - there are lots of practical issues with existing implementations that could be solved, but haven't, and it's a large part of why the ecosystem split into other protocols)
It may also be worth pointing out that DHTs also need stable hosts to serve as initial bootstrappers, and apps that use them tend to hard-code a web URL where they can get a small list of those nodes. They just use them to discover other nodes, and save them for next time so the bootstrappers aren't constantly needed.
Iroh doesn't use DHT as its main discovery mechanism. It uses DNS/pkarr and relays as the primary method. DHT is a separate feature if folks like to do that, we don't have it on by default and don't have it on the public relays.
The whole idea is p2p but trying to be realistic about what network environments actually look like.
Iroh is definitely near the top of my "play with it some time" list, that pragmatism is very important for building normal things that normal people will be using :)
[1]: https://nebula.defined.net/docs/#technical-details
Those days, rather that actual "vpn overlay", I use Tailscale myself mostly for the Tailscale Funnel - a somewhat stable, yet free arbitrary DNS and free reverse proxy termination of incoming data for anonymous users.
Sigh..
I like the project though. It looks very similar to something I vibed up recently, must be in the air
"Membership is a question they ask a server" is a bogus sentence. "membership" is not a "question". It's syntactically valid semantic nonsense.
"Membership is dictated by a server" is one of several human sentences saying what that one is trying to.
https://github.com/rayfish/rayfish/commit/c49816e6dfba19e91a...
Also that sentence structure is very claudelike.
The core idea: every node has a keypair, and its identity on the network is that public key. From the key we derive a stable IPv4 in 100.64.0.0/10 and a stable IPv6 in 200::/7, similar in spirit to yggdrasil. Those addresses are yours for as long as you hold the key, and they don't change when you move networks or your physical IP changes. You still reach peers by IP or by a name.ray DNS name, the difference is that the address comes from the identity rather than from where you happen to be.
"No server to trust" is the part we care about most. There is no central control plane that brokers your traffic or holds the keys to your network. Peers find each other and connect directly over iroh's QUIC stack, with NAT traversal, hole punching, and relay fallback handled underneath. Relays, when used, only forward encrypted packets and never see your keys or decide who is in your network. Membership and trust live with the peers, not with us.
How it works in practice:
- Networks are closed by default. You join with a one-time invite, a reusable key for fleets of servers, or live approval from a member already inside. The room id is only for discovery, it is never an admission credential. - Any member can be granted the network key and act as a coordinator, so admitting new peers keeps working even if the original creator is offline. - There is a per-device firewall, directional and scoped by port and protocol, plus Magic DNS so you can reach nodes at name.ray (or just name, no need for the .ray suffix). - A "ray connect" flow links two people directly with no shared room, like a friend request between keys. - No ACLs. Networks are logical partitions. Firewall is per-host. You can combine both to have custom ACLs.
It is a single binary with a daemon and a CLI. `ray up`, then `ray create` or `ray join <invite>`, and you have a private network.
Honest limitations: it is early. The mesh protocol is gated at the transport layer, so we break compatibility between releases when we need to. There has been no third-party security audit yet. Mobile is not there. It runs on Linux and macOS today.
Code: https://github.com/rayfish/rayfish
Happy to get into the addressing scheme, the iroh transport, the admission and coordinator model, or anything else.
With only 22 bits of entropy in your v4 addresses, you'll get accidental collisions with only ~2000 users.
> Happy to get into the addressing scheme
I truly loathe how all of the HN spambots promoting shovelware include a stupid call-to-action for feedback/discussion.
No reply to various questions an hour later. I guess they're not really watching.
im also afraid of exploits disseminating from a mesh network it would be impossible to stop
great work