Ephemeral vs. Persistent Keys in Tailcat: What's the Difference?

Ephemeral keys are temporary session-only identities generated on startup and discarded when the process exits, while persistent keys are saved as JSON files to disk and reused across multiple Tailcat sessions.

When running the tailscale/tailcat application, you choose between two identity key types that determine how your node authenticates to the network. Understanding the differences between ephemeral and persistent keys ensures you select the right option for short-lived debugging sessions versus long-running production servers. This guide breaks down the technical implementation in tailcat.go, storage mechanisms, and CLI usage patterns.

Core Differences Between Ephemeral and Persistent Keys

Tailcat distinguishes between these key types based on storage persistence and lifecycle management.

Ephemeral Keys (Session-Only)

Ephemeral keys are cryptographically generated on-the-fly when the Start function in tailcat.go detects no existing key is provided. According to the source code comments, "If zero, Start generates a new ephemeral key." These credentials exist only in memory for the duration of the process and are never written to disk.

  • Lifetime: Exists only for the current process lifecycle; discarded on exit.
  • Use case: Ideal for temporary debugging, one-off connections, or disposable containers where node identity does not need to persist.
  • CLI option: Use --key new to force generation of a fresh ephemeral key.

Persistent Keys (Saved Identities)

Persistent keys are stored as *.private.json files in the Tailcat configuration directory and maintain a stable node identity across restarts. The default saved key names are default for server mode and client-default for client modes, as defined in the command-line handling logic in cmd/tailcat/tailcat.go.

  • Lifetime: Stored indefinitely on disk until explicitly deleted.
  • Use case: Required for long-running servers or when other peers need a consistent, recognizable node identity.
  • Storage location: $CONFIG/tailcat/keys/ (typically $HOME/.config/tailcat/keys/ on Unix systems).
  • CLI options: Specify a key name or file path, or omit the flag to use the default.

How Tailcat Handles Key Generation and Storage

The underlying implementation in tailscale/tailcat treats these key types differently in terms of file operations and JSON serialization.

Automatic Generation Logic

In tailcat.go, the Start method implements the following logic: if the key value is zero, it generates a new ephemeral key automatically at first use. This ensures Tailcat can start immediately without pre-configuration, falling back to temporary credentials when no persistent key exists.

File Format and Directory Structure

Persistent keys follow the JSON structure defined by the PrivateKey type in tailcat.go. When saved, Tailcat writes a .private.json file containing the serialized key material. You can reference named keys stored in $CONFIG/tailcat/keys/ by simple identifiers like foo rather than full file paths.

Using the --key Flag to Control Key Selection

The --key flag implemented in cmd/tailcat/tailcat.go accepts several argument patterns to determine identity:

  • --key new – Forces generation of a fresh ephemeral key that is discarded when the process exits.
  • --key <name> – Loads a persistent key from $CONFIG/tailcat/keys/<name>.private.json.
  • --key <path> – Loads a persistent key directly from a specific *.private.json file path.
  • Omitting --key – Attempts to load the default saved key (default in server mode, client-default in client modes); if no default exists, Tailcat automatically falls back to generating an ephemeral key.

Practical Examples

Generate a temporary ephemeral key for a quick session:

tailcat --key new --ssh :2222

Run with the default persistent key (or ephemeral if no default exists):

tailcat --ssh :2222

Load a named persistent key from the config directory:

tailcat --key work-node --ssh :2222

Load a key from a specific file path:

tailcat --key /etc/secrets/mykey.private.json --ssh :2222

Summary

  • Ephemeral keys are generated automatically when no key is supplied, exist only in RAM, and vanish when the process terminates.
  • Persistent keys are saved as JSON files in $CONFIG/tailcat/keys/, providing stable identities across restarts.
  • The --key flag accepts new for ephemeral generation, a name for persistent keys in the config directory, or a direct file path.
  • Server mode defaults to the default key, while client modes use client-default when no flag is specified.

Frequently Asked Questions

What happens if I start Tailcat without specifying a --key flag?

If you omit the --key flag, Tailcat attempts to load the default saved key—default for server mode or client-default for client modes—from the $CONFIG/tailcat/keys/ directory. According to the cmd/tailcat/tailcat.go implementation, if no default key exists, the system automatically generates a new ephemeral key instead.

Where are persistent keys stored on my system?

Persistent keys are written to the $CONFIG/tailcat/keys/ directory, which typically resolves to $HOME/.config/tailcat/keys/ on Unix systems. Each key is saved as a separate .private.json file containing the serialized PrivateKey structure defined in tailcat.go.

Can I convert an ephemeral key to a persistent key?

Tailcat does not automatically convert ephemeral keys to persistent ones during runtime. To save an ephemeral identity, you would need to manually export the current key material and write it to a .private.json file in the correct directory format, then reference it by name in subsequent invocations.

Is there a security difference between ephemeral and persistent keys?

Yes. Ephemeral keys reduce long-term exposure because they never touch the filesystem and cannot be recovered after the process exits, making them ideal for untrusted environments. Persistent keys require proper file permissions (typically 0600) on the .private.json files since they represent reusable credentials that could be compromised if the disk is accessed by unauthorized users.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →