@fedify/fedify 2.3.0-dev.1013 → 2.3.0-dev.1034
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-CROLcFVM.mjs → builder-y06Dq5bp.mjs} +14 -4
- package/dist/chunk-QSgtlS85.mjs +29 -0
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +1 -1
- package/dist/compat/public-audience.test.mjs +1 -1
- package/dist/compat/transformers.test.mjs +2 -2
- package/dist/{context-Dk_tacqz.mjs → context-7Azky82W.mjs} +3 -2
- package/dist/{context-BDl7Y6f-.d.cts → context-BKLGj9QO.d.cts} +24 -1
- package/dist/{context-zTZAI3KP.d.ts → context-DrNqYkPw.d.ts} +24 -1
- package/dist/{deno-Ctd-K-t6.mjs → deno-DB1H1VHx.mjs} +1 -1
- package/dist/{docloader-q9QT51g3.mjs → docloader-3HwiWeYL.mjs} +2 -2
- package/dist/{esm-DVILvP5e.mjs → esm-DhnRLoG9.mjs} +1 -24
- package/dist/execAsync-eck5rbtb.mjs +13 -0
- package/dist/federation/builder.test.mjs +8 -3
- package/dist/federation/collection.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +48 -11
- package/dist/federation/idempotency.test.mjs +4 -4
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/keycache.test.mjs +2 -2
- package/dist/federation/kv.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +138 -15
- 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/negotiation.test.mjs +1 -1
- package/dist/federation/retry.test.mjs +1 -1
- package/dist/federation/send.test.mjs +4540 -11
- package/dist/federation/webfinger.test.mjs +3 -3
- package/dist/getMachineId-bsd-DqZ4QRFp.mjs +29 -0
- package/dist/getMachineId-darwin-DMbbW3m7.mjs +26 -0
- package/dist/getMachineId-linux-lyeD2ug3.mjs +22 -0
- package/dist/getMachineId-unsupported-JuKr57jY.mjs +17 -0
- package/dist/getMachineId-win-Dxyf5pJq.mjs +28 -0
- package/dist/{http-1NL30qCe.js → http-BUr93aO6.js} +1 -1
- package/dist/{http-CX_zHeOD.mjs → http-D9zG-L9N.mjs} +3 -3
- package/dist/{http-Du1Jgf2P.cjs → http-FnUTcdMf.cjs} +1 -1
- package/dist/{key-C1Oto4it.mjs → key-CV57mOYH.mjs} +1 -1
- package/dist/{kv-cache-aGOwL6Vj.cjs → kv-cache-BG9O8wVV.cjs} +1 -1
- package/dist/{kv-cache-CsC3P4uu.js → kv-cache-C3esyJFP.js} +1 -1
- package/dist/{ld-Dl1HIB1a.mjs → ld-sUf94RJ8.mjs} +2 -2
- package/dist/{middleware-BiFLcrEX.cjs → middleware-CKkBrsOD.cjs} +203 -32
- package/dist/{middleware-C1cf3_6V.mjs → middleware-cMxbPxDe.mjs} +1 -1
- package/dist/{middleware-CKJC8DRf.js → middleware-fAuUxD9-.js} +203 -32
- package/dist/{middleware-BwC5U8zJ.cjs → middleware-ohzkLsW4.cjs} +1 -1
- package/dist/{middleware-CEWDB8EB.mjs → middleware-pb2EqN_r.mjs} +75 -27
- package/dist/{mod-ckCOmoCz.d.ts → mod-B8Z8mBLk.d.ts} +1 -1
- package/dist/{mod-BghZgD_U.d.cts → mod-DClCOv0M.d.cts} +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/client.test.mjs +2 -2
- package/dist/nodeinfo/handler.test.mjs +3 -3
- package/dist/nodeinfo/types.test.mjs +1 -1
- package/dist/otel/exporter.test.mjs +25 -22
- package/dist/otel/mod.cjs +6 -5
- package/dist/otel/mod.d.cts +3 -2
- package/dist/otel/mod.d.ts +3 -2
- package/dist/otel/mod.js +6 -5
- package/dist/{outgoing-jsonld-CNmZLixq.mjs → outgoing-jsonld-Bi7n-dEy.mjs} +1 -1
- package/dist/{owner-CEWFJlqo.mjs → owner-DsPgl527.mjs} +2 -2
- package/dist/{proof-CDA3f-i5.cjs → proof-BhJpq_J9.cjs} +1 -1
- package/dist/{proof-BFyPVl1r.mjs → proof-iVfYyJpY.mjs} +4 -4
- package/dist/{proof-Dedf8md5.js → proof-k4mEvvdS.js} +1 -1
- package/dist/send-D-vYdfC6.mjs +306 -0
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +4 -4
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +2 -2
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/testing/mod.d.mts +18 -1
- package/dist/testing/mod.mjs +1 -1
- package/dist/utils/docloader.test.mjs +4 -4
- 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 +7 -6
- package/dist/send-BJickEP4.mjs +0 -193
- /package/dist/{accept-CPkZzmGN.mjs → accept-CceiKpCy.mjs} +0 -0
- /package/dist/{activity-listener-ell7W1s9.mjs → activity-listener-tztVvlNb.mjs} +0 -0
- /package/dist/{client-D_1QpnWt.mjs → client-CIiz1WX7.mjs} +0 -0
- /package/dist/{collection-D-HqUuA2.mjs → collection-CA3V5zyK.mjs} +0 -0
- /package/dist/{keycache-EGATflN-.mjs → keycache-BeU0LCII.mjs} +0 -0
- /package/dist/{keys-DGu1NFwu.mjs → keys-C3kae-6B.mjs} +0 -0
- /package/dist/{kv-cache-U__xU4qR.mjs → kv-cache-Bmv7tUzz.mjs} +0 -0
- /package/dist/{kv-rV3vodCc.mjs → kv-x2IvBUyq.mjs} +0 -0
- /package/dist/{negotiation-SQvQgUqe.mjs → negotiation-VnHNB0Q5.mjs} +0 -0
- /package/dist/{public-audience-DYFHzm_c.mjs → public-audience-PVTwU_Ex.mjs} +0 -0
- /package/dist/{retry-bMXBL97A.mjs → retry-_VvV0h9f.mjs} +0 -0
- /package/dist/{types-J53Kw7so.mjs → types-BFowWFTT.mjs} +0 -0
|
@@ -2,13 +2,13 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
import { t as __exportAll } from "./chunk-nlSIicah.js";
|
|
4
4
|
import { r as getDefaultActivityTransformers } from "./transformers-ve6e2xcg.js";
|
|
5
|
-
import { _ as version, a as verifyRequestDetailed, d as validateCryptoKey, f as formatAcceptSignature, g as name, i as verifyRequest, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, u as importJwk } from "./http-
|
|
6
|
-
import { c as getKeyOwner, d as detachSignature, f as hasSignatureLike, i as verifyObject, m as verifyJsonLd, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, p as signJsonLd, r as signObject, s as doesActorOwnKey } from "./proof-
|
|
5
|
+
import { _ as version, a as verifyRequestDetailed, d as validateCryptoKey, f as formatAcceptSignature, g as name, i as verifyRequest, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, u as importJwk } from "./http-BUr93aO6.js";
|
|
6
|
+
import { c as getKeyOwner, d as detachSignature, f as hasSignatureLike, i as verifyObject, m as verifyJsonLd, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, p as signJsonLd, r as signObject, s as doesActorOwnKey } from "./proof-k4mEvvdS.js";
|
|
7
7
|
import { n as getNodeInfo, t as nodeInfoToJson } from "./types-hvL8ElAs.js";
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-C3esyJFP.js";
|
|
9
9
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
10
10
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
11
|
-
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
11
|
+
import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
|
|
12
12
|
import { cloneDeep, uniq } from "es-toolkit";
|
|
13
13
|
import { Router } from "uri-template-router";
|
|
14
14
|
import { parseTemplate } from "url-template";
|
|
@@ -160,6 +160,7 @@ var RouterError = class extends Error {
|
|
|
160
160
|
};
|
|
161
161
|
//#endregion
|
|
162
162
|
//#region src/federation/builder.ts
|
|
163
|
+
const ACTOR_ALIAS_PREFIX = "actorAlias:";
|
|
163
164
|
function validateSingleIdentifierVariablePath(path, errorMessage) {
|
|
164
165
|
const operatorMatches = globalThis.Array.from(path.matchAll(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g));
|
|
165
166
|
if (operatorMatches.length !== 1 || operatorMatches[0]?.[2] !== "identifier") throw new RouterError(errorMessage);
|
|
@@ -346,6 +347,15 @@ var FederationBuilderImpl = class {
|
|
|
346
347
|
callbacks.aliasMapper = mapper;
|
|
347
348
|
return setters;
|
|
348
349
|
},
|
|
350
|
+
mapActorAlias: (path, identifier) => {
|
|
351
|
+
if (identifier === "") throw new RouterError("Identifier cannot be empty.");
|
|
352
|
+
if (this.router.has(`actorAlias:${identifier}`)) throw new RouterError(`Actor alias for "${identifier}" already set.`);
|
|
353
|
+
if (new Router$1().add(path, "temp").size > 0) throw new RouterError("Path for actor alias must have no variables.");
|
|
354
|
+
const existingRoute = this.router.route(path);
|
|
355
|
+
if (existingRoute != null) throw new RouterError(`Actor alias path "${path}" conflicts with existing route "${existingRoute.name}".`);
|
|
356
|
+
this.router.add(path, `${ACTOR_ALIAS_PREFIX}${identifier}`);
|
|
357
|
+
return setters;
|
|
358
|
+
},
|
|
349
359
|
authorize(predicate) {
|
|
350
360
|
callbacks.authorizePredicate = predicate;
|
|
351
361
|
return setters;
|
|
@@ -769,8 +779,90 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
|
769
779
|
return `collectionId="${collectionId}", url="${url}", digest="${encodeHex(await digest(actorIds))}"`;
|
|
770
780
|
}
|
|
771
781
|
//#endregion
|
|
782
|
+
//#region src/federation/metrics.ts
|
|
783
|
+
var FederationMetrics = class {
|
|
784
|
+
deliverySent;
|
|
785
|
+
deliveryPermanentFailure;
|
|
786
|
+
signatureVerificationFailure;
|
|
787
|
+
deliveryDuration;
|
|
788
|
+
inboxProcessingDuration;
|
|
789
|
+
constructor(meterProvider) {
|
|
790
|
+
const meter = meterProvider.getMeter(name, version);
|
|
791
|
+
this.deliverySent = meter.createCounter("activitypub.delivery.sent", {
|
|
792
|
+
description: "ActivityPub delivery attempts.",
|
|
793
|
+
unit: "{attempt}"
|
|
794
|
+
});
|
|
795
|
+
this.deliveryPermanentFailure = meter.createCounter("activitypub.delivery.permanent_failure", {
|
|
796
|
+
description: "ActivityPub deliveries abandoned as permanent failures.",
|
|
797
|
+
unit: "{failure}"
|
|
798
|
+
});
|
|
799
|
+
this.signatureVerificationFailure = meter.createCounter("activitypub.signature.verification_failure", {
|
|
800
|
+
description: "ActivityPub signature verification failures.",
|
|
801
|
+
unit: "{failure}"
|
|
802
|
+
});
|
|
803
|
+
this.deliveryDuration = meter.createHistogram("activitypub.delivery.duration", {
|
|
804
|
+
description: "Duration of ActivityPub delivery attempts.",
|
|
805
|
+
unit: "ms"
|
|
806
|
+
});
|
|
807
|
+
this.inboxProcessingDuration = meter.createHistogram("activitypub.inbox.processing_duration", {
|
|
808
|
+
description: "Duration of ActivityPub inbox listener processing.",
|
|
809
|
+
unit: "ms"
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
recordDelivery(inbox, durationMs, success, activityType) {
|
|
813
|
+
const deliveryAttributes = {
|
|
814
|
+
"activitypub.remote.host": getRemoteHost(inbox),
|
|
815
|
+
"activitypub.delivery.success": success
|
|
816
|
+
};
|
|
817
|
+
if (activityType != null) deliveryAttributes["activitypub.activity.type"] = activityType;
|
|
818
|
+
this.deliverySent.add(1, deliveryAttributes);
|
|
819
|
+
this.deliveryDuration.record(durationMs, deliveryAttributes);
|
|
820
|
+
}
|
|
821
|
+
recordPermanentFailure(inbox, statusCode) {
|
|
822
|
+
this.deliveryPermanentFailure.add(1, {
|
|
823
|
+
"activitypub.remote.host": getRemoteHost(inbox),
|
|
824
|
+
"http.response.status_code": statusCode
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
recordSignatureVerificationFailure(reason, remoteHost) {
|
|
828
|
+
const attributes = { "activitypub.verification.failure_reason": reason };
|
|
829
|
+
if (remoteHost != null) attributes["activitypub.remote.host"] = remoteHost;
|
|
830
|
+
this.signatureVerificationFailure.add(1, attributes);
|
|
831
|
+
}
|
|
832
|
+
recordInboxProcessingDuration(activityType, durationMs) {
|
|
833
|
+
this.inboxProcessingDuration.record(durationMs, { "activitypub.activity.type": activityType });
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
const federationMetrics = /* @__PURE__ */ new WeakMap();
|
|
837
|
+
/**
|
|
838
|
+
* Gets the cached Fedify metric instruments for a meter provider.
|
|
839
|
+
* @since 2.3.0
|
|
840
|
+
*/
|
|
841
|
+
function getFederationMetrics(meterProvider = metrics.getMeterProvider()) {
|
|
842
|
+
let instruments = federationMetrics.get(meterProvider);
|
|
843
|
+
if (instruments == null) {
|
|
844
|
+
instruments = new FederationMetrics(meterProvider);
|
|
845
|
+
federationMetrics.set(meterProvider, instruments);
|
|
846
|
+
}
|
|
847
|
+
return instruments;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Gets the bounded remote host attribute value for a URL.
|
|
851
|
+
* @since 2.3.0
|
|
852
|
+
*/
|
|
853
|
+
function getRemoteHost(url) {
|
|
854
|
+
return url.hostname;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Gets an elapsed duration in milliseconds from a `performance.now()` value.
|
|
858
|
+
* @since 2.3.0
|
|
859
|
+
*/
|
|
860
|
+
function getDurationMs(start) {
|
|
861
|
+
return Math.max(0, performance.now() - start);
|
|
862
|
+
}
|
|
863
|
+
//#endregion
|
|
772
864
|
//#region src/federation/inbox.ts
|
|
773
|
-
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
|
|
865
|
+
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
774
866
|
const logger = getLogger([
|
|
775
867
|
"fedify",
|
|
776
868
|
"federation",
|
|
@@ -871,7 +963,13 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
|
|
|
871
963
|
const { class: cls, listener } = dispatched;
|
|
872
964
|
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
873
965
|
try {
|
|
874
|
-
|
|
966
|
+
const activityType = getTypeId(activity).href;
|
|
967
|
+
const started = performance.now();
|
|
968
|
+
try {
|
|
969
|
+
await listener(inboxContextFactory(recipient, json, activity?.id?.href, activityType), activity);
|
|
970
|
+
} finally {
|
|
971
|
+
getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
972
|
+
}
|
|
875
973
|
} catch (error) {
|
|
876
974
|
try {
|
|
877
975
|
await inboxErrorHandler?.(ctx, error);
|
|
@@ -1681,6 +1779,8 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1681
1779
|
});
|
|
1682
1780
|
if (verification.verified === false) {
|
|
1683
1781
|
const reason = verification.reason;
|
|
1782
|
+
const remoteHost = "keyId" in reason && reason.keyId != null ? getRemoteHost(reason.keyId) : void 0;
|
|
1783
|
+
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure(reason.type, remoteHost);
|
|
1684
1784
|
logger.error("Failed to verify the request's HTTP Signatures.", {
|
|
1685
1785
|
recipient,
|
|
1686
1786
|
reason: reason.type,
|
|
@@ -1767,6 +1867,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1767
1867
|
"http_signatures.key_id": httpSigKey?.id?.href ?? ""
|
|
1768
1868
|
});
|
|
1769
1869
|
if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
|
|
1870
|
+
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("actorKeyMismatch", httpSigKey.id == null ? void 0 : getRemoteHost(httpSigKey.id));
|
|
1770
1871
|
logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
|
|
1771
1872
|
activity: json,
|
|
1772
1873
|
recipient,
|
|
@@ -1784,6 +1885,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1784
1885
|
}
|
|
1785
1886
|
if (pendingNonceLabel != null) {
|
|
1786
1887
|
if (!await verifySignatureNonce(request, kv, kvPrefixes.acceptSignatureNonce, pendingNonceLabel)) {
|
|
1888
|
+
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("invalidNonce", httpSigKey?.id == null ? void 0 : getRemoteHost(httpSigKey.id));
|
|
1787
1889
|
logger.error("Signature nonce verification failed (missing, expired, or replayed).", { recipient });
|
|
1788
1890
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
1789
1891
|
}
|
|
@@ -1800,6 +1902,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1800
1902
|
kvPrefixes,
|
|
1801
1903
|
queue,
|
|
1802
1904
|
span,
|
|
1905
|
+
meterProvider: parameters.meterProvider,
|
|
1803
1906
|
tracerProvider,
|
|
1804
1907
|
idempotencyStrategy: parameters.idempotencyStrategy
|
|
1805
1908
|
});
|
|
@@ -2447,6 +2550,25 @@ function sendActivity(options) {
|
|
|
2447
2550
|
});
|
|
2448
2551
|
}
|
|
2449
2552
|
const MAX_ERROR_RESPONSE_BODY_BYTES = 1024;
|
|
2553
|
+
function getActivityActorId(activity) {
|
|
2554
|
+
if (!isRecord(activity)) return void 0;
|
|
2555
|
+
return getIdValue(activity.actor);
|
|
2556
|
+
}
|
|
2557
|
+
function getIdValue(value) {
|
|
2558
|
+
if (typeof value === "string" && value !== "") return value;
|
|
2559
|
+
if (value instanceof URL) return value.href;
|
|
2560
|
+
if (Array.isArray(value)) {
|
|
2561
|
+
for (const item of value) {
|
|
2562
|
+
const id = getIdValue(item);
|
|
2563
|
+
if (id != null) return id;
|
|
2564
|
+
}
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2567
|
+
if (isRecord(value)) return getIdValue(value.id);
|
|
2568
|
+
}
|
|
2569
|
+
function isRecord(value) {
|
|
2570
|
+
return typeof value === "object" && value != null;
|
|
2571
|
+
}
|
|
2450
2572
|
async function readLimitedResponseBody(response, maxBytes) {
|
|
2451
2573
|
if (response.body == null) return "";
|
|
2452
2574
|
const reader = response.body.getReader();
|
|
@@ -2474,12 +2596,15 @@ async function readLimitedResponseBody(response, maxBytes) {
|
|
|
2474
2596
|
if (truncated) result += "… (truncated)";
|
|
2475
2597
|
return result;
|
|
2476
2598
|
}
|
|
2477
|
-
async function sendActivityInternal({ activity, activityId, keys, inbox, headers, specDeterminer, tracerProvider }, span) {
|
|
2599
|
+
async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, headers, specDeterminer, meterProvider, tracerProvider }, span) {
|
|
2478
2600
|
const logger = getLogger([
|
|
2479
2601
|
"fedify",
|
|
2480
2602
|
"federation",
|
|
2481
2603
|
"outbox"
|
|
2482
2604
|
]);
|
|
2605
|
+
const federationMetrics = getFederationMetrics(meterProvider);
|
|
2606
|
+
const started = performance.now();
|
|
2607
|
+
let deliverySuccess = false;
|
|
2483
2608
|
headers = new Headers(headers);
|
|
2484
2609
|
headers.set("Content-Type", "application/activity+json");
|
|
2485
2610
|
const request = new Request(inbox, {
|
|
@@ -2511,29 +2636,38 @@ async function sendActivityInternal({ activity, activityId, keys, inbox, headers
|
|
|
2511
2636
|
inbox: inbox.href,
|
|
2512
2637
|
error
|
|
2513
2638
|
});
|
|
2639
|
+
federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
|
|
2514
2640
|
throw error;
|
|
2515
2641
|
}
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2642
|
+
try {
|
|
2643
|
+
if (!response.ok) {
|
|
2644
|
+
let error;
|
|
2645
|
+
try {
|
|
2646
|
+
error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
|
|
2647
|
+
} catch (_) {
|
|
2648
|
+
error = "";
|
|
2649
|
+
}
|
|
2650
|
+
logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
2651
|
+
activityId,
|
|
2652
|
+
inbox: inbox.href,
|
|
2653
|
+
status: response.status,
|
|
2654
|
+
statusText: response.statusText,
|
|
2655
|
+
error
|
|
2656
|
+
});
|
|
2657
|
+
throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error);
|
|
2522
2658
|
}
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
inbox: inbox.href,
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2659
|
+
deliverySuccess = true;
|
|
2660
|
+
const eventAttributes = {
|
|
2661
|
+
"activitypub.inbox.url": inbox.href,
|
|
2662
|
+
"activitypub.activity.id": activityId ?? ""
|
|
2663
|
+
};
|
|
2664
|
+
if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
|
|
2665
|
+
const actorId = getActivityActorId(activity);
|
|
2666
|
+
if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
|
|
2667
|
+
span.addEvent("activitypub.activity.sent", eventAttributes);
|
|
2668
|
+
} finally {
|
|
2669
|
+
federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
|
|
2531
2670
|
}
|
|
2532
|
-
span.addEvent("activitypub.activity.sent", {
|
|
2533
|
-
"activitypub.activity.json": JSON.stringify(activity),
|
|
2534
|
-
"activitypub.inbox.url": inbox.href,
|
|
2535
|
-
"activitypub.activity.id": activityId ?? ""
|
|
2536
|
-
});
|
|
2537
2671
|
}
|
|
2538
2672
|
/**
|
|
2539
2673
|
* An error that is thrown when an activity fails to send to a remote inbox.
|
|
@@ -2751,6 +2885,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2751
2885
|
inboxRetryPolicy;
|
|
2752
2886
|
activityTransformers;
|
|
2753
2887
|
_tracerProvider;
|
|
2888
|
+
_meterProvider;
|
|
2754
2889
|
firstKnock;
|
|
2755
2890
|
inboxChallengePolicy;
|
|
2756
2891
|
constructor(options) {
|
|
@@ -2836,11 +2971,15 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2836
2971
|
this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
|
|
2837
2972
|
this.activityTransformers = options.activityTransformers ?? getDefaultActivityTransformers();
|
|
2838
2973
|
this._tracerProvider = options.tracerProvider;
|
|
2974
|
+
this._meterProvider = options.meterProvider;
|
|
2839
2975
|
this.firstKnock = options.firstKnock;
|
|
2840
2976
|
}
|
|
2841
2977
|
get tracerProvider() {
|
|
2842
2978
|
return this._tracerProvider ?? trace.getTracerProvider();
|
|
2843
2979
|
}
|
|
2980
|
+
get meterProvider() {
|
|
2981
|
+
return this._meterProvider ?? metrics.getMeterProvider();
|
|
2982
|
+
}
|
|
2844
2983
|
_initializeRouter() {
|
|
2845
2984
|
this.router.add("/.well-known/webfinger", "webfinger");
|
|
2846
2985
|
this.router.add("/.well-known/nodeinfo", "nodeInfoJrd");
|
|
@@ -3019,6 +3158,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3019
3158
|
sharedInbox: message.sharedInbox,
|
|
3020
3159
|
headers: new Headers(message.headers),
|
|
3021
3160
|
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock),
|
|
3161
|
+
meterProvider: this.meterProvider,
|
|
3022
3162
|
tracerProvider: this.tracerProvider
|
|
3023
3163
|
});
|
|
3024
3164
|
} catch (error) {
|
|
@@ -3026,6 +3166,21 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3026
3166
|
code: SpanStatusCode.ERROR,
|
|
3027
3167
|
message: String(error)
|
|
3028
3168
|
});
|
|
3169
|
+
const remoteHost = (() => {
|
|
3170
|
+
if (error instanceof SendActivityError) return getRemoteHost(error.inbox);
|
|
3171
|
+
try {
|
|
3172
|
+
return getRemoteHost(new URL(message.inbox));
|
|
3173
|
+
} catch (_) {
|
|
3174
|
+
logger.warn("Invalid inbox URL in queued outbox message: {inbox}", logData);
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
})();
|
|
3178
|
+
span.addEvent("activitypub.delivery.failed", {
|
|
3179
|
+
...remoteHost == null ? {} : { "activitypub.remote.host": remoteHost },
|
|
3180
|
+
"activitypub.delivery.attempt": message.attempt,
|
|
3181
|
+
"activitypub.delivery.permanent_failure": error instanceof SendActivityError && this.permanentFailureStatusCodes.includes(error.statusCode),
|
|
3182
|
+
...error instanceof SendActivityError ? { "http.response.status_code": error.statusCode } : {}
|
|
3183
|
+
});
|
|
3029
3184
|
const loaderOptions = this.#getLoaderOptions(message.baseUrl);
|
|
3030
3185
|
const activity = await Activity.fromJsonLd(message.activity, {
|
|
3031
3186
|
contextLoader: this.contextLoaderFactory(loaderOptions),
|
|
@@ -3041,6 +3196,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3041
3196
|
});
|
|
3042
3197
|
}
|
|
3043
3198
|
if (error instanceof SendActivityError && this.permanentFailureStatusCodes.includes(error.statusCode)) {
|
|
3199
|
+
getFederationMetrics(this.meterProvider).recordPermanentFailure(error.inbox, error.statusCode);
|
|
3044
3200
|
logger.warn("Permanent delivery failure for activity {activityId} to {inbox} ({status}); not retrying.", {
|
|
3045
3201
|
...logData,
|
|
3046
3202
|
status: error.statusCode
|
|
@@ -3149,7 +3305,13 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3149
3305
|
const { class: cls, listener } = dispatched;
|
|
3150
3306
|
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
3151
3307
|
try {
|
|
3152
|
-
|
|
3308
|
+
const activityType = getTypeId(activity).href;
|
|
3309
|
+
const started = performance.now();
|
|
3310
|
+
try {
|
|
3311
|
+
await listener(context.toInboxContext(message.identifier, message.activity, activity.id?.href, activityType), activity);
|
|
3312
|
+
} finally {
|
|
3313
|
+
getFederationMetrics(this.meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
3314
|
+
}
|
|
3153
3315
|
} catch (error) {
|
|
3154
3316
|
try {
|
|
3155
3317
|
await this.inboxErrorHandler?.(context, error);
|
|
@@ -3332,6 +3494,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3332
3494
|
sharedInbox: inboxes[inbox].sharedInbox,
|
|
3333
3495
|
headers: collectionSync == null ? void 0 : new Headers({ "Collection-Synchronization": await buildCollectionSynchronizationHeader(collectionSync, inboxes[inbox].actorIds) }),
|
|
3334
3496
|
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock),
|
|
3497
|
+
meterProvider: this.meterProvider,
|
|
3335
3498
|
tracerProvider: this.tracerProvider
|
|
3336
3499
|
}));
|
|
3337
3500
|
await Promise.all(promises);
|
|
@@ -3508,15 +3671,18 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3508
3671
|
if (request.method !== "POST" && !acceptsJsonLd(request)) return await onNotAcceptable(request);
|
|
3509
3672
|
switch (routeName) {
|
|
3510
3673
|
case "actor":
|
|
3511
|
-
|
|
3674
|
+
case "actorAlias": {
|
|
3675
|
+
const identifier = route.name.startsWith("actorAlias:") ? route.name.substring(11) : route.values.identifier;
|
|
3676
|
+
context = this.#createContext(request, contextData, { invokedFromActorDispatcher: { identifier } });
|
|
3512
3677
|
return await handleActor(request, {
|
|
3513
|
-
identifier
|
|
3678
|
+
identifier,
|
|
3514
3679
|
context,
|
|
3515
3680
|
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
3516
3681
|
authorizePredicate: this.actorCallbacks?.authorizePredicate,
|
|
3517
3682
|
onUnauthorized,
|
|
3518
3683
|
onNotFound
|
|
3519
3684
|
});
|
|
3685
|
+
}
|
|
3520
3686
|
case "object": {
|
|
3521
3687
|
const typeId = route.name.replace(/^object:/, "");
|
|
3522
3688
|
const callbacks = this.objectCallbacks[typeId];
|
|
@@ -3598,6 +3764,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3598
3764
|
signatureTimeWindow: this.signatureTimeWindow,
|
|
3599
3765
|
skipSignatureVerification: this.skipSignatureVerification,
|
|
3600
3766
|
inboxChallengePolicy: this.inboxChallengePolicy,
|
|
3767
|
+
meterProvider: this.meterProvider,
|
|
3601
3768
|
tracerProvider: this.tracerProvider,
|
|
3602
3769
|
idempotencyStrategy: this.idempotencyStrategy
|
|
3603
3770
|
});
|
|
@@ -3759,13 +3926,16 @@ var ContextImpl = class ContextImpl {
|
|
|
3759
3926
|
get tracerProvider() {
|
|
3760
3927
|
return this.federation.tracerProvider;
|
|
3761
3928
|
}
|
|
3929
|
+
get meterProvider() {
|
|
3930
|
+
return this.federation.meterProvider;
|
|
3931
|
+
}
|
|
3762
3932
|
getNodeInfoUri() {
|
|
3763
3933
|
const path = this.federation.router.build("nodeInfo", {});
|
|
3764
3934
|
if (path == null) throw new RouterError("No NodeInfo dispatcher registered.");
|
|
3765
3935
|
return new URL(path, this.canonicalOrigin);
|
|
3766
3936
|
}
|
|
3767
3937
|
getActorUri(identifier) {
|
|
3768
|
-
const path = this.federation.router.build("actor", { identifier });
|
|
3938
|
+
const path = this.federation.router.build(`actorAlias:${identifier}`, {}) ?? this.federation.router.build("actor", { identifier });
|
|
3769
3939
|
if (path == null) throw new RouterError("No actor dispatcher registered.");
|
|
3770
3940
|
return new URL(path, this.canonicalOrigin);
|
|
3771
3941
|
}
|
|
@@ -3831,8 +4001,8 @@ var ContextImpl = class ContextImpl {
|
|
|
3831
4001
|
type: "inbox",
|
|
3832
4002
|
identifier: void 0
|
|
3833
4003
|
};
|
|
3834
|
-
const identifier = route.values.identifier;
|
|
3835
|
-
if (route.name === "actor") return {
|
|
4004
|
+
const identifier = route.name.startsWith("actorAlias:") ? route.name.substring(11) : route.values.identifier;
|
|
4005
|
+
if (route.name === "actor" || route.name.startsWith("actorAlias:")) return {
|
|
3836
4006
|
type: "actor",
|
|
3837
4007
|
identifier
|
|
3838
4008
|
};
|
|
@@ -4508,6 +4678,7 @@ async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipient
|
|
|
4508
4678
|
activityType: ctx.activityType,
|
|
4509
4679
|
inbox: new URL(inbox),
|
|
4510
4680
|
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4681
|
+
meterProvider: ctx.meterProvider,
|
|
4511
4682
|
tracerProvider: ctx.tracerProvider,
|
|
4512
4683
|
specDeterminer: new KvSpecDeterminer(ctx.federation.kv, ctx.federation.kvPrefixes.httpMessageSignaturesSpec, ctx.federation.firstKnock)
|
|
4513
4684
|
}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
const { Temporal } = require("@js-temporal/polyfill");
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
|
-
const require_middleware = require("./middleware-
|
|
3
|
+
const require_middleware = require("./middleware-CKkBrsOD.cjs");
|
|
4
4
|
exports.FederationImpl = require_middleware.FederationImpl;
|