vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Name branches after the account, and labels after the display
474a812d199a109ba894124cd6f26be12c73285d · Keenan Nemetz · 16d ago · parent ade6107e
Name branches after the account, and labels after the display
A contributor's folder was `refs/heads/contrib/<account-id>/`, which told a
reader nothing without decoding a ULID. It is now the account name,
refs/heads/contrib/<name>/<branch>
and the name is therefore fixed at creation: renaming an account would strand
every ref under its old folder, so `vole admin user rename` is gone and the
users row itself — kept after retirement — is what stops a name being reissued.
What people see is now a separate, mutable display name, validated by the same
rules wherever it is set (CLI, remote admin, registration, `/account`) and shown
on pages, approvals, listings and refusals. Nothing is authorized by it and
nothing on disk is keyed by it: metadata authors stay account names because the
push boundary resolves them for ownership, and approvals stay account ids.
`user_names`, `Snapshot.Reserved` and `Account.Former` are dropped: with a fixed
name there are no former names to remember, and the UNIQUE column already does
the reserving. Opening an older roster adds the `display` column and drops the
alias table in place; contributor refs written under the old id namespace are
left where they are, readable, and landable by a maintainer.
server/web/account.go +27
| @@ -5,6 +5,33 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | ) | |
| 7 | 7 | ||
| 8 | // SetAccountDisplay changes the signed-in account's display label. This is the | ||
| 9 | // one profile field a person owns: the account name is fixed at creation because | ||
| 10 | // refs and metadata files were written with it, but the label nobody is | ||
| 11 | // authorized by can be anything they like. | ||
| 12 | func (w *Web) SetAccountDisplay(rw http.ResponseWriter, r *http.Request) { | ||
| 13 | pr, ok := w.requireHTMLUser(rw, r) | ||
| 14 | if !ok { | ||
| 15 | return | ||
| 16 | } | ||
| 17 | r.Body = http.MaxBytesReader(rw, r.Body, 16<<10) | ||
| 18 | if !w.checkCSRF(r) { | ||
| 19 | http.Error(rw, "bad request", http.StatusForbidden) | ||
| 20 | return | ||
| 21 | } | ||
| 22 | if err := r.ParseForm(); err != nil { | ||
| 23 | http.Error(rw, "cannot read the form", http.StatusBadRequest) | ||
| 24 | return | ||
| 25 | } | ||
| 26 | display := strings.TrimSpace(r.PostFormValue("display")) | ||
| 27 | if _, err := w.Ident.SetDisplay(pr.account.ID, display, pr.account.Name); err != nil { | ||
| 28 | http.Error(rw, err.Error(), http.StatusBadRequest) | ||
| 29 | return | ||
| 30 | } | ||
| 31 | w.audit("web.display", pr.account.ID, display) | ||
| 32 | http.Redirect(rw, r, "/account", http.StatusSeeOther) | ||
| 33 | } | ||
| 34 | |||
| 8 | 35 | // AddAccountKey lets a signed-in person attach an SSH public key without an | |
| 9 | 36 | // administrator. Open registration is only useful for Git contributions if the | |
| 10 | 37 | // account can turn its browser session into a key that pushes. | |