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.
Packages
A release can be installed with the system package manager instead of unpacking a
tarball. Pushing an annotated tag builds the .deb and the .rpm for amd64 and
arm64 and attaches them to the release page; nothing is published anywhere else.
dpkg -i vole_0.2.0-1_amd64.deb # Debian, Ubuntu
dnf install ./vole-0.2.0-1.x86_64.rpm # Fedora, RHEL, Rocky
There is no apt or dnf repository and no signature on these files yet. Each release
page also carries SHA256SUMS; compare it before installing.
To have a fresh cloud machine install and configure the package for you, use the
deployment guides. They need the URL of the file above, which
is how a .deb from a release page is named:
.../vole/releases/v0.2.0/artifacts/vole_0.2.0-1_amd64.deb, or arm64 for an
arm64 machine.
Both packages depend on git and the system SQLite library: libsqlite3-0 on
Debian and Ubuntu, sqlite-libs on Fedora, RHEL, and Rocky. Vole links SQLite
dynamically, so the library must be present where the binary runs; the package
manager installs it with the package. The system library must include SQLite’s
FTS5 module, which every distribution listed above enables in its build; the
command palette is what needs it, and the binary carries the tag that asks for it
when the driver’s bundled copy is used instead.
What the package installs
| Path | What it is |
|---|---|
/usr/bin/vole |
The binary, built from the tag with vole version set to it |
/usr/lib/systemd/system/vole.service |
The unit, with hardening and a /var/lib/vole state directory |
/usr/lib/systemd/system/vole-runner.service |
The CI runner’s unit. Not enabled, and inert until /etc/vole/runner.env names an instance |
/etc/vole/runner.env |
Arguments for that unit, as a conffile. See Deploying § CI runners |
/etc/containers/registries.conf.d/50-vole-runner.conf |
Where a short image name is looked up, as a conffile. Debian and Ubuntu ship no unqualified search registries, so a digest-pinned base written as a short name cannot resolve without it. See Deploying § CI runners |
/etc/containers/containers.conf.d/50-vole-runner.conf |
cgroup_manager = "cgroupfs" for the runner’s rootless runtime, as a conffile. A system service has no systemd user manager for podman to ask for a cgroup scope. See Deploying § CI runners |
/etc/vole/vole.toml |
Configuration, marked as a conffile so your edits survive upgrades |
/usr/share/doc/vole/ |
LICENSE and NOTICE |
/var/lib/vole/data |
The instance: repositories, databases, stores, host key, audit log |
The package creates the vole service account and runs vole admin init against
the shipped config. It does not create an instance admin and does not enable the
service: who may administer the instance is the operator’s decision, not the
package’s.
The shipped config listens on 0.0.0.0:2222 for SSH and 127.0.0.1:8080 for the
web. Open 2222 in the firewall, set public_url before anyone signs in with a
passkey, and either terminate TLS in a proxy in front of 8080 or use https_addr
to let Vole obtain certificates itself. See
Configuration § Automatic HTTPS with ACME
and Configuration § Running behind a proxy.
The unit is written for the packaged layout and is safe to use as shipped: it
drops all capabilities, sets UMask=0027, gives the service a private /tmp and
device tree, hides other processes, and makes /var/lib/vole the only writable
path. If you need to bind a port below 1024 — SSH on 2222 does not, HTTP on 80 and
HTTPS on 443 do — add the one capability with a drop-in, which must widen the
bounding set as well:
# /etc/systemd/system/vole.service.d/bind.conf
[Service]
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
MemoryDenyWriteExecute=true is safe for the forge because it runs no JIT. A CI
runner executes repository code in containers and is a separate process with its
own unit; do not relax this unit to accommodate it.
First steps after install
vole admin --config /etc/vole/vole.toml account create you --key ~/.ssh/id_ed25519.pub
vole admin --config /etc/vole/vole.toml repo create demo --owner you
vole admin --config /etc/vole/vole.toml acl set-default-role demo read
systemctl enable --now vole
Run admin commands as the vole user or with --config /etc/vole/vole.toml; the
service account owns the files they write. A command that writes as the wrong user
leaves state the service cannot open, so prefer:
sudo -u vole vole admin --config /etc/vole/vole.toml account list
Upgrading
users.db is migrated in place the first time a newer binary opens it, and the
migration cannot be rolled back. Back up before upgrading:
sudo -u vole vole admin --config /etc/vole/vole.toml account backup /var/lib/vole/users.db.bak
sudo cp /var/lib/vole/data/ssh_host_ed25519_key /var/lib/vole/ssh_host_ed25519_key.bak
Then install the new package. See Operations for what else is worth copying.
How the release is built
.vole/workflows/release.yaml runs on a v* tag and does three things: it runs
the tests, it cross-compiles the binary for both architectures, and it packages
each one with nfpm from packaging/nfpm.yaml. The
.deb, the .rpm, the plain binaries, and SHA256SUMS are attached to the tag’s
release with release: true, so they appear on the release page like any other
attached file.
Two requirements on the runner:
- Network.
go buildfetches modules, and the packaging step fetches a pinnednfpm, and a job container has none unless the runner was started with--container-network bridge. The image build already needs the network; it installsgit, the C toolchain, and both architectures’ SQLite development files (see.vole/images/release.yaml). - Nothing else. The binaries are cross-compiled, so any runner that can start a Linux container produces the same four packages.
Reproduce a release locally without a runner, from the tag:
git switch --detach v0.2.0
go run ./cmd/vole workflow run release
That runs the steps in your working tree as a check of the commands. It does not
publish the artifacts. The packaging step fetches nfpm itself with a pinned
go run, so there is no tool to install first — only Go and the network.
The version comes from git describe --tags --exact-match, so a checkout with no
tag stops before building anything.
The package definition is checked on every push
The CI workflow (.vole/workflows/verify.yaml) builds one architecture and packages
it with the same packaging/nfpm.yaml on every branch push. A broken definition,
install path, or unit file therefore fails the push that broke it, instead of the
tag that cannot be rebuilt.