@atproto/pds 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/account-manager/account-manager.d.ts.map +1 -1
  3. package/dist/account-manager/account-manager.js +16 -19
  4. package/dist/account-manager/account-manager.js.map +1 -1
  5. package/dist/account-manager/oauth-store.d.ts.map +1 -1
  6. package/dist/account-manager/oauth-store.js +46 -26
  7. package/dist/account-manager/oauth-store.js.map +1 -1
  8. package/dist/api/com/atproto/admin/deleteAccount.d.ts.map +1 -1
  9. package/dist/api/com/atproto/admin/deleteAccount.js +9 -4
  10. package/dist/api/com/atproto/admin/deleteAccount.js.map +1 -1
  11. package/dist/api/com/atproto/admin/updateSubjectStatus.js +1 -1
  12. package/dist/api/com/atproto/admin/updateSubjectStatus.js.map +1 -1
  13. package/dist/api/com/atproto/identity/submitPlcOperation.js +1 -1
  14. package/dist/api/com/atproto/identity/submitPlcOperation.js.map +1 -1
  15. package/dist/api/com/atproto/server/activateAccount.js +1 -3
  16. package/dist/api/com/atproto/server/activateAccount.js.map +1 -1
  17. package/dist/api/com/atproto/server/createAccount.d.ts.map +1 -1
  18. package/dist/api/com/atproto/server/createAccount.js +61 -45
  19. package/dist/api/com/atproto/server/createAccount.js.map +1 -1
  20. package/dist/api/com/atproto/server/deactivateAccount.js +1 -1
  21. package/dist/api/com/atproto/server/deactivateAccount.js.map +1 -1
  22. package/dist/api/com/atproto/server/deleteAccount.d.ts.map +1 -1
  23. package/dist/api/com/atproto/server/deleteAccount.js +9 -4
  24. package/dist/api/com/atproto/server/deleteAccount.js.map +1 -1
  25. package/dist/api/com/atproto/sync/getRepo.d.ts.map +1 -1
  26. package/dist/api/com/atproto/sync/getRepo.js +5 -1
  27. package/dist/api/com/atproto/sync/getRepo.js.map +1 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +4 -39
  30. package/dist/index.js.map +1 -1
  31. package/dist/rate-limits.d.ts +7 -0
  32. package/dist/rate-limits.d.ts.map +1 -0
  33. package/dist/rate-limits.js +50 -0
  34. package/dist/rate-limits.js.map +1 -0
  35. package/dist/scripts/publish-identity.js +1 -1
  36. package/dist/scripts/publish-identity.js.map +1 -1
  37. package/dist/scripts/rebuild-repo.js +1 -1
  38. package/dist/scripts/rebuild-repo.js.map +1 -1
  39. package/dist/scripts/rotate-keys.js +2 -2
  40. package/dist/scripts/rotate-keys.js.map +1 -1
  41. package/dist/scripts/sequencer-recovery/recoverer.js +7 -5
  42. package/dist/scripts/sequencer-recovery/recoverer.js.map +1 -1
  43. package/dist/sequencer/sequencer.d.ts +8 -6
  44. package/dist/sequencer/sequencer.d.ts.map +1 -1
  45. package/dist/sequencer/sequencer.js +40 -21
  46. package/dist/sequencer/sequencer.js.map +1 -1
  47. package/package.json +7 -7
  48. package/src/account-manager/account-manager.ts +26 -23
  49. package/src/account-manager/oauth-store.ts +55 -36
  50. package/src/api/com/atproto/admin/deleteAccount.ts +9 -7
  51. package/src/api/com/atproto/admin/updateSubjectStatus.ts +1 -1
  52. package/src/api/com/atproto/identity/submitPlcOperation.ts +1 -1
  53. package/src/api/com/atproto/server/activateAccount.ts +3 -3
  54. package/src/api/com/atproto/server/createAccount.ts +72 -63
  55. package/src/api/com/atproto/server/deactivateAccount.ts +1 -1
  56. package/src/api/com/atproto/server/deleteAccount.ts +9 -7
  57. package/src/api/com/atproto/sync/getRepo.ts +9 -1
  58. package/src/index.ts +3 -42
  59. package/src/rate-limits.ts +59 -0
  60. package/src/scripts/publish-identity.ts +1 -1
  61. package/src/scripts/rebuild-repo.ts +1 -1
  62. package/src/scripts/rotate-keys.ts +2 -2
  63. package/src/scripts/sequencer-recovery/recoverer.ts +9 -5
  64. package/src/sequencer/sequencer.ts +52 -23
  65. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"recoverer.js","sourceRoot":"","sources":["../../../src/scripts/sequencer-recovery/recoverer.ts"],"names":[],"mappings":"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","sourcesContent":["import { rmIfExists } from '@atproto/common'\nimport { Secp256k1Keypair } from '@atproto/crypto'\nimport { DidString, isNsidString, isRecordKeyString } from '@atproto/lex'\nimport {\n BlockMap,\n CidSet,\n CommitData,\n WriteOpAction,\n cborToLexRecord,\n parseDataKey,\n readCar,\n} from '@atproto/repo'\nimport {\n AccountManager,\n AccountStatus,\n} from '../../account-manager/account-manager.js'\nimport { ActorStoreTransactor } from '../../actor-store/actor-store-transactor.js'\nimport { ActorStore } from '../../actor-store/actor-store.js'\nimport { countAll } from '../../db/index.js'\nimport {\n PreparedWrite,\n prepareCreate,\n prepareDelete,\n prepareUpdate,\n} from '../../repo/index.js'\nimport {\n AccountEvt,\n CommitEvt,\n SeqEvt,\n Sequencer,\n} from '../../sequencer/index.js'\nimport { RecoveryDb } from './recovery-db.js'\nimport { UserQueues } from './user-queues.js'\n\nexport type RecovererContextNoDb = {\n sequencer: Sequencer\n accountManager: AccountManager\n actorStore: ActorStore\n}\n\nexport type RecovererContext = RecovererContextNoDb & {\n recoveryDb: RecoveryDb\n}\n\nconst PAGE_SIZE = 5000\n\nexport class Recoverer {\n queues: UserQueues\n failed: Set<string>\n\n constructor(\n public ctx: RecovererContext,\n opts: { concurrency: number },\n ) {\n this.queues = new UserQueues(opts.concurrency)\n this.failed = new Set()\n }\n\n async run(startCursor = 0) {\n const failed = await this.ctx.recoveryDb.db\n .selectFrom('failed')\n .select('did')\n .execute()\n for (const row of failed) {\n this.failed.add(row.did)\n }\n\n const totalRes = await this.ctx.sequencer.db.db\n .selectFrom('repo_seq')\n .select(countAll.as('count'))\n .executeTakeFirstOrThrow()\n const totalEvts = totalRes.count\n let completed = 0\n\n let cursor: number | undefined = startCursor\n while (cursor !== undefined) {\n const page = await this.ctx.sequencer.requestSeqRange({\n earliestSeq: cursor,\n limit: PAGE_SIZE,\n })\n page.forEach((evt) => this.processEvent(evt))\n cursor = page.at(-1)?.seq\n\n await this.queues.onEmpty()\n\n completed += PAGE_SIZE\n const percentComplete = (completed / totalEvts) * 100\n console.log(`${percentComplete.toFixed(2)}% - ${cursor}`)\n }\n\n await this.queues.processAll()\n }\n\n async processAll() {\n await this.queues.processAll()\n }\n\n async destroy() {\n await this.queues.destroy()\n }\n\n processEvent(evt: SeqEvt) {\n const did = didFromEvt(evt)\n if (!did) {\n return\n }\n this.queues.addToUser(did, async () => {\n if (this.failed.has(did)) {\n return\n }\n await processSeqEvt(this.ctx, evt).catch(async (err) => {\n this.failed.add(did)\n await trackFailure(this.ctx.recoveryDb, did, err)\n })\n })\n }\n}\n\nexport const processSeqEvt = async (ctx: RecovererContext, evt: SeqEvt) => {\n // only need to process commits & tombstones\n if (evt.type === 'account') {\n await processAccountEvt(ctx, evt.evt)\n }\n if (evt.type === 'commit') {\n await processCommit(ctx, evt.evt).catch()\n }\n}\n\nconst processCommit = async (ctx: RecovererContext, evt: CommitEvt) => {\n const did = evt.repo\n const { writes, blocks } = await parseCommitEvt(evt)\n if (evt.since === null) {\n const actorExists = await ctx.actorStore.exists(did)\n if (!actorExists) {\n await processRepoCreation(ctx, evt, writes, blocks)\n return\n }\n }\n await ctx.actorStore.transact(did, async (actorTxn) => {\n const root = await actorTxn.repo.storage.getRootDetailed()\n if (root.rev >= evt.rev) {\n return\n }\n const commit = await actorTxn.repo.formatCommit(writes)\n commit.newBlocks = blocks\n commit.cid = evt.commit\n commit.rev = evt.rev\n await actorTxn.repo.storage.applyCommit(commit)\n await actorTxn.repo.indexWrites(writes, commit.rev)\n await trackBlobs(actorTxn, writes)\n })\n}\n\nconst processRepoCreation = async (\n ctx: RecovererContext,\n evt: CommitEvt,\n writes: PreparedWrite[],\n blocks: BlockMap,\n) => {\n const did = evt.repo\n const keypair = await Secp256k1Keypair.create({ exportable: true })\n await ctx.actorStore.create(did, keypair)\n const commit: CommitData = {\n cid: evt.commit,\n rev: evt.rev,\n since: evt.since,\n prev: null,\n newBlocks: blocks,\n relevantBlocks: new BlockMap(),\n removedCids: new CidSet(),\n }\n await ctx.actorStore.transact(did, async (actorTxn) => {\n await actorTxn.repo.storage.applyCommit(commit, true)\n await actorTxn.repo.indexWrites(writes, commit.rev)\n await actorTxn.repo.blob.processWriteBlobs(commit.rev, writes)\n })\n await trackNewAccount(ctx.recoveryDb, did)\n}\n\nconst processAccountEvt = async (ctx: RecovererContext, evt: AccountEvt) => {\n // do not need to process deactivation/takedowns because we backup account DB as well\n if (evt.status !== AccountStatus.Deleted) {\n return\n }\n const { directory } = await ctx.actorStore.getLocation(evt.did)\n await rmIfExists(directory, true)\n await ctx.accountManager.deleteAccount(evt.did)\n}\n\nconst trackBlobs = async (\n store: ActorStoreTransactor,\n writes: PreparedWrite[],\n) => {\n await store.repo.blob.deleteDereferencedBlobs(writes)\n\n for (const write of writes) {\n if (\n write.action === WriteOpAction.Create ||\n write.action === WriteOpAction.Update\n ) {\n for (const blob of write.blobs) {\n await store.repo.blob.insertBlobMetadata(blob)\n await store.repo.blob.associateBlob(blob, write.uri)\n }\n }\n }\n}\n\nconst trackFailure = async (\n recoveryDb: RecoveryDb,\n did: DidString,\n err: unknown,\n) => {\n await recoveryDb.db\n .insertInto('failed')\n .values({\n did,\n error: err?.toString(),\n fixed: 0,\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n}\n\nconst trackNewAccount = async (recoveryDb: RecoveryDb, did: DidString) => {\n await recoveryDb.db\n .insertInto('new_account')\n .values({\n did,\n published: 0,\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n}\n\nconst parseCommitEvt = async (\n evt: CommitEvt,\n): Promise<{\n writes: PreparedWrite[]\n blocks: BlockMap\n}> => {\n const did = evt.repo\n const evtCar = await readCar(evt.blocks, { skipCidVerification: true })\n const writesUnfiltered = await Promise.all(\n evt.ops.map(async (op) => {\n const { collection, rkey } = parseDataKey(op.path)\n if (!isNsidString(collection)) return undefined\n if (!isRecordKeyString(rkey)) return undefined\n\n if (op.action === 'delete') {\n return prepareDelete({ did, collection, rkey })\n }\n if (!op.cid) return undefined\n\n const recordBytes = evtCar.blocks.get(op.cid)\n if (!recordBytes) return undefined\n const record = cborToLexRecord(recordBytes)\n\n if (op.action === 'create') {\n return prepareCreate({\n did,\n collection,\n rkey,\n record,\n validate: false,\n })\n } else {\n return prepareUpdate({\n did,\n collection,\n rkey,\n record,\n validate: false,\n })\n }\n }),\n )\n\n return {\n writes: writesUnfiltered.filter((w) => w != null),\n blocks: evtCar.blocks,\n }\n}\n\nconst didFromEvt = (evt: SeqEvt): DidString | null => {\n if (evt.type === 'account') {\n return evt.evt.did\n } else if (evt.type === 'commit') {\n return evt.evt.repo\n } else {\n return null\n }\n}\n"]}
1
+ {"version":3,"file":"recoverer.js","sourceRoot":"","sources":["../../../src/scripts/sequencer-recovery/recoverer.ts"],"names":[],"mappings":"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","sourcesContent":["import { rmIfExists } from '@atproto/common'\nimport { Secp256k1Keypair } from '@atproto/crypto'\nimport { DidString, isNsidString, isRecordKeyString } from '@atproto/lex'\nimport {\n BlockMap,\n CidSet,\n CommitData,\n WriteOpAction,\n cborToLexRecord,\n parseDataKey,\n readCar,\n} from '@atproto/repo'\nimport {\n AccountManager,\n AccountStatus,\n} from '../../account-manager/account-manager.js'\nimport { ActorStoreTransactor } from '../../actor-store/actor-store-transactor.js'\nimport { ActorStore } from '../../actor-store/actor-store.js'\nimport { countAll } from '../../db/index.js'\nimport {\n PreparedWrite,\n prepareCreate,\n prepareDelete,\n prepareUpdate,\n} from '../../repo/index.js'\nimport {\n AccountEvt,\n CommitEvt,\n SeqEvt,\n Sequencer,\n} from '../../sequencer/index.js'\nimport { RecoveryDb } from './recovery-db.js'\nimport { UserQueues } from './user-queues.js'\n\nexport type RecovererContextNoDb = {\n sequencer: Sequencer\n accountManager: AccountManager\n actorStore: ActorStore\n}\n\nexport type RecovererContext = RecovererContextNoDb & {\n recoveryDb: RecoveryDb\n}\n\nconst PAGE_SIZE = 5000\n\nexport class Recoverer {\n queues: UserQueues\n failed: Set<string>\n\n constructor(\n public ctx: RecovererContext,\n opts: { concurrency: number },\n ) {\n this.queues = new UserQueues(opts.concurrency)\n this.failed = new Set()\n }\n\n async run(startCursor = 0) {\n const failed = await this.ctx.recoveryDb.db\n .selectFrom('failed')\n .select('did')\n .execute()\n for (const row of failed) {\n this.failed.add(row.did)\n }\n\n const totalRes = await this.ctx.sequencer.db.db\n .selectFrom('repo_seq')\n .select(countAll.as('count'))\n .executeTakeFirstOrThrow()\n const totalEvts = totalRes.count\n let completed = 0\n\n let cursor: number | undefined = startCursor\n while (cursor !== undefined) {\n const page = await this.ctx.sequencer.requestSeqRange({\n earliestSeq: cursor,\n limit: PAGE_SIZE,\n })\n page.forEach((evt) => this.processEvent(evt))\n cursor = page.at(-1)?.seq\n\n await this.queues.onEmpty()\n\n completed += PAGE_SIZE\n const percentComplete = (completed / totalEvts) * 100\n console.log(`${percentComplete.toFixed(2)}% - ${cursor}`)\n }\n\n await this.queues.processAll()\n }\n\n async processAll() {\n await this.queues.processAll()\n }\n\n async destroy() {\n await this.queues.destroy()\n }\n\n processEvent(evt: SeqEvt) {\n const did = didFromEvt(evt)\n if (!did) {\n return\n }\n this.queues.addToUser(did, async () => {\n if (this.failed.has(did)) {\n return\n }\n await processSeqEvt(this.ctx, evt).catch(async (err) => {\n this.failed.add(did)\n await trackFailure(this.ctx.recoveryDb, did, err)\n })\n })\n }\n}\n\nexport const processSeqEvt = async (ctx: RecovererContext, evt: SeqEvt) => {\n // only need to process commits & tombstones\n if (evt.type === 'account') {\n await processAccountEvt(ctx, evt.evt)\n }\n if (evt.type === 'commit') {\n await processCommit(ctx, evt.evt).catch()\n }\n}\n\nconst processCommit = async (ctx: RecovererContext, evt: CommitEvt) => {\n const did = evt.repo\n const { writes, blocks } = await parseCommitEvt(evt)\n if (evt.since === null) {\n const actorExists = await ctx.actorStore.exists(did)\n if (!actorExists) {\n await processRepoCreation(ctx, evt, writes, blocks)\n return\n }\n }\n await ctx.actorStore.transact(did, async (actorTxn) => {\n const root = await actorTxn.repo.storage.getRootDetailed()\n if (root.rev >= evt.rev) {\n return\n }\n const commit = await actorTxn.repo.formatCommit(writes)\n commit.newBlocks = blocks\n commit.cid = evt.commit\n commit.rev = evt.rev\n await actorTxn.repo.storage.applyCommit(commit)\n await actorTxn.repo.indexWrites(writes, commit.rev)\n await trackBlobs(actorTxn, writes)\n })\n}\n\nconst processRepoCreation = async (\n ctx: RecovererContext,\n evt: CommitEvt,\n writes: PreparedWrite[],\n blocks: BlockMap,\n) => {\n const did = evt.repo\n const keypair = await Secp256k1Keypair.create({ exportable: true })\n await ctx.actorStore.create(did, keypair)\n const commit: CommitData = {\n cid: evt.commit,\n rev: evt.rev,\n since: evt.since,\n prev: null,\n newBlocks: blocks,\n relevantBlocks: new BlockMap(),\n removedCids: new CidSet(),\n }\n await ctx.actorStore.transact(did, async (actorTxn) => {\n await actorTxn.repo.storage.applyCommit(commit, true)\n await actorTxn.repo.indexWrites(writes, commit.rev)\n await actorTxn.repo.blob.processWriteBlobs(commit.rev, writes)\n })\n await trackNewAccount(ctx.recoveryDb, did)\n}\n\nconst processAccountEvt = async (ctx: RecovererContext, evt: AccountEvt) => {\n // do not need to process deactivation/takedowns because we backup account DB as well\n\n if (evt.status === AccountStatus.Deleted) {\n // In case an account deletion was sequenced, let's make sure to (first)\n // delete the accounts database, and (then) unlink the actor store from the\n // file system. Order matters here.\n await ctx.accountManager.deleteAccount(evt.did)\n\n const { directory } = await ctx.actorStore.getLocation(evt.did)\n await rmIfExists(directory, true)\n }\n}\n\nconst trackBlobs = async (\n store: ActorStoreTransactor,\n writes: PreparedWrite[],\n) => {\n await store.repo.blob.deleteDereferencedBlobs(writes)\n\n for (const write of writes) {\n if (\n write.action === WriteOpAction.Create ||\n write.action === WriteOpAction.Update\n ) {\n for (const blob of write.blobs) {\n await store.repo.blob.insertBlobMetadata(blob)\n await store.repo.blob.associateBlob(blob, write.uri)\n }\n }\n }\n}\n\nconst trackFailure = async (\n recoveryDb: RecoveryDb,\n did: DidString,\n err: unknown,\n) => {\n await recoveryDb.db\n .insertInto('failed')\n .values({\n did,\n error: err?.toString(),\n fixed: 0,\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n}\n\nconst trackNewAccount = async (recoveryDb: RecoveryDb, did: DidString) => {\n await recoveryDb.db\n .insertInto('new_account')\n .values({\n did,\n published: 0,\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n}\n\nconst parseCommitEvt = async (\n evt: CommitEvt,\n): Promise<{\n writes: PreparedWrite[]\n blocks: BlockMap\n}> => {\n const did = evt.repo\n const evtCar = await readCar(evt.blocks, { skipCidVerification: true })\n const writesUnfiltered = await Promise.all(\n evt.ops.map(async (op) => {\n const { collection, rkey } = parseDataKey(op.path)\n if (!isNsidString(collection)) return undefined\n if (!isRecordKeyString(rkey)) return undefined\n\n if (op.action === 'delete') {\n return prepareDelete({ did, collection, rkey })\n }\n if (!op.cid) return undefined\n\n const recordBytes = evtCar.blocks.get(op.cid)\n if (!recordBytes) return undefined\n const record = cborToLexRecord(recordBytes)\n\n if (op.action === 'create') {\n return prepareCreate({\n did,\n collection,\n rkey,\n record,\n validate: false,\n })\n } else {\n return prepareUpdate({\n did,\n collection,\n rkey,\n record,\n validate: false,\n })\n }\n }),\n )\n\n return {\n writes: writesUnfiltered.filter((w) => w != null),\n blocks: evtCar.blocks,\n }\n}\n\nconst didFromEvt = (evt: SeqEvt): DidString | null => {\n if (evt.type === 'account') {\n return evt.evt.did\n } else if (evt.type === 'commit') {\n return evt.evt.repo\n } else {\n return null\n }\n}\n"]}
@@ -29,12 +29,14 @@ export declare class Sequencer extends Sequencer_base {
29
29
  }): Promise<SeqEvt[]>;
30
30
  private pollDb;
31
31
  private exponentialBackoff;
32
- sequenceEvt(evt: RepoSeqInsert): Promise<number>;
33
- sequenceCommit(did: DidString, commitData: CommitDataWithOps): Promise<number>;
34
- sequenceSyncEvt(did: DidString, data: SyncEvtData): Promise<number>;
35
- sequenceIdentityEvt(did: DidString, handle?: HandleString): Promise<number>;
36
- sequenceAccountEvt(did: DidString, status: AccountStatus): Promise<number>;
37
- deleteAllForUser(did: string, excludingSeqs?: number[]): Promise<void>;
32
+ protected sequenceEvts(events: readonly RepoSeqInsert[]): Promise<number[]>;
33
+ sequenceCommit(did: DidString, commitData: CommitDataWithOps): Promise<void>;
34
+ sequenceSync(did: DidString, data: SyncEvtData): Promise<void>;
35
+ sequenceIdentity(did: DidString, handle?: HandleString): Promise<void>;
36
+ sequenceAccount(did: DidString, status: AccountStatus): Promise<void>;
37
+ sequenceAccountCreation(did: DidString, handle: HandleString, commit: CommitDataWithOps): Promise<void>;
38
+ sequenceAccountActivation(did: DidString, handle: HandleString, status: AccountStatus, syncData: SyncEvtData): Promise<void>;
39
+ sequenceAccountDeletion(did: DidString): Promise<void>;
38
40
  }
39
41
  export declare const parseRepoSeqRows: (rows: RepoSeqEntry[]) => SeqEvt[];
40
42
  type SeqRow = RepoSeqEntry;
@@ -1 +1 @@
1
- {"version":3,"file":"sequencer.d.ts","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,YAAY,MAAM,eAAe,CAAA;AAG7C,OAAO,EAAkB,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,EACL,YAAY,EACZ,aAAa,EACb,WAAW,EAGZ,MAAM,eAAe,CAAA;AACtB,OAAO,EAIL,MAAM,EAMP,MAAM,aAAa,CAAA;AAEpB,cAAc,aAAa,CAAA;8BAEqB,UAAU,gBAAgB;AAA1E,qBAAa,SAAU,SAAQ,cAA4C;IAOhE,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,QAAQ;IAClB,QAAQ;IARjB,EAAE,EAAE,WAAW,CAAA;IACf,SAAS,UAAQ;IACjB,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAO;IACxC,kBAAkB,SAAI;gBAGb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,SAAI,EACnB,wBAAwB,UAAQ;IAQ5B,KAAK;IAWL,OAAO;IAQP,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAU9B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAW5C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAWvD,eAAe,CAAC,IAAI,EAAE;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YA6BP,MAAM;YAwBN,kBAAkB;IAM1B,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAQhD,cAAc,CAClB,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,iBAAiB,GAC5B,OAAO,CAAC,MAAM,CAAC;IAKZ,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW;IAKjD,mBAAmB,CACvB,GAAG,EAAE,SAAS,EACd,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,kBAAkB,CACtB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,GAAE,MAAM,EAAO;CAUjE;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,YAAY,EAAE,KAAG,MAAM,EAuC7D,CAAA;AAED,KAAK,MAAM,GAAG,YAAY,CAAA;AAE1B,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IAChC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;AAEpE,eAAe,SAAS,CAAA"}
1
+ {"version":3,"file":"sequencer.d.ts","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,YAAY,MAAM,eAAe,CAAA;AAG7C,OAAO,EAAkB,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,EACL,YAAY,EACZ,aAAa,EACb,WAAW,EAGZ,MAAM,eAAe,CAAA;AACtB,OAAO,EAIL,MAAM,EAOP,MAAM,aAAa,CAAA;AAEpB,cAAc,aAAa,CAAA;8BAEqB,UAAU,gBAAgB;AAA1E,qBAAa,SAAU,SAAQ,cAA4C;IAOhE,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,QAAQ;IAClB,QAAQ;IARjB,EAAE,EAAE,WAAW,CAAA;IACf,SAAS,UAAQ;IACjB,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAO;IACxC,kBAAkB,SAAI;gBAGb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,SAAI,EACnB,wBAAwB,UAAQ;IAQ5B,KAAK;IAWL,OAAO;IAQP,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAU9B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAW5C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAWvD,eAAe,CAAC,IAAI,EAAE;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YA6BP,MAAM;YAwBN,kBAAkB;cAMhB,YAAY,CAC1B,MAAM,EAAE,SAAS,aAAa,EAAE,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC;IASP,cAAc,CACzB,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,iBAAiB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAIH,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9D,gBAAgB,CAC3B,GAAG,EAAE,SAAS,EACd,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC;IAIH,eAAe,CAC1B,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC;IAIH,uBAAuB,CAClC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC;IAUH,yBAAyB,CACpC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC;IASH,uBAAuB,CAAC,GAAG,EAAE,SAAS;CAWpD;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,YAAY,EAAE,KAAG,MAAM,EAuC7D,CAAA;AAED,KAAK,MAAM,GAAG,YAAY,CAAA;AAE1B,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IAChC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;AAEpE,eAAe,SAAS,CAAA"}
@@ -1,9 +1,10 @@
1
1
  import EventEmitter from 'node:events';
2
2
  import { SECOND, wait } from '@atproto/common';
3
3
  import { decode as cborDecode } from '@atproto/lex-cbor';
4
+ import { AccountStatus } from '../account-manager/helpers/account.js';
4
5
  import { seqLogger as log } from '../logger.js';
5
6
  import { getDb, getMigrator, } from './db/index.js';
6
- import { formatSeqAccountEvt, formatSeqCommit, formatSeqIdentityEvt, formatSeqSyncEvt, } from './events.js';
7
+ import { formatSeqAccountEvt, formatSeqCommit, formatSeqIdentityEvt, formatSeqSyncEvt, syncEvtDataFromCommit, } from './events.js';
7
8
  export * from './events.js';
8
9
  export class Sequencer extends EventEmitter {
9
10
  constructor(dbLocation, crawlers, lastSeen = 0, disableWalAutoCheckpoint = false) {
@@ -120,32 +121,50 @@ export class Sequencer extends EventEmitter {
120
121
  const waitTime = Math.min(Math.pow(2, this.triesWithNoResults), SECOND);
121
122
  await wait(waitTime);
122
123
  }
123
- async sequenceEvt(evt) {
124
- const [{ seq }] = await this.db.executeWithRetry(this.db.db.insertInto('repo_seq').values(evt).returning('seq'));
124
+ async sequenceEvts(events) {
125
+ if (!events.length)
126
+ return [];
127
+ const rows = await this.db.executeWithRetry(this.db.db.insertInto('repo_seq').values(events).returning('seq'));
125
128
  this.crawlers.notifyOfUpdate();
126
- return seq;
129
+ return rows.map((row) => row.seq);
127
130
  }
128
131
  async sequenceCommit(did, commitData) {
129
- const evt = await formatSeqCommit(did, commitData);
130
- return this.sequenceEvt(evt);
131
- }
132
- async sequenceSyncEvt(did, data) {
133
- const evt = await formatSeqSyncEvt(did, data);
134
- return this.sequenceEvt(evt);
135
- }
136
- async sequenceIdentityEvt(did, handle) {
137
- const evt = await formatSeqIdentityEvt(did, handle);
138
- return this.sequenceEvt(evt);
139
- }
140
- async sequenceAccountEvt(did, status) {
141
- const evt = await formatSeqAccountEvt(did, status);
142
- return this.sequenceEvt(evt);
143
- }
144
- async deleteAllForUser(did, excludingSeqs = []) {
132
+ await this.sequenceEvts([await formatSeqCommit(did, commitData)]);
133
+ }
134
+ async sequenceSync(did, data) {
135
+ await this.sequenceEvts([await formatSeqSyncEvt(did, data)]);
136
+ }
137
+ async sequenceIdentity(did, handle) {
138
+ await this.sequenceEvts([await formatSeqIdentityEvt(did, handle)]);
139
+ }
140
+ async sequenceAccount(did, status) {
141
+ await this.sequenceEvts([await formatSeqAccountEvt(did, status)]);
142
+ }
143
+ async sequenceAccountCreation(did, handle, commit) {
144
+ // Atomically sequence all events
145
+ await this.sequenceEvts([
146
+ await formatSeqIdentityEvt(did, handle),
147
+ await formatSeqAccountEvt(did, AccountStatus.Active),
148
+ await formatSeqCommit(did, commit),
149
+ await formatSeqSyncEvt(did, syncEvtDataFromCommit(commit)),
150
+ ]);
151
+ }
152
+ async sequenceAccountActivation(did, handle, status, syncData) {
153
+ // Atomically sequence all events
154
+ await this.sequenceEvts([
155
+ await formatSeqAccountEvt(did, status),
156
+ await formatSeqIdentityEvt(did, handle),
157
+ await formatSeqSyncEvt(did, syncData),
158
+ ]);
159
+ }
160
+ async sequenceAccountDeletion(did) {
161
+ const [seq] = await this.sequenceEvts([
162
+ await formatSeqAccountEvt(did, AccountStatus.Deleted),
163
+ ]);
145
164
  await this.db.executeWithRetry(this.db.db
146
165
  .deleteFrom('repo_seq')
147
166
  .where('did', '=', did)
148
- .if(excludingSeqs.length > 0, (qb) => qb.where('seq', 'not in', excludingSeqs)));
167
+ .where('seq', '!=', seq));
149
168
  }
150
169
  }
151
170
  export const parseRepoSeqRows = (rows) => {
@@ -1 +1 @@
1
- {"version":3,"file":"sequencer.js","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'node:events'\nimport type TypedEmitter from 'typed-emitter'\nimport { SECOND, wait } from '@atproto/common'\nimport { decode as cborDecode } from '@atproto/lex-cbor'\nimport { DatetimeString, DidString, HandleString } from '@atproto/syntax'\nimport { AccountStatus } from '../account-manager/helpers/account.js'\nimport { Crawlers } from '../crawlers.js'\nimport { seqLogger as log } from '../logger.js'\nimport { CommitDataWithOps, SyncEvtData } from '../repo/index.js'\nimport {\n RepoSeqEntry,\n RepoSeqInsert,\n SequencerDb,\n getDb,\n getMigrator,\n} from './db/index.js'\nimport {\n AccountEvt,\n CommitEvt,\n IdentityEvt,\n SeqEvt,\n SyncEvt,\n formatSeqAccountEvt,\n formatSeqCommit,\n formatSeqIdentityEvt,\n formatSeqSyncEvt,\n} from './events.js'\n\nexport * from './events.js'\n\nexport class Sequencer extends (EventEmitter as new () => SequencerEmitter) {\n db: SequencerDb\n destroyed = false\n pollPromise: Promise<void> | null = null\n triesWithNoResults = 0\n\n constructor(\n public dbLocation: string,\n public crawlers: Crawlers,\n public lastSeen = 0,\n disableWalAutoCheckpoint = false,\n ) {\n super()\n // note: this does not err when surpassed, just prints a warning to stderr\n this.setMaxListeners(100)\n this.db = getDb(dbLocation, disableWalAutoCheckpoint)\n }\n\n async start() {\n await this.db.ensureWal()\n const migrator = getMigrator(this.db)\n await migrator.migrateToLatestOrThrow()\n const curr = await this.curr()\n this.lastSeen = curr ?? 0\n if (this.pollPromise === null) {\n this.pollPromise = this.pollDb()\n }\n }\n\n async destroy() {\n this.destroyed = true\n if (this.pollPromise) {\n await this.pollPromise\n }\n this.emit('close')\n }\n\n async curr(): Promise<number | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'desc')\n .limit(1)\n .executeTakeFirst()\n return got?.seq ?? null\n }\n\n async next(cursor: number): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('seq', '>', cursor)\n .limit(1)\n .orderBy('seq', 'asc')\n .executeTakeFirst()\n return got || null\n }\n\n async earliestAfterTime(time: string): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('sequencedAt', '>=', time)\n .orderBy('sequencedAt', 'asc')\n .limit(1)\n .executeTakeFirst()\n return got || null\n }\n\n async requestSeqRange(opts: {\n earliestSeq?: number\n latestSeq?: number\n earliestTime?: string\n limit?: number\n }): Promise<SeqEvt[]> {\n const { earliestSeq, latestSeq, earliestTime, limit } = opts\n\n let seqQb = this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'asc')\n .where('invalidated', '=', 0)\n if (earliestSeq !== undefined) {\n seqQb = seqQb.where('seq', '>', earliestSeq)\n }\n if (latestSeq !== undefined) {\n seqQb = seqQb.where('seq', '<=', latestSeq)\n }\n if (earliestTime !== undefined) {\n seqQb = seqQb.where('sequencedAt', '>=', earliestTime)\n }\n if (limit !== undefined) {\n seqQb = seqQb.limit(limit)\n }\n\n const rows = await seqQb.execute()\n if (rows.length < 1) {\n return []\n }\n\n return parseRepoSeqRows(rows)\n }\n\n private async pollDb(): Promise<void> {\n if (this.destroyed) return\n // if already polling, do not start another poll\n try {\n const evts = await this.requestSeqRange({\n earliestSeq: this.lastSeen,\n limit: 1000,\n })\n if (evts.length > 0) {\n this.triesWithNoResults = 0\n this.emit('events', evts)\n this.lastSeen = evts.at(-1)?.seq ?? this.lastSeen\n } else {\n await this.exponentialBackoff()\n }\n this.pollPromise = this.pollDb()\n } catch (err) {\n log.error({ err, lastSeen: this.lastSeen }, 'sequencer failed to poll db')\n await this.exponentialBackoff()\n this.pollPromise = this.pollDb()\n }\n }\n\n // when no results, exponential backoff on pulling, with a max of a second wait\n private async exponentialBackoff(): Promise<void> {\n this.triesWithNoResults++\n const waitTime = Math.min(Math.pow(2, this.triesWithNoResults), SECOND)\n await wait(waitTime)\n }\n\n async sequenceEvt(evt: RepoSeqInsert): Promise<number> {\n const [{ seq }] = await this.db.executeWithRetry(\n this.db.db.insertInto('repo_seq').values(evt).returning('seq'),\n )\n this.crawlers.notifyOfUpdate()\n return seq\n }\n\n async sequenceCommit(\n did: DidString,\n commitData: CommitDataWithOps,\n ): Promise<number> {\n const evt = await formatSeqCommit(did, commitData)\n return this.sequenceEvt(evt)\n }\n\n async sequenceSyncEvt(did: DidString, data: SyncEvtData) {\n const evt = await formatSeqSyncEvt(did, data)\n return this.sequenceEvt(evt)\n }\n\n async sequenceIdentityEvt(\n did: DidString,\n handle?: HandleString,\n ): Promise<number> {\n const evt = await formatSeqIdentityEvt(did, handle)\n return this.sequenceEvt(evt)\n }\n\n async sequenceAccountEvt(\n did: DidString,\n status: AccountStatus,\n ): Promise<number> {\n const evt = await formatSeqAccountEvt(did, status)\n return this.sequenceEvt(evt)\n }\n\n async deleteAllForUser(did: string, excludingSeqs: number[] = []) {\n await this.db.executeWithRetry(\n this.db.db\n .deleteFrom('repo_seq')\n .where('did', '=', did)\n .if(excludingSeqs.length > 0, (qb) =>\n qb.where('seq', 'not in', excludingSeqs),\n ),\n )\n }\n}\n\nexport const parseRepoSeqRows = (rows: RepoSeqEntry[]): SeqEvt[] => {\n const seqEvts: SeqEvt[] = []\n for (const row of rows) {\n // should never hit this because of WHERE clause\n if (row.seq === null) {\n continue\n }\n const evt = cborDecode(row.event)\n if (row.eventType === 'append') {\n seqEvts.push({\n type: 'commit',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as CommitEvt,\n })\n } else if (row.eventType === 'sync') {\n seqEvts.push({\n type: 'sync',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as SyncEvt,\n })\n } else if (row.eventType === 'identity') {\n seqEvts.push({\n type: 'identity',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as IdentityEvt,\n })\n } else if (row.eventType === 'account') {\n seqEvts.push({\n type: 'account',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as AccountEvt,\n })\n }\n }\n return seqEvts\n}\n\ntype SeqRow = RepoSeqEntry\n\ntype SequencerEvents = {\n events: (evts: SeqEvt[]) => void\n close: () => void\n}\n\nexport type SequencerEmitter = TypedEmitter.default<SequencerEvents>\n\nexport default Sequencer\n"]}
1
+ {"version":3,"file":"sequencer.js","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'node:events'\nimport type TypedEmitter from 'typed-emitter'\nimport { SECOND, wait } from '@atproto/common'\nimport { decode as cborDecode } from '@atproto/lex-cbor'\nimport { DatetimeString, DidString, HandleString } from '@atproto/syntax'\nimport { AccountStatus } from '../account-manager/helpers/account.js'\nimport { Crawlers } from '../crawlers.js'\nimport { seqLogger as log } from '../logger.js'\nimport { CommitDataWithOps, SyncEvtData } from '../repo/index.js'\nimport {\n RepoSeqEntry,\n RepoSeqInsert,\n SequencerDb,\n getDb,\n getMigrator,\n} from './db/index.js'\nimport {\n AccountEvt,\n CommitEvt,\n IdentityEvt,\n SeqEvt,\n SyncEvt,\n formatSeqAccountEvt,\n formatSeqCommit,\n formatSeqIdentityEvt,\n formatSeqSyncEvt,\n syncEvtDataFromCommit,\n} from './events.js'\n\nexport * from './events.js'\n\nexport class Sequencer extends (EventEmitter as new () => SequencerEmitter) {\n db: SequencerDb\n destroyed = false\n pollPromise: Promise<void> | null = null\n triesWithNoResults = 0\n\n constructor(\n public dbLocation: string,\n public crawlers: Crawlers,\n public lastSeen = 0,\n disableWalAutoCheckpoint = false,\n ) {\n super()\n // note: this does not err when surpassed, just prints a warning to stderr\n this.setMaxListeners(100)\n this.db = getDb(dbLocation, disableWalAutoCheckpoint)\n }\n\n async start() {\n await this.db.ensureWal()\n const migrator = getMigrator(this.db)\n await migrator.migrateToLatestOrThrow()\n const curr = await this.curr()\n this.lastSeen = curr ?? 0\n if (this.pollPromise === null) {\n this.pollPromise = this.pollDb()\n }\n }\n\n async destroy() {\n this.destroyed = true\n if (this.pollPromise) {\n await this.pollPromise\n }\n this.emit('close')\n }\n\n async curr(): Promise<number | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'desc')\n .limit(1)\n .executeTakeFirst()\n return got?.seq ?? null\n }\n\n async next(cursor: number): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('seq', '>', cursor)\n .limit(1)\n .orderBy('seq', 'asc')\n .executeTakeFirst()\n return got || null\n }\n\n async earliestAfterTime(time: string): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('sequencedAt', '>=', time)\n .orderBy('sequencedAt', 'asc')\n .limit(1)\n .executeTakeFirst()\n return got || null\n }\n\n async requestSeqRange(opts: {\n earliestSeq?: number\n latestSeq?: number\n earliestTime?: string\n limit?: number\n }): Promise<SeqEvt[]> {\n const { earliestSeq, latestSeq, earliestTime, limit } = opts\n\n let seqQb = this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'asc')\n .where('invalidated', '=', 0)\n if (earliestSeq !== undefined) {\n seqQb = seqQb.where('seq', '>', earliestSeq)\n }\n if (latestSeq !== undefined) {\n seqQb = seqQb.where('seq', '<=', latestSeq)\n }\n if (earliestTime !== undefined) {\n seqQb = seqQb.where('sequencedAt', '>=', earliestTime)\n }\n if (limit !== undefined) {\n seqQb = seqQb.limit(limit)\n }\n\n const rows = await seqQb.execute()\n if (rows.length < 1) {\n return []\n }\n\n return parseRepoSeqRows(rows)\n }\n\n private async pollDb(): Promise<void> {\n if (this.destroyed) return\n // if already polling, do not start another poll\n try {\n const evts = await this.requestSeqRange({\n earliestSeq: this.lastSeen,\n limit: 1000,\n })\n if (evts.length > 0) {\n this.triesWithNoResults = 0\n this.emit('events', evts)\n this.lastSeen = evts.at(-1)?.seq ?? this.lastSeen\n } else {\n await this.exponentialBackoff()\n }\n this.pollPromise = this.pollDb()\n } catch (err) {\n log.error({ err, lastSeen: this.lastSeen }, 'sequencer failed to poll db')\n await this.exponentialBackoff()\n this.pollPromise = this.pollDb()\n }\n }\n\n // when no results, exponential backoff on pulling, with a max of a second wait\n private async exponentialBackoff(): Promise<void> {\n this.triesWithNoResults++\n const waitTime = Math.min(Math.pow(2, this.triesWithNoResults), SECOND)\n await wait(waitTime)\n }\n\n protected async sequenceEvts(\n events: readonly RepoSeqInsert[],\n ): Promise<number[]> {\n if (!events.length) return []\n const rows = await this.db.executeWithRetry(\n this.db.db.insertInto('repo_seq').values(events).returning('seq'),\n )\n this.crawlers.notifyOfUpdate()\n return rows.map((row) => row.seq)\n }\n\n public async sequenceCommit(\n did: DidString,\n commitData: CommitDataWithOps,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqCommit(did, commitData)])\n }\n\n public async sequenceSync(did: DidString, data: SyncEvtData): Promise<void> {\n await this.sequenceEvts([await formatSeqSyncEvt(did, data)])\n }\n\n public async sequenceIdentity(\n did: DidString,\n handle?: HandleString,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqIdentityEvt(did, handle)])\n }\n\n public async sequenceAccount(\n did: DidString,\n status: AccountStatus,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqAccountEvt(did, status)])\n }\n\n public async sequenceAccountCreation(\n did: DidString,\n handle: HandleString,\n commit: CommitDataWithOps,\n ): Promise<void> {\n // Atomically sequence all events\n await this.sequenceEvts([\n await formatSeqIdentityEvt(did, handle),\n await formatSeqAccountEvt(did, AccountStatus.Active),\n await formatSeqCommit(did, commit),\n await formatSeqSyncEvt(did, syncEvtDataFromCommit(commit)),\n ])\n }\n\n public async sequenceAccountActivation(\n did: DidString,\n handle: HandleString,\n status: AccountStatus,\n syncData: SyncEvtData,\n ): Promise<void> {\n // Atomically sequence all events\n await this.sequenceEvts([\n await formatSeqAccountEvt(did, status),\n await formatSeqIdentityEvt(did, handle),\n await formatSeqSyncEvt(did, syncData),\n ])\n }\n\n public async sequenceAccountDeletion(did: DidString) {\n const [seq] = await this.sequenceEvts([\n await formatSeqAccountEvt(did, AccountStatus.Deleted),\n ])\n await this.db.executeWithRetry(\n this.db.db\n .deleteFrom('repo_seq')\n .where('did', '=', did)\n .where('seq', '!=', seq),\n )\n }\n}\n\nexport const parseRepoSeqRows = (rows: RepoSeqEntry[]): SeqEvt[] => {\n const seqEvts: SeqEvt[] = []\n for (const row of rows) {\n // should never hit this because of WHERE clause\n if (row.seq === null) {\n continue\n }\n const evt = cborDecode(row.event)\n if (row.eventType === 'append') {\n seqEvts.push({\n type: 'commit',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as CommitEvt,\n })\n } else if (row.eventType === 'sync') {\n seqEvts.push({\n type: 'sync',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as SyncEvt,\n })\n } else if (row.eventType === 'identity') {\n seqEvts.push({\n type: 'identity',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as IdentityEvt,\n })\n } else if (row.eventType === 'account') {\n seqEvts.push({\n type: 'account',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as AccountEvt,\n })\n }\n }\n return seqEvts\n}\n\ntype SeqRow = RepoSeqEntry\n\ntype SequencerEvents = {\n events: (evts: SeqEvt[]) => void\n close: () => void\n}\n\nexport type SequencerEmitter = TypedEmitter.default<SequencerEvents>\n\nexport default Sequencer\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/pds",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of atproto Personal Data Server (PDS)",
6
6
  "keywords": [
@@ -43,21 +43,21 @@
43
43
  "undici": "^6.19.8",
44
44
  "zod": "^3.23.8",
45
45
  "@atproto-labs/fetch-node": "^0.3.0",
46
- "@atproto-labs/simple-store": "^0.4.0",
47
46
  "@atproto-labs/simple-store-memory": "^0.2.0",
48
47
  "@atproto-labs/simple-store-redis": "^0.1.0",
49
48
  "@atproto-labs/xrpc-utils": "^0.1.0",
50
49
  "@atproto/aws": "^0.3.0",
51
50
  "@atproto/crypto": "^0.5.0",
52
- "@atproto/common": "^0.6.2",
53
51
  "@atproto/did": "^0.5.0",
54
52
  "@atproto/identity": "^0.5.0",
53
+ "@atproto-labs/simple-store": "^0.4.0",
55
54
  "@atproto/lex": "^0.1.3",
56
55
  "@atproto/lex-cbor": "^0.1.0",
56
+ "@atproto/lex-data": "^0.1.1",
57
57
  "@atproto/lex-json": "^0.1.0",
58
- "@atproto/oauth-provider": "^0.18.2",
58
+ "@atproto/oauth-provider": "^0.18.3",
59
+ "@atproto/common": "^0.6.2",
59
60
  "@atproto/oauth-scopes": "^0.5.0",
60
- "@atproto/lex-data": "^0.1.1",
61
61
  "@atproto/repo": "^0.10.0",
62
62
  "@atproto/xrpc-server": "^0.11.1",
63
63
  "@atproto/syntax": "^0.6.1",
@@ -81,8 +81,8 @@
81
81
  "ws": "^8.12.0",
82
82
  "@atproto/api": "^0.20.14",
83
83
  "@atproto/bsky": "^0.0.241",
84
- "@atproto/lex-document": "^0.1.0",
85
- "@atproto/oauth-client-browser-example": "^0.1.1"
84
+ "@atproto/oauth-client-browser-example": "^0.1.1",
85
+ "@atproto/lex-document": "^0.1.0"
86
86
  },
87
87
  "type": "module",
88
88
  "exports": {
@@ -221,33 +221,36 @@ export class AccountManager {
221
221
  throw new InvalidRequestError('Password too long')
222
222
  }
223
223
 
224
- const passwordScrypt = password
225
- ? await scrypt.genSaltAndHash(password)
226
- : undefined
224
+ const passwordScrypt =
225
+ email && password ? await scrypt.genSaltAndHash(password) : undefined
227
226
 
228
227
  const now = currentDatetimeString()
229
- await this.db.transaction(async (dbTxn) => {
228
+ return this.db.transaction(async (dbTxn) => {
230
229
  if (inviteCode) {
231
230
  await invite.ensureInviteIsAvailable(dbTxn, inviteCode)
232
231
  }
233
- await Promise.all([
234
- account.registerActor(dbTxn, { did, handle, deactivated }),
235
- email && passwordScrypt
236
- ? account.registerAccount(dbTxn, { did, email, passwordScrypt })
237
- : Promise.resolve(),
238
- invite.recordInviteUse(dbTxn, {
239
- did,
240
- inviteCode,
241
- now,
242
- }),
243
- refreshJwt &&
244
- auth.storeRefreshToken(
245
- dbTxn,
246
- auth.decodeRefreshToken(refreshJwt),
247
- null,
248
- ),
249
- repo.updateRoot(dbTxn, did, repoCid, repoRev),
250
- ])
232
+
233
+ await account.registerActor(dbTxn, { did, handle, deactivated })
234
+
235
+ if (email && passwordScrypt) {
236
+ await account.registerAccount(dbTxn, { did, email, passwordScrypt })
237
+ }
238
+
239
+ await invite.recordInviteUse(dbTxn, {
240
+ did,
241
+ inviteCode,
242
+ now,
243
+ })
244
+
245
+ if (refreshJwt) {
246
+ await auth.storeRefreshToken(
247
+ dbTxn,
248
+ auth.decodeRefreshToken(refreshJwt),
249
+ null,
250
+ )
251
+ }
252
+
253
+ await repo.updateRoot(dbTxn, did, repoCid, repoRev)
251
254
  })
252
255
  }
253
256
 
@@ -359,7 +362,7 @@ export class AccountManager {
359
362
  await account.updateHandle(this.db, did, handle)
360
363
 
361
364
  try {
362
- await this.sequencer.sequenceIdentityEvt(did, handle)
365
+ await this.sequencer.sequenceIdentity(did, handle)
363
366
  } catch (err) {
364
367
  httpLogger.error({ err, did, handle }, 'failed to sequence handle update')
365
368
  }
@@ -3,6 +3,7 @@ import { Client, createOp as createPlcOp } from '@did-plc/lib'
3
3
  import { Selectable } from 'kysely'
4
4
  import { Keypair, Secp256k1Keypair } from '@atproto/crypto'
5
5
  import {
6
+ DidString,
6
7
  HandleString,
7
8
  asAtIdentifierString,
8
9
  getBlobCidString,
@@ -60,15 +61,11 @@ import { fromDateISO } from '../db/index.js'
60
61
  import { ImageUrlBuilder } from '../image/image-url-builder.js'
61
62
  import { dbLogger } from '../logger.js'
62
63
  import { ServerMailer } from '../mailer/index.js'
63
- import { Sequencer, syncEvtDataFromCommit } from '../sequencer/index.js'
64
+ import { Sequencer } from '../sequencer/index.js'
64
65
  import { AccountManager, InvalidPasswordError } from './account-manager.js'
65
66
  import * as schemas from './db/schema/index.js'
66
67
  import * as accountDeviceHelper from './helpers/account-device.js'
67
- import {
68
- AccountStatus,
69
- ActorAccount,
70
- UserAlreadyExistsError,
71
- } from './helpers/account.js'
68
+ import { ActorAccount, UserAlreadyExistsError } from './helpers/account.js'
72
69
  import * as authRequestHelper from './helpers/authorization-request.js'
73
70
  import * as authorizedClientHelper from './helpers/authorized-client.js'
74
71
  import * as deviceHelper from './helpers/device.js'
@@ -157,7 +154,13 @@ export class OAuthStore
157
154
  const signingKey = await Secp256k1Keypair.create({ exportable: true })
158
155
  const signingKeyDid = signingKey.did()
159
156
 
160
- const plcCreate = await createPlcOp({
157
+ const canTombstone =
158
+ // @NOTE IMPORTANT We don't support "bring your own DID" here (yet?). If
159
+ // we ever do, make sure to update the computation of canTombstone so that
160
+ // the user's did don't get tombstoned.
161
+ true
162
+
163
+ const plc = await createPlcOp({
161
164
  signingKey: signingKeyDid,
162
165
  rotationKeys: this.recoveryDidKey
163
166
  ? [this.recoveryDidKey, this.plcRotationKey.did()]
@@ -167,44 +170,60 @@ export class OAuthStore
167
170
  signer: this.plcRotationKey,
168
171
  })
169
172
 
170
- const { did, op } = plcCreate
171
- assert(isDidString(did), 'Generated DID is not a valid DidString')
173
+ const did = plc.did as DidString
172
174
 
173
175
  try {
174
176
  await this.actorStore.create(did, signingKey)
177
+
175
178
  try {
176
- const commit = await this.actorStore.transact(did, (actorTxn) =>
177
- actorTxn.repo.createRepo([]),
178
- )
179
+ const commit = await this.actorStore.transact(did, (actorTxn) => {
180
+ return actorTxn.repo.createRepo([])
181
+ })
179
182
 
180
- await this.plcClient.sendOperation(did, op)
183
+ await this.plcClient.sendOperation(did, plc.op)
181
184
 
182
- await this.accountManager.createAccount({
183
- did,
184
- handle,
185
- email,
186
- password,
187
- inviteCode,
188
- repoCid: commit.cid,
189
- repoRev: commit.rev,
190
- })
191
185
  try {
192
- await this.sequencer.sequenceIdentityEvt(did, handle)
193
- await this.sequencer.sequenceAccountEvt(did, AccountStatus.Active)
194
- await this.sequencer.sequenceCommit(did, commit)
195
- await this.sequencer.sequenceSyncEvt(
186
+ await this.accountManager.createAccount({
196
187
  did,
197
- syncEvtDataFromCommit(commit),
198
- )
199
- await this.accountManager.updateRepoRoot(did, commit.cid, commit.rev)
200
- await this.actorStore.clearReservedKeypair(signingKeyDid, did)
201
-
202
- const account = await this.accountManager.getAccount(did)
203
- if (!account) throw new Error('Account not found')
204
-
205
- return await this.buildAccount(account)
188
+ handle,
189
+ email,
190
+ password,
191
+ inviteCode,
192
+ repoCid: commit.cid,
193
+ repoRev: commit.rev,
194
+ })
195
+
196
+ try {
197
+ await this.sequencer.sequenceAccountCreation(did, handle, commit)
198
+
199
+ try {
200
+ await this.actorStore
201
+ .clearReservedKeypair(signingKeyDid, did)
202
+ .catch((err) => {
203
+ // @NOTE This is a cleanup operation so we won't fail the
204
+ // whole flow if it fails, but we log it just in case
205
+ dbLogger.error(
206
+ { did, signingKeyDid, err },
207
+ 'Failed to clear reserved keypair',
208
+ )
209
+ })
210
+
211
+ const account = await this.accountManager.getAccount(did)
212
+ assert(account, 'Account not found after creation')
213
+
214
+ return await this.buildAccount(account)
215
+ } catch (err) {
216
+ await this.sequencer.sequenceAccountDeletion(did)
217
+ throw err
218
+ }
219
+ } catch (err) {
220
+ await this.accountManager.deleteAccount(did)
221
+ throw err
222
+ }
206
223
  } catch (err) {
207
- this.accountManager.deleteAccount(did)
224
+ if (canTombstone) {
225
+ await this.plcClient.tombstone(did, this.plcRotationKey)
226
+ }
208
227
  throw err
209
228
  }
210
229
  } catch (err) {
@@ -1,5 +1,4 @@
1
1
  import { Server } from '@atproto/xrpc-server'
2
- import { AccountStatus } from '../../../../account-manager/account-manager.js'
3
2
  import { AppContext } from '../../../../context.js'
4
3
  import { com } from '../../../../lexicons/index.js'
5
4
 
@@ -8,13 +7,16 @@ export default function (server: Server, ctx: AppContext) {
8
7
  auth: ctx.authVerifier.adminToken,
9
8
  handler: async ({ input }) => {
10
9
  const { did } = input.body
11
- await ctx.actorStore.destroy(did)
10
+
11
+ // @NOTE Order matters here: first "unlink" the account by removing it
12
+ // from the account manager database ("source of truth"), then notify the
13
+ // sequencer, and finally cleanup files from the file system.
12
14
  await ctx.accountManager.deleteAccount(did)
13
- const accountSeq = await ctx.sequencer.sequenceAccountEvt(
14
- did,
15
- AccountStatus.Deleted,
16
- )
17
- await ctx.sequencer.deleteAllForUser(did, [accountSeq])
15
+ try {
16
+ await ctx.sequencer.sequenceAccountDeletion(did)
17
+ } finally {
18
+ await ctx.actorStore.destroy(did)
19
+ }
18
20
  },
19
21
  })
20
22
  }
@@ -41,7 +41,7 @@ export default function (server: Server, ctx: AppContext) {
41
41
 
42
42
  if (com.atproto.admin.defs.repoRef.$isTypeOf(subject)) {
43
43
  const status = await ctx.accountManager.getAccountStatus(subject.did)
44
- await ctx.sequencer.sequenceAccountEvt(subject.did, status)
44
+ await ctx.sequencer.sequenceAccount(subject.did, status)
45
45
  }
46
46
 
47
47
  return {
@@ -50,7 +50,7 @@ export default function (server: Server, ctx: AppContext) {
50
50
  }
51
51
 
52
52
  await ctx.plcClient.sendOperation(requester, op)
53
- await ctx.sequencer.sequenceIdentityEvt(requester)
53
+ await ctx.sequencer.sequenceIdentity(requester)
54
54
 
55
55
  try {
56
56
  await ctx.idResolver.did.resolve(requester, true)
@@ -48,12 +48,12 @@ export default function (server: Server, ctx: AppContext) {
48
48
 
49
49
  // @NOTE: we're over-emitting for now for backwards compatibility, can reduce this in the future
50
50
  const status = await ctx.accountManager.getAccountStatus(requester)
51
- await ctx.sequencer.sequenceAccountEvt(requester, status)
52
- await ctx.sequencer.sequenceIdentityEvt(
51
+ await ctx.sequencer.sequenceAccountActivation(
53
52
  requester,
54
53
  account.handle ?? INVALID_HANDLE,
54
+ status,
55
+ syncData,
55
56
  )
56
- await ctx.sequencer.sequenceSyncEvt(requester, syncData)
57
57
  },
58
58
  })
59
59
  }