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 repository secrets encrypted, with a CLI and a settings form
0c40a1d3b48eb0ade37334f62ee526c371ecb61c · Keenan Nemetz · 7d ago · parent 122cbe4f
Store repository secrets encrypted, with a CLI and a settings form A repository has values that cannot live in its code: a deploy key, a registry token, a staging password. They are now stored in data/secrets.db, sealed with AES-256-GCM under data/secrets.key, and read through one store that records every read and every change. The key is a separate file, and secrets_key can move it, so a backup of the data directory can be a backup of ciphertext rather than of plaintext. `vole secret list|set|get|rm` asks the instance over the key the caller already pushes with, since the store is server-side. set reads the value from stdin or a file and never from the argument list; get writes the value and nothing else. The maintainer role is the capability: the accounts that approve work and allow CI own the credentials those jobs will use. The repository's Settings tab gains the same list — names, versions, who changed one, when a value was last read — with set, replace and delete. It never renders a value, and a maintainer who is not an admin now reaches that page for this section alone. Values are limited to what one environment variable can carry: 128 KiB for NAME=value, valid UTF-8 and no NUL. A longer value could never reach a step, and the refusal says so. Slice 1 of #154.
docs/configuration.md +2
| @@ -43,6 +43,7 @@ vole admin account list | |||
| 43 | 43 | | Setting | Default | What it does | | |
| 44 | 44 | |---|---:|---| | |
| 45 | 45 | | `data_dir` | `"./data"` | Where Vole keeps repositories, databases, stored files, and the audit log | | |
| 46 | | `secrets_key` | `<data_dir>/secrets.key` | The file holding the key that seals each repository's secrets. Point it somewhere a backup of `data_dir` does not reach, so a copy of the database is a copy of ciphertext | | ||
| 46 | 47 | | `http_addr` | `"127.0.0.1:8080"` | Address for the web interface and anonymous Git clone; with `https_addr`, the ACME challenge and redirect address | | |
| 47 | 48 | | `https_addr` | empty | Address for automatic HTTPS; setting it enables ACME | | |
| 48 | 49 | | `ssh_addr` | `"127.0.0.1:2222"` | Address for SSH, the authenticated Git transport | | |
| @@ -323,6 +324,7 @@ connection intact. | |||
| 323 | 324 | | `data/lfs/` | Stored large files and build artifacts. | | |
| 324 | 325 | | `data/logs/` | One file per CI attempt, streamed from the runner while the job runs. | | |
| 325 | 326 | | `data/audit.log` | A record of privileged actions: one tab-separated line per action, `timestamp`, `action`, `actor`, `subject`, `detail`. Created and kept at mode `0600`. See [Audit log](operations.md#audit-log). | | |
| 327 | | `data/secrets.db`, `data/secrets.key` | Each repository's secrets, sealed with AES-256-GCM under the key. Losing the key loses every stored value, and restoring the database beside a newly generated key reads nothing. | | ||
| 326 | 328 | | `data/ssh_host_ed25519_key` | The SSH host key. It is the instance's long-term secret; browser credentials are public keys and session tokens are hashed. | | |
| 327 | 329 | | `data/acme/` | The ACME account key and issued TLS certificates, when automatic HTTPS is enabled. | | |
| 328 | 330 | ||