From: wayne+blog@waynewerner.com To: everyone.everywhere.all.at.once Date: Fri, 21 Nov 2025 19:23:43 -0600 Subject: Introducing Fouroboros
In Which I Introduce Fouroboros - Using your DVCS as a software forge
Not only could this have been an email, but it really is an email. Not only this, but everything. Including your entire software forge could have been an email. Ya know, like Foregjo/Codeberg, GitLab, GitHub, and friends. Or enemies. Frenemies?
Did you ever use Google Code? Do you remember when Sourceforge was the only real option for sharing code? Did you use the free version of Kiln, or were you disappointed when they stopped hosting Mercurial repos on BitBucket?
Are you at least bummed out a little that those options are gone? Are you frustrated that Microsoft is trying to force AI in everything including GitHub? Does it seem very wrong that they took down YouTube-dl?
Maybe you're frustrated by the centrality of all of these hosting tools -- GitLab, and heck even Codeberg/Forgejo and friends are still centralized, even if they're hosted by you.
If you don't like depending on a centralized server (hellooooo, Internet! That's what it's for!), despite the convenience, if there's this little tickle in the back of your mind saying, "This is wrong"...
Then fouroboros is four for you.
Fouroboros, is the ouroboros software forge. Like the self-consuming namesnake, Fouroboros is what it looks like when your VCS is your software forge. They are one continuous loop. Forged in my frustration with the centralization involved in the modern Internet, sprinkled with my love for Internet emails, and polished off with my desire to be able to take my entire repository on the go -- issues and all.
In the beginning, I called it gitself, because I was using "git itself". Like
any self-respecting software project, I pivoted when I realized that was baking
git into the dependencies! That's silly, actually. Just more centralization!
There's no reason to tie it to any particular VCS -- really anyone would work, especially if it's a distributed version control system.
Thus was born Fouroboros, the decentralized software forge/issue tracking system that is included in your repository.
With Fouroboros, you don't actually need any tooling to use it, but I've built some tooling to remove the friction between Fouroboros the protocol(?) and You, the Developer. Or is that Maintainer?
In this post I'll detail first the protocol, and then the tooling that I've built to make it much easier on contributors and maintainers. As it turns out, tooling really is everything.
If you've ever tried to keep a changelog in a single file in a distributed repo, you'll know that it's very difficult because you get merge conflicts galore.
Fouroboros sidesteps this need (sort of).
Fouroboros is comprised mainly of two parts:
./Fouroboros/INBOX./Fouroboros/ISSUESTo put it simply, contributors put their issues in the INBOX, and as the maintainer you move them from the INBOX into ISSUES, and that's when they are assigned their real issue number. And because the only changes that contributors should really be making to an issue are posting, then we know exactly how to handle merge conflicts.
Say you have ISSUES/0001.eml. In this we have:
1
2
3
Now your contributors Gonzo and Rizzo are each commenting on the issue. In a typical git merge you're going to see something like this:
1
2
3
<<< gonzo
5
========
4
>>> rizzo
And you can manually go in and sort the entries -- what that really looks like is something like this:
git fetch --all
git merge gonzo rizzo
git checkout gonzo -- ISSUES/0001.eml
cp ISSUES/0001.eml /tmp/gonzo0001.eml
git checkout rizzo -- ISSUES/0001.eml
Then you simply have to parse each file (ISSUES/0001.eml and
/tmp/gonzo0001.eml) take the set of messages, sort them, and write them out to
ISSUES/0001.eml.
Now contributors can fetch your updates and view all of the goings-on.
At the most fundamental level -- that's all there is to it. The issues are only moved from the INBOX into ISSUES on your main branch (or really, whatever you want to specify. This is your repo, after all!) And practically, the way we avoid conflicts in the INBOX (and ensure some level of correctness) is that we use uuidv7 to create the INBOX issues. This guarantees a few things:
You might have some other questions here. Like, how do I reject an issue? Well, you have a couple of options:
This issue or message is spam or abuse and should be rejected out of hand?
Don't even merge the branch in, delete the offending remote repository, and
add them to a blocklist. We can use Fouroboros/blocklist.toml, with
something like:
[swedish chef]
remote = "https://erdy-berdy-borky-bork.example.com/spaghetti.git"
reason = "There was nothing I could understand from this contributor issue *at all*"
Now anyone else tracking your repo and this same remote will be able to ensure they're ignoring that repo.
This issue is just noise, and there's no reason to waste a number on it. It's fine if it stays in the repo history, but it doesn't really serve any purpose. Just delete the file from INBOX, commit the change, and move on. For a reply to a message, you could basically do the same.
So, that's how you as the maintainer handle things. How about the contributors?
Well, the first thing that you need is a place where they can clone your repo from. This could be a centralized service, but for maximum decentralization do this:
git init --bare
cat <<EOF>hooks/post-update
#!/bin/sh
exec git update-server-info
EOF
chmod +x hooks/post-update
Now you can either serve that directory directly from a webserver if you have a
VPS or something, or you can rsync it to a static directory somewhere. Even a
USB stick that you pass around.
Anyway, once contributors clone your repo, then they can commit to their own main branch (or whatever one you've specified is your Fouroboros branch) and create issues in the INBOX or reply to ISSUES. Then in order to get changes to you, they'll need to publish their own repo in a place that you can fetch from. Maybe it's a public web server, or a directory on a network share. Wherever it is, you can go ahead and fetch their changes and merge them in. OG pull requests say hi.
And that's it. That's the lot of it. UUID7 issues created in the INBOX. Someone accepts them, renaming the file to N+1 from the ISSUES folder. They make a commit, and then move the file to the ISSUES folder (this gets you a 100% rename in git, ensuring you can't lose that file history).
Any sufficiently advanced technology is indistinguishable from magic.
- Arthur C. Clarke
This might be your lucky day, but if not you'll know
that fancy tech and magic are no different. My manual approaches to Fouroboros
were boring, so I created some sufficiently advanced technology magic.
I have a lot more magic on the roadmap, but you can get what magic I do have by installing uv and then you can run:
uv tool install --index https://dev.waynewerner.com/python/ fouroboros[fancy]
Wait, what, not PyPI? Well, remember that whole thing about decentralization? PyPI is one of those centralized things. At least if that's all that you use. So I will mirror packages to PyPI, as a backup. So if you can't get it from my site, go for PyPI!
Once you've run uv tool install it will add fbs-fancy as a command (along
with the more boring fbs, and sore-finger inducing fouroboros).
Now you can head to your repo and run fbs-fancy. When you run this you should
see a fancy UI that looks like this:
When you first run Fouroboros, it will create the Fouroboros/INBOX and
Fouroboros/ISSUES directory. If you'd like to change that directory, the
Fouroboros title, or if you want to make Fouroboros automatically commit its
changes, go ahead and edit .fouroboros (in the future this will be
.fouroboros.toml).
You can modify these values to suit your needs:
autocommit = true
title = "My Very Cool Project"
root_path = "my_work"
In the future you'll be able to edit these from the UI and we'll have a little creation wizard. But for now, that's what we've got!
Feel free to play around, and if you want to contribute, check out https://waynewerner.com/projects/fouroboros.html. This post won't get updated, but that page will!
~Wayne