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 6 slice 1: the tree is the site
d3296cda0c18f78097e37373822292707113d245 · Keenan Nemetz · 18d 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.
internal/config/config.go +24
| @@ -29,6 +29,19 @@ type Config struct { | |||
| 29 | 29 | // LeaseSeconds is how long a runner may hold a job without a heartbeat before | |
| 30 | 30 | // the job is handed to somebody else. | |
| 31 | 31 | LeaseSeconds int `toml:"lease_seconds"` | |
| 32 | // PagesDomain is the suffix a repository's site is served under: | ||
| 33 | // <repo>.<pages_domain>. Empty disables site hosting entirely. | ||
| 34 | PagesDomain string `toml:"pages_domain"` | ||
| 35 | // PagesRef is the branch a site is read from. It is a normal branch, pushed | ||
| 36 | // and reviewed like any other, so there is no export step to drift. | ||
| 37 | PagesRef string `toml:"pages_ref"` | ||
| 38 | // PagesProcs is how many `git cat-file --batch` processes serve blobs. | ||
| 39 | // Reads parallelise across processes: the Phase 0 spike measured 12.1k req/s | ||
| 40 | // with one and 24.8k with four. | ||
| 41 | PagesProcs int `toml:"pages_procs"` | ||
| 42 | // PagesCacheBytes caps the in-memory blob cache. The cache is required, not an | ||
| 43 | // optimisation: cold git reads are ~1.2 ms p50, warm ones 0.075 ms. | ||
| 44 | PagesCacheBytes int64 `toml:"pages_cache_bytes"` | ||
| 32 | 45 | ||
| 33 | 46 | Path string `toml:"-"` | |
| 34 | 47 | } | |
| @@ -43,6 +56,10 @@ func defaults() *Config { | |||
| 43 | 56 | ||
| 44 | 57 | LFSObjectMaxBytes: 2 << 30, // 2 GiB per LFS object | |
| 45 | 58 | LFSWarnBytes: 5 << 20, // flag binaries over 5 MiB that are not in LFS | |
| 59 | |||
| 60 | PagesRef: "pages", | ||
| 61 | PagesProcs: 4, | ||
| 62 | PagesCacheBytes: 32 << 20, // 32 MiB holds several 10k-file sites | ||
| 46 | 63 | } | |
| 47 | 64 | } | |
| 48 | 65 | ||
| @@ -117,6 +134,13 @@ lfs_object_max_bytes = 2147483648 | |||
| 117 | 134 | # pointer, gets a warning naming the git lfs track command. Zero disables it. | |
| 118 | 135 | lfs_warn_bytes = 5242880 | |
| 119 | 136 | ||
| 137 | # Static sites: a repository's pages branch is served at | ||
| 138 | # <repo>.<pages_domain>. Leave empty to serve no sites at all. | ||
| 139 | # pages_domain = "pages.example.com" | ||
| 140 | # pages_ref = "pages" | ||
| 141 | # pages_procs = 4 | ||
| 142 | # pages_cache_bytes = 33554432 | ||
| 143 | |||
| 120 | 144 | # Cap a whole push. Enforced by git's receive.maxInputSize in each repo, because | |
| 121 | 145 | # receive-pack indexes a pack in memory and a large push costs its own size in | |
| 122 | 146 | # RSS. | |