@atproto/bsky 0.0.36 → 0.0.37

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @atproto/bsky
2
2
 
3
+ ## 0.0.37
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2279](https://github.com/bluesky-social/atproto/pull/2279) [`192223f12`](https://github.com/bluesky-social/atproto/commit/192223f127c0b226287df1ecfcd953636db08655) Thanks [@gaearon](https://github.com/gaearon)! - Change Following feed prefs to only show replies from people you follow by default
8
+
9
+ - Updated dependencies [[`192223f12`](https://github.com/bluesky-social/atproto/commit/192223f127c0b226287df1ecfcd953636db08655)]:
10
+ - @atproto/api@0.10.5
11
+
3
12
  ## 0.0.36
4
13
 
5
14
  ### Patch Changes
@@ -10,7 +10,7 @@ export declare enum RoleStatus {
10
10
  }
11
11
  type NullOutput = {
12
12
  credentials: {
13
- type: 'null';
13
+ type: 'none';
14
14
  iss: null;
15
15
  };
16
16
  };
@@ -27,22 +27,22 @@ type RoleOutput = {
27
27
  admin: boolean;
28
28
  };
29
29
  };
30
- type AdminServiceOutput = {
30
+ type ModServiceOutput = {
31
31
  credentials: {
32
- type: 'admin_service';
32
+ type: 'mod_service';
33
33
  aud: string;
34
34
  iss: string;
35
35
  };
36
36
  };
37
37
  export type AuthVerifierOpts = {
38
38
  ownDid: string;
39
- adminDid: string;
39
+ modServiceDid: string;
40
40
  adminPasses: string[];
41
41
  };
42
42
  export declare class AuthVerifier {
43
43
  dataplane: DataPlaneClient;
44
44
  ownDid: string;
45
- adminDid: string;
45
+ modServiceDid: string;
46
46
  private adminPasses;
47
47
  constructor(dataplane: DataPlaneClient, opts: AuthVerifierOpts);
48
48
  standard: (ctx: ReqCtx) => Promise<StandardOutput>;
@@ -51,8 +51,8 @@ export declare class AuthVerifier {
51
51
  role: (ctx: ReqCtx) => RoleOutput;
52
52
  standardOrRole: (ctx: ReqCtx) => Promise<StandardOutput | RoleOutput>;
53
53
  optionalStandardOrRole: (ctx: ReqCtx) => Promise<StandardOutput | RoleOutput | NullOutput>;
54
- adminService: (reqCtx: ReqCtx) => Promise<AdminServiceOutput>;
55
- roleOrAdminService: (reqCtx: ReqCtx) => Promise<RoleOutput | AdminServiceOutput>;
54
+ modService: (reqCtx: ReqCtx) => Promise<ModServiceOutput>;
55
+ roleOrModService: (reqCtx: ReqCtx) => Promise<RoleOutput | ModServiceOutput>;
56
56
  parseRoleCreds(req: express.Request): {
57
57
  status: RoleStatus;
58
58
  admin: boolean;
@@ -71,8 +71,9 @@ export declare class AuthVerifier {
71
71
  iss: string;
72
72
  aud: string;
73
73
  }>;
74
+ isModService(iss: string): boolean;
74
75
  nullCreds(): NullOutput;
75
- parseCreds(creds: StandardOutput | RoleOutput | AdminServiceOutput | NullOutput): {
76
+ parseCreds(creds: StandardOutput | RoleOutput | ModServiceOutput | NullOutput): {
76
77
  viewer: string | null;
77
78
  canViewTakedowns: boolean;
78
79
  canPerformTakedown: boolean;
package/dist/config.d.ts CHANGED
@@ -17,6 +17,8 @@ export interface ServerConfigValues {
17
17
  courierIgnoreBadTls?: boolean;
18
18
  searchUrl?: string;
19
19
  cdnUrl?: string;
20
+ blobRateLimitBypassKey?: string;
21
+ blobRateLimitBypassHostname?: string;
20
22
  didPlcUrl: string;
21
23
  handleResolveNameservers?: string[];
22
24
  modServiceDid: string;
@@ -49,6 +51,8 @@ export declare class ServerConfig {
49
51
  get courierIgnoreBadTls(): boolean | undefined;
50
52
  get searchUrl(): string | undefined;
51
53
  get cdnUrl(): string | undefined;
54
+ get blobRateLimitBypassKey(): string | undefined;
55
+ get blobRateLimitBypassHostname(): string | undefined;
52
56
  get didPlcUrl(): string;
53
57
  get handleResolveNameservers(): string[] | undefined;
54
58
  get adminPasswords(): string[];
package/dist/index.js CHANGED
@@ -132036,6 +132036,9 @@ var getHandle = (doc) => {
132036
132036
  return found.slice(5);
132037
132037
  };
132038
132038
  var getSigningKey = (doc) => {
132039
+ return getVerificationMaterial(doc, "atproto");
132040
+ };
132041
+ var getVerificationMaterial = (doc, keyId) => {
132039
132042
  const did2 = getDid(doc);
132040
132043
  let keys = doc.verificationMethod;
132041
132044
  if (!keys)
@@ -132045,7 +132048,7 @@ var getSigningKey = (doc) => {
132045
132048
  if (!Array.isArray(keys)) {
132046
132049
  keys = [keys];
132047
132050
  }
132048
- const found = keys.find((key) => key.id === "#atproto" || key.id === `${did2}#atproto`);
132051
+ const found = keys.find((key) => key.id === `#${keyId}` || key.id === `${did2}#${keyId}`);
132049
132052
  if (!found?.publicKeyMultibase)
132050
132053
  return void 0;
132051
132054
  return {
@@ -134406,6 +134409,10 @@ var schemaDict = {
134406
134409
  type: "string",
134407
134410
  description: "The subject line of the email sent to the user."
134408
134411
  },
134412
+ content: {
134413
+ type: "string",
134414
+ description: "The content of the email sent to the user."
134415
+ },
134409
134416
  comment: {
134410
134417
  type: "string",
134411
134418
  description: "Additional comment about the outgoing comm."
@@ -138553,7 +138560,8 @@ var schemaDict = {
138553
138560
  },
138554
138561
  hideRepliesByUnfollowed: {
138555
138562
  type: "boolean",
138556
- description: "Hide replies in the feed if they are not by followed users."
138563
+ description: "Hide replies in the feed if they are not by followed users.",
138564
+ default: true
138557
138565
  },
138558
138566
  hideRepliesByLikeCount: {
138559
138567
  type: "integer",
@@ -148834,6 +148842,10 @@ var schemaDict2 = {
148834
148842
  type: "string",
148835
148843
  description: "The subject line of the email sent to the user."
148836
148844
  },
148845
+ content: {
148846
+ type: "string",
148847
+ description: "The content of the email sent to the user."
148848
+ },
148837
148849
  comment: {
148838
148850
  type: "string",
148839
148851
  description: "Additional comment about the outgoing comm."
@@ -152981,7 +152993,8 @@ var schemaDict2 = {
152981
152993
  },
152982
152994
  hideRepliesByUnfollowed: {
152983
152995
  type: "boolean",
152984
- description: "Hide replies in the feed if they are not by followed users."
152996
+ description: "Hide replies in the feed if they are not by followed users.",
152997
+ default: true
152985
152998
  },
152986
152999
  hideRepliesByLikeCount: {
152987
153000
  type: "integer",
@@ -179402,13 +179415,21 @@ var AuthVerifier2 = class {
179402
179415
  if (!this.parseRoleCreds(ctx.req).admin) {
179403
179416
  throw new AuthRequiredError("bad credentials");
179404
179417
  }
179405
- return { credentials: { type: "standard", iss: iss2, aud: aud2 } };
179418
+ return {
179419
+ credentials: { type: "standard", iss: iss2, aud: aud2 }
179420
+ };
179406
179421
  }
179407
179422
  const { iss, aud } = await this.verifyServiceJwt(ctx, {
179408
179423
  aud: this.ownDid,
179409
179424
  iss: null
179410
179425
  });
179411
- return { credentials: { type: "standard", iss, aud } };
179426
+ return {
179427
+ credentials: {
179428
+ type: "standard",
179429
+ iss,
179430
+ aud
179431
+ }
179432
+ };
179412
179433
  };
179413
179434
  this.standardOptional = async (ctx) => {
179414
179435
  if (isBearerToken(ctx.req) || isBasicToken(ctx.req)) {
@@ -179464,22 +179485,22 @@ var AuthVerifier2 = class {
179464
179485
  }
179465
179486
  }
179466
179487
  };
179467
- this.adminService = async (reqCtx) => {
179488
+ this.modService = async (reqCtx) => {
179468
179489
  const { iss, aud } = await this.verifyServiceJwt(reqCtx, {
179469
179490
  aud: this.ownDid,
179470
- iss: [this.adminDid]
179491
+ iss: [this.modServiceDid, `${this.modServiceDid}#atproto_labeler`]
179471
179492
  });
179472
- return { credentials: { type: "admin_service", aud, iss } };
179493
+ return { credentials: { type: "mod_service", aud, iss } };
179473
179494
  };
179474
- this.roleOrAdminService = async (reqCtx) => {
179495
+ this.roleOrModService = async (reqCtx) => {
179475
179496
  if (isBearerToken(reqCtx.req)) {
179476
- return this.adminService(reqCtx);
179497
+ return this.modService(reqCtx);
179477
179498
  } else {
179478
179499
  return this.role(reqCtx);
179479
179500
  }
179480
179501
  };
179481
179502
  this.ownDid = opts.ownDid;
179482
- this.adminDid = opts.adminDid;
179503
+ this.modServiceDid = opts.modServiceDid;
179483
179504
  this.adminPasses = new Set(opts.adminPasses);
179484
179505
  }
179485
179506
  parseRoleCreds(req) {
@@ -179495,10 +179516,12 @@ var AuthVerifier2 = class {
179495
179516
  return { status: Invalid, admin: false };
179496
179517
  }
179497
179518
  async verifyServiceJwt(reqCtx, opts) {
179498
- const getSigningKey2 = async (did2, _forceRefresh) => {
179499
- if (opts.iss !== null && !opts.iss.includes(did2)) {
179519
+ const getSigningKey2 = async (iss, _forceRefresh) => {
179520
+ if (opts.iss !== null && !opts.iss.includes(iss)) {
179500
179521
  throw new AuthRequiredError("Untrusted issuer", "UntrustedIss");
179501
179522
  }
179523
+ const [did2, serviceId] = iss.split("#");
179524
+ const keyId = serviceId === "atproto_labeler" ? "atproto_label" : "atproto";
179502
179525
  let identity3;
179503
179526
  try {
179504
179527
  identity3 = await this.dataplane.getIdentityByDid({ did: did2 });
@@ -179509,7 +179532,7 @@ var AuthVerifier2 = class {
179509
179532
  throw err;
179510
179533
  }
179511
179534
  const keys = unpackIdentityKeys(identity3.keys);
179512
- const didKey = getKeyAsDidKey(keys, { id: "atproto" });
179535
+ const didKey = getKeyAsDidKey(keys, { id: keyId });
179513
179536
  if (!didKey) {
179514
179537
  throw new AuthRequiredError("missing or bad key");
179515
179538
  }
@@ -179522,18 +179545,24 @@ var AuthVerifier2 = class {
179522
179545
  const payload = await verifyJwt(jwtStr, opts.aud, getSigningKey2);
179523
179546
  return { iss: payload.iss, aud: payload.aud };
179524
179547
  }
179548
+ isModService(iss) {
179549
+ return [
179550
+ this.modServiceDid,
179551
+ `${this.modServiceDid}#atproto_labeler`
179552
+ ].includes(iss);
179553
+ }
179525
179554
  nullCreds() {
179526
179555
  return {
179527
179556
  credentials: {
179528
- type: "null",
179557
+ type: "none",
179529
179558
  iss: null
179530
179559
  }
179531
179560
  };
179532
179561
  }
179533
179562
  parseCreds(creds) {
179534
179563
  const viewer = creds.credentials.type === "standard" ? creds.credentials.iss : null;
179535
- const canViewTakedowns = creds.credentials.type === "role" && creds.credentials.admin || creds.credentials.type === "admin_service";
179536
- const canPerformTakedown = creds.credentials.type === "role" && creds.credentials.admin || creds.credentials.type === "admin_service";
179564
+ const canViewTakedowns = creds.credentials.type === "role" && creds.credentials.admin || creds.credentials.type === "mod_service" || creds.credentials.type === "standard" && this.isModService(creds.credentials.iss);
179565
+ const canPerformTakedown = creds.credentials.type === "role" && creds.credentials.admin || creds.credentials.type === "mod_service";
179537
179566
  return {
179538
179567
  viewer,
179539
179568
  canViewTakedowns,
@@ -183417,7 +183446,7 @@ function getTaggedSuggestions_default(server, ctx) {
183417
183446
  // src/api/com/atproto/admin/getSubjectStatus.ts
183418
183447
  function getSubjectStatus_default(server, ctx) {
183419
183448
  server.com.atproto.admin.getSubjectStatus({
183420
- auth: ctx.authVerifier.roleOrAdminService,
183449
+ auth: ctx.authVerifier.roleOrModService,
183421
183450
  handler: async ({ params: params2 }) => {
183422
183451
  const { did: did2, uri: uri2, blob: blob2 } = params2;
183423
183452
  let body = null;
@@ -183499,7 +183528,7 @@ function isMain5(v) {
183499
183528
  // src/api/com/atproto/admin/updateSubjectStatus.ts
183500
183529
  function updateSubjectStatus_default(server, ctx) {
183501
183530
  server.com.atproto.admin.updateSubjectStatus({
183502
- auth: ctx.authVerifier.roleOrAdminService,
183531
+ auth: ctx.authVerifier.roleOrModService,
183503
183532
  handler: async ({ input, auth }) => {
183504
183533
  const { canPerformTakedown } = ctx.authVerifier.parseCreds(auth);
183505
183534
  if (!canPerformTakedown) {
@@ -183567,7 +183596,7 @@ function updateSubjectStatus_default(server, ctx) {
183567
183596
  // src/api/com/atproto/admin/getAccountInfos.ts
183568
183597
  function getAccountInfos_default(server, ctx) {
183569
183598
  server.com.atproto.admin.getAccountInfos({
183570
- auth: ctx.authVerifier.roleOrAdminService,
183599
+ auth: ctx.authVerifier.roleOrModService,
183571
183600
  handler: async ({ params: params2 }) => {
183572
183601
  const { dids } = params2;
183573
183602
  const actors = await ctx.hydrator.actor.getActors(dids, true);
@@ -183788,7 +183817,7 @@ async function resolveBlob(ctx, did2, cid2) {
183788
183817
  if (takenDown) {
183789
183818
  throw (0, import_http_errors2.default)(404, "Blob not found");
183790
183819
  }
183791
- const blobResult = await retryHttp(() => getBlob({ pds, did: did2, cid: cidStr }));
183820
+ const blobResult = await retryHttp(() => getBlob(ctx, { pds, did: did2, cid: cidStr }));
183792
183821
  const imageStream = blobResult.data;
183793
183822
  const verifyCid = new VerifyCidTransform(cid2);
183794
183823
  forwardStreamErrors(imageStream, verifyCid);
@@ -183798,15 +183827,36 @@ async function resolveBlob(ctx, did2, cid2) {
183798
183827
  stream: imageStream.pipe(verifyCid)
183799
183828
  };
183800
183829
  }
183801
- async function getBlob(opts) {
183830
+ async function getBlob(ctx, opts) {
183802
183831
  const { pds, did: did2, cid: cid2 } = opts;
183803
183832
  return import_axios4.default.get(`${pds}/xrpc/com.atproto.sync.getBlob`, {
183804
183833
  params: { did: did2, cid: cid2 },
183805
183834
  decompress: true,
183806
183835
  responseType: "stream",
183807
- timeout: 5e3
183836
+ timeout: 5e3,
183837
+ headers: getRateLimitBypassHeaders(ctx, pds)
183808
183838
  });
183809
183839
  }
183840
+ function getRateLimitBypassHeaders(ctx, pds) {
183841
+ const {
183842
+ blobRateLimitBypassKey: bypassKey,
183843
+ blobRateLimitBypassHostname: bypassHostname
183844
+ } = ctx.cfg;
183845
+ if (!bypassKey || !bypassHostname) {
183846
+ return {};
183847
+ }
183848
+ const url = new URL(pds);
183849
+ if (bypassHostname.startsWith(".")) {
183850
+ if (url.hostname.endsWith(bypassHostname)) {
183851
+ return { "x-ratelimit-bypass": bypassKey };
183852
+ }
183853
+ } else {
183854
+ if (url.hostname === bypassHostname) {
183855
+ return { "x-ratelimit-bypass": bypassKey };
183856
+ }
183857
+ }
183858
+ return {};
183859
+ }
183810
183860
 
183811
183861
  // src/api/index.ts
183812
183862
  function api_default(server, ctx) {
@@ -185527,6 +185577,9 @@ var ServerConfig = class {
185527
185577
  const courierHttpVersion = process.env.BSKY_COURIER_HTTP_VERSION || "2";
185528
185578
  const courierIgnoreBadTls = process.env.BSKY_COURIER_IGNORE_BAD_TLS === "true";
185529
185579
  (0, import_node_assert3.default)(courierHttpVersion === "1.1" || courierHttpVersion === "2");
185580
+ const blobRateLimitBypassKey = process.env.BSKY_BLOB_RATE_LIMIT_BYPASS_KEY || void 0;
185581
+ const blobRateLimitBypassHostname = process.env.BSKY_BLOB_RATE_LIMIT_BYPASS_HOSTNAME || void 0;
185582
+ (0, import_node_assert3.default)(!blobRateLimitBypassKey || blobRateLimitBypassHostname, "must specify a hostname when using a blob rate limit bypass key");
185530
185583
  const adminPasswords = envList(process.env.BSKY_ADMIN_PASSWORDS || process.env.BSKY_ADMIN_PASSWORD);
185531
185584
  const modServiceDid = process.env.MOD_SERVICE_DID;
185532
185585
  (0, import_node_assert3.default)(modServiceDid);
@@ -185555,6 +185608,8 @@ var ServerConfig = class {
185555
185608
  courierApiKey,
185556
185609
  courierHttpVersion,
185557
185610
  courierIgnoreBadTls,
185611
+ blobRateLimitBypassKey,
185612
+ blobRateLimitBypassHostname,
185558
185613
  adminPasswords,
185559
185614
  modServiceDid,
185560
185615
  ...stripUndefineds(overrides ?? {})
@@ -185622,6 +185677,12 @@ var ServerConfig = class {
185622
185677
  get cdnUrl() {
185623
185678
  return this.cfg.cdnUrl;
185624
185679
  }
185680
+ get blobRateLimitBypassKey() {
185681
+ return this.cfg.blobRateLimitBypassKey;
185682
+ }
185683
+ get blobRateLimitBypassHostname() {
185684
+ return this.cfg.blobRateLimitBypassHostname;
185685
+ }
185625
185686
  get didPlcUrl() {
185626
185687
  return this.cfg.didPlcUrl;
185627
185688
  }
@@ -186585,7 +186646,7 @@ var BskyAppView = class {
186585
186646
  });
186586
186647
  const authVerifier = new AuthVerifier2(dataplane, {
186587
186648
  ownDid: config2.serverDid,
186588
- adminDid: config2.modServiceDid,
186649
+ modServiceDid: config2.modServiceDid,
186589
186650
  adminPasses: config2.adminPasswords
186590
186651
  });
186591
186652
  const ctx = new context_default({