@fedify/fedify 2.4.0-dev.2169 → 2.4.0-dev.2193

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 (103) hide show
  1. package/dist/{builder-B2d_Pt0j.mjs → builder-0onlMY4u.mjs} +26 -6
  2. package/dist/{circuit-breaker-C5NqoLBJ.mjs → circuit-breaker-XJ53md4S.mjs} +16 -11
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/transformers.test.mjs +1 -1
  6. package/dist/{compound-proof-D28gMFqP.mjs → compound-proof-B-Fk6Slo.mjs} +3 -3
  7. package/dist/{context-BykNUf04.d.ts → context-9T--NSo4.d.ts} +55 -1
  8. package/dist/{context-L0Fnib9j.d.cts → context-CDcAgm4l.d.cts} +55 -1
  9. package/dist/{deno-DU8dvnWk.mjs → deno-DB_Sh0LK.mjs} +1 -1
  10. package/dist/{docloader-CghLH7Zi.mjs → docloader-DpWYYY6a.mjs} +25 -17
  11. package/dist/federation/builder.test.mjs +1 -1
  12. package/dist/federation/circuit-breaker.test.mjs +50 -1
  13. package/dist/federation/compound-fanout.test.mjs +3 -3
  14. package/dist/federation/handler-compound-proof.test.mjs +52 -3
  15. package/dist/federation/handler.test.mjs +5 -5
  16. package/dist/federation/hashlink-media.test.mjs +2 -2
  17. package/dist/federation/idempotency.test.mjs +2 -2
  18. package/dist/federation/keycache.test.mjs +1 -1
  19. package/dist/federation/metrics.test.mjs +1 -1
  20. package/dist/federation/middleware.test.mjs +182 -13
  21. package/dist/federation/mod.cjs +1 -1
  22. package/dist/federation/mod.d.cts +2 -2
  23. package/dist/federation/mod.d.ts +2 -2
  24. package/dist/federation/mod.js +1 -1
  25. package/dist/federation/outgoing-proof.test.mjs +5 -5
  26. package/dist/federation/portable-actor.test.mjs +3 -3
  27. package/dist/federation/portable-chain.test.mjs +3 -3
  28. package/dist/federation/portable-collection.test.mjs +4 -4
  29. package/dist/federation/portable-gateway-keys.test.mjs +6 -6
  30. package/dist/federation/portable-inbox.test.mjs +35 -5
  31. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  32. package/dist/federation/portable-route-activity.test.d.mts +2 -0
  33. package/dist/federation/portable-route-activity.test.mjs +259 -0
  34. package/dist/federation/portable.test.mjs +49 -5
  35. package/dist/federation/send.test.mjs +3 -3
  36. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  37. package/dist/federation/tasks/tasks.test.mjs +2 -2
  38. package/dist/federation/temporal.test.mjs +1 -1
  39. package/dist/federation/webfinger.test.mjs +1 -1
  40. package/dist/{federation-DrNgkLfJ.cjs → federation-BsGo0EH2.cjs} +76 -35
  41. package/dist/{http-5CJBdU8n.cjs → http-BEivI0TS.cjs} +279 -87
  42. package/dist/{http-BGpexPO7.js → http-BGmxtpo2.js} +263 -89
  43. package/dist/{http-Cok5-vIC.d.ts → http-BrwKINQn.d.cts} +29 -1
  44. package/dist/{http-DC1cqAQ2.d.cts → http-DGx0KOws.d.ts} +29 -1
  45. package/dist/{http-Bd6rZlJX.mjs → http-DQf9Mw4V.mjs} +180 -92
  46. package/dist/{key-gIhVZO3p.mjs → key-DA3Yx8p2.mjs} +4 -4
  47. package/dist/{key-owner-evidence-Bo3yaeL5.mjs → key-owner-evidence-APB6NpSk.mjs} +1 -1
  48. package/dist/{keycache-PiOM9pM0.mjs → keycache-Da8A8mZa.mjs} +1 -1
  49. package/dist/{kv-cache-YRi_g8GB.cjs → kv-cache-1hafSoqH.cjs} +23 -15
  50. package/dist/{kv-cache-iR55fqlk.js → kv-cache-DduYhpee.js} +24 -16
  51. package/dist/{kv-cache-D0qjSeWY.mjs → kv-cache-OFBvrKh3.mjs} +1 -1
  52. package/dist/{ld-BsTkRrwF.mjs → ld-C15yzuY6.mjs} +3 -3
  53. package/dist/{metrics-jUDFfVgi.mjs → metrics-B1H23mlq.mjs} +1 -1
  54. package/dist/{middleware-Dq-OCmb6.mjs → middleware-BZQnf0nI.mjs} +1 -1
  55. package/dist/{middleware-Coqgp-K0.mjs → middleware-BqqycC0P.mjs} +52 -36
  56. package/dist/{middleware-DlxMxVp5.js → middleware-ClaUFaLb.js} +77 -36
  57. package/dist/{mod-DK0y9fd-.d.ts → mod-B3OBLj5g.d.ts} +1 -1
  58. package/dist/{mod-B5z_q9TH.d.ts → mod-C5Mwx5ty.d.ts} +5 -1
  59. package/dist/{mod-CPPqkyV_.d.cts → mod-Cy9VNHVw.d.ts} +14 -3
  60. package/dist/{mod-BLOXGs6m.d.cts → mod-D58KCqUg.d.cts} +1 -1
  61. package/dist/{mod--jv6vyrP.d.ts → mod-DoTWIc7F.d.cts} +14 -3
  62. package/dist/{mod-BA_F2x53.d.cts → mod-Dp3Ij7sw.d.cts} +5 -1
  63. package/dist/mod.cjs +4 -4
  64. package/dist/mod.d.cts +5 -5
  65. package/dist/mod.d.ts +5 -5
  66. package/dist/mod.js +4 -4
  67. package/dist/nodeinfo/handler.test.mjs +1 -1
  68. package/dist/{owner-D3GoetXS.mjs → owner-BVpZBM-V.mjs} +5 -5
  69. package/dist/{portable-collection-CnlCamup.cjs → portable-collection-BFSShzwh.cjs} +1 -1
  70. package/dist/{portable-collection-Bkdh_WpL.js → portable-collection-D535cSz_.js} +1 -1
  71. package/dist/{portable-collection-C60_acmx.mjs → portable-collection-Dn2GdZdI.mjs} +2 -2
  72. package/dist/{portable-key-DyF5AnZo.mjs → portable-key-Bmwmq7Y6.mjs} +2 -2
  73. package/dist/{portable-key-id-Bg259c4Z.mjs → portable-key-id-Ca30-hOa.mjs} +45 -1
  74. package/dist/{proof-CQAYqWdj.mjs → proof-DrJWKeOl.mjs} +49 -7
  75. package/dist/{send-DjkQrxt5.mjs → send-CT3a-0Vd.mjs} +3 -3
  76. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  77. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  78. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  79. package/dist/sig/compound-proof.test.mjs +2 -2
  80. package/dist/sig/http.test.mjs +495 -23
  81. package/dist/sig/key-owner-evidence.test.mjs +2 -2
  82. package/dist/sig/key.test.mjs +1 -1
  83. package/dist/sig/ld.test.mjs +2 -2
  84. package/dist/sig/mod.cjs +2 -2
  85. package/dist/sig/mod.d.cts +2 -2
  86. package/dist/sig/mod.d.ts +2 -2
  87. package/dist/sig/mod.js +2 -2
  88. package/dist/sig/owner.test.mjs +1 -1
  89. package/dist/sig/portable-collection.test.mjs +1 -1
  90. package/dist/sig/portable-key-id.test.d.mts +2 -0
  91. package/dist/sig/portable-key-id.test.mjs +67 -0
  92. package/dist/sig/portable-key.test.mjs +7 -7
  93. package/dist/sig/proof.test.mjs +141 -7
  94. package/dist/{temporal-oRKP803M.mjs → temporal-DNqluyEc.mjs} +1 -1
  95. package/dist/testing/kv.test.mjs +1 -1
  96. package/dist/testing/mod.d.mts +52 -0
  97. package/dist/utils/docloader.test.mjs +93 -2
  98. package/dist/utils/kv-cache.test.mjs +1 -1
  99. package/dist/utils/mod.cjs +1 -1
  100. package/dist/utils/mod.d.cts +1 -1
  101. package/dist/utils/mod.d.ts +1 -1
  102. package/dist/utils/mod.js +1 -1
  103. package/package.json +7 -7
@@ -1,10 +1,10 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
2
  const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
3
3
  const require_transformers = require("./transformers-BalCrSsr.cjs");
4
- const require_http = require("./http-5CJBdU8n.cjs");
4
+ const require_http = require("./http-BEivI0TS.cjs");
5
5
  const require_types = require("./types-poIao40f.cjs");
6
- const require_portable_collection = require("./portable-collection-CnlCamup.cjs");
7
- const require_kv_cache = require("./kv-cache-YRi_g8GB.cjs");
6
+ const require_portable_collection = require("./portable-collection-BFSShzwh.cjs");
7
+ const require_kv_cache = require("./kv-cache-1hafSoqH.cjs");
8
8
  let _logtape_logtape = require("@logtape/logtape");
9
9
  let _fedify_uri_template = require("@fedify/uri-template");
10
10
  let _fedify_vocab = require("@fedify/vocab");
@@ -210,6 +210,7 @@ var FederationBuilderImpl = class {
210
210
  if (actor.id == null) logger.warn("Actor dispatcher returned an actor without an id property. Set the property with Context.getActorUri(identifier).");
211
211
  else if (!portable && actor.id.href != context.getActorUri(identifier).href) logger.warn("Actor dispatcher returned an actor with an id property that does not match the actor URI. Set the property with Context.getActorUri(identifier).");
212
212
  if (actor instanceof _fedify_vocab.Tombstone) return actor;
213
+ if (portable && actor.id != null) warnPortableActorGateways(actor.id, actor.gateways);
213
214
  if (actor.id != null && (0, _fedify_vocab_runtime_internal_portable_dereference.isCompatibleEf61Iri)(actor.id)) warnCompatibleActorId(actor.id, actor.gateway);
214
215
  if (portable) this.#warnPortableCollectionIds(context, identifier, actor);
215
216
  if (this.followingCallbacks != null && this.followingCallbacks.dispatcher != null) {
@@ -813,9 +814,28 @@ function warnMismatchedTombstoneId(context, cls, values, tombstone) {
813
814
  });
814
815
  }
815
816
  /**
817
+ * Warns about an FEP-ef61 portable actor whose `gateways` is empty or has
818
+ * an item that is not an HTTP(S) origin, which FEP-ef61 does not allow.
819
+ * Every item is checked, since the list and its URLs can be mutated after
820
+ * the actor is constructed.
821
+ */
822
+ function warnPortableActorGateways(actorId, gateways) {
823
+ if (gateways.length > 0 && gateways.every(_fedify_vocab_runtime.isGatewayUrl)) return;
824
+ (0, _logtape_logtape.getLogger)([
825
+ "fedify",
826
+ "federation",
827
+ "actor"
828
+ ]).warn("Actor dispatcher returned an FEP-ef61 portable actor, {actorId}, whose gateways property is empty or has an item that is not an HTTP(S) origin. FEP-ef61 requires a portable actor to have at least one gateway, and every gateway to be an HTTP(S) URI with an empty path, query, and fragment. Set the gateways property.", {
829
+ actorId: actorId.href,
830
+ gateways: gateways.map((g) => g.href)
831
+ });
832
+ }
833
+ /**
816
834
  * Warns about an FEP-ef61 portable actor identified by a compatible identifier
817
835
  * that is malformed, or is not on the actor's first gateway, where FEP-ef61
818
- * requires publishers to construct compatible identifiers.
836
+ * requires publishers to construct compatible identifiers. A missing or
837
+ * invalid first gateway is reported by {@link warnPortableActorGateways}
838
+ * instead.
819
839
  */
820
840
  function warnCompatibleActorId(actorId, gateway) {
821
841
  const logger = (0, _logtape_logtape.getLogger)([
@@ -833,8 +853,8 @@ function warnCompatibleActorId(actorId, gateway) {
833
853
  });
834
854
  return;
835
855
  }
836
- if (gateway == null || !(0, _fedify_vocab_runtime.isGatewayUrl)(gateway)) logger.warn("Actor dispatcher returned an actor whose id property, {actorId}, is an FEP-ef61 compatible identifier, but whose first gateway is not an HTTP(S) origin. Set the gateways property, and construct the compatible identifier with its first gateway.", { actorId: actorId.href });
837
- else if (actorId.origin !== gateway.origin) logger.warn("Actor dispatcher returned an actor whose id property, {actorId}, is an FEP-ef61 compatible identifier on another gateway than its first gateway, {gateway}. FEP-ef61 requires publishers to construct compatible identifiers with the first gateway in the actor's gateways.", {
856
+ if (gateway == null || !(0, _fedify_vocab_runtime.isGatewayUrl)(gateway)) return;
857
+ if (actorId.origin !== gateway.origin) logger.warn("Actor dispatcher returned an actor whose id property, {actorId}, is an FEP-ef61 compatible identifier on another gateway than its first gateway, {gateway}. FEP-ef61 requires publishers to construct compatible identifiers with the first gateway in the actor's gateways.", {
838
858
  actorId: actorId.href,
839
859
  gateway: gateway.origin
840
860
  });
@@ -886,7 +906,7 @@ var CircuitBreaker = class {
886
906
  };
887
907
  let lastConflictingState;
888
908
  for (let attempt = 0; attempt < 10; attempt++) {
889
- const oldState = await this.#get(remoteHost);
909
+ const { state: oldState, raw: oldValue } = await this.#get(remoteHost);
890
910
  if (oldState == null || oldState.state === "closed") return {
891
911
  type: "send",
892
912
  probe: false
@@ -912,7 +932,7 @@ var CircuitBreaker = class {
912
932
  state: "half-open",
913
933
  halfOpened: now.toString()
914
934
  };
915
- if (await this.#replace(remoteHost, oldState, newState)) return {
935
+ if (await this.#replace(remoteHost, oldValue, newState)) return {
916
936
  type: "send",
917
937
  probe: true
918
938
  };
@@ -934,7 +954,7 @@ var CircuitBreaker = class {
934
954
  state: "half-open",
935
955
  halfOpened: now.toString()
936
956
  };
937
- if (await this.#replace(remoteHost, oldState, newState)) {
957
+ if (await this.#replace(remoteHost, oldValue, newState)) {
938
958
  await this.#notifyStateChange(remoteHost, "open", "half-open");
939
959
  return {
940
960
  type: "send",
@@ -961,9 +981,9 @@ var CircuitBreaker = class {
961
981
  async recordSuccess(remoteHost) {
962
982
  this.#sweepLegacyStates();
963
983
  for (let attempt = 0; attempt < 10; attempt++) {
964
- const oldState = await this.#get(remoteHost);
984
+ const { state: oldState, raw: oldValue } = await this.#get(remoteHost);
965
985
  if (oldState == null) return void 0;
966
- if (await this.#replace(remoteHost, oldState, void 0)) {
986
+ if (await this.#replace(remoteHost, oldValue, void 0)) {
967
987
  if (oldState.state !== "closed") {
968
988
  await this.#notifyStateChange(remoteHost, oldState.state, "closed");
969
989
  return {
@@ -983,7 +1003,7 @@ var CircuitBreaker = class {
983
1003
  this.#sweepLegacyStates();
984
1004
  const now = this.#now();
985
1005
  for (let attempt = 0; attempt < 10; attempt++) {
986
- const oldState = await this.#get(remoteHost);
1006
+ const { state: oldState, raw: oldValue } = await this.#get(remoteHost);
987
1007
  if (oldState?.state === "open") return void 0;
988
1008
  const oldFailures = oldState?.failures.map(Temporal$11.Instant.from) ?? [];
989
1009
  const failures = this.#options.pruneFailures([...oldFailures, now], now);
@@ -1000,7 +1020,7 @@ var CircuitBreaker = class {
1000
1020
  state: "closed",
1001
1021
  failures: failures.map((t) => t.toString())
1002
1022
  };
1003
- if (await this.#replace(remoteHost, oldState, newState)) {
1023
+ if (await this.#replace(remoteHost, oldValue, newState)) {
1004
1024
  if (transition != null) {
1005
1025
  await this.#notifyStateChange(remoteHost, transition[0], transition[1]);
1006
1026
  return {
@@ -1029,7 +1049,7 @@ var CircuitBreaker = class {
1029
1049
  }
1030
1050
  async getState(remoteHost) {
1031
1051
  this.#sweepLegacyStates();
1032
- return stripStoredCircuitBreakerState(await this.#get(remoteHost));
1052
+ return stripStoredCircuitBreakerState((await this.#get(remoteHost)).state);
1033
1053
  }
1034
1054
  /**
1035
1055
  * The currently running background legacy sweep, if any.
@@ -1149,9 +1169,14 @@ var CircuitBreaker = class {
1149
1169
  return Temporal$11.Instant.compare(expiresAt, retryAt) < 0 ? expiresAt : retryAt;
1150
1170
  }
1151
1171
  async #get(remoteHost) {
1152
- return parseStoredCircuitBreakerKvState(await this.#kv.get(this.#key(remoteHost)));
1172
+ const value = await this.#kv.get(this.#key(remoteHost));
1173
+ const state = parseStoredCircuitBreakerKvState(value);
1174
+ return {
1175
+ state,
1176
+ raw: state == null ? void 0 : value
1177
+ };
1153
1178
  }
1154
- async #replace(remoteHost, oldState, newState) {
1179
+ async #replace(remoteHost, oldValue, newState) {
1155
1180
  const key = this.#key(remoteHost);
1156
1181
  const storedState = newState == null ? void 0 : markCircuitBreakerState(newState);
1157
1182
  if (this.#kv.cas == null) {
@@ -1159,7 +1184,7 @@ var CircuitBreaker = class {
1159
1184
  else await this.#kv.set(key, storedState, this.#setOptions());
1160
1185
  return true;
1161
1186
  }
1162
- return await this.#kv.cas(key, oldState, storedState, storedState == null ? void 0 : this.#setOptions());
1187
+ return await this.#kv.cas(key, oldValue, storedState, storedState == null ? void 0 : this.#setOptions());
1163
1188
  }
1164
1189
  #setOptions() {
1165
1190
  return this.#options.stateTtl == null ? void 0 : { ttl: this.#options.stateTtl };
@@ -3787,7 +3812,7 @@ function isInvalidJsonLdError(error) {
3787
3812
  return name === "UnsafeJsonLdError" || error instanceof require_http.InvalidContextReferenceError || isPermanentRemoteContextError$1(error) || name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure$1(error);
3788
3813
  }
3789
3814
  function isValidationTypeError(error) {
3790
- return error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:|Invalid @id:)/.test(error.message) || require_http.isInvalidUrlTypeError(error));
3815
+ return error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:|Invalid @id:|Invalid FEP-ef61 gateway:)/.test(error.message) || require_http.isInvalidUrlTypeError(error));
3791
3816
  }
3792
3817
  function isPermanentActivityParseError(error) {
3793
3818
  return isInvalidJsonLdError(error) || isValidationTypeError(error);
@@ -3930,7 +3955,11 @@ async function handlePortableObject(request, { values, context, objectDispatcher
3930
3955
  return await onNotFound(request);
3931
3956
  }
3932
3957
  if (tombstone || result.reason.type !== "unsecuredCollection" && result.reason.type !== "unsupportedObjectType") {
3933
- logger.error("Refusing to serve the portable object {portableId}, as it does not satisfy the FEP-ef61 proof policy: {reason}. Portable actors, activities, objects, and tombstones need an Object Integrity Proof made with a key of the DID in their ID.", {
3958
+ if (result.reason.type === "invalidGateways") logger.error("Refusing to serve the portable actor {portableId}, as its gateways property is missing, empty, or has an item that is not an HTTP(S) origin. FEP-ef61 requires a portable actor to list at least one gateway where it can be retrieved.", {
3959
+ portableId: canonicalId,
3960
+ reason: result.reason.type
3961
+ });
3962
+ else logger.error("Refusing to serve the portable object {portableId}, as it does not satisfy the FEP-ef61 proof policy: {reason}. Portable actors, activities, objects, and tombstones need an Object Integrity Proof made with a key of the DID in their ID.", {
3934
3963
  portableId: canonicalId,
3935
3964
  reason: result.reason.type
3936
3965
  });
@@ -4049,13 +4078,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
4049
4078
  * @param parameters The parameters for handling the collection.
4050
4079
  * @returns A promise that resolves to an HTTP response.
4051
4080
  */
4052
- async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
4053
- const spanName = name$1.trim().replace(/\s+/g, "_");
4081
+ async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
4082
+ const spanName = name$2.trim().replace(/\s+/g, "_");
4054
4083
  tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
4055
4084
  const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
4056
4085
  const cursor = new URL(request.url).searchParams.get("cursor");
4057
4086
  const metricBase = {
4058
- kind: getCollectionMetricKind(name$1),
4087
+ kind: getCollectionMetricKind(name$2),
4059
4088
  page: cursor != null,
4060
4089
  dispatcher: "built_in"
4061
4090
  };
@@ -4101,7 +4130,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
4101
4130
  span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
4102
4131
  return await onNotFound(request);
4103
4132
  }
4104
- const items = filterCollectionItems(page.items, name$1, filterPredicate);
4133
+ const items = filterCollectionItems(page.items, name$2, filterPredicate);
4105
4134
  itemCount = items.length;
4106
4135
  span.setAttribute("fedify.collection.items", itemCount);
4107
4136
  return items;
@@ -4143,7 +4172,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
4143
4172
  } else {
4144
4173
  const uri = new URL(portable == null ? baseUri : pageBase);
4145
4174
  uri.searchParams.set("cursor", cursor);
4146
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
4175
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
4147
4176
  kind: _opentelemetry_api.SpanKind.SERVER,
4148
4177
  attributes: {
4149
4178
  "activitypub.collection.id": uri.href,
@@ -4159,7 +4188,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
4159
4188
  span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
4160
4189
  return await onNotFound(request);
4161
4190
  }
4162
- const items = filterCollectionItems(page.items, name$1, filterPredicate);
4191
+ const items = filterCollectionItems(page.items, name$2, filterPredicate);
4163
4192
  itemCount = items.length;
4164
4193
  span.setAttribute("fedify.collection.items", itemCount);
4165
4194
  return {
@@ -4667,7 +4696,7 @@ async function handleInbox(request, options) {
4667
4696
  * @returns A promise that resolves to an HTTP response.
4668
4697
  */
4669
4698
  async function handleInboxInternal(request, parameters, span) {
4670
- const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider, portableInbox } = parameters;
4699
+ const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, maxHttpSignatures, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider, portableInbox } = parameters;
4671
4700
  const logger = (0, _logtape_logtape.getLogger)([
4672
4701
  "fedify",
4673
4702
  "federation",
@@ -4953,6 +4982,7 @@ async function handleInboxInternal(request, parameters, span) {
4953
4982
  contextLoader: ctx.contextLoader,
4954
4983
  documentLoader: ctx.documentLoader,
4955
4984
  timeWindow: signatureTimeWindow,
4985
+ maxSignatures: maxHttpSignatures,
4956
4986
  keyCache,
4957
4987
  meterProvider,
4958
4988
  tracerProvider
@@ -5175,7 +5205,7 @@ async function handleInboxInternal(request, parameters, span) {
5175
5205
  inbox: portableInbox.recipient.canonicalInboxId
5176
5206
  });
5177
5207
  else try {
5178
- await portableInbox.forward(json, activity.id, (0, _fedify_vocab.getTypeId)(activity).href, require_http.listRequestSignatures(request).map((signature) => ({
5208
+ await portableInbox.forward(json, activity.id, (0, _fedify_vocab.getTypeId)(activity).href, require_http.listRequestSignatures(request, maxHttpSignatures).map((signature) => ({
5179
5209
  keyId: signature.keyId,
5180
5210
  coversDelivery: coversDelivery(signature),
5181
5211
  verify: async () => {
@@ -5349,8 +5379,8 @@ var CustomCollectionHandler = class {
5349
5379
  * @param CollectionPage The CollectionPage constructor.
5350
5380
  * @param filterPredicate Optional filter predicate for items.
5351
5381
  */
5352
- constructor(name$2, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
5353
- this.name = name$2;
5382
+ constructor(name$1, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
5383
+ this.name = name$1;
5354
5384
  this.values = values;
5355
5385
  this.context = context;
5356
5386
  this.callbacks = callbacks;
@@ -7229,7 +7259,7 @@ function isPermanentRemoteContextError(error) {
7229
7259
  return isRemoteContextLoadingFailure(error) && typeof details?.url === "string" && !URL.canParse(details.url) && require_http.isClearlyMalformedContextReference(details.url);
7230
7260
  }
7231
7261
  function isPermanentInboxParseError(error) {
7232
- return error instanceof Error && (error.name === "UnsafeJsonLdError" || error instanceof require_http.InvalidContextReferenceError || isPermanentRemoteContextError(error) || error.name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure(error)) || error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:|Invalid @id:)/.test(error.message) || require_http.isInvalidUrlTypeError(error));
7262
+ return error instanceof Error && (error.name === "UnsafeJsonLdError" || error instanceof require_http.InvalidContextReferenceError || isPermanentRemoteContextError(error) || error.name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure(error)) || error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:|Invalid @id:|Invalid FEP-ef61 gateway:)/.test(error.message) || require_http.isInvalidUrlTypeError(error));
7233
7263
  }
7234
7264
  function hasLinkedDataSignatureJsonLdProcessingError(error) {
7235
7265
  if (error.message.startsWith("Maximum deep iterations exceeded")) return true;
@@ -7246,6 +7276,8 @@ function isLinkedDataSignatureJsonLdProcessingError(error) {
7246
7276
  * @returns A new {@link Federation} instance.
7247
7277
  * @throws {TypeError} If benchmark mode and `meterProvider` are both
7248
7278
  * specified.
7279
+ * @throws {RangeError} If {@link FederationOptions.maxHttpSignatures} is not
7280
+ * a positive integer or `Infinity`.
7249
7281
  * @since 0.10.0
7250
7282
  */
7251
7283
  function createFederation(options) {
@@ -7272,6 +7304,7 @@ var FederationImpl = class extends FederationBuilderImpl {
7272
7304
  onOutboxError;
7273
7305
  permanentFailureStatusCodes;
7274
7306
  signatureTimeWindow;
7307
+ maxHttpSignatures;
7275
7308
  skipSignatureVerification;
7276
7309
  outboxRetryPolicy;
7277
7310
  inboxRetryPolicy;
@@ -7300,6 +7333,7 @@ var FederationImpl = class extends FederationBuilderImpl {
7300
7333
  const hasCustomLoaderFactory = options.documentLoaderFactory != null || options.contextLoaderFactory != null;
7301
7334
  const allowPrivateAddress = options.allowPrivateAddress ?? (benchmarkMode && !hasCustomLoaderFactory ? true : false);
7302
7335
  const signatureTimeWindow = options.signatureTimeWindow ?? (benchmarkMode ? false : { hours: 1 });
7336
+ if (options.maxHttpSignatures !== void 0) require_http.validateMaxSignatures(options.maxHttpSignatures, "maxHttpSignatures");
7303
7337
  if (benchmarkMode && options.meterProvider != null) throw new TypeError("benchmarkMode requires Fedify to own the meterProvider; OpenTelemetry metric readers cannot be added after a MeterProvider is constructed.");
7304
7338
  if (benchmarkMode) {
7305
7339
  const relaxations = getBenchmarkRelaxations(allowPrivateAddress, signatureTimeWindow);
@@ -7392,13 +7426,15 @@ var FederationImpl = class extends FederationBuilderImpl {
7392
7426
  }
7393
7427
  const { userAgent } = options;
7394
7428
  this.allowPrivateAddress = allowPrivateAddress;
7429
+ const documentLoaderTimeout = (0, _fedify_vocab_runtime.resolveDocumentLoaderTimeout)(options.documentLoaderTimeout == null ? options.documentLoaderTimeout : Temporal$2.Duration.from(options.documentLoaderTimeout).total("millisecond"));
7395
7430
  const userDocumentLoaderFactory = options.documentLoaderFactory;
7396
7431
  const userContextLoaderFactory = options.contextLoaderFactory;
7397
7432
  const userAuthFactory = options.authenticatedDocumentLoaderFactory;
7398
7433
  const builtinDocumentLoaderFactory = (opts) => require_kv_cache.kvCache({
7399
7434
  loader: (0, _fedify_vocab_runtime.getDocumentLoader)({
7400
7435
  allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
7401
- userAgent: opts?.userAgent ?? userAgent
7436
+ userAgent: opts?.userAgent ?? userAgent,
7437
+ timeout: opts?.timeout !== void 0 ? opts.timeout : documentLoaderTimeout
7402
7438
  }),
7403
7439
  kv: options.kv,
7404
7440
  prefix: this.kvPrefixes.remoteDocument,
@@ -7408,7 +7444,8 @@ var FederationImpl = class extends FederationBuilderImpl {
7408
7444
  const builtinContextLoaderFactory = (opts) => require_kv_cache.kvCache({
7409
7445
  loader: (0, _fedify_vocab_runtime.getDocumentLoader)({
7410
7446
  allowPrivateAddress: opts?.allowPrivateAddress ?? allowPrivateAddress,
7411
- userAgent: opts?.userAgent ?? userAgent
7447
+ userAgent: opts?.userAgent ?? userAgent,
7448
+ timeout: opts?.timeout !== void 0 ? opts.timeout : documentLoaderTimeout
7412
7449
  }),
7413
7450
  kv: options.kv,
7414
7451
  prefix: this.kvPrefixes.remoteDocument,
@@ -7429,6 +7466,7 @@ var FederationImpl = class extends FederationBuilderImpl {
7429
7466
  this.authenticatedDocumentLoaderFactory = (identity, factoryOpts) => require_http.instrumentDocumentLoader(userAuthFactory != null ? userAuthFactory(identity, factoryOpts) : require_kv_cache.getAuthenticatedDocumentLoader(identity, {
7430
7467
  allowPrivateAddress: factoryOpts?.allowPrivateAddress ?? allowPrivateAddress,
7431
7468
  userAgent: factoryOpts?.userAgent ?? userAgent,
7469
+ timeout: factoryOpts?.timeout !== void 0 ? factoryOpts.timeout : documentLoaderTimeout,
7432
7470
  specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, options.firstKnock, { specTtl: this.httpMessageSignaturesSpecTtl }),
7433
7471
  tracerProvider: this.tracerProvider
7434
7472
  }), {
@@ -7440,6 +7478,7 @@ var FederationImpl = class extends FederationBuilderImpl {
7440
7478
  this.onOutboxError = options.onOutboxError;
7441
7479
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
7442
7480
  this.signatureTimeWindow = signatureTimeWindow;
7481
+ this.maxHttpSignatures = options.maxHttpSignatures ?? 3;
7443
7482
  this.skipSignatureVerification = options.skipSignatureVerification ?? false;
7444
7483
  this.inboxChallengePolicy = options.inboxChallengePolicy;
7445
7484
  this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
@@ -9065,6 +9104,7 @@ var FederationImpl = class extends FederationBuilderImpl {
9065
9104
  unverifiedActivityHandler: this.unverifiedActivityHandler,
9066
9105
  onNotFound,
9067
9106
  signatureTimeWindow: this.signatureTimeWindow,
9107
+ maxHttpSignatures: this.maxHttpSignatures,
9068
9108
  skipSignatureVerification: this.skipSignatureVerification,
9069
9109
  inboxChallengePolicy: this.inboxChallengePolicy,
9070
9110
  meterProvider: this.meterProvider,
@@ -10053,7 +10093,7 @@ var ContextImpl = class ContextImpl {
10053
10093
  activity: await fetched.toJsonLd({ contextLoader })
10054
10094
  });
10055
10095
  return false;
10056
- } else if (fetched.id?.href !== activity.id.href) {
10096
+ } else if (fetched.id == null || !require_http.isSameObjectId(fetched.id, activity.id)) {
10057
10097
  logger.debug("Fetched activity object has a different ID; failed to verify.", {
10058
10098
  recipient,
10059
10099
  activity: await fetched.toJsonLd({ contextLoader })
@@ -10066,8 +10106,8 @@ var ContextImpl = class ContextImpl {
10066
10106
  });
10067
10107
  return false;
10068
10108
  }
10069
- const activityId = fetched.id;
10070
- if (!fetched.actorIds.every((actor) => actor.origin === activityId.origin)) {
10109
+ const activityOrigin = require_http.getAuthenticationOrigin(fetched.id);
10110
+ if (activityOrigin == null || !fetched.actorIds.every((actor) => require_http.getAuthenticationOrigin(actor) === activityOrigin)) {
10071
10111
  logger.debug("Fetched activity object has actors from different origins; unable to verify.", {
10072
10112
  recipient,
10073
10113
  activity: await fetched.toJsonLd({ contextLoader })
@@ -10203,6 +10243,7 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
10203
10243
  contextLoader: options.contextLoader ?? this.contextLoader,
10204
10244
  documentLoader: options.documentLoader ?? this.documentLoader,
10205
10245
  timeWindow: this.federation.signatureTimeWindow,
10246
+ maxSignatures: this.federation.maxHttpSignatures,
10206
10247
  meterProvider: this.meterProvider,
10207
10248
  tracerProvider: options.tracerProvider ?? this.tracerProvider
10208
10249
  });