vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Phase 7 slice 3: artifacts attach over SSH and are one pointer deep
85cc89fe9eafd1793702d143ea2eb41db635fbe2 · Keenan Nemetz · 17d ago · parent c56911e2
Phase 7 slice 3: artifacts attach over SSH and are one pointer deep A released binary is a pointer blob at releases/<tag>/<name> on the metadata branch. That is the whole record, and it was chosen because the machinery that already exists does the work: gc marks LFS objects named by pointers in any tree reachable from any ref (refs/heads/vole is one), and pre-receive's pointer hygiene already refuses a pointer whose bytes are missing. A JSON manifest of sha256 strings would have parsed as no pointer and been swept once the grace period passed. - cmd/vole attach speaks the pure-SSH LFS transfer protocol itself (version, batch, put-object, verify-object), so bytes move over the key that already writes: no token, no second endpoint, no new write surface - upload happens before the pointer commit, because the reverse order publishes a download that 404s and receive-pack cannot see bytes that were never in the pack. A test pushes a pointer with no object and asserts the push is refused - the artifact list is read from the pointer tree (internal/releases), which the metadata validator now knows about; a file under releases/ that is not a pointer is refused at push instead of silently unservable - downloads: /r/<repo>/releases/<tag>/artifacts/<name> out of the store, ETag = object id, always Content-Disposition: attachment with nosniff, ServeContent for Range and 304, clone read rule so private artifacts are 404 - the "not on this server" hook message now names vole release attach as well as git lfs push, because it fires for both now Measured end to end through the binary: two 2.9 MiB files attached over SSH in 390 ms, the identical second file deduplicating to one object; both downloaded in 13.9 ms with matching sha256, 304 on If-None-Match, 206 on Range; vole gc --dry-run reports the published artifact reachable and removes nothing. The planned releases/<tag>.json record and drafts are dropped with it: the pointer directory is the artifact list and being in the ref is what published means, so the file would only have duplicated the tag.
README.md +32−8
| @@ -52,10 +52,12 @@ Working today: | |||
| 52 | 52 | (`git-lfs-transfer`); downloads come through the HTTP clone URL, anonymously for a | |
| 53 | 53 | public repository. A pointer whose object is missing fails the push that introduced | |
| 54 | 54 | it, and `vole gc` sweeps what nothing reaches | |
| 55 | - Releases are tags: the tag message is the notes and `.tar` / `.tar.gz` are generated | ||
| 56 | from the tagged tree per request, with an `ETag` from the tag object and a `304` for | ||
| 57 | repeat downloads. `vole release create` writes the annotated tag; `git tag -a` does | ||
| 58 | the same thing without the CLI | ||
| 55 | - Releases are tags: the tag message is the notes, `.tar` / `.tar.gz` are generated from | ||
| 56 | the tagged tree per request (with an `ETag` from the tag object and a `304` for repeat | ||
| 57 | downloads), and binaries travel as LFS pointers on the `vole` branch — `vole release | ||
| 58 | attach` uploads them over the same SSH key and the store keeps one copy of identical | ||
| 59 | bytes. `vole release create` writes the annotated tag; `git tag -a` does the same thing | ||
| 60 | without the CLI | ||
| 59 | 61 | - Repo create / rename / delete from the CLI | |
| 60 | 62 | - A derived SQLite index that can be deleted and rebuilt from the repos | |
| 61 | 63 | - Static sites: a repository's `pages` branch is served as a website at | |
| @@ -64,8 +66,8 @@ Working today: | |||
| 64 | 66 | - An instance ed25519 key generated at first boot (used for metadata signing in | |
| 65 | 67 | Phase 7) | |
| 66 | 68 | ||
| 67 | Not yet: release artifacts (binaries attached to a release), workflow artifact uploads, | ||
| 68 | LFS locking. | ||
| 69 | Not yet: workflow artifact uploads, LFS locking, and release immutability (a tag that has | ||
| 70 | artifacts can still be moved until Phase 7's last slice lands). | ||
| 69 | 71 | Signature verification is deferred, | |
| 70 | 72 | not planned: the SSH handshake already establishes who pushed, so signing approvals | |
| 71 | 73 | would restate it for a verifier this deployment does not have (see the roadmap). | |
| @@ -253,8 +255,30 @@ tag object id, `If-None-Match` gets a `304`, and the same tag yields the same by | |||
| 253 | 255 | every instance. Measured on an 11.6 MiB, 2,566-file repository: 54 ms for `.tar`, 218 ms | |
| 254 | 256 | for `.tar.gz`, and every extracted file checks out against the tree. A repository that | |
| 255 | 257 | tracks files with Git LFS gets a warning on the page, because `git archive` copies | |
| 256 | pointers rather than their contents. Binaries attached to a release are not built yet | ||
| 257 | (see the roadmap). | ||
| 258 | pointers rather than their contents. | ||
| 259 | |||
| 260 | Binaries attach to the release the same way large files move everywhere else — over SSH, | ||
| 261 | into the content-addressed store, with a pointer committed for them: | ||
| 262 | |||
| 263 | ```sh | ||
| 264 | vole release attach v1.0.0 dist/vole-linux-amd64 dist/vole-darwin-arm64 | ||
| 265 | curl -O http://host/r/demo/releases/v1.0.0/artifacts/vole-linux-amd64 | ||
| 266 | sha256sum vole-linux-amd64 | ||
| 267 | ``` | ||
| 268 | |||
| 269 | Attaching byte-identical files twice stores one object and transfers nothing the second | ||
| 270 | time. Downloads carry the object id as `ETag` (so `304` works), `Content-Disposition: | ||
| 271 | attachment` with `nosniff` (an artifact never renders on the forge's origin), and Range | ||
| 272 | support, so a 100 MiB download resumes. The read rule is the clone rule: artifacts of a | ||
| 273 | private repository are `404`. Attach *after* pushing the tag, not before: the pointer is | ||
| 274 | what publishes the bytes, and a pointer whose object was never uploaded is refused by | ||
| 275 | `pre-receive` rather than advertised as a download. | ||
| 276 | |||
| 277 | Artifacts are reachable by `vole gc` because the pointers live on the `vole` branch: | ||
| 278 | |||
| 279 | ```sh | ||
| 280 | vole gc --dry-run # the published artifact is a reachable root | ||
| 281 | ``` | ||
| 258 | 282 | ||
| 259 | 283 | ## Hosting a site | |
| 260 | 284 | ||