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.

Add browser auth, contributor access, and gated CI

6c84d387e5f608360e30484a1a5b1cff41076850 · Keenan Nemetz · 16d ago · parent c36d5073

Add browser auth, contributor access, and gated CI

Browser sign-in now uses passkeys. Enrollment links are one-time, sessions are hashed in users.db, and issue/comment/approval writes use the session account. HTTPS public_url is required for WebAuthn.

Add the contributor role between read and maintain. Contributors can discuss and open change requests, and may push only refs/heads/contrib/<account-id>/. Contributor CI is held per commit until a maintainer allows the exact tip from the repository page.

Rename the write role to maintain. write remains accepted as the historical spelling, and access.Write remains as a Go alias so existing callers and ACL rows keep working.

Update the web, CLI, queue schema, and guides for the new flows, and cover the role, namespace, auth, and CI-gating paths in tests.

docs/users-and-access.md +92−16

@@ -1,8 +1,12 @@
1 1 # Users and access
2 2
3 Vole has no password database and no login screen. Your SSH key is your identity:
4 the same key you use to push. That means one less account system to maintain, and
5 one less password to protect.
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
8 Both resolve to the same permanent account id, so access rules, approvals, and
9 audit records do not care which one you used.
6 10
7 11 ## Accounts and keys
8 12
@@ -14,9 +18,11 @@ Names are never reused. When you retire an account, its keys stop working and it
14 18 name stays reserved. An old reference can never silently point at a different
15 19 person.
16 20
17 Account data lives in `data/users.db`. It also holds repository access rules and
18 file locks. Nothing in the file is secret. Vole reloads it every few seconds, so
19 key changes and retirements take effect without restarting the server.
21 Account data lives in `data/users.db`. It also holds passkeys, one-time
22 enrollment links, browser sessions, repository access rules, and file locks.
23 Credentials are public keys and session or invite tokens are stored only as
24 hashes. Vole reloads the roster every few seconds, so key changes and retirements
25 take effect without restarting the server.
20 26
21 27 ```sh
22 28 vole admin user create alice --key ~/.ssh/id_ed25519.pub
@@ -28,9 +34,42 @@ vole admin user retire alice
28 34 vole admin user backup backup/users.db
29 35 ```
30 36
31 `user create` requires the first key and writes the account and key together.
32 `user add-key` adds another key; use it to rotate credentials without changing
33 the account id that access rules and approvals refer to.
37 `user create --key` writes the account and its first SSH key together. Without
38 `--key` (and with no key on stdin), it creates a browser-only account and prints a
39 one-time enrollment link. `user invite` issues a fresh link for an existing
40 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.
42
43 ## Browser sign-in
44
45 Browser login uses a passkey (Touch ID, Windows Hello, a phone, or a security
46 key). It requires `public_url` to be an `https://` URL; Vole refuses to start a
47 WebAuthn ceremony without a canonical secure origin.
48
49 1. An administrator creates the account and gives the person its one-time
50 enrollment link.
51 2. The person opens the link and registers a passkey.
52 3. The passkey creates a browser session. The session cookie is HttpOnly and
53 Secure; Vole stores only its hash.
54 4. From **Account**, the person can register another passkey or review their
55 sessions.
56
57 A link is single-use and expires. Issuing a new link invalidates the previous
58 one. If the person loses every passkey, an administrator issues a new link; there
59 is no password reset to phish. Retiring an account deletes its sessions
60 immediately, and removing a passkey deletes the sessions that passkey created.
61
62 Admins can inspect and revoke sessions:
63
64 ```sh
65 vole admin user credentials alice
66 vole admin user rm-credential alice <credential-id>
67 vole admin user sessions alice
68 vole admin user revoke-sessions alice
69 vole admin user revoke-sessions --all
70 ```
71
72 `revoke-sessions --all` is the recovery step after restoring an old `users.db`.
34 73
35 74 To see which account the current remote's key belongs to:
36 75
@@ -76,8 +115,9 @@ accounts.
76 115 |---|---|
77 116 | `none` | No access |
78 117 | `read` | Clone, fetch, and browse the repository |
79 | `write` | Push normal branches and work with issues and change requests |
80 | `admin` | Manage access, push protected branches, override locks, and repair metadata |
118 | `contributor` | Read, file issues and comments, push only `refs/heads/contrib/<account-id>/*`, and open change requests from those branches |
119 | `maintain` | Everything a contributor can do, plus push shared branches, triage issues, and approve change requests |
120 | `admin` | Everything a maintainer can do, plus manage access, push protected branches, override locks, and repair metadata |
81 121
82 122 A new repository is private. Its default role is `none`, and its owner is an
83 123 admin. Private repositories return `404` to anonymous visitors, so people cannot
@@ -93,8 +133,9 @@ vole admin acl default demo read
93 133
94 134 ```sh
95 135 vole admin acl show demo
96 vole admin acl set demo alice write
97 vole admin acl set demo bob read
136 vole admin acl set demo alice maintain
137 vole admin acl set demo bob contributor
138 vole admin acl set demo carol read
98 139 vole admin acl default demo read
99 140 vole admin acl protect demo refs/heads/main
100 141 vole admin acl approvals demo 2
@@ -102,7 +143,9 @@ vole admin acl checks demo on
102 143 vole admin acl runners demo ci-runner-1
103 144 ```
104 145
105 - `set` gives one account a role.
146 - `set` gives one account a role. The roles are `none`, `read`, `contributor`,
147 `maintain` and `admin`. `contribute` and `contrib` are accepted as aliases for
148 `contributor`; `write` is accepted as the historical spelling of `maintain`.
106 149 - `default` changes the role for everyone else. `read` or higher makes the
107 150 repository public.
108 151 - `protect` replaces the list of protected branch patterns. Admins can still push
@@ -113,10 +156,43 @@ vole admin acl runners demo ci-runner-1
113 156 - `runners` names the accounts whose CI results this repository trusts. Admins
114 157 always count. See [CI](ci.md#who-can-run-jobs-and-report-results).
115 158
159 ## Contributors and their branches
160
161 The `contributor` role is for open-source projects that accept outside patches.
162 A contributor can read, file issues and comments, and open a change request from
163 the browser. Their Git writes are confined to a branch folder named after their
164 permanent account id:
165
166 ```text
167 refs/heads/contrib/<account-id>/<branch>
168 ```
169
170 For example, account `01M5JR2JK29KKMAA1T9MDNTPBP` may push
171 `refs/heads/contrib/01M5JR2JK29KKMAA1T9MDNTPBP/fix-docs`. They cannot push
172 `refs/heads/main`, another account's folder, tags, or the `vole` metadata branch.
173 The pre-receive hook checks the namespace, and rebasing or deleting a branch
174 inside their own folder is allowed because it is not shared history.
175
176 The browser's **Open change request** form lists only the branches the account
177 may use as a head. The head commit is resolved from that branch; a contributor
178 cannot name a commit they did not push. A maintainer can approve the request, and
179 landing it still goes through the same protected-branch gate.
180
181 The CLI's `vole issue new`, `vole issue comment` and `vole request new` write the
182 metadata branch directly, so those commands need `maintain`. Contributors use the
183 web for those actions.
184
185 Contributor branches are ordinary branches except for one rule: CI does not run
186 for them automatically. A maintainer or admin must choose **Allow CI for this tip** on
187 the repository page. The approval names the exact commit, so every new push waits
188 again. Treat runner isolation as part of the trust decision before granting
189 `contributor` on a public project.
190
116 191 ## When rules are checked
117 192
118 193 Every rule is checked before Vole accepts a push. If a push breaks a rule, none of
119 its branches move.
194 its branches move. Browser issue, comment, and approval writes check the same ACL
195 capability and use the same metadata writers and validation as the CLI.
120 196
121 197 That includes protected branches, approvals, required checks, file size limits,
122 198 locks, issue formatting, and the CI runner allowlist. Privileged actions — such as
@@ -124,5 +200,5 @@ an admin force-push, a lock override, a user change, or a merge through review
124 200 are written to `data/audit.log`.
125 201
126 202 For the reasoning behind this design, see
127 [Architecture](architecture.md#trust-your-key-is-your-identity). For file locks, see
203 [Architecture](architecture.md#trust-a-credential-resolves-to-an-account). For file locks, see
128 204 [LFS § Locking](lfs.md#locking-files).