5 min read

"One home, two front doors: running an open-source project on your own forge, with GitHub as a mirror"

Kestrel's development lives on its own forge. As of 1.0.12 it's on GitHub too, as a mirror, so people can open issues and send pull requests where they already are, while every patch is still applied, tested and released in one place.
Headline "One home, two front doors." beside the Kestrel mark: one home for all development, two ways to send a patch, one identity on every commit, three files copied with every release.

Most open-source projects live on GitHub, because that's where people are. Kestrel doesn't. Its code, reviews, tests and releases live on a self-hosted Forgejo forge, where its release builds run on their own build machine and turn into a ready-to-flash memory card image.

That's a good home for the work. It's a poor front door. The forge is closed to new accounts, so until now the only way to contribute was to email a patch. Plenty of people won't, and they shouldn't have to.

As of Kestrel 1.0.12, the project has a second front door on GitHub, without moving house: github.com/aluminum-ice/kestrel.

Kestrel is my nights-and-weekends project, built with Claude as my AI teammate: an open-source Raspberry Pi device for authorized Wi-Fi security research, with a dashboard on your phone. It's unaffiliated with my employer, and the views here are my own.

One home for the work

Forgejo, the project's own forge, holds all code and reviews, runs lint and tests on every change, builds releases and holds beta checklists, and is where every patch is applied. A push mirror copies every change to GitHub, and a release copy brings notes, image, checksum and manifest. On GitHub people read, star, fork, open issues, send pull requests and download releases. Patches by email or GitHub pull request are both applied on the forge.
Two front doors, one place where the work happens.

The rule is simple: all development happens on the forge. GitHub is a push mirror. It updates itself from the forge after every change, and nothing is ever committed on GitHub directly.

Contributions come in through either door:

  • By email, as before, with no account needed anywhere.
  • On GitHub, as an issue or a pull request, from wherever people already are.

Either way, a patch lands in the same place. It's applied on the forge, reviewed there, and run through the same lint and tests as every other change. Once merged, it reaches GitHub through the mirror, and the GitHub pull request is closed with a link to where it landed. One history, one review process, one set of checks, whichever door someone walked through.

Releases, copied carefully

People also expect to download releases from GitHub, so every Kestrel release is copied there too: the notes, the memory card image, its checksum and the small manifest devices use to decide whether an update installs in place.

Copying a release to GitHub in six steps: wait until the image, checksum and manifest are all attached; check the image against its own published checksum; point the GitHub release at exactly the commit the forge built; copy the notes and pre-release flag with a link back; upload only what's missing or wrong; safe to run again. The GitHub access key is only ever sent to GitHub.
Six steps, safe to repeat for any release.

A few choices made it trustworthy rather than merely convenient:

  • It checks before it copies. The image must match its own published checksum, so a damaged download never becomes a GitHub release.
  • Same commit, same words. The GitHub release points at exactly the commit the forge built, with the same notes and the same beta flag, and a link back to the original.
  • Safe to repeat. It uploads only what's missing or wrong, so running it twice changes nothing. Fixing a typo in the notes on the forge flows through to GitHub the same way.
  • One key, one destination. The GitHub access key is only ever sent to GitHub. The forge's release is read the way any visitor would read it.

Adding it taught us one more thing. The update system decides whether a release can install in place from what changed in its build, and its old rule counted any new workflow file as a build change. Adding this mirroring job would have told every device to reflash for no reason. The rule now counts only what can actually change the image.

A public repository: nothing real, ever

Kestrel studies real Wi-Fi networks, so the people building it handle real network names, addresses and locations every day. A public repository is one careless commit away from publishing somebody else's data. So the rule is absolute, and it's enforced by a machine, not by memory:

Placeholders, every time: example.com and user@example.com, documentation address ranges like 192.0.2.10, radio addresses from 00:00:5E:00:53:xx, network names like My Network and Cafe Guest, and generic coordinates. A scanner blocks real-looking radio addresses, personal emails, private keys, access tokens, password hashes, capture files, location tracks, trained models and diagnostics bundles, on every pull request and before every commit my AI teammate makes.
Placeholders in, real data out, checked by a machine every time.
  • Placeholders everywhere, from address ranges set aside for documentation to cafés that don't exist.
  • A scanner on every pull request, and before every commit my AI teammate makes, that blocks anything that looks real: radio addresses, personal emails, keys, tokens, password hashes, capture files, location tracks, trained models and diagnostics bundles.
  • Never weakened to get a change through. If a genuine placeholder trips it, the placeholder is added to the scanner's allowlist in the same change, with a reason.

The same thinking covers security reports. Vulnerabilities are reported privately, to a role-based email address, with a safe-harbor promise for good-faith research. They're tracked privately too, never in public issues, pull requests or commit messages, until they're fixed.

Easy to help, safe to help

Contributors: your name stays out of the history, since an accepted patch is committed under the project's identity and credit is by a handle you choose; security problems are reported privately; the rules are stated up front.
What a contributor can count on.
  • Your name stays out of the history. An accepted patch is committed under the project's identity, because the repository never records contributors' personal details. If you'd like credit, you pick a handle. A GitHub pull request is still public under your GitHub account, and the contributing guide says so plainly.
  • The rules come first. Authorized testing only, nothing real in a patch, and new features live in their own plugins. Said before you start, not after you've sent something.

For engineering leaders

  • Separate where people arrive from where work happens. Meet contributors where they are, but keep one source of truth, one review process and one set of checks.
  • Automate the boring bridges carefully. A release copy that checks checksums and is safe to re-run is worth more than a fast one.
  • Enforce the rules people can't afford to forget. "Don't commit real data" belongs in a scanner, not a wiki page.

An AI teammate helped most with the parts that are easy to skip: the mirroring workflow's edge cases, and keeping the contributing guide, the project rules and the docs in step with each change.

For investors

Open source is a distribution channel, and GitHub is where that channel lives. It's also a governance question: who controls the code, the release pipeline and the data that flows through them. A small team can now have both: the reach of GitHub and the control of its own infrastructure, with hygiene (privacy, security reporting, contributor protection) that used to take a dedicated person. Ask an open-source company where the work actually happens, and what stops a contributor's mistake from becoming a headline.

Come say hi

Kestrel is on GitHub now: github.com/aluminum-ice/kestrel. Stars, issues and pull requests are all welcome, and so are patches by email.

If posts like this are useful to you, subscribe and the next one lands in your inbox.

Where does your team's work really happen, and where do people expect to find it? 🪶

Kestrel is a personal, open-source project built on nights and weekends, unaffiliated with my employer, and for authorized testing only. The user guide and source are public.