@atproto/pds 0.5.28 → 0.5.30

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 (50) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/api/com/atproto/identity/resolveHandle.d.ts.map +1 -1
  3. package/dist/api/com/atproto/identity/resolveHandle.js +1 -1
  4. package/dist/api/com/atproto/identity/resolveHandle.js.map +1 -1
  5. package/dist/auth-verifier.d.ts.map +1 -1
  6. package/dist/auth-verifier.js +12 -6
  7. package/dist/auth-verifier.js.map +1 -1
  8. package/dist/context.d.ts +2 -1
  9. package/dist/context.d.ts.map +1 -1
  10. package/dist/context.js +6 -0
  11. package/dist/context.js.map +1 -1
  12. package/dist/lexicons/app/bsky/actor/status.defs.d.ts +5 -3
  13. package/dist/lexicons/app/bsky/actor/status.defs.d.ts.map +1 -1
  14. package/dist/lexicons/app/bsky/actor/status.defs.js +3 -0
  15. package/dist/lexicons/app/bsky/actor/status.defs.js.map +1 -1
  16. package/dist/lexicons/app/bsky/embed/video.defs.d.ts.map +1 -1
  17. package/dist/lexicons/app/bsky/embed/video.defs.js +2 -4
  18. package/dist/lexicons/app/bsky/embed/video.defs.js.map +1 -1
  19. package/dist/lexicons/app/bsky/feed/defs.defs.d.ts +84 -42
  20. package/dist/lexicons/app/bsky/feed/defs.defs.d.ts.map +1 -1
  21. package/dist/lexicons/app/bsky/feed/defs.defs.js +52 -0
  22. package/dist/lexicons/app/bsky/feed/defs.defs.js.map +1 -1
  23. package/dist/lexicons/app/bsky/graph/defs.defs.d.ts +15 -9
  24. package/dist/lexicons/app/bsky/graph/defs.defs.d.ts.map +1 -1
  25. package/dist/lexicons/app/bsky/graph/defs.defs.js +9 -0
  26. package/dist/lexicons/app/bsky/graph/defs.defs.js.map +1 -1
  27. package/dist/lexicons/com/atproto/moderation/defs.defs.d.ts +35 -21
  28. package/dist/lexicons/com/atproto/moderation/defs.defs.d.ts.map +1 -1
  29. package/dist/lexicons/com/atproto/moderation/defs.defs.js +21 -0
  30. package/dist/lexicons/com/atproto/moderation/defs.defs.js.map +1 -1
  31. package/dist/lexicons/tools/ozone/moderation/defs.defs.d.ts +35 -21
  32. package/dist/lexicons/tools/ozone/moderation/defs.defs.d.ts.map +1 -1
  33. package/dist/lexicons/tools/ozone/moderation/defs.defs.js +21 -0
  34. package/dist/lexicons/tools/ozone/moderation/defs.defs.js.map +1 -1
  35. package/dist/lexicons/tools/ozone/report/defs.defs.d.ts +200 -120
  36. package/dist/lexicons/tools/ozone/report/defs.defs.d.ts.map +1 -1
  37. package/dist/lexicons/tools/ozone/report/defs.defs.js +126 -0
  38. package/dist/lexicons/tools/ozone/report/defs.defs.js.map +1 -1
  39. package/dist/lexicons/tools/ozone/team/defs.defs.d.ts +20 -12
  40. package/dist/lexicons/tools/ozone/team/defs.defs.d.ts.map +1 -1
  41. package/dist/lexicons/tools/ozone/team/defs.defs.js +12 -0
  42. package/dist/lexicons/tools/ozone/team/defs.defs.js.map +1 -1
  43. package/dist/scripts/index.d.ts +2 -2
  44. package/dist/scripts/index.d.ts.map +1 -1
  45. package/dist/scripts/index.js +2 -2
  46. package/dist/scripts/index.js.map +1 -1
  47. package/dist/telemetry.js +8 -186
  48. package/dist/telemetry.js.map +1 -1
  49. package/example.env +55 -0
  50. package/package.json +19 -28
@@ -1 +1 @@
1
- {"version":3,"file":"defs.defs.js","sourceRoot":"","sources":["../../../../../src/lexicons/tools/ozone/team/defs.defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAChC,OAAO,KAAK,SAAS,MAAM,sCAAsC,CAAA;AAEjE,MAAM,KAAK,GAAG,uBAAuB,CAAA;AAIrC,OAAO,EAAE,KAAK,EAAE,CAAA;AAoBhB,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CACxC,KAAK,EACL,QAAQ;AACR,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrB,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC9C,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAC7D,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IAC/B,aAAa,CAAC,CAAC,CAAC,GAAG,CACjB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,mBAAmB,CAAQ,CAC7C,CACF;IACD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IACjC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAC/C;IACD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IACjC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAC/C;IACD,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,EAOxB;CACL,CAAC,CACH,CAAA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA;AAOjB,oEAAoE;AACpE,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;AAE3D,OAAO,EAAE,SAAS,EAAE,CAAA;AAOpB,gDAAgD;AAChD,MAAM,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,eAAe,CAAC,CAAA;AAEnE,OAAO,EAAE,aAAa,EAAE,CAAA;AAOxB,yEAAyE;AACzE,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAA;AAE7D,OAAO,EAAE,UAAU,EAAE,CAAA;AAOrB,0DAA0D;AAC1D,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,cAAc,CAAC,CAAA;AAEjE,OAAO,EAAE,YAAY,EAAE,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nimport { l } from '@atproto/lex'\nimport * as ActorDefs from '../../../app/bsky/actor/defs.defs.js'\n\nconst $nsid = 'tools.ozone.team.defs'\n\ntype $nsid = typeof $nsid\n\nexport { $nsid }\n\ntype Member = {\n $type?: 'tools.ozone.team.defs#member'\n did: l.DidString\n disabled?: boolean\n profile?: ActorDefs.ProfileViewDetailed\n createdAt?: l.DatetimeString\n updatedAt?: l.DatetimeString\n lastUpdatedBy?: string\n role:\n | 'tools.ozone.team.defs#roleAdmin'\n | 'tools.ozone.team.defs#roleModerator'\n | 'tools.ozone.team.defs#roleTriage'\n | 'tools.ozone.team.defs#roleVerifier'\n | l.UnknownString\n}\n\nexport type { Member }\n\nconst member = /*#__PURE__*/ l.typedObject<Member>(\n $nsid,\n 'member',\n /*#__PURE__*/ l.object({\n did: /*#__PURE__*/ l.string({ format: 'did' }),\n disabled: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.boolean()),\n profile: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.ref<ActorDefs.ProfileViewDetailed>(\n (() => ActorDefs.profileViewDetailed) as any,\n ),\n ),\n createdAt: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.string({ format: 'datetime' }),\n ),\n updatedAt: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.string({ format: 'datetime' }),\n ),\n lastUpdatedBy: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.string()),\n role: /*#__PURE__*/ l.string<{\n knownValues: [\n 'tools.ozone.team.defs#roleAdmin',\n 'tools.ozone.team.defs#roleModerator',\n 'tools.ozone.team.defs#roleTriage',\n 'tools.ozone.team.defs#roleVerifier',\n ]\n }>(),\n }),\n)\n\nexport { member }\n\n/** Admin role. Highest level of access, can perform all actions. */\ntype RoleAdmin = 'tools.ozone.team.defs#roleAdmin'\n\nexport type { RoleAdmin }\n\n/** Admin role. Highest level of access, can perform all actions. */\nconst roleAdmin = /*#__PURE__*/ l.token($nsid, 'roleAdmin')\n\nexport { roleAdmin }\n\n/** Moderator role. Can perform most actions. */\ntype RoleModerator = 'tools.ozone.team.defs#roleModerator'\n\nexport type { RoleModerator }\n\n/** Moderator role. Can perform most actions. */\nconst roleModerator = /*#__PURE__*/ l.token($nsid, 'roleModerator')\n\nexport { roleModerator }\n\n/** Triage role. Mostly intended for monitoring and escalating issues. */\ntype RoleTriage = 'tools.ozone.team.defs#roleTriage'\n\nexport type { RoleTriage }\n\n/** Triage role. Mostly intended for monitoring and escalating issues. */\nconst roleTriage = /*#__PURE__*/ l.token($nsid, 'roleTriage')\n\nexport { roleTriage }\n\n/** Verifier role. Only allowed to issue verifications. */\ntype RoleVerifier = 'tools.ozone.team.defs#roleVerifier'\n\nexport type { RoleVerifier }\n\n/** Verifier role. Only allowed to issue verifications. */\nconst roleVerifier = /*#__PURE__*/ l.token($nsid, 'roleVerifier')\n\nexport { roleVerifier }\n"]}
1
+ {"version":3,"file":"defs.defs.js","sourceRoot":"","sources":["../../../../../src/lexicons/tools/ozone/team/defs.defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAChC,OAAO,KAAK,SAAS,MAAM,sCAAsC,CAAA;AAEjE,MAAM,KAAK,GAAG,uBAAuB,CAAA;AAIrC,OAAO,EAAE,KAAK,EAAE,CAAA;AAoBhB,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CACxC,KAAK,EACL,QAAQ;AACR,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrB,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC9C,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAC7D,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IAC/B,aAAa,CAAC,CAAC,CAAC,GAAG,CACjB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,mBAAmB,CAAQ,CAC7C,CACF;IACD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IACjC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAC/C;IACD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IACjC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAC/C;IACD,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,EAOxB;CACL,CAAC,CACH,CAAA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA;AAEjB,oEAAoE;AACpE,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;AAE3D,OAAO,EAAE,SAAS,EAAE,CAAA;AAEpB,oEAAoE;AACpE,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,KAAK,CAAA;AAK/C,OAAO,EAAE,SAAS,EAAE,CAAA;AAEpB,gDAAgD;AAChD,MAAM,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,eAAe,CAAC,CAAA;AAEnE,OAAO,EAAE,aAAa,EAAE,CAAA;AAExB,gDAAgD;AAChD,MAAM,aAAa,GAAG,aAAa,CAAC,aAAa,CAAC,KAAK,CAAA;AAKvD,OAAO,EAAE,aAAa,EAAE,CAAA;AAExB,yEAAyE;AACzE,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAA;AAE7D,OAAO,EAAE,UAAU,EAAE,CAAA;AAErB,yEAAyE;AACzE,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,CAAC,KAAK,CAAA;AAKjD,OAAO,EAAE,UAAU,EAAE,CAAA;AAErB,0DAA0D;AAC1D,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,cAAc,CAAC,CAAA;AAEjE,OAAO,EAAE,YAAY,EAAE,CAAA;AAEvB,0DAA0D;AAC1D,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC,KAAK,CAAA;AAKrD,OAAO,EAAE,YAAY,EAAE,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nimport { l } from '@atproto/lex'\nimport * as ActorDefs from '../../../app/bsky/actor/defs.defs.js'\n\nconst $nsid = 'tools.ozone.team.defs'\n\ntype $nsid = typeof $nsid\n\nexport { $nsid }\n\ntype Member = {\n $type?: 'tools.ozone.team.defs#member'\n did: l.DidString\n disabled?: boolean\n profile?: ActorDefs.ProfileViewDetailed\n createdAt?: l.DatetimeString\n updatedAt?: l.DatetimeString\n lastUpdatedBy?: string\n role:\n | 'tools.ozone.team.defs#roleAdmin'\n | 'tools.ozone.team.defs#roleModerator'\n | 'tools.ozone.team.defs#roleTriage'\n | 'tools.ozone.team.defs#roleVerifier'\n | l.UnknownString\n}\n\nexport type { Member }\n\nconst member = /*#__PURE__*/ l.typedObject<Member>(\n $nsid,\n 'member',\n /*#__PURE__*/ l.object({\n did: /*#__PURE__*/ l.string({ format: 'did' }),\n disabled: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.boolean()),\n profile: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.ref<ActorDefs.ProfileViewDetailed>(\n (() => ActorDefs.profileViewDetailed) as any,\n ),\n ),\n createdAt: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.string({ format: 'datetime' }),\n ),\n updatedAt: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.string({ format: 'datetime' }),\n ),\n lastUpdatedBy: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.string()),\n role: /*#__PURE__*/ l.string<{\n knownValues: [\n 'tools.ozone.team.defs#roleAdmin',\n 'tools.ozone.team.defs#roleModerator',\n 'tools.ozone.team.defs#roleTriage',\n 'tools.ozone.team.defs#roleVerifier',\n ]\n }>(),\n }),\n)\n\nexport { member }\n\n/** Admin role. Highest level of access, can perform all actions. */\nconst roleAdmin = /*#__PURE__*/ l.token($nsid, 'roleAdmin')\n\nexport { roleAdmin }\n\n/** Admin role. Highest level of access, can perform all actions. */\nconst RoleAdmin = /*#__PURE__*/ roleAdmin.value\n\n/** Admin role. Highest level of access, can perform all actions. */\ntype RoleAdmin = typeof RoleAdmin\n\nexport { RoleAdmin }\n\n/** Moderator role. Can perform most actions. */\nconst roleModerator = /*#__PURE__*/ l.token($nsid, 'roleModerator')\n\nexport { roleModerator }\n\n/** Moderator role. Can perform most actions. */\nconst RoleModerator = /*#__PURE__*/ roleModerator.value\n\n/** Moderator role. Can perform most actions. */\ntype RoleModerator = typeof RoleModerator\n\nexport { RoleModerator }\n\n/** Triage role. Mostly intended for monitoring and escalating issues. */\nconst roleTriage = /*#__PURE__*/ l.token($nsid, 'roleTriage')\n\nexport { roleTriage }\n\n/** Triage role. Mostly intended for monitoring and escalating issues. */\nconst RoleTriage = /*#__PURE__*/ roleTriage.value\n\n/** Triage role. Mostly intended for monitoring and escalating issues. */\ntype RoleTriage = typeof RoleTriage\n\nexport { RoleTriage }\n\n/** Verifier role. Only allowed to issue verifications. */\nconst roleVerifier = /*#__PURE__*/ l.token($nsid, 'roleVerifier')\n\nexport { roleVerifier }\n\n/** Verifier role. Only allowed to issue verifications. */\nconst RoleVerifier = /*#__PURE__*/ roleVerifier.value\n\n/** Verifier role. Only allowed to issue verifications. */\ntype RoleVerifier = typeof RoleVerifier\n\nexport { RoleVerifier }\n"]}
@@ -1,10 +1,10 @@
1
1
  import { publishIdentity, publishIdentityFromFile } from './publish-identity.js';
2
- import { rebuildRepo } from './rebuild-repo.js';
2
+ import { rebuildRepoScript } from './rebuild-repo.js';
3
3
  import { rotateKeys, rotateKeysFromFile, rotateKeysRecovery } from './rotate-keys.js';
4
4
  import { sequencerRecovery } from './sequencer-recovery/index.js';
5
5
  import { repairRepos } from './sequencer-recovery/repair-repos.js';
6
6
  export declare const scripts: {
7
- 'rebuild-repo': typeof rebuildRepo;
7
+ 'rebuild-repo': typeof rebuildRepoScript;
8
8
  'sequencer-recovery': typeof sequencerRecovery;
9
9
  'recovery-repair-repos': typeof repairRepos;
10
10
  'rotate-keys': typeof rotateKeys;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAA;AAElE,eAAO,MAAM,OAAO;IAClB,cAAc;IACd,oBAAoB;IACpB,uBAAuB;IACvB,aAAa;IACb,kBAAkB;IAClB,sBAAsB;IACtB,kBAAkB;IAClB,uBAAuB;CACxB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAA;AAElE,eAAO,MAAM,OAAO;;;;;;;;;CASmD,CAAA"}
@@ -1,10 +1,10 @@
1
1
  import { publishIdentity, publishIdentityFromFile } from './publish-identity.js';
2
- import { rebuildRepo } from './rebuild-repo.js';
2
+ import { rebuildRepoScript } from './rebuild-repo.js';
3
3
  import { rotateKeys, rotateKeysFromFile, rotateKeysRecovery, } from './rotate-keys.js';
4
4
  import { sequencerRecovery } from './sequencer-recovery/index.js';
5
5
  import { repairRepos } from './sequencer-recovery/repair-repos.js';
6
6
  export const scripts = {
7
- 'rebuild-repo': rebuildRepo,
7
+ 'rebuild-repo': rebuildRepoScript,
8
8
  'sequencer-recovery': sequencerRecovery,
9
9
  'recovery-repair-repos': repairRepos,
10
10
  'rotate-keys': rotateKeys,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAA;AAElE,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,cAAc,EAAE,WAAW;IAC3B,oBAAoB,EAAE,iBAAiB;IACvC,uBAAuB,EAAE,WAAW;IACpC,aAAa,EAAE,UAAU;IACzB,kBAAkB,EAAE,kBAAkB;IACtC,sBAAsB,EAAE,kBAAkB;IAC1C,kBAAkB,EAAE,eAAe;IACnC,uBAAuB,EAAE,uBAAuB;CACjD,CAAA","sourcesContent":["import { publishIdentity, publishIdentityFromFile } from './publish-identity.js'\nimport { rebuildRepo } from './rebuild-repo.js'\nimport {\n rotateKeys,\n rotateKeysFromFile,\n rotateKeysRecovery,\n} from './rotate-keys.js'\nimport { sequencerRecovery } from './sequencer-recovery/index.js'\nimport { repairRepos } from './sequencer-recovery/repair-repos.js'\n\nexport const scripts = {\n 'rebuild-repo': rebuildRepo,\n 'sequencer-recovery': sequencerRecovery,\n 'recovery-repair-repos': repairRepos,\n 'rotate-keys': rotateKeys,\n 'rotate-keys-file': rotateKeysFromFile,\n 'rotate-keys-recovery': rotateKeysRecovery,\n 'publish-identity': publishIdentity,\n 'publish-identity-file': publishIdentityFromFile,\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAA;AAElE,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,cAAc,EAAE,iBAAiB;IACjC,oBAAoB,EAAE,iBAAiB;IACvC,uBAAuB,EAAE,WAAW;IACpC,aAAa,EAAE,UAAU;IACzB,kBAAkB,EAAE,kBAAkB;IACtC,sBAAsB,EAAE,kBAAkB;IAC1C,kBAAkB,EAAE,eAAe;IACnC,uBAAuB,EAAE,uBAAuB;CACqB,CAAA","sourcesContent":["import { publishIdentity, publishIdentityFromFile } from './publish-identity.js'\nimport { rebuildRepoScript } from './rebuild-repo.js'\nimport {\n rotateKeys,\n rotateKeysFromFile,\n rotateKeysRecovery,\n} from './rotate-keys.js'\nimport { sequencerRecovery } from './sequencer-recovery/index.js'\nimport { repairRepos } from './sequencer-recovery/repair-repos.js'\n\nexport const scripts = {\n 'rebuild-repo': rebuildRepoScript,\n 'sequencer-recovery': sequencerRecovery,\n 'recovery-repair-repos': repairRepos,\n 'rotate-keys': rotateKeys,\n 'rotate-keys-file': rotateKeysFromFile,\n 'rotate-keys-recovery': rotateKeysRecovery,\n 'publish-identity': publishIdentity,\n 'publish-identity-file': publishIdentityFromFile,\n} satisfies Record<string, (ctx: any, args: string[]) => Promise<void>>\n"]}
package/dist/telemetry.js CHANGED
@@ -1,193 +1,15 @@
1
- import { register } from 'node:module';
2
- import { diag } from '@opentelemetry/api';
3
- import { getResourceDetectors } from '@opentelemetry/auto-instrumentations-node';
4
- import { getBooleanFromEnv, getStringFromEnv, getStringListFromEnv, } from '@opentelemetry/core';
5
1
  import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk';
6
- import { ExpressInstrumentation, ExpressLayerType, } from '@opentelemetry/instrumentation-express';
7
- import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
8
2
  import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
9
- import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
10
- import { RuntimeNodeInstrumentation } from '@opentelemetry/instrumentation-runtime-node';
11
- import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
12
- import { NodeSDK } from '@opentelemetry/sdk-node';
13
- import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';
14
3
  import { BetterSqlite3Instrumentation } from 'opentelemetry-plugin-better-sqlite3';
15
- const ATTR_XRPC_METHOD = 'xrpc.method';
16
- const ATTR_XRPC_PROXIED = 'xrpc.proxied';
17
- const ATTR_XRPC_PROXY = 'xrpc.proxy';
18
- // @NOTE Hand-rolled equivalent of "@opentelemetry/auto-instrumentations-node"'s
19
- // register script, because that one lacks better-sqlite3 instrumentation and
20
- // doesn't let us customize the HTTP span naming for XRPC. We use `NodeSDK`
21
- // rather than `registerInstrumentations` to get exporter setup from the
22
- // conventional OTEL_* env vars for free.
23
- // @NOTE Telemetry is opt-in via exporter endpoint rather than a non-standard
24
- // flag: configuring an OTLP endpoint is what enables the SDK. OTEL_SDK_DISABLED
25
- // remains a kill switch, per spec.
26
- const otelDisabled = getBooleanFromEnv('OTEL_SDK_DISABLED');
27
- const tracesConfigured = isSignalConfigured('TRACES');
28
- const metricsConfigured = isSignalConfigured('METRICS');
29
- const logsConfigured = isSignalConfigured('LOGS');
30
- const otelConfigured = tracesConfigured || metricsConfigured || logsConfigured;
31
- const otelEnabled = !otelDisabled && otelConfigured;
32
- if (otelEnabled) {
33
- try {
34
- register('@opentelemetry/instrumentation/hook.mjs', import.meta.url);
35
- const sdk = new NodeSDK({
36
- // @NOTE Unlike sdk-node's default detectors, these include the
37
- // "container" detector.
38
- resourceDetectors: getResourceDetectors(),
39
- instrumentations: getInstrumentations(),
40
- // @NOTE The gate above enables the SDK as soon as *any* signal is
41
- // configured, but NodeSDK then defaults every unspecified
42
- // OTEL_{SIGNAL}_EXPORTER to "otlp" on http://localhost:4318 — shipping
43
- // signals the operator never opted into. An *empty* pipeline opts a
44
- // signal out: NodeSDK only reads OTEL_{SIGNAL}_EXPORTER when the pipeline
45
- // option is `undefined`, and registers no provider when handed an empty
46
- // one. Opted-in signals get `undefined` so they stay env-configured.
47
- spanProcessors: tracesConfigured ? undefined : [],
48
- metricReaders: metricsConfigured ? undefined : [],
49
- logRecordProcessors: logsConfigured ? undefined : [],
50
- });
51
- sdk.start();
52
- // @NOTE On SIGINT/SIGTERM the PDS releases its resources without calling
53
- // process.exit(), so the event loop empties and Node emits "beforeExit"
54
- // (https://nodejs.org/api/process.html#event-beforeexit). That's our window
55
- // to flush pending telemetry.
56
- process.once('beforeExit', () => {
57
- sdk.shutdown().catch((err) => {
58
- diag.error('Error terminating OpenTelemetry SDK', err);
59
- });
60
- });
61
- }
62
- catch (err) {
63
- diag.error('Error initializing OpenTelemetry SDK', err);
64
- }
65
- }
66
- function getInstrumentations() {
67
- return [
68
- // @NOTE Not using getNodeAutoInstrumentations: it pulls in many
69
- // instrumentations we don't need, with no easy way to filter them out.
70
- new RuntimeNodeInstrumentation(),
71
- new HttpInstrumentation({
72
- // Derives "http.route" (and the span name) from the normalized XRPC path,
73
- // for both incoming and outgoing requests.
74
- //
75
- // @NOTE Must be applyCustomAttributesOnSpan (fires on response finish),
76
- // not requestHook (fires on request start). The express instrumentation
77
- // overwrites the shared rpcMetadata.route on every layer it enters, which
78
- // in this app resolves to "/" more often than not (multiple express apps
79
- // and routers mounted at "/", plus catchall middlewares with no route
80
- // layer). On finish, the http instrumentation copies rpcMetadata.route
81
- // into "http.route" and renames the span from it, clobbering anything a
82
- // requestHook set. This hook runs after that, so it wins.
83
- applyCustomAttributesOnSpan: (span, request) => {
84
- const { url, method, proxy } = 'path' in request
85
- ? // ClientRequest
86
- {
87
- url: request.path,
88
- method: request.method,
89
- proxy: request.getHeader('atproto-proxy'),
90
- }
91
- : // IncomingMessage
92
- {
93
- url: request.url ?? '/',
94
- method: request.method ?? 'GET',
95
- proxy: request.headers['atproto-proxy'],
96
- };
97
- const lxm = method === 'GET' || method === 'POST'
98
- ? extractNormalizedLxm(url)
99
- : undefined;
100
- // Normalized route for XRPC, raw path otherwise
101
- const route = lxm ? `/xrpc/${lxm}` : url.split('?')[0];
102
- span.setAttribute(ATTR_HTTP_ROUTE, route);
103
- if (lxm) {
104
- span.updateName(`${method} /xrpc/${lxm}`);
105
- span.setAttribute(ATTR_XRPC_METHOD, lxm);
106
- span.setAttribute(ATTR_XRPC_PROXIED, !!proxy);
107
- if (proxy) {
108
- span.setAttribute(ATTR_XRPC_PROXY, proxy);
109
- }
110
- }
111
- },
112
- }),
113
- new ExpressInstrumentation({
114
- ignoreLayersType: [ExpressLayerType.MIDDLEWARE],
115
- }),
116
- new UndiciInstrumentation({
117
- requestHook: (span, request) => {
118
- const lxm = extractNormalizedLxm(request.path);
119
- if (lxm) {
120
- span.setAttribute(ATTR_XRPC_METHOD, lxm);
121
- }
122
- },
123
- }),
4
+ import pkg from '@atproto/pds/package.json' with { type: 'json' };
5
+ import { setup } from '@atproto-labs/opentelemetry-node';
6
+ setup(() => ({
7
+ name: pkg.name,
8
+ version: pkg.version,
9
+ instrumentations: [
124
10
  new AwsInstrumentation(),
125
11
  new IORedisInstrumentation(),
126
12
  new BetterSqlite3Instrumentation(),
127
- // @NOTE Keep log correlation (trace_id/span_id injected into pino records)
128
- // but disable log sending: it JSON.parse()s every record on the main thread
129
- // and would forward all subsystems indiscriminately. Events we actually want
130
- // in the OTEL stack go through the Logs API explicitly (see ./events.ts).
131
- new PinoInstrumentation({ disableLogSending: true }),
132
- ];
133
- }
134
- /**
135
- * A signal counts as configured when an OTLP endpoint applies to it
136
- * (signal-specific or generic) and its exporter isn't set to "none".
137
- *
138
- * @NOTE OTEL_{SIGNAL}_EXPORTER is a comma-separated list, and the SDK handles
139
- * "none" alongside other exporters inconsistently (metrics & logs disable the
140
- * signal, traces falls back to "otlp"). Such a combination is a
141
- * misconfiguration, so we treat any list containing "none" as disabling.
142
- */
143
- function isSignalConfigured(signal) {
144
- if (getStringListFromEnv(`OTEL_${signal}_EXPORTER`)?.includes('none')) {
145
- return false;
146
- }
147
- return (!!getStringFromEnv(`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`) ||
148
- !!getStringFromEnv('OTEL_EXPORTER_OTLP_ENDPOINT'));
149
- }
150
- // @NOTE Hand-rolled (rather than using URL/split) because this runs on every
151
- // instrumented request. Should become obsolete once we have dedicated
152
- // XrpcClient/XrpcServer instrumentations.
153
- function extractNormalizedLxm(url) {
154
- if (typeof url !== 'string') {
155
- return undefined;
156
- }
157
- // 9 = "/xrpc/".length + shortest conceivable NSID ("a.b")
158
- if (url.length < 9 || !url.startsWith('/xrpc/')) {
159
- return undefined;
160
- }
161
- const firstMethodCharPos = 6; // "/xrpc/".length
162
- // Characters that can never open an NSID (note "_" skips "/xrpc/_health")
163
- const nextChar = url.charCodeAt(firstMethodCharPos);
164
- if (nextChar === 0x2e /* '.' */ ||
165
- nextChar === 0x2f /* '/' */ ||
166
- nextChar === 0x3f /* '?' */ ||
167
- nextChar === 0x5f /* '_' */) {
168
- return undefined;
169
- }
170
- const queryIndex = url.indexOf('?', firstMethodCharPos + 1);
171
- let lastMethodCharPos = queryIndex === -1 ? url.length - 1 : queryIndex - 1;
172
- // Ignore the trailing slash, if there is one
173
- if (url.charCodeAt(lastMethodCharPos) === 0x2f /* '/' */) {
174
- lastMethodCharPos--;
175
- }
176
- if (lastMethodCharPos < 9) {
177
- return undefined;
178
- }
179
- // Make sure there is no other slash in the path
180
- if (url.lastIndexOf('/', lastMethodCharPos) !== firstMethodCharPos - 1) {
181
- return undefined;
182
- }
183
- // Require at least one dot, and not as the last character
184
- const lastDotPos = url.lastIndexOf('.', lastMethodCharPos);
185
- if (lastDotPos === -1 || lastDotPos === lastMethodCharPos) {
186
- return undefined;
187
- }
188
- // @NOTE Only the domain authority is case-insensitive; the trailing name
189
- // segment is not, so it must be preserved as-is to avoid conflating
190
- // distinct NSIDs.
191
- return `${url.substring(firstMethodCharPos, lastDotPos).toLowerCase()}${url.substring(lastDotPos, lastMethodCharPos + 1)}`;
192
- }
13
+ ],
14
+ }));
193
15
  //# sourceMappingURL=telemetry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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","sourcesContent":["import { register } from 'node:module'\nimport { diag } from '@opentelemetry/api'\nimport { getResourceDetectors } from '@opentelemetry/auto-instrumentations-node'\nimport {\n getBooleanFromEnv,\n getStringFromEnv,\n getStringListFromEnv,\n} from '@opentelemetry/core'\nimport type { Instrumentation } from '@opentelemetry/instrumentation'\nimport { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'\nimport {\n ExpressInstrumentation,\n ExpressLayerType,\n} from '@opentelemetry/instrumentation-express'\nimport { HttpInstrumentation } from '@opentelemetry/instrumentation-http'\nimport { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis'\nimport { PinoInstrumentation } from '@opentelemetry/instrumentation-pino'\nimport { RuntimeNodeInstrumentation } from '@opentelemetry/instrumentation-runtime-node'\nimport { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici'\nimport { NodeSDK } from '@opentelemetry/sdk-node'\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'\nimport { BetterSqlite3Instrumentation } from 'opentelemetry-plugin-better-sqlite3'\n\nconst ATTR_XRPC_METHOD = 'xrpc.method'\nconst ATTR_XRPC_PROXIED = 'xrpc.proxied'\nconst ATTR_XRPC_PROXY = 'xrpc.proxy'\n\n// @NOTE Hand-rolled equivalent of \"@opentelemetry/auto-instrumentations-node\"'s\n// register script, because that one lacks better-sqlite3 instrumentation and\n// doesn't let us customize the HTTP span naming for XRPC. We use `NodeSDK`\n// rather than `registerInstrumentations` to get exporter setup from the\n// conventional OTEL_* env vars for free.\n\n// @NOTE Telemetry is opt-in via exporter endpoint rather than a non-standard\n// flag: configuring an OTLP endpoint is what enables the SDK. OTEL_SDK_DISABLED\n// remains a kill switch, per spec.\nconst otelDisabled = getBooleanFromEnv('OTEL_SDK_DISABLED')\nconst tracesConfigured = isSignalConfigured('TRACES')\nconst metricsConfigured = isSignalConfigured('METRICS')\nconst logsConfigured = isSignalConfigured('LOGS')\nconst otelConfigured = tracesConfigured || metricsConfigured || logsConfigured\nconst otelEnabled = !otelDisabled && otelConfigured\n\nif (otelEnabled) {\n try {\n register('@opentelemetry/instrumentation/hook.mjs', import.meta.url)\n\n const sdk = new NodeSDK({\n // @NOTE Unlike sdk-node's default detectors, these include the\n // \"container\" detector.\n resourceDetectors: getResourceDetectors(),\n instrumentations: getInstrumentations(),\n\n // @NOTE The gate above enables the SDK as soon as *any* signal is\n // configured, but NodeSDK then defaults every unspecified\n // OTEL_{SIGNAL}_EXPORTER to \"otlp\" on http://localhost:4318 — shipping\n // signals the operator never opted into. An *empty* pipeline opts a\n // signal out: NodeSDK only reads OTEL_{SIGNAL}_EXPORTER when the pipeline\n // option is `undefined`, and registers no provider when handed an empty\n // one. Opted-in signals get `undefined` so they stay env-configured.\n spanProcessors: tracesConfigured ? undefined : [],\n metricReaders: metricsConfigured ? undefined : [],\n logRecordProcessors: logsConfigured ? undefined : [],\n })\n\n sdk.start()\n\n // @NOTE On SIGINT/SIGTERM the PDS releases its resources without calling\n // process.exit(), so the event loop empties and Node emits \"beforeExit\"\n // (https://nodejs.org/api/process.html#event-beforeexit). That's our window\n // to flush pending telemetry.\n process.once('beforeExit', () => {\n sdk.shutdown().catch((err) => {\n diag.error('Error terminating OpenTelemetry SDK', err)\n })\n })\n } catch (err) {\n diag.error('Error initializing OpenTelemetry SDK', err)\n }\n}\n\nfunction getInstrumentations(): Instrumentation[] {\n return [\n // @NOTE Not using getNodeAutoInstrumentations: it pulls in many\n // instrumentations we don't need, with no easy way to filter them out.\n new RuntimeNodeInstrumentation(),\n new HttpInstrumentation({\n // Derives \"http.route\" (and the span name) from the normalized XRPC path,\n // for both incoming and outgoing requests.\n //\n // @NOTE Must be applyCustomAttributesOnSpan (fires on response finish),\n // not requestHook (fires on request start). The express instrumentation\n // overwrites the shared rpcMetadata.route on every layer it enters, which\n // in this app resolves to \"/\" more often than not (multiple express apps\n // and routers mounted at \"/\", plus catchall middlewares with no route\n // layer). On finish, the http instrumentation copies rpcMetadata.route\n // into \"http.route\" and renames the span from it, clobbering anything a\n // requestHook set. This hook runs after that, so it wins.\n applyCustomAttributesOnSpan: (span, request) => {\n const { url, method, proxy } =\n 'path' in request\n ? // ClientRequest\n {\n url: request.path,\n method: request.method,\n proxy: request.getHeader('atproto-proxy'),\n }\n : // IncomingMessage\n {\n url: request.url ?? '/',\n method: request.method ?? 'GET',\n proxy: request.headers['atproto-proxy'],\n }\n\n const lxm =\n method === 'GET' || method === 'POST'\n ? extractNormalizedLxm(url)\n : undefined\n\n // Normalized route for XRPC, raw path otherwise\n const route = lxm ? `/xrpc/${lxm}` : url.split('?')[0]\n span.setAttribute(ATTR_HTTP_ROUTE, route)\n\n if (lxm) {\n span.updateName(`${method} /xrpc/${lxm}`)\n span.setAttribute(ATTR_XRPC_METHOD, lxm)\n span.setAttribute(ATTR_XRPC_PROXIED, !!proxy)\n\n if (proxy) {\n span.setAttribute(ATTR_XRPC_PROXY, proxy)\n }\n }\n },\n }),\n new ExpressInstrumentation({\n ignoreLayersType: [ExpressLayerType.MIDDLEWARE],\n }),\n new UndiciInstrumentation({\n requestHook: (span, request) => {\n const lxm = extractNormalizedLxm(request.path)\n if (lxm) {\n span.setAttribute(ATTR_XRPC_METHOD, lxm)\n }\n },\n }),\n new AwsInstrumentation(),\n new IORedisInstrumentation(),\n new BetterSqlite3Instrumentation(),\n // @NOTE Keep log correlation (trace_id/span_id injected into pino records)\n // but disable log sending: it JSON.parse()s every record on the main thread\n // and would forward all subsystems indiscriminately. Events we actually want\n // in the OTEL stack go through the Logs API explicitly (see ./events.ts).\n new PinoInstrumentation({ disableLogSending: true }),\n ]\n}\n\n/**\n * A signal counts as configured when an OTLP endpoint applies to it\n * (signal-specific or generic) and its exporter isn't set to \"none\".\n *\n * @NOTE OTEL_{SIGNAL}_EXPORTER is a comma-separated list, and the SDK handles\n * \"none\" alongside other exporters inconsistently (metrics & logs disable the\n * signal, traces falls back to \"otlp\"). Such a combination is a\n * misconfiguration, so we treat any list containing \"none\" as disabling.\n */\nfunction isSignalConfigured(signal: 'TRACES' | 'METRICS' | 'LOGS'): boolean {\n if (getStringListFromEnv(`OTEL_${signal}_EXPORTER`)?.includes('none')) {\n return false\n }\n\n return (\n !!getStringFromEnv(`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`) ||\n !!getStringFromEnv('OTEL_EXPORTER_OTLP_ENDPOINT')\n )\n}\n\n// @NOTE Hand-rolled (rather than using URL/split) because this runs on every\n// instrumented request. Should become obsolete once we have dedicated\n// XrpcClient/XrpcServer instrumentations.\nfunction extractNormalizedLxm(url: unknown): string | undefined {\n if (typeof url !== 'string') {\n return undefined\n }\n\n // 9 = \"/xrpc/\".length + shortest conceivable NSID (\"a.b\")\n if (url.length < 9 || !url.startsWith('/xrpc/')) {\n return undefined\n }\n\n const firstMethodCharPos = 6 // \"/xrpc/\".length\n\n // Characters that can never open an NSID (note \"_\" skips \"/xrpc/_health\")\n const nextChar = url.charCodeAt(firstMethodCharPos)\n if (\n nextChar === 0x2e /* '.' */ ||\n nextChar === 0x2f /* '/' */ ||\n nextChar === 0x3f /* '?' */ ||\n nextChar === 0x5f /* '_' */\n ) {\n return undefined\n }\n\n const queryIndex = url.indexOf('?', firstMethodCharPos + 1)\n\n let lastMethodCharPos = queryIndex === -1 ? url.length - 1 : queryIndex - 1\n\n // Ignore the trailing slash, if there is one\n if (url.charCodeAt(lastMethodCharPos) === 0x2f /* '/' */) {\n lastMethodCharPos--\n }\n\n if (lastMethodCharPos < 9) {\n return undefined\n }\n\n // Make sure there is no other slash in the path\n if (url.lastIndexOf('/', lastMethodCharPos) !== firstMethodCharPos - 1) {\n return undefined\n }\n\n // Require at least one dot, and not as the last character\n const lastDotPos = url.lastIndexOf('.', lastMethodCharPos)\n if (lastDotPos === -1 || lastDotPos === lastMethodCharPos) {\n return undefined\n }\n\n // @NOTE Only the domain authority is case-insensitive; the trailing name\n // segment is not, so it must be preserved as-is to avoid conflating\n // distinct NSIDs.\n return `${url.substring(firstMethodCharPos, lastDotPos).toLowerCase()}${url.substring(lastDotPos, lastMethodCharPos + 1)}`\n}\n"]}
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAA;AAC/E,OAAO,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAA;AAClF,OAAO,GAAG,MAAM,2BAA2B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAA;AAExD,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,OAAO,EAAE,GAAG,CAAC,OAAO;IACpB,gBAAgB,EAAE;QAChB,IAAI,kBAAkB,EAAE;QACxB,IAAI,sBAAsB,EAAE;QAC5B,IAAI,4BAA4B,EAAE;KACnC;CACF,CAAC,CAAC,CAAA","sourcesContent":["import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'\nimport { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis'\nimport { BetterSqlite3Instrumentation } from 'opentelemetry-plugin-better-sqlite3'\nimport pkg from '@atproto/pds/package.json' with { type: 'json' }\nimport { setup } from '@atproto-labs/opentelemetry-node'\n\nsetup(() => ({\n name: pkg.name,\n version: pkg.version,\n instrumentations: [\n new AwsInstrumentation(),\n new IORedisInstrumentation(),\n new BetterSqlite3Instrumentation(),\n ],\n}))\n"]}
package/example.env ADDED
@@ -0,0 +1,55 @@
1
+ # See more env options in src/config/env.ts
2
+ # Hostname - the public domain that you intend to deploy your service at
3
+ PDS_HOSTNAME="example.com"
4
+ PDS_PORT="2583"
5
+
6
+ # Database config - use one or the other
7
+ PDS_DATA_DIRECTORY="data"
8
+
9
+ # Blobstore - filesystem location to store uploaded blobs
10
+ PDS_BLOBSTORE_DISK_LOCATION="blobs"
11
+
12
+ # Private keys - these are each expected to be 64 char hex strings (256 bit)
13
+ PDS_REPO_SIGNING_KEY_K256_PRIVATE_KEY_HEX="3ee68..."
14
+ PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="e049f..."
15
+
16
+ # Secrets - update to secure high-entropy strings
17
+ PDS_DPOP_SECRET="32-random-bytes-hex-encoded"
18
+ PDS_JWT_SECRET="jwt-secret"
19
+ PDS_ADMIN_PASSWORD="admin-pass"
20
+
21
+ # Environment - example is for live network
22
+ PDS_DID_PLC_URL="https://plc.directory"
23
+ PDS_BSKY_APP_VIEW_URL="https://api.bsky.app"
24
+ PDS_BSKY_APP_VIEW_DID="did:web:api.bsky.app"
25
+ PDS_CRAWLERS="https://bsky.network"
26
+
27
+ # OAuth Provider - branding for the login / consent screens
28
+ PDS_SERVICE_NAME="John's self hosted PDS"
29
+ PDS_LOGO_URL=
30
+ PDS_PRIMARY_COLOR="#7507e3"
31
+ PDS_ERROR_COLOR=
32
+ PDS_HOME_URL=
33
+ PDS_TERMS_OF_SERVICE_URL=
34
+ PDS_PRIVACY_POLICY_URL=
35
+ PDS_SUPPORT_URL=
36
+ # Auth screen background images, shown behind the login card, by colour scheme
37
+ PDS_BACKGROUND_LIGHT_URL=
38
+ PDS_BACKGROUND_DARK_URL=
39
+
40
+ # Debugging
41
+ NODE_TLS_REJECT_UNAUTHORIZED=1
42
+ LOG_ENABLED=0
43
+ LOG_LEVEL=info
44
+ PDS_INVITE_REQUIRED=1
45
+ PDS_DISABLE_SSRF_PROTECTION=0
46
+
47
+ # Telemetry (optional) - requires running with `node --import @atproto/pds/telemetry`.
48
+ # See:
49
+ # - https://opentelemetry.io/docs/languages/sdk-configuration/general/
50
+ # - https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/
51
+ #OTEL_SERVICE_NAME="pds"
52
+ #OTEL_NODE_RESOURCE_DETECTORS="container,env,host"
53
+ #OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.example.com"
54
+ #OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
55
+ #OTEL_SEMCONV_STABILITY_OPT_IN="http"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/pds",
3
- "version": "0.5.28",
3
+ "version": "0.5.30",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of atproto Personal Data Server (PDS)",
6
6
  "keywords": [
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "files": [
17
17
  "./dist",
18
+ "example.env",
18
19
  "./README.md",
19
20
  "./CHANGELOG.md"
20
21
  ],
@@ -36,21 +37,11 @@
36
37
  "dependencies": {
37
38
  "@did-plc/lib": "^0.0.4",
38
39
  "@hapi/address": "^5.1.1",
40
+ "@noble/curves": "^1.7.0",
39
41
  "@opentelemetry/api": "^1.9.1",
40
42
  "@opentelemetry/api-logs": "^0.219.0",
41
- "@opentelemetry/auto-instrumentations-node": "^0.77.0",
42
- "@opentelemetry/core": "^2.9.0",
43
- "@opentelemetry/instrumentation": "^0.219.0",
44
43
  "@opentelemetry/instrumentation-aws-sdk": "^0.74.0",
45
- "@opentelemetry/instrumentation-express": "^0.67.0",
46
- "@opentelemetry/instrumentation-http": "^0.219.0",
47
44
  "@opentelemetry/instrumentation-ioredis": "^0.67.0",
48
- "@opentelemetry/instrumentation-pino": "^0.65.0",
49
- "@opentelemetry/instrumentation-runtime-node": "^0.32.0",
50
- "@opentelemetry/instrumentation-undici": "^0.29.0",
51
- "@opentelemetry/resource-detector-container": "^0.8.10",
52
- "@opentelemetry/sdk-node": "^0.219.0",
53
- "@opentelemetry/semantic-conventions": "^1.41.1",
54
45
  "better-sqlite3": "^12.0.0",
55
46
  "compression": "^1.7.4",
56
47
  "cors": "^2.8.5",
@@ -62,7 +53,6 @@
62
53
  "http-terminator": "^3.2.0",
63
54
  "ioredis": "^5.3.2",
64
55
  "jose": "^5.0.1",
65
- "key-encoder": "^2.0.3",
66
56
  "kysely": "^0.29.2",
67
57
  "nodemailer": "^6.8.0",
68
58
  "nodemailer-html-to-text": "^3.2.0",
@@ -75,24 +65,25 @@
75
65
  "undici": "^8.5.0",
76
66
  "zod": "^3.23.8",
77
67
  "@atproto-labs/fetch-node": "^0.3.7",
68
+ "@atproto-labs/opentelemetry-node": "^0.1.0",
69
+ "@atproto-labs/simple-store": "^0.5.1",
78
70
  "@atproto-labs/simple-store-redis": "^0.1.6",
79
- "@atproto-labs/xrpc-utils": "^0.1.17",
80
- "@atproto/aws": "^0.3.13",
71
+ "@atproto-labs/simple-store-memory": "^0.2.6",
72
+ "@atproto-labs/xrpc-utils": "^0.1.19",
73
+ "@atproto/aws": "^0.3.15",
74
+ "@atproto/common": "^0.8.1",
81
75
  "@atproto/crypto": "^0.5.4",
82
- "@atproto/common": "^0.8.0",
83
76
  "@atproto/did": "^0.5.4",
84
- "@atproto/lex": "^0.3.6",
77
+ "@atproto/identity": "^0.5.10",
78
+ "@atproto/lex": "^0.3.7",
79
+ "@atproto/lex-data": "^0.1.7",
85
80
  "@atproto/lex-cbor": "^0.1.6",
86
- "@atproto-labs/simple-store": "^0.5.1",
87
- "@atproto/identity": "^0.5.9",
88
- "@atproto-labs/simple-store-memory": "^0.2.6",
89
- "@atproto/repo": "^0.10.11",
90
81
  "@atproto/lex-json": "^0.1.6",
91
- "@atproto/lex-data": "^0.1.7",
92
- "@atproto/oauth-scopes": "^0.5.9",
93
- "@atproto/syntax": "^0.7.4",
94
- "@atproto/xrpc-server": "^0.12.4",
95
- "@atproto/oauth-provider": "^0.22.2"
82
+ "@atproto/oauth-provider": "^0.22.4",
83
+ "@atproto/oauth-scopes": "^0.5.10",
84
+ "@atproto/xrpc-server": "^0.12.6",
85
+ "@atproto/syntax": "^0.7.5",
86
+ "@atproto/repo": "^0.10.12"
96
87
  },
97
88
  "devDependencies": {
98
89
  "@did-plc/server": "^0.0.1",
@@ -107,8 +98,8 @@
107
98
  "jest": "^30.0.0",
108
99
  "puppeteer": "^23.11.1",
109
100
  "ws": "^8.12.0",
110
- "@atproto/api": "^0.20.40",
111
- "@atproto/lex-document": "^0.1.9",
101
+ "@atproto/api": "^0.20.42",
102
+ "@atproto/lex-document": "^0.1.10",
112
103
  "@atproto/oauth-client-browser-example": "^0.1.7"
113
104
  },
114
105
  "scripts": {