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.
server/web/web.go +13−9
| @@ -29,19 +29,23 @@ import ( | |||
| 29 | 29 | "github.com/rodent-software/vole/forge/index" | |
| 30 | 30 | "github.com/rodent-software/vole/forge/pageview" | |
| 31 | 31 | "github.com/rodent-software/vole/forge/repo" | |
| 32 | "github.com/rodent-software/vole/forge/secret" | ||
| 32 | 33 | "github.com/rodent-software/vole/forge/session" | |
| 33 | 34 | ) | |
| 34 | 35 | ||
| 35 | 36 | type Web struct { | |
| 36 | Repos *repo.Store | ||
| 37 | Index *index.Store | ||
| 38 | Ident *identity.Store | ||
| 39 | Sessions *session.Store | ||
| 40 | Runs *runs.Store | ||
| 41 | Logs *logs.Store | ||
| 42 | LFS *lfs.Store | ||
| 43 | Locks *lfs.LockStore | ||
| 44 | PageViews *pageview.Store | ||
| 37 | Repos *repo.Store | ||
| 38 | Index *index.Store | ||
| 39 | Ident *identity.Store | ||
| 40 | Sessions *session.Store | ||
| 41 | Runs *runs.Store | ||
| 42 | Logs *logs.Store | ||
| 43 | LFS *lfs.Store | ||
| 44 | Locks *lfs.LockStore | ||
| 45 | PageViews *pageview.Store | ||
| 46 | // Secrets is the per-repository store the settings page writes to. The page | ||
| 47 | // never reads a value back: it shows names, versions and read times. | ||
| 48 | Secrets *secret.Store | ||
| 45 | 49 | Cfg *config.Config | |
| 46 | 50 | Tmpl *template.Template | |
| 47 | 51 | Auth *Auth | |