authn: name the grant scope dolt.sr.ht/DATABASES
api-meta.json published the scope as lowercase "repos" while every other
service on the instance publishes it upper case — git.sr.ht/REPOSITORIES,
todo.sr.ht/TRACKERS, paste.sr.ht/PASTES, builds.sr.ht/JOBS. Upstream derives
those from a GraphQL enum; this service has no @access directive to derive
from, so the spelling was free and drifted.
It matters because meta.sr.ht compares the string verbatim. Its oauth2
blueprint fetches every service's api-meta.json once at import time and
validates a requested grant with `scope in service_scopes[svc]` — no case
folding, no aliasing. So a grant typed by hand as dolt.sr.ht/REPOS:RO was
refused, by analogy with every neighbouring service.
DATABASES rather than REPOS: that is what the surface calls the object
everywhere a user meets it — the GraphQL databases connection, the web pages,
the docs. The storage layer underneath still says "repo"; renaming that is a
deeper change and is not what a token grant names.
No backward compatibility. auth.Grants.Has is a map lookup, so a PAT minted
against the old spelling is refused rather than quietly honoured, and two table
cases assert that instead of leaving it implied. Deploying this is two-sided:
meta.sr.ht has to be restarted before anyone can mint a token carrying the new
name.
The federation gateway is unaffected — it forwards the client's Authorization
header to each service and never reads api-meta.json.
sr-ht-dolt-xic
authn: cookie, PAT, and dolt-JWT caller resolution
Add the authn package resolving the SourceHut caller across dolt.sr.ht's
three auth flows, producing core-go *auth.AuthContext values mapped onto the
pure core.Caller domain type:
- ctx.go: WithCaller/CallerFromContext (nil for anonymous, never panics) and
AsCoreCaller (maps UserType, derives Suspended).
- cookie.go: OptionalCookieMiddleware, the never-rejecting unified-login
cookie reader (fernet decrypt -> {name} -> LookupUser); any failure degrades
to anonymous so public browsing keeps working.
- token.go: ResolveBasic, the meta personal-access-token trio (offline
DecodeBearerToken -> username match -> LookupUser + revocation) with a 60s
positive cache keyed by sha512(password); TokenGrantsAllow gates
dolt.sr.ht/repos RO/RW grants (empty grants pass).
- jwt.go: ResolveDoltJWT, EdDSA JWS verification for dolt keypair auth (kid ->
KeyStore pubkey, alg=EdDSA, aud/exp/sub checked, iss ignored), reusing
dolt's creds.PubKeyToKIDStr for the kid<->pubkey integrity check.
- grpc.go: ResolveGRPCAuth dispatching Basic/Bearer/anonymous.
Meta lookup + revocation sit behind the MetaBackend interface and JWT keys
behind the KeyStore interface (implemented later by db/), so tests forge
cookies (fernet), PATs (BearerToken.Encode) and real Ed25519 JWTs against
in-memory stubs with no network or Postgres.