rename the service to diff.sr.ht
The instance-facing name changes and nothing else does: the config.ini section,
the origin, the apk package, the nginx block and the systemd unit are all
diff.sr.ht now, and the brand's red service label reads "diff" because chrome
derives it from the section name.
What deliberately stays is everything whose rename would cost more than it is
worth: the Go module path, the repository name, the comparesrht binary, and the
/~owner/repo/compare/base...head routes people have in their history. README
says so at the top so the split is not read as an oversight.
login: ecore's cookie decoder, which validates the name ours did not
authz/identity.go was the fifth copy of a decode the instance has one of,
and it was one of the two that validated nothing: whatever name a sealed
payload carried went on to the GraphQL viewer field, the chrome's nav and
every log line the request produced. login.ValidName refuses it, and
there is no spelling of the API that turns the check off.
Gone with it: CookieName, UsernameFromRequest, Middleware, ForContext and
the ctxKey, plus identity_test.go, whose round-trip, tilde-stripping and
garbage-cookie cases are ecore's tests now. The Middleware becomes
login.Optional() — this service refuses nobody, git.sr.ht decides what an
anonymous viewer may see — and the default validator is kept rather than
core.ValidOwner, which is lowercase-only and would log a real account out
of compare alone.
The one behaviour change a viewer could notice: a cookie whose name
carries a '/', a control byte or a non-ASCII letter now reads as
anonymous instead of as that name.
authz: unified-login identity and git.sr.ht GraphQL authorization
Add the authz package: cookie-derived identity and per-request repository
authorization delegated to git.sr.ht's internal GraphQL API, with no local
database.
- identity.go: UsernameFromRequest decrypts the sr.ht.unified-login.v1 Fernet
cookie to a bare username ("" for anonymous, never rejects); Middleware/
ForContext carry it in the request context.
- authz.go: Authorizer interface + GQLAuthorizer over core-go client.Do. Repo
strips a leading ~ from the owner, maps null user/repository to
core.ErrNotFound (never leaking existence) and transport/GraphQL failures to
a wrapped non-NotFound error. MyRepos paginates me{repositories} to a 500-repo
cap. A mutex-guarded TTL cache memoizes positive and not-found Repo results
(keyed viewer\0owner\0name) but never transport errors, with lazy expiry and
opportunistic sweeps.
- Tests: in-memory config (generated Fernet + ed25519 keys) + httptest server
asserting the Internal auth header decrypts to the expected viewer; covers
cookie round-trip, found/null/500 repo cases, per-viewer cache keying, TTL
expiry, transport-error non-caching, and MyRepos pagination.