Help us shape the future of Go! Take the 2026 Go Developer Survey

ermite

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: AGPL-3.0

README

ermite

A single-user Git forge in one Go binary. ermite hosts bare repositories, serves clone and push over HTTPS, and shows them in a read-only web interface.

Status: early development. ermite serve serves Git over HTTP (clone, fetch, push), answers GET /-/health, and serves the read-only web: the home list, the summary of a repository (README, clone URL, facts, recent commits) and its refs. Tree, file, commit and live pages are still to come.

Run

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_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 version prints the version (and the server's when ERMITE_URL is set).

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.

export ERMITE_URL=https://git.example.com ERMITE_TOKEN=ermite_...
ermite repo list | show <name> | create <name> [--description …] [--visibility public] | edit <name> [--description …] [--visibility …] [--default-branch …] | delete <name> [--yes]
ermite token create <label> [--preset cli|agent|ci] [--scope repo:read …] [--repo name …] [--expires 30d]   # prints the secret once
ermite token list | revoke <id>
ermite version                  # client and server, warns about missing server features
ermite watch [repo] [--json]    # follows events, reconnects and resumes

--json on repo list|show|create|edit, token create|list and version prints stable field names, listed in each command's --help; watch --json prints one object per line. Errors are one line on standard error (ermite: …) and exit status 1. ermite completion bash|zsh|fish prints a shell completion script that completes repository names from the server.

Git over HTTP

git clone http://git.example.com/<repo>.git          # public repositories need no credentials
git push http://u:<token>@git.example.com/<repo>.git main

Use the admin token (ERMITE_ADMIN_TOKEN) or a stored token as the password; the username is ignored. Store it in a Git credential helper rather than in the URL. Pushing to a new name creates a private repository. Repository names are lowercase, [a-z0-9._-]. TLS is terminated by your proxy; list its network in ERMITE_TRUSTED_PROXIES so that failed-login limits see the real client address (X-Forwarded-For).

The server keeps its runtime files in $ERMITE_DATA_DIR/run/ (the hook socket and scripts that git calls on each push); the hook socket path must stay under about 100 bytes.

Run with Docker

docker build --build-arg VERSION="$(git describe --tags --always --dirty)" -t ermite .
token=$(head -c 32 /dev/urandom | base64)   # keep it: it is the root credential
docker run -d -p 127.0.0.1:7341:7341 -v ermite-data:/data \
  -e ERMITE_PUBLIC_URL=http://localhost:7341 -e ERMITE_ADMIN_TOKEN="$token" ermite

The image is alpine with git, runs as uid 10001 and keeps its data in the /data volume. Logs are JSON by default. The build fetches bnema.dev/ermite-* from git.bnema.dev, so the host must reach it (NetBird); the build container uses the host's DNS and routes. Behind another route, pass --build-arg GIT_REWRITE_BASE=http://git.bnema.dev:8088/ with --network host, as the CI job does.

Develop with compose

docker compose up

Serves on http://localhost:7341 with the development admin token in compose.yaml. The source is mounted and air (go tool air, .air.toml) rebuilds and restarts the server on each change to a .go or .templ file; the module and build caches and the data live in named volumes. docker compose down -v removes them. The dev container runs as root, which maps to your user under rootless Docker.

Build

go build ./cmd/ermite
go test ./...

Develop

Tools are pinned in go.mod:

go tool buf lint
go tool buf generate        # proto/ -> gen/
go generate ./...           # mockery mocks of the ports
go tool hexcheck -fail-on warn ./...

License

AGPL-3.0-or-later. See LICENSE.

Directories

Path Synopsis
cmd
ermite command
Command ermite is a single-user Git forge.
Command ermite is a single-user Git forge.
gen
internal
adapters/client
Package client is the Connect client of the remote CLI: it turns the ERMITE_URL and ERMITE_TOKEN settings into authenticated service clients for the ermite-api contract, maps Connect errors to one-line messages and keeps a Watch stream alive across reconnections.
Package client is the Connect client of the remote CLI: it turns the ERMITE_URL and ERMITE_TOKEN settings into authenticated service clients for the ermite-api contract, maps Connect errors to one-line messages and keeps a Watch stream alive across reconnections.
adapters/eventbus
Package eventbus implements ports.EventBus: in-process fan-out to subscribers, never blocking the publisher.
Package eventbus implements ports.EventBus: in-process fan-out to subscribers, never blocking the publisher.
adapters/gitcli
Package gitcli implements the Git ports over the git binary.
Package gitcli implements the Git ports over the git binary.
adapters/githttp
Package githttp serves Git over smart HTTP: it routes, authenticates and authorizes a request, then lets `git http-backend` answer it through net/http/cgi.
Package githttp serves Git over smart HTTP: it routes, authenticates and authorizes a request, then lets `git http-backend` answer it through net/http/cgi.
adapters/httpserver
Package httpserver is the HTTP entry of ermite: the router, the security headers, the request log and the graceful shutdown.
Package httpserver is the HTTP entry of ermite: the router, the security headers, the request log and the graceful shutdown.
adapters/kvstore
Package kvstore implements the metadata ports over ermite-kv.
Package kvstore implements the metadata ports over ermite-kv.
adapters/rpc
Package rpc serves the ermite API contract (bnema.dev/ermite-api) with ConnectRPC: handlers that map the messages of the contract to the use cases of app and back, the interceptor that authenticates callers, and the error mapping.
Package rpc serves the ermite API contract (bnema.dev/ermite-api) with ConnectRPC: handlers that map the messages of the contract to the use cases of app and back, the interceptor that authenticates callers, and the error mapping.
adapters/sshsig
Package sshsig signs the commits ermite creates with the instance key (ADR 0022): an ed25519 key, in the SSH signature format Git reads with gpg.format=ssh.
Package sshsig signs the commits ermite creates with the instance key (ADR 0022): an ed25519 key, in the SSH signature format Git reads with gpg.format=ssh.
adapters/web
Package web is the HTML front of ermite: the pages of the read-only showcase, rendered with templ from the data of the ermite API.
Package web is the HTML front of ermite: the pages of the read-only showcase, rendered with templ from the data of the ermite API.
app
Package app holds the use cases of ermite.
Package app holds the use cases of ermite.
clientlimit
Package clientlimit counts failed authentications per client address and finds the address of a request behind trusted proxies.
Package clientlimit counts failed authentications per client address and finds the address of a request behind trusted proxies.
config
Package config parses and validates the server configuration from the environment.
Package config parses and validates the server configuration from the environment.
ports
Package ports declares the interfaces the use cases need from the outside world.
Package ports declares the interfaces the use cases need from the outside world.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL