Image for MacBook to Mission Control Part 4: Git, SSH, Secrets, and Remote Development
Technology Jun 25, 2026 • 15 min read

MacBook to Mission Control Part 4: Git, SSH, Secrets, and Remote Development

Configure Git identity, generate SSH keys, manage secrets safely, and set up remote development on a fresh MacBook in one focused guide.

Share:
Lee Foropoulos

Lee Foropoulos

15 min read

Continue where you left off?
Text size:

Contents

Part 3 got your Mac configured from scratch: Homebrew, your shell, your terminal, your core tools. The machine is functional. But functional isn't the same as ready. Before you clone a single repository, push a single commit, or connect to a single remote server, you need to establish who you are on this machine and prove it in a way that's consistent, secure, and repeatable. That's what Part 4 is about.

This isn't ceremony. Git identity, SSH keys, GitHub authentication, and secrets hygiene are the foundation everything else sits on. Skip them, or rush through them, and you spend the next six months cleaning up attribution errors, rotating leaked credentials, and explaining to your team why your commits show up under a stranger's email address. The configuration takes about thirty minutes. The mistakes it prevents can take days to untangle.

Why Identity and Authentication Come First

The cost of skipping this step

Most developers set up authentication exactly once, on their first machine, years ago, and never think about it again until something breaks. A new machine is the moment that pattern catches up with you. The wrong Git email means your commits don't appear on your GitHub contribution graph. Worse, if you're working across personal and professional accounts, a mismatched email can attribute your employer's code to your personal account or expose your work email in a public repository's commit history. Neither is a good situation.

A developer reviewing code on a laptop with authentication and security concepts visible
Identity and authentication are the foundation of every commit, connection, and credential you'll use on this machine.

Leaked API keys are a different category of problem entirely. A single .env file committed to a public repository can expose database credentials, payment processor keys, or cloud provider tokens within minutes. Automated bots scan GitHub for secrets constantly. The average time between a secret being pushed and being discovered by a malicious actor is measured in seconds, not hours.

The wrong email costs you a contribution graph. A leaked key can cost you a production database.
3.5s
Average time for an exposed secret to be detected by an automated scanner on a public GitHub repository

Private key loss is quieter but just as damaging. If you generate an SSH key, never back up the passphrase or store the key securely, and then lose the machine, you're locked out of every server you added that key to. Recovery means touching every remote host individually.

What this part covers

This part covers four things in sequence. First, configuring your Git identity so every commit carries the right name and email. Second, authenticating with GitHub CLI so your local machine can push, pull, and interact with GitHub without re-entering credentials. Third, generating and managing an SSH key pair using the ed25519 algorithm with macOS Keychain integration. Fourth, building a clean SSH config file that makes remote connections readable and repeatable.

By the end, you'll have a fully authenticated, secure developer identity on your machine. Part 5 will build on this foundation when we move into dotfiles and environment configuration, but none of that works cleanly without what you're setting up here.

Configuring Your Git Identity

Setting name, email, and default branch

Git's global configuration is stored in ~/.gitconfig and applies to every repository on your machine unless a repo-level config overrides it. The first thing to set is your name and email. Open your terminal and run:

bash
git config --global user.name "Your Name"
git config --global user.email "[email protected]"

The email here is not cosmetic. It must match exactly the email address associated with your GitHub account. GitHub uses this value to link commits to your profile, populate your contribution graph, and, if you later configure commit signing, to verify your identity. A single character difference means your commits show up as unlinked gray boxes instead of attributed contributions.

Terminal window showing Git configuration commands being entered
A few minutes in the terminal now prevents weeks of attribution headaches later.

While you're here, set your default branch name:

bash
git config --global init.defaultBranch main

Git still defaults to master on a fresh install. Every new repository you initialize will use main instead, which aligns with current GitHub conventions and avoids the awkward rename step after the fact.

Personal vs. work email

If this machine is for work, use your work email. If it's personal, use your personal email. Mixing them is the most common Git identity mistake developers make on new machines. Your commits are permanent and public. The email embedded in them is too.

Choosing a pull strategy

Set your pull strategy explicitly so Git doesn't warn you every time you run git pull:

bash
git config --global pull.rebase false

This tells Git to use a merge commit when pulling remote changes rather than rebasing your local commits on top of them. For most developers, especially those working on shared branches or coming back to Git after a break, merge-based pulls are easier to reason about. The history is messier but the behavior is predictable. You can always change this per-repository once you have a stronger opinion about it.

Verifying your global config

Before you touch any repository, confirm everything looks right:

bash
git config --global --list

You should see your name, email, default branch, and pull strategy all listed. If anything is missing or wrong, rerun the relevant config command. The ~/.gitconfig file is plain text and can also be opened directly in any editor if you prefer to review or adjust it that way.

Authenticating with GitHub CLI

Installing and running gh auth login

If you followed Part 2 of this series, GitHub CLI (gh) is already installed via Homebrew. Confirm it's available:

bash
gh --version

If the command returns a version number, you're ready. If not, run brew install gh before continuing. With gh confirmed, start the authentication flow:

bash
gh auth login

The CLI will walk you through a short series of prompts. The first asks which account type you're connecting to. Choose GitHub.com unless you're working against a GitHub Enterprise instance.

HTTPS vs SSH: which to choose

The next prompt asks which protocol you want to use for Git operations: HTTPS or SSH. Both work. The practical difference is where authentication happens.

Abstract visualization of network authentication and secure connections
HTTPS and SSH both get you to GitHub. The difference is in how your identity travels with you.

HTTPS uses a token stored by a credential helper. It's simpler to set up and works everywhere, including environments where SSH ports are blocked. SSH uses a key pair you control entirely, which is more portable across machines if you manage your keys well. For most developers on a personal or work Mac, HTTPS is the easier default. If you're already planning to set up SSH keys for remote server access (which this part covers next), you can choose SSH here and use the same key pair for GitHub too.

Pick HTTPS if you want the simple path. Pick SSH if you want one key to rule every connection you make.

Browser-based login flow

After choosing your protocol, gh will ask how you want to authenticate. Choose Login with a web browser. The CLI generates a one-time device code and opens your browser to github.com/login/device. Paste the code, confirm the authorization, and GitHub sends the token back to your terminal. The whole flow takes under a minute.

Verifying authentication

Once the flow completes, confirm everything worked:

bash
gh auth status

You should see your GitHub username, the authentication method, and the token scopes granted. The default scopes cover repository access, Gist creation, and read access to your profile. That's sufficient for the vast majority of development workflows. A useful side effect of gh auth login is that it also configures Git's credential helper automatically, so git push and git pull work without prompting you for credentials every time.

Generating and Managing Your SSH Key

Generating an ed25519 key pair

RSA was the standard SSH key algorithm for a long time. ed25519 is better. It produces smaller keys, signs faster, and offers equivalent security with a shorter key length. There's no practical reason to generate an RSA key on a new machine today.

Abstract data visualization representing cryptographic key generation
ed25519 keys are shorter, faster, and just as secure as RSA keys at any comparable length.

Generate your key pair with:

bash
ssh-keygen -t ed25519 -C "[email protected]"

The -C flag sets a comment on the key. Using your email here makes it easy to identify which key belongs to which account when you're looking at an authorized_keys file on a remote server. When prompted for a file location, press Enter to accept the default (~/.ssh/id_ed25519). When prompted for a passphrase, set one. Always set one.

256
Bit key length of ed25519, compared to the 2048–4096 bits required for comparable RSA security

Starting the SSH agent

The passphrase protects your private key at rest. The SSH agent holds your decrypted key in memory so you don't have to type the passphrase every time you open a connection. Start the agent in your current shell session:

bash
eval $(ssh-agent -s)

This starts the agent process and sets the environment variables your shell needs to communicate with it. You'll see an agent PID printed to confirm it's running.

Never share your private key

The file at ~/.ssh/id_ed25519 is your private key. It never leaves your machine. Don't copy it to cloud notes, don't paste it into a chat, don't commit it to a repository. If it's ever exposed, delete it and generate a new pair immediately.

Adding your key to the macOS Keychain

Add your key to the agent with macOS Keychain integration so the passphrase persists across reboots:

bash
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

The --apple-use-keychain flag stores your passphrase in the macOS Keychain. The next time your machine restarts and the SSH agent loads, it retrieves the passphrase automatically. You get the security of a passphrase-protected key without the friction of typing it every session.

Your public key (~/.ssh/id_ed25519.pub) is safe to share. It's designed to be shared. Copy it to your clipboard with:

bash
pbcopy < ~/.ssh/id_ed25519.pub

From there, paste it into GitHub under Settings > SSH and GPG Keys, or into the authorized_keys file on any remote server you need to access.

Creating a Clean SSH Config File

Why SSH aliases matter

The raw SSH command for connecting to a remote development server looks something like this:

bash
ssh [email protected] -i ~/.ssh/id_ed25519

That's fine once. It's tedious every time, and it's easy to mistype a hostname or forget which key goes with which server. The SSH config file at ~/.ssh/config solves this by letting you define named host aliases with all the connection parameters baked in.

Terminal window showing SSH configuration and remote connection management
A well-structured SSH config file turns long, forgettable commands into short, memorable ones.

The file doesn't exist by default on a fresh Mac. Create it:

bash
touch ~/.ssh/config

Setting correct file permissions

SSH is strict about file permissions. If your config file or the .ssh directory itself has permissions that are too open, SSH will refuse to use the config entirely and print a warning. Set the permissions correctly before writing anything:

bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config

700 on the directory means only your user can read, write, or execute it. 600 on the config file means only your user can read or write it. Other users on the system get nothing. SSH enforces these requirements as a security guarantee, not a suggestion.

SSH doesn't warn you about bad permissions. It just quietly refuses to use the file. Set them right the first time.

Writing your first SSH host alias

Open ~/.ssh/config in your editor and add a host block:

1Host remote-host-01
2  HostName remote-host-01.example.internal
3  User devuser
4  IdentityFile ~/.ssh/id_ed25519
5  AddKeysToAgent yes
6  UseKeychain yes

Each line does something specific. Host sets the alias you type in the terminal. HostName is the actual address SSH connects to. User sets the remote username so you don't have to include it in the command. IdentityFile points to the private key to use. AddKeysToAgent yes loads the key into the SSH agent automatically on first use. UseKeychain yes pulls the passphrase from macOS Keychain so you're never prompted.

With this block in place, ssh remote-host-01 replaces the full command entirely. You can add as many Host blocks as you need, one per server, environment, or service.

If you chose SSH over HTTPS when authenticating with GitHub CLI, add this block as well:

1Host github.com
2  HostName github.com
3  User git
4  IdentityFile ~/.ssh/id_ed25519
5  AddKeysToAgent yes
6  UseKeychain yes

This ensures every Git operation targeting GitHub uses your ed25519 key automatically, without any additional flags or configuration at the repository level.

Part 5 takes everything you've established here and builds your dotfiles system on top of it: a portable, version-controlled configuration layer that makes any new Mac feel like home in under an hour.

Secrets Management: Keeping Credentials Out of the Wrong Places

Part 3 covered SSH key generation, agent configuration, and the ~/.ssh/config file that makes remote connections predictable. Now the focus shifts to something that trips up developers at every experience level: keeping credentials away from places they should never be.

A secret is anything that grants access or proves identity. API keys, OAuth tokens, database passwords, private keys, webhook secrets. If someone else gets it, they get what it unlocks. That's the whole definition.

Where Secrets Must Never Live

The list of wrong places is longer than most developers expect.

Git history is the most dangerous. A secret committed even once is permanently embedded in every clone of that repository, even after deletion from the working tree. Screenshots get synced to cloud photo libraries and indexed by services you don't control. Notes apps like Apple Notes or Notion often sync to mobile devices and share data with third-party servers. Shell history stores every command you've typed, including any export SECRET=abc123 you ran inline. Committed config files like config.json or settings.py with hardcoded credentials get pushed to remotes and shared with every contributor.

The History Problem

Deleting a file doesn't remove it from Git history. Every clone of your repository still contains every version of every file ever committed. Rotation is the only real fix.

15%
of public GitHub repositories have contained at least one exposed secret, according to research across major code hosts

The .env Pattern: Real Files vs Example Files

The .env pattern solves the configuration problem cleanly. You commit a .env.example file with placeholder values so collaborators know what variables the project needs. You keep the real .env ignored from version control entirely.

1# .env.example
2DATABASE_URL=postgres://user:password@localhost:5432/mydb
3STRIPE_SECRET_KEY=sk_live_REPLACE_ME
4WEBHOOK_SECRET=your_webhook_secret_here
1# .env (never committed)
2DATABASE_URL=postgres://forop:actualpassword@localhost:5432/proddb
3STRIPE_SECRET_KEY=sk_live_abc123realkey
4WEBHOOK_SECRET=wh_sec_actualvalue

"The .env.example is a contract. The .env is the implementation. Only one of them belongs in version control."

Using a Password Manager CLI for Runtime Injection

The .env pattern still writes secrets to disk. For higher-sensitivity credentials, you can skip the file entirely using a password manager CLI to inject secrets at runtime.

1Password's CLI makes this clean:

bash
export STRIPE_SECRET_KEY=$(op read "op://Personal/Stripe API/credential")

That command reads the secret from your 1Password vault and assigns it to the environment variable in memory. Nothing touches a file on disk.

Bitwarden, Doppler, and AWS Secrets Manager all offer equivalent CLI patterns. The approach is the same regardless of which tool you use: the secret lives in the vault, gets injected at process start, and disappears when the session ends.

Runtime Injection Principle

Secrets injected at runtime through a password manager CLI never exist as plaintext files. They live in memory for the duration of the process and nowhere else.


Keeping Secrets Out of Git

.gitignore is your first line of defense, and it needs to be configured before the first commit on any new project. Not after. Before.

Configuring .gitignore Correctly

Add secret-bearing files to .gitignore at project initialization:

1# .gitignore
2.env
3.env.local
4.env.production
5*.pem
6*.key
7.DS_Store

You should also configure a global ignore file that applies across every repository on your machine:

bash
git config --global core.excludesfile ~/.gitignore_global

Then add your most common patterns to ~/.gitignore_global. .DS_Store, .env, *.pem, and *.key belong there. This catches the cases where you forget to create a project-level .gitignore before the first commit.

1 in 5
developers has accidentally committed a secret to a repository at least once, according to developer survey data

What to Do If a Secret Is Already Committed

Rotate the credential first. Always. Before you touch Git history, before you open a terminal, before you do anything else: go to the service that issued the secret and revoke it. Generate a new one. The old credential is compromised the moment it touches a commit, and cleaning history doesn't undo any access that may have already occurred.

After rotation, you can rewrite history using git filter-repo or BFG Repo Cleaner. Both tools remove a file or string from every commit in the repository's history. BFG is faster for simple cases. git filter-repo is more flexible and actively maintained.

Cleaning Git history is the cleanup. Rotating the credential is the fix. Do not confuse the order.

Secret Scanning Tools

GitHub's built-in secret scanning checks pushed commits against known secret patterns and alerts you when a match is found. It's enabled by default on public repositories and configurable on private ones.

For local prevention, detect-secrets and gitleaks both work as pre-commit hooks, scanning staged files before a commit completes. A failed scan blocks the commit until you resolve the issue.

No tool replaces the habit of running git diff --staged before every commit. Scanning tools catch patterns they recognize. Your own eyes catch the things that don't match a known pattern but still shouldn't be in source control.


Setting Up Remote Development Workflows

SSH Aliases as the Foundation

Every remote workflow in this section builds on the ~/.ssh/config aliases from Part 3. When you type ssh remote-host-01 instead of ssh -i ~/.ssh/id_ed25519 -p 2222 [email protected], you've already eliminated the most common source of remote workflow friction. The config file does the work once so you don't repeat it every session.

3x
faster average time to reconnect to a remote session when using named SSH aliases and tmux versus raw SSH commands

Persistent Sessions with tmux

The problem with a plain SSH connection is that it dies when your network drops, your laptop sleeps, or you close the terminal. Everything running in that session disappears with it.

tmux solves this by running a persistent session on the remote server that exists independently of your connection. You attach to it, detach from it, and reconnect to it from anywhere.

Start a named session after connecting:

bash
ssh remote-host-01
tmux new -s dev

When you disconnect or close the window, the session keeps running. Reconnect from a new terminal:

bash
ssh remote-host-01
tmux attach -t dev

Everything is exactly where you left it. The process you were running, the directory you were in, the output on screen. Nothing lost.

Remote Logs and Project Directories

Keep remote project directories consistent. ~/projects/ as the root for all repositories means you always know where to navigate. ~/projects/api/, ~/projects/frontend/, ~/projects/scripts/. Predictable structure matters more on remote machines than local ones because you're navigating without a file browser.

For quick log inspection without opening a full session, SSH accepts a command directly:

bash
ssh remote-host-01 tail -f /var/log/app.log

That one-liner opens a connection, runs the command, and streams output to your local terminal. No tmux session required for short-lived tasks.

Avoiding Raw SSH Command Sprawl

A developer working at a desk with multiple monitors showing terminal windows and code
Organized remote workflows start with a clean SSH config and a consistent directory structure on every server.

Raw SSH commands with multiple flags become unmanageable fast. A command like ssh -i ~/.ssh/deploy_key -p 2222 -L 5432:localhost:5432 [email protected] is correct once and wrong every other time you mistype a flag. The ~/.ssh/config alias collapses that entire command into ssh remote-host-01. For developers who prefer not to live in the terminal for editing, the VS Code Remote SSH extension connects to any host alias from your config and opens a full editor session on the remote machine. The underlying mechanism is identical. The interface is just different.


Common Mistakes and How to Avoid Them

Identity and Key Mistakes

Wrong Git email is more consequential than it looks. Commits attributed to an unknown email address don't appear in your GitHub contribution graph, can break GPG commit signing, and create confusion in code review when nobody can tell who authored a change. Verify with git config --global --list immediately after setup, and verify again after any machine migration.

Losing your private key with no backup strategy means losing access to every server and service that trusted it. Generate keys with a passphrase so a backup stored in a password manager is encrypted at rest. Storing the key only on one machine is one hardware failure away from a complete lockout.

Copying private keys into Slack, email, or cloud notes is the equivalent of photographing your house key and posting it publicly. The key is the credential. Treat it accordingly.

Using the same SSH key across every machine means a single compromised device compromises every service you've ever authorized. Generate a unique key per device. Name them clearly in your ~/.ssh/config.

Secrets and Configuration Mistakes

The Hardcoding Trap

Hardcoding an API key directly in a source file feels like a shortcut. It becomes a liability the moment that file is committed, shared, or read over someone's shoulder.

Forgetting to add .env to .gitignore before the first commit is the single most common way secrets end up in Git history. Add it before you write a single line of application code.

Skipping the passphrase on SSH keys because unlocking feels inconvenient is a false tradeoff. macOS Keychain stores the passphrase after the first unlock and handles every subsequent authentication silently. The friction is a one-time cost. The protection is permanent.

The Keychain removes the inconvenience argument entirely. A key without a passphrase is just a file anyone can use.

Not verifying git config after setup means you might push dozens of commits under the wrong identity before noticing. Run git config --global --list and confirm the output before your first commit on any new machine.


Part 4 Checklist: Your Secure Developer Identity

Part 4 Checklist: Git, SSH, Secrets, and Remote Development 0/14

The foundation is set. Identity, keys, secrets hygiene, and remote session management are all in place. Part 5 moves into terminal configuration and shell tooling: building a zsh environment that's fast, readable, and consistent across every machine you work on.

How was this article?

Share

Link copied to clipboard!

You Might Also Like

Lee Foropoulos

Lee Foropoulos

Business Development Lead at Lookatmedia, fractional executive, and founder of gotHABITS.

🔔

Never Miss a Post

Get notified when new articles are published. No email required.

You will see a banner on the site when a new post is published, plus a browser notification if you allow it.

Browser notifications only. No spam, no email.

0 / 0