@fedify/fedify 2.3.0-dev.1145 → 2.3.0-dev.1154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{builder-ShiR1K6b.mjs → builder-BwSH45lU.mjs} +2 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-DI2gRbyN.d.cts → context-CRXCkTM6.d.cts} +48 -6
- package/dist/{context-DCtsSHDv.d.ts → context-MgCh7YGu.d.ts} +48 -6
- package/dist/{deno-h0TWFuEz.mjs → deno-Aas8ryCk.mjs} +1 -1
- package/dist/{docloader-BdDN0Aqx.mjs → docloader-_1lh7Dn1.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +1363 -44
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +1667 -163
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/retry.test.mjs +1 -1
- package/dist/federation/send.test.mjs +8 -8
- package/dist/federation/temporal.test.d.mts +2 -0
- package/dist/federation/temporal.test.mjs +71 -0
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{getMachineId-bsd-etIyxDet.mjs → getMachineId-bsd-BY01PL1n.mjs} +1 -1
- package/dist/{getMachineId-darwin-D23zTf4g.mjs → getMachineId-darwin-Dr1gkBkp.mjs} +1 -1
- package/dist/{getMachineId-win-Dpap6v5i.mjs → getMachineId-win-QEYwcJiy.mjs} +1 -1
- package/dist/{http-B2hxA7dO.js → http-CP1Qje2Q.js} +1 -1
- package/dist/{http-QzW9IWfs.mjs → http-DUBr4pJL.mjs} +3 -3
- package/dist/{http-7kAB7PVx.cjs → http-bAPHYmg8.cjs} +1 -1
- package/dist/{key-Dh2OK1XQ.mjs → key-DVh4I9kS.mjs} +2 -2
- package/dist/{kv-cache-DCPp-MT0.cjs → kv-cache-2zYOM6Q7.cjs} +1 -1
- package/dist/{kv-cache-EZRIPZXD.mjs → kv-cache-6CA6Rx92.mjs} +1 -1
- package/dist/{kv-cache-b22dNkjt.js → kv-cache-azKKIdQE.js} +1 -1
- package/dist/{ld-eZbar1rr.mjs → ld-D9435Gn1.mjs} +302 -6
- package/dist/{metrics-E0hAHtLZ.mjs → metrics-K7CyLZhK.mjs} +1 -1
- package/dist/{middleware-mToCR2tG.mjs → middleware-Bn8SYmaa.mjs} +1 -1
- package/dist/{middleware-BUl1BH4x.cjs → middleware-Cu8Lw81i.cjs} +429 -99
- package/dist/{middleware-CyJDCmNg.mjs → middleware-DZjXGmiF.mjs} +348 -108
- package/dist/{middleware-BrGIM_Ra.js → middleware-tbfw9GfY.js} +428 -99
- package/dist/{mod-CI9fduEi.d.cts → mod-C7HOzGqH.d.cts} +1 -1
- package/dist/{mod-CkRiJHGA.d.ts → mod-CpQHB3Ys.d.ts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +2 -2
- package/dist/mod.d.ts +2 -2
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-ByO_Fw6U.mjs → owner-Bt9Zdipr.mjs} +2 -2
- package/dist/{proof-jVqClF49.cjs → proof-3YeQ4Z5A.cjs} +353 -3
- package/dist/{proof-CSo0S8OK.mjs → proof-CfevUec1.mjs} +3 -3
- package/dist/{proof-BkRyFchv.js → proof-CqwCBFaT.js} +300 -4
- package/dist/{send-jzrTV1FU.mjs → send-CuS5FYIt.mjs} +3 -3
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +558 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +1 -1
- package/dist/temporal-BacpfwuJ.mjs +95 -0
- package/dist/testing/mod.d.mts +48 -6
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- /package/dist/{execAsync-DCBrgFiV.mjs → execAsync-Dxb7rNf3.mjs} +0 -0
- /package/dist/{getMachineId-linux-ObI47Hql.mjs → getMachineId-linux-Bbhofx-s.mjs} +0 -0
- /package/dist/{getMachineId-unsupported-Ddu-PFeh.mjs → getMachineId-unsupported-dIOte2Ct.mjs} +0 -0
- /package/dist/{retry-v_sGLH1d.mjs → retry-_VvV0h9f.mjs} +0 -0
|
@@ -2,10 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
import { t as __exportAll } from "./chunk-CRNNMoPX.js";
|
|
4
4
|
import { r as getDefaultActivityTransformers } from "./transformers-BGMIq1cs.js";
|
|
5
|
-
import { C as recordWebFingerHandle, O as name, S as recordOutboxEnqueue, a as verifyRequestDetailed, b as recordInboxActivity, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, k as version, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, w as formatAcceptSignature, x as recordOutboxActivity, y as recordFanoutRecipients } from "./http-
|
|
6
|
-
import { c as getKeyOwner,
|
|
5
|
+
import { C as recordWebFingerHandle, O as name, S as recordOutboxEnqueue, a as verifyRequestDetailed, b as recordInboxActivity, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, k as version, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, w as formatAcceptSignature, x as recordOutboxActivity, y as recordFanoutRecipients } from "./http-CP1Qje2Q.js";
|
|
6
|
+
import { _ as hasSignatureLike, b as signJsonLd, c as getKeyOwner, f as compactJsonLd, g as hasSignature, h as getNormalizationContextLoader, i as verifyObject, l as InvalidContextReferenceError, m as detachSignature, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, r as signObject, s as doesActorOwnKey, u as assertSafeJsonLd, v as isClearlyMalformedContextReference, w as wrapContextLoaderForJsonLd, x as verifyCompactJsonLd, y as isInvalidUrlTypeError } from "./proof-CqwCBFaT.js";
|
|
7
7
|
import { n as getNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-azKKIdQE.js";
|
|
9
9
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
10
10
|
import { Router, RouterError, assertPath } from "@fedify/uri-template";
|
|
11
11
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
@@ -14,6 +14,7 @@ import { encodeHex } from "byte-encodings/hex";
|
|
|
14
14
|
import { uniq } from "es-toolkit";
|
|
15
15
|
import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
16
16
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
17
|
+
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
17
18
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
18
19
|
import { domainToASCII } from "node:url";
|
|
19
20
|
//#region src/federation/activity-listener.ts
|
|
@@ -691,7 +692,7 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
|
691
692
|
}
|
|
692
693
|
//#endregion
|
|
693
694
|
//#region src/federation/inbox.ts
|
|
694
|
-
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
695
|
+
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
695
696
|
const logger = getLogger([
|
|
696
697
|
"fedify",
|
|
697
698
|
"federation",
|
|
@@ -750,7 +751,9 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
750
751
|
type: "inbox",
|
|
751
752
|
id: crypto.randomUUID(),
|
|
752
753
|
baseUrl: ctx.origin,
|
|
753
|
-
activity: json,
|
|
754
|
+
activity: originalJson ?? json,
|
|
755
|
+
...normalizedActivity == null ? {} : { normalizedActivity },
|
|
756
|
+
...ldSignatureVerified == null ? {} : { ldSignatureVerified },
|
|
754
757
|
identifier: recipient,
|
|
755
758
|
attempt: 0,
|
|
756
759
|
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -804,7 +807,8 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
804
807
|
const activityType = getTypeId(activity).href;
|
|
805
808
|
const started = performance.now();
|
|
806
809
|
try {
|
|
807
|
-
|
|
810
|
+
const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
|
|
811
|
+
await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
|
|
808
812
|
} finally {
|
|
809
813
|
getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
810
814
|
}
|
|
@@ -1029,7 +1033,123 @@ function acceptsJsonLd(request) {
|
|
|
1029
1033
|
return types.includes("application/activity+json") || types.includes("application/ld+json") || types.includes("application/json");
|
|
1030
1034
|
}
|
|
1031
1035
|
//#endregion
|
|
1036
|
+
//#region src/federation/temporal.ts
|
|
1037
|
+
function isPlainObject(value) {
|
|
1038
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
1039
|
+
}
|
|
1040
|
+
function normalizeDateTimeLiteral(value) {
|
|
1041
|
+
return value.substring(19).match(/[Z+-]/) ? value : value + "Z";
|
|
1042
|
+
}
|
|
1043
|
+
function isMalformedDateTimeLiteral(value) {
|
|
1044
|
+
if (typeof value !== "string") return false;
|
|
1045
|
+
try {
|
|
1046
|
+
Temporal.Instant.from(normalizeDateTimeLiteral(value));
|
|
1047
|
+
return false;
|
|
1048
|
+
} catch {
|
|
1049
|
+
return true;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
function isMalformedDurationLiteral(value) {
|
|
1053
|
+
if (typeof value !== "string") return false;
|
|
1054
|
+
try {
|
|
1055
|
+
Temporal.Duration.from(value);
|
|
1056
|
+
return false;
|
|
1057
|
+
} catch {
|
|
1058
|
+
return true;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
const TEMPORAL_DATE_TIME_IRIS = new Set([
|
|
1062
|
+
"https://www.w3.org/ns/activitystreams#deleted",
|
|
1063
|
+
"https://www.w3.org/ns/activitystreams#endTime",
|
|
1064
|
+
"https://www.w3.org/ns/activitystreams#published",
|
|
1065
|
+
"https://www.w3.org/ns/activitystreams#startTime",
|
|
1066
|
+
"https://www.w3.org/ns/activitystreams#updated",
|
|
1067
|
+
"http://purl.org/dc/terms/created",
|
|
1068
|
+
"https://w3id.org/security#created"
|
|
1069
|
+
]);
|
|
1070
|
+
const TEMPORAL_DURATION_IRIS = new Set(["https://www.w3.org/ns/activitystreams#duration"]);
|
|
1071
|
+
const QUESTION_CLOSED_IRI = "https://www.w3.org/ns/activitystreams#closed";
|
|
1072
|
+
const XSD_DATE_TIME_IRI = "http://www.w3.org/2001/XMLSchema#dateTime";
|
|
1073
|
+
function hasMalformedExpandedDateTimeLiteral(value) {
|
|
1074
|
+
if (Array.isArray(value)) return value.some(hasMalformedExpandedDateTimeLiteral);
|
|
1075
|
+
return isPlainObject(value) && "@value" in value && isMalformedDateTimeLiteral(value["@value"]);
|
|
1076
|
+
}
|
|
1077
|
+
function hasMalformedExpandedQuestionClosedLiteral(value) {
|
|
1078
|
+
if (Array.isArray(value)) return value.some(hasMalformedExpandedQuestionClosedLiteral);
|
|
1079
|
+
if (!isPlainObject(value) || !("@value" in value)) return false;
|
|
1080
|
+
const literal = value["@value"];
|
|
1081
|
+
if (typeof literal === "boolean") return false;
|
|
1082
|
+
if (typeof literal !== "string") return false;
|
|
1083
|
+
if (value["@type"] !== XSD_DATE_TIME_IRI) return false;
|
|
1084
|
+
if (new Date(literal).toString() === "Invalid Date") return false;
|
|
1085
|
+
return isMalformedDateTimeLiteral(literal);
|
|
1086
|
+
}
|
|
1087
|
+
function hasMalformedExpandedDurationLiteral(value) {
|
|
1088
|
+
if (Array.isArray(value)) return value.some(hasMalformedExpandedDurationLiteral);
|
|
1089
|
+
return isPlainObject(value) && "@value" in value && isMalformedDurationLiteral(value["@value"]);
|
|
1090
|
+
}
|
|
1091
|
+
function hasMalformedKnownTemporalLiteralInternal(value, visited) {
|
|
1092
|
+
if (Array.isArray(value)) return value.some((item) => hasMalformedKnownTemporalLiteralInternal(item, visited));
|
|
1093
|
+
if (!isPlainObject(value)) return false;
|
|
1094
|
+
if (visited.has(value)) return false;
|
|
1095
|
+
visited.add(value);
|
|
1096
|
+
if ("@value" in value) return false;
|
|
1097
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1098
|
+
if (TEMPORAL_DATE_TIME_IRIS.has(key)) {
|
|
1099
|
+
if (hasMalformedExpandedDateTimeLiteral(child)) return true;
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
if (key === QUESTION_CLOSED_IRI) {
|
|
1103
|
+
if (hasMalformedExpandedQuestionClosedLiteral(child)) return true;
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
if (TEMPORAL_DURATION_IRIS.has(key)) {
|
|
1107
|
+
if (hasMalformedExpandedDurationLiteral(child)) return true;
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
if (hasMalformedKnownTemporalLiteralInternal(child, visited)) return true;
|
|
1111
|
+
}
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
async function hasMalformedKnownTemporalLiteral(value, contextLoader) {
|
|
1115
|
+
try {
|
|
1116
|
+
return hasMalformedKnownTemporalLiteralInternal(await jsonld.expand(value, {
|
|
1117
|
+
documentLoader: getNormalizationContextLoader(contextLoader),
|
|
1118
|
+
keepFreeFloatingNodes: true
|
|
1119
|
+
}), /* @__PURE__ */ new Set());
|
|
1120
|
+
} catch {
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
//#endregion
|
|
1032
1125
|
//#region src/federation/handler.ts
|
|
1126
|
+
const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
|
|
1127
|
+
function isRemoteContextLoadingFailure$1(error) {
|
|
1128
|
+
return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
|
|
1129
|
+
}
|
|
1130
|
+
function isPermanentRemoteContextError$1(error) {
|
|
1131
|
+
if (!(error instanceof Error) || error.name !== "jsonld.InvalidUrl") return false;
|
|
1132
|
+
const details = error.details;
|
|
1133
|
+
if (details?.code === "invalid remote context") return true;
|
|
1134
|
+
return isRemoteContextLoadingFailure$1(error) && typeof details?.url === "string" && !URL.canParse(details.url) && isClearlyMalformedContextReference(details.url);
|
|
1135
|
+
}
|
|
1136
|
+
function isInvalidJsonLdError(error) {
|
|
1137
|
+
if (!(error instanceof Error)) return false;
|
|
1138
|
+
const name = error.name;
|
|
1139
|
+
return name === "UnsafeJsonLdError" || error instanceof InvalidContextReferenceError || isPermanentRemoteContextError$1(error) || name === "jsonld.SyntaxError" && !isRemoteContextLoadingFailure$1(error);
|
|
1140
|
+
}
|
|
1141
|
+
function isValidationTypeError(error) {
|
|
1142
|
+
return error instanceof TypeError && (/^(Invalid JSON-LD:|Invalid type:|Unexpected type:)/.test(error.message) || isInvalidUrlTypeError(error));
|
|
1143
|
+
}
|
|
1144
|
+
function isPermanentActivityParseError(error) {
|
|
1145
|
+
return isInvalidJsonLdError(error) || isValidationTypeError(error);
|
|
1146
|
+
}
|
|
1147
|
+
function hasHttpSignatureHeaders(request) {
|
|
1148
|
+
return request.headers.has("Signature") || request.headers.has("Signature-Input");
|
|
1149
|
+
}
|
|
1150
|
+
function hasObjectIntegrityProof(json) {
|
|
1151
|
+
return typeof json === "object" && json != null && "proof" in json;
|
|
1152
|
+
}
|
|
1033
1153
|
/**
|
|
1034
1154
|
* Handles an actor request.
|
|
1035
1155
|
* @template TContextData The context data to pass to the context.
|
|
@@ -1101,8 +1221,8 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
1101
1221
|
* @param parameters The parameters for handling the collection.
|
|
1102
1222
|
* @returns A promise that resolves to an HTTP response.
|
|
1103
1223
|
*/
|
|
1104
|
-
async function handleCollection(request, { name: name$
|
|
1105
|
-
const spanName = name$
|
|
1224
|
+
async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
|
|
1225
|
+
const spanName = name$1.trim().replace(/\s+/g, "_");
|
|
1106
1226
|
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
1107
1227
|
const tracer = tracerProvider.getTracer(name, version);
|
|
1108
1228
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
@@ -1144,7 +1264,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
1144
1264
|
collection = new OrderedCollection({
|
|
1145
1265
|
id: baseUri,
|
|
1146
1266
|
totalItems: totalItems == null ? null : Number(totalItems),
|
|
1147
|
-
items: filterCollectionItems(itemsOrResponse, name$
|
|
1267
|
+
items: filterCollectionItems(itemsOrResponse, name$1, filterPredicate)
|
|
1148
1268
|
});
|
|
1149
1269
|
} else {
|
|
1150
1270
|
const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
|
|
@@ -1165,7 +1285,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
1165
1285
|
} else {
|
|
1166
1286
|
const uri = new URL(baseUri);
|
|
1167
1287
|
uri.searchParams.set("cursor", cursor);
|
|
1168
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
1288
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
|
|
1169
1289
|
kind: SpanKind.SERVER,
|
|
1170
1290
|
attributes: {
|
|
1171
1291
|
"activitypub.collection.id": uri.href,
|
|
@@ -1209,7 +1329,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
1209
1329
|
id: uri,
|
|
1210
1330
|
prev,
|
|
1211
1331
|
next,
|
|
1212
|
-
items: filterCollectionItems(items, name$
|
|
1332
|
+
items: filterCollectionItems(items, name$1, filterPredicate),
|
|
1213
1333
|
partOf
|
|
1214
1334
|
});
|
|
1215
1335
|
}
|
|
@@ -1556,29 +1676,105 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1556
1676
|
});
|
|
1557
1677
|
}
|
|
1558
1678
|
const keyCache = new KvKeyCache(kv, kvPrefixes.publicKey, ctx);
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1679
|
+
const jsonWithoutSig = detachSignature(json);
|
|
1680
|
+
const hasLdSignature = hasSignature(json);
|
|
1681
|
+
const canAttemptAlternateAuthAfterLdSignatureFailure = skipSignatureVerification || hasHttpSignatureHeaders(request) || hasObjectIntegrityProof(jsonWithoutSig);
|
|
1682
|
+
let deferredLdSignatureError = void 0;
|
|
1683
|
+
const respondInvalidActivity = async (error) => {
|
|
1684
|
+
logger.error("Failed to parse activity:\n{error}", {
|
|
1685
|
+
recipient,
|
|
1686
|
+
activity: json,
|
|
1687
|
+
error
|
|
1567
1688
|
});
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1689
|
+
try {
|
|
1690
|
+
await inboxErrorHandler?.(ctx, error);
|
|
1691
|
+
} catch (error) {
|
|
1692
|
+
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
1693
|
+
error,
|
|
1694
|
+
activity: json,
|
|
1695
|
+
recipient
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
span.setStatus({
|
|
1699
|
+
code: SpanStatusCode.ERROR,
|
|
1700
|
+
message: `Failed to parse activity:\n${error}`
|
|
1701
|
+
});
|
|
1702
|
+
return new Response("Invalid activity.", {
|
|
1703
|
+
status: 400,
|
|
1704
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1705
|
+
});
|
|
1706
|
+
};
|
|
1707
|
+
let compactedJson = json;
|
|
1708
|
+
let compactedJsonWithoutSig = jsonWithoutSig;
|
|
1709
|
+
let ldSigVerified = false;
|
|
1710
|
+
if (hasLdSignature) {
|
|
1711
|
+
try {
|
|
1712
|
+
compactedJson = await compactJsonLd(json, ctx.contextLoader);
|
|
1713
|
+
} catch (error) {
|
|
1714
|
+
if (isInvalidJsonLdError(error)) {
|
|
1715
|
+
logger.error("Failed to parse JSON-LD:\n{error}", {
|
|
1716
|
+
recipient,
|
|
1717
|
+
error
|
|
1718
|
+
});
|
|
1719
|
+
return new Response("Invalid JSON-LD.", {
|
|
1720
|
+
status: 400,
|
|
1721
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
|
|
1725
|
+
if (!skipSignatureVerification) deferredLdSignatureError = error;
|
|
1726
|
+
logger.debug("Failed to normalize JSON-LD for Linked Data Signatures; deferring to another authentication path only if it verifies:\n{error}", {
|
|
1571
1727
|
recipient,
|
|
1572
1728
|
error
|
|
1573
1729
|
});
|
|
1574
|
-
return new Response("Invalid JSON-LD.", {
|
|
1575
|
-
status: 400,
|
|
1576
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1577
|
-
});
|
|
1578
1730
|
}
|
|
1579
|
-
|
|
1731
|
+
if (compactedJson !== json) {
|
|
1732
|
+
compactedJsonWithoutSig = detachSignature(compactedJson);
|
|
1733
|
+
try {
|
|
1734
|
+
ldSigVerified = await verifyCompactJsonLd(compactedJson, {
|
|
1735
|
+
contextLoader: ctx.contextLoader,
|
|
1736
|
+
documentLoader: ctx.documentLoader,
|
|
1737
|
+
keyCache,
|
|
1738
|
+
meterProvider,
|
|
1739
|
+
tracerProvider
|
|
1740
|
+
});
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
|
|
1743
|
+
if (isInvalidJsonLdError(error)) {
|
|
1744
|
+
logger.error("Failed to parse JSON-LD:\n{error}", {
|
|
1745
|
+
recipient,
|
|
1746
|
+
error
|
|
1747
|
+
});
|
|
1748
|
+
return new Response("Invalid JSON-LD.", {
|
|
1749
|
+
status: 400,
|
|
1750
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
|
|
1754
|
+
if (!skipSignatureVerification) try {
|
|
1755
|
+
await Object$1.fromJsonLd(compactedJson, {
|
|
1756
|
+
contextLoader: getNormalizationContextLoader(ctx.contextLoader),
|
|
1757
|
+
documentLoader: ctx.documentLoader,
|
|
1758
|
+
tracerProvider
|
|
1759
|
+
});
|
|
1760
|
+
} catch (parseError) {
|
|
1761
|
+
if (parseError instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(parseError);
|
|
1762
|
+
if (isInvalidJsonLdError(parseError)) {
|
|
1763
|
+
logger.error("Failed to parse JSON-LD:\n{error}", {
|
|
1764
|
+
recipient,
|
|
1765
|
+
error: parseError
|
|
1766
|
+
});
|
|
1767
|
+
return new Response("Invalid JSON-LD.", {
|
|
1768
|
+
status: 400,
|
|
1769
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
deferredLdSignatureError = parseError;
|
|
1773
|
+
}
|
|
1774
|
+
ldSigVerified = false;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1580
1777
|
}
|
|
1581
|
-
const jsonWithoutSig = detachSignature(json);
|
|
1582
1778
|
let activity = null;
|
|
1583
1779
|
let activityVerified = false;
|
|
1584
1780
|
if (ldSigVerified) {
|
|
@@ -1586,7 +1782,16 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1586
1782
|
recipient,
|
|
1587
1783
|
json
|
|
1588
1784
|
});
|
|
1589
|
-
|
|
1785
|
+
try {
|
|
1786
|
+
activity = await Activity.fromJsonLd(compactedJsonWithoutSig, {
|
|
1787
|
+
...ctx,
|
|
1788
|
+
contextLoader: getNormalizationContextLoader(ctx.contextLoader)
|
|
1789
|
+
});
|
|
1790
|
+
} catch (error) {
|
|
1791
|
+
if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(compactedJsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
|
|
1792
|
+
if (!isPermanentActivityParseError(error)) throw error;
|
|
1793
|
+
return await respondInvalidActivity(error);
|
|
1794
|
+
}
|
|
1590
1795
|
activityVerified = true;
|
|
1591
1796
|
} else {
|
|
1592
1797
|
logger.debug("Linked Data Signatures are not verified.", {
|
|
@@ -1595,13 +1800,22 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1595
1800
|
});
|
|
1596
1801
|
try {
|
|
1597
1802
|
activity = await verifyObject(Activity, jsonWithoutSig, {
|
|
1598
|
-
contextLoader: ctx.contextLoader,
|
|
1803
|
+
contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
|
|
1599
1804
|
documentLoader: ctx.documentLoader,
|
|
1600
1805
|
keyCache,
|
|
1601
1806
|
meterProvider,
|
|
1602
1807
|
tracerProvider
|
|
1603
1808
|
});
|
|
1604
1809
|
} catch (error) {
|
|
1810
|
+
if (error instanceof RangeError && await hasMalformedKnownTemporalLiteral(jsonWithoutSig, ctx.contextLoader)) return await respondInvalidActivity(error);
|
|
1811
|
+
if (deferredLdSignatureError != null) {
|
|
1812
|
+
logger.debug("Object Integrity Proof fallback did not supersede a deferred Linked Data Signature failure:\n{error}", {
|
|
1813
|
+
recipient,
|
|
1814
|
+
error
|
|
1815
|
+
});
|
|
1816
|
+
activity = null;
|
|
1817
|
+
}
|
|
1818
|
+
if (!isPermanentActivityParseError(error)) throw error;
|
|
1605
1819
|
logger.error("Failed to parse activity:\n{error}", {
|
|
1606
1820
|
recipient,
|
|
1607
1821
|
activity: json,
|
|
@@ -1650,6 +1864,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1650
1864
|
tracerProvider
|
|
1651
1865
|
});
|
|
1652
1866
|
if (verification.verified === false) {
|
|
1867
|
+
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
1653
1868
|
const reason = verification.reason;
|
|
1654
1869
|
const remoteHost = "keyId" in reason && reason.keyId != null ? getRemoteHost(reason.keyId) : void 0;
|
|
1655
1870
|
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure(reason.type, remoteHost);
|
|
@@ -1727,7 +1942,15 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1727
1942
|
}
|
|
1728
1943
|
httpSigKey = verification.key;
|
|
1729
1944
|
}
|
|
1730
|
-
|
|
1945
|
+
try {
|
|
1946
|
+
activity = await Activity.fromJsonLd(jsonWithoutSig, {
|
|
1947
|
+
...ctx,
|
|
1948
|
+
contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader)
|
|
1949
|
+
});
|
|
1950
|
+
} catch (error) {
|
|
1951
|
+
if (!isPermanentActivityParseError(error)) throw error;
|
|
1952
|
+
return await respondInvalidActivity(error);
|
|
1953
|
+
}
|
|
1731
1954
|
}
|
|
1732
1955
|
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
1733
1956
|
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
|
|
@@ -1739,6 +1962,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1739
1962
|
"http_signatures.key_id": httpSigKey?.id?.href ?? ""
|
|
1740
1963
|
});
|
|
1741
1964
|
if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
|
|
1965
|
+
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
1742
1966
|
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("actorKeyMismatch", httpSigKey.id == null ? void 0 : getRemoteHost(httpSigKey.id));
|
|
1743
1967
|
logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
|
|
1744
1968
|
activity: json,
|
|
@@ -1765,10 +1989,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1765
1989
|
const routeResult = await routeActivity({
|
|
1766
1990
|
context: ctx,
|
|
1767
1991
|
json,
|
|
1992
|
+
originalJson: json,
|
|
1993
|
+
normalizedActivity: hasLdSignature && compactedJson !== json ? compactedJson : void 0,
|
|
1994
|
+
ldSignatureVerified: hasLdSignature ? ldSigVerified : void 0,
|
|
1768
1995
|
activity,
|
|
1769
1996
|
recipient,
|
|
1770
1997
|
inboxListeners,
|
|
1771
1998
|
inboxContextFactory,
|
|
1999
|
+
listenerInboxContextFactory: ldSigVerified ? inboxContextFactory[rawInboxContextFactorySymbol] : void 0,
|
|
1772
2000
|
inboxErrorHandler,
|
|
1773
2001
|
kv,
|
|
1774
2002
|
kvPrefixes,
|
|
@@ -1895,8 +2123,8 @@ var CustomCollectionHandler = class {
|
|
|
1895
2123
|
* @param CollectionPage The CollectionPage constructor.
|
|
1896
2124
|
* @param filterPredicate Optional filter predicate for items.
|
|
1897
2125
|
*/
|
|
1898
|
-
constructor(name$
|
|
1899
|
-
this.name = name$
|
|
2126
|
+
constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
|
|
2127
|
+
this.name = name$2;
|
|
1900
2128
|
this.values = values;
|
|
1901
2129
|
this.context = context;
|
|
1902
2130
|
this.callbacks = callbacks;
|
|
@@ -2782,6 +3010,18 @@ var middleware_exports = /* @__PURE__ */ __exportAll({
|
|
|
2782
3010
|
OutboxContextImpl: () => OutboxContextImpl,
|
|
2783
3011
|
createFederation: () => createFederation
|
|
2784
3012
|
});
|
|
3013
|
+
function isRemoteContextLoadingFailure(error) {
|
|
3014
|
+
return error instanceof Error && typeof error.details === "object" && error.details != null && error.details.code === "loading remote context failed";
|
|
3015
|
+
}
|
|
3016
|
+
function isPermanentRemoteContextError(error) {
|
|
3017
|
+
if (!(error instanceof Error) || error.name !== "jsonld.InvalidUrl") return false;
|
|
3018
|
+
const details = error.details;
|
|
3019
|
+
if (details?.code === "invalid remote context") return true;
|
|
3020
|
+
return isRemoteContextLoadingFailure(error) && typeof details?.url === "string" && !URL.canParse(details.url) && isClearlyMalformedContextReference(details.url);
|
|
3021
|
+
}
|
|
3022
|
+
function isPermanentInboxParseError(error) {
|
|
3023
|
+
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));
|
|
3024
|
+
}
|
|
2785
3025
|
/**
|
|
2786
3026
|
* Create a new {@link Federation} instance.
|
|
2787
3027
|
* @param parameters Parameters for initializing the instance.
|
|
@@ -3284,78 +3524,37 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3284
3524
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
3285
3525
|
if (identity != null) context = this.#createContext(baseUrl, ctxData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
3286
3526
|
}
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
const cacheKey = activity.id == null ? null : [
|
|
3293
|
-
...this.kvPrefixes.activityIdempotence,
|
|
3294
|
-
context.origin,
|
|
3295
|
-
activity.id.href
|
|
3296
|
-
];
|
|
3297
|
-
if (cacheKey != null) {
|
|
3298
|
-
if (await this.kv.get(cacheKey) === true) {
|
|
3299
|
-
logger.debug("Activity {activityId} has already been processed.", {
|
|
3300
|
-
activityId: activity.id?.href,
|
|
3301
|
-
activity: message.activity,
|
|
3302
|
-
recipient: message.identifier
|
|
3303
|
-
});
|
|
3304
|
-
recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
3305
|
-
return;
|
|
3306
|
-
}
|
|
3307
|
-
}
|
|
3308
|
-
await this._getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
3309
|
-
const dispatched = this.inboxListeners?.dispatchWithClass(activity);
|
|
3310
|
-
if (dispatched == null) {
|
|
3311
|
-
logger.error("Unsupported activity type:\n{activity}", {
|
|
3312
|
-
activityId: activity.id?.href,
|
|
3313
|
-
activity: message.activity,
|
|
3314
|
-
recipient: message.identifier,
|
|
3315
|
-
trial: message.attempt
|
|
3316
|
-
});
|
|
3317
|
-
span.setStatus({
|
|
3318
|
-
code: SpanStatusCode.ERROR,
|
|
3319
|
-
message: `Unsupported activity type: ${activityType}`
|
|
3320
|
-
});
|
|
3321
|
-
recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
3322
|
-
span.end();
|
|
3323
|
-
return;
|
|
3324
|
-
}
|
|
3325
|
-
const { class: cls, listener } = dispatched;
|
|
3326
|
-
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
3327
|
-
try {
|
|
3328
|
-
const started = performance.now();
|
|
3329
|
-
try {
|
|
3330
|
-
await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, activityType), activity);
|
|
3331
|
-
} finally {
|
|
3332
|
-
getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
3333
|
-
}
|
|
3334
|
-
recordInboxActivity(this.meterProvider, "processed", activityType);
|
|
3335
|
-
} catch (error) {
|
|
3527
|
+
await this._getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (listenerSpan) => {
|
|
3528
|
+
let activity = null;
|
|
3529
|
+
let cacheKey = null;
|
|
3530
|
+
let activityType;
|
|
3531
|
+
const reportInboxError = async (error) => {
|
|
3336
3532
|
try {
|
|
3337
3533
|
await this.inboxErrorHandler?.(context, error);
|
|
3338
3534
|
} catch (error) {
|
|
3339
3535
|
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
3340
3536
|
error,
|
|
3341
3537
|
trial: message.attempt,
|
|
3342
|
-
activityId: activity
|
|
3538
|
+
activityId: activity?.id?.href,
|
|
3343
3539
|
activity: message.activity,
|
|
3344
3540
|
recipient: message.identifier
|
|
3345
3541
|
});
|
|
3346
3542
|
}
|
|
3543
|
+
};
|
|
3544
|
+
const handleRetriableFailure = async (error) => {
|
|
3545
|
+
await reportInboxError(error);
|
|
3347
3546
|
if (this.inboxQueue?.nativeRetrial) {
|
|
3348
3547
|
logger.error("Failed to process the incoming activity {activityId}; backend will handle retry:\n{error}", {
|
|
3349
3548
|
error,
|
|
3350
|
-
activityId: activity
|
|
3549
|
+
activityId: activity?.id?.href,
|
|
3351
3550
|
activity: message.activity,
|
|
3352
3551
|
recipient: message.identifier
|
|
3353
3552
|
});
|
|
3354
|
-
|
|
3553
|
+
listenerSpan.setStatus({
|
|
3355
3554
|
code: SpanStatusCode.ERROR,
|
|
3356
3555
|
message: String(error)
|
|
3357
3556
|
});
|
|
3358
|
-
|
|
3557
|
+
listenerSpan.end();
|
|
3359
3558
|
throw error;
|
|
3360
3559
|
}
|
|
3361
3560
|
const delay = this.inboxRetryPolicy({
|
|
@@ -3366,20 +3565,27 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3366
3565
|
logger.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
|
|
3367
3566
|
error,
|
|
3368
3567
|
attempt: message.attempt,
|
|
3369
|
-
activityId: activity
|
|
3568
|
+
activityId: activity?.id?.href,
|
|
3370
3569
|
activity: message.activity,
|
|
3371
3570
|
recipient: message.identifier
|
|
3372
3571
|
});
|
|
3572
|
+
if (this.inboxQueue == null) {
|
|
3573
|
+
listenerSpan.setStatus({
|
|
3574
|
+
code: SpanStatusCode.ERROR,
|
|
3575
|
+
message: String(error)
|
|
3576
|
+
});
|
|
3577
|
+
listenerSpan.end();
|
|
3578
|
+
throw error;
|
|
3579
|
+
}
|
|
3373
3580
|
const retryMessage = {
|
|
3374
3581
|
...message,
|
|
3375
3582
|
attempt: message.attempt + 1
|
|
3376
3583
|
};
|
|
3377
|
-
|
|
3378
|
-
if (
|
|
3379
|
-
await inboxQueue.enqueue(retryMessage, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
|
|
3584
|
+
await this.inboxQueue.enqueue(retryMessage, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
|
|
3585
|
+
if (activityType != null) {
|
|
3380
3586
|
getFederationMetrics(this.meterProvider).recordQueueTaskEnqueued({
|
|
3381
3587
|
role: "inbox",
|
|
3382
|
-
queue: inboxQueue,
|
|
3588
|
+
queue: this.inboxQueue,
|
|
3383
3589
|
activityType
|
|
3384
3590
|
}, retryMessage.attempt);
|
|
3385
3591
|
recordInboxActivity(this.meterProvider, "retried", activityType);
|
|
@@ -3387,26 +3593,137 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3387
3593
|
} else {
|
|
3388
3594
|
logger.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
|
|
3389
3595
|
error,
|
|
3390
|
-
activityId: activity
|
|
3596
|
+
activityId: activity?.id?.href,
|
|
3391
3597
|
activity: message.activity,
|
|
3392
3598
|
recipient: message.identifier
|
|
3393
3599
|
});
|
|
3394
|
-
recordInboxActivity(this.meterProvider, "abandoned", activityType);
|
|
3600
|
+
if (activityType != null) recordInboxActivity(this.meterProvider, "abandoned", activityType);
|
|
3395
3601
|
}
|
|
3396
|
-
|
|
3602
|
+
listenerSpan.setStatus({
|
|
3397
3603
|
code: SpanStatusCode.ERROR,
|
|
3398
3604
|
message: String(error)
|
|
3399
3605
|
});
|
|
3400
|
-
|
|
3606
|
+
listenerSpan.end();
|
|
3607
|
+
};
|
|
3608
|
+
let dispatched;
|
|
3609
|
+
let parseInput = void 0;
|
|
3610
|
+
let parseContextLoader = context.contextLoader;
|
|
3611
|
+
try {
|
|
3612
|
+
const hasSignatureField = hasSignature(message.activity);
|
|
3613
|
+
const shouldParseFromNormalizedSignedPayload = message.ldSignatureVerified === true || message.normalizedActivity != null || message.ldSignatureVerified == null && hasSignatureField;
|
|
3614
|
+
const parseContext = hasSignatureField ? {
|
|
3615
|
+
...context,
|
|
3616
|
+
contextLoader: getNormalizationContextLoader(context.contextLoader)
|
|
3617
|
+
} : {
|
|
3618
|
+
...context,
|
|
3619
|
+
contextLoader: wrapContextLoaderForJsonLd(context.contextLoader)
|
|
3620
|
+
};
|
|
3621
|
+
parseContextLoader = parseContext.contextLoader;
|
|
3622
|
+
let normalizedActivity;
|
|
3623
|
+
if (shouldParseFromNormalizedSignedPayload) {
|
|
3624
|
+
normalizedActivity = message.normalizedActivity ?? await compactJsonLd(message.activity, context.contextLoader);
|
|
3625
|
+
assertSafeJsonLd(normalizedActivity);
|
|
3626
|
+
}
|
|
3627
|
+
parseInput = shouldParseFromNormalizedSignedPayload ? detachSignature(normalizedActivity) : hasSignatureField ? detachSignature(message.activity) : message.activity;
|
|
3628
|
+
activity = await Activity.fromJsonLd(parseInput, parseContext);
|
|
3629
|
+
activityType = getTypeId(activity).href;
|
|
3630
|
+
span.setAttribute("activitypub.activity.type", activityType);
|
|
3631
|
+
listenerSpan.setAttribute("activitypub.activity.type", activityType);
|
|
3632
|
+
onActivityType?.(activityType);
|
|
3633
|
+
if (activity.id != null) {
|
|
3634
|
+
span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
3635
|
+
listenerSpan.setAttribute("activitypub.activity.id", activity.id.href);
|
|
3636
|
+
}
|
|
3637
|
+
cacheKey = activity.id == null ? null : [
|
|
3638
|
+
...this.kvPrefixes.activityIdempotence,
|
|
3639
|
+
context.origin,
|
|
3640
|
+
activity.id.href
|
|
3641
|
+
];
|
|
3642
|
+
if (cacheKey != null) {
|
|
3643
|
+
if (await this.kv.get(cacheKey) === true) {
|
|
3644
|
+
logger.debug("Activity {activityId} has already been processed.", {
|
|
3645
|
+
activityId: activity.id?.href,
|
|
3646
|
+
activity: message.activity,
|
|
3647
|
+
recipient: message.identifier
|
|
3648
|
+
});
|
|
3649
|
+
recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
3650
|
+
listenerSpan.end();
|
|
3651
|
+
return;
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
dispatched = this.inboxListeners?.dispatchWithClass(activity);
|
|
3655
|
+
} catch (error) {
|
|
3656
|
+
if (activity == null && error instanceof RangeError && await hasMalformedKnownTemporalLiteral(parseInput, parseContextLoader)) {
|
|
3657
|
+
await reportInboxError(error);
|
|
3658
|
+
logger.error("Failed to parse the queued incoming activity {activityId}:\n{error}", {
|
|
3659
|
+
error,
|
|
3660
|
+
trial: message.attempt,
|
|
3661
|
+
activityId: null,
|
|
3662
|
+
activity: message.activity,
|
|
3663
|
+
recipient: message.identifier
|
|
3664
|
+
});
|
|
3665
|
+
listenerSpan.setStatus({
|
|
3666
|
+
code: SpanStatusCode.ERROR,
|
|
3667
|
+
message: String(error)
|
|
3668
|
+
});
|
|
3669
|
+
listenerSpan.end();
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
if (isPermanentInboxParseError(error)) {
|
|
3673
|
+
await reportInboxError(error);
|
|
3674
|
+
logger.error("Failed to parse the queued incoming activity {activityId}:\n{error}", {
|
|
3675
|
+
error,
|
|
3676
|
+
trial: message.attempt,
|
|
3677
|
+
activityId: activity?.id?.href,
|
|
3678
|
+
activity: message.activity,
|
|
3679
|
+
recipient: message.identifier
|
|
3680
|
+
});
|
|
3681
|
+
listenerSpan.setStatus({
|
|
3682
|
+
code: SpanStatusCode.ERROR,
|
|
3683
|
+
message: String(error)
|
|
3684
|
+
});
|
|
3685
|
+
listenerSpan.end();
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3688
|
+
await handleRetriableFailure(error);
|
|
3689
|
+
return;
|
|
3690
|
+
}
|
|
3691
|
+
if (dispatched == null) {
|
|
3692
|
+
logger.error("Unsupported activity type:\n{activity}", {
|
|
3693
|
+
activityId: activity.id?.href,
|
|
3694
|
+
activity: message.activity,
|
|
3695
|
+
recipient: message.identifier,
|
|
3696
|
+
trial: message.attempt
|
|
3697
|
+
});
|
|
3698
|
+
listenerSpan.setStatus({
|
|
3699
|
+
code: SpanStatusCode.ERROR,
|
|
3700
|
+
message: `Unsupported activity type: ${activityType}`
|
|
3701
|
+
});
|
|
3702
|
+
recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
3703
|
+
listenerSpan.end();
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
const { class: cls, listener } = dispatched;
|
|
3707
|
+
listenerSpan.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
3708
|
+
try {
|
|
3709
|
+
const started = performance.now();
|
|
3710
|
+
try {
|
|
3711
|
+
await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, activityType), activity);
|
|
3712
|
+
} finally {
|
|
3713
|
+
getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
3714
|
+
}
|
|
3715
|
+
recordInboxActivity(this.meterProvider, "processed", activityType);
|
|
3716
|
+
} catch (error) {
|
|
3717
|
+
await handleRetriableFailure(error);
|
|
3401
3718
|
return;
|
|
3402
3719
|
}
|
|
3403
3720
|
if (cacheKey != null) await this.kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
3404
3721
|
logger.info("Activity {activityId} has been processed.", {
|
|
3405
|
-
activityId: activity
|
|
3722
|
+
activityId: activity?.id?.href,
|
|
3406
3723
|
activity: message.activity,
|
|
3407
3724
|
recipient: message.identifier
|
|
3408
3725
|
});
|
|
3409
|
-
|
|
3726
|
+
listenerSpan.end();
|
|
3410
3727
|
});
|
|
3411
3728
|
}
|
|
3412
3729
|
startQueue(contextData, options = {}) {
|
|
@@ -3787,16 +4104,18 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3787
4104
|
onNotFound
|
|
3788
4105
|
});
|
|
3789
4106
|
context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier: route.values.identifier }) });
|
|
3790
|
-
case "sharedInbox":
|
|
4107
|
+
case "sharedInbox": {
|
|
3791
4108
|
if (routeName !== "inbox" && this.sharedInboxKeyDispatcher != null) {
|
|
3792
4109
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
3793
4110
|
if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
3794
4111
|
}
|
|
3795
4112
|
if (!this.manuallyStartQueue) this._startQueueInternal(contextData);
|
|
4113
|
+
const inboxContextFactory = context.toInboxContext.bind(context);
|
|
4114
|
+
inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
|
|
3796
4115
|
return await handleInbox(request, {
|
|
3797
4116
|
recipient: route.values.identifier ?? null,
|
|
3798
4117
|
context,
|
|
3799
|
-
inboxContextFactory
|
|
4118
|
+
inboxContextFactory,
|
|
3800
4119
|
kv: this.kv,
|
|
3801
4120
|
kvPrefixes: this.kvPrefixes,
|
|
3802
4121
|
queue: this.inboxQueue,
|
|
@@ -3812,6 +4131,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3812
4131
|
tracerProvider: this.tracerProvider,
|
|
3813
4132
|
idempotencyStrategy: this.idempotencyStrategy
|
|
3814
4133
|
});
|
|
4134
|
+
}
|
|
3815
4135
|
case "following": return await handleCollection(request, {
|
|
3816
4136
|
name: "following",
|
|
3817
4137
|
identifier: route.values.identifier,
|
|
@@ -4545,6 +4865,7 @@ var ContextImpl = class ContextImpl {
|
|
|
4545
4865
|
const routeResult = await routeActivity({
|
|
4546
4866
|
context: this,
|
|
4547
4867
|
json,
|
|
4868
|
+
ldSignatureVerified: false,
|
|
4548
4869
|
activity,
|
|
4549
4870
|
recipient,
|
|
4550
4871
|
inboxListeners: this.federation.inboxListeners,
|
|
@@ -4833,6 +5154,14 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
|
|
|
4833
5154
|
}
|
|
4834
5155
|
var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
4835
5156
|
recipient;
|
|
5157
|
+
/**
|
|
5158
|
+
* The original received activity payload.
|
|
5159
|
+
*
|
|
5160
|
+
* Fedify may normalize a Linked Data Signature payload internally for safe
|
|
5161
|
+
* parsing, but forwarding must keep the sender's payload unchanged so
|
|
5162
|
+
* third-party signatures/proofs remain intact.
|
|
5163
|
+
* @internal
|
|
5164
|
+
*/
|
|
4836
5165
|
activity;
|
|
4837
5166
|
activityId;
|
|
4838
5167
|
activityType;
|