9.2 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
Related Links
- PR#42 — corrected the sizing numbers; this PR corrects how loudly they are said, and adds the order button this PR now gates.
The Problem
A full quota was as red as a full disk. The SSD group quota on the production host reached 92 %, so the advice chip turned red and the machine verdict read overloaded. Neither is true in the way the colour claims: a quota is raised by asking a service desk, nothing stops while it is being asked for, and the hardware behind it had well over a terabyte free. Red belongs to what cannot be asked for.
The build pages showed every repository's work to everyone. The pages are readable without any token — that is deliberate, a build page is meant to be linkable — and they name every branch, every commit, every artifact and every log line of every served repository. For a repository that is private on the forge, that is more than a passer-by should see, and the instance is on the internet.
The order button offered to spend money. PR#42 put a button on the System page that composes a hardware order; it appeared for anyone who opened the page, and it named the webspace and the machine while doing so.
Non-Goals
- No login, no sessions, no users. The control token is what Werkator has, and this PR only makes it readable earlier in the request.
- No configuration key for visibility. The forge knows, and a second place to declare it could only drift from the truth.
- Private repositories are not removed from the overview. That a repository exists, that it builds, and that a build failed stays visible; only what names the work goes.
- Nothing changes for a public repository, or for an instance with no forge configured.
The Scenarios
Feature: how loudly an advice is shown
Scenario#43.01: A quota to raise is yellow, a filling volume is red
So that a colour says how urgent a thing is, not merely what kind of thing it is.
- Given a disk whose peak is 92 % of a group quota
- and whose physical volume has room to spare
- Then the verdict stays
upgradeand the chip is drawn in the watch colour- and the machine verdict counts it as pressure, not as overload
- Given the same fill against the volume itself, or against a quota whose volume is the tighter budget
- Then the chip is drawn in the critical colour and the machine verdict counts it as overload
- because raising a quota whose backing is what runs out buys nothing
Verified by
Feature: private repositories
Background
- The pages and the JSON API are readable without a token; only mutating endpoints have ever required one.
- A repository's visibility is the forge's own fact, reachable through the Gitea repository API.
Scenario#43.02: What a visitor sees of a private repository
So that a build page can stay public without publishing the work behind it.
- Given a repository the forge calls private
- and a browser without the control token
- When any page or API of that repository is requested
- Then its name reads
hidden private repo, greyed out, and carries no link- and branch, build name and commit read
n/a - and the artifact key is empty, so no artifact, no log and no permanent branch link is offered
- and no link into the forge is rendered
- and the redaction happened on the server: the JSON the browser received never held the values
- and branch, build name and commit read
- And then the counts, the statuses and the timestamps are untouched, and the shapes are unchanged, so the page renders the repository like any other
- Given a public repository
- Then nothing changes at all
Verified by
Scenario#43.03: The overview keeps the shape, loses the identity
- Given a private repository among the served ones
- Then its entry shows
hidden private repoand is not clickable- and its failed builds are listed as
n/a, with their timestamps - and its branch count, running and pending counts and watcher problem stay
- and its failed builds are listed as
Verified by
Scenario#43.04: The forge is asked, and the fallbacks are asymmetric
So that nothing has to be configured, and a failure does not publish a repository.
- Given a repository whose forge answers
private - Then it is hidden; answered
public, it is shown - Given a repository with no forge configured
- Then it is public, exactly as before this change — there is nothing to ask and nothing to hide behind
- Given a repository whose forge is configured but does not answer
- Then the last answer stands, and without one the repository is hidden
- And the answer is cached, so the pages' polling never reaches the forge
Verified by
Scenario#43.05: The token in a cookie reveals, the token in a header acts
So that a server-rendered page can decide what to show, without weakening what protects the actions.
- Given a browser that has been given the control token
- Then it is kept in local storage as before and in a cookie
- and the cookie makes every page and every API answer unredacted
- and restarting or deleting a build still requires the token in the request header
- Given a foreign page that makes the browser send the cookie
- Then it gains nothing: it can neither read the response across origins nor set the header the actions require
Verified by
Feature: the order button
Scenario#43.06: Only the operator may order
- Given a browser without the control token
- Then
GET /api/systemreports no hosting, so neither the order button nor the webspace and machine names reach it - Given the control token
- Then the button appears as in PR#42
Verified by
Scenario#43.07: An action nobody may take is shown, but inert
So that the page says what exists without inviting a click that can only fail.
- Given a browser without a valid control token
- Then the restart, delete and cancel buttons are rendered greyed out and disabled
- and their tooltip says why:
Needs the control token - and this holds for the server-rendered rows and for the rows the script re-renders alike
- and their tooltip says why:
- And the header carries a key button that asks for the token and reloads
- because with every action disabled there would otherwise be nothing left to click that asks for it
- Given a valid token
- Then the buttons behave as before, and the key button offers to forget it again
Verified by
The Solution
ResourceAdvice gains an askable flag and, from it, a severity in the page's own ok/warn/crit vocabulary.
The chips take their colour from the severity rather than from the verdict, so the text keeps saying upgrade while the colour says how loudly; SystemHealth applies the same rule to the machine verdict.
A disk is askable when its budget is a quota and the volume behind it is not the tighter budget.
GiteaClient.readVisibility asks the forge, and RepositoryPrivacy caches the answer per repository with the fallbacks of Scenario#43.04.
Redaction is one object holding what a redacted build, branch and overview look like, applied in the controllers before anything is serialized — never in the page.
That is what keeps the frontend unaware: same fields, same types, one row per build, and a first paint that already agrees with the first poll.
The same cookie tells a page whether it may act: canAct reaches the templates and, as a data- attribute, the script, so the disabled state is decided in one place and by the only party that can tell a valid token from a stale one.
ViewerIdentity reads the token from the cookie, from the current request where a controller has many handlers.
The cookie is written by werkator.js next to the local-storage copy it has always kept, including once on load for a browser that was given the token before this change.
Open Questions
- The visibility cache has no invalidation other than its fifteen minutes; making a repository public shows up within that window, not immediately.
- A repository is hidden as a whole. A forge that distinguishes internal from private is treated as private.
Additional Changes
- Version 1.5.0 and its release-notes entry, since this is what gets deployed.