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.
Answer whoami --role from the repository's rules
eec0e60344bf5686fc278ba5a8415dc0777ba9e2 · Keenan Nemetz · 6d ago · parent f422c24f
Answer whoami --role from the repository's rules A role is per repository, so whoami cannot print one truth without knowing which repository is meant. Keep the default answer name-only, for the scripts that call it, and make the role an explicit query on the remote's repository. The server's vole-whoami verb now accepts a repository path and returns the effective role after the same private-repository rule as a fetch. A run credential may ask only about its own repository. Update the CLI help and the contributor and account docs to point at the new query.
server/ssh/server.go +18−4
| @@ -411,7 +411,7 @@ func (s *Server) exec(ch xssh.Channel, req *xssh.Request, command, user, fp, rem | |||
| 411 | 411 | if credErr != nil { | |
| 412 | 412 | s.cfg.Log.Warn("ssh: cannot read a run credential", "fingerprint", fp, "err", credErr) | |
| 413 | 413 | } | |
| 414 | if cred != nil && !(len(argv) == 1 && argv[0] == "vole-whoami") && | ||
| 414 | if cred != nil && !(len(argv) >= 1 && argv[0] == "vole-whoami" && len(argv) <= 2) && | ||
| 415 | 415 | !(len(argv) == 2 && (argv[0] == "git-upload-pack" || argv[0] == "git-receive-pack")) { | |
| 416 | 416 | refuse("a job credential may only push, fetch, and ask who it is") | |
| 417 | 417 | return | |
| @@ -517,10 +517,24 @@ func (s *Server) exec(ch xssh.Channel, req *xssh.Request, command, user, fp, rem | |||
| 517 | 517 | // not exist. The second field is where a browser reads this instance, empty | |
| 518 | 518 | // when the operator has not set public_url — the caller that asks "who am I" | |
| 519 | 519 | // is usually the same one that wants to print a link to what it just wrote. | |
| 520 | if len(argv) == 1 && argv[0] == "vole-whoami" { | ||
| 520 | // With a repository it also answers the effective role there, which is the | ||
| 521 | // question `vole whoami --role` asks. | ||
| 522 | if len(argv) >= 1 && argv[0] == "vole-whoami" && len(argv) <= 2 { | ||
| 523 | repoArg := "" | ||
| 524 | if len(argv) == 2 { | ||
| 525 | name, ok := repoName(argv[1]) | ||
| 526 | if !ok { | ||
| 527 | refuse("invalid repository path") | ||
| 528 | return | ||
| 529 | } | ||
| 530 | if cred != nil && cred.Repo != name { | ||
| 531 | refuse("this job's credential is for %q, not %q", cred.Repo, name) | ||
| 532 | return | ||
| 533 | } | ||
| 534 | repoArg = argv[1] | ||
| 535 | } | ||
| 521 | 536 | req.Reply(true, nil) | |
| 522 | fmt.Fprintf(ch, "%s\t%s\n", user, s.cfg.PublicURL) | ||
| 523 | exit(ch, 0) | ||
| 537 | exit(ch, s.serveWhoAmI(ch, user, repoArg)) | ||
| 524 | 538 | return | |
| 525 | 539 | } | |
| 526 | 540 | if len(argv) != 2 { | |