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.
Name branches after the account, and labels after the display
474a812d199a109ba894124cd6f26be12c73285d · Keenan Nemetz · 16d ago · parent ade6107e
Name branches after the account, and labels after the display
A contributor's folder was `refs/heads/contrib/<account-id>/`, which told a
reader nothing without decoding a ULID. It is now the account name,
refs/heads/contrib/<name>/<branch>
and the name is therefore fixed at creation: renaming an account would strand
every ref under its old folder, so `vole admin user rename` is gone and the
users row itself — kept after retirement — is what stops a name being reissued.
What people see is now a separate, mutable display name, validated by the same
rules wherever it is set (CLI, remote admin, registration, `/account`) and shown
on pages, approvals, listings and refusals. Nothing is authorized by it and
nothing on disk is keyed by it: metadata authors stay account names because the
push boundary resolves them for ownership, and approvals stay account ids.
`user_names`, `Snapshot.Reserved` and `Account.Former` are dropped: with a fixed
name there are no former names to remember, and the UNIQUE column already does
the reserving. Opening an older roster adds the `display` column and drops the
alias table in place; contributor refs written under the old id namespace are
left where they are, readable, and landable by a maintainer.
docs/users-and-access.md +34−19
| @@ -5,18 +5,25 @@ Vole has no password database. An account has two kinds of credential: | |||
| 5 | 5 | - an **SSH key** for Git over SSH and remote administration; | |
| 6 | 6 | - a **passkey** for signing in to the browser. | |
| 7 | 7 | ||
| 8 | Both resolve to the same permanent account id, so access rules, approvals, and | ||
| 9 | audit records do not care which one you used. | ||
| 8 | Both resolve to the same account, so access rules, approvals, and audit records | ||
| 9 | do not care which one you used. | ||
| 10 | 10 | ||
| 11 | 11 | ## Accounts and keys | |
| 12 | 12 | ||
| 13 | An account has a permanent id and a name you can change. Access rules and | ||
| 14 | approvals are attached to the permanent id, so renaming an account does not break | ||
| 15 | anything. | ||
| 13 | An account has three names, and they are not interchangeable: | ||
| 16 | 14 | ||
| 17 | Names are never reused. When you retire an account, its keys stop working and its | ||
| 18 | name stays reserved. An old reference can never silently point at a different | ||
| 19 | person. | ||
| 15 | - the **name**, chosen when the account is created and fixed for good. It is what | ||
| 16 | your contributor branches are named after (`refs/heads/contrib/<name>/`), so a | ||
| 17 | rename would strand every ref and every author field already written. Keep it. | ||
| 18 | - the **display name**, an optional label shown next to your account on requests, | ||
| 19 | issues, approvals and user listings. It can be anything printable and can be | ||
| 20 | changed at any time. Nothing is authorized by it. | ||
| 21 | - the **id**, a ULID Vole assigns internally. Access rules and approval files are | ||
| 22 | keyed by it. | ||
| 23 | |||
| 24 | Names are never reused: because the account row outlives retirement, retiring | ||
| 25 | someone keeps their name attached to them. An old reference can never silently | ||
| 26 | point at a different person. | ||
| 20 | 27 | ||
| 21 | 28 | Account data lives in `data/users.db`. It also holds passkeys, one-time | |
| 22 | 29 | enrollment links, browser sessions, repository access rules, and file locks. | |
| @@ -26,10 +33,11 @@ take effect without restarting the server. | |||
| 26 | 33 | ||
| 27 | 34 | ```sh | |
| 28 | 35 | vole admin user create alice --key ~/.ssh/id_ed25519.pub | |
| 36 | vole admin user create bob --display "Bob Smith" --key bob.pub | ||
| 29 | 37 | vole admin user add-key alice --key ~/.ssh/id_ed25519_work.pub | |
| 30 | 38 | vole admin user list | |
| 31 | 39 | vole admin user rm-key alice SHA256:... | |
| 32 | vole admin user rename alice a-alice | ||
| 40 | vole admin user display alice "Alice O'Brien" | ||
| 33 | 41 | vole admin user retire alice | |
| 34 | 42 | vole admin user backup backup/users.db | |
| 35 | 43 | ``` | |
| @@ -38,7 +46,9 @@ vole admin user backup backup/users.db | |||
| 38 | 46 | `--key` (and with no key on stdin), it creates a browser-only account and prints a | |
| 39 | 47 | one-time enrollment link. `user invite` issues a fresh link for an existing | |
| 40 | 48 | account. `user add-key` adds another SSH key; use it to rotate credentials | |
| 41 | without changing the account id that access rules and approvals refer to. | ||
| 49 | without changing the account that access rules and approvals refer to. | ||
| 50 | `user display` sets or clears the label (an empty string clears it); there is no | ||
| 51 | command to change the account name. | ||
| 42 | 52 | ||
| 43 | 53 | ## Browser sign-in | |
| 44 | 54 | ||
| @@ -70,7 +80,7 @@ vole admin acl default demo contributor | |||
| 70 | 80 | ``` | |
| 71 | 81 | ||
| 72 | 82 | A registered account can add its own SSH key from **Account**, which is what lets | |
| 73 | it push to `refs/heads/contrib/<account-id>/`. Open registration has no email | ||
| 83 | it push to `refs/heads/contrib/<name>/`. Open registration has no email | ||
| 74 | 84 | verification, so leave it off on a private instance unless you want that policy. | |
| 75 | 85 | ||
| 76 | 86 | Admins can inspect and revoke sessions: | |
| @@ -129,7 +139,7 @@ accounts. | |||
| 129 | 139 | |---|---| | |
| 130 | 140 | | `none` | No access | | |
| 131 | 141 | | `read` | Clone, fetch, and browse the repository | | |
| 132 | | `contributor` | Read, file issues and comments, push only `refs/heads/contrib/<account-id>/*`, and open change requests from those branches | | ||
| 142 | | `contributor` | Read, file issues and comments, push only `refs/heads/contrib/<name>/*`, and open change requests from those branches | | ||
| 133 | 143 | | `maintain` | Everything a contributor can do, plus push shared branches, triage issues, and approve change requests | | |
| 134 | 144 | | `admin` | Everything a maintainer can do, plus manage access, push protected branches, override locks, and repair metadata | | |
| 135 | 145 | ||
| @@ -175,17 +185,22 @@ vole admin acl runners demo ci-runner-1 | |||
| 175 | 185 | The `contributor` role is for open-source projects that accept outside patches. | |
| 176 | 186 | A contributor can read, file issues and comments, and open a change request from | |
| 177 | 187 | the browser. Their Git writes are confined to a branch folder named after their | |
| 178 | permanent account id: | ||
| 188 | account name: | ||
| 179 | 189 | ||
| 180 | 190 | ```text | |
| 181 | refs/heads/contrib/<account-id>/<branch> | ||
| 191 | refs/heads/contrib/<name>/<branch> | ||
| 182 | 192 | ``` | |
| 183 | 193 | ||
| 184 | For example, account `01M5JR2JK29KKMAA1T9MDNTPBP` may push | ||
| 185 | `refs/heads/contrib/01M5JR2JK29KKMAA1T9MDNTPBP/fix-docs`. They cannot push | ||
| 186 | `refs/heads/main`, another account's folder, tags, or the `vole` metadata branch. | ||
| 187 | The pre-receive hook checks the namespace, and rebasing or deleting a branch | ||
| 188 | inside their own folder is allowed because it is not shared history. | ||
| 194 | For example, account `alice` may push `refs/heads/contrib/alice/fix-docs`. They | ||
| 195 | cannot push `refs/heads/main`, another account's folder, tags, or the `vole` | ||
| 196 | metadata branch. The pre-receive hook checks the namespace, and rebasing or | ||
| 197 | deleting a branch inside their own folder is allowed because it is not shared | ||
| 198 | history. | ||
| 199 | |||
| 200 | The namespace is the account name rather than the internal id, so a ref says | ||
| 201 | whose work it is without decoding a ULID. It is also why names are fixed at | ||
| 202 | creation: renaming an account would otherwise orphan every branch under its old | ||
| 203 | folder. A display name is safe to change because no ref is named after it. | ||
| 189 | 204 | ||
| 190 | 205 | The browser's **Open change request** form lists only the branches the account | |
| 191 | 206 | may use as a head. The head commit is resolved from that branch; a contributor | |