DEV Community

mariatanbobo
mariatanbobo

Posted on

I Deleted My API Keys and Nothing Broke

I looked at my servers recently and felt a quiet unease. Every machine that talked to an LLM had its own set of API keys — DeepSeek, Gemini, OpenRouter, scattered across VPS instances and web apps. Each new project added more copies. If I wanted to rotate a key, I had to remember every place it lived.

Then I found Aperture.

What Aperture Actually Is

Aperture is Tailscale's LLM gateway, currently in beta. The name suggests a platform, but it's a simpler thing: a proxy.

Your app sends an OpenAI-format request to the gateway with a dummy API key (-). Aperture receives it, does three things:

  1. Auth swap — replaces the dummy key with the real one from its vault
  2. Route — reads the model name, forwards to the right provider
  3. Log — records tokens and cost for a unified dashboard

Aperture proxy flow

Because it runs on your Tailscale tailnet, auth is identity-based. The fact that your server is on the network IS the authorization. No API keys fly around in environment variables.

The Tailscale Question

Whenever I mention depending on Tailscale for something critical, I get the same look: "You're putting a lot of trust in one company."

Fair instinct. But Tailscale's data plane is WireGuard — the same protocol in the Linux kernel. If Tailscale the company disappeared tomorrow, connections would keep working until your next key rotation. You can even run Headscale, an open-source control server, for full independence.

The control plane — key distribution, ACLs, MagicDNS — is where Tailscale adds value. And that control plane has a strong track record: millions of devices, production use at companies that care about uptime.

More practically: if you're already using Tailscale (and I was — servers, a Jetson, home devices were all on it), Aperture adds zero new infrastructure. It runs on top of what you already have.

The Migration: Six Lines

I had a web app using three LLM providers — DeepSeek for text enrichment, Gemini for image analysis, OpenRouter for vision. Each had its own client factory:

# Before: three providers, three base URLs, three API keys
def _get_gemini_client():
    return OpenAI(
        base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
        api_key=os.environ.get("GEMINI_API_KEY")
    )

def _get_openrouter_vision_client():
    return OpenAI(
        base_url="https://openrouter.ai/api/v1",
        api_key=os.environ.get("OPENROUTER_API_KEY")
    )

def _get_deepseek_client():
    return OpenAI(
        base_url="https://api.deepseek.com/v1",
        api_key=os.environ.get("DEEPSEEK_API_KEY")
    )
Enter fullscreen mode Exit fullscreen mode

After:

# After: all through one gateway, one address, no keys
def _get_gemini_client():
    return OpenAI(base_url="http://aperture/v1", api_key="-")

def _get_openrouter_vision_client():
    return OpenAI(base_url="http://aperture/v1", api_key="-")

def _get_deepseek_client():
    return OpenAI(base_url="http://aperture/v1", api_key="-")
Enter fullscreen mode Exit fullscreen mode

Six lines changed. Three base_url values and three api_key values. That's the whole migration.

Then came the part that felt almost reckless: I deleted the keys from the server. The .env file went from six entries to two:

Before:  GEMINI_API_KEY, DEEPSEEK_API_KEY, OPENROUTER_API_KEY,
         XAI_API_KEY, JWT_SECRET, TAVILY_API_KEY
After:   JWT_SECRET, TAVILY_API_KEY
Enter fullscreen mode Exit fullscreen mode

The two I kept aren't LLM keys — JWT is a local signing secret, Tavily is a web search API with its own format. Aperture only proxies OpenAI-compatible chat completions.

I restarted the service and hit the API. It worked. First try.

The One Exception

One machine stays direct: the server running my AI agent.

This is a circular dependency problem. If Aperture goes down, I need the agent online to debug it. If the agent routes through Aperture and Aperture breaks, I'm dead in the water — no diagnosis, no fix, SSH-only recovery.

The rule: control plane stays direct, everything else routes through the gateway. One DeepSeek key on one machine is cheap insurance.

Automating Tailscale on New Servers

The final piece: making this zero-friction for new machines. If routing through Aperture requires Tailscale, then adding Tailscale to a new server needs to be painless.

Tailscale has a feature for this: auth keys. Unlike the interactive browser login, a pre-approved auth key lets you join the tailnet with:

tailscale up --authkey=tskey-auth-...
Enter fullscreen mode Exit fullscreen mode

No browser, no human in the loop. You can create one-time keys or reusable ones — reusable keys are ideal for automation, letting you provision servers without generating a new key each time. You can also pre-assign tags like tag:server to automatically apply ACL rules.

For my setup, I store a reusable auth key in my agent's credential store. Adding a server to the tailnet is one command. The server comes online, MagicDNS resolves the gateway automatically, and it can immediately route LLM traffic — no keys deployed, no manual config.

What I Learned

  1. Aperture is smaller than you think. It's not a platform. It's a proxy on your existing Tailscale network. The value-to-complexity ratio is unusually high.

  2. "All providers through one URL" is liberating. Three client factories collapsed to three identical lines. Add a provider to the gateway once, every app gets it.

  3. The proxy model inverts trust. Instead of trusting every server with every key, you trust one gateway. The gateway is the only place that holds real credentials.

  4. Don't route your control plane through it. If your debugging tool depends on the thing it might need to debug, you've created a problem that requires physical access to solve.

  5. Auth keys make Tailscale zero-touch. Pre-approve a reusable key, and adding a server is one command. No browser, no login flow, no human bottleneck.

  6. Not everything belongs in the gateway. Non-LLM services (search APIs, crypto secrets) still need their own keys. Aperture is strictly a chat-completion proxy.

  7. The real win is the sprawl you prevent. The keys I deleted were the ones I knew about. The value is the keys I'll never deploy because the default is now "route through the gateway."


Built with Hermes Agent. Follow me on X at @MariaTanBoBo.

Top comments (0)