# How to Generate a Persistent WireGuard Key for Tailcat

> Learn how to generate a persistent WireGuard key for Tailcat. Ensure stable network identity by letting Tailcat create the key automatically or generating one manually with wg genkey.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: how-to-guide
- Published: 2026-08-30

---

**To generate a persistent WireGuard key for Tailcat, either let the binary create it automatically at `$HOME/.config/tailcat/wireguard.key` on first launch, or manually generate one using `wg genkey` and place it in that location; Tailcat reuses this key across restarts to maintain a stable network identity.**

Tailcat is a networking utility from the tailscale/tailcat repository that establishes secure connections using WireGuard. According to the source code, the application manages cryptographic identity persistence by automatically generating and storing private keys in the user's configuration directory, eliminating the need for manual key management in typical deployments.

## Automatic Key Generation on First Run

When Tailcat initializes, the logic in [`config.go`](https://github.com/tailscale/tailcat/blob/main/config.go) checks for an existing private key file. If the file does not exist, the program invokes the generation routine in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), which calls `tailscale.com/wgkey.NewPrivateKey()` to create a cryptographically secure private key. The application then writes this key to the configuration directory with restrictive permissions, ensuring it persists for subsequent executions.

To trigger automatic generation, simply execute the Tailcat binary:

```bash
./tailcat

```

On first run, Tailcat creates the directory `$HOME/.config/tailcat/` (respecting `$XDG_CONFIG_HOME` on Linux) and writes the `wireguard.key` file containing the base64-encoded private key.

## Manual Key Generation Methods

For infrastructure-as-code deployments or when you require key control before the first execution, you can generate the key manually using two approaches supported by the Tailcat architecture.

### Using the WireGuard Command-Line Tools

The standard WireGuard utilities provide the simplest method for key generation. Create the configuration directory and generate the key in one pipeline:

```bash

# Create the config directory and generate the key

mkdir -p "$HOME/.config/tailcat"
wg genkey | tee "$HOME/.config/tailcat/wireguard.key"

# Set restrictive permissions (owner read/write only)

chmod 600 "$HOME/.config/tailcat/wireguard.key"

```

This approach uses the `wg genkey` utility to produce a Curve25519 private key and immediately persists it to the location Tailcat expects.

### Programmatic Generation with Go

For applications integrating Tailcat's logic directly, replicate the exact method used in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) by importing the Tailscale wireless key library:

```go
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"tailscale.com/wgkey"
)

func main() {
	// Generate a new private WireGuard key using Tailcat's implementation
	priv, err := wgkey.NewPrivateKey()
	if err != nil {
		panic(err)
	}

	// Encode in the format Tailcat expects (base64 string plus newline)
	data := []byte(priv.String() + "\n")

	// Determine configuration directory
	cfgDir, _ := os.UserConfigDir()
	keyPath := filepath.Join(cfgDir, "tailcat", "wireguard.key")
	
	// Ensure directory exists with appropriate permissions
	if err := os.MkdirAll(filepath.Dir(keyPath), 0o700); err != nil {
		panic(err)
	}
	
	// Write key with restricted permissions (0o600 = owner read/write only)
	if err := ioutil.WriteFile(keyPath, data, 0o600); err != nil {
		panic(err)
	}

	fmt.Printf("Persistent WireGuard key written to %s\n", keyPath)
}

```

This Go implementation mirrors the behavior in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), utilizing `wgkey.NewPrivateKey()` to ensure cryptographic compatibility with Tailcat's WireGuard stack.

## Configuration File Locations

Tailcat follows platform-specific conventions for configuration storage:

- **Linux**: `$HOME/.config/tailcat/wireguard.key` (or `$XDG_CONFIG_HOME/tailcat/wireguard.key` if set)
- **macOS**: `$HOME/Library/Application Support/tailcat/wireguard.key`
- **Windows**: `%AppData%\tailcat\wireguard.key`

The key file must contain a single line with the base64-encoded private key. When [`config.go`](https://github.com/tailscale/tailcat/blob/main/config.go) loads on startup, it reads this file and derives the corresponding public key automatically for WireGuard handshake negotiations.

## Security Best Practices

The private key generated for Tailcat operations requires strict confidentiality:

- **File permissions**: Always set `0o600` (read/write for owner only) on the key file. Both automatic generation and manual scripts must respect this permission mask.
- **Backup considerations**: If you back up the key, encrypt the backup. Anyone with access to this private key can impersonate your node on the WireGuard network.
- **Rotation**: To rotate keys, delete the `wireguard.key` file and restart Tailcat. The application will generate a new key in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) and establish a fresh network identity.

## Summary

- **Automatic approach**: Run Tailcat once to trigger the generation logic in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), which creates the key at the platform-appropriate config path.
- **Manual approach**: Use `wg genkey` or the Go `wgkey.NewPrivateKey()` API to create the key file before launching the application.
- **Key location**: Store the file at `$HOME/.config/tailcat/wireguard.key` (Linux) or the equivalent platform-specific path handled by [`config.go`](https://github.com/tailscale/tailcat/blob/main/config.go).
- **Permissions**: Always restrict the key file to `0o600` to prevent unauthorized access.
- **Persistence**: Once created, Tailcat reuses this key across all subsequent executions, maintaining a stable WireGuard public key for network authentication.

## Frequently Asked Questions

### Where does Tailcat store the WireGuard private key?

Tailcat stores the private key in your user configuration directory under a subdirectory named `tailcat`. On Linux systems, this defaults to `$HOME/.config/tailcat/wireguard.key`. The exact path determination logic resides in [`config.go`](https://github.com/tailscale/tailcat/blob/main/config.go), which respects platform standards such as `$XDG_CONFIG_HOME` on Linux and `Library/Application Support` on macOS.

### Can I use an existing WireGuard key with Tailcat?

Yes. If you have an existing private key generated by `wg genkey` or another WireGuard-compatible tool, you can place it at the expected configuration path. Ensure the file contains only the base64-encoded private key followed by a newline, and set permissions to `0o600`. Tailcat's [`config.go`](https://github.com/tailscale/tailcat/blob/main/config.go) loader will accept any valid Curve25519 private key that conforms to the WireGuard specification.

### What happens if I delete the wireguard.key file?

If you delete the key file and restart Tailcat, the automatic generation routine in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) triggers immediately upon startup. The application generates a new private key using `wgkey.NewPrivateKey()`, writes it to the configuration directory, and uses the new key for all subsequent connections. This effectively rotates your node's WireGuard identity, though you will need to re-authenticate with any coordination servers or peers that whitelist specific public keys.

### Is the private key generated by Tailcat compatible with standard WireGuard tools?

Yes. Tailcat uses the standard WireGuard key format implemented in the `tailscale.com/wgkey` package. The private keys are standard Curve25519 keys encoded in base64, fully compatible with `wg`, `wg-quick`, and other WireGuard implementations. You can extract the key from Tailcat's config directory and use it with other tools, or import keys generated by `wg genkey` into Tailcat.