I was already doing this. Badly.
Long before Jenni had a name, I was already self-hosting downloads.
Old habit, predates this stack: zip the binary, drop it in a download
folder on the server, link the URL from wherever it had to be linked.
It worked. It was also messy.
GitHub had a different role for me — it's where I show code to people.
Pull requests, READMEs, the public face. Not where my
apps go to fetch their next version. That always felt mismatched: the
place where strangers read your code is not the same place where
production should download from.
When WordPress plugins joined the pile of things I ship, the versioning
side stopped being optional. Two plugins becomes ten becomes twenty,
and every one of them needs a stable update endpoint, a verifiable hash,
a virus scan that someone in legal can point to. The zip-in-a-folder
pattern doesn't scale, and nobody needs another half-finished script
to glue it together.
Jenni is the formal version of what I was already doing — plus the
things I should have been doing all along. She also offers
download widgets you can embed on external sites:
upload a new version on jenni.download, the widget on someone else's
page delivers the latest version with the ClamAV scan attached.
Why include the scan? Why not.
Then a different problem walked in. One of my apps wanted to expose
its own plug-in directory — a way for users to discover modules
without me running a marketplace. Same uploads, same scans, same
hashing — just a different endpoint shape on the way out. So I
taught Jenni to group releases under a collection and
return them as a single JSON catalog. Same machine, same trust
pipeline, one new shape. That became the fourth thing she does.
A third problem walked in not long after, and it was the one I had
been quietly ignoring. My auto-updater was trusting TLS for the
whole stack — version number, hash, scan status, the works. The
moment someone got a shell on the server, every client would
install whatever the server claimed was the new release the next
morning. So Jenni learned to sign every release with Ed25519, and
then she learned the harder trick: a second key, kept on my laptop
and never on the server, that signs the list of which
signing keys are valid. Clients pin one 32-byte pubkey for years.
Rotations and revocations happen without a client update. A breached
server can still lie — it cannot impersonate. The
full protocol lives here.
And then — because this is how it always goes — a fourth problem.
The module catalog worked, but I was the only one who could fill it.
People who wrote extensions for my apps had to email me a ZIP and wait.
So Jenni grew an authenticated author API: a token scoped to a single
collection, a submit endpoint, the same scan and signature
pipeline my own uploads run, and a draft state I approve before anything
goes live. The self-service frontend that turns it into a proper little
marketplace — JenniHUB — is the next thing on the bench.