Hosts identity data names are asked only on https, at public addresses
MergedThis pull moves backfill's fetch policy into grain::guard and applies it to every fetch that identity data drives. Those are did:web documents (including the one auth::verify reads), handle /.well-known lookups, the PDS a DID document names, and indexRecords' read-back. It follows the code I read: Ctx::may_ask is called before each guarded request, guarded has redirects off and the public-only resolver, and GRAIN_ALLOW_PRIVATE lifts both consistently. I found no other identity-driven fetch still on ctx.http. The remaining ctx.http uses are settings or the command line's own account, which the body already lists as shortcomings.
The map from Refused to 400 InvalidRequest in indexRecords is correct, and tests cover it. Backfill's old tests moved with the code, and the GRAIN_BACKFILL_PRIVATE references are gone. The check workflow passed. rust and harness were still running when I looked, and test was queued. Merge only once they pass, since nothing was run locally.
Nothing blocks the merge.
- Hosts identity data names are asked only on https, at public addresses
- main
What changes
One policy, in
grain::guard. The rule backfill had for itself now lives in grain's core. A host that identity data names is asked only over https and only at a public address. Its hostname has to resolve to public addresses (PublicOnly, a reqwest DNS resolver), and no redirect is followed.public,may_ask,PublicOnlyandclientkeep their per-range unit tests.Ctx carries a guarded client.
Ctxnow holdsguardedbesidehttp, plusCtx::may_ask. Identity data drives these fetches, and all of them go through the guard:auth::verify, which reads one before it checks any signature, so any token namingdid:web:<anything>used to make grain-serve fetch that host./.well-known/atproto-didhandle lookups.identity::pds, called fromresolve_pds, refuses one that is not askable.indexRecords' read-back (sync::read_back). It checks the PDS it is given and asks it through the guarded client. When the caller's DID document names a PDS that is not askable,indexRecordsanswers 400InvalidRequest, not 502.Backfill uses the shared policy and drops its own copy of it, along with
Config::private.GRAIN_ALLOW_PRIVATE=1(Env::allow_private) replacesGRAIN_BACKFILL_PRIVATEand lifts the policy for tests and dev. The harness contexts incrates/grain/tests/commonset it, because the reference PDS runs over http on localhost.The PLC directory, the configured handle resolvers (
GRAIN_HANDLE_RESOLVER's XRPC services),GRAIN_SERVICE, the Jetstream and backfill's directories and mirror are settings, so they are not checked. They still go throughCtx::http, or backfill's own client as before.Why
Since
social.grain.sync.indexRecordslanded, anyone with a service token can make grain-serve fetch a URL of their choosing: a DID document can name any PDS, and a did:web can name any host. Putting the policy in one place means every fetch driven by identity data gets the same rule.knotless has no equivalent: its identity resolution uses one plain client. So this follows backfill's version of the policy, not knotless's.
How it was checked
All of it is left to CI; nothing was built or run locally. New tests:
guardunit tests per address range. Another test shows a guarded client refusinglocalhost.identityrefuses a did:web on loopback (both http and https), a handle over http, and http or private-address PDS endpoints.indexRecordsrefuses a caller whose DID document names the reference PDS (loopback, http) when the override is off.read_backrefuseshttps://127.0.0.1by itself, andhttps://localhostat DNS.GRAIN_ALLOW_PRIVATE=0.Shortcomings
/.well-known/atproto-didredirects no longer resolves over HTTPS. It can still resolve through the XRPC resolver.Ctx::http. Their PDS URL has passedmay_ask, but their hostname is not DNS-checked.🤖 Generated with Claude Code