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.
Store CI run records in the database, not the repository
b312e1660ac3d570fc35350a22b3514c56ab466f · Keenan Nemetz · 11d ago · parent db5e4382
Store CI run records in the database, not the repository A finished attempt was a set of files on the vole branch: a record, step ranges, and artifact pointers, pushed as a commit. At 959 files, CI records were 849 of them, and every human metadata write had to carry them. The runner now sends the record with its finish message over the SSH channel that holds the lease. The server checks the account, the job, and the log and artifact pointers, then writes the attempt, steps, artifacts, attestation, and run result in one transaction in data/runs.db. A tagged build still pushes its release pointers to the branch after the record is stored. Pre-receive refuses a new runs/ file. vole admin index rebuild imports records still on an old branch; records written since are never deleted by a rebuild. The run page reads the stored attempt, and runs.db is now a source that must be backed up. This stops new CI history from entering Git. Existing commits keep the records they already have; removing those would require rewriting the protected vole branch, which this change does not do.
docs/operations.md +26−11
| @@ -51,6 +51,7 @@ Back up the instance state: | |||
| 51 | 51 | ||
| 52 | 52 | ```sh | |
| 53 | 53 | vole admin account backup backup/users.db # accounts, keys, sessions, rules, locks, views | |
| 54 | sqlite3 data/runs.db ".backup 'backup/runs.db'" # CI history, queue, and approvals | ||
| 54 | 55 | cp data/ssh_host_ed25519_key backup/ # the SSH host key | |
| 55 | 56 | cp -a data/acme backup/ 2>/dev/null # TLS account key and certificates, if ACME is on | |
| 56 | 57 | cp data/audit.log backup/ 2>/dev/null # history of admin actions | |
| @@ -58,7 +59,8 @@ cp data/audit.log backup/ 2>/dev/null # history of admin actions | |||
| 58 | 59 | ||
| 59 | 60 | `vole admin account backup` takes a consistent copy, even while Vole is running. Copying | |
| 60 | 61 | `users.db` by hand can miss recent changes that have not been written to the main | |
| 61 | database file yet. | ||
| 62 | database file yet. Do the same for `runs.db` with SQLite's backup command; copying it | ||
| 63 | by hand while the server is writing can miss WAL pages. | ||
| 62 | 64 | ||
| 63 | 65 | If you lose the account database, you need to recreate accounts and re-enroll | |
| 64 | 66 | their passkeys. You do not lose repositories. The backup contains account metadata, | |
| @@ -66,14 +68,17 @@ public keys, and hashes of session and enrollment tokens; it does not contain a | |||
| 66 | 68 | usable password or a plaintext session token. Treat it as private anyway. | |
| 67 | 69 | ||
| 68 | 70 | Repositories are ordinary Git repositories under `data/repos/`. You can back them | |
| 69 | up by copying or cloning. Issues, change requests, releases, and CI records travel | ||
| 70 | with them because they are stored in Git. | ||
| 71 | up by copying or cloning. Issues, change requests, and releases travel with them | ||
| 72 | because they are stored in Git. CI history does not: it lives in `data/runs.db` and | ||
| 73 | must be backed up with it. | ||
| 71 | 74 | ||
| 72 | 75 | ## Restore an instance | |
| 73 | 76 | ||
| 74 | 1. Restore `data/users.db`, `data/ssh_host_ed25519_key`, `data/audit.log`, and | ||
| 75 | `data/acme/` if the instance obtains its own TLS certificates. | ||
| 76 | 2. Restore the repositories under `data/repos/`. | ||
| 77 | 1. Restore `data/users.db`, `data/runs.db`, `data/ssh_host_ed25519_key`, | ||
| 78 | `data/audit.log`, and `data/acme/` if the instance obtains its own TLS | ||
| 79 | certificates. | ||
| 80 | 2. Restore the repositories under `data/repos/` and the object store under | ||
| 81 | `data/lfs/` if you keep CI logs and artifacts. | ||
| 77 | 82 | 3. Run `vole admin index rebuild`. | |
| 78 | 83 | 4. Run `vole admin account revoke-sessions --all` so sessions from the restored copy | |
| 79 | 84 | cannot outlive their cookies. | |
| @@ -94,6 +99,12 @@ fetched, but only a maintainer can push them; existing change requests still nam | |||
| 94 | 99 | the same head commit. A contributor who wants to keep working pushes the same | |
| 95 | 100 | commit under their new folder and opens a request from it. | |
| 96 | 101 | ||
| 102 | CI records moved from the `vole` branch to `data/runs.db`. Upgrade the server and | ||
| 103 | its runners together: an older runner's finish message carries no record and is | ||
| 104 | refused. Run `vole admin index rebuild` once after upgrading to import any records | ||
| 105 | still on a repository's branch, and start backing up `runs.db`. A job that was | ||
| 106 | running during the upgrade may need to be rerun. | ||
| 107 | |||
| 97 | 108 | Large files, CI logs, and build artifacts live in `data/lfs/`. Back that directory | |
| 98 | 109 | up too if you need those files. `vole admin gc` treats files reachable from a | |
| 99 | 110 | repository, an archived CI log, or a published artifact as things to keep. | |
| @@ -124,10 +135,14 @@ storage only grows when someone pushes files. | |||
| 124 | 135 | vole admin index rebuild | |
| 125 | 136 | ``` | |
| 126 | 137 | ||
| 127 | `data/runs.db` holds the CI queue, run index, and contributor-branch CI approvals. Deleting it loses attempts that | ||
| 128 | were in progress. Vole queues current branch and tag tips again from Git, and | ||
| 129 | `vole admin index rebuild` restores finished history from the run records on each | ||
| 130 | repository's `vole` branch. | ||
| 138 | `data/runs.db` holds the CI queue, finished-run records, and contributor-branch CI | ||
| 139 | approvals. It is a source of truth, not a rebuildable index: deleting it loses CI | ||
| 140 | history as well as attempts that were in progress. Back it up with the rest of the | ||
| 141 | instance. | ||
| 142 | |||
| 143 | `vole admin index rebuild` imports run records that are still on an older | ||
| 144 | repository's `vole` branch. It cannot restore records written after CI history moved | ||
| 145 | into the database; only a `runs.db` backup can do that. | ||
| 131 | 146 | ||
| 132 | 147 | ## Security notes | |
| 133 | 148 | ||
| @@ -197,7 +212,7 @@ the linked sections explain each in full. | |||
| 197 | 212 | - **Quotas.** Every repository is bounded, so one contributor or maintainer cannot | |
| 198 | 213 | make every page slow or fill the disk: `repo_max_refs` (500), `repo_max_metadata_entries` | |
| 199 | 214 | (2000) and `repo_max_metadata_bytes` (32 MiB) of documents on the metadata branch | |
| 200 | (issues, requests, comments, approvals, run and release records), | ||
| 215 | (issues, requests, comments, approvals, and release records), | ||
| 201 | 216 | `repo_max_bytes` (5 GiB) of Git objects, and `repo_max_lfs_bytes` (10 GiB) of | |
| 202 | 217 | LFS objects, logs and artifacts. The checks run in pre-receive, so a refused push | |
| 203 | 218 | moves no refs, and the object bound is enforced on upload. When one bites, the | |