git-gemini-forge 0.6.1

A simple Gemini server that serves a read-only view of public repositories from a Git forge.
# git-gemini-forge

A simple [Gemini](https://geminiprotocol.net/) capsule (server) that bridges public repositories from a Git forge onto Geminispace. Inspired by masalachai's [gemini-git-browser](https://github.com/masalachai/gemini-git-browser).

View the Project Demo at [gemini://git.average.name](gemini://git.average.name) with a Gemini browser such as [Lagrange](https://gmi.skyjake.fi/lagrange/), or [via an HTTP proxy](https://portal.mozz.us/gemini/git.average.name/).

## Under Construction

> [!WARNING]
> This project is under active construction, and is missing basic features. Interface layouts, routes, and environment variables may change at any time until v1.0.0.

## Features

This capsule proxies a given git forge's public API, usually via localhost. Proxying remote hosts is possible, but subject to [restrictions](#restrictions).

### Supported Forges

- [Forgejo](https://forgejo.org/)
- [Gitea](https://about.gitea.com/)
- [GitLab](https://about.gitlab.com/)

### Wishlist

- [Sourcehut](https://man.sr.ht/)
- [GitHub](https://github.com/)

### Routes

- `/` -> Lists several code repositories with recent activity.
- `/users` -> If enabled, lists several users with recent activity. (See the "[Environment Variables](#environment-variables)" section below.)
- `/{username}` -> Lists several of the user's code repositories.
- `/{username}/{repository}` -> Lists the repository's branches and top-level file tree.
- `/{username}/{repository}/src/branch/{branch}` -> Lists the top-level file tree for the branch.
- `/{username}/{repository}/src/branch/{branch}/{filepath...}` -> Browse the directory tree. Viewing source files is not implemented yet.

### To do

- Render a repository's README file on its main page.
- Paginate long lists.
- Work out the kinks in GitLab integration.
- Proper unit testing in all the places.

### Restrictions

When proxying a remote host (a domain name and not localhost or an IP address) without an auth token, this proxy respects robots.txt. This is to help combat potential bot traffic to the capsule and to avoid this proxy becoming a nuisance to git forges. Provide an auth token or deploy the proxy against your forge at localhost to ignore robots.txt.

To add an auth token, see `FORGE_AUTH_TOKEN` in the [Environment Variables](#environment-variables) section below.

See also the [Disclaimer](#disclaimer) and [Robots.txt](#robotstxt) sections below.

## Installation

[See below](#environment-variables) on configuring environment variables.

### Docker

Create a `compose.yaml` file like the following:

```yaml
services:
  git-gemini-forge:
    image: git.average.name/averagehelper/git-gemini-forge:latest
    container_name: git-gemini-forge
    restart: unless-stopped
    environment:
      - FORGE_TYPE=forgejo
      - FORGE_URL=http://localhost:3000
    volumes:
      - "./.certs:/app/.certs:ro"
    network_mode: "host"
```

See also the example [`compose.yaml`](./compose.yaml) file provided here.

If your git forge is on a different network from the host, then omit `network_mode` and specify a `ports` map:

```yaml
services:
  git-gemini-forge:
    # [...]
    ports:
      - "1965:1965"
```

Then run:

```sh
docker compose pull
docker compose up -d
```

### Cargo

This crate is published on our own package registry. With [`cargo`](https://www.rust-lang.org/tools/install) installed, run the following:

```sh
cargo install --index sparse+https://git.average.name/api/packages/AverageHelper/cargo/ git-gemini-forge
```

Also available from [crates.io](https://crates.io/crates/git-gemini-forge):

```sh
cargo install git-gemini-forge
```

Then run with:

```sh
FORGE_URL=http://localhost:3000 git-gemini-forge
```

### Homebrew (EXPERIMENTAL)

This package is available as a formula on [our own formula repository](https://git.average.name/AverageHelper/homebrew/src/branch/main/Formula/git-gemini-forge.rb). To tap this registry:

```sh
brew tap --force-auto-update averagehelper/homebrew 'https://git.average.name/AverageHelper/homebrew'
```

Then, to install the formula:

```sh
brew install averagehelper/homebrew/git-gemini-forge
```

### Build from source

Clone this project using `git`. Then, with [`cargo`](https://www.rust-lang.org/tools/install) installed, run the project using:

```sh
FORGE_URL=http://localhost:3000 cargo run --release
```

## Environment Variables

- `CERTS_DIR`: The directory, relative to the current working directory when the binary runs, that `git-gemini-forge` should check for the `key.pem` and `cert.pem` files. [See below](#certificates) on how to generate and configure those. Defaults to `.certs`.
- `FORGE_TYPE`: Describes the type of forge that is being proxied, and defines the expected shape of the API to call. Must be one of `forgejo`, `gitea`, or `gitlab`, case insensitively. Defaults to `forgejo`.
- `FORGE_URL`: The URL for the forge. The program will halt immediately if the given URL is malformed. Defaults to `http://localhost:3000` ([a common port for running Forgejo](https://forgejo.org/docs/latest/admin/installation-docker/)).
- `FORGE_AUTH_TOKEN`: An optional token that would grant access to certain privileged routes, such as `/metadata` or `/users` on GitLab. If omitted, no authorization will be sent and some functions may respond with a Gemini [`43 Proxy Error`](https://geminiprotocol.net/docs/protocol-specification.gmi#status-43-proxy-error). An API key is required for proxying remote hosts which block their API routes in robots.txt.
- `FORGE_LIST_USERS`: Whether we should attempt to list users, for example at the `/users` endpoint. Consider leaving this disabled if your forge does not permit enumerating users. Defaults to `false`.

## Certificates

### For local development

If you're already developing a Gemini capsule locally, copy those certs into a new `.certs` directory in this project. Otherwise, create TLS certificates like so:

```sh
mkdir -p .certs
cd .certs
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 1103760 -nodes -subj '/CN=localhost'
```

> [!IMPORTANT]
> Change the `'/CN=localhost'` part to your expected domain when using in production.

Note that the `-days` arg in the command above sets the cert to expire sometime around the year 3024. This is sufficient for use with Gemini capsules or local development, but not much else.

The `<project>/.certs` directory must contain `key.pem` and `cert.pem`, or the server will panic.

## Robots.txt

While not a "bot", this client does respect [robots.txt](https://en.wikipedia.org/wiki/Robots.txt). To block unidentified requests from this proxy for your git forge, modify your `robots.txt` file to specify a `User-agent` that matches `git-gemini-forge`, and block the API routes.

Note that this proxy ignores robots.txt when deployed against `localhost` or an IP address, or the deployment has an API key for the forge. The API key, if given, is provided in the headers of each request this proxy sends to the forge, even to endpoints which do not require it. Blocking this API key will prevent this proxy from starting. If the proxy is already running, then disabling the key will only disable the proxy for API routes which require the key. Contact the offending user if their use of `git-gemini-forge` is unacceptable to your infrastructure.

You may [open an issue](https://git.average.name/AverageHelper/git-gemini-forge/issues/new) here or [contact me directly](https://average.name/contact) if these restrictions on `git-gemini-forge` are not sufficient.

## Disclaimer

This tool is meant to bring HTTP-based git forges to Geminispace. The intent is for Forgejo admins to host this capsule alongside their own Forgejo installation, pinging the forge's API via localhost. While proxy via HTTPS is supported for distributed forge infrastructure, proxying external git forges that you do not own is very rude when done without permission. Please ask first before bogging down someone else's server with proxy traffic. I am not responsible for misuse of this tool.