metapat: the meta.sr.ht PAT plane a federated endpoint needs api.sr.ht forwards ONE client Authorization header to every service a federated query touches — AuthMiddleware copies it verbatim, and the Internal credential it can mint is used only to fetch schemas at startup. So a GraphQL endpoint that refuses meta.sr.ht personal access tokens cannot be federated: the first authenticated query answers 401. Four services on this instance refuse them today and publish an empty scope list besides, which is the stronger half of the same problem — meta cannot mint a token for a service whose api-meta.json names no scope. This is the half of accepting one that is the same everywhere: routing a credential to its plane, the decode/lookup/revocation path, and the OAuth scope gate. dolt is where it is written today; putting a fourth and fifth copy of a credential path in four services is how the copies start to disagree. PlaneOf routes without a validator, which matters more than it looks: an instance with no [tokens.sr.ht] section holds no working-token validator to call, and its PAT plane must keep working anyway. Only successes are cached, so an outage cannot be pinned into the cache and outlive itself, and the cache is bounded — dolt's grows without one. Options.Service is required because auth.DecodeGrants reads the calling service's name off the context and config.ServiceName PANICS for the absence of it rather than answering "". In production config.Middleware puts it there; anywhere else — a job, a CLI, a test — a validator that leaned on the ambient context would take the process down. So the validator carries its own. bearer's step-2 prose said bench and cover had no reason to accept a meta PAT. That was true of a service reached only by its own clients and is not true of one behind the gateway; it now says which surface accepts which credential, and points at PlaneOf.
bearer: mint the internal authorization through internalauth This package assembled the header by hand — json.Marshal of an auth.InternalAuth plus "Internal " and a fernet seal — two files away from the package whose whole purpose is to hold both ends of that handshake. It is the same drift internalauth was hoisted to end, sitting inside the library that hoisted it: a change to the payload shape here would have gone unnoticed by every Guard on the instance until a revocation check started failing.
bearer: split the grant check out of validation A sourcehut service resolves identity once per request in middleware, upstream of the router — that is where the cookie plane and the bearer plane meet and where a principal is put on the context — and at that point nothing knows which route will run, so nothing knows the action. The action is known one layer down, in the handler. Validate insisted on both at once, which left every adopting service two bad options: invent an action before it has one, or lift its bearer plane out of the middleware every other plane goes through. The second is how a surface ends up with two different ideas of who is calling. So Inspect answers steps 1, 2 and 4 — who, what may they do, is it still live — and Token.Authorize is step 3, asked where the action finally exists. Validate is unchanged for callers that know both at one point. It keeps its own ordering rather than becoming Inspect+Authorize: step 3 before step 4 means a token that does not carry the grant is refused without a round trip to the daemon, and a test pins that. Inspect cannot keep that ordering, having no action to refuse on, and that is the one cost of the split — written down at both call sites.
bearer: the shared working-token validator of SPEC ch. 6