Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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: