Skip to content
quarry Documentation
Login

Documentation

Summary

Today the settings page is a table of text inputs holding fossil capability strings. To let someone chat you type C. That is a users' manual in the shape of a form. This RFC keeps RFC 0004's model — fossil letters as the stored truth, lossless both ways — and replaces the interface: five named roles described by who they are for, a short list of abilities written as sentences for deviating from a role, and the raw string always one click away with its meaning decoded beside it. Roles gate nothing on their own; they are a way of writing cap strings. The legacy repo_role table, which still gates the web verifier in parallel with the letters, is retired so the page's sentences have one verifier to be true against.

Motivation

Three facts from the forge as it is:

The field agrees on the shape of the fix. GitHub: five roles named by who they are for, custom roles as a base plus permissions. GitLab: five roles, custom roles as a base plus granular permissions, the base immutable. Forgejo: teams with none/read/write per unit. Fossil itself: four inherited categories and a user editor that tags each letter with the category that already grants it. The permission-settings UX literature says a permissions page "is not just a list of toggles. It is an explanation system." Every good product here shows what is needed for the decision at hand and keeps the rest on demand.

Decisions

The proposal that preceded this RFC left four questions open. They are decided here, with the reasoning, so the build does not reopen them.

Five roles, not six: Contributor folds into Reader. Contributor differed from Reader by two letters, b and B (attachments). On this forge landings are the default path for everyone who may post — a landing is a forum thread — so "someone whose work lands through landings" is every Reader, not a distinct kind of person. Attachments ride the same moderation as posts (3 is the moderated write). A role that differs by a distinction nobody will remember is a role nobody will choose correctly.

Readers chat. C is in the Reader string and in the signed-in category of the public preset. Chat is the forum in real time; anyone who may post in one may speak in the other, and the moderation letter is the same (5). A forge that wants quieter chat removes one letter from one constant, once, in a diff someone reviews.

No teams in this RFC. GitHub, GitLab and Forgejo assign roles to teams as well as people, and quarry will (ticket 8318bd2819ca). The model here is team-shaped already — a team is a name that expands to a role on a set of repositories — and nothing below needs changing to add one. Building the people layer first and the team layer on it is the order that does not have to be undone.

Site admin stays an account flag, mapped to s everywhere, and is shown. The fleet's account page lists a person's role per repository; the site-admin flag appears beside them as what it is, so "why can dan do this" is answered on one page.

The model

Three layers. Each compiles to the one below; the bottom is what is stored.

Layer 1 — Roles

A role is a name, a sentence about who it is for, and a fixed cap string.

Role Who it is for Cap string What that adds
Reader reads, asks, discusses — most accounts on a public project u3bBC7 reader category (kptw: edit wiki, own password, ticket reports, edit tickets) plus forum posting, attachments, chat, email alerts
Developer checks in — on any branch that is not protected uv3bBC7 Reader plus the developer category (ei: check-in, see addresses). RFC 0007 decides which branches; this decides whether.
Maintainer a developer who also keeps the place tidy uv45bBlqCA7 Developer plus posting without moderation, moderating the forum, wiki and tickets, and announcements. Usually also an owner of protected branches — a protection row, not a letter.
Admin changes this repository's settings a everything except setup, private branches, unversioned writes — fossil's Admin-vs-Setup rule
Custom anything that is not exactly one of the above the string as written inferred on read: the nearest role and the difference, e.g. Developer, plus moderate forum, minus chat

Roles are constants in one file with a table-driven test asserting each letter set. A role is never stored; it is recovered by matching the string.

Visibility — the two categories as presets

Visibility nobody anonymous Meaning
Public project gjorzh2 mnc3C fossil's stock defaults plus forum reading for visitors, forum posting and chat for anyone signed in
Members only — gjorzh2mnc3C visitors see nothing; a sign-in sees a public project
Private — — only people with explicit roles; RFC 0004's invisible-not-403 posture

Fossil's stock nobody is gjorz; anonymous adds hmnc. Public project differs by h for visitors (the hyperlink anti-bot letter is quarry's own posture), 2 for visitors, and 3C for the signed-in. Those additions are the whole reason forum and chat work for a new account without anyone typing a letter. A forge that wants fossil's exact defaults picks Custom.

Layer 2 — Abilities

A sentence, a letter set, a group. Toggling one on a person whose role lacks it makes the role Custom with the difference stated; toggling it back restores the role. The compiler owns implications — w ⇒ rcn, 5 ⇒ 4 ⇒ 3 ⇒ 2, k ⇒ jm, i ⇒ o — so the abilities layer cannot produce a string fossil reads differently from the way the page said it.

Group Ability Letters
Code Read the code, clone, download archives ogzh
Code Check in and push i (⇒ o)
Code See private branches · push unversioned files x · y (advanced)
Tickets Read tickets · file tickets · comment on tickets r · n · c
Tickets Edit tickets w (⇒ rcn)
Tickets Moderate ticket comments · create report formats q · t
Wiki Read the wiki and notes j
Wiki Edit wiki pages · create new pages k (⇒ jm) · f
Wiki Approve wiki appends l
Forum Read the forum · post (moderated) 2 · 3 (⇒ 2)
Forum Post without moderation · moderate · grant exemptions 4 (⇒ 3) · 5 (⇒ 4) · 6
Talk Chat · send announcements · receive email alerts C · A · 7
Files Attach to tickets and wiki · to forum posts b · B
People See people's email addresses e (advanced)
Repository Change this repository's settings a

Not abilities: d (legacy, unused), D (debug), s (site admin — an account flag), u and v (a role's business; the fossil trap where v does not imply u is what the compiler hides), L (virtual). All reachable in Layer 3.

Layer 3 — Letters, settings, configuration

Advanced (fossil): the raw string for every person and category with a decoder beneath each input; the fossil settings names quarry passes to the store (ignore-glob, binary-glob, crlf-glob, manifest, autosync, …) as name and value, versionable ones marked and tree-overridden ones flagged; export and import of fossil's configuration areas (user, skin, ticket, project, shun, …), the files fossil configuration pull/push move.

Information architecture

Settings, by the question asked rather than by implementation order:

Section Answers Holds
Who can see this is it public? the three visibility presets; Custom reveals the category strings decoded
People who can do what? one row per person: role chip, deviation chips, owns trunk chip, effective sentence; a row opens the editor
Branches which are guarded, by whom? RFC 0007's rules; owners link to their People rows
Repository what is it? description, homepage, topics, the clone line
Integrations what does it talk to? webhooks, mirror posture
Advanced (fossil) the letters and the knobs raw strings with decoders · fossil settings · config areas
Danger zone the irreversible remove, delete

Plus: a per-person page (/r/{repo}/people/{name}) that is the editor on its own URL — fossil's /setup_uedit, in sentences; the fleet account page with the same editor per repository; and a fleet default for what a new repository starts as.

Progressive enhancement

The baseline is forms: a radio for the role, checkboxes for abilities, a text input for the string, a reload on save showing the effective sentence computed server-side and a line saying what changed. Script adds the same things live: the sentence as you click, the string and the checkboxes as two views of one signal, the change line and the lockout refusal before Save. Script never makes a control possible.

Bulletproofing

One system

repo_role is retired. The verifier asks what the ops ask — does the effective set contain the letter this needs — with the three floors mapped once: signed-in ⇒ L, repository admin ⇒ a, site admin ⇒ s. account grant --role stays as a command and writes the role's string. The migration runs one last time and the table goes.

Build order

Rung Work
1 This RFC
2 The compiler: roles, abilities, implication closure, decode, infer; table-driven tests
3 One system: verifier on letters; grant writes strings; migration; repo_role gone
4 People section and the per-person editor as forms; account.explain; the lockout guard
5 Visibility presets, the take-private guard, the fleet default
6 Settings page reorganised; Advanced (fossil) with decoders
7 Datastar: live sentence, two-way string ⇄ checkboxes, pre-save preview
8 fossil settings editor; configuration-area import — the settings read/write and config import run against the served bare store through zit … -R, unblocked by zit#153; an older zit that predates it sees a note to upgrade

Rung 3 before 4, deliberately: sentences built on two verifiers are promises the other one can break.

Rung 8 reads and writes the served bare store's fossil settings and imports its configuration areas through zit settings -R and zit config -R. Those verbs used to refuse -R — the repository and global tiers need no checkout (fossil's own fossil settings -R reads them), so the refusal was too broad; that was filed as zit#153 and fixed there rather than worked around here (the store-interface rule, 0001 §2, puts the fix upstream, not in a per-repo throwaway checkout). A zit predating the fix falls back to a note to upgrade.

What this is not

Not a new capability letter (RFC 0004 §4 forbids inventing one). Not a change to what any letter means in fossil. Not portable: roles are how this forge writes strings; the strings are what travels.