Expand description
Drives didbot_pds::OperatorRelation’s poll against a real operator repository,
and reacts to what it finds. See plan/handshake.md.
didbot-pds holds no HTTP client and no async runtime, so
didbot_pds::ClaimSource is a plain synchronous trait; the fetch that
actually reaches the operator’s repository lives here instead, where both
are already dependencies. The bridge is Prefetched, a private
ClaimSource below: this module does the one HTTP round trip a poll needs
before calling
didbot_pds::OperatorRelation::poll, then hands the already-fetched result to
it through a didbot_pds::ClaimSource that does no I/O of its own. That
keeps the decision logic in didbot-pds, testable with no network at all,
while the network reaches this crate exactly once per tick.
§What each poll actually does
- Resolve the operator’s DID document through
didbot_identity::document_url, which dispatches by method, to find its#atproto_pdsservice endpoint. com.atproto.repo.getRecordat that endpoint, forbot.did.operatorat the record keydidbot_pds::OperatorRelation::hostname— a single direct lookup, not a list-and-filter, because that is what keying the record by hostname buys.- Hand the result to
didbot_pds::OperatorRelation::poll, and act on thedidbot_pds::OperatorTransitionit reports: throw or release the self-triggered pause, and republish the server’s own registration record once claimed. - Look for the same record once per root account — one the operator
created from their own repository — keyed by that hostname, and
report to the registry whether it still stands. A record missing for
longer than the grace window quarantines the account and everything
beneath it; see
refresh_root_standing.
§What is unverified
This has never been run against a real operator PDS — there is no such
server reachable from where this was built. The getRecord shape, the
DID resolution and the error handling for “no such record” versus “the
server is actually down” are implemented against atproto’s documented
XRPC conventions, not exercised against a live implementation. The
did:plc path reaches https://plc.directory in production and is
exercised here against a loopback stand-in for it.
Structs§
- GetRecord
Response 🔒 - What
com.atproto.repo.getRecordanswers with on success. - Operator
Poll - A running operator poll, stopped by
OperatorPoll::stopor by dropping it. - Operator
Record Line - One
bot.did.operatorrecord in the operator’s repository, as the last listing read it. - Operator
Records - Every
bot.did.operatorrecord the last successful listing of the operator’s repository read. - Poll
Nudge - A request that this server look for its operator’s claim sooner than its timer would.
- Poll
Schedule - When an
OperatorPollpolls: its timer, and the floor a nudge cannot get under. - Prefetched 🔒
- A
ClaimSourceholding a result already fetched, sodidbot_pds::OperatorRelation::poll— synchronous by design — can be called with no I/O of its own. Notpub: the whole point is that nothing outside this module’s own poll tick constructs one. - Root
Standing - What
refresh_root_standingcarries from one tick to the next.
Constants§
- COLLECTION 🔒
- The collection this poll reads.
didbot-pdsholds no schema for a collection defined by someone else’s repository convention here, so this is a plain string rather than adidbot_lexicon::nsidconstant — the constant lives with the lexicon, indidbot_lexicon::nsid::OPERATOR. - DEFAULT_
POLL_ INTERVAL - How often this deployment ships polling for an operator record, absent
configuration. Independent of
didbot_pds::DEFAULT_GRACE_WINDOW: the interval is how often the network is asked, the grace window is how long a missed answer is tolerated before it means something. Five minutes against a six-hour grace window is 72 chances to succeed before a transient outage could ever matter. - MIN_
POLL_ INTERVAL - The shortest interval between two polls, however many nudges arrive.
- POLL_
TICK_ 🔒TIMEOUT - The most one poll tick – the claim’s up to two sequential HTTP round trips, then the local decision – may take before the tick is abandoned.
- RECORD_
NOT_ 🔒FOUND - The atproto error name a repository uses for “there is no record at that key” – the one 400 this poll reads as absence rather than as a failure.
- VOUCH_
REREAD_ LIMIT - The most root accounts one tick re-reads the operator’s record for.
Functions§
- configure
- Builds the
OperatorRelationa poll needs. - fetch_
claim_ 🔒from - Reads the
bot.did.operatorrecord athostnamefrom the operator’s repository atendpoint. - poll_
once - One poll: fetch, decide, act. Split out from
OperatorPoll::spawn’s closure, and public, so a caller — an integration test walking the bootstrap, in particular — can drive exactly one tick against a real operator repository without spawning a task or waiting on a timer. The spawned poll runs this and, on the same resolved endpoint, thePolicyPollit was given. Every call here re-reads standing vouches from the first host; the spawned poll is what carries on between ticks. - refresh_
allowances - Lists every
bot.did.operatorrecord in the operator’s repository and installs thecreatesallowances they carry; seedidbot_pds::allowance. A listing that fails leaves the allowances last installed in force, the same way a failed policy listing leaves the last policy set serving: a blip at the operator’s PDS must not widen or narrow what hosted accounts may create. - refresh_
root_ standing - Re-reads the operator’s record for every root account and reports each one’s standing to the registry. Answers with the DIDs whose standing changed.
- resolve_
endpoint 🔒 - Resolves
operator_did’s document and reads its#atproto_pdsendpoint out of it, on its own so one tick resolves the operator once and reads every record it needs from the same endpoint. - resolve_
operator 🔒 resolve_endpoint, with the document it read the endpoint from.- resolve_
operator_ 🔒at resolve_operatorfromdocument_urlonward.- tick 🔒
poll_oncewithstandingcarried forrefresh_root_standing, followed bypolicies’ own tick against the endpoint the claim was read from. A tick that could not resolve the operator reads no policies either, and the gate keeps the set it had. Answers the transition, and whether the operator’s records were read and their allowances installed.