Skip to content
quarry Artifact [ef2e5533d8]
Login

Artifact [ef2e5533d8]

Artifact ef2e5533d899a6ed0f68fe506252e999b55196e0342214234fac3bb5d583dbb1:


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.

RFC 0018 is that layer, and it qualifies the claim above in two places. A team also carries a membership floor — authority arriving from membership itself, with no grant behind it — which is a new source rather than a name expanding to a role. And one gate below does move: ticket.intake reads r instead of n (RFC 0017). The letters, presets, categories, expansion, guard and editor are unchanged, so the order still did 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 bcghjoprz2378BC read code, wiki, tickets and forum; post in the forum, comment on a ticket, chat, attachments, CI, email alerts, own password. Amended: see §"Reader stopped routing through the reader category"
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

Reader stopped routing through the reader category

The table above used to give Reader the string u3bBC7 and describe what it adds as "reader category (kptw: edit wiki, own password, ticket reports, edit tickets)". Both halves were true, and together they were the bug: the row said this role edits wikis and tickets, while the words the role is granted BY — "reads, asks, discusses" — said it reads. The RFC was honest and the role was misnamed.

u is fossil's reader category and effective() expands it, so on any repository that has not overridden its categories it resolved through fossil's shipped default kptw. Granting Reader granted wiki editing and ticket editing, and the person granting it could not see that happen.

Reader now spells its letters out: bcghjoprz2378BC. Three decisions inside that, recorded because each could look like an oversight:

What does not change: RFC 0004's model. u still means the reader category and still expands. A preset is quarry's own interface over the letters (this RFC's whole premise), so redefining one is not a reinterpretation of fossil — it is this layer doing its job.

The durable half of the fix is disclosure. A hand-written string holding u has the same surprise, and always will. The person editor now lists what an account holds that its string does not say, so whatever arrives from a category or a team is visible at the moment of granting rather than discovered later.

No migration. A stored string is not rewritten, so nobody loses access; existing u holders keep what they have and can now see it.

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.