An Indie Developer's GitHub Secrets Went Public

TL;DR

An indie SaaS developer pushed to the wrong repository. A private repo's code went to a public one. The commit contained database credentials, API keys, and a JWT secret. GitHub's secret scanning caught most of it, but the 15 minutes before he noticed felt like hours. Everything had to be rotated, and he learned to never have both public and private remotes configured.

The Setup

The developer maintained both open source projects and his own SaaS product. For convenience, he had multiple git remotes configured on his machine. One project had both a private backup remote and a public fork.

You can probably see where this is going.

The Mistake

He was working late, finishing a feature for a client integration in his SaaS app. The codebase included a config.js file with database credentials. Yes, environment variables exist. He was going to fix it "soon."

He typed what he thought was a push to his private backup:

git push origin main

But his current directory wasn't what he thought it was. He was in the wrong project folder. And "origin" in that folder pointed to a public repository.

The push completed. He didn't notice for about 10 minutes.

The Discovery

He got an email from GitHub: "Secret scanning alert for repository name."

His heart stopped. He opened the email and saw it had detected:

  • A database connection string with username and password
  • An AWS access key
  • A Stripe secret key

He sprinted to his computer and checked the repository. There it was. His SaaS product's entire codebase, including the config file with credentials, was now public.

The Scramble

The next 30 minutes were a blur of activity:

  1. Made the repo private (this doesn't undo the exposure, but stops new access)
  2. Rotated every credential in the config file
  3. Changed the database password
  4. Revoked and regenerated the AWS keys
  5. Rotated the Stripe API key
  6. Changed the JWT secret (which invalidated all user sessions)

Then came the uncomfortable part: figuring out if any of his paying customers had been affected.

The Aftermath

He checked his logs immediately. He reviewed every database query and API call from the window of exposure.

"I had about 200 paying users at that point. I kept refreshing the access logs, looking for anything out of the ordinary. The thought of someone accessing my users' data because of my mistake was gut-wrenching."

There was no evidence of unauthorized access, but he couldn't be 100% certain. The credentials had been public for about 15 minutes.

15 minutes is plenty of time. Automated bots scan GitHub constantly. Research shows credentials can be discovered and tested within minutes of being pushed. Even brief exposure is exposure.

What He Did Wrong

1. Credentials in Code

The fundamental problem. Even in a "private" project, credentials shouldn't be in code. Environment variables exist for a reason.

2. Multiple Remotes Without Safeguards

Having both public and private remotes configured is risky. It's too easy to push to the wrong one.

3. No Pre-push Hook

A pre-push hook could have scanned for secrets and blocked the push. He had one set up on his main projects but not on this one.

4. Not Double-Checking the Directory

He assumed he was in the right folder. A quick pwd or git remote -v would have shown the mistake.

Changes He Made

Global Git Hooks

He set up global pre-commit and pre-push hooks that scan for common secret patterns. They run on every repository, not just the ones he remembers to configure.

# ~/.config/git/hooks/pre-push
# Scans for common secret patterns before any push
if git diff --cached | grep -qE "(sk_live|AKIA|password\s*=)"; then
  echo "Potential secret detected. Push blocked."
  exit 1
fi

Removed Public Remotes from Private Projects

If a project is private, it has no public remotes. Period. If he needs to create a public version, he does it in a separate, clean repository.

Prompt Shows Current Directory

He modified his shell prompt to prominently display the current directory and git remote. He can see at a glance where he is and where he's pushing to.

Separate Workspaces for Client and Personal Projects

Client work and his SaaS product now live in separate workspace directories. No more mixing open source forks with production codebases on the same machine.

The lesson: Multiple layers of defense matter. Any single safeguard can fail. When you have credentials in code AND multiple remotes AND no pre-push hooks AND you're tired, disaster happens. Remove as many failure points as possible.

What GitHub Did Right

GitHub's secret scanning detected the exposed credentials within minutes and sent alerts. For some services (Stripe, AWS), they also notified the providers, who revoked the credentials automatically.

This is a safety net, not a solution. But it limited the damage significantly.

Can I remove secrets from git history?

Yes, using tools like BFG Repo-Cleaner or git filter-branch. But this doesn't help if the secret was already copied by bots or attackers. Always assume an exposed secret is compromised and rotate it.

Does making a repo private remove the exposure?

No. Making a repo private stops future access, but anyone who already cloned the repo or copied the secrets still has them. Additionally, search engines and archive services may have cached the content.

How do I prevent pushing to the wrong remote?

Remove unnecessary remotes from your git config. Use unique, descriptive remote names. Add confirmation prompts with pre-push hooks. Display current remote in your shell prompt.

What if I can't rotate a credential immediately?

Contact the service provider. Many have emergency processes for compromised credentials. At minimum, add additional authentication (IP restrictions, MFA) while you work on rotation.

Scan for Exposed Secrets

Check your repositories for accidentally committed credentials.

Security Stories

An Indie Developer's GitHub Secrets Went Public