Home

ugit @main - refs - log -
-
https://git.jolheiser.com/ugit.git
The code powering this h*ckin' site
tree log patch
readme: add setup Signed-off-by: jolheiser <git@jolheiser.com>
Signature
-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQH5lR31EN76opZEPbL98u2Mxn5O5r7d/k5LoXDbdgGny3fmVI02/W5wwK/kN02yLZv lyCdImT+mAHV1hT18qUws= -----END SSH SIGNATURE-----
jolheiser <git@jolheiser.com>
7 hours ago
1 changed files, 127 additions(+), 0 deletions(-)
README.md
M README.md -> README.md
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
diff --git a/README.md b/README.md
index 6a5d1c3bd3604103eeb4ab33b812880fe415a2e0..8c188ff355b31ec3a713a3c2c1c1d6f7753f3336 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,133 @@ For issue/bug-tracking, I recommend [git-bug](https://github.com/git-bug/git-bug).
 
 Currently all HTML is allowed in markdown, µgit is intended to be run by/for a trusted user.
 
+## Features
+
+### Push-to-create
+
+Pushing to a repo path that doesn't exist yet creates it automatically, there's no separate "create repo" step.
+Combined with [push options](#push-options), this means a repo can be created and configured in the same command as its first push:
+
+```sh
+git push -o "private=false" ssh://git@example.com:8448/new-repo.git main
+```
+
+### Push options
+
+A handful of `git push -o <key>=<value>` (`--push-option`) options are read on every push to update the repo's metadata, in addition to actually pushing the ref(s):
+
+| Option               | Value                                                       | Effect                                                              |
+|----------------------|-------------------------------------------------------------|---------------------------------------------------------------------|
+| `desc`/`description` | any string                                                  | Sets the repo description                                           |
+| `private`            | `true`/`false`                                              | Sets the repo's `private` tag (see [Configuration](#configuration)) |
+| `tags`               | comma-separated list, prefix an entry with `-` to remove it | Adds/removes tags on the repo, for filtering                        |
+
+For example:
+
+```sh
+git push -o desc="My project" -o "private=false" -o "tags=go,-wip" origin main
+```
+
+Push options can be repeated (`-o key=value -o key2=value2`) and are applied in order; unrecognized options are ignored.
+
+### Search
+
+The web UI's repo search is a lightweight, `git grep`-style regex scan over the files at the tip of the default branch, implemented in-process rather than shelling out to `git`.
+If you need more advanced code search, consider running something like [zoekt](https://github.com/sourcegraph/zoekt) alongside µgit rather than relying on the built-in search.
+
+## Installation
+
+### NixOS
+
+µgit ships a `flake.nix` with a NixOS module that can run one or more independent instances via `services.ugit.<name>`.
+Each instance gets its own user/group, home directory, and systemd service.
+See `nix/module.nix` for the full list of options.
+
+```nix
+{
+  inputs.ugit.url = "git+https://git.jolheiser.com/ugit";
+
+  outputs = { self, nixpkgs, ugit, ... }: {
+    nixosConfigurations.example = nixpkgs.lib.nixosSystem {
+      modules = [
+        ugit.nixosModules.default
+        {
+          services.ugit.public = {
+            enable = true;
+            authorizedKeysFile = "/etc/ugit/authorized_keys";
+            config = {
+              show-private = false;
+              http.port = 8449;
+            };
+          };
+        }
+      ];
+    };
+  };
+}
+```
+
+The `config` option is written out as the instance's YAML config file (see [Configuration](#configuration) below), so any flag documented there can be set through it.
+
+### Everything else
+
+µgit is a single statically-linked Go binary with no runtime dependencies beyond `git` itself on `$PATH`.
+
+Build it from source with a recent Go toolchain: `go build ./cmd/ugitd`, or `go install go.jolheiser.com/ugit/cmd/ugitd@latest`
+
+Run the resulting `ugitd` binary directly, or under your process supervisor of choice (systemd, runit, etc).
+µgit needs write access to its repo directory, SSH host key path, and (if generated) authorized_keys path.
+
+## Configuration
+
+µgit is configured via CLI flags, environment variables, and/or a YAML config file, in that order of precedence.
+
+- CLI flags use the names below, e.g. `--ssh.port=2222`
+- Environment variables are the flag name upper-cased with `.` and `-` replaced by `_`, prefixed with `UGIT_`,
+  e.g. `ssh.port` becomes `UGIT_SSH_PORT`
+- The config file (default `ugit.yaml`, override with `--config`) is YAML, with dotted flag names expanded into
+  nested keys, e.g.
+  ```yaml
+  ssh:
+    port: 2222
+  ```
+
+| Flag                     | Default                     | Description                                                  |
+|--------------------------|-----------------------------|--------------------------------------------------------------|
+| `--config`               | `ugit.yaml`                 | Path to config file                                          |
+| `--repo-dir`             | `.ugit`                     | Path to directory containing repositories                    |
+| `--show-private`         | `false`                     | Show private repos in the web interface                      |
+| `--log.level`            | `error`                     | Logging level: `debug`, `info`, `warn`, `error`              |
+| `--log.json`             | `false`                     | Print logs in JSON(L) format                                 |
+| `--ssh.enable`           | `true`                      | Enable the SSH server                                        |
+| `--ssh.authorized-keys`  | `.ssh/authorized_keys`      | Path to `authorized_keys`                                    |
+| `--ssh.clone-url`        | `ssh://localhost:8448`      | SSH clone URL base, shown to clients                         |
+| `--ssh.port`             | `8448`                      | SSH port                                                     |
+| `--ssh.host-key`         | `.ssh/ugit_ed25519`         | SSH host key path (created if it doesn't exist)              |
+| `--http.enable`          | `true`                      | Enable the HTTP server                                       |
+| `--http.clone-url`       | `http://localhost:8449`     | HTTP clone URL base, shown to clients                        |
+| `--http.port`            | `8449`                      | HTTP port                                                    |
+| `--meta.title`           | `ugit`                      | App title, shown in the web interface                        |
+| `--meta.description`     | `Minimal git server`        | App description, shown in the web interface                  |
+| `--profile.username`     |                             | Username shown on the index page                             |
+| `--profile.email`        |                             | Email shown on the index page                                |
+| `--profile.links`        |                             | `name,url` pair for the index page; repeat the flag for more |
+
+Each repository also has its own `private` flag, which is just a tag on the repo and is set to `true` by default when a repo is created.
+It has no effect on its own; `--show-private` is what decides whether repos tagged `private` are shown in the web interface, and it also defaults to `false`.
+Out of the box, newly created repos are private and `--show-private` is off, meaning repos are hidden from the web UI by default until you opt in one way or the other.
+Neither setting affects SSH access: any user in `authorized_keys` can always push/pull a repo, private or not.
+
+### Running public and private instances
+
+The setup I run is two `ugit` instances pointed at the same `--repo-dir`:
+
+- a **public** instance with `--show-private=false` (the default), reverse-proxied to the internet
+- a **private** instance with `--show-private=true`, reachable only over Tailscale (or another private network)
+
+Both instances see the same repositories, but only the private instance's web UI will list/serve repos marked `private`.
+Pushing/cloning over SSH is unaffected by `--show-private`, it only gates the web interface.
+
 ## Getting your public SSH keys from another forge
 
 Using GitHub as an example (although Gitea/GitLab should have the same URL scheme)