@fedify/fedify 2.3.0-dev.1137 → 2.3.0-dev.1150

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 (68) hide show
  1. package/dist/{builder-BCkBXxky.mjs → builder-Bjm1Jq9n.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-DI2gRbyN.d.cts → context-CRXCkTM6.d.cts} +48 -6
  6. package/dist/{context-DCtsSHDv.d.ts → context-MgCh7YGu.d.ts} +48 -6
  7. package/dist/{deno-B_9yJW3w.mjs → deno-CKFE6Uya.mjs} +1 -1
  8. package/dist/{docloader-BT89tyFr.mjs → docloader-B-ZE1cZf.mjs} +2 -2
  9. package/dist/federation/builder.test.mjs +1 -1
  10. package/dist/federation/handler.test.mjs +1363 -44
  11. package/dist/federation/idempotency.test.mjs +2 -2
  12. package/dist/federation/metrics.test.mjs +60 -1
  13. package/dist/federation/middleware.test.mjs +1667 -163
  14. package/dist/federation/mod.cjs +1 -1
  15. package/dist/federation/mod.d.cts +2 -2
  16. package/dist/federation/mod.d.ts +2 -2
  17. package/dist/federation/mod.js +1 -1
  18. package/dist/federation/retry.test.mjs +1 -1
  19. package/dist/federation/send.test.mjs +8 -8
  20. package/dist/federation/temporal.test.d.mts +2 -0
  21. package/dist/federation/temporal.test.mjs +71 -0
  22. package/dist/federation/webfinger.test.mjs +147 -2
  23. package/dist/{getMachineId-bsd-etIyxDet.mjs → getMachineId-bsd-BY01PL1n.mjs} +1 -1
  24. package/dist/{getMachineId-darwin-D23zTf4g.mjs → getMachineId-darwin-Dr1gkBkp.mjs} +1 -1
  25. package/dist/{getMachineId-win-Dpap6v5i.mjs → getMachineId-win-QEYwcJiy.mjs} +1 -1
  26. package/dist/{http-CWoeyogl.cjs → http-DQYEA7AZ.cjs} +53 -1
  27. package/dist/{http-CToqG5ap.js → http-WbS1gKzr.js} +48 -2
  28. package/dist/{http-Cyx5SNuu.mjs → http-vHCgbhTg.mjs} +3 -3
  29. package/dist/{key-CkkMJBjF.mjs → key-N0zP_oJA.mjs} +2 -2
  30. package/dist/{kv-cache-CuCn2xvM.js → kv-cache-DM2O-Yjy.js} +1 -1
  31. package/dist/{kv-cache-DuEwFYcN.cjs → kv-cache-Dsg_bi4N.cjs} +1 -1
  32. package/dist/{kv-cache-VHFP42vY.mjs → kv-cache-GXXZEemD.mjs} +1 -1
  33. package/dist/{ld-k8yqD2a-.mjs → ld-BwKhquPx.mjs} +302 -6
  34. package/dist/{metrics-iRBg8jTk.mjs → metrics-7Vy9FvEw.mjs} +48 -2
  35. package/dist/{middleware-D7FrhN9q.js → middleware-BscgvU-m.js} +496 -115
  36. package/dist/{middleware-BWLUrbS9.cjs → middleware-D_iXrYHJ.cjs} +497 -115
  37. package/dist/{middleware-CztxpARM.mjs → middleware-Db1_qAFG.mjs} +1 -1
  38. package/dist/{middleware-DQEgdr83.mjs → middleware-ZuUcO0t1.mjs} +416 -124
  39. package/dist/{mod-C504qevA.d.cts → mod-C7HOzGqH.d.cts} +11 -2
  40. package/dist/{mod-wYfuXeDE.d.ts → mod-CpQHB3Ys.d.ts} +11 -2
  41. package/dist/mod.cjs +4 -4
  42. package/dist/mod.d.cts +2 -2
  43. package/dist/mod.d.ts +2 -2
  44. package/dist/mod.js +4 -4
  45. package/dist/nodeinfo/handler.test.mjs +1 -1
  46. package/dist/{owner-nmXdvXpc.mjs → owner-FD0H_vpj.mjs} +2 -2
  47. package/dist/{proof-CcsIJLTn.cjs → proof-CYK8T8IS.cjs} +353 -3
  48. package/dist/{proof-NRmtrTDu.js → proof-I3EokKN-.js} +300 -4
  49. package/dist/{proof-DpwO1T4S.mjs → proof-V_lafPmA.mjs} +3 -3
  50. package/dist/{send-DvX2tYyZ.mjs → send-Cc2_10tF.mjs} +3 -3
  51. package/dist/sig/http.test.mjs +2 -2
  52. package/dist/sig/key.test.mjs +1 -1
  53. package/dist/sig/ld.test.mjs +558 -2
  54. package/dist/sig/mod.cjs +2 -2
  55. package/dist/sig/mod.js +2 -2
  56. package/dist/sig/owner.test.mjs +1 -1
  57. package/dist/sig/proof.test.mjs +1 -1
  58. package/dist/temporal-BkmBfs__.mjs +95 -0
  59. package/dist/testing/mod.d.mts +48 -6
  60. package/dist/utils/docloader.test.mjs +2 -2
  61. package/dist/utils/kv-cache.test.mjs +1 -1
  62. package/dist/utils/mod.cjs +1 -1
  63. package/dist/utils/mod.js +1 -1
  64. package/package.json +6 -6
  65. /package/dist/{execAsync-DCBrgFiV.mjs → execAsync-Dxb7rNf3.mjs} +0 -0
  66. /package/dist/{getMachineId-linux-ObI47Hql.mjs → getMachineId-linux-Bbhofx-s.mjs} +0 -0
  67. /package/dist/{getMachineId-unsupported-Ddu-PFeh.mjs → getMachineId-unsupported-dIOte2Ct.mjs} +0 -0
  68. /package/dist/{retry-v_sGLH1d.mjs → retry-_VvV0h9f.mjs} +0 -0
@@ -1,25 +1,26 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-B_9yJW3w.mjs";
5
- import { a as instrumentDocumentLoader, d as recordInboxActivity, i as getRemoteHost, m as recordOutboxEnqueue, n as getDurationMs, o as isAbortError, p as recordOutboxActivity, r as getFederationMetrics, u as recordFanoutRecipients } from "./metrics-iRBg8jTk.mjs";
4
+ import { n as version, t as name } from "./deno-CKFE6Uya.mjs";
5
+ import { a as instrumentDocumentLoader, d as recordInboxActivity, h as recordWebFingerHandle, i as getRemoteHost, m as recordOutboxEnqueue, n as getDurationMs, o as isAbortError, p as recordOutboxActivity, r as getFederationMetrics, u as recordFanoutRecipients } from "./metrics-7Vy9FvEw.mjs";
6
6
  import { t as formatAcceptSignature } from "./accept-CceiKpCy.mjs";
7
- import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-CkkMJBjF.mjs";
8
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-Cyx5SNuu.mjs";
9
- import { t as getAuthenticatedDocumentLoader } from "./docloader-BT89tyFr.mjs";
10
- import { n as kvCache } from "./kv-cache-VHFP42vY.mjs";
11
- import { a as signJsonLd, i as hasSignatureLike, o as verifyJsonLd, r as detachSignature } from "./ld-k8yqD2a-.mjs";
12
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-nmXdvXpc.mjs";
7
+ import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-N0zP_oJA.mjs";
8
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-vHCgbhTg.mjs";
9
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-B-ZE1cZf.mjs";
10
+ import { n as kvCache } from "./kv-cache-GXXZEemD.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-BwKhquPx.mjs";
12
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-FD0H_vpj.mjs";
13
13
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-BgFLCJQ_.mjs";
14
- import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-DpwO1T4S.mjs";
14
+ import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-V_lafPmA.mjs";
15
15
  import { t as getNodeInfo } from "./client-B_A6mfn3.mjs";
16
16
  import { t as nodeInfoToJson } from "./types-BFowWFTT.mjs";
17
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-BCkBXxky.mjs";
17
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-Bjm1Jq9n.mjs";
18
18
  import { t as buildCollectionSynchronizationHeader } from "./collection-CA3V5zyK.mjs";
19
19
  import { t as KvKeyCache } from "./keycache-BYMd8q7F.mjs";
20
20
  import { t as acceptsJsonLd } from "./negotiation-CDW-_gUU.mjs";
21
- import { t as createExponentialBackoffPolicy } from "./retry-v_sGLH1d.mjs";
22
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-DvX2tYyZ.mjs";
21
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-BkmBfs__.mjs";
22
+ import { t as createExponentialBackoffPolicy } from "./retry-_VvV0h9f.mjs";
23
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-Cc2_10tF.mjs";
23
24
  import { getLogger, withContext } from "@logtape/logtape";
24
25
  import { RouterError } from "@fedify/uri-template";
25
26
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -155,7 +156,7 @@ function handleNodeInfoJrd(_request, context) {
155
156
  }
156
157
  //#endregion
157
158
  //#region src/federation/inbox.ts
158
- async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
159
+ async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
159
160
  const logger = getLogger([
160
161
  "fedify",
161
162
  "federation",
@@ -214,7 +215,9 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
214
215
  type: "inbox",
215
216
  id: crypto.randomUUID(),
216
217
  baseUrl: ctx.origin,
217
- activity: json,
218
+ activity: originalJson ?? json,
219
+ ...normalizedActivity == null ? {} : { normalizedActivity },
220
+ ...ldSignatureVerified == null ? {} : { ldSignatureVerified },
218
221
  identifier: recipient,
219
222
  attempt: 0,
220
223
  started: (/* @__PURE__ */ new Date()).toISOString(),
@@ -268,7 +271,8 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
268
271
  const activityType = getTypeId(activity).href;
269
272
  const started = performance.now();
270
273
  try {
271
- await listener(inboxContextFactory(recipient, json, activity.id?.href, activityType), activity);
274
+ const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
275
+ await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
272
276
  } finally {
273
277
  getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
274
278
  }
@@ -310,6 +314,33 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
310
314
  }
311
315
  //#endregion
312
316
  //#region src/federation/handler.ts
317
+ const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
318
+ function isRemoteContextLoadingFailure$1(error) {
319
+ return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
320
+ }
321
+ function isPermanentRemoteContextError$1(error) {
322
+ if (!(error instanceof Error) || error.name !== "jsonld.InvalidUrl") return false;
323
+ const details = error.details;
324
+ if (details?.code === "invalid remote context") return true;
325
+ return isRemoteContextLoadingFailure$1(error) && typeof details?.url === "string" && !URL.canParse(details.url) && isClearlyMalformedContextReference(details.url);
326
+ }
327
+ function isInvalidJsonLdError(error) {
328
+ if (!(error instanceof Error)) return false;
329
+ const name = error.name;
330
+ return name === "UnsafeJsonLdError" || error instanceof InvalidContextReferenceError || isPermanentRemoteContextError$1(error) || name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure$1(error);
331
+ }
332
+ function isValidationTypeError(error) {
333
+ return error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:)/.test(error.message) || isInvalidUrlTypeError(error));
334
+ }
335
+ function isPermanentActivityParseError(error) {
336
+ return isInvalidJsonLdError(error) || isValidationTypeError(error);
337
+ }
338
+ function hasHttpSignatureHeaders(request) {
339
+ return request.headers.has("Signature") || request.headers.has("Signature-Input");
340
+ }
341
+ function hasObjectIntegrityProof(json) {
342
+ return typeof json === "object" && json != null && "proof" in json;
343
+ }
313
344
  /**
314
345
  * Handles an actor request.
315
346
  * @template TContextData The context data to pass to the context.
@@ -381,8 +412,8 @@ async function handleObject(request, { values, context, objectDispatcher, author
381
412
  * @param parameters The parameters for handling the collection.
382
413
  * @returns A promise that resolves to an HTTP response.
383
414
  */
384
- async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
385
- const spanName = name$2.trim().replace(/\s+/g, "_");
415
+ async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
416
+ const spanName = name$1.trim().replace(/\s+/g, "_");
386
417
  tracerProvider = tracerProvider ?? trace.getTracerProvider();
387
418
  const tracer = tracerProvider.getTracer(name, version);
388
419
  const cursor = new URL(request.url).searchParams.get("cursor");
@@ -424,7 +455,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
424
455
  collection = new OrderedCollection({
425
456
  id: baseUri,
426
457
  totalItems: totalItems == null ? null : Number(totalItems),
427
- items: filterCollectionItems(itemsOrResponse, name$2, filterPredicate)
458
+ items: filterCollectionItems(itemsOrResponse, name$1, filterPredicate)
428
459
  });
429
460
  } else {
430
461
  const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
@@ -445,7 +476,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
445
476
  } else {
446
477
  const uri = new URL(baseUri);
447
478
  uri.searchParams.set("cursor", cursor);
448
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
479
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
449
480
  kind: SpanKind.SERVER,
450
481
  attributes: {
451
482
  "activitypub.collection.id": uri.href,
@@ -489,7 +520,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
489
520
  id: uri,
490
521
  prev,
491
522
  next,
492
- items: filterCollectionItems(items, name$2, filterPredicate),
523
+ items: filterCollectionItems(items, name$1, filterPredicate),
493
524
  partOf
494
525
  });
495
526
  }
@@ -836,29 +867,105 @@ async function handleInboxInternal(request, parameters, span) {
836
867
  });
837
868
  }
838
869
  const keyCache = new KvKeyCache(kv, kvPrefixes.publicKey, ctx);
839
- let ldSigVerified;
840
- try {
841
- ldSigVerified = await verifyJsonLd(json, {
842
- contextLoader: ctx.contextLoader,
843
- documentLoader: ctx.documentLoader,
844
- keyCache,
845
- meterProvider,
846
- tracerProvider
870
+ const jsonWithoutSig = detachSignature(json);
871
+ const hasLdSignature = hasSignature(json);
872
+ const canAttemptAlternateAuthAfterLdSignatureFailure = skipSignatureVerification || hasHttpSignatureHeaders(request) || hasObjectIntegrityProof(jsonWithoutSig);
873
+ let deferredLdSignatureError = void 0;
874
+ const respondInvalidActivity = async (error) => {
875
+ logger.error("Failed to parse activity:\n{error}", {
876
+ recipient,
877
+ activity: json,
878
+ error
847
879
  });
848
- } catch (error) {
849
- if (error instanceof Error && error.name === "jsonld.SyntaxError") {
850
- logger.error("Failed to parse JSON-LD:\n{error}", {
880
+ try {
881
+ await inboxErrorHandler?.(ctx, error);
882
+ } catch (error) {
883
+ logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
884
+ error,
885
+ activity: json,
886
+ recipient
887
+ });
888
+ }
889
+ span.setStatus({
890
+ code: SpanStatusCode.ERROR,
891
+ message: `Failed to parse activity:\n${error}`
892
+ });
893
+ return new Response("Invalid activity.", {
894
+ status: 400,
895
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
896
+ });
897
+ };
898
+ let compactedJson = json;
899
+ let compactedJsonWithoutSig = jsonWithoutSig;
900
+ let ldSigVerified = false;
901
+ if (hasLdSignature) {
902
+ try {
903
+ compactedJson = await compactJsonLd(json, ctx.contextLoader);
904
+ } catch (error) {
905
+ if (isInvalidJsonLdError(error)) {
906
+ logger.error("Failed to parse JSON-LD:\n{error}", {
907
+ recipient,
908
+ error
909
+ });
910
+ return new Response("Invalid JSON-LD.", {
911
+ status: 400,
912
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
913
+ });
914
+ }
915
+ if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
916
+ if (!skipSignatureVerification) deferredLdSignatureError = error;
917
+ logger.debug("Failed to normalize JSON-LD for Linked Data Signatures; deferring to another authentication path only if it verifies:\n{error}", {
851
918
  recipient,
852
919
  error
853
920
  });
854
- return new Response("Invalid JSON-LD.", {
855
- status: 400,
856
- headers: { "Content-Type": "text/plain; charset=utf-8" }
857
- });
858
921
  }
859
- ldSigVerified = false;
922
+ if (compactedJson !== json) {
923
+ compactedJsonWithoutSig = detachSignature(compactedJson);
924
+ try {
925
+ ldSigVerified = await verifyCompactJsonLd(compactedJson, {
926
+ contextLoader: ctx.contextLoader,
927
+ documentLoader: ctx.documentLoader,
928
+ keyCache,
929
+ meterProvider,
930
+ tracerProvider
931
+ });
932
+ } catch (error) {
933
+ if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
934
+ if (isInvalidJsonLdError(error)) {
935
+ logger.error("Failed to parse JSON-LD:\n{error}", {
936
+ recipient,
937
+ error
938
+ });
939
+ return new Response("Invalid JSON-LD.", {
940
+ status: 400,
941
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
942
+ });
943
+ }
944
+ if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
945
+ if (!skipSignatureVerification) try {
946
+ await Object$1.fromJsonLd(compactedJson, {
947
+ contextLoader: getNormalizationContextLoader(ctx.contextLoader),
948
+ documentLoader: ctx.documentLoader,
949
+ tracerProvider
950
+ });
951
+ } catch (parseError) {
952
+ if (parseError instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(parseError);
953
+ if (isInvalidJsonLdError(parseError)) {
954
+ logger.error("Failed to parse JSON-LD:\n{error}", {
955
+ recipient,
956
+ error: parseError
957
+ });
958
+ return new Response("Invalid JSON-LD.", {
959
+ status: 400,
960
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
961
+ });
962
+ }
963
+ deferredLdSignatureError = parseError;
964
+ }
965
+ ldSigVerified = false;
966
+ }
967
+ }
860
968
  }
861
- const jsonWithoutSig = detachSignature(json);
862
969
  let activity = null;
863
970
  let activityVerified = false;
864
971
  if (ldSigVerified) {
@@ -866,7 +973,16 @@ async function handleInboxInternal(request, parameters, span) {
866
973
  recipient,
867
974
  json
868
975
  });
869
- activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
976
+ try {
977
+ activity = await Activity.fromJsonLd(compactedJsonWithoutSig, {
978
+ ...ctx,
979
+ contextLoader: getNormalizationContextLoader(ctx.contextLoader)
980
+ });
981
+ } catch (error) {
982
+ if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
983
+ if (!isPermanentActivityParseError(error)) throw error;
984
+ return await respondInvalidActivity(error);
985
+ }
870
986
  activityVerified = true;
871
987
  } else {
872
988
  logger.debug("Linked Data Signatures are not verified.", {
@@ -875,13 +991,22 @@ async function handleInboxInternal(request, parameters, span) {
875
991
  });
876
992
  try {
877
993
  activity = await verifyObject(Activity, jsonWithoutSig, {
878
- contextLoader: ctx.contextLoader,
994
+ contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
879
995
  documentLoader: ctx.documentLoader,
880
996
  keyCache,
881
997
  meterProvider,
882
998
  tracerProvider
883
999
  });
884
1000
  } catch (error) {
1001
+ if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(jsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
1002
+ if (deferredLdSignatureError != null) {
1003
+ logger.debug("Object Integrity Proof fallback did not supersede a deferred Linked Data Signature failure:\n{error}", {
1004
+ recipient,
1005
+ error
1006
+ });
1007
+ activity = null;
1008
+ }
1009
+ if (!isPermanentActivityParseError(error)) throw error;
885
1010
  logger.error("Failed to parse activity:\n{error}", {
886
1011
  recipient,
887
1012
  activity: json,
@@ -930,6 +1055,7 @@ async function handleInboxInternal(request, parameters, span) {
930
1055
  tracerProvider
931
1056
  });
932
1057
  if (verification.verified === false) {
1058
+ if (deferredLdSignatureError != null) throw deferredLdSignatureError;
933
1059
  const reason = verification.reason;
934
1060
  const remoteHost = "keyId" in reason && reason.keyId != null ? getRemoteHost(reason.keyId) : void 0;
935
1061
  getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure(reason.type, remoteHost);
@@ -1007,7 +1133,15 @@ async function handleInboxInternal(request, parameters, span) {
1007
1133
  }
1008
1134
  httpSigKey = verification.key;
1009
1135
  }
1010
- activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
1136
+ try {
1137
+ activity = await Activity.fromJsonLd(jsonWithoutSig, {
1138
+ ...ctx,
1139
+ contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader)
1140
+ });
1141
+ } catch (error) {
1142
+ if (!isPermanentActivityParseError(error)) throw error;
1143
+ return await respondInvalidActivity(error);
1144
+ }
1011
1145
  }
1012
1146
  if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
1013
1147
  span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
@@ -1019,6 +1153,7 @@ async function handleInboxInternal(request, parameters, span) {
1019
1153
  "http_signatures.key_id": httpSigKey?.id?.href ?? ""
1020
1154
  });
1021
1155
  if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
1156
+ if (deferredLdSignatureError != null) throw deferredLdSignatureError;
1022
1157
  getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("actorKeyMismatch", httpSigKey.id == null ? void 0 : getRemoteHost(httpSigKey.id));
1023
1158
  logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
1024
1159
  activity: json,
@@ -1045,10 +1180,14 @@ async function handleInboxInternal(request, parameters, span) {
1045
1180
  const routeResult = await routeActivity({
1046
1181
  context: ctx,
1047
1182
  json,
1183
+ originalJson: json,
1184
+ normalizedActivity: hasLdSignature && compactedJson !== json ? compactedJson : void 0,
1185
+ ldSignatureVerified: hasLdSignature ? ldSigVerified : void 0,
1048
1186
  activity,
1049
1187
  recipient,
1050
1188
  inboxListeners,
1051
1189
  inboxContextFactory,
1190
+ listenerInboxContextFactory: ldSigVerified ? inboxContextFactory[rawInboxContextFactorySymbol] : void 0,
1052
1191
  inboxErrorHandler,
1053
1192
  kv,
1054
1193
  kvPrefixes,
@@ -1175,8 +1314,8 @@ var CustomCollectionHandler = class {
1175
1314
  * @param CollectionPage The CollectionPage constructor.
1176
1315
  * @param filterPredicate Optional filter predicate for items.
1177
1316
  */
1178
- constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
1179
- this.name = name$1;
1317
+ constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
1318
+ this.name = name$2;
1180
1319
  this.values = values;
1181
1320
  this.context = context;
1182
1321
  this.callbacks = callbacks;
@@ -1600,22 +1739,72 @@ const logger = getLogger([
1600
1739
  * @returns The response to the request.
1601
1740
  */
1602
1741
  async function handleWebFinger(request, options) {
1603
- if (options.tracer == null) return await handleWebFingerInternal(request, options);
1604
- return await options.tracer.startActiveSpan("webfinger.handle", { kind: SpanKind.SERVER }, async (span) => {
1605
- try {
1606
- const response = await handleWebFingerInternal(request, options);
1607
- span.setStatus({ code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR });
1608
- return response;
1609
- } catch (error) {
1610
- span.setStatus({
1611
- code: SpanStatusCode.ERROR,
1612
- message: String(error)
1613
- });
1614
- throw error;
1615
- } finally {
1616
- span.end();
1742
+ const meterProvider = options.meterProvider;
1743
+ const start = meterProvider == null ? 0 : performance.now();
1744
+ const scheme = computeResourceScheme(options.context.url.searchParams.get("resource"));
1745
+ let notFoundResponse;
1746
+ const wrappedOptions = {
1747
+ ...options,
1748
+ async onNotFound(req) {
1749
+ const r = await options.onNotFound(req);
1750
+ notFoundResponse = r;
1751
+ return r;
1617
1752
  }
1618
- });
1753
+ };
1754
+ let response;
1755
+ try {
1756
+ if (options.tracer == null) response = await handleWebFingerInternal(request, wrappedOptions);
1757
+ else response = await options.tracer.startActiveSpan("webfinger.handle", { kind: SpanKind.SERVER }, async (span) => {
1758
+ try {
1759
+ const inner = await handleWebFingerInternal(request, wrappedOptions);
1760
+ span.setStatus({ code: inner.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR });
1761
+ return inner;
1762
+ } catch (error) {
1763
+ span.setStatus({
1764
+ code: SpanStatusCode.ERROR,
1765
+ message: String(error)
1766
+ });
1767
+ throw error;
1768
+ } finally {
1769
+ span.end();
1770
+ }
1771
+ });
1772
+ return response;
1773
+ } finally {
1774
+ if (meterProvider != null) recordWebFingerHandle(meterProvider, {
1775
+ durationMs: Math.max(0, performance.now() - start),
1776
+ result: classifyWebFingerHandleResult(response, notFoundResponse),
1777
+ scheme,
1778
+ statusCode: response?.status
1779
+ });
1780
+ }
1781
+ }
1782
+ const WEBFINGER_HANDLE_SCHEME_WHITELIST = new Set([
1783
+ "acct",
1784
+ "http",
1785
+ "https",
1786
+ "mailto"
1787
+ ]);
1788
+ function isAllowedResourceScheme(scheme) {
1789
+ return WEBFINGER_HANDLE_SCHEME_WHITELIST.has(scheme);
1790
+ }
1791
+ function computeResourceScheme(resource) {
1792
+ if (resource == null) return void 0;
1793
+ const colon = resource.indexOf(":");
1794
+ if (colon <= 0) return void 0;
1795
+ const candidate = resource.substring(0, colon).toLowerCase();
1796
+ return isAllowedResourceScheme(candidate) ? candidate : "other";
1797
+ }
1798
+ function classifyWebFingerHandleResult(response, notFoundResponse) {
1799
+ if (response == null) return "error";
1800
+ if (notFoundResponse != null && response === notFoundResponse) return "not_found";
1801
+ switch (response.status) {
1802
+ case 200: return "resolved";
1803
+ case 400: return "invalid";
1804
+ case 404: return "not_found";
1805
+ case 410: return "tombstoned";
1806
+ default: return "error";
1807
+ }
1619
1808
  }
1620
1809
  async function handleWebFingerInternal(request, { context, host, actorDispatcher, actorHandleMapper, actorAliasMapper, onNotFound, span, webFingerLinksDispatcher }) {
1621
1810
  if (actorDispatcher == null) {
@@ -1723,6 +1912,18 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
1723
1912
  }
1724
1913
  //#endregion
1725
1914
  //#region src/federation/middleware.ts
1915
+ function isRemoteContextLoadingFailure(error) {
1916
+ return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
1917
+ }
1918
+ function isPermanentRemoteContextError(error) {
1919
+ if (!(error instanceof Error) || error.name !== "jsonld.InvalidUrl") return false;
1920
+ const details = error.details;
1921
+ if (details?.code === "invalid remote context") return true;
1922
+ return isRemoteContextLoadingFailure(error) && typeof details?.url === "string" && !URL.canParse(details.url) && isClearlyMalformedContextReference(details.url);
1923
+ }
1924
+ function isPermanentInboxParseError(error) {
1925
+ 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));
1926
+ }
1726
1927
  /**
1727
1928
  * Create a new {@link Federation} instance.
1728
1929
  * @param parameters Parameters for initializing the instance.
@@ -2225,78 +2426,37 @@ var FederationImpl = class extends FederationBuilderImpl {
2225
2426
  const identity = await this.sharedInboxKeyDispatcher(context);
2226
2427
  if (identity != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
2227
2428
  }
2228
- const activity = await Activity.fromJsonLd(message.activity, context);
2229
- const activityType = getTypeId(activity).href;
2230
- span.setAttribute("activitypub.activity.type", activityType);
2231
- onActivityType?.(activityType);
2232
- if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
2233
- const cacheKey = activity.id == null ? null : [
2234
- ...this.kvPrefixes.activityIdempotence,
2235
- context.origin,
2236
- activity.id.href
2237
- ];
2238
- if (cacheKey != null) {
2239
- if (await this.kv.get(cacheKey) === true) {
2240
- logger.debug("Activity {activityId} has already been processed.", {
2241
- activityId: activity.id?.href,
2242
- activity: message.activity,
2243
- recipient: message.identifier
2244
- });
2245
- recordInboxActivity(this.meterProvider, "rejected", activityType);
2246
- return;
2247
- }
2248
- }
2249
- await this._getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
2250
- const dispatched = this.inboxListeners?.dispatchWithClass(activity);
2251
- if (dispatched == null) {
2252
- logger.error("Unsupported activity type:\n{activity}", {
2253
- activityId: activity.id?.href,
2254
- activity: message.activity,
2255
- recipient: message.identifier,
2256
- trial: message.attempt
2257
- });
2258
- span.setStatus({
2259
- code: SpanStatusCode.ERROR,
2260
- message: `Unsupported activity type: ${activityType}`
2261
- });
2262
- recordInboxActivity(this.meterProvider, "rejected", activityType);
2263
- span.end();
2264
- return;
2265
- }
2266
- const { class: cls, listener } = dispatched;
2267
- span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
2268
- try {
2269
- const started = performance.now();
2270
- try {
2271
- await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, activityType), activity);
2272
- } finally {
2273
- getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
2274
- }
2275
- recordInboxActivity(this.meterProvider, "processed", activityType);
2276
- } catch (error) {
2429
+ await this._getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (listenerSpan) => {
2430
+ let activity = null;
2431
+ let cacheKey = null;
2432
+ let activityType;
2433
+ const reportInboxError = async (error) => {
2277
2434
  try {
2278
2435
  await this.inboxErrorHandler?.(context, error);
2279
2436
  } catch (error) {
2280
2437
  logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
2281
2438
  error,
2282
2439
  trial: message.attempt,
2283
- activityId: activity.id?.href,
2440
+ activityId: activity?.id?.href,
2284
2441
  activity: message.activity,
2285
2442
  recipient: message.identifier
2286
2443
  });
2287
2444
  }
2445
+ };
2446
+ const handleRetriableFailure = async (error) => {
2447
+ await reportInboxError(error);
2288
2448
  if (this.inboxQueue?.nativeRetrial) {
2289
2449
  logger.error("Failed to process the incoming activity {activityId}; backend will handle retry:\n{error}", {
2290
2450
  error,
2291
- activityId: activity.id?.href,
2451
+ activityId: activity?.id?.href,
2292
2452
  activity: message.activity,
2293
2453
  recipient: message.identifier
2294
2454
  });
2295
- span.setStatus({
2455
+ listenerSpan.setStatus({
2296
2456
  code: SpanStatusCode.ERROR,
2297
2457
  message: String(error)
2298
2458
  });
2299
- span.end();
2459
+ listenerSpan.end();
2300
2460
  throw error;
2301
2461
  }
2302
2462
  const delay = this.inboxRetryPolicy({
@@ -2307,20 +2467,27 @@ var FederationImpl = class extends FederationBuilderImpl {
2307
2467
  logger.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
2308
2468
  error,
2309
2469
  attempt: message.attempt,
2310
- activityId: activity.id?.href,
2470
+ activityId: activity?.id?.href,
2311
2471
  activity: message.activity,
2312
2472
  recipient: message.identifier
2313
2473
  });
2474
+ if (this.inboxQueue == null) {
2475
+ listenerSpan.setStatus({
2476
+ code: SpanStatusCode.ERROR,
2477
+ message: String(error)
2478
+ });
2479
+ listenerSpan.end();
2480
+ throw error;
2481
+ }
2314
2482
  const retryMessage = {
2315
2483
  ...message,
2316
2484
  attempt: message.attempt + 1
2317
2485
  };
2318
- const { inboxQueue } = this;
2319
- if (inboxQueue != null) {
2320
- await inboxQueue.enqueue(retryMessage, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
2486
+ await this.inboxQueue.enqueue(retryMessage, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
2487
+ if (activityType != null) {
2321
2488
  getFederationMetrics(this.meterProvider).recordQueueTaskEnqueued({
2322
2489
  role: "inbox",
2323
- queue: inboxQueue,
2490
+ queue: this.inboxQueue,
2324
2491
  activityType
2325
2492
  }, retryMessage.attempt);
2326
2493
  recordInboxActivity(this.meterProvider, "retried", activityType);
@@ -2328,26 +2495,137 @@ var FederationImpl = class extends FederationBuilderImpl {
2328
2495
  } else {
2329
2496
  logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
2330
2497
  error,
2331
- activityId: activity.id?.href,
2498
+ activityId: activity?.id?.href,
2332
2499
  activity: message.activity,
2333
2500
  recipient: message.identifier
2334
2501
  });
2335
- recordInboxActivity(this.meterProvider, "abandoned", activityType);
2502
+ if (activityType != null) recordInboxActivity(this.meterProvider, "abandoned", activityType);
2336
2503
  }
2337
- span.setStatus({
2504
+ listenerSpan.setStatus({
2338
2505
  code: SpanStatusCode.ERROR,
2339
2506
  message: String(error)
2340
2507
  });
2341
- span.end();
2508
+ listenerSpan.end();
2509
+ };
2510
+ let dispatched;
2511
+ let parseInput = void 0;
2512
+ let parseContextLoader = context.contextLoader;
2513
+ try {
2514
+ const hasSignatureField = hasSignature(message.activity);
2515
+ const shouldParseFromNormalizedSignedPayload = message.ldSignatureVerified === true || message.normalizedActivity != null || message.ldSignatureVerified == null && hasSignatureField;
2516
+ const parseContext = hasSignatureField ? {
2517
+ ...context,
2518
+ contextLoader: getNormalizationContextLoader(context.contextLoader)
2519
+ } : {
2520
+ ...context,
2521
+ contextLoader: wrapContextLoaderForJsonLd(context.contextLoader)
2522
+ };
2523
+ parseContextLoader = parseContext.contextLoader;
2524
+ let normalizedActivity;
2525
+ if (shouldParseFromNormalizedSignedPayload) {
2526
+ normalizedActivity = message.normalizedActivity ?? await compactJsonLd(message.activity, context.contextLoader);
2527
+ assertSafeJsonLd(normalizedActivity);
2528
+ }
2529
+ parseInput = shouldParseFromNormalizedSignedPayload ? detachSignature(normalizedActivity) : hasSignatureField ? detachSignature(message.activity) : message.activity;
2530
+ activity = await Activity.fromJsonLd(parseInput, parseContext);
2531
+ activityType = getTypeId(activity).href;
2532
+ span.setAttribute("activitypub.activity.type", activityType);
2533
+ listenerSpan.setAttribute("activitypub.activity.type", activityType);
2534
+ onActivityType?.(activityType);
2535
+ if (activity.id != null) {
2536
+ span.setAttribute("activitypub.activity.id", activity.id.href);
2537
+ listenerSpan.setAttribute("activitypub.activity.id", activity.id.href);
2538
+ }
2539
+ cacheKey = activity.id == null ? null : [
2540
+ ...this.kvPrefixes.activityIdempotence,
2541
+ context.origin,
2542
+ activity.id.href
2543
+ ];
2544
+ if (cacheKey != null) {
2545
+ if (await this.kv.get(cacheKey) === true) {
2546
+ logger.debug("Activity {activityId} has already been processed.", {
2547
+ activityId: activity.id?.href,
2548
+ activity: message.activity,
2549
+ recipient: message.identifier
2550
+ });
2551
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
2552
+ listenerSpan.end();
2553
+ return;
2554
+ }
2555
+ }
2556
+ dispatched = this.inboxListeners?.dispatchWithClass(activity);
2557
+ } catch (error) {
2558
+ if (activity == null && error instanceof RangeError && await hasMalformedKnownTemporalLiteral(parseInput, parseContextLoader)) {
2559
+ await reportInboxError(error);
2560
+ logger.error("Failed to parse the queued incoming activity {activityId}:\n{error}", {
2561
+ error,
2562
+ trial: message.attempt,
2563
+ activityId: null,
2564
+ activity: message.activity,
2565
+ recipient: message.identifier
2566
+ });
2567
+ listenerSpan.setStatus({
2568
+ code: SpanStatusCode.ERROR,
2569
+ message: String(error)
2570
+ });
2571
+ listenerSpan.end();
2572
+ return;
2573
+ }
2574
+ if (isPermanentInboxParseError(error)) {
2575
+ await reportInboxError(error);
2576
+ logger.error("Failed to parse the queued incoming activity {activityId}:\n{error}", {
2577
+ error,
2578
+ trial: message.attempt,
2579
+ activityId: activity?.id?.href,
2580
+ activity: message.activity,
2581
+ recipient: message.identifier
2582
+ });
2583
+ listenerSpan.setStatus({
2584
+ code: SpanStatusCode.ERROR,
2585
+ message: String(error)
2586
+ });
2587
+ listenerSpan.end();
2588
+ return;
2589
+ }
2590
+ await handleRetriableFailure(error);
2591
+ return;
2592
+ }
2593
+ if (dispatched == null) {
2594
+ logger.error("Unsupported activity type:\n{activity}", {
2595
+ activityId: activity.id?.href,
2596
+ activity: message.activity,
2597
+ recipient: message.identifier,
2598
+ trial: message.attempt
2599
+ });
2600
+ listenerSpan.setStatus({
2601
+ code: SpanStatusCode.ERROR,
2602
+ message: `Unsupported activity type: ${activityType}`
2603
+ });
2604
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
2605
+ listenerSpan.end();
2606
+ return;
2607
+ }
2608
+ const { class: cls, listener } = dispatched;
2609
+ listenerSpan.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
2610
+ try {
2611
+ const started = performance.now();
2612
+ try {
2613
+ await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, activityType), activity);
2614
+ } finally {
2615
+ getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
2616
+ }
2617
+ recordInboxActivity(this.meterProvider, "processed", activityType);
2618
+ } catch (error) {
2619
+ await handleRetriableFailure(error);
2342
2620
  return;
2343
2621
  }
2344
2622
  if (cacheKey != null) await this.kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
2345
2623
  logger.info("Activity {activityId} has been processed.", {
2346
- activityId: activity.id?.href,
2624
+ activityId: activity?.id?.href,
2347
2625
  activity: message.activity,
2348
2626
  recipient: message.identifier
2349
2627
  });
2350
- span.end();
2628
+ listenerSpan.end();
2351
2629
  });
2352
2630
  }
2353
2631
  startQueue(contextData, options = {}) {
@@ -2641,7 +2919,8 @@ var FederationImpl = class extends FederationBuilderImpl {
2641
2919
  actorAliasMapper: this.actorCallbacks?.aliasMapper,
2642
2920
  webFingerLinksDispatcher: this.webFingerLinksDispatcher,
2643
2921
  onNotFound,
2644
- tracer
2922
+ tracer,
2923
+ meterProvider: this._meterProvider
2645
2924
  });
2646
2925
  case "nodeInfoJrd": return await handleNodeInfoJrd(request, context);
2647
2926
  case "nodeInfo": return await handleNodeInfo(request, {
@@ -2727,16 +3006,18 @@ var FederationImpl = class extends FederationBuilderImpl {
2727
3006
  onNotFound
2728
3007
  });
2729
3008
  context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier: route.values.identifier }) });
2730
- case "sharedInbox":
3009
+ case "sharedInbox": {
2731
3010
  if (routeName !== "inbox" && this.sharedInboxKeyDispatcher != null) {
2732
3011
  const identity = await this.sharedInboxKeyDispatcher(context);
2733
3012
  if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
2734
3013
  }
2735
3014
  if (!this.manuallyStartQueue) this._startQueueInternal(contextData);
3015
+ const inboxContextFactory = context.toInboxContext.bind(context);
3016
+ inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
2736
3017
  return await handleInbox(request, {
2737
3018
  recipient: route.values.identifier ?? null,
2738
3019
  context,
2739
- inboxContextFactory: context.toInboxContext.bind(context),
3020
+ inboxContextFactory,
2740
3021
  kv: this.kv,
2741
3022
  kvPrefixes: this.kvPrefixes,
2742
3023
  queue: this.inboxQueue,
@@ -2752,6 +3033,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2752
3033
  tracerProvider: this.tracerProvider,
2753
3034
  idempotencyStrategy: this.idempotencyStrategy
2754
3035
  });
3036
+ }
2755
3037
  case "following": return await handleCollection(request, {
2756
3038
  name: "following",
2757
3039
  identifier: route.values.identifier,
@@ -3200,6 +3482,7 @@ var ContextImpl = class ContextImpl {
3200
3482
  ...options,
3201
3483
  userAgent: options.userAgent ?? this.federation.userAgent,
3202
3484
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
3485
+ meterProvider: options.meterProvider ?? this.federation._meterProvider,
3203
3486
  allowPrivateAddress: this.federation.allowPrivateAddress
3204
3487
  });
3205
3488
  }
@@ -3484,6 +3767,7 @@ var ContextImpl = class ContextImpl {
3484
3767
  const routeResult = await routeActivity({
3485
3768
  context: this,
3486
3769
  json,
3770
+ ldSignatureVerified: false,
3487
3771
  activity,
3488
3772
  recipient,
3489
3773
  inboxListeners: this.federation.inboxListeners,
@@ -3772,6 +4056,14 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
3772
4056
  }
3773
4057
  var InboxContextImpl = class InboxContextImpl extends ContextImpl {
3774
4058
  recipient;
4059
+ /**
4060
+ * The original received activity payload.
4061
+ *
4062
+ * Fedify may normalize a Linked Data Signature payload internally for safe
4063
+ * parsing, but forwarding must keep the sender's payload unchanged so
4064
+ * third-party signatures/proofs remain intact.
4065
+ * @internal
4066
+ */
3775
4067
  activity;
3776
4068
  activityId;
3777
4069
  activityType;