···453453 // TakenDown is set to true if the user in question has been taken down.
454454 // A user in this state will have all future events related to it dropped
455455 // and no data about this user will be served.
456456- TakenDown bool
456456+ TakenDown bool
457457+ Tombstoned bool
457458}
458459459460type addTargetBody struct {
···776777 return fmt.Errorf("rebase was true in event seq:%d,host:%s", evt.Seq, host.Host)
777778 }
778779780780+ if host.ID != u.PDS {
781781+ log.Infow("received event for repo from different pds than expected", "repo", evt.Repo, "expPds", u.PDS, "gotPds", host.Host)
782782+ subj, err := bgs.createExternalUser(ctx, evt.Repo)
783783+ if err != nil {
784784+ return err
785785+ }
786786+787787+ if subj.PDS != host.ID {
788788+ return fmt.Errorf("event from non-authoritative pds")
789789+ }
790790+ }
791791+792792+ if u.Tombstoned {
793793+ // we've checked the authority of the users PDS, so reinstate the account
794794+ if err := bgs.db.Model(&User{}).Where("id = ?", u.ID).UpdateColumn("tombstoned", false).Error; err != nil {
795795+ return fmt.Errorf("failed to un-tombstone a user: %w", err)
796796+ }
797797+798798+ ai, err := bgs.Index.LookupUser(ctx, u.ID)
799799+ if err != nil {
800800+ return fmt.Errorf("failed to look up user (tombstone recover): %w", err)
801801+ }
802802+803803+ // Now a simple re-crawl should suffice to bring the user back online
804804+ return bgs.Index.Crawler.AddToCatchupQueue(ctx, host, ai, evt)
805805+ }
806806+779807 // skip the fast path for rebases or if the user is already in the slow path
780808 if bgs.Index.Crawler.RepoInSlowPath(ctx, host, u.ID) {
781809 rebasesCounter.WithLabelValues(host.Host).Add(1)
···861889 }
862890863891 return nil
892892+ case env.RepoTombstone != nil:
893893+ if err := bgs.handleRepoTombstone(ctx, host, env.RepoTombstone); err != nil {
894894+ return err
895895+ }
896896+897897+ return nil
864898 default:
865899 return fmt.Errorf("invalid fed event")
866900 }
867901}
868902903903+func (bgs *BGS) handleRepoTombstone(ctx context.Context, pds *models.PDS, evt *atproto.SyncSubscribeRepos_Tombstone) error {
904904+ u, err := bgs.lookupUserByDid(ctx, evt.Did)
905905+ if err != nil {
906906+ return err
907907+ }
908908+909909+ if u.PDS != pds.ID {
910910+ return fmt.Errorf("unauthoritative tombstone event from %s for %s", pds.Host, evt.Did)
911911+ }
912912+913913+ if err := bgs.db.Model(&User{}).Where("id = ?", u.ID).UpdateColumns(map[string]any{
914914+ "tombstoned": true,
915915+ "handle": nil,
916916+ }).Error; err != nil {
917917+ return err
918918+ }
919919+920920+ if err := bgs.db.Model(&models.ActorInfo{}).Where("uid = ?", u.ID).UpdateColumns(map[string]any{
921921+ "handle": nil,
922922+ }).Error; err != nil {
923923+ return err
924924+ }
925925+926926+ // delete data from carstore
927927+ if err := bgs.repoman.TakeDownRepo(ctx, u.ID); err != nil {
928928+ // don't let a failure here prevent us from propagating this event
929929+ log.Errorf("failed to delete user data from carstore: %s", err)
930930+ }
931931+932932+ return bgs.events.AddEvent(ctx, &events.XRPCStreamEvent{
933933+ RepoTombstone: evt,
934934+ })
935935+}
936936+869937func (s *BGS) syncUserBlobs(ctx context.Context, pds *models.PDS, user models.Uid, blobs []string) error {
870938 if s.blobs == nil {
871939 log.Debugf("blob syncing disabled")
···10091077 return nil, fmt.Errorf("failed to update users pds: %w", err)
10101078 }
1011107910801080+ exu.PDS = peering.ID
10121081 }
1013108210141083 if exu.Handle.String != handle {
+33-8
bgs/handlers.go
···2121)
22222323func (s *BGS) handleComAtprotoSyncGetRecord(ctx context.Context, collection string, commit string, did string, rkey string) (io.Reader, error) {
2424- u, err := s.Index.LookupUserByDid(ctx, did)
2424+ u, err := s.lookupUserByDid(ctx, did)
2525 if err != nil {
2626 if errors.Is(err, gorm.ErrRecordNotFound) {
2727 return nil, echo.NewHTTPError(http.StatusNotFound, "user not found")
···2929 return nil, echo.NewHTTPError(http.StatusInternalServerError, "failed to lookup user")
3030 }
31313232+ if u.Tombstoned {
3333+ return nil, fmt.Errorf("account was deleted")
3434+ }
3535+3636+ if u.TakenDown {
3737+ return nil, fmt.Errorf("account was taken down")
3838+ }
3939+3240 reqCid := cid.Undef
3341 if commit != "" {
3442 reqCid, err = cid.Decode(commit)
···3745 }
3846 }
39474040- _, record, err := s.repoman.GetRecord(ctx, u.Uid, collection, rkey, reqCid)
4848+ _, record, err := s.repoman.GetRecord(ctx, u.ID, collection, rkey, reqCid)
4149 if err != nil {
4250 return nil, fmt.Errorf("failed to get record: %w", err)
4351 }
···5260}
53615462func (s *BGS) handleComAtprotoSyncGetRepo(ctx context.Context, did string, since string) (io.Reader, error) {
5555- u, err := s.Index.LookupUserByDid(ctx, did)
6363+ u, err := s.lookupUserByDid(ctx, did)
5664 if err != nil {
5765 if errors.Is(err, gorm.ErrRecordNotFound) {
5866 return nil, echo.NewHTTPError(http.StatusNotFound, "user not found")
···6068 return nil, echo.NewHTTPError(http.StatusInternalServerError, "failed to lookup user")
6169 }
62707171+ if u.Tombstoned {
7272+ return nil, fmt.Errorf("account was deleted")
7373+ }
7474+7575+ if u.TakenDown {
7676+ return nil, fmt.Errorf("account was taken down")
7777+ }
7878+6379 // TODO: stream the response
6480 buf := new(bytes.Buffer)
6565- if err := s.repoman.ReadRepo(ctx, u.Uid, since, buf); err != nil {
8181+ if err := s.repoman.ReadRepo(ctx, u.ID, since, buf); err != nil {
6682 return nil, fmt.Errorf("failed to read repo: %w", err)
6783 }
6884···158174 }
159175160176 users := []User{}
161161- if err := s.db.Model(&User{}).Where("id > ?", c).Order("id").Limit(limit).Find(&users).Error; err != nil {
177177+ if err := s.db.Model(&User{}).Where("id > ? AND NOT tombstoned AND NOT taken_down", c).Order("id").Limit(limit).Find(&users).Error; err != nil {
162178 if err == gorm.ErrRecordNotFound {
163179 return &comatprototypes.SyncListRepos_Output{}, nil
164180 }
···175191176192 for i := range users {
177193 user := users[i]
194194+178195 root, err := s.repoman.GetRepoRoot(ctx, user.ID)
179196 if err != nil {
180197 return nil, fmt.Errorf("failed to get repo root for (%s): %w", user.Did, err)
···194211}
195212196213func (s *BGS) handleComAtprotoSyncGetLatestCommit(ctx context.Context, did string) (*comatprototypes.SyncGetLatestCommit_Output, error) {
197197- u, err := s.Index.LookupUserByDid(ctx, did)
214214+ u, err := s.lookupUserByDid(ctx, did)
198215 if err != nil {
199216 if errors.Is(err, gorm.ErrRecordNotFound) {
200217 return nil, echo.NewHTTPError(http.StatusNotFound, "user not found")
···202219 return nil, echo.NewHTTPError(http.StatusInternalServerError, "failed to lookup user")
203220 }
204221205205- root, err := s.repoman.GetRepoRoot(ctx, u.Uid)
222222+ if u.Tombstoned {
223223+ return nil, fmt.Errorf("account was deleted")
224224+ }
225225+226226+ if u.TakenDown {
227227+ return nil, fmt.Errorf("account was taken down")
228228+ }
229229+230230+ root, err := s.repoman.GetRepoRoot(ctx, u.ID)
206231 if err != nil {
207232 return nil, fmt.Errorf("failed to get repo root: %w", err)
208233 }
209234210210- rev, err := s.repoman.GetRepoRev(ctx, u.Uid)
235235+ rev, err := s.repoman.GetRepoRev(ctx, u.ID)
211236 if err != nil {
212237 return nil, fmt.Errorf("failed to get repo rev: %w", err)
213238 }