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.
Turn a fresh machine into an instance on first boot
04e063ca5d73b8f57f45d7c8bb07b0fdaa91b5b8 · Keenan Nemetz · 3d ago · parent ea21f923
Turn a fresh machine into an instance on first boot A new instance is one package, one configuration, one directory, and one account. Assembling that by hand is where a first deployment goes wrong: the capability the hardened unit needs for ports 80 and 443, the order that makes the package's own init harmless, and the fact that the first instance admin has to be granted on the host. deploy/cloud-init/vole.yaml is that assembly. It stages two configurations, one that lets Vole hold its own certificate and one that leaves TLS to a proxy, then installs the package, writes the configuration, creates the instance admin, and starts the service. Running it again changes nothing. render.sh renders it for providers that take user data without Terraform, using the placeholder list as the contract with the template. The three Terraform configurations create the machine, its firewall, its address, and optionally the DNS record, and hand it that same file. They are root modules: init and apply, three variables, no other file to edit. First boot is not configuration management. apply does not upgrade an instance or touch its data, because users.db migrates in place and cannot be rolled back.
deploy/cloud-init/render.sh +67
| @@ -0,0 +1,67 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # Render deploy/cloud-init/vole.yaml for one instance, for the terminals that do | ||
| 3 | # not use Terraform. | ||
| 4 | # | ||
| 5 | # Usage: | ||
| 6 | # | ||
| 7 | # HOSTNAME=forge.example.com \ | ||
| 8 | # ADMIN_ACCOUNT=you \ | ||
| 9 | # ADMIN_EMAIL=you@example.com \ | ||
| 10 | # ADMIN_SSH_KEY="$(cat ~/.ssh/id_ed25519.pub)" \ | ||
| 11 | # VOLE_PACKAGE_URL=https://example.com/r/vole/releases/v0.2.0/artifacts/vole_0.2.0-1_amd64.deb \ | ||
| 12 | # ./render.sh > user-data.yaml | ||
| 13 | # | ||
| 14 | # Unset values fall back to the defaults below. INSTANCE_NAME defaults to "Vole" | ||
| 15 | # and TLS_MODE to "acme"; set TLS_MODE=proxy to leave TLS to a proxy you run. | ||
| 16 | # | ||
| 17 | # The output is what a cloud provider's "user data" field wants. See | ||
| 18 | # docs/deploy/vps.md for providers that accept it through their web console. | ||
| 19 | set -eu | ||
| 20 | |||
| 21 | here="$(cd "$(dirname "$0")" && pwd)" | ||
| 22 | |||
| 23 | : "${HOSTNAME:?set HOSTNAME to the DNS name the instance will answer on}" | ||
| 24 | : "${ADMIN_ACCOUNT:?set ADMIN_ACCOUNT to the account name to create}" | ||
| 25 | : "${ADMIN_SSH_KEY:?set ADMIN_SSH_KEY to the public key of that account}" | ||
| 26 | : "${VOLE_PACKAGE_URL:?set VOLE_PACKAGE_URL to the .deb for this machine's architecture}" | ||
| 27 | : "${ADMIN_EMAIL:=}" | ||
| 28 | : "${INSTANCE_NAME:=Vole}" | ||
| 29 | : "${TLS_MODE:=acme}" | ||
| 30 | |||
| 31 | case "$TLS_MODE" in | ||
| 32 | acme | proxy) ;; | ||
| 33 | *) | ||
| 34 | echo "TLS_MODE must be acme or proxy, got $TLS_MODE" >&2 | ||
| 35 | exit 2 | ||
| 36 | ;; | ||
| 37 | esac | ||
| 38 | |||
| 39 | if ! command -v envsubst >/dev/null 2>&1; then | ||
| 40 | echo "envsubst is missing; install gettext (Debian and Ubuntu: apt install gettext-base)" >&2 | ||
| 41 | exit 2 | ||
| 42 | fi | ||
| 43 | |||
| 44 | # Only the placeholders are substituted. A name outside this list is a typo, and | ||
| 45 | # envsubst would silently leave it in the output as literal text, so the list is | ||
| 46 | # the contract between this script and the template. | ||
| 47 | export hostname="$HOSTNAME" | ||
| 48 | export admin_account="$ADMIN_ACCOUNT" | ||
| 49 | export admin_ssh_key="$ADMIN_SSH_KEY" | ||
| 50 | export admin_email="$ADMIN_EMAIL" | ||
| 51 | export instance_name="$INSTANCE_NAME" | ||
| 52 | export tls_mode="$TLS_MODE" | ||
| 53 | export vole_package_url="$VOLE_PACKAGE_URL" | ||
| 54 | |||
| 55 | placeholder_list='${hostname} ${admin_account} ${admin_ssh_key} ${admin_email} ${instance_name} ${tls_mode} ${vole_package_url}' | ||
| 56 | |||
| 57 | rendered="$(envsubst "$placeholder_list" <"$here/vole.yaml")" | ||
| 58 | |||
| 59 | # A placeholder means the caller's value did not arrive, and the instance would | ||
| 60 | # boot with the literal text in its configuration. | ||
| 61 | if printf '%s\n' "$rendered" | grep -q '\${'; then | ||
| 62 | echo "unsubstituted placeholder in the rendered file:" >&2 | ||
| 63 | printf '%s\n' "$rendered" | grep -n '\${' >&2 | ||
| 64 | exit 1 | ||
| 65 | fi | ||
| 66 | |||
| 67 | printf '%s\n' "$rendered" | ||