All writing

Field Notes · 4 min read

Publishing Internal Tools on GitHub Without Leaking Anything: My Checklist

Miniature packing station where small workers take keys, ID badges and maps out of wooden crates on a conveyor before the crates roll into a bright showroom

Don't publish your working repository. Copy the code into a fresh repository with new history, scan it for secrets, personal data and client names until the scan comes back empty, push it as private, audit a fresh clone, and only then make it public. Treat anything that was ever in the history as already published.

In October 2026 I published about ten of my own tools: security audit helpers, a questionnaire assistant, network and migration tooling, a local voice dictation app. All of them started life in a private working tree that had seen client projects, test credentials and my own network. Here is the process I used, and what it still missed.

Why not just flip the repository to public

Because git remembers everything. A token you committed three years ago and deleted in the next commit is still in the history. So is the client name in an old test fixture, the internal hostname in a config example and the personal email address in every commit's author field.

You can rewrite history, and GitHub documents how. But for a handful of tools it was cleaner to start over: one fresh repository per tool, the current code only, one initial commit. There's no old branch to forget and no stale tag to miss.

The checklist

  1. Fresh repository, fresh history. Copy only the current state of the code. No git clone, no history.
  2. Commit as your noreply address. GitHub gives every account a private commit address; for accounts created after July 18, 2017 the format is ID+USERNAME@users.noreply.github.com, according to GitHub's email reference. With email privacy enabled, "the commit author email address cannot be changed and will be a no-reply by default" (setting your commit email).
  3. Run a secret scanner. I used Gitleaks, "a tool for detecting secrets like passwords, API keys, and tokens in git repos, files". Worth knowing: its README now says the project is feature complete and future releases are security patches only.
  4. Run your own pattern list. Client and employer names, personal identifiers (names, addresses, phone numbers, emails), internal hostnames, private IP ranges, and author emails in any history you did keep.
  5. Remove binaries and dumps. Database exports, model files, packet captures, screenshots with something in the corner.
  6. Push private first. Then clone that private repository into a new folder and audit the clone, not your working copy. The clone is exactly what the world will see.
  7. Fix, re-scan, repeat until both the secret scanner and your own patterns come back clean.
  8. Make it public, then keep it clean. Turn on Dependabot alerts and merge the security updates; a public repository that rots is its own kind of leak.

GitHub also helps at push time. Push protection is "designed to prevent hardcoded credentials, such as secrets or tokens, from ever being pushed to your repository". At user level it "is enabled by default" and "stops you from pushing secrets to public repositories on GitHub"; at repository level it is disabled by default. Use it as a second net, not as the scan.

What scanners miss

Secret scanners look for things shaped like credentials. They won't flag a client's name in a comment, a colleague's email in a test fixture or the hostname of a firewall in an example config. None of that is a secret in the technical sense. All of it is information you didn't mean to publish. That is why step 4 exists, and why it has to be your own list: nobody else knows your clients.

Test fixtures are the other trap. Some of my test data looks like a token on purpose. Instead of switching the scanner off, I added those findings to a .gitleaksignore file, which ignores specific findings by fingerprint. Everything else still gets caught. The same thinking applies to any control: controls that fail silently are worse than noisy ones.

If something slips through anyway

It happened to me once. After publication, a private network range turned up in a configuration example. It wasn't a credential, but it was more about my setup than I wanted to share.

GitHub's guide on removing sensitive data is clear on the order. If the data is a secret, the first step is to "revoke and/or rotate that secret". Rewriting history comes after, with git-filter-repo and its --sensitive-data-removal option, and the guide itself says that rewriting "may not be warranted" in every case. For a credential, rotation is what makes it useless; the rewrite only tidies up. For my network range there was nothing to rotate, so I rewrote the history.

The second lesson was less technical. A few weeks later I deleted three repositories again. Nothing leaked from them; they just didn't belong under my name. My test now is simple: would I explain this repository in an interview without hesitating? If not, it stays private.

Where mine ended up

The tools that passed are on github.com/falshaw. The same checklist works for anyone who wants to show real work without showing a client's network along with it, and it fits with treating machine identities and tokens as first-class risks, as in AI agent identity sprawl and why the credential is dead.

Related field notes:

Need an experienced firewall and network-security contractor for your next project?

Independent review of firewall rule bases, migrations and network segmentation in German enterprises.

Request a review