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/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · docs/accounts-and-access.md

Line attribution on main · 468 line(s)

ab326d3a
Keenan Nemetz
14d ago
1 # Accounts and access
520470e1
Keenan Nemetz
16d ago
2
6c84d387
Keenan Nemetz
16d ago
3 Vole has no password database. An account has two kinds of credential:
4
5 - an **SSH key** for Git over SSH and remote administration;
6 - a **passkey** for signing in to the browser.
7
474a812d
Keenan Nemetz
16d ago
8 Both resolve to the same account, so access rules, approvals, and audit records
9 do not care which one you used.
520470e1
Keenan Nemetz
16d ago
10
11 ## Accounts and keys
12
62576fa8
Keenan Nemetz
14d ago
13 An account is identified by its **name**, chosen when the account is created and
14 fixed for good. It is what your contributor branches are named after
15 (`refs/heads/contrib/<name>/`), and it is the key for access rules, approvals,
16 file locks and CI records. There is no second internal identifier and no separate
17 display label, so a rename would strand every ref, approval and author field
18 already written. Keep the name.
474a812d
Keenan Nemetz
16d ago
19
20 Names are never reused: because the account row outlives retirement, retiring
21 someone keeps their name attached to them. An old reference can never silently
22 point at a different person.
520470e1
Keenan Nemetz
16d ago
23
29a795b9
Keenan Nemetz
7d ago
24 ## Bot accounts
25
26 An automation can have an account of its own, so the work it does is attributable
27 to it and it can be assigned an issue or named in a comment like anyone else. A bot
28 is created by an instance admin with the repository role it should have:
29
30 ```sh
31 vole admin account create coding-agent --bot --key agent.pub --role contribute
32 ```
33
8c72543b
Keenan Nemetz
5d ago
34 An instance admin can do the same from the **Create a bot account** form on the
35 Accounts page: name the account, paste its public key, and choose a role. A bot
36 cannot be given the instance-admin role in either place.
37
29a795b9
Keenan Nemetz
7d ago
38 The kind is fixed at creation, like the name. A bot differs from a person in three
39 ways, and only three:
40
41 - **No browser sign-in.** A bot cannot be given a passkey or an enrollment link,
42 because it has no browser, and a link it could never use is a secret in the roster
43 for no reason. `account invite` and adding a passkey are refused for a bot.
44 - **No instance admin.** A bot is a repository automation, and the Admin role reaches
45 every repository and every instance surface, so it is refused when the account is
46 created and when its role is changed later.
47 - **It is shown as a bot** in the user directory and on its profile, so a reader
48 knows a machine wrote what they are reading.
49
50 Everything else is the same, and deliberately so: a bot is subject to the rules of
51 whatever role it holds. A `contribute` bot may push only
52 `refs/heads/contrib/<name>/`, and CI on that tip waits for a maintainer to allow it,
53 exactly as for a person. A `maintain` bot may push shared branches, its pushes run
54 CI immediately, it receives the secrets its workflows declare, and its approval
55 counts when it is not the author. Granting a role is the trust decision, and the
56 audit log and every approval name the bot.
57
6e1fca51
Keenan Nemetz
6d ago
58 A workflow can act as a bot without holding a long-lived key. A job that names the
ac541b26
Keenan Nemetz
6d ago
59 account with `acts-as:` is given a credential when it is leased: a key generated for
60 that run, written into the job, and gone when the run ends. The key may do what the
6e1fca51
Keenan Nemetz
6d ago
61 account may do: discuss, file an issue, manage an item it wrote, open a change from a
62 branch it may push, triage and approve when the role allows it, and push the branches
63 the role allows. It cannot reach an admin surface, read repository secrets, or lease
64 work, because those are not repository writes.
65
66 The credential is not the account's key, and it does not widen the role: a
67 `contribute` bot's job still opens changes in its own namespace, and a job cannot
68 approve unless its account can. See
ac541b26
Keenan Nemetz
6d ago
69 [Workflow reference § acts-as](reference/workflow-reference.md#acts-as). A bot may also hold an
6e1fca51
Keenan Nemetz
6d ago
70 ordinary key, provisioned the way a person's is, for an automation that is not a
338a19dd
Keenan Nemetz
6d ago
71 workflow run. A CI runner is that case: the runner process uses the bot's ordinary
72 key to lease work, while the jobs it runs still get a credential for the run. See
73 [Runners § The runner's account](runner.md#the-runners-account).
29a795b9
Keenan Nemetz
7d ago
74
75 Retiring a bot revokes it immediately: its key stops resolving, and a queued job it
76 was a runner for is no longer owed. Its name stays attached to its history, like
77 anyone else's.
78
c14588ef
Keenan Nemetz
7d ago
79 ## Profiles and the user directory
a6b42433
Keenan Nemetz
8d ago
80
b7396a11
Keenan Nemetz
8h ago
81 Account names are public on an instance. `/-/u` lists everyone who has an account,
82 and `/-/u/<name>` is that person's profile: what they have opened, reviewed and
a6b42433
Keenan Nemetz
8d ago
83 commented on, and the repositories that work is in. Retiring an account keeps its
84 page, marked as no longer active, because every closed issue and merged change
85 still refers to the name.
86
4739664b
Keenan Nemetz
7d ago
87 The directory is a table of every account and its visible counts. Search it by
88 name, narrow it to active or retired accounts, and order it from a column header
89 — by name, repositories, issues, changes, reviews or joining date. The search, the
90 filter, the order and the page are all in the URL, so a view can be linked and
91 paging cannot change the question.
92
a6b42433
Keenan Nemetz
8d ago
93 What a profile counts is not public. Every number and every event is limited to
94 the repositories the reader may open, so two people can see different totals for
95 the same account, and a private repository's work contributes nothing to a
96 stranger's view. The page says what its numbers cover instead of pretending they
97 are the whole history. The same rule covers the counts beside a name in the
98 directory.
99
100 A profile is derived, not stored. The numbers and the feed are read from the web
101 index (`data/index.db`), which is rebuilt from Git and from the CI records; no
102 profile is written to `data/users.db`. Keys, passkeys, sessions and a person's
103 instance role are not shown, with one exception: an instance admin sees the role
104 on the profile, because they can already see it on the admin pages. Git commit
105 authors are shown as text and never counted, because a commit's author is a name
106 someone typed, not the credential that proves an account.
107
108 Writing `@name` in an issue, a comment, a change request, or a release note links
109 to that account's profile. It is the same mention syntax a workflow's `mentioned`
110 filter evaluates ([Workflows](workflows.md)), so a name that reads as an address
111 on the page is the name a job sees. A name that does not belong to an account
112 stays plain text, and a mention inside a code sample is not a mention.
113
114 Both pages ask crawlers to leave them out of an index. The names are public; a
115 scraped roster is not what public has to mean.
116
6c84d387
Keenan Nemetz
16d ago
117 Account data lives in `data/users.db`. It also holds passkeys, one-time
118 enrollment links, browser sessions, repository access rules, and file locks.
ab326d3a
Keenan Nemetz
14d ago
119 Credentials are public keys, and session or enrollment-link tokens are stored
120 only as hashes. Vole reloads the roster every few seconds, so key changes and retirements
6c84d387
Keenan Nemetz
16d ago
121 take effect without restarting the server.
520470e1
Keenan Nemetz
16d ago
122
123 ```sh
2e024cc9
Keenan Nemetz
14d ago
124 vole admin account create alice --key ~/.ssh/id_ed25519.pub
125 vole admin account create bob --key bob.pub
126 vole admin account add-key alice --key ~/.ssh/id_ed25519_work.pub
127 vole admin account list
128 vole admin account rm-key alice SHA256:...
129 vole admin account retire alice
130 vole admin account backup backup/users.db
520470e1
Keenan Nemetz
16d ago
131 ```
132
2e024cc9
Keenan Nemetz
14d ago
133 `account create --key` writes the account and its first SSH key together. Without
6c84d387
Keenan Nemetz
16d ago
134 `--key` (and with no key on stdin), it creates a browser-only account and prints a
2e024cc9
Keenan Nemetz
14d ago
135 one-time enrollment link. `account invite` issues a fresh link for an existing
136 account. `account add-key` adds another SSH key; use it to rotate credentials
62576fa8
Keenan Nemetz
14d ago
137 without changing the account that access rules and approvals refer to. There is no
474a812d
Keenan Nemetz
16d ago
138 command to change the account name.
6c84d387
Keenan Nemetz
16d ago
139
140 ## Browser sign-in
141
ab326d3a
Keenan Nemetz
14d ago
142 Browser sign-in uses a passkey (Touch ID, Windows Hello, a phone, or a security
6c84d387
Keenan Nemetz
16d ago
143 key). It requires `public_url` to be an `https://` URL; Vole refuses to start a
144 WebAuthn ceremony without a canonical secure origin.
145
ab326d3a
Keenan Nemetz
14d ago
146 1. An instance admin creates the account and gives the person its one-time
f920a9f1
Keenan Nemetz
16d ago
147 enrollment link, or the server has `open_registration = true` and the person
b7396a11
Keenan Nemetz
8h ago
148 creates the account at `/-/register`.
f920a9f1
Keenan Nemetz
16d ago
149 2. The person registers a passkey.
6c84d387
Keenan Nemetz
16d ago
150 3. The passkey creates a browser session. The session cookie is HttpOnly and
151 Secure; Vole stores only its hash.
7d7718fa
Keenan Nemetz
6h ago
152 4. From **Account**, the person can register another passkey, rename or remove the
153 ones they have, add or remove SSH keys, or review their sessions. A label is
154 display text only: the credential is what signs in, so renaming one is not a
155 re-registration. Removing a passkey cannot be undone, and the last one is
156 refused while it is the account's only way in — an SSH key counts as a way in
157 for Git, so an account with one loses only its browser sign-in. Otherwise the
158 person would need an admin to invite them back.
6c84d387
Keenan Nemetz
16d ago
159
ad79b7ab
Keenan Nemetz
3d ago
160 The account field opts into WebAuthn conditional UI (`autocomplete="username
161 webauthn"`), so a supporting browser can offer a saved passkey while the field is
162 focused. The username-first form remains for browsers without autofill and for a
163 passkey created before discoverable credentials were required. New passkeys are
164 registered as discoverable credentials (resident keys) and require user
165 verification; that is what lets the browser find one without being told which
166 account it belongs to.
167
6c84d387
Keenan Nemetz
16d ago
168 A link is single-use and expires. Issuing a new link invalidates the previous
ab326d3a
Keenan Nemetz
14d ago
169 one. If the person loses every passkey, an instance admin issues a new link; there
6c84d387
Keenan Nemetz
16d ago
170 is no password reset to phish. Retiring an account deletes its sessions
7d7718fa
Keenan Nemetz
6h ago
171 immediately, and removing a passkey deletes the sessions that passkey created,
172 including the one the removal was made from.
173
174 Both writes are audited: `identity.credential-rename` names the passkey and its new
175 label, and `identity.credential-remove` names the passkey, with the account as the
176 subject. The account page's own record of the write is `web.passkey.rename` and
177 `web.passkey.remove`, the same way `web.key.add` and `web.key.remove` sit beside the
178 identity actions for SSH keys.
6c84d387
Keenan Nemetz
16d ago
179
b7396a11
Keenan Nemetz
8h ago
180 The link carries its token in the URL fragment — `https://forge.example.com/-/invite#<token>`
edf9db6e
Keenan Nemetz
13d ago
181 — so the browser never sends it to the server, a proxy's access log, or the
b7396a11
Keenan Nemetz
8h ago
182 referrer of anything the page loads. Links issued in the older `/-/invite/<token>`
edf9db6e
Keenan Nemetz
13d ago
183 shape still work: Vole redirects them into the fragment, and its own logs redact
184 the old path.
185
186 Sign-in, registration, enrollment and passkey-adding are rate-limited per client
187 address (and per account name for sign-in), and the ceremony store is bounded and
188 expires after five minutes. A client that exceeds a budget gets `429` with
189 `Retry-After` rather than a slower answer. Behind a proxy, set
190 `git_http_client_ip_header` so those budgets follow the real client instead of
191 the proxy.
192
193 An account name is not proof of anything: asking to sign in as an account that
194 does not exist, is retired, or has no passkey gets the same assertion request as
195 a real one. The difference only appears when the ceremony is finished, and the
196 failure is generic.
197
198 A passkey's signature counter must advance. If an assertion arrives with a
199 counter that did not move, two copies of the key probably exist, so the sign-in
200 is refused and the event is recorded in the audit log as
201 `auth.clone-warning`. This can lock out an authenticator with a broken counter
202 until an admin removes the passkey; that is the deliberate trade. Synced
203 passkeys that always report zero are handled by WebAuthn's own rule and do not
204 trigger the refusal.
205
b7396a11
Keenan Nemetz
8h ago
206 With `open_registration = true`, `/-/register` creates a new account and its first
ab326d3a
Keenan Nemetz
14d ago
207 passkey without an instance admin. Set `open_registration_role` to the
5b814e99
Keenan Nemetz
14d ago
208 instance-wide role those accounts should have; `contribute` is the usual choice
209 for an open-source instance:
f920a9f1
Keenan Nemetz
16d ago
210
5b814e99
Keenan Nemetz
14d ago
211 ```toml
212 open_registration = true
213 open_registration_role = "contribute"
f920a9f1
Keenan Nemetz
16d ago
214 ```
215
5b814e99
Keenan Nemetz
14d ago
216 That role applies to every **public** repository, so a new contributor can read,
217 discuss, and push to `refs/heads/contrib/<name>/` without being added to each
9e8cc155
Keenan Nemetz
13d ago
218 repository. It does not open private repositories. Only `none`, `read` and
219 `contribute` are accepted: a stranger who typed a name must not be able to move a
220 shared branch, approve a review or administer the instance, so `maintain` and
221 `admin` are refused and have to be granted explicitly. A registered account can
222 add its own SSH key from **Account**, which is what lets it push. Open
223 registration has no email verification and is rate-limited per address, so leave
224 it off on a private instance unless you want that policy.
f920a9f1
Keenan Nemetz
16d ago
225
6c84d387
Keenan Nemetz
16d ago
226 Admins can inspect and revoke sessions:
227
228 ```sh
2e024cc9
Keenan Nemetz
14d ago
229 vole admin account credentials alice
230 vole admin account rm-credential alice <credential-id>
231 vole admin account sessions alice
232 vole admin account revoke-sessions alice
233 vole admin account revoke-sessions --all
6c84d387
Keenan Nemetz
16d ago
234 ```
235
236 `revoke-sessions --all` is the recovery step after restoring an old `users.db`.
c36d5073
Keenan Nemetz
16d ago
237
520470e1
Keenan Nemetz
16d ago
238 To see which account the current remote's key belongs to:
239
240 ```sh
241 vole whoami
242 ```
243
eec0e603
Keenan Nemetz
6d ago
244 To see the role that account holds on the repository named by the remote:
245
246 ```sh
247 vole whoami --role
248 ```
249
3abbe749
Keenan Nemetz
16d ago
250 ## Remote administration
251
252 By default, server management commands run on the host. To use them from another
253 machine, mark an account as an **instance admin** and pass `--remote`:
254
255 ```sh
2e024cc9
Keenan Nemetz
14d ago
256 vole admin account grant-admin alice
257 vole admin --remote ssh://git@forge.example.com:2222 account create bob --key bob.pub
3abbe749
Keenan Nemetz
16d ago
258 vole admin --remote origin repo create demo --owner bob
2e024cc9
Keenan Nemetz
14d ago
259 vole admin --remote origin acl set-default-role demo read
3abbe749
Keenan Nemetz
16d ago
260 ```
261
ab326d3a
Keenan Nemetz
14d ago
262 An instance admin is the instance-wide `admin` role. They can create accounts and
5b814e99
Keenan Nemetz
14d ago
263 repositories, change access rules anywhere, and use `--remote`. They are also a
264 repository admin on every repository, private ones included: they can push,
265 change settings, and manage access without a repository-specific entry. SSH Git
266 and SSH LFS use the key's account for this check; anonymous HTTP clone has no
267 identity, so a private repository still answers `404` there.
381d6aac
Keenan Nemetz
14d ago
268
43e681d8
Keenan Nemetz
5d ago
269 Instance admins also get an **Admin** page in the top navigation. Its tabs cover
ea21f923
Keenan Nemetz
5d ago
270 the instance's overview, storage, runners, and effective configuration; an
271 Accounts tab that creates a person from an enrollment link or an automation
272 account; and the audit log. Existing accounts are managed from the users
b7396a11
Keenan Nemetz
8h ago
273 directory at `/-/u`, which is searchable and paginated: an instance admin gets a
ea21f923
Keenan Nemetz
5d ago
274 **Manage** action on each row that opens the account page, where they set its
275 instance-wide role (including `admin`), retire it, start a fresh enrollment link,
276 and inspect or revoke SSH keys and browser sessions. Repository creation is a
277 **New repository** action on the home page, and per-repository access rules live
278 on that repository's **Settings** tab, visible there to instance admins.
395554f4
Keenan Nemetz
15d ago
279
5b814e99
Keenan Nemetz
14d ago
280 The first instance admin must be granted on the host. Instance admin is the
2e024cc9
Keenan Nemetz
14d ago
281 instance-wide `admin` role, so `vole admin account role alice admin` and
282 `vole admin account grant-admin alice` mean the same thing. Vole refuses to remove or
3abbe749
Keenan Nemetz
16d ago
283 retire the last active instance admin, so a remote mistake cannot lock everyone
284 out. Add `--json` to any remote command for a machine-readable response:
285
286 ```sh
2e024cc9
Keenan Nemetz
14d ago
287 vole admin --remote origin --json account list
3abbe749
Keenan Nemetz
16d ago
288 ```
289
290 Backups, garbage collection, and index rebuilds stay on the host; they are
291 recovery operations, not remote administration.
292
520470e1
Keenan Nemetz
16d ago
293 ## Roles
294
5b814e99
Keenan Nemetz
14d ago
295 A role can be granted in two places:
296
297 - **instance-wide**, on the account: it applies to every public repository;
298 - **per repository**: an explicit entry for that repository.
299
300 The effective role is the higher of the repository's default role, the
301 repository entry for the account, and the account's instance-wide role. Instance
302 roles only take effect on public repositories. The exception is `admin`: an
303 instance admin is an admin on every repository, including private ones.
304
305 Each repository also has a default role for everyone and optional roles for
306 specific accounts.
520470e1
Keenan Nemetz
16d ago
307
308 | Role | What it allows |
309 |---|---|
310 | `none` | No access |
311 | `read` | Clone, fetch, and browse the repository |
5b814e99
Keenan Nemetz
14d ago
312 | `contribute` | Read, file issues and comments, push only `refs/heads/contrib/<name>/*`, and open change requests from those branches |
0c40a1d3
Keenan Nemetz
7d ago
313 | `maintain` | Everything the contribute role can do, plus push shared branches, triage issues, approve change requests, and manage the repository's secrets |
6c84d387
Keenan Nemetz
16d ago
314 | `admin` | Everything a maintainer can do, plus manage access, push protected branches, override locks, and repair metadata |
520470e1
Keenan Nemetz
16d ago
315
62b59a76
Keenan Nemetz
13d ago
316 Rules are edited with a compare-and-swap. A page or a command that read the rules
317 before somebody else changed them is refused rather than allowed to write its copy
318 back, so an instance admin revoking a role cannot be undone by a repository admin
319 who still has the older page open. The operations that own one field — the
320 visibility toggle, adding one account, one runners entry — re-read the row and
321 apply only that field, so an unrelated concurrent change is kept.
322
520470e1
Keenan Nemetz
16d ago
323 A new repository is private. Its default role is `none`, and its owner is an
324 admin. Private repositories return `404` to anonymous visitors, so people cannot
5b814e99
Keenan Nemetz
14d ago
325 tell whether they exist. An instance-wide `read`, `contribute`, or `maintain`
326 grant does not open a private repository; an instance admin does.
520470e1
Keenan Nemetz
16d ago
327
328 To publish a repository, give the default role `read`:
329
330 ```sh
2e024cc9
Keenan Nemetz
14d ago
331 vole admin acl set-default-role demo read
520470e1
Keenan Nemetz
16d ago
332 ```
333
1948358f
Keenan Nemetz
15d ago
334 A repository admin can also change the description, default branch, and
2973f727
Keenan Nemetz
15d ago
335 visibility, and rename or delete the repository, from its **Settings** tab in
5b814e99
Keenan Nemetz
14d ago
336 the browser. An instance admin has those rights on every repository too.
e189a23e
Keenan Nemetz
1d ago
337 Settings is split into tabs: **General** (the repository's own fields),
338 **Access** (the instance-admin rules), **Secrets**, and **Danger** (rename and
339 delete). Each tab is offered only to the accounts that may use it, and a viewer
340 who can use one of them sees no row at all. Visibility
1948358f
Keenan Nemetz
15d ago
341 toggles the default role between `none` and `read`; explicit role entries are
5b814e99
Keenan Nemetz
14d ago
342 left alone. The Accounts table shows only accounts with an explicit role or
343 runner entry; use **Add account** below it to grant another account a
344 repository-specific role. Access rules and review policy remain instance-admin
e189a23e
Keenan Nemetz
1d ago
345 operations: instance admins can edit them on the repository's **Access** tab,
5b814e99
Keenan Nemetz
14d ago
346 or with `vole admin acl`.
1948358f
Keenan Nemetz
15d ago
347
e189a23e
Keenan Nemetz
1d ago
348 A maintainer who is not an admin also opens the page, and its one tab is
349 Secrets. Values a repository keeps out of its code — a deploy key, a
0c40a1d3
Keenan Nemetz
7d ago
350 registry token, a staging password — belong to the accounts that approve work and allow CI,
351 so the page shows them the names, versions and read times, and the form that sets
352 them. It never shows a value; `vole secret get` prints one when somebody needs it.
353
520470e1
Keenan Nemetz
16d ago
354 ## Changing access
355
356 ```sh
2e024cc9
Keenan Nemetz
14d ago
357 vole admin account create carol --role contribute --key carol.pub
358 vole admin account role alice maintain
359 vole admin account role bob none
5b814e99
Keenan Nemetz
14d ago
360
3abbe749
Keenan Nemetz
16d ago
361 vole admin acl show demo
2e024cc9
Keenan Nemetz
14d ago
362 vole admin acl set-role demo alice maintain
363 vole admin acl set-role demo bob contribute
364 vole admin acl set-role demo carol read
365 vole admin acl set-default-role demo read
366 vole admin acl protect add demo refs/heads/main
367 vole admin acl set-approvals demo 2
368 vole admin acl checks enable demo
0789f22e
Keenan Nemetz
9d ago
369 vole admin acl require-issues enable demo
2e024cc9
Keenan Nemetz
14d ago
370 vole admin acl runners add demo ci-runner-1
520470e1
Keenan Nemetz
16d ago
371 ```
372
2e024cc9
Keenan Nemetz
14d ago
373 - `account create --role` and `account role` set the account's instance-wide role.
5b814e99
Keenan Nemetz
14d ago
374 On a public repository that role applies without a per-repository entry; on a
375 private repository it does not, unless the role is `admin`.
2e024cc9
Keenan Nemetz
14d ago
376 - `set-role` gives one account a role in one repository. The roles are `none`,
5b814e99
Keenan Nemetz
14d ago
377 `read`, `contribute`, `maintain` and `admin`. `contributor` and `contrib` are
378 accepted as aliases for `contribute`; `write` is accepted as the historical
379 spelling of `maintain`. A repository entry can raise the instance-wide role but
2e024cc9
Keenan Nemetz
14d ago
380 cannot lower it. Use `none` to remove the entry.
381 - `set-default-role` changes the role for everyone else. `read` or higher makes
382 the repository public.
383 - `protect` manages the protected branch patterns. `set` replaces the list,
384 `add` and `remove` change it one entry at a time, `clear` empties it, and
93c7cae8
Keenan Nemetz
23h ago
385 `list` shows it. A pattern is an exact ref (`refs/heads/main`) or a prefix
386 ending in one `*` (`refs/heads/release/*`); `add` and `set` refuse anything
387 else, because a pattern the hook can never match would leave the branch open
388 while `acl show` listed it as protected. Admins can still push to protected
389 branches directly.
2e024cc9
Keenan Nemetz
14d ago
390 - `set-approvals` sets how many approvals a protected branch needs. `0` means
520470e1
Keenan Nemetz
16d ago
391 admin-only.
2e024cc9
Keenan Nemetz
14d ago
392 - `checks enable` requires CI checks to pass before a protected branch can be
393 updated; `checks disable` allows approvals alone to land it.
0789f22e
Keenan Nemetz
9d ago
394 - `require-issues enable` requires at least one linked issue when a new change
395 request is opened; `disable` makes it optional again. The rule is checked when
396 the request is opened, so requests that already exist keep their history and
397 can still be approved and landed.
2e024cc9
Keenan Nemetz
14d ago
398 - `runners` manages the accounts whose CI results this repository trusts. It uses
399 the same `list`, `add`, `remove`, `set`, and `clear` verbs as `protect`.
62b59a76
Keenan Nemetz
13d ago
400 Admins always count. A retired account stops counting even while its name is
401 still in the list: the ACL row outlives the account, so membership alone is not
402 a grant that lasts. See
2e024cc9
Keenan Nemetz
14d ago
403 [Runners](runner.md#who-can-run-jobs-and-report-results).
520470e1
Keenan Nemetz
16d ago
404
6c84d387
Keenan Nemetz
16d ago
405 ## Contributors and their branches
406
ab326d3a
Keenan Nemetz
14d ago
407 The `contribute` role is for open-source repositories that accept outside patches.
7e0b5950
Keenan Nemetz
6d ago
408 A contributor can read, file issues and comments, manage the items it wrote, and
409 open a change request from a branch it may push — through the CLI or the browser,
410 which apply the same rules. Their Git writes are confined to a branch folder named
411 after their account name:
6c84d387
Keenan Nemetz
16d ago
412
413 ```text
474a812d
Keenan Nemetz
16d ago
414 refs/heads/contrib/<name>/<branch>
6c84d387
Keenan Nemetz
16d ago
415 ```
416
474a812d
Keenan Nemetz
16d ago
417 For example, account `alice` may push `refs/heads/contrib/alice/fix-docs`. They
7e0b5950
Keenan Nemetz
6d ago
418 cannot push `refs/heads/main`, another account's folder, or tags. The pre-receive
419 hook checks the namespace, and rebasing or deleting a branch inside their own
420 folder is allowed because it is not shared history.
421
422 They may also write `refs/heads/vole`, the issues branch, because that is where a
423 comment, an issue, or a change request lives. What the push may contain is the
424 same list the browser offers: a comment, an issue, a change request from their own
425 branch, or a change to an item they wrote. Pre-receive refuses anything else, so
426 the two transports cannot disagree about what a contributor may do.
474a812d
Keenan Nemetz
16d ago
427
62576fa8
Keenan Nemetz
14d ago
428 The namespace is the account name, so a ref says whose work it is without a
429 lookup. It is also why names are fixed at creation: renaming an account would
430 otherwise orphan every branch under its old folder.
6c84d387
Keenan Nemetz
16d ago
431
432 The browser's **Open change request** form lists only the branches the account
433 may use as a head. The head commit is resolved from that branch; a contributor
434 cannot name a commit they did not push. A maintainer can approve the request, and
435 landing it still goes through the same protected-branch gate.
436
04e00cfc
Keenan Nemetz
16d ago
437 The CLI's `vole issue new`, `vole issue comment` and `vole change new` write the
a781cd9b
Keenan Nemetz
6d ago
438 metadata branch directly. Pre-receive checks the same per-path rules as the
439 browser, so a `contribute` account can use those commands too: it may file an
440 issue, comment, and open a change from a branch in its own namespace.
6c84d387
Keenan Nemetz
16d ago
441
442 Contributor branches are ordinary branches except for one rule: CI does not run
564acd4b
Keenan Nemetz
15d ago
443 for them automatically. A maintainer or admin opens the change request for the
f6f4b1db
Keenan Nemetz
15d ago
444 branch and chooses **Allow CI** there. The approval names the exact
564acd4b
Keenan Nemetz
15d ago
445 commit, so every new push waits again. Treat runner isolation as part of the trust decision before granting
ab326d3a
Keenan Nemetz
14d ago
446 `contribute` on a public repository.
6c84d387
Keenan Nemetz
16d ago
447
4ac42680
Keenan Nemetz
7d ago
448 Allowing CI also grants that commit the repository's secrets, if its workflow
449 declares them. It is the same decision as letting the code run on a runner: the
450 values arrive on the machine that runs it, and any step in that job can read the
451 ones the job declared. A maintainer who would not hand a value to an outside patch
452 should not approve a tip whose workflow asks for it.
453
520470e1
Keenan Nemetz
16d ago
454 ## When rules are checked
455
456 Every rule is checked before Vole accepts a push. If a push breaks a rule, none of
6c84d387
Keenan Nemetz
16d ago
457 its branches move. Browser issue, comment, and approval writes check the same ACL
458 capability and use the same metadata writers and validation as the CLI.
520470e1
Keenan Nemetz
16d ago
459
460 That includes protected branches, approvals, required checks, file size limits,
461 locks, issue formatting, and the CI runner allowlist. Privileged actions — such as
ab326d3a
Keenan Nemetz
14d ago
462 an admin force-push, a lock override, an account change, or a merge through review —
43e681d8
Keenan Nemetz
5d ago
463 are written to `data/audit.log` and can be read by an instance admin at
464 **Admin → Audit log**.
520470e1
Keenan Nemetz
16d ago
465
466 For the reasoning behind this design, see
6c84d387
Keenan Nemetz
16d ago
467 [Architecture](architecture.md#trust-a-credential-resolves-to-an-account). For file locks, see
520470e1
Keenan Nemetz
16d ago
468 [LFS § Locking](lfs.md#locking-files).