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.
|
520470e1
Keenan Nemetz
16d ago
|
1 | # Sites |
| 2 | ||
| 3 | A website can be a branch. That keeps publishing simple and keeps the files with | |
|
ab326d3a
Keenan Nemetz
14d ago
|
4 | the repository. Vole serves a repository's `pages` branch at `<repo>.<pages_domain>`. |
|
520470e1
Keenan Nemetz
16d ago
|
5 | There is no build step on the server: change the branch and the next request sees |
| 6 | the new files. | |
| 7 | ||
| 8 | ## Publish a build directory | |
| 9 | ||
| 10 | Most websites are generated into a directory such as `dist/`. Vole can publish | |
| 11 | that directory as a snapshot, so the branch contains exactly those files. If you | |
| 12 | delete a page from the build, it stops being served. | |
| 13 | ||
| 14 | ```sh | |
| 15 | npm run build | |
| 16 | vole pages deploy dist # publish dist/ to the pages branch | |
| 17 | vole pages deploy dist --dry-run # show changes without pushing | |
| 18 | vole pages status # show the current branch and files | |
| 19 | vole pages unpublish # take the site down | |
| 20 | ``` | |
| 21 | ||
| 22 | Publishing the same files twice does nothing. Use `--branch` if the instance | |
| 23 | serves a different branch than `pages`. | |
| 24 | ||
| 25 | Dotfiles such as `.nojekyll` and `.well-known/` are published. Vole skips `.git` | |
| 26 | and symbolic links. A site is just files, and a link pointing outside the build | |
| 27 | directory would publish something you did not choose. | |
| 28 | ||
| 29 | ## Publish by hand | |
| 30 | ||
| 31 | Plain Git is enough: | |
| 32 | ||
| 33 | ```sh | |
| 34 | git switch --orphan pages | |
| 35 | printf '<h1>hello</h1>\n' > index.html | |
| 36 | git add index.html | |
| 37 | git commit -m "site" | |
| 38 | git push origin pages # live at https://demo.pages.example.com/ | |
| 39 | ``` | |
| 40 | ||
| 41 | ## Turn sites on | |
| 42 | ||
| 43 | ```toml | |
| 44 | pages_domain = "pages.example.com" # <repo>.pages.example.com | |
| 45 | pages_ref = "pages" # branch to serve | |
| 46 | pages_procs = 4 # Git reader processes per active site | |
| 47 | pages_cache_bytes = 33554432 # 32 MiB of cached files | |
| 48 | ``` | |
| 49 | ||
| 50 | Set `pages_domain` to enable websites. If you change `pages_ref`, pass the same | |
| 51 | branch name to `vole pages deploy --branch`. | |
| 52 | ||
| 53 | ## How sites are served | |
| 54 | ||
| 55 | - A directory serves its `index.html`. A directory URL without a trailing slash | |
| 56 | redirects to the version with one. | |
| 57 | - Vole uses the file's Git hash as its `ETag` and the commit time as | |
| 58 | `Last-Modified`, so unchanged pages return `304`. | |
| 59 | - Vole checks the branch and repository visibility once a second, so changes take | |
|
816cc543
Keenan Nemetz
11d ago
|
60 | effect within that window. The repository header links to the site only once |
| 61 | the pages branch exists and the repository is public; until then there is no | |
| 62 | Site button, because the link would lead to a 404. | |
|
520470e1
Keenan Nemetz
16d ago
|
63 | - A private repository has no public website. There is no logged-in reader, so the |
| 64 | site returns `404`, just like its clone URL. | |
| 65 | ||
|
642e5994
Keenan Nemetz
14d ago
|
66 | With automatic HTTPS enabled (`https_addr`), Vole obtains a certificate for each |
| 67 | site host the first time it is requested, as long as the repository exists and is | |
| 68 | publicly readable. A wildcard DNS record pointing at the instance is enough; | |
| 69 | wildcard certificates are not supported, so each host is issued separately. Without `https_addr`, terminate | |
| 70 | TLS in a proxy in front of `http_addr` and keep the `Host` header: it is how Vole | |
| 71 | knows which site was requested. See | |
| 72 | [Configuration § Automatic HTTPS with ACME](configuration.md#automatic-https-with-acme) | |
| 73 | and [Configuration § Running behind a proxy](configuration.md#running-behind-a-proxy). | |
|
520470e1
Keenan Nemetz
16d ago
|
74 | |
| 75 | A workflow can publish the site as its last step. See | |
|
70b70264
Keenan Nemetz
15d ago
|
76 | [Workflows § Publish a website](workflows.md#publish-a-website). |
|
701232cf
Keenan Nemetz
12d ago
|
77 | |
| 78 | ## Same-site risk | |
| 79 | ||
| 80 | A site is content somebody else wrote, served from a hostname you control. If | |
| 81 | `pages_domain` shares a registrable domain with the forge — `forge.example.com` | |
| 82 | and `*.pages.example.com`, say — then every site is *same-site* with the forge, | |
| 83 | and a malicious site can: | |
| 84 | ||
| 85 | - make the browser send the forge's `SameSite=Lax` cookies on a top-level | |
| 86 | navigation (Vole's writes still need a CSRF token, so this does not authorize | |
| 87 | anything on its own, but it is the assumption a weaker application would rely on); | |
| 88 | - open the forge in a window or frame and interact with it as a same-site | |
| 89 | document, which is why `Cross-Origin-Opener-Policy` and the CSP frame rules are | |
| 90 | set on every page; | |
| 91 | - share a cookie domain if you ever set one wider than a host. | |
| 92 | ||
| 93 | Use a separate registrable domain for sites when the repositories publishing them | |
| 94 | are not all trusted: `pages.example.net` for a forge at `forge.example.com`. The | |
| 95 | two then cannot be same-site. If the sites are as trusted as the forge itself, a | |
| 96 | subdomain is fine and keeps certificate management to one domain. |