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.
Give a job a credential for the bot it acts as
6e1fca5115b709d31eeb51864b187d79aa104e5f · Keenan Nemetz · 6d ago · parent 446542e6
Give a job a credential for the bot it acts as A workflow can name a bot account with `bot:`. When the job is leased the instance generates a key for that run, stores the public half with the run, repository and expiry, and sends the private half on the lease channel. The runner writes it into the job's run directory with a known_hosts entry for the instance, so a step's `git push` or `vole issue comment` authenticates as the account and nothing has to be wired per job. The account's role is the permission, in full. The credential replaces exactly one requirement — that writing to the issues branch needs `maintain` — and adds no rule of its own. What a job may write there is what its account may do, asked the same way the browser asks: discuss, file an issue, manage an item it wrote, open a change from a branch it may push, triage and approve when the role allows it. Those are the ACL's own capabilities (`CanDiscuss`, `CanOpenRequest`, `CanTriage`, `CanApprove`), so a maintainer's job may do everything a maintainer's key may do, including labels, states, and other people's items. A session is scoped only when the fingerprint it authenticated with is a live credential row; an expired one reads as absent, so the push falls back to the account's own role and is refused where a contribute account cannot write. Two things end the key: the run finishing, which drops the row, and the expiry, which is what stops a credential whose runner was killed and never reported. The key is also masked in the log like a secret, since a step that cats it should not leave it in the attempt. Closes #165.
docs/reference/workflow-reference.md +50−1
| @@ -413,6 +413,7 @@ letter. At least one job is required. | |||
| 413 | 413 | | `runs-on` | string or list of strings | no | any runner | Runner labels the job requires. | | |
| 414 | 414 | | `network` | `none` or `bridge` | no | the runner's setting | Whether the job's container has a network. | | |
| 415 | 415 | | `secrets` | string or list of strings | no | none | Repository secrets the job reads, by name. Each name is also the variable a step finds it in. | | |
| 416 | | `bot` | account name | no | none | A bot account the job acts as, with a credential for that run. | | ||
| 416 | 417 | | `env` | mapping | no | none | Environment every step of the job runs with. | | |
| 417 | 418 | | `timeout` | duration string | no | none | Wall clock the job's steps share. | | |
| 418 | 419 | | `steps` | list | yes | — | Commands the job runs. At least one is required. | | |
| @@ -580,7 +581,10 @@ jobs: | |||
| 580 | 581 | ||
| 581 | 582 | `CI`, `VOLE_CI`, `GIT_TERMINAL_PROMPT`, `HOME` and `TMPDIR` are set by the runner and | |
| 582 | 583 | cannot be replaced: the sandbox depends on them, so the push that tries is refused | |
| 583 | rather than silently disagreeing with it. | ||
| 584 | rather than silently disagreeing with it. `VOLE_RUN_ID`, `VOLE_REPO`, | ||
| 585 | `VOLE_SSH_COMMAND` and `VOLE_KNOWN_HOSTS` are reserved for the same reason: they point | ||
| 586 | a job at its credential, and a workflow that could rewrite them could redirect the | ||
| 587 | push. | ||
| 584 | 588 | ||
| 585 | 589 | This is how a job opts in to something the runner provides but does not decide for | |
| 586 | 590 | it. The job cache is the one that matters today: the runner mounts a directory at | |
| @@ -645,6 +649,51 @@ best effort, and worth knowing exactly: | |||
| 645 | 649 | - an artifact, a site file or a release attachment is stored as the step wrote it, | |
| 646 | 650 | so a secret a step writes to a file it publishes is published. | |
| 647 | 651 | ||
| 652 | ### `bot` | ||
| 653 | |||
| 654 | The bot account the job acts as: | ||
| 655 | |||
| 656 | ```yaml | ||
| 657 | jobs: | ||
| 658 | triage: | ||
| 659 | image-file: agent | ||
| 660 | bot: coding-agent | ||
| 661 | steps: | ||
| 662 | - run: vole issue comment "$ISSUE" --reply-to "$COMMENT" -F reply.md | ||
| 663 | ``` | ||
| 664 | |||
| 665 | The account must be a bot account (see | ||
| 666 | [Accounts and access § Bot accounts](../accounts-and-access.md#bot-accounts)), and | ||
| 667 | the instance issues the run a credential for it when the job is leased: a key | ||
| 668 | generated for that run, written into the job, and gone when the run ends. The job | ||
| 669 | inherits the account's role, so nothing here grants more than that account may do. | ||
| 670 | |||
| 671 | Two things change, and only two: | ||
| 672 | |||
| 673 | - writing to the issues branch no longer requires `maintain`. What a job may write | ||
| 674 | there is what its account may do, asked the same way the browser asks: an account | ||
| 675 | that may discuss files an issue and comments, an author manages the item it wrote, | ||
| 676 | a maintainer triages any of them, and an account that may approve may approve. The | ||
| 677 | credential adds no rule of its own; it removes the one requirement that made every | ||
| 678 | metadata write a maintainer's act; | ||
| 679 | - the job can push to the instance without a long-lived key in a repository secret. | ||
| 680 | The key is valid for this run, and a job whose account is retired or whose key has | ||
| 681 | expired writes nothing. | ||
| 682 | |||
| 683 | The credential is a file, not a variable, and the runner points git at it, so a step | ||
| 684 | does not need to know it exists: | ||
| 685 | |||
| 686 | | Variable | Value | | ||
| 687 | |---|---| | ||
| 688 | | `VOLE_RUN_ID` | The run this job is executing. | | ||
| 689 | | `VOLE_REPO` | The repository, as the instance names it. | | ||
| 690 | | `VOLE_SSH_COMMAND` | The ssh command that uses the run's key and known_hosts file. | | ||
| 691 | | `VOLE_KNOWN_HOSTS` | The known_hosts file with this instance's host key. | | ||
| 692 | |||
| 693 | These four are set by the runner and cannot be replaced by the workflow's `env:`, | ||
| 694 | the same rule as `CI` and `HOME`. A job that names no `bot` gets none of them and no | ||
| 695 | credential, which is what every workflow had before this field existed. | ||
| 696 | |||
| 648 | 697 | ### `timeout` | |
| 649 | 698 | ||
| 650 | 699 | A wall-clock budget for the job's steps, instead of a cap on each step separately: | |