Netcat, but the network already trusts you.
That’s the shortest way I can describe Tailcat, a small tool that takes the classic nc pattern — open a pipe, listen on a port, shove bytes through it — and runs it over Tailscale’s data plane instead of whatever the local network happens to be doing that day. Same muscle memory, different transport.
I build bots for a living, which mostly means I spend my time moving data between machines that were never designed to talk to each other. A scraper on a cheap VPS. An inference box under my desk with a GPU in it. A staging container that exists for forty minutes. Every one of those hops has historically meant a decision about ports, firewalls, tunnels, or some SSH incantation I have to look up again. So a tool like this gets my attention fast.
What the idea actually buys you
The interesting part isn’t the netcat interface. It’s where the connection lives.
Plain netcat assumes reachability. You need an IP you can hit, a port that’s open, and a path between the two that nobody’s dropping. On a home network that’s fine. Across three cloud providers and a laptop on hotel wifi, it’s a project. The usual workaround is a reverse tunnel, a relay you rent, or a firewall rule you promise yourself you’ll clean up later.
Running the same primitive over a tailnet changes the assumption. Nodes on a tailnet address each other directly by their tailnet identity, with the mesh handling NAT traversal and encryption underneath. If both ends are on your tailnet, they can already reach each other. The pipe stops being a networking problem and becomes a one-liner again.
Worth being precise here: I haven’t audited Tailcat’s source, so I’m describing the shape of the approach rather than making claims about its internals. Tailscale does publish a Go library that lets a program join a tailnet as a node in-process, and that’s the obvious way to build something like this — no system daemon required on either side, just a binary that shows up as its own device. If that’s the design, the implications for bot work are the fun part.
Why bot builders should care
A few patterns I hit constantly that this maps onto cleanly:
- Log streaming from ephemeral workers. A short-lived scraper container that pipes its stdout to your dev machine while it runs, without standing up a log aggregator you’ll use twice.
- Shuttling model artifacts. Moving a fine-tuned checkpoint from the GPU box to the inference host without staging it in object storage first.
- Poking at a service that isn’t exposed. The classic netcat debugging move — connect, type a raw request, read the response — but against something that only lives inside your private network.
- Agent-to-agent plumbing. If you’re building multi-agent systems, sometimes you just want a byte channel between two processes on two machines. Not a message broker. Not a REST API. A pipe.
That last one is the direction I’d actually experiment in. A lot of agent frameworks reach for HTTP and a schema before they’ve earned the complexity. If two of your bots are on the same tailnet, a raw stream is a legitimate transport for prototyping, and you can add structure later once you know what messages you’re actually sending.
The part I’d think about before shipping it
A listener is a listener. Moving it onto a tailnet narrows who can reach it, but it does not authenticate anything on its own. Anything on your tailnet that can route to that node can talk to that pipe, and netcat-style tools traditionally have no concept of a user, a token, or a permission.
So the access control has to come from somewhere else, and on Tailscale that somewhere is your ACL policy. If your tailnet is currently wide open because it’s just you and a laptop, adding raw byte pipes to it is the moment to go write some rules. Scope which nodes can reach which. Tag your bot hosts. Treat the tailnet as a network you administer, not a magic trust bubble.
I’d also keep this firmly in the operations and debugging column rather than making it load-bearing in production. Netcat’s charm has always been that it’s the fastest way to answer a question, not the most durable way to build a system. Same reasoning applies here. Use it to find out whether two machines can talk and what they say to each other. Then, if the pattern sticks, build the real thing with proper framing, retries, and identity checks.
Small tools that remove a step are underrated. This one removes the step where I stop building a bot and start negotiating with a firewall, and that’s a trade I’ll take most days.
🕒 Published: