Introducing Rayfish
Today we're releasing Rayfish, a peer to peer mesh VPN. It builds a private network between your machines without a server in the middle, and without a company you have to trust to keep it running. You get a key, your devices use it to find each other, and the network that forms belongs to the people in it.
Why we built it
Two things bothered us about the private networks people rely on every day.
The first is trust. Almost every modern VPN runs its control plane through a company. That company gets to decide who is in your network and holds the policy that governs it, and it stays in the loop the whole time your machines are talking. The day it goes down, or raises its price, or decides it would rather not have you as a customer, your private network stops being entirely yours. Rayfish takes that company out. There's no account to hold and no operator to answer to. The network's state is just a signed record that any member can hand to any other, so things keep running even if we disappear.
The second is dealing with more than one network at a time. Nobody actually lives on a single flat network. You might have one with friends, one for a side project, one at work, plus a couple of machines that need to sit in several of those at once. Rayfish assumes that from the start. It runs as one process behind one virtual interface, and through it you're a full member of every network you've joined, with each one kept isolated from the rest.
An old idea, finally shippable
This isn't something we dreamed up last quarter. The core idea has been kicking around with us for more than four years, and it never turned into a product for a fairly dull reason: it was never the priority, and a decentralized network is hard to build a business on. The very thing that makes it good for you, no central operator, is what takes away the obvious place to charge rent. We can't bill you for a server we keep running, because there isn't one.
That may change as it grows. There are enterprise-shaped problems sitting nearby, things like fleet management and audit, that companies do pay to have solved, and we may build toward some of them later. None of that exists yet, and we didn't want to hold the open tool back while we work out the business around it.
Why now: iroh v1
We can ship now, and couldn't two years ago, because of iroh. Rayfish leans on it for the genuinely hard parts: encrypted QUIC connections between peers, NAT traversal, hole punching, and a relay fallback for when no direct path exists. iroh hitting v1 is what made this release possible; it gives us a transport that isn't going to shift under us mid-build. From here, the job is to take Rayfish from a young, sharp tool to one you'd run in production without thinking twice.
A spinoff of Field Technologies
Rayfish is a spinoff from Field Technologies, a high frequency trading firm where we spent years on distributed systems in house. Getting machines to find each other, agree on shared state, and talk fast and privately is something we'd built several times over internally, mostly to keep our own infrastructure off other people's control planes.
On top of that, Rayfish came together fast. It's a small idea we'd carried around for years and could finally ship without much fuss. It's worth a lot more out in the open than sitting on a laptop, so here it is.
What Rayfish gives you
Before the comparisons, a quick tour of what the tool actually does today.
Point to point, not hub and spoke
A lot of networks that feel decentralized still route their control, and sometimes their data, through a central hub. Everyone talks to the hub, the hub knows everyone, and when it's gone the whole thing goes dark.
Central hub
Member A
Member B
Member C
Member D
Rayfish is point to point. Every member connects directly to every other member, and membership is a signed record they each carry, not a question they ask a server.
Member A
Member B
Member C
Member D
The coordinator who created the network is needed only to admit new members. Once you are in, the network runs with no one at the center.
Many networks, one device
You can be in as many networks as you want at once, each fully isolated from the rest. It's still one process behind one virtual interface on your machine, and you're a full member of every network you've joined. The one with friends, the one for a side project, and the one at work all sit side by side without leaking into each other.
Closed by default, easy to join
Networks are closed by default. The coordinator who created a network lets people in one of two ways: a one time invite code they hand out, or live approval when someone asks to join. If you want a public network, ray create --open drops the gate and the room id alone admits.
Once you are in, every member is reachable by a stable IP and a friendly Magic DNS name, so you connect to db or web, not an address you have to remember. You choose your own name. Set a default once and every network you join picks it up:
ray up --hostname dario # your default name across networks
ray create --name prod # closed network
ray invite prod --hostname web # mint a one time, hostname bound code
ray join # the holder redeems it and is in
You can still override the name per network with --hostname on ray create or ray join when a machine should be called something different in a particular network.
A firewall on every device
Each device carries its own firewall, so a machine decides for itself what it accepts and from whom, independent of any central policy. On any network the coordinator can publish suggested firewall rules, and each host chooses whether to take them, or opts in to install them automatically. That gives you a sane shared default without a policy server that everyone has to obey.
Provisioning a fleet
For more than a couple of machines you should not be clicking through anything or typing commands box by box. You write the networks and their firewall rules in one declarative file and apply it. A spec is just a networks: map; under each network, every host says which peers and ports it accepts. Here is the kind of thing ray apply --example hands you:
networks:
prod:
web:
allows:
"*": "tcp:443" # web accepts 443 from anyone, rest denied
db:
allows:
web: "tcp:5432" # db accepts 5432, but only from web
game:
"*": # every node in 'game'...
allows:
"*": "tcp:6969" # ...opens 6969 to any peer
ray apply deploy.yaml --dry-run # preview the normalized spec
ray apply deploy.yaml --invite-missing # create networks, publish rules, mint invites
For a fleet you don't want to mint a code per box. Mint one reusable key and let every server join non interactively with it, taking the suggested firewall rules in the same command:
ray invite prod --reusable # one multi-use, expiring key
# then on each server:
ray join --hostname web01 --auto-accept-firewall
Each host joins and installs the coordinator's suggested rules with no per box configuration and no manual review queue. Revoking the key blocks new joins without disturbing the servers already in, and re-running ray apply is idempotent, so the spec file stays the source of truth as the fleet grows.
Just two people? ray connect
Not everything deserves a network. To link up with one other person you can skip room ids and invite codes entirely and connect by contact id, a handle you share like a phone number:
ray contact id # your shareable handle
ray connect # ask to connect; you wait, pending
ray connections approve # they approve, and you're linked
Approval spins up a private two peer network automatically. It's a real one, so Magic DNS, the firewall, and the mesh all work the same, shown as [direct] in ray status. It behaves like a friend request: you consent by asking, they consent by approving, and nobody reaches you without it. Rotate your contact id any time to stop new requests while every link you already made keeps working.
Sending files
Because every member already has an authenticated, encrypted path to every other member, moving a file between them needs no third party service and no link that lives on someone's server:
ray send ./build.tar.gz web01 # send to a peer by name
ray files accept 1 # the other side takes it
The bytes go straight over the mesh, end to end.
Rayfish vs Tailscale
Tailscale is the obvious comparison and a fair one. Both give you a flat, identity addressed network with stable IPs, Magic DNS, and NAT traversal, and in both the data plane is peer to peer. The difference is where control lives.
| Tailscale | Rayfish | |
|---|---|---|
| Data plane | WireGuard, peer to peer | iroh / QUIC datagrams, peer to peer |
| Throughput | Often faster, WireGuard runs in the kernel | A bit slower, iroh/QUIC runs in userspace |
| Control plane | A coordination server, Tailscale's or self hosted Headscale | None. Network state is a signed record served peer to peer |
| Identity | Tied to an account or SSO (Google, Okta) | A keypair on your disk. No account, no provider |
| Admission | Through the control plane or admin console | A coordinator admits peers with one time invite codes or live approval |
| Who must be online | The control plane is always in the loop | The coordinator is needed only to admit new members |
| Maturity | Mature, polished, broad feature set | Young, minimal, deliberately narrow |
Speed is the one place Tailscale usually wins. Its WireGuard data plane runs in the kernel and tends to beat Rayfish's userspace QUIC, most visibly when you're moving large volumes between two boxes on a fast link. You're trading a bit of raw throughput for having nobody in the middle. For most mesh traffic you won't notice it, but if you're saturating a 10 gig LAN, it's real.
Tailscale, in the end, coordinates your network through a server and pins your machines to an account. Rayfish has no such server, and the only thing your machines answer to is a key you hold.
Rayfish vs Yggdrasil
Yggdrasil deserves real respect. It's a thoughtful, genuinely decentralized network that's been routing real traffic for years. If what you want is an end to end encrypted IPv6 network with no central authority, Yggdrasil already delivers that, and it's solid.
Where it falls short for most people is the last mile. It hands you one global network and an IPv6 address and leaves the rest up to you. There's no concept of separate private networks that you create and admit people into, and none of the conveniences that come with that: invite codes, friendly DNS names, a per network firewall, an approval prompt when someone wants in. If you're a network engineer, none of that is a problem. If you just want a network for three friends and a game server, it's a lot of assembly.
| Yggdrasil | Rayfish | |
|---|---|---|
| Model | One global self routing network | Separate private networks you create |
| Joining | Peer with any node, share an address | Closed by default, one time invites or live approval |
| Names | IPv6 address only | Magic DNS names plus stable IPs |
| Policy | Up to you | Per network segmentation and a device firewall |
| Audience | Network minded engineers | Anyone who wants a private network with a few people |
Rayfish aims for the same trustless core, with the parts that make a network pleasant to actually use.
What you can build on it, and what you can't
Rayfish is a finished tool rather than a library. It's built on iroh, so the hard transport parts (encrypted QUIC, NAT traversal, hole punching, relay fallback) rest on solid ground, and on top of that you get a running private network with stable names, a per device firewall, and admission you control. What you don't get is an SDK to embed inside your own program.
The boundary is easy to state. Anything that needs machines to talk privately works, as long as those machines run Rayfish: a peer to peer game server your friends reach by name, an internal API that never touches the public internet, a backup target, a chat or calls app among a network's members, a database two teams share without seeing one another. Rayfish is the private wire, and your application rides on it just as it would on any other network, reaching peers at name.network.ray. The catch is that you can't bundle Rayfish inside your app as a dependency, and anyone without it installed can't reach the network. The peers are the network, and there's no gateway for an outsider to come in through.
A few real setups
Two departments that share a database. Put each department in its own network, say payments and analytics, and they can't see each other at all. There's no rule to misconfigure, because there's no connection between the two networks to filter in the first place. The database box joins both, and on each network its suggested firewall opens only the ports that side needs:
networks:
payments:
db:
allows:
"*": "tcp:8123,tcp:9000" # ClickHouse, payments side
analytics:
db:
allows:
"*": "tcp:8123,tcp:9000" # ClickHouse, analytics side
Each team reaches db.payments.ray or db.analytics.ray on those ports and nothing else; neither network knows the other exists. That's the whole access control story, two networks and one shared host, with no central ACL to audit.
A Minecraft server for friends. Create a closed network, invite a few people, and they connect their clients by name: no IP addresses, no port forwarding, no dynamic DNS:
ray create --name mc
ray invite mc # hand a code to each friend
# everyone points their client at: mc-host.mc.ray:25565
A closed group where everyone opens a port. For a self hosted game where every member runs a listener, say everyone needs inbound TCP 6969, the admin doesn't chase people one machine at a time. They suggest the rule once, and it rides the signed network record out to everyone:
ray firewall suggest mc --subject '*' --allow '*:tcp:6969'
Each member then reviews it and decides for themselves:
ray firewall pending mc # see what the admin proposed
ray firewall accept mc # take it (or `deny` to skip)
Nobody has rules forced on them, since a suggestion is advisory, but one command from the admin gives the whole group a sane default, and anyone who'd rather not can simply decline.
FAQ
How does Rayfish avoid IP collisions? Addresses are derived from each peer's cryptographic identity instead of being handed out by a server, so they're stable and almost never clash. In the rare case two identities map to the same address, the coordinator seats the second one at the next free slot, the IP equivalent of a -1 hostname suffix, and every peer converges on the same assignment deterministically, with no central allocator to ask.
How does Rayfish stay secure without ACLs? It replaces the central rule list with two simpler things. First, segmentation: two peers can talk only if they share a network, and that falls out of the transport. There's no connection to a peer you share no network with, so there's nothing to filter. Need prod and dev isolated? Make them two networks. Second, a per device firewall on every machine, with optional coordinator suggested rules per network. The split is the access control, and unlike an ACL you can't get it subtly wrong. A host that lives in several networks carries its own firewall in each one, so "who can reach me here" is answered locally and per network, not by a policy server everyone has to obey.
Do I have to open ports or run a server? No. iroh handles NAT traversal and hole punching, with encrypted relay fallback when a direct path isn't possible. There's no control server to host; the only "server" is whoever created the network, and they can be offline once everyone's admitted. (If you do control a router and want a guaranteed direct path for one box, you can forward Rayfish's fixed UDP port, but it's optional.)
What if the people who made Rayfish disappear? The network keeps working. Its state is a signed record any member can serve, with no account and no operator in the loop, so there's nothing for us to switch off.
What comes next
This is a first release. On desktop and servers it's a command line tool today, and the next stretch of work is mostly about reach: getting Rayfish onto the devices people actually carry. That means Android and iOS clients and a proper desktop app, so joining a network is a couple of clicks instead of a terminal session. Running alongside all of that is the slower work of hardening what's already here until you'd run it in production without a second thought.
Rayfish might not be for you
If you already have a private network that works, whether that's Tailscale, a corporate VPN, or a WireGuard mesh you set up by hand, Rayfish gives you little reason to switch. It's younger, it has fewer features, and most of what it does those tools already do, often more smoothly. Being one more mesh VPN isn't a reason to tear down a setup that's fine.
What Rayfish removes is one specific thing: the central party. There's no account, no company that can suspend your network, no control server that has to stay online, and no policy database run by someone else. So it comes down to one question. Do you want a private network that answers to nobody but the people in it? If so, that's exactly what we built. If you don't especially care and you just want your devices to reach each other, use Tailscale and don't feel bad about it.
Try it
One line gets you started:
curl -fsSL https://rayfish.xyz/install.sh | sh
From there, the docs walk you through creating your first network and inviting people in. We'd love to hear what you build with it.



