# mugit mugit is a small, read-only web front end for a directory of bare git repositories, with cloning over HTTPS. It is for the person who keeps their own git server, pushes to it over SSH, and wants the public parts of it to have a web page and a clone URL without running a forge. What it does: - lists the public repositories, with descriptions and last-commit dates; - shows each repository's README, rendered from Markdown, and lets visitors browse the files on the default branch; - serves `git clone` and `git fetch` over HTTP(S) through git's own smart protocol; - obtains and renews its own Let's Encrypt certificate, or runs plain HTTP behind a reverse proxy. What it does not do: - issues - pull requests - accounts - pushing over HTTP - anything else that writes. Repositories are managed with git itself, over SSH or on the machine. ## How repositories are published mugit serves a single directory, `/srv/git` by default. Each bare repository in it is a subdirectory named `.git`. A name is the part before `.git`: letters, digits, `.`, `_` and `-`, not starting with a dot and not itself ending in `.git`. A repository is public when it contains a file named `git-daemon-export-ok`, the same convention git-daemon and git-http-backend use. Everything else in the directory is invisible: a private repository and a nonexistent one both answer with the same 404, so the site never confirms which private names exist. cd /srv/git git init --bare widget.git echo "A widget for widgeting" > widget.git/description touch widget.git/git-daemon-export-ok The `description` file, which git creates in every repository, is shown on the index and the repository page. The README is looked for at the root of the default branch, under the names `README.md`, `README.markdown`, `README` and `README.txt`, in that order of preference. Relative links and images in a Markdown README resolve into the site's own file and raw views. Bear in mind that publishing a repository publishes all of it: every branch, tag and commit that a clone would fetch, not just the default branch the web pages show. ## Building mugit is a single Rust binary. It needs a Rust toolchain of 1.88 or later, and the `git` command on the PATH at runtime for clones, fetches and raw file downloads (everything else uses a bundled libgit2). cargo build --release The binary is `target/release/mugit`. The stylesheet and favicon are embedded in it, so it has no files to install alongside it. ## Running Every option is a command line flag or an environment variable of the same name with a `MUGIT_` prefix; `mugit --help` lists them all with their defaults. The ones that matter: | Flag | Environment | Default | Meaning | |---|---|---|---| | `--repos` | `MUGIT_REPOS` | `/srv/git` | The directory of bare repositories | | `--listen` | `MUGIT_LISTEN` | `0.0.0.0:8000` | Address for plain HTTP | | `--tls-domain` | `MUGIT_TLS_DOMAIN` | | Enables HTTPS with a Let's Encrypt certificate for this domain | | `--tls-listen` | `MUGIT_TLS_LISTEN` | `0.0.0.0:443` | Address for HTTPS | | `--acme-cache` | `MUGIT_ACME_CACHE` | `/var/lib/mugit/acme` | Where the ACME account key and certificates are kept | | `--acme-contact` | `MUGIT_ACME_CONTACT` | | Contact email registered with the certificate authority | | `--acme-staging` | `MUGIT_ACME_STAGING` | off | Use the Let's Encrypt staging environment while testing | | `--site-name` | `MUGIT_SITE_NAME` | `mugit` | Name shown in page headers and titles | | `--public-url` | `MUGIT_PUBLIC_URL` | | Base URL that clone URLs are built from | | `--page-size` | `MUGIT_PAGE_SIZE` | `50` | Repositories per page of the index | | `--git-processes` | `MUGIT_GIT_PROCESSES` | `8` | Git processes serving clones, fetches and raw files at once | | `--git-idle-timeout` | `MUGIT_GIT_IDLE_TIMEOUT` | `60` | Seconds a stalled clone or download is tolerated before it is dropped | | `--http-idle-timeout` | `MUGIT_HTTP_IDLE_TIMEOUT` | `300` | Seconds a silent connection is held before it is closed; must exceed twice `--git-idle-timeout` | Logging goes to standard error. `MUGIT_LOG` takes a [tracing filter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html) such as `debug` or `mugit=debug,warn`; a release build logs warnings and errors by default. ### Standalone with HTTPS Give mugit a domain and it takes care of the certificate itself, using the TLS-ALPN-01 challenge. That challenge is answered on port 443, so the HTTPS listener must be reachable from the internet on that port, and the plain listener then does nothing but redirect to HTTPS. mugit --tls-domain git.example.org --acme-contact you@example.org \ --listen [::]:80 --tls-listen [::]:443 Clone URLs are built from `https://` unless `--public-url` says otherwise. Try `--acme-staging` first if you are unsure of the setup: Let's Encrypt rate-limits failed attempts against production. ### Behind a reverse proxy Without a TLS domain, mugit speaks plain HTTP on the listen address and leaves TLS to the proxy in front of it. Set `--public-url` to the address visitors use, so that the clone URL shown on each page is right; without it, mugit falls back to the scheme and Host header of each request. mugit --listen 127.0.0.1:8000 --public-url https://git.example.org The proxy should pass requests through unbuffered: git's smart protocol streams in both directions, and buffering a whole fetch would defeat the bound mugit keeps on memory. ## Deploying as a service A worked example for a Linux host with systemd, where repositories belong to a `git` user that people push to over SSH, and mugit runs as its own user with read-only access to them. Adjust paths and names to taste. 1. Create the service user and give it read access to the repositories through the `git` group. It owns nothing under the repository directory. useradd --system --home-dir /var/lib/mugit --shell /usr/sbin/nologin mugit usermod -aG git mugit chmod 750 /srv/git Repositories created by the `git` user need to be group-readable, which is git's default; check with `ls -l /srv/git/*.git` if in doubt. 2. Create the state directory for the ACME account key and certificates, and install the binary. install -d -m 750 -o mugit -g mugit /var/lib/mugit /var/lib/mugit/acme install -d -m 755 /opt/mugit install -m 755 target/release/mugit /opt/mugit/mugit 3. Install the unit from [`deploy/mugit.service`](deploy/mugit.service). It runs the binary from `/opt/mugit`, serves `/srv/git` on ports 80 and 443, and hardens the process; everything specific to your site goes in `/etc/mugit/env`: install -m 644 deploy/mugit.service /etc/systemd/system/mugit.service install -d -m 755 /etc/mugit cat > /etc/mugit/env <