vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Phase 5: LFS objects, over SSH for writes and the proxy for reads
64ac5071a173e4e9ce8bc8d7170f9b0411bee52a · Keenan Nemetz · 17d ago · parent 35eaeccf
Phase 5: LFS objects, over SSH for writes and the proxy for reads Large files leave the git object database and land in one content-addressed store per instance. Reads are anonymous like clone; writes ride the SSH key that already authenticates the push, so no token exists anywhere. - internal/lfs: sha256-addressed store that verifies content and size before an object becomes visible; parser for the pointer format a client writes - HTTP: POST .../info/lfs/objects/batch for downloads, GET .../objects/<oid> with the object id as ETag; upload over HTTP is refused and names SSH - SSH: git-lfs-transfer speaking the pure SSH protocol (pkt-line), so `git lfs push` needs nothing but the key at the handshake - pre-receive: a pointer whose object is missing, or whose size disagrees with the stored object, fails the push that introduced it; a binary over lfs_warn_bytes is accepted with a `git lfs track` suggestion. Checks run before the protected-branch gate, because an admin's approval bypass is about review, not about whether the commit can be checked out - runs: a finished log becomes a content-addressed object in the same write that records the status, served at /r/<repo>/runs/<id>/log - vole gc [--dry-run] [--grace]: marks pointer targets from every ref in every repository plus archived logs, sweeps the rest. Mark-and-sweep rather than reference counts: a counter that is one too low deletes an artifact, a walk over the refs cannot be wrong about what is reachable
README.md +45−2
| @@ -18,7 +18,7 @@ See [docs/roadmap.md](docs/roadmap.md) for the phased implementation plan. | |||
| 18 | 18 | ||
| 19 | 19 | ## Status | |
| 20 | 20 | ||
| 21 | **Phase 1 is implemented.** Vole is a working forge: writes go over SSH and are | ||
| 21 | **Phases 1–5 are implemented.** Vole is a working forge: writes go over SSH and are | ||
| 22 | 22 | authenticated by your key; reads are open. | |
| 23 | 23 | ||
| 24 | 24 | Working today: | |
| @@ -42,12 +42,22 @@ Working today: | |||
| 42 | 42 | branch, validated at push time and rendered read-only on the web | |
| 43 | 43 | - Review as a git artifact: an approval names the exact commit it reviewed, and | |
| 44 | 44 | `git push` is the merge, gated by `pre-receive` | |
| 45 | - CI: workflows in `.vole/workflows/*.yaml`, validated by the push that introduces | ||
| 46 | them. A push queues jobs; `vole runner` leases one over SSH with the same key, runs | ||
| 47 | the shell steps, heartbeats and reports the log. Protected branches can require | ||
| 48 | green checks as well as approvals | ||
| 49 | - LFS: large files stay out of the git object database and live in one | ||
| 50 | content-addressed store per instance. Uploads ride the SSH key | ||
| 51 | (`git-lfs-transfer`); downloads come through the HTTP clone URL, anonymously for a | ||
| 52 | public repository. A pointer whose object is missing fails the push that introduced | ||
| 53 | it, and `vole gc` sweeps what nothing reaches | ||
| 45 | 54 | - Repo create / rename / delete from the CLI | |
| 46 | 55 | - A derived SQLite index that can be deleted and rebuilt from the repos | |
| 47 | 56 | - An instance ed25519 key generated at first boot (used for metadata signing in | |
| 48 | 57 | Phase 7) | |
| 49 | 58 | ||
| 50 | Not yet: change requests, CI, LFS, static sites, signature verification. | ||
| 59 | Not yet: workflow artifact uploads, static site hosting, signature verification, LFS | ||
| 60 | locking. | ||
| 51 | 61 | ||
| 52 | 62 | ## Try it | |
| 53 | 63 | ||
| @@ -170,6 +180,39 @@ admin can, and that is audited). The names in `meta.json` — `author` on an iss | |||
| 170 | 180 | request — are display fields and carry no authority; the approval path is the only | |
| 171 | 181 | name that decides who may land a commit. | |
| 172 | 182 | ||
| 183 | ## Large files | ||
| 184 | |||
| 185 | Track a pattern with git-lfs as usual; the only thing that changes is where the bytes | ||
| 186 | go. Vole answers `git-lfs-transfer` on the SSH connection it already trusts, so there | ||
| 187 | is no token to mint and nothing extra to configure: | ||
| 188 | |||
| 189 | ```sh | ||
| 190 | git lfs install # once per machine | ||
| 191 | git lfs track "*.psd" | ||
| 192 | git add .gitattributes logo.psd | ||
| 193 | git commit -m "add the logo" && git push origin main | ||
| 194 | |||
| 195 | git clone http://host/git/demo.git # LFS objects come through the same URL | ||
| 196 | ``` | ||
| 197 | |||
| 198 | Repositories keep the pointer; Vole keeps the object, once, however many repositories | ||
| 199 | use it. Configure the sizes in `vole.toml`: | ||
| 200 | |||
| 201 | ```toml | ||
| 202 | lfs_object_max_bytes = 2147483648 # one object; larger is refused, not spooled | ||
| 203 | lfs_warn_bytes = 5242880 # a binary this big committed raw gets a warning | ||
| 204 | ``` | ||
| 205 | |||
| 206 | A push that lands a binary over `lfs_warn_bytes` without LFS is accepted with the | ||
| 207 | `git lfs track` command to fix it; a push that commits a pointer whose object was never | ||
| 208 | uploaded is refused, because nobody could check that commit out. Objects are swept | ||
| 209 | only when nothing reaches them: | ||
| 210 | |||
| 211 | ```sh | ||
| 212 | vole gc --dry-run # mark from every ref, report what is unreachable | ||
| 213 | vole gc # remove it (objects younger than --grace stay) | ||
| 214 | ``` | ||
| 215 | |||
| 173 | 216 | ## Backup | |
| 174 | 217 | ||
| 175 | 218 | Two things, and both are small: | |