@fedify/fedify 2.2.3-dev.1098 → 2.2.3

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 (60) hide show
  1. package/dist/{builder-mqtih91o.mjs → builder-CaVN56-q.mjs} +2 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{context-BPMgyX7m.d.ts → context-BU-1O90h.d.ts} +48 -6
  6. package/dist/{context-DwkhwUX9.d.cts → context-DVA8wHZ0.d.cts} +48 -6
  7. package/dist/{deno-CziVFvS6.mjs → deno-DMg4SgCb.mjs} +1 -1
  8. package/dist/{docloader-fI9DeYyB.mjs → docloader-Da15YRxG.mjs} +2 -2
  9. package/dist/federation/builder.test.mjs +1 -1
  10. package/dist/federation/handler.test.mjs +1363 -43
  11. package/dist/federation/idempotency.test.mjs +2 -2
  12. package/dist/federation/middleware.test.mjs +1584 -80
  13. package/dist/federation/mod.cjs +1 -1
  14. package/dist/federation/mod.d.cts +2 -2
  15. package/dist/federation/mod.d.ts +2 -2
  16. package/dist/federation/mod.js +1 -1
  17. package/dist/federation/retry.test.mjs +1 -1
  18. package/dist/federation/send.test.mjs +3 -3
  19. package/dist/federation/temporal.test.d.mts +2 -0
  20. package/dist/federation/temporal.test.mjs +71 -0
  21. package/dist/federation/webfinger.test.mjs +1 -1
  22. package/dist/{http-D8qsXrUS.js → http-BPPaA2uz.js} +1 -1
  23. package/dist/{http-BDCGf4Ac.mjs → http-C_edJspG.mjs} +2 -2
  24. package/dist/{http-kPc328Pc.cjs → http-Cl0Q2bUO.cjs} +1 -1
  25. package/dist/{key-D3TgMhcs.mjs → key-BAQuZEU1.mjs} +1 -1
  26. package/dist/{kv-cache-D_eVhctK.js → kv-cache-C4DGZ_t4.js} +1 -1
  27. package/dist/{kv-cache-zxW74Wfd.cjs → kv-cache-DmGi6uC-.cjs} +1 -1
  28. package/dist/ld-tusP_XxG.mjs +573 -0
  29. package/dist/{middleware-xR9KxICq.cjs → middleware-0V-9qj7m.cjs} +399 -73
  30. package/dist/{middleware-gXlDLkok.js → middleware-Ar1QOOPG.js} +396 -71
  31. package/dist/{middleware-2gmMVy8b.mjs → middleware-D9k0Knum.mjs} +314 -78
  32. package/dist/{middleware-BuOXw_hM.cjs → middleware-OQPBzyvx.cjs} +1 -1
  33. package/dist/{middleware-CfaiRKQ9.mjs → middleware-madKLp2f.mjs} +1 -1
  34. package/dist/{mod-CNAHY39V.d.ts → mod-BVt6iTmH.d.ts} +1 -1
  35. package/dist/{mod-Bi6WOdti.d.cts → mod-q-NFLW6B.d.cts} +1 -1
  36. package/dist/mod.cjs +4 -4
  37. package/dist/mod.d.cts +2 -2
  38. package/dist/mod.d.ts +2 -2
  39. package/dist/mod.js +4 -4
  40. package/dist/nodeinfo/handler.test.mjs +1 -1
  41. package/dist/{owner-DBSV2TSl.mjs → owner-DRHNR5YO.mjs} +2 -2
  42. package/dist/{proof-tz91vdtN.mjs → proof-DLhLRv3m.mjs} +2 -2
  43. package/dist/{proof-CZDkoeWG.cjs → proof-DfrItHmh.cjs} +351 -3
  44. package/dist/{proof-z93OkIov.js → proof-SQ4cQs3A.js} +298 -4
  45. package/dist/{send-CNjG31rJ.mjs → send-C7tim5U9.mjs} +2 -2
  46. package/dist/sig/http.test.mjs +2 -2
  47. package/dist/sig/key.test.mjs +1 -1
  48. package/dist/sig/ld.test.mjs +558 -2
  49. package/dist/sig/mod.cjs +2 -2
  50. package/dist/sig/mod.js +2 -2
  51. package/dist/sig/owner.test.mjs +1 -1
  52. package/dist/sig/proof.test.mjs +1 -1
  53. package/dist/temporal-LL61Ddf2.mjs +95 -0
  54. package/dist/testing/mod.d.mts +48 -6
  55. package/dist/utils/docloader.test.mjs +2 -2
  56. package/dist/utils/mod.cjs +1 -1
  57. package/dist/utils/mod.js +1 -1
  58. package/package.json +5 -5
  59. package/dist/ld-D_u8mdpv.mjs +0 -279
  60. /package/dist/{retry-bMXBL97A.mjs → retry-v_sGLH1d.mjs} +0 -0
@@ -2,24 +2,25 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-CziVFvS6.mjs";
5
+ import { n as version, t as name } from "./deno-DMg4SgCb.mjs";
6
6
  import { t as formatAcceptSignature } from "./accept-CPkZzmGN.mjs";
7
- import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-D3TgMhcs.mjs";
8
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-BDCGf4Ac.mjs";
9
- import { t as getAuthenticatedDocumentLoader } from "./docloader-fI9DeYyB.mjs";
7
+ import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-BAQuZEU1.mjs";
8
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-C_edJspG.mjs";
9
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-Da15YRxG.mjs";
10
10
  import { n as kvCache } from "./kv-cache-DYsF2MhP.mjs";
11
- import { a as signJsonLd, i as hasSignatureLike, o as verifyJsonLd, r as detachSignature } from "./ld-D_u8mdpv.mjs";
12
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DBSV2TSl.mjs";
11
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-tusP_XxG.mjs";
12
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DRHNR5YO.mjs";
13
13
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-CNmZLixq.mjs";
14
- import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-tz91vdtN.mjs";
14
+ import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-DLhLRv3m.mjs";
15
15
  import { t as getNodeInfo } from "./client-D_1QpnWt.mjs";
16
16
  import { t as nodeInfoToJson } from "./types-J53Kw7so.mjs";
17
- import { t as FederationBuilderImpl } from "./builder-mqtih91o.mjs";
17
+ import { t as FederationBuilderImpl } from "./builder-CaVN56-q.mjs";
18
18
  import { t as buildCollectionSynchronizationHeader } from "./collection-D-HqUuA2.mjs";
19
19
  import { t as KvKeyCache } from "./keycache-EGATflN-.mjs";
20
20
  import { t as acceptsJsonLd } from "./negotiation-SQvQgUqe.mjs";
21
- import { t as createExponentialBackoffPolicy } from "./retry-bMXBL97A.mjs";
22
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CNjG31rJ.mjs";
21
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-LL61Ddf2.mjs";
22
+ import { t as createExponentialBackoffPolicy } from "./retry-v_sGLH1d.mjs";
23
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-C7tim5U9.mjs";
23
24
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
24
25
  import { lookupWebFinger } from "@fedify/webfinger";
25
26
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
@@ -154,7 +155,7 @@ function handleNodeInfoJrd(_request, context) {
154
155
  }
155
156
  //#endregion
156
157
  //#region src/federation/inbox.ts
157
- async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
158
+ async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
158
159
  const logger = getLogger([
159
160
  "fedify",
160
161
  "federation",
@@ -211,7 +212,9 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
211
212
  type: "inbox",
212
213
  id: crypto.randomUUID(),
213
214
  baseUrl: ctx.origin,
214
- activity: json,
215
+ activity: originalJson ?? json,
216
+ ...normalizedActivity == null ? {} : { normalizedActivity },
217
+ ...ldSignatureVerified == null ? {} : { ldSignatureVerified },
215
218
  identifier: recipient,
216
219
  attempt: 0,
217
220
  started: (/* @__PURE__ */ new Date()).toISOString(),
@@ -255,7 +258,8 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
255
258
  const { class: cls, listener } = dispatched;
256
259
  span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
257
260
  try {
258
- await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
261
+ const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
262
+ await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity?.id?.href, getTypeId(activity).href), activity);
259
263
  } catch (error) {
260
264
  try {
261
265
  await inboxErrorHandler?.(ctx, error);
@@ -292,6 +296,33 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
292
296
  }
293
297
  //#endregion
294
298
  //#region src/federation/handler.ts
299
+ const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
300
+ function isRemoteContextLoadingFailure$1(error) {
301
+ return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
302
+ }
303
+ function isPermanentRemoteContextError$1(error) {
304
+ if (!(error instanceof Error) || error.name !== "jsonld.InvalidUrl") return false;
305
+ const details = error.details;
306
+ if (details?.code === "invalid remote context") return true;
307
+ return isRemoteContextLoadingFailure$1(error) && typeof details?.url === "string" && !URL.canParse(details.url) && isClearlyMalformedContextReference(details.url);
308
+ }
309
+ function isInvalidJsonLdError(error) {
310
+ if (!(error instanceof Error)) return false;
311
+ const name = error.name;
312
+ return name === "UnsafeJsonLdError" || error instanceof InvalidContextReferenceError || isPermanentRemoteContextError$1(error) || name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure$1(error);
313
+ }
314
+ function isValidationTypeError(error) {
315
+ return error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:)/.test(error.message) || isInvalidUrlTypeError(error));
316
+ }
317
+ function isPermanentActivityParseError(error) {
318
+ return isInvalidJsonLdError(error) || isValidationTypeError(error);
319
+ }
320
+ function hasHttpSignatureHeaders(request) {
321
+ return request.headers.has("Signature") || request.headers.has("Signature-Input");
322
+ }
323
+ function hasObjectIntegrityProof(json) {
324
+ return typeof json === "object" && json != null && "proof" in json;
325
+ }
295
326
  /**
296
327
  * Handles an actor request.
297
328
  * @template TContextData The context data to pass to the context.
@@ -818,28 +849,104 @@ async function handleInboxInternal(request, parameters, span) {
818
849
  });
819
850
  }
820
851
  const keyCache = new KvKeyCache(kv, kvPrefixes.publicKey, ctx);
821
- let ldSigVerified;
822
- try {
823
- ldSigVerified = await verifyJsonLd(json, {
824
- contextLoader: ctx.contextLoader,
825
- documentLoader: ctx.documentLoader,
826
- keyCache,
827
- tracerProvider
852
+ const jsonWithoutSig = detachSignature(json);
853
+ const hasLdSignature = hasSignature(json);
854
+ const canAttemptAlternateAuthAfterLdSignatureFailure = skipSignatureVerification || hasHttpSignatureHeaders(request) || hasObjectIntegrityProof(jsonWithoutSig);
855
+ let deferredLdSignatureError = void 0;
856
+ const respondInvalidActivity = async (error) => {
857
+ logger.error("Failed to parse activity:\n{error}", {
858
+ recipient,
859
+ activity: json,
860
+ error
828
861
  });
829
- } catch (error) {
830
- if (error instanceof Error && error.name === "jsonld.SyntaxError") {
831
- logger.error("Failed to parse JSON-LD:\n{error}", {
862
+ try {
863
+ await inboxErrorHandler?.(ctx, error);
864
+ } catch (error) {
865
+ logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
866
+ error,
867
+ activity: json,
868
+ recipient
869
+ });
870
+ }
871
+ span.setStatus({
872
+ code: SpanStatusCode.ERROR,
873
+ message: `Failed to parse activity:\n${error}`
874
+ });
875
+ return new Response("Invalid activity.", {
876
+ status: 400,
877
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
878
+ });
879
+ };
880
+ let compactedJson = json;
881
+ let compactedJsonWithoutSig = jsonWithoutSig;
882
+ let ldSigVerified = false;
883
+ if (hasLdSignature) {
884
+ try {
885
+ compactedJson = await compactJsonLd(json, ctx.contextLoader);
886
+ } catch (error) {
887
+ if (isInvalidJsonLdError(error)) {
888
+ logger.error("Failed to parse JSON-LD:\n{error}", {
889
+ recipient,
890
+ error
891
+ });
892
+ return new Response("Invalid JSON-LD.", {
893
+ status: 400,
894
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
895
+ });
896
+ }
897
+ if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
898
+ if (!skipSignatureVerification) deferredLdSignatureError = error;
899
+ logger.debug("Failed to normalize JSON-LD for Linked Data Signatures; deferring to another authentication path only if it verifies:\n{error}", {
832
900
  recipient,
833
901
  error
834
902
  });
835
- return new Response("Invalid JSON-LD.", {
836
- status: 400,
837
- headers: { "Content-Type": "text/plain; charset=utf-8" }
838
- });
839
903
  }
840
- ldSigVerified = false;
904
+ if (compactedJson !== json) {
905
+ compactedJsonWithoutSig = detachSignature(compactedJson);
906
+ try {
907
+ ldSigVerified = await verifyCompactJsonLd(compactedJson, {
908
+ contextLoader: ctx.contextLoader,
909
+ documentLoader: ctx.documentLoader,
910
+ keyCache,
911
+ tracerProvider
912
+ });
913
+ } catch (error) {
914
+ if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
915
+ if (isInvalidJsonLdError(error)) {
916
+ logger.error("Failed to parse JSON-LD:\n{error}", {
917
+ recipient,
918
+ error
919
+ });
920
+ return new Response("Invalid JSON-LD.", {
921
+ status: 400,
922
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
923
+ });
924
+ }
925
+ if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
926
+ if (!skipSignatureVerification) try {
927
+ await Object$1.fromJsonLd(compactedJson, {
928
+ contextLoader: getNormalizationContextLoader(ctx.contextLoader),
929
+ documentLoader: ctx.documentLoader,
930
+ tracerProvider
931
+ });
932
+ } catch (parseError) {
933
+ if (parseError instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(parseError);
934
+ if (isInvalidJsonLdError(parseError)) {
935
+ logger.error("Failed to parse JSON-LD:\n{error}", {
936
+ recipient,
937
+ error: parseError
938
+ });
939
+ return new Response("Invalid JSON-LD.", {
940
+ status: 400,
941
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
942
+ });
943
+ }
944
+ deferredLdSignatureError = parseError;
945
+ }
946
+ ldSigVerified = false;
947
+ }
948
+ }
841
949
  }
842
- const jsonWithoutSig = detachSignature(json);
843
950
  let activity = null;
844
951
  let activityVerified = false;
845
952
  if (ldSigVerified) {
@@ -847,7 +954,16 @@ async function handleInboxInternal(request, parameters, span) {
847
954
  recipient,
848
955
  json
849
956
  });
850
- activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
957
+ try {
958
+ activity = await Activity.fromJsonLd(compactedJsonWithoutSig, {
959
+ ...ctx,
960
+ contextLoader: getNormalizationContextLoader(ctx.contextLoader)
961
+ });
962
+ } catch (error) {
963
+ if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
964
+ if (!isPermanentActivityParseError(error)) throw error;
965
+ return await respondInvalidActivity(error);
966
+ }
851
967
  activityVerified = true;
852
968
  } else {
853
969
  logger.debug("Linked Data Signatures are not verified.", {
@@ -856,12 +972,21 @@ async function handleInboxInternal(request, parameters, span) {
856
972
  });
857
973
  try {
858
974
  activity = await verifyObject(Activity, jsonWithoutSig, {
859
- contextLoader: ctx.contextLoader,
975
+ contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
860
976
  documentLoader: ctx.documentLoader,
861
977
  keyCache,
862
978
  tracerProvider
863
979
  });
864
980
  } catch (error) {
981
+ if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(jsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
982
+ if (deferredLdSignatureError != null) {
983
+ logger.debug("Object Integrity Proof fallback did not supersede a deferred Linked Data Signature failure:\n{error}", {
984
+ recipient,
985
+ error
986
+ });
987
+ activity = null;
988
+ }
989
+ if (!isPermanentActivityParseError(error)) throw error;
865
990
  logger.error("Failed to parse activity:\n{error}", {
866
991
  recipient,
867
992
  activity: json,
@@ -909,6 +1034,7 @@ async function handleInboxInternal(request, parameters, span) {
909
1034
  tracerProvider
910
1035
  });
911
1036
  if (verification.verified === false) {
1037
+ if (deferredLdSignatureError != null) throw deferredLdSignatureError;
912
1038
  const reason = verification.reason;
913
1039
  logger.error("Failed to verify the request's HTTP Signatures.", {
914
1040
  recipient,
@@ -984,7 +1110,15 @@ async function handleInboxInternal(request, parameters, span) {
984
1110
  }
985
1111
  httpSigKey = verification.key;
986
1112
  }
987
- activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
1113
+ try {
1114
+ activity = await Activity.fromJsonLd(jsonWithoutSig, {
1115
+ ...ctx,
1116
+ contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader)
1117
+ });
1118
+ } catch (error) {
1119
+ if (!isPermanentActivityParseError(error)) throw error;
1120
+ return await respondInvalidActivity(error);
1121
+ }
988
1122
  }
989
1123
  if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
990
1124
  span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
@@ -996,6 +1130,7 @@ async function handleInboxInternal(request, parameters, span) {
996
1130
  "http_signatures.key_id": httpSigKey?.id?.href ?? ""
997
1131
  });
998
1132
  if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
1133
+ if (deferredLdSignatureError != null) throw deferredLdSignatureError;
999
1134
  logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
1000
1135
  activity: json,
1001
1136
  recipient,
@@ -1020,10 +1155,14 @@ async function handleInboxInternal(request, parameters, span) {
1020
1155
  const routeResult = await routeActivity({
1021
1156
  context: ctx,
1022
1157
  json,
1158
+ originalJson: json,
1159
+ normalizedActivity: hasLdSignature && compactedJson !== json ? compactedJson : void 0,
1160
+ ldSignatureVerified: hasLdSignature ? ldSigVerified : void 0,
1023
1161
  activity,
1024
1162
  recipient,
1025
1163
  inboxListeners,
1026
1164
  inboxContextFactory,
1165
+ listenerInboxContextFactory: ldSigVerified ? inboxContextFactory[rawInboxContextFactorySymbol] : void 0,
1027
1166
  inboxErrorHandler,
1028
1167
  kv,
1029
1168
  kvPrefixes,
@@ -1689,6 +1828,18 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
1689
1828
  }
1690
1829
  //#endregion
1691
1830
  //#region src/federation/middleware.ts
1831
+ function isRemoteContextLoadingFailure(error) {
1832
+ return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
1833
+ }
1834
+ function isPermanentRemoteContextError(error) {
1835
+ if (!(error instanceof Error) || error.name !== "jsonld.InvalidUrl") return false;
1836
+ const details = error.details;
1837
+ if (details?.code === "invalid remote context") return true;
1838
+ return isRemoteContextLoadingFailure(error) && typeof details?.url === "string" && !URL.canParse(details.url) && isClearlyMalformedContextReference(details.url);
1839
+ }
1840
+ function isPermanentInboxParseError(error) {
1841
+ return error instanceof Error && (error.name === "UnsafeJsonLdError" || error instanceof InvalidContextReferenceError || isPermanentRemoteContextError(error) || error.name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure(error)) || error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:)/.test(error.message) || isInvalidUrlTypeError(error));
1842
+ }
1692
1843
  /**
1693
1844
  * Create a new {@link Federation} instance.
1694
1845
  * @param parameters Parameters for initializing the instance.
@@ -2070,7 +2221,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2070
2221
  }
2071
2222
  logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
2072
2223
  }
2073
- async #listenInboxMessage(ctxData, message, span) {
2224
+ async #listenInboxMessage(ctxData, message, _span) {
2074
2225
  const logger = getLogger([
2075
2226
  "fedify",
2076
2227
  "federation",
@@ -2083,60 +2234,28 @@ var FederationImpl = class extends FederationBuilderImpl {
2083
2234
  const identity = await this.sharedInboxKeyDispatcher(context);
2084
2235
  if (identity != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
2085
2236
  }
2086
- const activity = await Activity.fromJsonLd(message.activity, context);
2087
- span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
2088
- if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
2089
- const cacheKey = activity.id == null ? null : [
2090
- ...this.kvPrefixes.activityIdempotence,
2091
- context.origin,
2092
- activity.id.href
2093
- ];
2094
- if (cacheKey != null) {
2095
- if (await this.kv.get(cacheKey) === true) {
2096
- logger.debug("Activity {activityId} has already been processed.", {
2097
- activityId: activity.id?.href,
2098
- activity: message.activity,
2099
- recipient: message.identifier
2100
- });
2101
- return;
2102
- }
2103
- }
2104
2237
  await this._getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
2105
- const dispatched = this.inboxListeners?.dispatchWithClass(activity);
2106
- if (dispatched == null) {
2107
- logger.error("Unsupported activity type:\n{activity}", {
2108
- activityId: activity.id?.href,
2109
- activity: message.activity,
2110
- recipient: message.identifier,
2111
- trial: message.attempt
2112
- });
2113
- span.setStatus({
2114
- code: SpanStatusCode.ERROR,
2115
- message: `Unsupported activity type: ${getTypeId(activity).href}`
2116
- });
2117
- span.end();
2118
- return;
2119
- }
2120
- const { class: cls, listener } = dispatched;
2121
- span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
2122
- try {
2123
- await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, getTypeId(activity).href), activity);
2124
- } catch (error) {
2238
+ let activity = null;
2239
+ let cacheKey = null;
2240
+ const reportInboxError = async (error) => {
2125
2241
  try {
2126
2242
  await this.inboxErrorHandler?.(context, error);
2127
2243
  } catch (error) {
2128
2244
  logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
2129
2245
  error,
2130
2246
  trial: message.attempt,
2131
- activityId: activity.id?.href,
2247
+ activityId: activity?.id?.href,
2132
2248
  activity: message.activity,
2133
2249
  recipient: message.identifier
2134
2250
  });
2135
2251
  }
2252
+ };
2253
+ const handleRetriableFailure = async (error) => {
2254
+ await reportInboxError(error);
2136
2255
  if (this.inboxQueue?.nativeRetrial) {
2137
2256
  logger.error("Failed to process the incoming activity {activityId}; backend will handle retry:\n{error}", {
2138
2257
  error,
2139
- activityId: activity.id?.href,
2258
+ activityId: activity?.id?.href,
2140
2259
  activity: message.activity,
2141
2260
  recipient: message.identifier
2142
2261
  });
@@ -2155,17 +2274,25 @@ var FederationImpl = class extends FederationBuilderImpl {
2155
2274
  logger.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
2156
2275
  error,
2157
2276
  attempt: message.attempt,
2158
- activityId: activity.id?.href,
2277
+ activityId: activity?.id?.href,
2159
2278
  activity: message.activity,
2160
2279
  recipient: message.identifier
2161
2280
  });
2281
+ if (this.inboxQueue == null) {
2282
+ span.setStatus({
2283
+ code: SpanStatusCode.ERROR,
2284
+ message: String(error)
2285
+ });
2286
+ span.end();
2287
+ throw error;
2288
+ }
2162
2289
  await this.inboxQueue?.enqueue({
2163
2290
  ...message,
2164
2291
  attempt: message.attempt + 1
2165
2292
  }, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
2166
2293
  } else logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
2167
2294
  error,
2168
- activityId: activity.id?.href,
2295
+ activityId: activity?.id?.href,
2169
2296
  activity: message.activity,
2170
2297
  recipient: message.identifier
2171
2298
  });
@@ -2174,11 +2301,108 @@ var FederationImpl = class extends FederationBuilderImpl {
2174
2301
  message: String(error)
2175
2302
  });
2176
2303
  span.end();
2304
+ };
2305
+ let dispatched;
2306
+ let parseInput = void 0;
2307
+ let parseContextLoader = context.contextLoader;
2308
+ try {
2309
+ const hasSignatureField = hasSignature(message.activity);
2310
+ const shouldParseFromNormalizedSignedPayload = message.ldSignatureVerified === true || message.normalizedActivity != null || message.ldSignatureVerified == null && hasSignatureField;
2311
+ const parseContext = hasSignatureField ? {
2312
+ ...context,
2313
+ contextLoader: getNormalizationContextLoader(context.contextLoader)
2314
+ } : {
2315
+ ...context,
2316
+ contextLoader: wrapContextLoaderForJsonLd(context.contextLoader)
2317
+ };
2318
+ parseContextLoader = parseContext.contextLoader;
2319
+ let normalizedActivity;
2320
+ if (shouldParseFromNormalizedSignedPayload) {
2321
+ normalizedActivity = message.normalizedActivity ?? await compactJsonLd(message.activity, context.contextLoader);
2322
+ assertSafeJsonLd(normalizedActivity);
2323
+ }
2324
+ parseInput = shouldParseFromNormalizedSignedPayload ? detachSignature(normalizedActivity) : hasSignatureField ? detachSignature(message.activity) : message.activity;
2325
+ activity = await Activity.fromJsonLd(parseInput, parseContext);
2326
+ span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
2327
+ if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
2328
+ cacheKey = activity.id == null ? null : [
2329
+ ...this.kvPrefixes.activityIdempotence,
2330
+ context.origin,
2331
+ activity.id.href
2332
+ ];
2333
+ if (cacheKey != null) {
2334
+ if (await this.kv.get(cacheKey) === true) {
2335
+ logger.debug("Activity {activityId} has already been processed.", {
2336
+ activityId: activity.id?.href,
2337
+ activity: message.activity,
2338
+ recipient: message.identifier
2339
+ });
2340
+ span.end();
2341
+ return;
2342
+ }
2343
+ }
2344
+ dispatched = this.inboxListeners?.dispatchWithClass(activity);
2345
+ } catch (error) {
2346
+ if (activity == null && error instanceof RangeError && await hasMalformedKnownTemporalLiteral(parseInput, parseContextLoader)) {
2347
+ await reportInboxError(error);
2348
+ logger.error("Failed to parse the queued incoming activity {activityId}:\n{error}", {
2349
+ error,
2350
+ trial: message.attempt,
2351
+ activityId: null,
2352
+ activity: message.activity,
2353
+ recipient: message.identifier
2354
+ });
2355
+ span.setStatus({
2356
+ code: SpanStatusCode.ERROR,
2357
+ message: String(error)
2358
+ });
2359
+ span.end();
2360
+ return;
2361
+ }
2362
+ if (isPermanentInboxParseError(error)) {
2363
+ await reportInboxError(error);
2364
+ logger.error("Failed to parse the queued incoming activity {activityId}:\n{error}", {
2365
+ error,
2366
+ trial: message.attempt,
2367
+ activityId: activity?.id?.href,
2368
+ activity: message.activity,
2369
+ recipient: message.identifier
2370
+ });
2371
+ span.setStatus({
2372
+ code: SpanStatusCode.ERROR,
2373
+ message: String(error)
2374
+ });
2375
+ span.end();
2376
+ return;
2377
+ }
2378
+ await handleRetriableFailure(error);
2379
+ return;
2380
+ }
2381
+ if (dispatched == null) {
2382
+ logger.error("Unsupported activity type:\n{activity}", {
2383
+ activityId: activity.id?.href,
2384
+ activity: message.activity,
2385
+ recipient: message.identifier,
2386
+ trial: message.attempt
2387
+ });
2388
+ span.setStatus({
2389
+ code: SpanStatusCode.ERROR,
2390
+ message: `Unsupported activity type: ${getTypeId(activity).href}`
2391
+ });
2392
+ span.end();
2393
+ return;
2394
+ }
2395
+ const { class: cls, listener } = dispatched;
2396
+ span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
2397
+ try {
2398
+ await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, getTypeId(activity).href), activity);
2399
+ } catch (error) {
2400
+ await handleRetriableFailure(error);
2177
2401
  return;
2178
2402
  }
2179
2403
  if (cacheKey != null) await this.kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
2180
2404
  logger.info("Activity {activityId} has been processed.", {
2181
- activityId: activity.id?.href,
2405
+ activityId: activity?.id?.href,
2182
2406
  activity: message.activity,
2183
2407
  recipient: message.identifier
2184
2408
  });
@@ -2548,16 +2772,18 @@ var FederationImpl = class extends FederationBuilderImpl {
2548
2772
  onNotFound
2549
2773
  });
2550
2774
  context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier: route.values.identifier }) });
2551
- case "sharedInbox":
2775
+ case "sharedInbox": {
2552
2776
  if (routeName !== "inbox" && this.sharedInboxKeyDispatcher != null) {
2553
2777
  const identity = await this.sharedInboxKeyDispatcher(context);
2554
2778
  if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
2555
2779
  }
2556
2780
  if (!this.manuallyStartQueue) this._startQueueInternal(contextData);
2781
+ const inboxContextFactory = context.toInboxContext.bind(context);
2782
+ inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
2557
2783
  return await handleInbox(request, {
2558
2784
  recipient: route.values.identifier ?? null,
2559
2785
  context,
2560
- inboxContextFactory: context.toInboxContext.bind(context),
2786
+ inboxContextFactory,
2561
2787
  kv: this.kv,
2562
2788
  kvPrefixes: this.kvPrefixes,
2563
2789
  queue: this.inboxQueue,
@@ -2572,6 +2798,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2572
2798
  tracerProvider: this.tracerProvider,
2573
2799
  idempotencyStrategy: this.idempotencyStrategy
2574
2800
  });
2801
+ }
2575
2802
  case "following": return await handleCollection(request, {
2576
2803
  name: "following",
2577
2804
  identifier: route.values.identifier,
@@ -3272,6 +3499,7 @@ var ContextImpl = class ContextImpl {
3272
3499
  const routeResult = await routeActivity({
3273
3500
  context: this,
3274
3501
  json,
3502
+ ldSignatureVerified: false,
3275
3503
  activity,
3276
3504
  recipient,
3277
3505
  inboxListeners: this.federation.inboxListeners,
@@ -3552,6 +3780,14 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
3552
3780
  }
3553
3781
  var InboxContextImpl = class InboxContextImpl extends ContextImpl {
3554
3782
  recipient;
3783
+ /**
3784
+ * The original received activity payload.
3785
+ *
3786
+ * Fedify may normalize a Linked Data Signature payload internally for safe
3787
+ * parsing, but forwarding must keep the sender's payload unchanged so
3788
+ * third-party signatures/proofs remain intact.
3789
+ * @internal
3790
+ */
3555
3791
  activity;
3556
3792
  activityId;
3557
3793
  activityType;
@@ -1,4 +1,4 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
- const require_middleware = require("./middleware-xR9KxICq.cjs");
3
+ const require_middleware = require("./middleware-0V-9qj7m.cjs");
4
4
  exports.FederationImpl = require_middleware.FederationImpl;
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as FederationImpl } from "./middleware-2gmMVy8b.mjs";
4
+ import { n as FederationImpl } from "./middleware-D9k0Knum.mjs";
5
5
  export { FederationImpl };
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { $ as ActorAliasMapper, St as WebFingerLinksDispatcher, et as ActorDispatcher, l as RequestContext, tt as ActorHandleMapper } from "./context-BPMgyX7m.js";
2
+ import { $ as ActorAliasMapper, St as WebFingerLinksDispatcher, et as ActorDispatcher, l as RequestContext, tt as ActorHandleMapper } from "./context-BU-1O90h.js";
3
3
  import { Span, Tracer } from "@opentelemetry/api";
4
4
 
5
5
  //#region src/federation/webfinger.d.ts
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { $ as ActorAliasMapper, St as WebFingerLinksDispatcher, et as ActorDispatcher, l as RequestContext, tt as ActorHandleMapper } from "./context-DwkhwUX9.cjs";
2
+ import { $ as ActorAliasMapper, St as WebFingerLinksDispatcher, et as ActorDispatcher, l as RequestContext, tt as ActorHandleMapper } from "./context-DVA8wHZ0.cjs";
3
3
  import { Span, Tracer } from "@opentelemetry/api";
4
4
 
5
5
  //#region src/federation/webfinger.d.ts
package/dist/mod.cjs CHANGED
@@ -4,11 +4,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("./chunk-DDcVe30Y.cjs");
5
5
  const require_transformers = require("./transformers-NeAONrAq.cjs");
6
6
  require("./compat/mod.cjs");
7
- const require_http = require("./http-kPc328Pc.cjs");
8
- const require_middleware = require("./middleware-xR9KxICq.cjs");
9
- const require_proof = require("./proof-CZDkoeWG.cjs");
7
+ const require_http = require("./http-Cl0Q2bUO.cjs");
8
+ const require_middleware = require("./middleware-0V-9qj7m.cjs");
9
+ const require_proof = require("./proof-DfrItHmh.cjs");
10
10
  const require_types = require("./types-KC4QAoxe.cjs");
11
- const require_kv_cache = require("./kv-cache-zxW74Wfd.cjs");
11
+ const require_kv_cache = require("./kv-cache-DmGi6uC-.cjs");
12
12
  const require_federation_mod = require("./federation/mod.cjs");
13
13
  require("./nodeinfo/mod.cjs");
14
14
  require("./runtime/mod.cjs");
package/dist/mod.d.cts CHANGED
@@ -2,10 +2,10 @@
2
2
  import { a as InboundService, c as OutboundService, d as Software, f as Usage, i as parseNodeInfo, l as Protocol, n as ParseNodeInfoOptions, o as JsonValue, p as nodeInfoToJson, r as getNodeInfo, s as NodeInfo, t as GetNodeInfoOptions, u as Services } from "./client-CAM_bQXx.cjs";
3
3
  import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "./http-C87EWkO0.cjs";
4
4
  import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "./owner-DEvZuyOE.cjs";
5
- import { $ as ActorAliasMapper, A as FederationKvPrefixes, B as Router, C as IdempotencyKeyCallback, Ct as SendActivityError, D as ObjectCallbackSetters, Dt as digest, E as InboxListenerSetters, Et as buildCollectionSynchronizationHeader, F as RetryContext, G as respondWithObject, H as RouterOptions, I as RetryPolicy, J as InProcessMessageQueueOptions, K as respondWithObjectIfAcceptable, L as createExponentialBackoffPolicy, M as FederationQueueOptions, N as createFederation, O as OutboxListenerSetters, Ot as ActivityTransformer, P as CreateExponentialBackoffPolicyOptions, Q as ParallelMessageQueue, R as Message, S as FederationStartQueueOptions, St as WebFingerLinksDispatcher, T as InboxChallengePolicy, Tt as PageItems, U as RouterRouteResult, V as RouterError, W as RespondWithObjectOptions, X as MessageQueueEnqueueOptions, Y as MessageQueue, Z as MessageQueueListenOptions, _ as Federatable, _t as OutboxListenerErrorHandler, a as GetSignedKeyOptions, at as CollectionCursor, b as FederationFetchOptions, bt as UnverifiedActivityHandler, c as ParseUriResult, ct as CustomCollectionCursor, d as SendActivityOptions, dt as InboxListener, et as ActorDispatcher, f as SendActivityOptionsForCollection, ft as NodeInfoDispatcher, g as CustomCollectionCallbackSetters, gt as OutboxListener, h as ConstructorWithTypeId, ht as OutboxErrorHandler, i as GetActorOptions, it as CollectionCounter, j as FederationOrigin, k as Rfc6570Expression, l as RequestContext, lt as CustomCollectionDispatcher, m as CollectionCallbackSetters, mt as ObjectDispatcher, n as Context, nt as ActorKeyPairsDispatcher, o as InboxContext, ot as CollectionDispatcher, p as ActorCallbackSetters, pt as ObjectAuthorizePredicate, q as InProcessMessageQueue, r as ForwardActivityOptions, rt as AuthorizePredicate, s as OutboxContext, st as CustomCollectionCounter, t as ActorKeyPair, tt as ActorHandleMapper, u as RouteActivityOptions, ut as InboxErrorHandler, v as Federation, vt as OutboxPermanentFailureHandler, w as IdempotencyStrategy, wt as SenderKeyPair, x as FederationOptions, xt as UnverifiedActivityReason, y as FederationBuilder, yt as SharedInboxKeyDispatcher, z as createFederationBuilder } from "./context-DwkhwUX9.cjs";
5
+ import { $ as ActorAliasMapper, A as FederationKvPrefixes, B as Router, C as IdempotencyKeyCallback, Ct as SendActivityError, D as ObjectCallbackSetters, Dt as digest, E as InboxListenerSetters, Et as buildCollectionSynchronizationHeader, F as RetryContext, G as respondWithObject, H as RouterOptions, I as RetryPolicy, J as InProcessMessageQueueOptions, K as respondWithObjectIfAcceptable, L as createExponentialBackoffPolicy, M as FederationQueueOptions, N as createFederation, O as OutboxListenerSetters, Ot as ActivityTransformer, P as CreateExponentialBackoffPolicyOptions, Q as ParallelMessageQueue, R as Message, S as FederationStartQueueOptions, St as WebFingerLinksDispatcher, T as InboxChallengePolicy, Tt as PageItems, U as RouterRouteResult, V as RouterError, W as RespondWithObjectOptions, X as MessageQueueEnqueueOptions, Y as MessageQueue, Z as MessageQueueListenOptions, _ as Federatable, _t as OutboxListenerErrorHandler, a as GetSignedKeyOptions, at as CollectionCursor, b as FederationFetchOptions, bt as UnverifiedActivityHandler, c as ParseUriResult, ct as CustomCollectionCursor, d as SendActivityOptions, dt as InboxListener, et as ActorDispatcher, f as SendActivityOptionsForCollection, ft as NodeInfoDispatcher, g as CustomCollectionCallbackSetters, gt as OutboxListener, h as ConstructorWithTypeId, ht as OutboxErrorHandler, i as GetActorOptions, it as CollectionCounter, j as FederationOrigin, k as Rfc6570Expression, l as RequestContext, lt as CustomCollectionDispatcher, m as CollectionCallbackSetters, mt as ObjectDispatcher, n as Context, nt as ActorKeyPairsDispatcher, o as InboxContext, ot as CollectionDispatcher, p as ActorCallbackSetters, pt as ObjectAuthorizePredicate, q as InProcessMessageQueue, r as ForwardActivityOptions, rt as AuthorizePredicate, s as OutboxContext, st as CustomCollectionCounter, t as ActorKeyPair, tt as ActorHandleMapper, u as RouteActivityOptions, ut as InboxErrorHandler, v as Federation, vt as OutboxPermanentFailureHandler, w as IdempotencyStrategy, wt as SenderKeyPair, x as FederationOptions, xt as UnverifiedActivityReason, y as FederationBuilder, yt as SharedInboxKeyDispatcher, z as createFederationBuilder } from "./context-DVA8wHZ0.cjs";
6
6
  import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-gJ8LYbxX.cjs";
7
7
  import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.cjs";
8
- import { n as handleWebFinger, t as WebFingerHandlerParameters } from "./mod-Bi6WOdti.cjs";
8
+ import { n as handleWebFinger, t as WebFingerHandlerParameters } from "./mod-q-NFLW6B.cjs";
9
9
  import { _ as hasSignatureLike, a as createProof, b as verifySignature, c as verifyObject, d as SignJsonLdOptions, f as VerifyJsonLdOptions, g as detachSignature, h as createSignature, i as VerifyProofOptions, l as verifyProof, m as attachSignature, n as SignObjectOptions, o as hasProofLike, p as VerifySignatureOptions, r as VerifyObjectOptions, s as signObject, t as CreateProofOptions, u as CreateSignatureOptions, v as signJsonLd, y as verifyJsonLd } from "./mod-DXY9JF28.cjs";
10
10
  import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-B0rWmfW5.cjs";
11
11
  export * from "@fedify/vocab-runtime";