@fedify/fedify 2.4.0-dev.2200 → 2.4.0-dev.2212
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/audience-CHQ-FlLD.mjs +95 -0
- package/dist/{builder-BGSNMnVH.mjs → builder-CKAVFvcH.mjs} +2 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/public-audience.test.mjs +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-Zpg7Fbk9.mjs → compound-proof-BP5SOdXv.mjs} +2 -2
- package/dist/{context-B2rlkkyJ.mjs → context-8Tu9kea0.mjs} +2 -0
- package/dist/{context-CDcAgm4l.d.cts → context-Bl_Y7T_3.d.cts} +80 -1
- package/dist/{context-9T--NSo4.d.ts → context-DMd11IAu.d.ts} +80 -1
- package/dist/{deno-CxoV48e0.mjs → deno-BKp9fYB3.mjs} +1 -1
- package/dist/{docloader-U141HKIl.mjs → docloader-BjJreYQU.mjs} +3 -3
- package/dist/federation/audience.test.d.mts +2 -0
- package/dist/federation/audience.test.mjs +130 -0
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +4 -4
- package/dist/federation/handler-compound-proof.test.mjs +5 -5
- package/dist/federation/handler.test.mjs +7 -7
- package/dist/federation/hashlink-media.test.mjs +3 -3
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +2 -2
- package/dist/federation/middleware.test.mjs +9 -9
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +6 -6
- package/dist/federation/portable-actor.test.mjs +4 -4
- package/dist/federation/portable-chain.test.mjs +4 -4
- package/dist/federation/portable-collection.test.mjs +8 -6
- package/dist/federation/portable-delivery.test.d.mts +2 -0
- package/dist/federation/portable-delivery.test.mjs +746 -0
- package/dist/federation/portable-gateway-keys.test.mjs +6 -6
- package/dist/federation/portable-inbox.test.mjs +5 -5
- package/dist/federation/portable-parse-uri.test.mjs +2 -2
- package/dist/federation/portable-route-activity.test.mjs +3 -3
- package/dist/federation/portable.test.mjs +234 -19
- package/dist/federation/send.test.mjs +4 -4
- package/dist/federation/tasks/enqueue.test.mjs +2 -2
- package/dist/federation/tasks/tasks.test.mjs +3 -3
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +3 -3
- package/dist/{federation-Ij8VLHpy.cjs → federation-B-0EXZPH.cjs} +679 -35
- package/dist/{http-BQq1Uldg.mjs → http--IGUT1mg.mjs} +5 -5
- package/dist/{http-D8XakCKn.cjs → http-ByBlnY3D.cjs} +1 -1
- package/dist/{http-CshNXXui.js → http-Di8yiDMY.js} +1 -1
- package/dist/{key-DYgoirTo.mjs → key-ChfV9C3v.mjs} +2 -2
- package/dist/{kv-cache-f_GNq0e7.cjs → kv-cache-BDwUzxBJ.cjs} +1 -1
- package/dist/{kv-cache-D0Mn-VQW.mjs → kv-cache-DFIUR-E5.mjs} +1 -1
- package/dist/{kv-cache-Bpnt_Cck.js → kv-cache-DpwDPt9V.js} +1 -1
- package/dist/{ld-eIekvNtm.mjs → ld-DBBVabBC.mjs} +3 -3
- package/dist/{metrics-BVpdjsaR.mjs → metrics-DPQKFkiJ.mjs} +1 -1
- package/dist/{middleware-BbTJ6C1-.mjs → middleware-BW7-tHAG.mjs} +422 -44
- package/dist/{middleware-rpYJG85a.mjs → middleware-G9zpxUsD.mjs} +1 -1
- package/dist/{middleware-CS3H_4tQ.js → middleware-VXGLiHtY.js} +681 -37
- package/dist/{mod-B3OBLj5g.d.ts → mod-BVNGHgVr.d.ts} +1 -1
- package/dist/{mod-D58KCqUg.d.cts → mod-Cyy1Wahy.d.cts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/client.test.mjs +1 -1
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/{owner-DpxyMeo9.mjs → owner-BW5VsXG3.mjs} +3 -3
- package/dist/{portable-collection-DyJwtPz1.cjs → portable-collection-CXcAlwei.cjs} +1 -1
- package/dist/{portable-collection-DOL3l2C9.mjs → portable-collection-Ct99JsBe.mjs} +2 -2
- package/dist/{portable-collection-BvJ5om91.js → portable-collection-FTPa5D91.js} +1 -1
- package/dist/{portable-key-B8kTV4n8.mjs → portable-key-U3lW06QJ.mjs} +1 -1
- package/dist/{proof-B9DEHHiO.mjs → proof-MFz2960e.mjs} +4 -4
- package/dist/{send-CZckda2X.mjs → send-Duj8xys1.mjs} +191 -10
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +3 -3
- package/dist/sig/compound-proof.test.mjs +3 -3
- package/dist/sig/http.test.mjs +3 -3
- package/dist/sig/key-owner-evidence.test.mjs +2 -2
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +2 -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/portable-collection.test.mjs +2 -2
- package/dist/sig/portable-key-id.test.mjs +2 -2
- package/dist/sig/portable-key.test.mjs +7 -7
- package/dist/sig/proof.test.mjs +4 -4
- package/dist/{temporal-Cc1ymEup.mjs → temporal-CVcPKB1N.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +79 -0
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-C1itIjqW.mjs → testing-t8xDeJZ5.mjs} +1 -1
- package/dist/utils/docloader.test.mjs +6 -6
- package/dist/utils/kv-cache.test.mjs +2 -2
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-CxoV48e0.mjs";
|
|
5
|
-
import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-BVpdjsaR.mjs";
|
|
6
|
-
import { t as formatAcceptSignature } from "./accept-CPVqki78.mjs";
|
|
7
4
|
import { d as isPortableUri, f as isSameObjectId, i as getPortableDid, l as isPortableId, n as getCanonicalPortableId, o as hasPortableActor, r as getGatewayKeyBase, s as isCompatibleKeyId, t as getAuthenticationOrigin, u as isPortableKeyId } from "./portable-key-id-Ca30-hOa.mjs";
|
|
8
|
-
import {
|
|
5
|
+
import { n as isPubliclyAddressedNode, t as isInAudience } from "./audience-CHQ-FlLD.mjs";
|
|
6
|
+
import { n as version, t as name } from "./deno-BKp9fYB3.mjs";
|
|
7
|
+
import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-DPQKFkiJ.mjs";
|
|
8
|
+
import { t as formatAcceptSignature } from "./accept-CPVqki78.mjs";
|
|
9
|
+
import { c as validateCryptoKey, n as exportJwk, s as importJwk } from "./key-ChfV9C3v.mjs";
|
|
9
10
|
import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-4IJqPYos.mjs";
|
|
10
|
-
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-
|
|
11
|
-
import { a as signObject, c as verifyPortableObjectProof, i as hasProofLike, s as verifyObject, t as InvalidPortableObjectIdError, u as verifyPortableObjectProofWithRoot } from "./proof-
|
|
12
|
-
import { a as listRequestSignatures, c as selectRequestSignature, d as validateMaxSignatures, f as verifyRequest, p as verifyRequestDetailed, s as parseRfc9421SignatureInput } from "./http
|
|
13
|
-
import { t as getAuthenticatedDocumentLoader } from "./docloader-
|
|
14
|
-
import { n as kvCache } from "./kv-cache-
|
|
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-DBBVabBC.mjs";
|
|
12
|
+
import { a as signObject, c as verifyPortableObjectProof, i as hasProofLike, s as verifyObject, t as InvalidPortableObjectIdError, u as verifyPortableObjectProofWithRoot } from "./proof-MFz2960e.mjs";
|
|
13
|
+
import { a as listRequestSignatures, c as selectRequestSignature, d as validateMaxSignatures, f as verifyRequest, p as verifyRequestDetailed, s as parseRfc9421SignatureInput } from "./http--IGUT1mg.mjs";
|
|
14
|
+
import { t as getAuthenticatedDocumentLoader } from "./docloader-BjJreYQU.mjs";
|
|
15
|
+
import { n as kvCache } from "./kv-cache-DFIUR-E5.mjs";
|
|
15
16
|
import { d as readBoundedText, l as BodyTooLargeError, t as getNodeInfo, u as MAX_BODY_SIZE } from "./client-CEnNz8O7.mjs";
|
|
16
17
|
import { t as nodeInfoToJson } from "./types-DE_LV1JB.mjs";
|
|
17
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
18
|
-
import { t as verifyPortableObject } from "./portable-collection-
|
|
19
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-
|
|
20
|
-
import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-
|
|
18
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-BW5VsXG3.mjs";
|
|
19
|
+
import { t as verifyPortableObject } from "./portable-collection-Ct99JsBe.mjs";
|
|
20
|
+
import { a as getOrderingDestination, i as sendActivityThroughGateways, n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-Duj8xys1.mjs";
|
|
21
|
+
import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-CKAVFvcH.mjs";
|
|
21
22
|
import { t as CircuitBreaker } from "./circuit-breaker-XJ53md4S.mjs";
|
|
22
23
|
import { t as buildCollectionSynchronizationHeader } from "./collection-CnnP1UAv.mjs";
|
|
23
|
-
import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, s as verifyServedPortableObjects, t as containsCompoundPortableObject } from "./compound-proof-
|
|
24
|
+
import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, s as verifyServedPortableObjects, t as containsCompoundPortableObject } from "./compound-proof-BP5SOdXv.mjs";
|
|
24
25
|
import { t as KvKeyCache } from "./keycache-Da8A8mZa.mjs";
|
|
25
26
|
import { t as acceptsJsonLd } from "./negotiation-CCocWT_N.mjs";
|
|
26
|
-
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-
|
|
27
|
+
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-CVcPKB1N.mjs";
|
|
27
28
|
import { t as createExponentialBackoffPolicy } from "./retry-zX5xw5e0.mjs";
|
|
28
29
|
import { t as TaskCodec } from "./codec-C6rTrpIp.mjs";
|
|
29
30
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
@@ -31,10 +32,10 @@ import { RouterError } from "@fedify/uri-template";
|
|
|
31
32
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, isActor, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
32
33
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
33
34
|
import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
|
|
34
|
-
import { uniq } from "es-toolkit";
|
|
35
35
|
import { FetchError, canonicalizePortableUri, decodeMultibase, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, isGatewayUrl, parseDigestMultibase, parseHashlink, parseIri, resolveDocumentLoaderTimeout, toCompatibleEf61Id } from "@fedify/vocab-runtime";
|
|
36
|
-
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";
|
|
37
36
|
import { isCompatibleEf61Iri } from "@fedify/vocab-runtime/internal/portable-dereference";
|
|
37
|
+
import { uniq } from "es-toolkit";
|
|
38
|
+
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";
|
|
38
39
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
39
40
|
import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
|
|
40
41
|
import { domainToASCII } from "node:url";
|
|
@@ -1212,6 +1213,15 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
1212
1213
|
* only if its ID canonically matches the requested portable ID and it
|
|
1213
1214
|
* satisfies the FEP-ef61 proof policy.
|
|
1214
1215
|
*
|
|
1216
|
+
* FEP-ef61 forbids serving a non-public object to anyone but its intended
|
|
1217
|
+
* audience. If the dispatcher has an authorization predicate, it decides who
|
|
1218
|
+
* may retrieve the object, and the response is marked with
|
|
1219
|
+
* `Cache-Control: private`, as it depends on the requester. Otherwise, only
|
|
1220
|
+
* a publicly addressed object is served, and any other object is responded
|
|
1221
|
+
* to with `404 Not Found`, as if this server did not store it. Actors, and
|
|
1222
|
+
* anything the actor dispatcher returns, are exempt from the latter, as
|
|
1223
|
+
* actors have no audience.
|
|
1224
|
+
*
|
|
1215
1225
|
* A {@link Tombstone} is served with `410 Gone` if it has an Object Integrity
|
|
1216
1226
|
* Proof made with a key of the DID in its ID. An unsigned tombstone cannot
|
|
1217
1227
|
* be served as a portable object, so the response is the same as for
|
|
@@ -1221,7 +1231,7 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
1221
1231
|
* @param parameters The parameters for handling the portable object.
|
|
1222
1232
|
* @returns A promise that resolves to an HTTP response.
|
|
1223
1233
|
*/
|
|
1224
|
-
async function handlePortableObject(request, { values, context, objectDispatcher, authorizePredicate, canonicalId, onNotFound, onUnauthorized }) {
|
|
1234
|
+
async function handlePortableObject(request, { values, context, objectDispatcher, authorizePredicate, canonicalId, kind, onNotFound, onUnauthorized }) {
|
|
1225
1235
|
const logger = getLogger([
|
|
1226
1236
|
"fedify",
|
|
1227
1237
|
"federation",
|
|
@@ -1240,14 +1250,32 @@ async function handlePortableObject(request, { values, context, objectDispatcher
|
|
|
1240
1250
|
if (authorizePredicate != null) {
|
|
1241
1251
|
if (!await authorizePredicate(context, values)) return await onUnauthorized(request);
|
|
1242
1252
|
}
|
|
1253
|
+
const contextLoader = getNormalizationContextLoader(context.contextLoader);
|
|
1243
1254
|
let jsonLd;
|
|
1244
|
-
let
|
|
1245
|
-
let result;
|
|
1255
|
+
let root;
|
|
1246
1256
|
try {
|
|
1247
1257
|
jsonLd = await object.toJsonLd(context);
|
|
1248
|
-
|
|
1258
|
+
root = await expandJsonLdRoot(jsonLd, contextLoader);
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
logger.error("Failed to serialize the portable object {portableId}:\n{error}", {
|
|
1261
|
+
portableId: canonicalId,
|
|
1262
|
+
error
|
|
1263
|
+
});
|
|
1264
|
+
return portableObjectInternalServerError(request);
|
|
1265
|
+
}
|
|
1266
|
+
const servedId = root?.["@id"];
|
|
1267
|
+
if (root == null || typeof servedId !== "string" || !isRequestedPortableId(servedId, canonicalId)) {
|
|
1268
|
+
logger.debug("The serialized object does not match the requested portable object {portableId}.", { portableId: canonicalId });
|
|
1269
|
+
return await onNotFound(request);
|
|
1270
|
+
}
|
|
1271
|
+
if (authorizePredicate == null && kind === "object" && !isActor(object) && !isPubliclyAddressedNode(root)) {
|
|
1272
|
+
logger.debug("Not serving the portable object {portableId}, as it is not publicly addressed and its object dispatcher has no authorization predicate; responding as if this server did not store the object. Set an authorization predicate that checks if the request is signed by an actor in the object's audience to serve it.", { portableId: canonicalId });
|
|
1273
|
+
return await onNotFound(request);
|
|
1274
|
+
}
|
|
1275
|
+
let result;
|
|
1276
|
+
try {
|
|
1249
1277
|
result = await verifyPortableObjectProof(jsonLd, {
|
|
1250
|
-
contextLoader
|
|
1278
|
+
contextLoader,
|
|
1251
1279
|
documentLoader: context.documentLoader,
|
|
1252
1280
|
tracerProvider: context.tracerProvider,
|
|
1253
1281
|
meterProvider: context.meterProvider
|
|
@@ -1259,10 +1287,6 @@ async function handlePortableObject(request, { values, context, objectDispatcher
|
|
|
1259
1287
|
});
|
|
1260
1288
|
return portableObjectInternalServerError(request);
|
|
1261
1289
|
}
|
|
1262
|
-
if (servedId == null || !isRequestedPortableId(servedId, canonicalId)) {
|
|
1263
|
-
logger.debug("The serialized object does not match the requested portable object {portableId}.", { portableId: canonicalId });
|
|
1264
|
-
return await onNotFound(request);
|
|
1265
|
-
}
|
|
1266
1290
|
const tombstone = object instanceof Tombstone;
|
|
1267
1291
|
if (!result.verified) {
|
|
1268
1292
|
if (tombstone && result.reason.type === "missingProof") {
|
|
@@ -1281,25 +1305,26 @@ async function handlePortableObject(request, { values, context, objectDispatcher
|
|
|
1281
1305
|
return portableObjectInternalServerError(request);
|
|
1282
1306
|
}
|
|
1283
1307
|
}
|
|
1308
|
+
const headers = new Headers({
|
|
1309
|
+
"Content-Type": PORTABLE_OBJECT_CONTENT_TYPE,
|
|
1310
|
+
Vary: "Accept"
|
|
1311
|
+
});
|
|
1312
|
+
if (authorizePredicate != null) headers.set("Cache-Control", "private");
|
|
1284
1313
|
return new Response(request.method === "HEAD" ? null : JSON.stringify(jsonLd), {
|
|
1285
1314
|
status: tombstone ? 410 : 200,
|
|
1286
|
-
headers
|
|
1287
|
-
"Content-Type": PORTABLE_OBJECT_CONTENT_TYPE,
|
|
1288
|
-
Vary: "Accept"
|
|
1289
|
-
}
|
|
1315
|
+
headers
|
|
1290
1316
|
});
|
|
1291
1317
|
}
|
|
1292
1318
|
function isRequestedPortableObject(object, canonicalId) {
|
|
1293
1319
|
return object.id != null && getCanonicalPortableId(object.id) === canonicalId;
|
|
1294
1320
|
}
|
|
1295
|
-
async function
|
|
1321
|
+
async function expandJsonLdRoot(jsonLd, contextLoader) {
|
|
1296
1322
|
const expanded = await jsonld.expand(jsonLd, {
|
|
1297
|
-
documentLoader:
|
|
1323
|
+
documentLoader: contextLoader,
|
|
1298
1324
|
keepFreeFloatingNodes: true
|
|
1299
1325
|
});
|
|
1300
1326
|
if (expanded.length !== 1) return null;
|
|
1301
|
-
|
|
1302
|
-
return typeof id === "string" ? id : null;
|
|
1327
|
+
return expanded[0];
|
|
1303
1328
|
}
|
|
1304
1329
|
function isRequestedPortableId(id, canonicalId) {
|
|
1305
1330
|
if (!/^ap(?:\+ef61)?:\/\//i.test(id)) return URL.canParse(id) && getCanonicalPortableId(new URL(id)) === canonicalId;
|
|
@@ -4045,6 +4070,37 @@ function getBenchmarkTriggerOptions(benchmarkOptions) {
|
|
|
4045
4070
|
function maxDelay(first, second) {
|
|
4046
4071
|
return Temporal.Duration.compare(first, second) >= 0 ? first : second;
|
|
4047
4072
|
}
|
|
4073
|
+
/**
|
|
4074
|
+
* Gets the fields of a queued message that describe the FEP-ef61 portable
|
|
4075
|
+
* inbox an inbox stands for, if any, omitting the absent ones.
|
|
4076
|
+
*/
|
|
4077
|
+
function getPortableMessageFields(portableInbox, gatewayInboxes) {
|
|
4078
|
+
return {
|
|
4079
|
+
...portableInbox == null ? {} : { portableInbox },
|
|
4080
|
+
...gatewayInboxes == null ? {} : { gatewayInboxes: [...gatewayInboxes] }
|
|
4081
|
+
};
|
|
4082
|
+
}
|
|
4083
|
+
/**
|
|
4084
|
+
* Sends an activity to an inbox, or, if the inbox is the compatible
|
|
4085
|
+
* identifier of an FEP-ef61 portable inbox on more than one gateway, through
|
|
4086
|
+
* the first gateway that accepts it.
|
|
4087
|
+
*/
|
|
4088
|
+
function sendActivityToInbox(inbox, gatewayInboxes, parameters) {
|
|
4089
|
+
if (gatewayInboxes == null || gatewayInboxes.length < 2) return sendActivity({
|
|
4090
|
+
...parameters,
|
|
4091
|
+
inbox: new URL(inbox)
|
|
4092
|
+
});
|
|
4093
|
+
return sendActivityThroughGateways(parameters, gatewayInboxes.map((i) => new URL(i)));
|
|
4094
|
+
}
|
|
4095
|
+
function parseInstant(value) {
|
|
4096
|
+
if (value == null) return void 0;
|
|
4097
|
+
try {
|
|
4098
|
+
return Temporal.Instant.from(value);
|
|
4099
|
+
} catch (error) {
|
|
4100
|
+
if (error instanceof RangeError) return void 0;
|
|
4101
|
+
throw error;
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4048
4104
|
function isTransportDeliveryError(error) {
|
|
4049
4105
|
return error instanceof FetchError || isAbortError(error);
|
|
4050
4106
|
}
|
|
@@ -4625,6 +4681,20 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
4625
4681
|
}
|
|
4626
4682
|
recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
|
|
4627
4683
|
};
|
|
4684
|
+
const { gatewayInboxes } = message;
|
|
4685
|
+
if (gatewayInboxes != null && gatewayInboxes.length > 1) {
|
|
4686
|
+
await this.#listenGatewayOutboxMessage(_, {
|
|
4687
|
+
...message,
|
|
4688
|
+
gatewayInboxes
|
|
4689
|
+
}, span, {
|
|
4690
|
+
keys,
|
|
4691
|
+
logData,
|
|
4692
|
+
getActorIds,
|
|
4693
|
+
parseActivity,
|
|
4694
|
+
dropHeldOutboxMessage
|
|
4695
|
+
});
|
|
4696
|
+
return;
|
|
4697
|
+
}
|
|
4628
4698
|
try {
|
|
4629
4699
|
const inbox = new URL(message.inbox);
|
|
4630
4700
|
const circuit = this.outboxQueue == null ? void 0 : this.circuitBreaker;
|
|
@@ -4847,6 +4917,308 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
4847
4917
|
}
|
|
4848
4918
|
logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
|
|
4849
4919
|
}
|
|
4920
|
+
/**
|
|
4921
|
+
* Delivers a queued activity to an FEP-ef61 portable inbox through the first
|
|
4922
|
+
* of its gateways that accepts it, trying them one after another in a single
|
|
4923
|
+
* round, so that the round counts as one attempt for the retry policy, keeps
|
|
4924
|
+
* the message's place in its ordering key, and is replayed as a whole by
|
|
4925
|
+
* queues that retry natively.
|
|
4926
|
+
*
|
|
4927
|
+
* A gateway that fails with a permanent failure status is not tried again.
|
|
4928
|
+
* A gateway whose circuit is open, or which asked to be retried later with
|
|
4929
|
+
* `Retry-After`, is skipped in this round. If no gateway accepts the
|
|
4930
|
+
* activity, the message is held while every remaining gateway is held, and
|
|
4931
|
+
* otherwise retried according to the retry policy.
|
|
4932
|
+
*/
|
|
4933
|
+
async #listenGatewayOutboxMessage(contextData, message, span, helpers) {
|
|
4934
|
+
const logger = getLogger([
|
|
4935
|
+
"fedify",
|
|
4936
|
+
"federation",
|
|
4937
|
+
"outbox"
|
|
4938
|
+
]);
|
|
4939
|
+
const circuitLogger = getLogger([
|
|
4940
|
+
"fedify",
|
|
4941
|
+
"federation",
|
|
4942
|
+
"circuit"
|
|
4943
|
+
]);
|
|
4944
|
+
const { keys, logData, getActorIds, parseActivity, dropHeldOutboxMessage } = helpers;
|
|
4945
|
+
const circuit = this.outboxQueue == null ? void 0 : this.circuitBreaker;
|
|
4946
|
+
const specDeterminer = new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock, { specTtl: this.httpMessageSignaturesSpecTtl });
|
|
4947
|
+
const survivors = [];
|
|
4948
|
+
let lastError;
|
|
4949
|
+
let lastPermanent;
|
|
4950
|
+
let activity;
|
|
4951
|
+
const getActivity = async () => activity ??= await parseActivity();
|
|
4952
|
+
const drop = async (circuit, remoteHost, inbox, heldSince) => {
|
|
4953
|
+
await dropHeldOutboxMessage(circuit, remoteHost, inbox, heldSince, await getActivity());
|
|
4954
|
+
};
|
|
4955
|
+
const heldSince = parseInstant(message.circuitHeldSince);
|
|
4956
|
+
if (circuit != null && heldSince != null && Temporal.Instant.compare(heldSince.add(circuit.options.heldActivityTtl), Temporal.Now.instant()) <= 0) {
|
|
4957
|
+
const inbox = new URL(message.gatewayInboxes[0]);
|
|
4958
|
+
await drop(circuit, getRemoteHost(inbox), inbox, heldSince);
|
|
4959
|
+
return;
|
|
4960
|
+
}
|
|
4961
|
+
for (const inboxHref of message.gatewayInboxes) {
|
|
4962
|
+
const inbox = new URL(inboxHref);
|
|
4963
|
+
const remoteHost = getRemoteHost(inbox);
|
|
4964
|
+
const now = Temporal.Now.instant();
|
|
4965
|
+
const notBefore = parseInstant(message.gatewayNotBefore?.[inboxHref]);
|
|
4966
|
+
if (notBefore != null && Temporal.Instant.compare(notBefore, now) > 0) {
|
|
4967
|
+
survivors.push({
|
|
4968
|
+
inbox: inboxHref,
|
|
4969
|
+
holdDelay: now.until(notBefore),
|
|
4970
|
+
notBefore
|
|
4971
|
+
});
|
|
4972
|
+
continue;
|
|
4973
|
+
}
|
|
4974
|
+
let decision;
|
|
4975
|
+
if (circuit != null) try {
|
|
4976
|
+
decision = await circuit.beforeSend(remoteHost, message);
|
|
4977
|
+
} catch (circuitError) {
|
|
4978
|
+
circuitLogger.error("Failed to check circuit breaker state before sending; proceeding with delivery:\n{error}", {
|
|
4979
|
+
...logData,
|
|
4980
|
+
remoteHost,
|
|
4981
|
+
error: circuitError
|
|
4982
|
+
});
|
|
4983
|
+
}
|
|
4984
|
+
if (decision != null && circuit != null) {
|
|
4985
|
+
if (decision.type === "drop") {
|
|
4986
|
+
await drop(circuit, remoteHost, inbox, decision.heldSince);
|
|
4987
|
+
return;
|
|
4988
|
+
} else if (decision.type === "hold") {
|
|
4989
|
+
recordCircuitBreakerHeldSpanEvent(span, remoteHost, decision.state);
|
|
4990
|
+
survivors.push({
|
|
4991
|
+
inbox: inboxHref,
|
|
4992
|
+
holdDelay: decision.delay,
|
|
4993
|
+
circuitHeld: true
|
|
4994
|
+
});
|
|
4995
|
+
continue;
|
|
4996
|
+
} else if (decision.stateChange != null) recordCircuitBreakerSpanEvent(span, remoteHost, decision.stateChange);
|
|
4997
|
+
}
|
|
4998
|
+
try {
|
|
4999
|
+
await sendActivity({
|
|
5000
|
+
allowPrivateAddress: this.allowPrivateAddress,
|
|
5001
|
+
keys,
|
|
5002
|
+
activity: message.activity,
|
|
5003
|
+
activityId: message.activityId,
|
|
5004
|
+
activityType: message.activityType,
|
|
5005
|
+
inbox,
|
|
5006
|
+
sharedInbox: message.sharedInbox,
|
|
5007
|
+
headers: new Headers(message.headers),
|
|
5008
|
+
specDeterminer,
|
|
5009
|
+
meterProvider: this.meterProvider,
|
|
5010
|
+
tracerProvider: this.tracerProvider
|
|
5011
|
+
});
|
|
5012
|
+
} catch (error) {
|
|
5013
|
+
lastError = error;
|
|
5014
|
+
const isPermanentFailure = error instanceof SendActivityError && this.permanentFailureStatusCodes.includes(error.statusCode);
|
|
5015
|
+
let retryAfter = !isPermanentFailure && error instanceof SendActivityError && (error.statusCode === 429 || error.statusCode === 503) ? parseRetryAfter(error.responseHeaders) : void 0;
|
|
5016
|
+
let retryAt;
|
|
5017
|
+
if (retryAfter != null) try {
|
|
5018
|
+
retryAt = Temporal.Now.instant().add(retryAfter);
|
|
5019
|
+
} catch (e) {
|
|
5020
|
+
if (!(e instanceof RangeError)) throw e;
|
|
5021
|
+
retryAfter = void 0;
|
|
5022
|
+
}
|
|
5023
|
+
let postDecision;
|
|
5024
|
+
if (circuit != null) try {
|
|
5025
|
+
if (error instanceof SendActivityError) {
|
|
5026
|
+
const { statusCode } = error;
|
|
5027
|
+
const stateChange = isPermanentFailure || statusCode === 429 || statusCode >= 400 && statusCode < 500 ? await circuit.recordReachableFailure(remoteHost) : statusCode >= 500 ? await circuit.recordFailure(remoteHost) : void 0;
|
|
5028
|
+
if (stateChange != null) recordCircuitBreakerSpanEvent(span, remoteHost, stateChange);
|
|
5029
|
+
} else if (isTransportDeliveryError(error)) {
|
|
5030
|
+
const stateChange = await circuit.recordFailure(remoteHost);
|
|
5031
|
+
if (stateChange != null) recordCircuitBreakerSpanEvent(span, remoteHost, stateChange);
|
|
5032
|
+
}
|
|
5033
|
+
if (!isPermanentFailure) postDecision = await circuit.beforeSend(remoteHost, message);
|
|
5034
|
+
} catch (circuitError) {
|
|
5035
|
+
circuitLogger.error("Failed to update circuit breaker state after delivery failure; falling back to normal failure handling:\n{error}", {
|
|
5036
|
+
...logData,
|
|
5037
|
+
remoteHost,
|
|
5038
|
+
error: circuitError
|
|
5039
|
+
});
|
|
5040
|
+
}
|
|
5041
|
+
span.addEvent("activitypub.delivery.failed", {
|
|
5042
|
+
"activitypub.remote.host": remoteHost,
|
|
5043
|
+
"activitypub.delivery.attempt": message.attempt,
|
|
5044
|
+
"activitypub.delivery.permanent_failure": isPermanentFailure,
|
|
5045
|
+
...error instanceof SendActivityError ? { "http.response.status_code": error.statusCode } : {}
|
|
5046
|
+
});
|
|
5047
|
+
try {
|
|
5048
|
+
await this.onOutboxError?.(error, await getActivity());
|
|
5049
|
+
} catch (handlerError) {
|
|
5050
|
+
logger.error("An unexpected error occurred in onError handler:\n{error}", {
|
|
5051
|
+
...logData,
|
|
5052
|
+
inbox: inboxHref,
|
|
5053
|
+
error: handlerError
|
|
5054
|
+
});
|
|
5055
|
+
}
|
|
5056
|
+
if (isPermanentFailure) {
|
|
5057
|
+
logger.warn("Permanent delivery failure for activity {activityId} to {inbox} ({status}); not trying the gateway again.", {
|
|
5058
|
+
...logData,
|
|
5059
|
+
inbox: inboxHref,
|
|
5060
|
+
status: error.statusCode
|
|
5061
|
+
});
|
|
5062
|
+
lastPermanent = {
|
|
5063
|
+
error,
|
|
5064
|
+
inbox
|
|
5065
|
+
};
|
|
5066
|
+
continue;
|
|
5067
|
+
}
|
|
5068
|
+
if (postDecision?.type === "drop" && circuit != null) {
|
|
5069
|
+
await drop(circuit, remoteHost, inbox, postDecision.heldSince);
|
|
5070
|
+
return;
|
|
5071
|
+
} else if (postDecision?.type === "hold") {
|
|
5072
|
+
recordCircuitBreakerHeldSpanEvent(span, remoteHost, postDecision.state);
|
|
5073
|
+
survivors.push({
|
|
5074
|
+
inbox: inboxHref,
|
|
5075
|
+
holdDelay: retryAfter == null ? postDecision.delay : maxDelay(postDecision.delay, retryAfter),
|
|
5076
|
+
circuitHeld: true,
|
|
5077
|
+
postFailure: true,
|
|
5078
|
+
notBefore: retryAt,
|
|
5079
|
+
retryAfter
|
|
5080
|
+
});
|
|
5081
|
+
} else survivors.push({
|
|
5082
|
+
inbox: inboxHref,
|
|
5083
|
+
notBefore: retryAt,
|
|
5084
|
+
retryAfter
|
|
5085
|
+
});
|
|
5086
|
+
logger.warn("Failed to send activity {activityId} to {inbox} (attempt #{attempt}); trying the next gateway if any:\n{error}", {
|
|
5087
|
+
...logData,
|
|
5088
|
+
inbox: inboxHref,
|
|
5089
|
+
error
|
|
5090
|
+
});
|
|
5091
|
+
continue;
|
|
5092
|
+
}
|
|
5093
|
+
if (circuit != null) try {
|
|
5094
|
+
const stateChange = await circuit.recordSuccess(remoteHost);
|
|
5095
|
+
if (stateChange != null) recordCircuitBreakerSpanEvent(span, remoteHost, stateChange);
|
|
5096
|
+
} catch (error) {
|
|
5097
|
+
circuitLogger.error("Failed to record successful delivery in circuit breaker state; the activity was already delivered:\n{error}", {
|
|
5098
|
+
...logData,
|
|
5099
|
+
remoteHost,
|
|
5100
|
+
error
|
|
5101
|
+
});
|
|
5102
|
+
}
|
|
5103
|
+
logger.info("Successfully sent activity {activityId} to {inbox}.", {
|
|
5104
|
+
...logData,
|
|
5105
|
+
inbox: inboxHref
|
|
5106
|
+
});
|
|
5107
|
+
return;
|
|
5108
|
+
}
|
|
5109
|
+
if (lastError != null) span.setStatus({
|
|
5110
|
+
code: SpanStatusCode.ERROR,
|
|
5111
|
+
message: String(lastError)
|
|
5112
|
+
});
|
|
5113
|
+
if (survivors.length < 1) {
|
|
5114
|
+
const { error, inbox } = lastPermanent;
|
|
5115
|
+
this.metrics.recordPermanentFailure(error.inbox, error.statusCode);
|
|
5116
|
+
logger.warn("Permanent delivery failure for activity {activityId} to every gateway of {portableInbox}; not retrying.", {
|
|
5117
|
+
...logData,
|
|
5118
|
+
portableInbox: message.portableInbox
|
|
5119
|
+
});
|
|
5120
|
+
if (this.outboxPermanentFailureHandler != null) {
|
|
5121
|
+
const ctx = this.#createContext(new URL(message.baseUrl), contextData, { documentLoader: this.documentLoaderFactory(this.#getLoaderOptions(message.baseUrl)) });
|
|
5122
|
+
try {
|
|
5123
|
+
await this.outboxPermanentFailureHandler(ctx, {
|
|
5124
|
+
reason: "http",
|
|
5125
|
+
inbox,
|
|
5126
|
+
activity: await getActivity(),
|
|
5127
|
+
error,
|
|
5128
|
+
statusCode: error.statusCode,
|
|
5129
|
+
actorIds: getActorIds()
|
|
5130
|
+
});
|
|
5131
|
+
} catch (handlerError) {
|
|
5132
|
+
logger.error("An unexpected error occurred in outboxPermanentFailureHandler:\n{error}", {
|
|
5133
|
+
...logData,
|
|
5134
|
+
error: handlerError
|
|
5135
|
+
});
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
|
|
5139
|
+
return;
|
|
5140
|
+
}
|
|
5141
|
+
const now = Temporal.Now.instant();
|
|
5142
|
+
const gatewayNotBefore = {};
|
|
5143
|
+
for (const survivor of survivors) if (survivor.notBefore != null && Temporal.Instant.compare(survivor.notBefore, now) > 0) gatewayNotBefore[survivor.inbox] = survivor.notBefore.toString();
|
|
5144
|
+
const { circuitHeld: _circuitHeld, gatewayNotBefore: _gatewayNotBefore, ...rest } = message;
|
|
5145
|
+
const nextMessage = {
|
|
5146
|
+
...rest,
|
|
5147
|
+
inbox: survivors[0].inbox,
|
|
5148
|
+
gatewayInboxes: survivors.map((s) => s.inbox),
|
|
5149
|
+
...globalThis.Object.keys(gatewayNotBefore).length < 1 ? {} : { gatewayNotBefore }
|
|
5150
|
+
};
|
|
5151
|
+
const { outboxQueue } = this;
|
|
5152
|
+
const policyDelay = this.outboxRetryPolicy({
|
|
5153
|
+
elapsedTime: Temporal.Instant.from(message.started).until(now),
|
|
5154
|
+
attempts: message.attempt
|
|
5155
|
+
});
|
|
5156
|
+
if (outboxQueue != null && survivors.every((s) => s.holdDelay != null) && (policyDelay != null || !survivors.some((s) => s.postFailure))) {
|
|
5157
|
+
let delay = survivors.map((s) => s.holdDelay).reduce((a, b) => Temporal.Duration.compare(a, b) <= 0 ? a : b);
|
|
5158
|
+
let heldMessage = nextMessage;
|
|
5159
|
+
if (survivors.some((s) => s.circuitHeld)) {
|
|
5160
|
+
const since = heldSince ?? now;
|
|
5161
|
+
if (circuit != null) delay = circuit.capHeldDelay(since, delay);
|
|
5162
|
+
heldMessage = {
|
|
5163
|
+
...nextMessage,
|
|
5164
|
+
circuitHeld: true,
|
|
5165
|
+
circuitHeldSince: since.toString()
|
|
5166
|
+
};
|
|
5167
|
+
}
|
|
5168
|
+
logger.warn("Failed to send activity {activityId} to {portableInbox}; holding it until one of its gateways can be tried again.", {
|
|
5169
|
+
...logData,
|
|
5170
|
+
portableInbox: message.portableInbox
|
|
5171
|
+
});
|
|
5172
|
+
await outboxQueue.enqueue(heldMessage, {
|
|
5173
|
+
delay: clampNegativeDelay(delay),
|
|
5174
|
+
orderingKey: message.orderingKey
|
|
5175
|
+
});
|
|
5176
|
+
this.metrics.recordQueueTaskEnqueued({
|
|
5177
|
+
role: "outbox",
|
|
5178
|
+
queue: outboxQueue,
|
|
5179
|
+
activityType: heldMessage.activityType
|
|
5180
|
+
}, heldMessage.attempt);
|
|
5181
|
+
return;
|
|
5182
|
+
}
|
|
5183
|
+
const hasRetryAfter = survivors.some((s) => s.retryAfter != null);
|
|
5184
|
+
if (outboxQueue?.nativeRetrial && !hasRetryAfter && survivors.length === message.gatewayInboxes.length && globalThis.Object.keys(gatewayNotBefore).length < 1) {
|
|
5185
|
+
logger.error("Failed to send activity {activityId} to every gateway of {portableInbox}; backend will handle retry:\n{error}", {
|
|
5186
|
+
...logData,
|
|
5187
|
+
portableInbox: message.portableInbox,
|
|
5188
|
+
error: lastError
|
|
5189
|
+
});
|
|
5190
|
+
throw lastError;
|
|
5191
|
+
}
|
|
5192
|
+
if (policyDelay == null || outboxQueue == null) {
|
|
5193
|
+
logger.error("Failed to send activity {activityId} to every gateway of {portableInbox} after {attempt} attempts; giving up:\n{error}", {
|
|
5194
|
+
...logData,
|
|
5195
|
+
portableInbox: message.portableInbox,
|
|
5196
|
+
error: lastError
|
|
5197
|
+
});
|
|
5198
|
+
recordOutboxActivity(this.meterProvider, "abandoned", message.activityType);
|
|
5199
|
+
return;
|
|
5200
|
+
}
|
|
5201
|
+
const delay = survivors.map((s) => s.holdDelay ?? s.retryAfter ?? policyDelay).reduce((a, b) => Temporal.Duration.compare(a, b) <= 0 ? a : b);
|
|
5202
|
+
const retryMessage = {
|
|
5203
|
+
...nextMessage,
|
|
5204
|
+
attempt: message.attempt + 1
|
|
5205
|
+
};
|
|
5206
|
+
logger.error("Failed to send activity {activityId} to every gateway of {portableInbox} (attempt #{attempt}); retry...:\n{error}", {
|
|
5207
|
+
...logData,
|
|
5208
|
+
portableInbox: message.portableInbox,
|
|
5209
|
+
error: lastError
|
|
5210
|
+
});
|
|
5211
|
+
await outboxQueue.enqueue(retryMessage, {
|
|
5212
|
+
delay: clampNegativeDelay(delay),
|
|
5213
|
+
orderingKey: message.orderingKey
|
|
5214
|
+
});
|
|
5215
|
+
this.metrics.recordQueueTaskEnqueued({
|
|
5216
|
+
role: "outbox",
|
|
5217
|
+
queue: outboxQueue,
|
|
5218
|
+
activityType: retryMessage.activityType
|
|
5219
|
+
}, retryMessage.attempt);
|
|
5220
|
+
recordOutboxActivity(this.meterProvider, "retried", retryMessage.activityType);
|
|
5221
|
+
}
|
|
4850
5222
|
async #listenInboxMessage(ctxData, message, span, onActivityType) {
|
|
4851
5223
|
const logger = getLogger([
|
|
4852
5224
|
"fedify",
|
|
@@ -5276,13 +5648,12 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5276
5648
|
activity: jsonLd
|
|
5277
5649
|
});
|
|
5278
5650
|
const promises = [];
|
|
5279
|
-
for (const inbox in inboxes) promises.push(
|
|
5651
|
+
for (const inbox in inboxes) promises.push(sendActivityToInbox(inbox, inboxes[inbox].gatewayInboxes, {
|
|
5280
5652
|
allowPrivateAddress: this.allowPrivateAddress,
|
|
5281
5653
|
keys,
|
|
5282
5654
|
activity: jsonLd,
|
|
5283
5655
|
activityId: activity.id?.href,
|
|
5284
5656
|
activityType: getTypeId(activity).href,
|
|
5285
|
-
inbox: new URL(inbox),
|
|
5286
5657
|
sharedInbox: inboxes[inbox].sharedInbox,
|
|
5287
5658
|
headers: collectionSync == null ? void 0 : new Headers({ "Collection-Synchronization": await buildCollectionSynchronizationHeader(collectionSync, inboxes[inbox].actorIds) }),
|
|
5288
5659
|
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock, { specTtl: this.httpMessageSignaturesSpecTtl }),
|
|
@@ -5309,8 +5680,8 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5309
5680
|
propagation.inject(context.active(), carrier);
|
|
5310
5681
|
const messages = [];
|
|
5311
5682
|
for (const inbox in inboxes) {
|
|
5312
|
-
const
|
|
5313
|
-
const messageOrderingKey = orderingKey == null ? void 0 : `${orderingKey}\n${
|
|
5683
|
+
const { portableInbox, gatewayInboxes } = inboxes[inbox];
|
|
5684
|
+
const messageOrderingKey = orderingKey == null ? void 0 : `${orderingKey}\n${getOrderingDestination(inbox, portableInbox)}`;
|
|
5314
5685
|
const message = {
|
|
5315
5686
|
type: "outbox",
|
|
5316
5687
|
id: crypto.randomUUID(),
|
|
@@ -5326,6 +5697,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5326
5697
|
attempt: 0,
|
|
5327
5698
|
headers: collectionSync == null ? {} : { "Collection-Synchronization": await buildCollectionSynchronizationHeader(collectionSync, inboxes[inbox].actorIds) },
|
|
5328
5699
|
orderingKey: messageOrderingKey,
|
|
5700
|
+
...getPortableMessageFields(portableInbox, gatewayInboxes),
|
|
5329
5701
|
traceContext: carrier
|
|
5330
5702
|
};
|
|
5331
5703
|
messages.push({
|
|
@@ -5775,6 +6147,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5775
6147
|
objectDispatcher: actorDispatcher == null ? void 0 : (ctx) => actorDispatcher(ctx, identifier),
|
|
5776
6148
|
authorizePredicate: authorizePredicate == null ? void 0 : (ctx) => authorizePredicate(ctx, identifier),
|
|
5777
6149
|
canonicalId: portable.canonicalId,
|
|
6150
|
+
kind: "actor",
|
|
5778
6151
|
onUnauthorized,
|
|
5779
6152
|
onNotFound
|
|
5780
6153
|
});
|
|
@@ -5795,6 +6168,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
5795
6168
|
objectDispatcher: callbacks?.dispatcher,
|
|
5796
6169
|
authorizePredicate: callbacks?.authorizePredicate,
|
|
5797
6170
|
canonicalId: portable.canonicalId,
|
|
6171
|
+
kind: "object",
|
|
5798
6172
|
onUnauthorized,
|
|
5799
6173
|
onNotFound
|
|
5800
6174
|
});
|
|
@@ -6791,9 +7165,10 @@ var ContextImpl = class ContextImpl {
|
|
|
6791
7165
|
id: crypto.randomUUID(),
|
|
6792
7166
|
baseUrl: this.origin,
|
|
6793
7167
|
keys: keyJwkPairs,
|
|
6794
|
-
inboxes: globalThis.Object.fromEntries(globalThis.Object.entries(inboxes).map(([k, { actorIds, sharedInbox }]) => [k, {
|
|
7168
|
+
inboxes: globalThis.Object.fromEntries(globalThis.Object.entries(inboxes).map(([k, { actorIds, sharedInbox, portableInbox, gatewayInboxes }]) => [k, {
|
|
6795
7169
|
actorIds: [...actorIds],
|
|
6796
|
-
sharedInbox
|
|
7170
|
+
sharedInbox,
|
|
7171
|
+
...getPortableMessageFields(portableInbox, gatewayInboxes)
|
|
6797
7172
|
}])),
|
|
6798
7173
|
activity: activityJsonLd,
|
|
6799
7174
|
activityId: activity.id?.href,
|
|
@@ -7090,6 +7465,9 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
7090
7465
|
throw error;
|
|
7091
7466
|
}
|
|
7092
7467
|
}
|
|
7468
|
+
async isSignedByAudience(object, options = {}) {
|
|
7469
|
+
return await isInAudience(object, null) || await isInAudience(object, await this.getSignedKeyOwner(options), options);
|
|
7470
|
+
}
|
|
7093
7471
|
};
|
|
7094
7472
|
function forwardActivity(ctx, loggerCategory, forwarder, recipients, options) {
|
|
7095
7473
|
return ctx.tracerProvider.getTracer(name, version).startActiveSpan(ctx.federation.outboxQueue == null || options?.immediate ? `activitypub.${loggerCategory}` : "activitypub.fanout", {
|
|
@@ -7177,13 +7555,12 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
|
|
|
7177
7555
|
if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
7178
7556
|
else logger.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
7179
7557
|
const promises = [];
|
|
7180
|
-
for (const inbox in inboxes) promises.push(
|
|
7558
|
+
for (const inbox in inboxes) promises.push(sendActivityToInbox(inbox, inboxes[inbox].gatewayInboxes, {
|
|
7181
7559
|
allowPrivateAddress: ctx.federation.allowPrivateAddress,
|
|
7182
7560
|
keys,
|
|
7183
7561
|
activity: ctx.activity,
|
|
7184
7562
|
activityId: ctx.activityId,
|
|
7185
7563
|
activityType: ctx.activityType,
|
|
7186
|
-
inbox: new URL(inbox),
|
|
7187
7564
|
sharedInbox: inboxes[inbox].sharedInbox,
|
|
7188
7565
|
meterProvider: ctx.meterProvider,
|
|
7189
7566
|
tracerProvider: ctx.tracerProvider,
|
|
@@ -7211,7 +7588,7 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
|
|
|
7211
7588
|
const started = (/* @__PURE__ */ new Date()).toISOString();
|
|
7212
7589
|
const messages = [];
|
|
7213
7590
|
for (const inbox in inboxes) {
|
|
7214
|
-
const
|
|
7591
|
+
const { portableInbox, gatewayInboxes } = inboxes[inbox];
|
|
7215
7592
|
const message = {
|
|
7216
7593
|
type: "outbox",
|
|
7217
7594
|
id: crypto.randomUUID(),
|
|
@@ -7226,7 +7603,8 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
|
|
|
7226
7603
|
started,
|
|
7227
7604
|
attempt: 0,
|
|
7228
7605
|
headers: {},
|
|
7229
|
-
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${
|
|
7606
|
+
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${getOrderingDestination(inbox, portableInbox)}`,
|
|
7607
|
+
...getPortableMessageFields(portableInbox, gatewayInboxes),
|
|
7230
7608
|
traceContext: carrier
|
|
7231
7609
|
};
|
|
7232
7610
|
messages.push({
|