Skip to main content

Module operator_poll

Module operator_poll 

Source
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

  1. Resolve the operator’s DID document through didbot_identity::document_url, which dispatches by method, to find its #atproto_pds service endpoint.
  2. com.atproto.repo.getRecord at that endpoint, for bot.did.operator at the record key didbot_pds::OperatorRelation::hostname — a single direct lookup, not a list-and-filter, because that is what keying the record by hostname buys.
  3. Hand the result to didbot_pds::OperatorRelation::poll, and act on the didbot_pds::OperatorTransition it reports: throw or release the self-triggered pause, and republish the server’s own registration record once claimed.
  4. 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§

GetRecordResponse 🔒
What com.atproto.repo.getRecord answers with on success.
OperatorPoll
A running operator poll, stopped by OperatorPoll::stop or by dropping it.
OperatorRecordLine
One bot.did.operator record in the operator’s repository, as the last listing read it.
OperatorRecords
Every bot.did.operator record the last successful listing of the operator’s repository read.
PollNudge
A request that this server look for its operator’s claim sooner than its timer would.
PollSchedule
When an OperatorPoll polls: its timer, and the floor a nudge cannot get under.
Prefetched 🔒
A ClaimSource holding a result already fetched, so didbot_pds::OperatorRelation::poll — synchronous by design — can be called with no I/O of its own. Not pub: the whole point is that nothing outside this module’s own poll tick constructs one.
RootStanding
What refresh_root_standing carries from one tick to the next.

Constants§

COLLECTION 🔒
The collection this poll reads. didbot-pds holds no schema for a collection defined by someone else’s repository convention here, so this is a plain string rather than a didbot_lexicon::nsid constant — the constant lives with the lexicon, in didbot_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 OperatorRelation a poll needs.
fetch_claim_from 🔒
Reads the bot.did.operator record at hostname from the operator’s repository at endpoint.
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, the PolicyPoll it 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.operator record in the operator’s repository and installs the creates allowances they carry; see didbot_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_pds endpoint 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_operator from document_url onward.
tick 🔒
poll_once with standing carried for refresh_root_standing, followed by policies’ 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.