Michael Alyn Miller

Super Guppy

There are a number of ways to share private crates between Rust projects. The easiest way is to use Git-based dependencies, but granting GitHub Actions access to private Git repositories is surprisingly hard. What you really want is your own private Cargo repository.

Amos Wenger (aka fasterthanlime) has a wonderful article that covers all sorts of Rust productivity tips, including creating your own private crate registry. Reading that article is well worth your time, especially since the primary job of this article is to expand on the solution in that article with a slightly more turnkey option.


Amos’s approach works great, but there were a couple of things I wanted to change:

Both of those changes have one thing in common: they involve securing and authenticating the network connection between cargo and the private registry.

Making auth someone else’s problem

Tailscale lets you, in their words, “easily manage access to private resources.” That sounds exactly like what we are trying to do. I have been using Tailscale for years, and it does exactly what it says: makes it easy to securely connect devices, regardless of their physical location.

Tailscale offers an Access Control List (ACL) feature that, as an individual managing their own devices, I had never needed to use. I also wasn’t sure that this functionality was at the right layer in the stack; wasn’t it safer to manage ACLs on each device?

Deploying a private Cargo registry opened up a whole new set of challenges that changed my perspective on this feature. Specifically, Tailscale ACLs give me a way to allow semi-untrusted devices into the private network. This meant that I could put the GitHub Actions runners on the same virtual network as my internal development machines, but limit their access solely to the private Cargo registry.

This also solved the larger authorization problem with the crate registry, in that I could simply let it run un-authenticated. Tailscale would ensure that only authorized users (and GitHub) could get at the crate registry.

The one wrinkle in all of this

We need to run a bunch of processes on the same machine in order to pull this off:

This is the point where you think, “That sounds like a lot of accidental complexity, you should leave this alone and get back to building your product.” But I sort of squinted and decided that our product also had the same problem of needing to run multiple processes on Fly.io, and so I had all the justification I neeeded to create Ground Control.

Ground Control has its own post, but in a nutshell, Ground Control makes it easy to run multiple processes inside of a single Docker container (or Fly.io micro-VM).

Super Guppy: Your Cargo transport in the clouds

Super Guppy is the result of this effort: a Docker image that makes it easy to run your own private Cargo registry. You do need Tailscale, and a place to host the registry (such as Fly.io), but those are both free to try, and are very affordable for small teams.

Next Steps

aka “Let’s really make auth someone else’s problem!”

You still have to create accounts in Ktra to use Super Guppy. That’s fine, but it turns out that Tailscale happens to know which user is associated with each active connection. This means you can do things like seamlessly authenticate to Grafana using Tailscale.

I am certain that Ktra could be modified to look for Tailscale authorization (perhaps in a generic auth header?), similar to the OpenID auth feature that was recently added to Ktra. This would eliminate the need to provision or manage user accounts in Ktra; everything would come from Tailscale.


A big shout-out to Amos, who deserves all of the credit for explaining Ktra in such a clear manner. Reading his article, and working through his examples, was what made it possible for me to figure all of this out and create Super Guppy.