detached 7586fbd

docs(readme): document merge requests, web sign-in and the signing key

commit
7586fbd9d4ca4dbd6ce176002db9321591d12729
author
brice · · 2026-10-10 19:36 UTC
parent
41cf2ec
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +18 -2
M README.md +18 -2
@@ -1,8 +1,8 @@
11 # ermite
22
3-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.
3+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.
44
5-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.
5+Status: early development. `ermite serve` serves Git over HTTP (clone, fetch, push), the API, `GET /-/health`, and the web: repositories, tree, 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.
66
77 ## Run
88
@@ -24,6 +24,8 @@ ermite serve
2424 | `ERMITE_SOURCE_URL` | `$ERMITE_PUBLIC_URL/ermite` | link to the source (AGPL) |
2525 | `ERMITE_LOG_LEVEL` | `info` | `trace`, `debug`, `info`, `warn`, `error` |
2626 | `ERMITE_LOG_FORMAT` | `json` | `json` or `console` |
27+| `ERMITE_COMMITTER_NAME` | `ermite` | committer of squash merges |
28+| `ERMITE_COMMITTER_EMAIL` | `ermite@<public host>` | committer email of squash merges |
2729
2830 `ermite version` prints the version (and the server's when `ERMITE_URL` is set).
2931
@@ -36,6 +38,12 @@ export ERMITE_URL=https://git.example.com ERMITE_TOKEN=ermite_...
3638 ermite repo list | show <name> | create <name> [--description …] [--visibility public] | edit <name> [--description …] [--visibility …] [--default-branch …] | delete <name> [--yes]
3739 ermite token create <label> [--preset cli|agent|ci] [--scope repo:read …] [--repo name …] [--expires 30d] # prints the secret once
3840 ermite token list | revoke <id>
41+ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff]
42+ermite mr comment <repo> <n> -m … [--path … --line … --side old|new] | resolve <repo> <n> <comment>
43+ermite mr merge <repo> <n> --head <id> [--squash] | close <repo> <n>
44+ermite web login # prints a one-time sign-in link (60 s)
45+ermite web logout --all # signs every browser out
46+ermite signing-key show # public key and allowed_signers line of squash commits
3947 ermite version # client and server, warns about missing server features
4048 ermite watch [repo] [--json] # follows events, reconnects and resumes
4149 ```
@@ -51,6 +59,14 @@ git push http://u:<token>@git.example.com/<repo>.git main
5159
5260 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`).
5361
62+## Merge requests
63+
64+A merge request proposes a branch for its target (the default branch unless set). The merge is refused when the branch moved since the reviewed head (`--head`) or when the target is not an ancestor of it. Fast-forward moves the target; squash writes one commit signed by the server's SSH key (`ermite signing-key show` prints the `allowed_signers` line for `git verify-commit`). `ermite repo edit --merge-modes` limits the modes per repository. Tokens need `mr:write` to open and comment, and `mr:merge` to merge.
65+
66+## Web sign-in
67+
68+`ermite web login`, run with the admin token or a token with every scope, prints a link that works once within 60 seconds. Opening it and confirming sets a session cookie for 12 hours (never longer than the token that asked for the link). It needs `https` in `ERMITE_PUBLIC_URL` (except on localhost). Browser writes require the session and an `Origin` equal to `ERMITE_PUBLIC_URL`. `ermite web logout --all`, or revoking a token with every scope, ends every session.
69+
5470 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.
5571
5672 ## Run with Docker
@@ -1,8 +1,8 @@
1 # ermite1 # ermite
2 2
3-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.3+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.
4 4
5-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.5+Status: early development. `ermite serve` serves Git over HTTP (clone, fetch, push), the API, `GET /-/health`, and the web: repositories, tree, 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.
6 6
7 ## Run7 ## Run
8 8
@@ -24,6 +24,8 @@ ermite serve
24 | `ERMITE_SOURCE_URL` | `$ERMITE_PUBLIC_URL/ermite` | link to the source (AGPL) |24 | `ERMITE_SOURCE_URL` | `$ERMITE_PUBLIC_URL/ermite` | link to the source (AGPL) |
25 | `ERMITE_LOG_LEVEL` | `info` | `trace`, `debug`, `info`, `warn`, `error` |25 | `ERMITE_LOG_LEVEL` | `info` | `trace`, `debug`, `info`, `warn`, `error` |
26 | `ERMITE_LOG_FORMAT` | `json` | `json` or `console` |26 | `ERMITE_LOG_FORMAT` | `json` | `json` or `console` |
27+| `ERMITE_COMMITTER_NAME` | `ermite` | committer of squash merges |
28+| `ERMITE_COMMITTER_EMAIL` | `ermite@<public host>` | committer email of squash merges |
27 29
28 `ermite version` prints the version (and the server's when `ERMITE_URL` is set).30 `ermite version` prints the version (and the server's when `ERMITE_URL` is set).
29 31
@@ -36,6 +38,12 @@ export ERMITE_URL=https://git.example.com ERMITE_TOKEN=ermite_...
36 ermite repo list | show <name> | create <name> [--description …] [--visibility public] | edit <name> [--description …] [--visibility …] [--default-branch …] | delete <name> [--yes]38 ermite repo list | show <name> | create <name> [--description …] [--visibility public] | edit <name> [--description …] [--visibility …] [--default-branch …] | delete <name> [--yes]
37 ermite token create <label> [--preset cli|agent|ci] [--scope repo:read …] [--repo name …] [--expires 30d] # prints the secret once39 ermite token create <label> [--preset cli|agent|ci] [--scope repo:read …] [--repo name …] [--expires 30d] # prints the secret once
38 ermite token list | revoke <id>40 ermite token list | revoke <id>
41+ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff]
42+ermite mr comment <repo> <n> -m … [--path … --line … --side old|new] | resolve <repo> <n> <comment>
43+ermite mr merge <repo> <n> --head <id> [--squash] | close <repo> <n>
44+ermite web login # prints a one-time sign-in link (60 s)
45+ermite web logout --all # signs every browser out
46+ermite signing-key show # public key and allowed_signers line of squash commits
39 ermite version # client and server, warns about missing server features47 ermite version # client and server, warns about missing server features
40 ermite watch [repo] [--json] # follows events, reconnects and resumes48 ermite watch [repo] [--json] # follows events, reconnects and resumes
41 ```49 ```
@@ -51,6 +59,14 @@ git push http://u:<token>@git.example.com/<repo>.git main
51 59
52 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`).60 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`).
53 61
62+## Merge requests
63+
64+A merge request proposes a branch for its target (the default branch unless set). The merge is refused when the branch moved since the reviewed head (`--head`) or when the target is not an ancestor of it. Fast-forward moves the target; squash writes one commit signed by the server's SSH key (`ermite signing-key show` prints the `allowed_signers` line for `git verify-commit`). `ermite repo edit --merge-modes` limits the modes per repository. Tokens need `mr:write` to open and comment, and `mr:merge` to merge.
65+
66+## Web sign-in
67+
68+`ermite web login`, run with the admin token or a token with every scope, prints a link that works once within 60 seconds. Opening it and confirming sets a session cookie for 12 hours (never longer than the token that asked for the link). It needs `https` in `ERMITE_PUBLIC_URL` (except on localhost). Browser writes require the session and an `Origin` equal to `ERMITE_PUBLIC_URL`. `ermite web logout --all`, or revoking a token with every scope, ends every session.
69+
54 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.70 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.
55 71
56 ## Run with Docker72 ## Run with Docker

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 45

↑↓ moveenter openesc close