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.
Add browser auth, contributor access, and gated CI
6c84d387e5f608360e30484a1a5b1cff41076850 · Keenan Nemetz · 16d ago · parent c36d5073
Add browser auth, contributor access, and gated CI Browser sign-in now uses passkeys. Enrollment links are one-time, sessions are hashed in users.db, and issue/comment/approval writes use the session account. HTTPS public_url is required for WebAuthn. Add the contributor role between read and maintain. Contributors can discuss and open change requests, and may push only refs/heads/contrib/<account-id>/. Contributor CI is held per commit until a maintainer allows the exact tip from the repository page. Rename the write role to maintain. write remains accepted as the historical spelling, and access.Write remains as a Go alias so existing callers and ACL rows keep working. Update the web, CLI, queue schema, and guides for the new flows, and cover the role, namespace, auth, and CI-gating paths in tests.
server/admin_test.go +13−1
| @@ -61,7 +61,19 @@ func TestRemoteAdminManagesUsersAndRepos(t *testing.T) { | |||
| 61 | 61 | ||
| 62 | 62 | out, errOut, err := f.adminSSH(f.keyPath, nil, "user", "create", "alice") | |
| 63 | 63 | if err == nil { | |
| 64 | t.Fatalf("user create without a key succeeded: %s%s", out, errOut) | ||
| 64 | t.Fatalf("user create without a key or --no-key succeeded: %s%s", out, errOut) | ||
| 65 | } | ||
| 66 | |||
| 67 | // --no-key is the explicit passkey-only path; it must not wait for stdin. | ||
| 68 | out, errOut, err = f.adminSSH(f.keyPath, nil, "user", "create", "bob", "--no-key") | ||
| 69 | if err != nil { | ||
| 70 | t.Fatalf("user create --no-key: %v\nstdout: %s\nstderr: %s", err, out, errOut) | ||
| 71 | } | ||
| 72 | if a, err := f.srv.Identity.Find("bob"); err != nil || len(a.Keys) != 0 { | ||
| 73 | t.Fatalf("passkey-only account has keys: %+v, %v", a, err) | ||
| 74 | } | ||
| 75 | if !strings.Contains(out, "enrollment") { | ||
| 76 | t.Fatalf("user create --no-key did not print an enrollment link: %s", out) | ||
| 65 | 77 | } | |
| 66 | 78 | ||
| 67 | 79 | pub, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_alice")) | |