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/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.

Code

396 commit(s) on main

Turn off the disk reserve for test-spawned runners Keenan Nemetz · 53m ago c284cb2c
source preview history blame raw 4.2 KiB

Sites

A website can be a branch. That keeps publishing simple and keeps the files with the repository. Vole serves a repository’s pages branch at <repo>.<pages_domain>. There is no build step on the server: change the branch and the next request sees the new files.

Publish a build directory

Most websites are generated into a directory such as dist/. Vole can publish that directory as a snapshot, so the branch contains exactly those files. If you delete a page from the build, it stops being served.

npm run build
vole pages deploy dist              # publish dist/ to the pages branch
vole pages deploy dist --dry-run    # show changes without pushing
vole pages status                   # show the current branch and files
vole pages unpublish                # take the site down

Publishing the same files twice does nothing. Use --branch if the instance serves a different branch than pages.

Dotfiles such as .nojekyll and .well-known/ are published. Vole skips .git and symbolic links. A site is just files, and a link pointing outside the build directory would publish something you did not choose.

Publish by hand

Plain Git is enough:

git switch --orphan pages
printf '<h1>hello</h1>\n' > index.html
git add index.html
git commit -m "site"
git push origin pages        # live at https://demo.pages.example.com/

Turn sites on

pages_domain      = "pages.example.com"   # <repo>.pages.example.com
pages_ref         = "pages"               # branch to serve
pages_procs       = 4                     # Git reader processes per active site
pages_cache_bytes = 33554432              # 32 MiB of cached files

Set pages_domain to enable websites. If you change pages_ref, pass the same branch name to vole pages deploy --branch.

How sites are served

  • A directory serves its index.html. A directory URL without a trailing slash redirects to the version with one.
  • Vole uses the file’s Git hash as its ETag and the commit time as Last-Modified, so unchanged pages return 304.
  • Vole checks the branch and repository visibility once a second, so changes take effect within that window. The repository header links to the site only once the pages branch exists and the repository is public; until then there is no Site button, because the link would lead to a 404.
  • A private repository has no public website. There is no logged-in reader, so the site returns 404, just like its clone URL.

With automatic HTTPS enabled (https_addr), Vole obtains a certificate for each site host the first time it is requested, as long as the repository exists and is publicly readable. A wildcard DNS record pointing at the instance is enough; wildcard certificates are not supported, so each host is issued separately. Without https_addr, terminate TLS in a proxy in front of http_addr and keep the Host header: it is how Vole knows which site was requested. See Configuration § Automatic HTTPS with ACME and Configuration § Running behind a proxy.

A workflow can publish the site as its last step. See Workflows § Publish a website.

Same-site risk

A site is content somebody else wrote, served from a hostname you control. If pages_domain shares a registrable domain with the forge — forge.example.com and *.pages.example.com, say — then every site is same-site with the forge, and a malicious site can:

  • make the browser send the forge’s SameSite=Lax cookies on a top-level navigation (Vole’s writes still need a CSRF token, so this does not authorize anything on its own, but it is the assumption a weaker application would rely on);
  • open the forge in a window or frame and interact with it as a same-site document, which is why Cross-Origin-Opener-Policy and the CSP frame rules are set on every page;
  • share a cookie domain if you ever set one wider than a host.

Use a separate registrable domain for sites when the repositories publishing them are not all trusted: pages.example.net for a forge at forge.example.com. The two then cannot be same-site. If the sites are as trusted as the forge itself, a subdomain is fine and keeps certificate management to one domain.