Skip to content

feat(ssh): native SSH and key management commands#809

Merged
paulocsanz merged 14 commits intomasterfrom
paulo/native-ssh-flow
Mar 18, 2026
Merged

feat(ssh): native SSH and key management commands#809
paulocsanz merged 14 commits intomasterfrom
paulo/native-ssh-flow

Conversation

@paulocsanz
Copy link
Collaborator

@paulocsanz paulocsanz commented Mar 7, 2026

Summary

  • Adds native SSH connections using ssh <serviceInstanceId>@ssh.railway.com
  • Adds railway ssh keys command for managing SSH keys

Changes

Native SSH

  • Native SSH for interactive shells (faster, more reliable than WebSocket relay)
  • WebSocket relay still used for command execution and tmux sessions (SSH exec not supported through QUIC tunnel)
  • Add --relay flag to force WebSocket fallback mode (hidden)

SSH Key Management (railway ssh keys)

  • railway ssh keys / railway ssh keys list - List registered, GitHub, and local SSH keys
  • railway ssh keys add - Register a local SSH key with Railway
  • railway ssh keys remove - Delete a registered SSH key (supports 2FA)
  • railway ssh keys github - Import SSH keys from GitHub account

Other

  • Scans ~/.ssh/*.pub for all local keys (not hardcoded filenames)
  • Shows key fingerprints, types, hostnames, and registration status
  • Auto-prompts to register key on first railway ssh if none registered

Test plan

  • railway ssh opens interactive shell via native SSH
  • railway ssh -- "hostname" runs command via WebSocket relay
  • railway ssh --session myname works with tmux via relay
  • railway ssh --relay forces WebSocket relay
  • railway ssh keys lists all keys with details
  • railway ssh keys add registers a local key
  • railway ssh keys remove deletes a key
  • railway ssh keys github imports from GitHub

@paulocsanz paulocsanz changed the title feat(ssh): add native SSH using serviceInstanceId feat(ssh): native SSH and key management commands Mar 13, 2026
@paulocsanz paulocsanz added the release/minor Author minor release label Mar 13, 2026
Adds support for native SSH connections using the new flow:
`ssh <serviceInstanceId>@ssh.railway.com`

Changes:
- Add native SSH module that uses serviceInstanceId for routing
- Add SSH key management (auto-detect local keys, register with Railway)
- Add GraphQL queries for SSH keys and service instances
- Native SSH is used by default when local SSH keys exist
- Add --relay flag to force WebSocket fallback mode

The native SSH flow:
1. Checks for local SSH keys (~/.ssh/id_*.pub)
2. Ensures key is registered with Railway (prompts or auto-registers)
3. Gets serviceInstanceId via GraphQL
4. Runs ssh <serviceInstanceId>@ssh.railway.com
Railway's SSH proxy doesn't forward exec commands through the QUIC
tunnel, so command execution requires relay mode. Native SSH is now
only used for interactive shells where it works correctly.

- Commands use relay mode (railway ssh <command>)
- Interactive shells use native SSH (railway ssh)
- Tmux sessions continue using relay mode
Display the SSH key path when connecting to help users understand
which key is being used for authentication.
- Remove unused run_native_ssh_with_tmux (exec commands not supported)
- Remove unused find_registered_local_key and ensure_ssh_key_registered
- Fix &PathBuf -> &Path clippy warnings
- Keep tmux sessions using WebSocket relay since SSH exec isn't supported
- Dynamically scans ~/.ssh/ directory for all .pub files
- Filters to supported key types (ed25519, ecdsa, rsa, dss)
- Sorts by key type preference (ed25519 first)
- Add `railway ssh keys` to list registered SSH keys
- Add `railway ssh keys add` to register a local key
- Add `railway ssh keys remove` to delete a registered key
- Shows which local keys match registered keys
- Supports 2FA for key deletion
Add `railway ssh keys` command with subcommands:
- `list` (default): Show registered, GitHub, and local SSH keys
- `add`: Register a local SSH key with Railway
- `remove`: Delete a registered SSH key
- `github`: Import SSH keys from GitHub account

Also removes unused LogFormat::Simple variant.
WebSocket relay is now the default SSH method. Users can opt into
native SSH with --native flag when they want direct SSH connections.
Pass command arguments to the ssh binary when using --native flag,
enabling commands like `railway ssh --native echo hello`.
- Use validateTwoFactor mutation before delete instead of passing code to sshPublicKeyDelete
- Update GraphQL mutation to remove unused code parameter
- Fix 2FA error detection to be case-insensitive and match "two factor" string
- Replace silent non-TTY key registration with explicit error directing user to register manually
- Simplify run_native_ssh to always inherit stdio regardless of command mode
@paulocsanz paulocsanz force-pushed the paulo/native-ssh-flow branch from 5e2db23 to 67f6e9f Compare March 18, 2026 16:38
@paulocsanz paulocsanz merged commit 81cd7d0 into master Mar 18, 2026
6 checks passed
@paulocsanz paulocsanz deleted the paulo/native-ssh-flow branch March 18, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release/minor Author minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant