Summary
Cirrus's firehose #commit events (com.atproto.sync.subscribeRepos#commit) omit the prevData field. Per the lexicon, prevData is the MST root CID of the previous commit (corresponding to the since rev) and is "effectively required for the 'inductive' version of firehose." Without it, relays running strict commit validation cannot verify cirrus commits and reject every commit after the first, freezing the repo.
Evidence
A cirrus #commit frame currently contains only:
[blobs, blocks, commit, ops, rebase, repo, rev, seq, since, time, tooBig] — no prevData.
The reference relay (indigo) cmd/relay/relay/verify.go:
// first commit from an unknown account (prevData nil) is accepted unverified
if prevRepo == nil && evt.PrevData == nil { return nil }
if evt.PrevData == nil { return fmt.Errorf("missing prevData field") }
Behavior is gated by Config.LenientSyncValidation: strict relays drop the event (repo freezes); lenient relays index it unverified (warning only).
Observed on two independent cirrus repos — did:web:simnaut.io and did:plc:uwbl4k3tza7eyjv3morkrld2. On the latter, two relays read the firehose to the same cursor (seq 6205), yet a strict relay (relay.feeds.blue) is frozen 106 days at 3me7toj67qs2c while a lenient relay (bsky.network) is at head 3mmk5tdw2mc27. Same input, divergent state — purely strict-vs-lenient. The relay.feeds.blue operator confirmed they run standard indigo with LenientSyncValidation off (deliberate strict mode, not an outdated build), and their logs show "commit message failed verification ... err: missing prevData field". As more relays tighten validation, this breaks cirrus federation more widely.
Fix
The previous commit's MST root is already in scope at every write call site in account-do.ts (the repo object captured before applyWrites, alongside the existing prevRev). Populate prevData = repo.commit.data:
packages/pds/src/sequencer.ts: add prevData: CID to CommitData and CommitEvent; set prevData: data.prevData in sequenceCommit's eventPayload (it then flows through the existing CBOR encoder automatically).
packages/pds/src/account-do.ts: in rpcCreateRecord, rpcDeleteRecord, rpcPutRecord, rpcApplyWrites, add prevData: repo.commit.data to the CommitData (next to since: prevRev).
- Update
test/firehose.test.ts to assert prevData equals the prior repo.commit.data, and scripts/verify-firehose.ts's CommitEvent interface.
Related: #167 (getLatestCommit) is a separate conformance gap, not the indexing blocker. Fixed by #170.
Summary
Cirrus's firehose
#commitevents (com.atproto.sync.subscribeRepos#commit) omit theprevDatafield. Per the lexicon,prevDatais the MST root CID of the previous commit (corresponding to thesincerev) and is "effectively required for the 'inductive' version of firehose." Without it, relays running strict commit validation cannot verify cirrus commits and reject every commit after the first, freezing the repo.Evidence
A cirrus
#commitframe currently contains only:[blobs, blocks, commit, ops, rebase, repo, rev, seq, since, time, tooBig]— noprevData.The reference relay (indigo)
cmd/relay/relay/verify.go:Behavior is gated by
Config.LenientSyncValidation: strict relays drop the event (repo freezes); lenient relays index it unverified (warning only).Observed on two independent cirrus repos —
did:web:simnaut.ioanddid:plc:uwbl4k3tza7eyjv3morkrld2. On the latter, two relays read the firehose to the same cursor (seq 6205), yet a strict relay (relay.feeds.blue) is frozen 106 days at3me7toj67qs2cwhile a lenient relay (bsky.network) is at head3mmk5tdw2mc27. Same input, divergent state — purely strict-vs-lenient. The relay.feeds.blue operator confirmed they run standard indigo withLenientSyncValidationoff (deliberate strict mode, not an outdated build), and their logs show"commit message failed verification ... err: missing prevData field". As more relays tighten validation, this breaks cirrus federation more widely.Fix
The previous commit's MST root is already in scope at every write call site in
account-do.ts(therepoobject captured beforeapplyWrites, alongside the existingprevRev). PopulateprevData = repo.commit.data:packages/pds/src/sequencer.ts: addprevData: CIDtoCommitDataandCommitEvent; setprevData: data.prevDatainsequenceCommit'seventPayload(it then flows through the existing CBOR encoder automatically).packages/pds/src/account-do.ts: inrpcCreateRecord,rpcDeleteRecord,rpcPutRecord,rpcApplyWrites, addprevData: repo.commit.datato theCommitData(next tosince: prevRev).test/firehose.test.tsto assertprevDataequals the priorrepo.commit.data, andscripts/verify-firehose.ts'sCommitEventinterface.Related: #167 (
getLatestCommit) is a separate conformance gap, not the indexing blocker. Fixed by #170.