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/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: