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.
Serve repositories from the root of the URL space
ed03b3d1fa16ef6b55e2773aaf10e3be13282c3a · Keenan Nemetz · 3h ago · parent d37d301f
Serve repositories from the root of the URL space A repository is now /<name> and its Git transport /<name>.git, so the URL a person opens, the URL a clone uses, and the Go module path are the same name. The forge's own pages moved under /-/ (admin, accounts, the directory, the feed, sign-in, assets), a prefix no repository name can start with: names are [a-z0-9...] and cannot begin with "-". That leaves the router with two disjoint spaces and removes the /r/ and /git/ prefixes, which existed only to keep repositories away from the forge's own paths. Routes: repoRoutes lose /r/; global, auth and admin routes gain /-/; assets move to /-/static/ because a subtree pattern like /static/ cannot coexist with one-segment-wildcard repo routes — Go's mux reports them as conflicting neither-more-specific patterns. The catch-all still renders the styled 404. Transport: githttp.Route parses /<name>.git/<rest> instead of a /git/ prefix, and forgepaths.go hands such requests to the proxy before the mux, because a wildcard fills a whole segment and cannot match "<name>.git". LFS stays on the mux (<name>.git/info/lfs/...), and the Go module discovery probe moves to the same pre-router so a package path under a public repository is answered even when its first segments name a page tab. Names: repo.ValidateName now refuses the four root paths the forge must keep — static, robots.txt, sitemap.xml, sitemap-pages.xml. Everything else is an ordinary repository name, including admin, u, and activity. Docs and the packaged URLs follow the new layout; robots.txt, the sitemaps and the instance activity feed point at the new paths. No aliases: the old /r/ and /git/ URLs are gone. Verified with ./vole workflow check and ./vole workflow run verify.
server/web/templates/admin_users.html +9−9
| @@ -1,11 +1,11 @@ | |||
| 1 | 1 | {{define "accounts_body"}} | |
| 2 | 2 | {{if .Error}}<div class="flash bad">{{.Error}}</div>{{end}} | |
| 3 | 3 | ||
| 4 | <p class="muted">To change a role, inspect keys or sessions, or retire an account, open it from the <a href="/u">users directory</a>.</p> | ||
| 4 | <p class="muted">To change a role, inspect keys or sessions, or retire an account, open it from the <a href="/-/u">users directory</a>.</p> | ||
| 5 | 5 | ||
| 6 | 6 | <h2>Invite a person</h2> | |
| 7 | 7 | <p class="muted">Send them a one-time link: they choose a passkey, then can add an SSH key.</p> | |
| 8 | <form method="post" action="/users" class="form-block"> | ||
| 8 | <form method="post" action="/-/users" class="form-block"> | ||
| 9 | 9 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | |
| 10 | 10 | <div class="card form-card"> | |
| 11 | 11 | <label>Account name | |
| @@ -18,7 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | 19 | <h2>Create a bot account</h2> | |
| 20 | 20 | <p class="muted">For a runner or another automation. It acts with the SSH key you paste here and cannot sign in to the browser or become an instance admin.</p> | |
| 21 | <form method="post" action="/users/bots" class="form-block"> | ||
| 21 | <form method="post" action="/-/users/bots" class="form-block"> | ||
| 22 | 22 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | |
| 23 | 23 | <div class="card form-card"> | |
| 24 | 24 | <label>Account name | |
| @@ -45,7 +45,7 @@ | |||
| 45 | 45 | </div> | |
| 46 | 46 | {{if not .Account.Bot}} | |
| 47 | 47 | <div class="page-head-actions"> | |
| 48 | <form class="inline" method="post" action="/users/{{.Account.Name}}/invite"> | ||
| 48 | <form class="inline" method="post" action="/-/users/{{.Account.Name}}/invite"> | ||
| 49 | 49 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | |
| 50 | 50 | <button type="submit" class="secondary">{{icon "external-link"}}New enrollment link</button> | |
| 51 | 51 | </form> | |
| @@ -57,7 +57,7 @@ | |||
| 57 | 57 | ||
| 58 | 58 | <h2>Instance role</h2> | |
| 59 | 59 | {{$role := .Account.Role.String}} | |
| 60 | <form method="post" action="/users/{{.Account.Name}}/role" class="form-block"> | ||
| 60 | <form method="post" action="/-/users/{{.Account.Name}}/role" class="form-block"> | ||
| 61 | 61 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | |
| 62 | 62 | <div class="card form-card"> | |
| 63 | 63 | {{template "role-picker" (rolePicker "role" $role (printf "Role for %s" .Account.Name) .RoleHelp .RoleScope false)}} | |
| @@ -79,7 +79,7 @@ | |||
| 79 | 79 | <td>{{.Type}}</td> | |
| 80 | 80 | <td class="muted">{{.Comment}}</td> | |
| 81 | 81 | <td> | |
| 82 | <form class="inline" method="post" action="/users/{{$.Account.Name}}/keys/remove"> | ||
| 82 | <form class="inline" method="post" action="/-/users/{{$.Account.Name}}/keys/remove"> | ||
| 83 | 83 | <input type="hidden" name="csrf" value="{{$.CSRF}}"> | |
| 84 | 84 | <input type="hidden" name="fingerprint" value="{{.Fingerprint}}"> | |
| 85 | 85 | <button type="submit" class="danger">Remove</button> | |
| @@ -99,7 +99,7 @@ | |||
| 99 | 99 | <div class="with-more"> | |
| 100 | 100 | <h2>Sessions</h2> | |
| 101 | 101 | {{if .Sessions}} | |
| 102 | <form class="inline" method="post" action="/users/{{.Account.Name}}/sessions/revoke"> | ||
| 102 | <form class="inline" method="post" action="/-/users/{{.Account.Name}}/sessions/revoke"> | ||
| 103 | 103 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | |
| 104 | 104 | <button type="submit" class="link">Revoke all sessions</button> | |
| 105 | 105 | </form> | |
| @@ -126,7 +126,7 @@ | |||
| 126 | 126 | {{end}} | |
| 127 | 127 | ||
| 128 | 128 | <h2>Danger zone</h2> | |
| 129 | <form method="post" action="/users/{{.Account.Name}}/retire" class="form-block"> | ||
| 129 | <form method="post" action="/-/users/{{.Account.Name}}/retire" class="form-block"> | ||
| 130 | 130 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | |
| 131 | 131 | <div class="card form-card danger-zone"> | |
| 132 | 132 | <h3>Retire account</h3> | |
| @@ -160,7 +160,7 @@ | |||
| 160 | 160 | </div> | |
| 161 | 161 | ||
| 162 | 162 | <div class="form-actions"> | |
| 163 | <a class="button secondary" href="/users/{{.Account.Name}}">{{icon "user"}}Back to account</a> | ||
| 163 | <a class="button secondary" href="/-/users/{{.Account.Name}}">{{icon "user"}}Back to account</a> | ||
| 164 | 164 | </div> | |
| 165 | 165 | {{template "footer" .}} | |
| 166 | 166 | {{end}} | |