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.
CLI reference
The command line is Vole’s admin interface. That is deliberate: if you can run Vole on the machine, you are the operator. There is no separate admin panel to secure or keep in sync.
Run vole help for the built-in summary. If you make a usage mistake, Vole exits
with code 129, just like Git.
Common options
| Option | Used by | Meaning |
|---|---|---|
--config <path> |
Local server management | Use a different vole.toml (default: ./vole.toml, then ~/.vole/vole.toml; VOLE_CONFIG overrides both) |
--remote <name|url> |
admin, issue, change, release, and pages commands |
Git remote or instance SSH URL to talk to |
--json |
admin --remote |
Print the server’s JSON response |
--no-push |
issue, change |
Commit locally without pushing |
--dir <path> |
init, workflow, runner |
Working or checkout directory |
--branch <name> |
pages |
Site branch (default pages) |
--format <fmt> |
issue list, change list |
Output fields such as %(id), %(number) and %(title), or json |
-h, --help |
All | Print usage |
Options follow Git’s conventions: --long value, --long=value, -o value,
-ovalue, bundled short options such as -am, repeated options, and -- to
end option parsing. Options may appear before or after arguments. vole help <command> and vole <command> -h print that command’s usage.
Secrets
A repository’s secrets are the values its maintainers share, stored on the instance, encrypted, and never in the repository:
vole secret list <repo>
vole secret set <repo> <NAME> # the value is read from stdin
vole secret set <repo> <NAME> --from-file key.pem
vole secret get <repo> <NAME> # the value, and nothing else
vole secret rm <repo> <NAME>
set reads the value from standard input or a file and never from the command
line: an argument is visible to every process on the host and lands in shell
history. On a terminal it prompts without echoing; from a pipe it removes one
trailing newline, so echo "$TOKEN" | vole secret set demo DEPLOY_KEY stores the
token and not the line ending. get writes the value with no newline added, so it
can be redirected to a file.
set needs the maintainer role in that repository, and every read and change is
recorded in data/audit.log. list shows names, versions, who last changed one,
and when a value was last read — never a value. The same values appear on the
repository’s Settings tab, which is write-only: a saved value is not shown
again.
Names are upper-case letters, digits and _, so a name can be read as an
environment variable. A value is limited to 128 KiB, the most one environment
variable can carry.
Instance
vole admin init [--dir <path> | --config <path>]
vole serve [--config <path>] [--log debug]
vole version
Server management
vole admin is the single namespace for account, repository (repo), and access
(acl) management.
Without --remote, it changes the instance named by the local vole.toml. With
--remote, it sends the same command over SSH and must be run by an instance
admin.
vole admin account create alice --key ~/.ssh/id_ed25519.pub
vole admin --config /srv/vole/vole.toml account list
vole admin --remote origin account create bob --key bob.pub
vole admin --remote ssh://git@forge.example.com:2222 account list
--config names a vole.toml other than the one discovery finds, and may sit on
either side of the command (vole admin --config P account list and vole admin account list --config P are the same). It is local only: with --remote the
server’s own config is the one that applies.
See Accounts and access.
vole admin account is the documented spelling. The old vole admin user form
and the pre-admin aliases (vole account, vole user, vole repo, vole acl,
vole index, and vole gc) remain for compatibility; documentation and help use
vole admin account.
Accounts
The account commands manage accounts.
vole admin account create [--key <file|->] [--role <role>] [--bot] <name>
vole admin account invite <name>
vole admin account credentials <name>
vole admin account rm-credential <name> <credential-id>
vole admin account sessions <name>
vole admin account revoke-sessions [--all | <name>]
vole admin account add-key --key <file|-> <name>
vole admin account rm-key <name> <fingerprint>
vole admin account retire <name>
vole admin account role <name> <none|read|contribute|maintain|admin>
vole admin account grant-admin <name>
vole admin account revoke-admin <name>
vole admin account show <name>
vole admin account list
vole admin account backup <path>
vole whoami [--remote <name>] [--role]
Omitting --key creates a browser-only account and prints a one-time enrollment
link. --bot creates an automation account instead: it has no browser sign-in and
cannot be an instance admin, so it must be created with a key. --role sets the
account’s instance-wide role; it applies to public repositories, while admin
applies to every repository. account role changes it later. account invite
issues a fresh link for an existing account. account backup writes a file on the
host, so it is local-only.
The account name is the only identifier an account has. It cannot be changed: it
is the folder a contribute account’s branches live under, so it is fixed at
creation and never reissued.
vole whoami prints the account name. With --role it prints the effective role
on the repository named by the remote: the repository’s default and per-account
rules, with the account’s instance-wide role applied where the repository is
public. A private repository the account cannot read is reported as not found,
the same as a fetch.
Repositories
vole admin repo create --owner <account> <name> [--description <text>]
vole admin repo list
vole admin repo show <name>
vole admin repo rename <old> <new>
vole admin repo delete <name>
New repositories are private. The owner becomes the first admin.
Access
Instance-wide roles are account standing; repository rules are set with acl:
vole admin account role <name> <none|read|contribute|maintain|admin>
vole admin acl show <repo>
vole admin acl set-role <repo> <account> <none|read|contribute|maintain|admin>
vole admin acl set-default-role <repo> <none|read|contribute|maintain|admin>
vole admin acl set-approvals <repo> <n>
vole admin acl checks enable <repo>
vole admin acl checks disable <repo>
vole admin acl require-issues enable <repo>
vole admin acl require-issues disable <repo>
vole admin acl protect list <repo>
vole admin acl protect add <repo> <ref-pattern>...
vole admin acl protect remove <repo> <ref-pattern>...
vole admin acl protect set <repo> <ref-pattern>...
vole admin acl protect clear <repo>
vole admin acl runners list <repo>
vole admin acl runners add <repo> <account>...
vole admin acl runners remove <repo> <account>...
vole admin acl runners set <repo> <account>...
vole admin acl runners clear <repo>
set-role and set-default-role accept none to remove access. protect set
and runners set replace the whole list; the add and remove forms change one
entry at a time. require-issues enable makes a linked issue mandatory when a
new change request is opened.
A protected ref pattern is either an exact ref (refs/heads/main) or a ref
prefix ending in one * (refs/heads/release/*), the two shapes the hook
understands. protect add and protect set refuse anything else, because a
pattern that can never match would leave the branch open while acl show listed
it as protected. A misspelled category is named in the refusal:
$ vole admin acl protect add demo refs/head/main
vole: protected pattern "refs/head/main" cannot match any ref: did you mean "refs/heads/main"?
See Accounts and access and Runners.
Maintenance
vole admin gc [--dry-run] [--grace <duration>]
vole admin index rebuild
These are local-only. See Operations.
Issues
vole issue new --title <title> [-m <text> | -F <file>] [-l <label>]... [-a <account>]...
vole issue comment <id> [-m <text> | -F <file>] [--reply-to <comment-id>]
vole issue edit-comment <id> <comment-id> [-m <text> | -F <file>]
vole issue rm-comment <id> <comment-id>
vole issue edit <id> [--title <title>] [-m <text> | -F <file>] [-l <label>]... [-a <account>]... [--unassign <account>]...
vole issue assign <id> <account>...
vole issue unassign <id> <account>...
vole issue close <id>
vole issue reopen <id>
vole issue list [--state open|closed|all] [-l <label>]... [--assignee <account>]... [--format <fmt>]
vole issue show <id>
Ids and comment ids accept a unique prefix. See Issues.
Change requests
vole change new --title <title> --base <branch> [--head <rev>] [--closes <issue>]... [-m <text> | -F <file>]
vole change list [--state open|closed|all] [--format <fmt>]
vole change show <id>
vole change comment <id> [-m <text> | -F <file>] [--path <file> --line <n> [--side new|old]] [--reply-to <comment-id>]
vole change edit-comment <id> <comment-id> [-m <text> | -F <file>]
vole change rm-comment <id> <comment-id>
vole change update <id> [--base <branch>] [--head <rev>] [--closes <issue>]... [--no-closes]
vole change approve <id> [--as <account>]
vole change merge <id>
vole change close <id>
vole change reopen <id>
change also accepts request, requests and cr as compatibility aliases. An
approval is only accepted from the account it names. --closes takes a full
issue id (repeatable, comma lists accepted) and names the issues landing the
request closes; it replaces the list on update, and --no-closes clears it.
A request opened with --head <branch> follows that branch: a later push moves its
head and invalidates the old approvals and checks. --head <commit> (or a tag)
pins the request to that commit instead. --base <branch> on update retargets an
open request, which is how a stacked request moves onto main after the branch
below it lands; approvals named the old base and stop counting. See
Change requests.
change merge lands the request. When the combined result is still building it
prints that the request will merge when the result passes, which is the same
“merge when ready” a click on the page arms. change show prints the combined
result and whether the landing is ready.
Releases
vole release create <tag> (-m <text> | -F <file|->)
vole release attach <tag> <file>... [--remote <name>]
create writes the annotated tag in the current repository; push it with
git push --follow-tags. See Releases.
Pages
vole pages deploy [<dir>] [--branch <name>] [-m <text> | -F <file>] [--dry-run] [--remote <name>]
vole pages status [--branch <name>] [--remote <name>]
vole pages unpublish [--branch <name>] [--remote <name>]
See Sites.
CI
vole runner --remote <ssh-url> [--once] [--jobs <n>] [--dir <path>] [--repos <name>]... [--label <name>]...
[--container-runtime <name>] [--container-network none|bridge]
[--container-pull missing|always|never] [--container-user <uid:gid>] [--container-userns <mode>]
[--container-memory <size>] [--container-cpus <n>] [--container-pids <n>]
[--cache-size <size>] [--git-cache-size <size>] [--no-cache]
vole run cancel <id> [--remote <name|url>]
vole run rerun <id> [--remote <name|url>]
vole workflow check [--dir <path>]
vole workflow list [--dir <path>]
vole workflow run [--dir <path>] [<workflow>...] [--job <id>]...
[--container-runtime <name>]
[--container-network none|bridge] [--container-pull missing|always|never]
[--container-user <uid:gid>] [--container-userns <mode>]
[--cache-size <size>] [--no-cache]
vole workflow dispatch --remote <name|url> --workflow <path> [--ref <branch>]
[--input name=value]...
--jobs is how many jobs one runner process runs at once (default 1). Each is a
session of its own, so the container limits are per job and a machine running four
jobs needs room for four containers. Under --once the runner still starts --jobs
jobs, and exits when they all finish.
workflow run executes steps in the working tree, in the job’s image. It needs no
runner, but it does read the secrets its jobs declare from the instance the
checkout’s origin names, using your key; a value it cannot read is left unset with
a note, and the values it did read are masked in the output. It needs a container
runtime; there is no host mode. Both it and
vole runner reuse a job cache (see Runners § Caching). A
runner also keeps a copy of every repository it builds and clones each job from it,
so the history crosses the network once rather than once per run (see
Runners § Saved checkouts). See
Workflows § Check and run a workflow locally.
workflow dispatch queues a manual run on the instance instead. The workflow must
declare on.workflow-dispatch; --ref defaults to the default branch, and each
--input is checked against the input’s declared type, options, and default. Only
a maintainer or instance admin may dispatch.
run cancel is the CLI counterpart of the cancel button on a run page. It takes the
26-character id in that page’s URL: a queued run is canceled outright, and a running
one is asked to stop, with its runner stopping at the next heartbeat. run rerun
queues a finished run again with the same pinned job. Only a maintainer or instance
admin may do either, and data/audit.log records the account, the repository, and the
run (run.cancel or run.rerun).
Internal
vole hook pre-receive|post-receive is installed in repositories by Vole. It is
not intended to be run by hand.