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 6 slice 1: the tree is the site
d3296cda0c18f78097e37373822292707113d245 · Keenan Nemetz · 17d ago · parent 64ac5071
Phase 6 slice 1: the tree is the site A repository's `pages` branch is served at <repo>.<pages_domain>, straight out of git. No export step, no build to drift: the push moves the branch and the next request reads the new tree. - internal/pages: a pool of `git cat-file --batch` processes per active repository (closed after two idle minutes), an LRU keyed by (commit, path), and tree parsing in git's binary format with the object-id width detected rather than assumed - ETag is the blob sha, Last-Modified the commit time, Content-Type from the extension or a sniff; If-None-Match and If-Modified-Since answer 304 - directories serve index.html and redirect to add the trailing slash; path resolution walks trees, so a traversal-shaped request is a missing page - repository and ACL are resolved once per one-second memo window together with the branch tip, which is what makes the read path fast: doing it per request spawned three git processes and cost 3.8 ms, against 6.7 µs warm - sites follow clone visibility, so a private repository answers 404; the repo page links to the site when pages_domain is configured Measured: a site pushed over SSH is live in 1.4 s (the phase criterion is 5 s); warm requests 6.7 µs, cold 144 µs. Custom domains and their TLS certificates are the remaining Phase 6 work.
README.md +33−2
| @@ -53,11 +53,14 @@ Working today: | |||
| 53 | 53 | it, and `vole gc` sweeps what nothing reaches | |
| 54 | 54 | - Repo create / rename / delete from the CLI | |
| 55 | 55 | - A derived SQLite index that can be deleted and rebuilt from the repos | |
| 56 | - Static sites: a repository's `pages` branch is served as a website at | ||
| 57 | `<repo>.<pages_domain>`, straight out of git — no export step, ETags from blob | ||
| 58 | shas, `304`s for unchanged pages | ||
| 56 | 59 | - An instance ed25519 key generated at first boot (used for metadata signing in | |
| 57 | 60 | Phase 7) | |
| 58 | 61 | ||
| 59 | Not yet: workflow artifact uploads, static site hosting, signature verification, LFS | ||
| 60 | locking. | ||
| 62 | Not yet: custom site domains and their TLS certificates, workflow artifact uploads, | ||
| 63 | signature verification, LFS locking. | ||
| 61 | 64 | ||
| 62 | 65 | ## Try it | |
| 63 | 66 | ||
| @@ -213,6 +216,34 @@ vole gc --dry-run # mark from every ref, report what is unreachable | |||
| 213 | 216 | vole gc # remove it (objects younger than --grace stay) | |
| 214 | 217 | ``` | |
| 215 | 218 | ||
| 219 | ## Hosting a site | ||
| 220 | |||
| 221 | A repository's `pages` branch is a website at `<repo>.<pages_domain>`. Nothing is | ||
| 222 | built or exported: the push moves the branch, and the next request reads the new tree. | ||
| 223 | |||
| 224 | ```sh | ||
| 225 | git switch --orphan pages | ||
| 226 | printf '<h1>hello</h1>\n' > index.html | ||
| 227 | git add index.html && git commit -m "site" | ||
| 228 | git push origin pages # live at https://demo.pages.example.com/ | ||
| 229 | ``` | ||
| 230 | |||
| 231 | Turn it on in `vole.toml`: | ||
| 232 | |||
| 233 | ```toml | ||
| 234 | pages_domain = "pages.example.com" # <repo>.pages.example.com | ||
| 235 | pages_ref = "pages" # the branch to serve | ||
| 236 | pages_procs = 4 # cat-file processes per active site | ||
| 237 | pages_cache_bytes = 33554432 # 32 MiB of blobs, `(commit, path)` keyed | ||
| 238 | ``` | ||
| 239 | |||
| 240 | A directory serves its `index.html`; a directory URL without a trailing slash | ||
| 241 | redirects. Responses carry the blob sha as `ETag` and the commit time as | ||
| 242 | `Last-Modified`, so unchanged pages cost a `304`. A private repository has no site: | ||
| 243 | there is no session for an anonymous reader to authenticate with, so it answers `404` | ||
| 244 | exactly like its clone URL does. Visibility and the branch tip are re-read once a | ||
| 245 | second, so publishing or unpublishing a site takes effect within that window. | ||
| 246 | |||
| 216 | 247 | ## Backup | |
| 217 | 248 | ||
| 218 | 249 | Two things, and both are small: | |