orib.dev: Self Hosting Git9

Git9 is a simple, clean implementation of git for Plan 9.

Every commit on it has been made using git9, but the hosting has always been on Github. This was done for two reasons: First, the server side of the protocol hadn't been implemented at the time, making hosting a git server difficult. Second, github is by far the most popular git server, so making sure that every commit was tested against it seemed like a good idea, especially since there are a number of github bugs I've run into. (More on that in another post)

That changes today: Git9 is running on git/serve, hosted on 9front. It's been towed within the environment.

I'll keep mirroring to github for the foreseeable future, because people keep promising that using github will bring in contributors -- and because it doesn't hurt to keep testing against github. Also, breaking links annoys people.

It lives here now: https://orib.dev/git/git9/HEAD/info.html


Why run this over other hosting providers?

Pride, mostly. I implemented a git client and a git server. Why the hell shouldn't it be self hosting?

If you're not in my position, I don't recommend it. It needs a weird operating system, has very few features, and isn't battle hardened. It's probably fine for up to medium scale projects with a few million lines of code and a few tens of thousands of commits, but I expect that fresh clones will be slow for linux-scale projects or larger.

If you're looking for a familiar option, use sr.ht, github.com, or similar. If you want self hosting, there are options like gittea, smithy, or cgit.

If you want to ignore my advice and use this anyways, I'm finding it to be a simple, lightweight, and easy to customize hosting platform, and I'm happy help with any rough spots in either the documentation or code.


Git9 supports the git:// protocol, over a various number of transports, with varying levels of authentication and security.

For Unix users on upstream git, the only supported protocol is the unencrypted git://. While technically you can push over the git protocol, this is disabled on my instance for good reason.

For read only access on Plan 9 clients, I also serve up the git:// protocol over ssl: You can use it by cloning using the gits:// transport. At some point I'll look at submitting a patch upstream, since it seems like a no-brainer to allow encrypted git.

Finally, for authenticated access, I use the hjgit:// protocol, which does a key exchange using Plan 9 authentication, then uses the exchanged secret to set up a TLS connection. On my server, only dp9ik is supported.

There's also a web view, hosted at https://orib.dev/git/repos.html, for exploring the code without cloning.

Hosting Setup

My instance of git9 is hosted using 9front, with a small number of additional utilities: cinap's tcp80 is used for the webserver, and execfs is used to provide dynamically generated static paths

The services are started using aux/listen. Aux/listen is similar to inetd: If a file exists in the service directory, then it's run when someone tries to connect. So, for example, if /rc/bin/service/tcp443 exists, then when someone connects to tcp port 443, that program gets executed.

The following config files live in the service directory:

tcp9418 runs the unencrypted git:// server. Every time you connect to git, a new instance of git/serve is started, responds to your request, and exits. The -w flag is omitted, so git/serve invocations on this port are read only.

exec git/serve -r/usr/git

tcp9419 runs the gits:// server. The invocation of git/serve is the same as for git://, however it's wrapped up in tlssrv, so the fd passed to git/serve is for an encrypted connection.

exec tlssrv -c/sys/lib/tcp80/fullchain.pem /bin/git/serve -r/usr/git

tcp17021 runs the hjgit server. By now, this will look familiar. The -a flag to tlssrv tells it to use plan9 auth to establish a shared secret for the TLS tunnel, and the -w flag to git/serve tells it to allow writing to repositories.

exec tlssrv -a /bin/git/serve -wr/usr/git

Finally, tcp443 runs the web server. It follows the pattern, but before starting, it mounts execfs to provide the virtual files for browsing repositories.


execfs -m /usr/web/git /sys/lib/tcp80/gitrules
rfork n
exec tlssrv -c/sys/lib/tcp80/fullchain.pem /bin/tcp80

execfs provides a virtual file system, where file paths map to the output of commands. Every line of the rules file contains two fields: A regex pattern to match the path, and a program invocation to use to provide those file contents. The rules used are below:

/repos.html				/bin/gitls /usr/git list
/([^'/]+)/info.html			/bin/gitls /usr/git info '\1' HEAD
/([^'/]+)/([^'/]+)/info.html		/bin/gitls /usr/git info '\1' '\2'
/([^'/]+)/([^'/]+)/snap.tar.gz		/bin/gitls /usr/git tar '\1' '\2'
/([^'/]+)/([^'/]+)/(([^']+)/)?f.html	/bin/gitls /usr/git view '\1' '\2' '\4'

The gitls script is used to actually render the repository contents into html. It's a short rc script, which lives here: https://orib.dev/git/git9/HEAD/extra/gitls/f.html. This script sandboxes itself, and then generates a web view.

It runs in one of four modes: tar, list, info, and view.

The list view generates a list of all repsoitories containing .git/webpublish.

The tar mode generates a snap.tar.gz of the repository. It exists largely to bootstrap git9 on systems that don't already have it. The info view shows the current commit, the list of files at the top level, and the contents of .git/README, or the readme in the repository. Finally, the view mode shows the contents of a file or directory.


In addition to self hosting, I've also set up a site to host other people's git repositories. Doing this is part of my evil plan to get other people to find bugs in my code.

Shithub is live, at shithub.us