# ermite A single-user Git forge in one Go binary. ermite hosts bare repositories, serves clone and push over HTTPS, reviews branches as merge requests, and shows everything in a web interface with live updates. Status: early development. `ermite serve` serves Git over HTTP (clone, fetch, push), the API, `GET /-/health`, and the web: repositories, an expandable file tree with a repository-wide fuzzy file finder, files, commits, refs and merge requests. Visitors read public repositories; the owner signs in with a one-time link from the CLI to see private ones and to review and merge. ## Run ```sh export ERMITE_DATA_DIR=/var/lib/ermite export ERMITE_PUBLIC_URL=https://git.example.com export ERMITE_ADMIN_TOKEN=$(head -c 32 /dev/urandom | base64) ermite serve ``` | Variable | Default | Notes | |---|---|---| | `ERMITE_DATA_DIR` | required | absolute path of the data root | | `ERMITE_PUBLIC_URL` | required | `http(s)` base URL, no path | | `ERMITE_ADMIN_TOKEN` | required | at least 32 characters | | `ERMITE_ADDR` | `:7341` | listen address | | `ERMITE_TRUSTED_PROXIES` | empty | comma-separated CIDRs | | `ERMITE_GIT` | `git` | git 2.45 or newer | | `ERMITE_GIT_READER` | `ermite-git` | `ermite-git` reads refs, trees, blobs and history with the [ermite-git](https://git.bnema.dev/bnema/ermite-git) library where it is faster than the `git` binary and falls back to `git` elsewhere and for repositories it does not read; `cli` uses the `git` binary for every read (the escape hatch if a read looks wrong: set it and restart) | | `ERMITE_SOURCE_URL` | `$ERMITE_PUBLIC_URL/ermite` | link to the source (AGPL) | | `ERMITE_LOG_LEVEL` | `info` | `trace`, `debug`, `info`, `warn`, `error` | | `ERMITE_LOG_FORMAT` | `json` | `json` or `console` | | `ERMITE_COMMITTER_NAME` | `ermite` | committer of squash merges | | `ERMITE_COMMITTER_EMAIL` | `ermite@` | committer email of squash merges | | `ERMITE_OWNER` | `owner` | owner login that CI shows in `github.repository` (`/`) and `github.repository_owner`; follows the rules of repository names | | `ERMITE_CI_LOG_RETENTION` | `90d` | how long CI runs, jobs, checks and logs are kept: `Nd` (days) or a Go duration such as `720h`, at least one hour | | `ERMITE_CI_POLL_TIMEOUT` | `50s` | how long the server holds a runner's `FetchJob` call when no job is queued, from `1s` to `55s`; set it below the response timeout of a reverse proxy in front of ermite (Gordon cuts a response that has no headers after 30 s, so use `25s` behind it) | `ermite version` prints the version (and the server's when a server URL is configured, see Remote CLI). ## Remote CLI The same binary drives a running server over its API. It reads `ERMITE_URL` (the server address, `http://` or `https://`) and `ERMITE_TOKEN` (a bearer token; optional for public reads). A token sent over plain `http://` to a host that is not loopback triggers a warning on every call. Each setting comes from the environment or from a config file, the environment first: | Setting | Environment | Config file key | |---|---|---| | server address | `ERMITE_URL` | `url` | | bearer token (optional for public reads) | `ERMITE_TOKEN` | `token.cmd`, a command that prints it | The file is `~/.config/ermite/config` (`$XDG_CONFIG_HOME/ermite/config` when set; `~/Library/Application Support/ermite/config` on macOS and `%AppData%\ermite\config` on Windows; `ERMITE_CONFIG` names another file). `ermite config show` prints the path in use. It holds no secret: `token.cmd` is a command line that fetches the token from your secret manager (pass, gopass, op, bw, secret-tool, …). ```ini # ~/.config/ermite/config url = https://git.example.com token.cmd = pass show ermite/token ``` ```sh ermite config set url https://git.example.com ermite config set token.cmd 'pass show ermite/token' # or: gopass show -o ermite/token, op read op://vault/ermite/token, secret-tool lookup service ermite ermite config show [--json] # path and stored values; unset removes a key ``` `token.cmd` runs with `sh -c` (`cmd /C` on Windows) only when a command needs the token, at most once per run, with your environment and, when standard input is a terminal, that terminal (so a pinentry or biometric prompt works; otherwise it reads nothing). On a timeout or when ermite is interrupted or terminated, the command and everything it started are killed (when standard input is a terminal, only the shell is, so that prompts keep working). It must print the token alone on one line, in under 4 KiB, within 60 seconds. It is not run when `ERMITE_TOKEN` is set, nor by `ermite version` or `ermite config`, and not when the config file, or the directory holding it, can be written by other users or the file belongs to another user (`ermite config show` and `set` warn about it). On Windows this check does not exist: keep the file in your own profile, the default place. It belongs to the server of the file: when `ERMITE_URL` names a different server than the file's `url`, it is not run and the call is anonymous (with a notice; set `ERMITE_TOKEN` to authenticate there). A failure is reported in one line without its output. Shell completion never runs it: it is anonymous unless `ERMITE_TOKEN` is set. ```sh export ERMITE_URL=https://git.example.com ERMITE_TOKEN=ermite_... ermite repo list | show | create [--description …] [--visibility public] | edit [--description …] [--visibility …] [--default-branch …] | delete [--yes] ermite token create