How to Configure SSH Authorized Keys for Tailcat: Complete Guide

Tailcat's SSH service authenticates clients using public keys specified via the --ssh-authorized-keys flag, which accepts comma-separated sources including literal keys, file paths, and GitHub users.

This guide explains how to configure SSH authorized keys for the tailscale/tailcat repository. Whether you're deploying a single instance or managing multiple users, understanding the authorized key configuration ensures secure, key-based SSH access to your Tailcat server.

What the --ssh-authorized-keys Flag Does

The --ssh-authorized-keys flag is defined in cmd/tailcat/tailcat.go at lines 104-106. It enables public-key authentication for Tailcat's built-in SSH service by accepting a comma-separated list of authorized-key sources.

The flag is required when launching the ssh service. The source code enforces this constraint at lines 66-73 of the same file—Tailcat will refuse to start the SSH service without it. Additionally, lines 69-71 prevent using --ssh-authorized-keys with the no-auth-ssh service, as these are mutually exclusive authentication modes.

Supported Authorized Key Sources

Tailcat's parsing logic in cmd/tailcat/ssh_authorized_keys.go (lines 26-33) recognizes three source types:

Source Type Format Description
Literal key ssh-ed25519 AAAAC3Nza... user@host Inline OpenSSH public key string
File path /path/to/authorized_keys Path to a file containing one or more keys
GitHub user username@github Resolves to https://github.com/username.keys

The loadSSHAuthorizedKeys function (lines 33-71) processes each comma-separated entry and dispatches to the appropriate loader based on the detected source type.

How Tailcat Loads and Validates Keys

Step 1: Source Detection and Fetching

For each entry in the comma-separated list, loadSSHAuthorizedKeysFrom performs the following:

  1. GitHub users — Detected via the @github suffix, validated against githubUsernameRx, then fetched via HTTPS using fetchGitHubSSHKeys (lines 49-53)
  2. File paths — Read via readSSHAuthorizedKeysFile (lines 85-99) with a hard 1 MiB limit (maxSSHAuthorizedKeysSize)
  3. Literal keys — Passed directly to validation

Step 2: Validation

After all sources are resolved to key strings, the entire collection undergoes final validation through tailcat.ValidateSSHAuthorizedKeys (lines 63-70). This ensures only well-formed OpenSSH public keys are accepted by the SSH subsystem.

Step 3: Runtime Binding

The validated keys are passed to the platform-specific SSH implementation (tailcat_ssh_*.go files), where they populate an ssh.ServerConfig that authorizes clients presenting matching private keys.

Practical Configuration Examples

Use a Local Authorized Keys File

tailcat serve --ssh-authorized-keys=$HOME/.ssh/authorized_keys ssh

This command starts Tailcat with SSH access limited to keys already trusted by your local system.

Mix Multiple Source Types

Create a file with a literal key:

cat > mykey.pub <<EOF
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO... user@example.com
EOF

Launch Tailcat with combined sources:

tailcat serve \
  --ssh-authorized-keys=alice@github,$HOME/.ssh/authorized_keys,mykey.pub \
  ssh

This configuration accepts connections from:

  • Any key in the GitHub user alice's public key list
  • All keys in your local ~/.ssh/authorized_keys file
  • The specific key stored in mykey.pub

GitHub-Based Team Access

tailcat serve \
  --ssh-authorized-keys=alice@github,bob@github,carol@github \
  ssh

Ideal for teams already using GitHub—no manual key distribution required.

Key Implementation Files

Understanding these source files helps troubleshoot configuration issues:

  • cmd/tailcat/tailcat.go — Flag definition and runtime constraint enforcement (lines 66-73, 104-106)
  • cmd/tailcat/ssh_authorized_keys.go — Core parsing, fetching, reading, and validation logic
  • tailcat/ssh_*.go — Platform-specific SSH server implementation consuming the loaded keys

Common Configuration Pitfalls

Issue Cause Solution
"ssh service requires --ssh-authorized-keys" Missing required flag Add authorized key sources to your command
GitHub keys not loading Invalid username format Ensure format is exactly username@github
File too large error Exceeds 1 MiB limit Split keys across multiple files or use GitHub references
Flag conflicts with no-auth-ssh Mutually exclusive modes Choose either --ssh-authorized-keys or no-auth-ssh, not both

Summary

  • The --ssh-authorized-keys flag is mandatory for Tailcat's SSH service and accepts comma-separated sources
  • Three source types are supported: literal keys, file paths, and GitHub users (user@github)
  • GitHub users are resolved to https://github.com/user.keys at runtime
  • File sources are limited to 1 MiB (maxSSHAuthorizedKeysSize)
  • All keys undergo double validation: once per source, then collectively via tailcat.ValidateSSHAuthorizedKeys
  • The loaded keys populate the ssh.ServerConfig used by the platform-specific SSH subsystem

Frequently Asked Questions

Can I use multiple authorized key files with Tailcat?

Yes. Pass multiple paths as comma-separated values to --ssh-authorized-keys. For example: --ssh-authorized-keys=/etc/ssh/admin_keys,/etc/ssh/dev_keys. Each file is read separately and subject to the 1 MiB size limit.

How does Tailcat handle GitHub users who have multiple SSH keys?

Tailcat fetches all public keys returned by https://github.com/username.keys via the fetchGitHubSSHKeys function. Any of the returned keys can authenticate. This matches GitHub's own SSH authentication behavior.

What happens if one source in my comma-separated list fails?

The loadSSHAuthorizedKeysFrom implementation processes each source independently. If a GitHub fetch fails or a file is unreadable, that specific source fails but the overall command may still succeed with remaining valid sources. Check server logs for per-source error details.

Is there a way to disable SSH authentication entirely?

Yes. Use the no-auth-ssh service instead of ssh. However, you cannot combine no-auth-ssh with --ssh-authorized-keys—the source code explicitly blocks this at lines 69-71 of cmd/tailcat/tailcat.go to prevent configuration confusion.

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 →