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
graph: a read schema for dolt.sr.ht at /query This service had no GraphQL at all, so nothing on the instance that already speaks SourceHut GraphQL could read it — not hut, not a script written against git.sr.ht's API, not api.sr.ht, which federates by fetching each service's /query. Everything else here has one; this is the missing surface rather than a new idea. The schema answers what a database IS: the listings under the store's own visibility rule, one database by owner and name, its branches, commit log and table list, and its ACL to the owner alone. Rows and diffs stay on /mcp, where a read that had to be clipped says so in its own answer — a shape this schema would have to reproduce field by field to stay honest. There are no mutations, for spec.sr.ht's reason: a federated type is a consumed contract, and creating, renaming and deleting a database is young code. Access is not spec.sr.ht's single-owner gate but this service's own matrix, applied per field: the bearer plane /mcp defines, then core.Allowed over the caller's grant. Anonymous is a normal caller — which is why the endpoint cannot be mounted the way core-go's WithSchema mounts one — and a database the caller may not see resolves to null rather than to an authorization error, so its existence cannot be read out of the shape of the refusal. model.Database is hand-written and branches/log/tables/acl are resolver fields on purpose: as generated struct fields they would open one bare store per row of every listing. A test asserts a metadata query opens none. Codegen is a module-independent `go run …@v0.17.94`, so the generator's dependency tree stays out of go.mod. v0.17.36 — the version spec.sr.ht pins — still cannot be run that way: its module pins an x/tools this toolchain refuses to compile, which generate.go records.