Skip to content

Install

Run on any new machine:

Terminal window
curl dotkit.run/sh | sh

Or with wget:

Terminal window
wget -qO- dotkit.run/sh | sh

Pass options using sh -s --:

Terminal window
curl -fsSL dotkit.run/sh | sh -s -- --username yourname --defaults
OptionDescription
-d, --defaultsSkip all prompts. Use defaults.
-u, --username NAMEClone NAME/dotkit from GitHub.
-r, --repo REPORepo as user/repo, full URL, or local path. Mutually exclusive with --username.
-p, --profile NAMEProfile to apply. Skips selection prompt.
-t, --target DIRWhere to clone the repo. Default: ~/dotkit.
--no-sudoDo not use sudo at any point.
--cli PATHUse a local CLI directory or tarball instead of downloading.
--pat TOKENGitHub PAT for private repos. Could instead be interactive or DOTKIT_PAT env var. See below.
--dry-runPrint what would happen. No system changes.

Skip prompts, use a specific profile:

Terminal window
curl -fsSL dotkit.run/sh | sh -s -- --username yourname --profile work --defaults

Clone to a custom location:

Terminal window
curl -fsSL dotkit.run/sh | sh -s -- --username yourname --target ~/dotfiles

Use a full URL (GitHub, GitLab, self-hosted):

Terminal window
curl -fsSL dotkit.run/sh | sh -s -- --repo https://github.com/yourname/dotkit

Use a local repo already on disk:

Terminal window
curl -fsSL dotkit.run/sh | sh -s -- --repo ~/dotfiles --profile work

Preview without changing anything:

Terminal window
curl -fsSL dotkit.run/sh | sh -s -- --username yourname --dry-run

By default, the installer will prompt for a GitHub personal access token (PAT) if it needs to access a private repository. That would be the most secure way to provide the token, as it won’t be stored in shell history or visible in the process list. However, you can also provide the token non-interactively with the --pat flag or DOTKIT_PAT environment variable for convenience. Please read the caution section for security considerations.

Terminal window
DOTKIT_PAT=ghp_xxx curl -fsSL dotkit.run/sh | sh -s -- --repo yourname/dotkit

The installer can decrypt and install SSH keys stored as age-encrypted files in a private GitHub repo. SSH key setup runs before the repo is cloned, so your keys are ready for private dotkit repo access.

To decrypt the keys, someone needs both the GitHub PAT and the age passphrase. The PAT alone gives repo access but the keys remain encrypted. Store both in a password manager. Enable 2FA on your GitHub account.

Your private ssh-keys repo must contain age binaries and your encrypted key files:

your-ssh-keys-repo/
age/
darwin-arm64/
age
age-plugin-batchpass
darwin-amd64/
age
age-plugin-batchpass
linux-amd64/
age
age-plugin-batchpass
linux-arm64/
age
age-plugin-batchpass
id_ed25519.age
id_ed25519.pub.age
id_rsa.age

The age/ folder provides the binaries you used to encrypt the keys (this way you won’t ever be missing them). The installer downloads the right one for the current platform.

Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens and generate a new token.

  • Name: something like ssh-keys-read
  • Resource owner: your account
  • Repository access: select only your ssh-keys repo
  • Permissions: Contents: Read-only

The token is shown only once. Save it to a password manager.

All files must use the same passphrase. The installer reads it once and uses it for every .age file.

  1. Create a private GitHub repo with the structure above.

  2. Encrypt all keys in ~/.ssh/ at once:

    Terminal window
    cd ~/.ssh
    printf "age passphrase: "; read -r AGE_PASSPHRASE; export AGE_PASSPHRASE
    for f in *; do
    [ -f "$f" ] || continue
    case "$f" in *.age|*.age.bak.*) continue ;; esac
    age -e -j batchpass -o "$f.age" "$f"
    echo "Encrypted: $f"
    done
  3. Verify all files decrypt correctly before pushing:

    Terminal window
    for f in *.age; do
    [ -f "$f" ] || continue
    original="${f%.age}"
    [ -f "$original" ] || continue
    age -d -j batchpass "$f" | diff - "$original" \
    && echo "OK: $f" || echo "FAIL: $f"
    done
  4. Commit the .age files to your private repo. Never commit the plaintext keys.

When the installer runs, it:

  1. Prompts for your ssh-keys repo (owner/repo or https://github.com/owner/repo)
  2. Prompts for a GitHub PAT to access the private repo (hidden input)
  3. Downloads the age binary and age-plugin-batchpass for your platform from the repo
  4. Downloads the .age files to ~/.ssh/
  5. Prompts for the age passphrase (hidden input)
  6. Decrypts keys in place with chmod 600 and removes the .age files
PathDescription
~/.local/bin/CLI scripts
~/.local/share/dotkit/State files (active profile, exported env vars)
~/dotkitDefault clone location