_____ __ ____ __ _ __
/ ___// /_ ___ / / / / / (_)___ / /__
\__ \/ __ \/ _ \/ / / / / / / __ \/ //_/
___/ / / / / __/ / / / /___/ / / / / ,<
/____/_/ /_/\___/_/_/ /_____/_/_/ /_/_/|_|
One command. Every machine. SSH ready.
ShellLink is a cross-platform SSH bootstrap system that takes you from a fresh machine to ssh myserver with a single command.
It uses Bitwarden as your secure key vault — no more copying SSH keys around on USB drives or pasting them into Slack.
- Installs dependencies — Bitwarden CLI, OpenSSH, git (+ jq on Linux/macOS)
- Authenticates with Bitwarden — API key login + vault unlock
- Retrieves your SSH key — from a Secure Note, custom field, or attachment
- Configures ssh-agent — starts the agent and loads your key
- Syncs SSH config — clones this repo and generates your SSH config from
.env
bash -c "$(curl -fsSL https://raw.githubusercontent.com/EpicSprout/ShellLink/main/bootstrap.sh)"iwr https://raw.githubusercontent.com/EpicSprout/ShellLink/main/bootstrap.ps1 | iexThat's it. One command.
ShellLink looks for a Bitwarden vault item (default name: ssh-key). You can store the private key in any of these formats:
| Method | How |
|---|---|
| Secure Note (recommended) | Create a Secure Note named ssh-key, paste the full private key into the Notes field |
| Custom Field | On any item, add a custom field named private-key with the key as the value |
| Attachment | Attach a file named id_ed25519 (or id_rsa) to any item named ssh-key |
- Go to vault.bitwarden.com → Settings → Security → Keys
- Under API Key, click View API Key
- Note your
client_idandclient_secret
You can either:
- Set environment variables before running (recommended for automation):
export BW_CLIENTID="user.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" export BW_CLIENTSECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- Let ShellLink prompt you during setup
| Variable | Description | Default |
|---|---|---|
BW_CLIENTID |
Bitwarden API client ID | (prompted) |
BW_CLIENTSECRET |
Bitwarden API client secret | (prompted) |
SHELLLINK_KEY |
Bitwarden item name for SSH key | ssh-key |
SHELLLINK_MODE |
disk (write key to file) or agent (memory only) |
disk |
SHELLLINK_REPO |
Custom repo URL for SSH config | EpicSprout/ShellLink |
./bootstrap.sh [OPTIONS]
-k, --key NAME Bitwarden item name for SSH key (default: ssh-key)
-a, --agent-only Load key into ssh-agent only (no disk write)
-d, --disk Write key to disk and load into agent (default)
--skip-config Skip SSH config sync
-h, --help Show help.\bootstrap.ps1 [OPTIONS]
-Key NAME Bitwarden item name for SSH key (default: ssh-key)
-AgentOnly Load key into ssh-agent only (no disk write)
-SkipConfig Skip SSH config sync
-Help Show helpShellLink generates your SSH config from a template and a .env file. This keeps secrets (hostnames, usernames) out of version control.
ssh-config/config.templatedefines global SSH defaults (e.g.,AddKeysToAgent,ServerAliveInterval)ssh-config/.envdefines your host entries usingHOST_<alias>_<PROPERTY>=valuevariables- At bootstrap time, ShellLink merges the template +
.envinto a generatedssh-config/configand installs it to~/.ssh/config
- Copy the example file:
cp ssh-config/.env.example ssh-config/.env
- Edit
ssh-config/.envwith your real values:HOST_myserver_LABEL=My Server HOST_myserver_HOSTNAME=192.168.1.100 HOST_myserver_USER=youruser HOST_myserver_PORT=22 HOST_myserver_IDENTITY_FILE=~/.ssh/id_ed25519 - To add more hosts, add another
HOST_<alias>_block:HOST_github_LABEL=GitHub HOST_github_HOSTNAME=github.com HOST_github_USER=git HOST_github_IDENTITY_FILE=~/.ssh/id_ed25519
| Property | Required | Default | Description |
|---|---|---|---|
HOSTNAME |
Yes | — | Server address |
USER |
No | — | SSH username |
PORT |
No | 22 |
SSH port |
IDENTITY_FILE |
No | ~/.ssh/id_ed25519 |
Path to private key |
LABEL |
No | alias name | Comment label in generated config |
Note:
ssh-config/.envis gitignored — it stays local to each machine. Only.env.exampleandconfig.templateare tracked in the repo.
┌─────────────────────────────────────────────────────────┐
│ ShellLink Bootstrap │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. curl/iwr → downloads bootstrap script │
│ 2. Clones ShellLink repo to temp directory │
│ 3. Installs: bw, git, jq, openssh │
│ 4. Authenticates with Bitwarden API key │
│ 5. Unlocks vault with master password │
│ 6. Retrieves SSH key from vault item │
│ 7. Starts ssh-agent │
│ 8. Loads key into agent (+ writes to ~/.ssh/) │
│ 9. Clones config repo → ~/.ssh-config/ │
│ 10. Generates config from template + .env │
│ 11. Installs config → ~/.ssh/config │
│ │
... Result: ssh myserver ✔
└─────────────────────────────────────────────────────────┘
ShellLink/
├── bootstrap.sh # Entry point (Linux/macOS)
├── bootstrap.ps1 # Entry point (Windows)
├── scripts/
│ ├── common.sh # Shared utilities (bash)
│ ├── common.ps1 # Shared utilities (PowerShell)
│ ├── install.sh # Dependency installer (bash)
│ ├── install.ps1 # Dependency installer (PowerShell)
│ ├── bw-auth.sh # Bitwarden auth (bash)
│ ├── bw-auth.ps1 # Bitwarden auth (PowerShell)
│ ├── ssh-setup.sh # SSH agent & key setup (bash)
│ └── ssh-setup.ps1 # SSH agent & key setup (PowerShell)
├── ssh-config/
│ ├── config.template # Global SSH defaults (tracked)
│ └── .env.example # Example host definitions (tracked)
├── .gitignore
└── README.md
ShellLink is designed with security in mind:
- No secrets in code — all credentials come from environment variables or interactive prompts
- No logging of sensitive data — passwords and keys are never echoed or written to logs
- Secure memory handling — passwords are cleared from variables after use
- Proper file permissions — keys are
chmod 600,.ssh/ischmod 700 - Agent-only mode — optionally keep keys only in memory, never on disk
- Secure temp file cleanup — temp files are shredded (Linux) or overwritten with random data (Windows)
- Bitwarden API keys — uses API key auth (not password-based login), supports 2FA
To use multiple SSH keys, create separate items in Bitwarden and run ShellLink for each:
# Load your personal key
SHELLLINK_KEY="ssh-key-personal" ./bootstrap.sh
# Load your work key
SHELLLINK_KEY="ssh-key-work" ./bootstrap.sh --skip-config- Make sure the item name matches exactly (default:
ssh-key) - Run
bw syncto refresh the vault cache - Check that the key is stored in the notes field, a custom field named
private-key, or as an attachment
- Double-check your master password
- Ensure
BW_CLIENTIDandBW_CLIENTSECRETare correct - Try
bw logoutand re-run
- Open PowerShell as Administrator
- Run:
Set-Service -Name ssh-agent -StartupType Automatic; Start-Service ssh-agent
- Linux/macOS:
chmod 600 ~/.ssh/id_ed25519 - Windows: Right-click → Properties → Security → Remove all users except yourself
- Ensure
ssh-config/.envexists and has validHOST_entries - Check
~/.ssh/configexists and was generated correctly - Verify permissions:
ls -la ~/.ssh/configshould show-rw------- - On Linux/macOS,
~/.ssh/configis symlinked to the generated config; on Windows it's copied
To update ShellLink on a machine where it's already set up:
cd ~/.ssh-config && git pullOr just re-run the bootstrap command — it handles updates automatically.
MIT — use it, fork it, make it yours.
Built with 💙 by EpicSprout
From zero to SSH in one command.