From 4be0a0c9c2f9468a4ebd1bcb4b8e5855a87318b6 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 26 Jun 2026 11:09:29 +0900 Subject: [PATCH 1/3] federation: fall back when LDS hits JSON-LD processing errors Assisted-by: Codex:gpt-5.5 --- CHANGES.md | 10 +++++ packages/fedify/src/federation/middleware.ts | 42 ++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d3d89eb01..aecea796d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,16 @@ Version 2.0.21 To be released. +### @fedify/fedify + + - Fixed outbound activity delivery aborting when Linked Data Signature + creation fails during JSON-LD canonicalization. Fedify now logs the + signing failure and continues delivery without the Linked Data Signature + for JSON-LD processing failures, while still surfacing key, configuration, + and programming errors from signing. [[#824]] + +[#824]: https://github.com/fedify-dev/fedify/issues/824 + Version 2.0.20 -------------- diff --git a/packages/fedify/src/federation/middleware.ts b/packages/fedify/src/federation/middleware.ts index 845bff1d9..cb16114e3 100644 --- a/packages/fedify/src/federation/middleware.ts +++ b/packages/fedify/src/federation/middleware.ts @@ -180,6 +180,30 @@ function isPermanentInboxParseError(error: unknown): error is Error { isInvalidUrlTypeError(error))); } +function isLinkedDataSignatureJsonLdProcessingError( + error: unknown, +): error is Error { + if (!(error instanceof Error)) return false; + if (error.message.startsWith("Maximum deep iterations exceeded")) { + return true; + } + const details = (error as Error & { + details?: { code?: unknown; cause?: unknown }; + }).details; + if ( + error.name === "jsonld.InvalidUrl" && + details?.code === "loading remote context failed" && + details.cause instanceof Error + ) { + return details.cause.message.startsWith( + "Maximum deep iterations exceeded", + ) || details.cause.name.startsWith("jsonld.") || + details.cause.name === "UnsafeJsonLdError"; + } + return error.name.startsWith("jsonld.") || + error.name === "UnsafeJsonLdError"; +} + /** * Options for {@link createFederation} function. * @template TContextData The type of the context data. @@ -1325,10 +1349,20 @@ export class FederationImpl }, ); } else { - jsonLd = await signJsonLd(jsonLd, rsaKey.privateKey, rsaKey.keyId, { - contextLoader, - tracerProvider: this.tracerProvider, - }); + try { + jsonLd = await signJsonLd(jsonLd, rsaKey.privateKey, rsaKey.keyId, { + contextLoader, + tracerProvider: this.tracerProvider, + }); + } catch (error) { + if (!isLinkedDataSignatureJsonLdProcessingError(error)) throw error; + logger.warn( + "Failed to create a Linked Data signature for the activity " + + "{activityId}. The activity will be sent without a Linked " + + "Data signature.", + { activityId, error }, + ); + } } if (!proofCreated) { logger.warn( From 0b352b771a457dc897f37e91efbdc199134ddd80 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 26 Jun 2026 11:09:29 +0900 Subject: [PATCH 2/3] keep unsafe JSON-LD signing failures fatal Assisted-by: Codex:gpt-5.5 --- packages/fedify/src/federation/middleware.ts | 30 +++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/fedify/src/federation/middleware.ts b/packages/fedify/src/federation/middleware.ts index cb16114e3..a0bbb1ce9 100644 --- a/packages/fedify/src/federation/middleware.ts +++ b/packages/fedify/src/federation/middleware.ts @@ -184,24 +184,34 @@ function isLinkedDataSignatureJsonLdProcessingError( error: unknown, ): error is Error { if (!(error instanceof Error)) return false; - if (error.message.startsWith("Maximum deep iterations exceeded")) { + const errorName = (error as Error & { name?: unknown }).name; + const errorMessage = (error as Error & { message?: unknown }).message; + if ( + typeof errorMessage === "string" && + errorMessage.startsWith("Maximum deep iterations exceeded") + ) { return true; } const details = (error as Error & { details?: { code?: unknown; cause?: unknown }; }).details; + const errorCause = (error as Error & { cause?: unknown }).cause; + const cause = errorCause instanceof Error ? errorCause : details?.cause; if ( - error.name === "jsonld.InvalidUrl" && + errorName === "jsonld.InvalidUrl" && details?.code === "loading remote context failed" && - details.cause instanceof Error + cause instanceof Error ) { - return details.cause.message.startsWith( - "Maximum deep iterations exceeded", - ) || details.cause.name.startsWith("jsonld.") || - details.cause.name === "UnsafeJsonLdError"; - } - return error.name.startsWith("jsonld.") || - error.name === "UnsafeJsonLdError"; + const causeName = (cause as Error & { name?: unknown }).name; + const causeMessage = (cause as Error & { message?: unknown }).message; + return ( + typeof causeMessage === "string" && + causeMessage.startsWith("Maximum deep iterations exceeded") + ) || (typeof causeName === "string" && causeName.startsWith("jsonld.")); + } + return errorName !== "jsonld.InvalidUrl" && + typeof errorName === "string" && + errorName.startsWith("jsonld."); } /** From cf43e9629d8dd0c17bf22ee74a00d0304e9c0fb5 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 26 Jun 2026 13:44:15 +0900 Subject: [PATCH 3/3] Split JSON-LD error predicate Assisted-by: Codex:gpt-5.5 --- packages/fedify/src/federation/middleware.ts | 47 ++++++++++---------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/packages/fedify/src/federation/middleware.ts b/packages/fedify/src/federation/middleware.ts index a0bbb1ce9..00c113fd0 100644 --- a/packages/fedify/src/federation/middleware.ts +++ b/packages/fedify/src/federation/middleware.ts @@ -180,38 +180,39 @@ function isPermanentInboxParseError(error: unknown): error is Error { isInvalidUrlTypeError(error))); } -function isLinkedDataSignatureJsonLdProcessingError( - error: unknown, -): error is Error { - if (!(error instanceof Error)) return false; - const errorName = (error as Error & { name?: unknown }).name; - const errorMessage = (error as Error & { message?: unknown }).message; +type LinkedDataSignatureJsonLdProcessingError = Error & { + details?: { code?: unknown; cause?: unknown }; + cause?: unknown; +}; + +function hasLinkedDataSignatureJsonLdProcessingError( + error: LinkedDataSignatureJsonLdProcessingError, +): boolean { if ( - typeof errorMessage === "string" && - errorMessage.startsWith("Maximum deep iterations exceeded") + error.message.startsWith("Maximum deep iterations exceeded") ) { return true; } - const details = (error as Error & { - details?: { code?: unknown; cause?: unknown }; - }).details; - const errorCause = (error as Error & { cause?: unknown }).cause; - const cause = errorCause instanceof Error ? errorCause : details?.cause; + const cause = error.cause instanceof Error + ? error.cause + : error.details?.cause; if ( - errorName === "jsonld.InvalidUrl" && - details?.code === "loading remote context failed" && + error.name === "jsonld.InvalidUrl" && + error.details?.code === "loading remote context failed" && cause instanceof Error ) { - const causeName = (cause as Error & { name?: unknown }).name; - const causeMessage = (cause as Error & { message?: unknown }).message; return ( - typeof causeMessage === "string" && - causeMessage.startsWith("Maximum deep iterations exceeded") - ) || (typeof causeName === "string" && causeName.startsWith("jsonld.")); + cause.message.startsWith("Maximum deep iterations exceeded") + ) || cause.name.startsWith("jsonld."); } - return errorName !== "jsonld.InvalidUrl" && - typeof errorName === "string" && - errorName.startsWith("jsonld."); + return error.name !== "jsonld.InvalidUrl" && error.name.startsWith("jsonld."); +} + +function isLinkedDataSignatureJsonLdProcessingError( + error: unknown, +): error is LinkedDataSignatureJsonLdProcessingError { + return error instanceof Error && + hasLinkedDataSignatureJsonLdProcessingError(error); } /**