Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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.

forge/identity/passkey_test.go +7−7

@@ -36,7 +36,7 @@ func testCredential(t *testing.T, label string) Credential {
36 36 func TestCreateWithInviteAndAcceptPasskey(t *testing.T) {
37 37 t.Parallel()
38 38 s := newStore(t)
39 a, inv, err := s.CreateWithInvite("alice", "operator", time.Hour)
39 a, inv, err := s.CreateWithInvite("alice", "", "operator", time.Hour)
40 40 if err != nil {
41 41 t.Fatal(err)
42 42 }
@@ -99,7 +99,7 @@ func TestCreateWithInviteAndAcceptPasskey(t *testing.T) {
99 99 func TestNewInviteSupersedesTheOldOne(t *testing.T) {
100 100 t.Parallel()
101 101 s := newStore(t)
102 _, first, err := s.CreateWithInvite("alice", "operator", time.Hour)
102 _, first, err := s.CreateWithInvite("alice", "", "operator", time.Hour)
103 103 if err != nil {
104 104 t.Fatal(err)
105 105 }
@@ -118,7 +118,7 @@ func TestNewInviteSupersedesTheOldOne(t *testing.T) {
118 118 func TestExpiredInviteIsInvalid(t *testing.T) {
119 119 t.Parallel()
120 120 s := newStore(t)
121 _, inv, err := s.CreateWithInvite("alice", "operator", time.Hour)
121 _, inv, err := s.CreateWithInvite("alice", "", "operator", time.Hour)
122 122 if err != nil {
123 123 t.Fatal(err)
124 124 }
@@ -137,7 +137,7 @@ func TestExpiredInviteIsInvalid(t *testing.T) {
137 137 func TestPasskeyOnlyAccountSurvivesReopen(t *testing.T) {
138 138 t.Parallel()
139 139 s := newStore(t)
140 a, inv, err := s.CreateWithInvite("alice", "operator", time.Hour)
140 a, inv, err := s.CreateWithInvite("alice", "", "operator", time.Hour)
141 141 if err != nil {
142 142 t.Fatal(err)
143 143 }
@@ -210,7 +210,7 @@ func TestUpdatePasskeyCounterAndRemove(t *testing.T) {
210 210 func TestRetireEndsInvitesAndSessionsAndHidesCredentials(t *testing.T) {
211 211 t.Parallel()
212 212 s := newStore(t)
213 a, inv, err := s.CreateWithInvite("alice", "operator", time.Hour)
213 a, inv, err := s.CreateWithInvite("alice", "", "operator", time.Hour)
214 214 if err != nil {
215 215 t.Fatal(err)
216 216 }
@@ -288,7 +288,7 @@ func TestCreatePasskeySelfRegistration(t *testing.T) {
288 288 s := newStore(t)
289 289 id := "01M5JR2JK29KKMAA1T9MDNTPBP"
290 290 cred := testCredential(t, "first passkey")
291 a, err := s.CreatePasskey("alice", id, cred, "self-registration")
291 a, err := s.CreatePasskey("alice", "", id, cred, "self-registration")
292 292 if err != nil {
293 293 t.Fatal(err)
294 294 }
@@ -302,7 +302,7 @@ func TestCreatePasskeySelfRegistration(t *testing.T) {
302 302
303 303 // A second registration for the same name must not leave an account behind.
304 304 other := "01M5JR2JK29KKMAA1T9MDNTPBQ"
305 if _, err := s.CreatePasskey("alice", other, testCredential(t, "other"), "self-registration"); err == nil {
305 if _, err := s.CreatePasskey("alice", "", other, testCredential(t, "other"), "self-registration"); err == nil {
306 306 t.Fatal("duplicate name was accepted")
307 307 }
308 308 if _, err := s.Find(other); err == nil {