grappa

How it's built

From a tag to your machine

Nobody uploads anything by hand. A tag starts it; the last step is your package manager.

Two clankers, a conveyor, and a key that never leaves the house.

1 The tag

A release is a git tag. Everything below is a consequence of it โ€” there is no "publish" button anyone presses.

2 The build

GitHub Actions compiles the release, bundles the cicchetto app into it, and produces the .deb, .rpm, .pkg.tar.zst and the container image. Public machines, public logs, reproducible from the tag.

3 The doorbell

When the build is green the workflow rings a doorbell on the publishing host: one authenticated request, reachable only from GitHub's own address ranges. It carries no payload โ€” it only says "go and look".

4 The throwaway machine

The publishing host runs FreeBSD and has none of the Debian, RPM or Arch indexing tools. So it rents a machine, boots it, has it build the repository indexes out of the public packages, takes the result back, and destroys it. It lives a couple of minutes and never sees a secret.

5 The signature

The indexes come home and are signed there, by a key that exists on exactly one machine. Not in a CI secret, not on GitHub: a compromised workflow could at worst publish a wrong package, never sign one of its own.

6 Your update

Then it is just apt upgrade, dnf upgrade or pacman -Syu, with the signature checked by your own package manager. Set it up โ†’

And the code itself?

Written the same way it is published: in the open, and mostly by machines that are told exactly what to do. grappa is built by three Claude sessions wired together in tmux โ€” one takes the requests on IRC, one plays project manager, one writes the code โ€” with a human holding the rulings. It is a strange way to run a software project, so I wrote it down.

Three hats and a tmux pane โ†’

Everything is on GitHub: the code, the issues, the release workflow that does all of the above.

Ready?

Log in with a nick โ€” you're on IRC in seconds.

Try it now โ†’