p.enthalabs

Rayfish · Your own private network. No servers, no setup.

Introducing Rayfish

Today we are releasing Rayfish, a peer to peer mesh VPN that builds private networks with no server in the middle and no company you have to trust. You hold a key. Your devices find each other by that key. The network belongs to its members and to no one else.

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 routes its control through a company. That company decides who is in your network, holds the policy, and stays in the loop while your machines talk. If it goes down, changes its pricing, or simply decides it no longer wants you as a customer, your private network stops being fully yours. Rayfish removes that party. There is no account and no operator. Network state is a signed record that any member can serve, so the network keeps working even if the people who wrote the software disappear.

The second is control across many networks at once. Real life is not one flat network. You have a network with friends, a network for a side project, a network at work, and a few machines that need to live in several of them. Rayfish treats this as the normal case. One process on your device, one virtual interface, and you are a full member of every network you joined, each one isolated from the others.

An old idea, finally shippable

We did not think of this last quarter. The core idea has been sitting with us for more than four years. It never became a real product for a plain reason: it was never the business priority, and a decentralized network is genuinely hard to turn into a business. The thing that makes it good for users, no central operator, is the same thing that takes away the obvious place to charge rent. There is no server you must pay us to keep running, because there is no server.

We are honest that this may take shape over time. There are enterprise problems here, things like fleet management and audit, that a company would happily pay to solve, and some of that may come down the pipeline later. It is not here yet, and we did not want to hold the open tool hostage to a business model that is still forming.

Why now: iroh v1

The reason we can ship today and not two years ago is iroh. Rayfish leans on iroh for the hard parts: encrypted QUIC connections between peers, NAT traversal, hole punching, and relay fallback when a direct path is not possible. iroh reaching v1 is what unlocks this release. It gives us a transport we can build on without the ground moving under us. With that settled, our job from here is clear: take Rayfish from a sharp young tool to something you would trust in production. That is the work ahead.

A spinoff of Field Technologies

Rayfish is a spinoff from Field Technologies, a high frequency trading firm where we spent years solving distributed systems problems in house. Getting machines to find each other, agree on state, and talk to each other quickly and privately is the kind of thing we have built more than once, internally, to keep our own infrastructure off other people's control planes.

Rayfish itself came together quickly on top of that experience. It is a small idea we had carried for years, finally easy to ship. We think it is worth far more 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

Plenty of networks that feel decentralized still funnel their control, and sometimes their data, through a central hub. Every member talks to the hub, the hub knows everyone, and if the hub is gone the picture goes dark.

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.

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 belong to as many networks as you like at the same time, and each one is fully isolated from the others. One process runs on your machine, one virtual interface, and you are a full member of every network you joined. A network with friends, a network for a side project, and a network at work can all live 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 <code> # 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 <key> --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 <their-contact-id> # ask to connect; you wait, pending ray connections approve <id> # 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 |

We'll also be straight about speed: Tailscale's WireGuard data plane is typically faster than Rayfish's userspace QUIC, most visibly when you're pushing large volumes between two boxes on a fast link. Rayfish trades a little raw throughput for having no one in the middle. For most mesh traffic the difference is not what you'll notice, but if you're saturating a 10 gig LAN it's real.

The short version: Tailscale coordinates your network through a server and ties your machines to an account. Rayfish has no such server and ties your machines to nothing but a key you hold.

Rayfish vs Yggdrasil

Yggdrasil deserves real respect. It is a thoughtful, genuinely decentralized network that has been routing traffic for years. If you want an end to end encrypted IPv6 network with no central authority, it works, and it works well.

Where it falls short for most people is the last mile. Yggdrasil hands you one global network and an IPv6 address, then leaves the rest to you. There is no notion of separate private networks you create and let people into, no invite codes, no friendly DNS names, no per network firewall, no live approval when someone wants in. For a network minded engineer that is fine. For a normal person who just wants a network with three friends and a game server, it is 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 tool, not a library. It's built on iroh, so we lean on solid foundations for the hard transport parts: encrypted QUIC, NAT traversal, hole punching, relay fallback. On top of that it gives you a running private network with stable names, a per device firewall, and admission you control. What it is _not_ is an SDK you embed in your own program.

So the line is simple. You can build anything that wants machines to talk privately, as long as those machines run Rayfish: a peer to peer game server your friends reach by name, an internal API that's never exposed to the internet, a backup target, a calls or chat app between members of a network, a database two teams share without seeing each other. Rayfish is the private wire; your application rides on top of it exactly as it would on any other network, reaching peers at `name.network.ray`. What you can't do is ship Rayfish _inside_ your app as a dependency, or expect someone without Rayfish installed to reach the network. The peers are the network; there's no gateway for outsiders to come 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, not a finished one. The desktop and server story runs through a command line tool today, and the next stretch of work is about reach. We want Rayfish on 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 rather than a terminal session. Alongside that, the ongoing work is hardening what is already here until it is something you would run in production without a second thought.

Rayfish might not be for you

Worth being blunt, the same way our docs are. If you already have a private network that works, Tailscale, a corporate VPN, a WireGuard mesh you configured, Rayfish gives you little reason to switch. It is younger, it has fewer features, and the things it does are things those tools already do, often more smoothly. Being one more mesh VPN is not a reason to move a setup that is fine.

Rayfish earns its place by removing one specific thing: the central party. No account, no company that can suspend your network, no control server that has to stay up, no policy database someone else runs. So the honest test is a single question. Do you want a private network that belongs to no one but its members? If yes, Rayfish was built for exactly that. If you do not really care, and you just want your devices to reach each other, use Tailscale without guilt.

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 would love to hear what you build with it.