@fedify/fedify 2.4.0-dev.2256 → 2.4.0-dev.2260
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-DvSQqk2l.mjs → builder-BK8HfSUm.mjs} +8 -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/{compound-proof-5bg_j_ye.mjs → compound-proof-D--HeIbV.mjs} +41 -4
- package/dist/{context-CtI307_A.d.ts → context-7NyiqIcg.d.ts} +111 -2
- package/dist/{context-B6X-t8gI.d.cts → context-tsYPJ2Ew.d.cts} +111 -2
- package/dist/{deno-AmcRMq5z.mjs → deno-DVP7DGJC.mjs} +1 -1
- package/dist/{docloader-CIP_SG39.mjs → docloader-Dv1JJl37.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +76 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.d.mts +2 -0
- package/dist/federation/inbox-report.test.mjs +475 -0
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +4 -3
- package/dist/federation/mod.d.ts +4 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +54 -10
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +66 -4
- package/dist/federation/portable-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +4 -4
- package/dist/federation/portable-inbox.test.mjs +67 -6
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-BDzIgqOd.cjs → federation-Bo5z395X.cjs} +552 -122
- package/dist/{http-1fZ83jj7.cjs → http-BIhWdDf5.cjs} +447 -110
- package/dist/{http-BGb3QioZ.js → http-DRMu1C2T.js} +430 -111
- package/dist/{http-DkqWu-sm.mjs → http-DvpuyDJB.mjs} +141 -98
- package/dist/{key-CEsu7oaD.mjs → key-B7N8mNoY.mjs} +30 -3
- package/dist/{http-BrwKINQn.d.cts → key-C1nv7_jD.d.ts} +638 -229
- package/dist/{http-DGx0KOws.d.ts → key-DYxuyd-s.d.cts} +638 -229
- package/dist/{kv-cache-CJS4mImS.mjs → kv-cache-D0kA0rml.mjs} +1 -1
- package/dist/{kv-cache-CGhEgt98.js → kv-cache-DZLpqhW9.js} +1 -1
- package/dist/{kv-cache-BEWuZSB4.cjs → kv-cache-mF8Zmjix.cjs} +1 -1
- package/dist/{ld-Cf4CPWN_.mjs → ld-BDMNyM2M.mjs} +40 -5
- package/dist/{metrics-CocsZP0E.mjs → metrics-BbBj-VZN.mjs} +2 -1
- package/dist/{middleware-Cne_jMZo.mjs → middleware-CE1X7-UM.mjs} +1 -1
- package/dist/{middleware-Dxn0E9ES.mjs → middleware-DAA7wDdS.mjs} +520 -131
- package/dist/{middleware-BfYke3YQ.js → middleware-DfUWPAa-.js} +552 -122
- package/dist/{mod-C7_2j7VT.d.cts → mod-BojP2Bw3.d.cts} +1 -1
- package/dist/{mod-BlLwewYe.d.ts → mod-CavhAgLw.d.cts} +5 -255
- package/dist/{mod-VHYh1lTK.d.cts → mod-CtpF2BU5.d.ts} +5 -255
- package/dist/{mod-B8MI1mpi.d.ts → mod-DEZXwV6J.d.ts} +1 -1
- package/dist/{mod-DFL6IxYQ.d.cts → mod-Dw-nPxQh.d.cts} +1 -1
- package/dist/{mod-BhTaDR5H.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +7 -7
- package/dist/mod.d.ts +7 -7
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-DSJ7nFgy.mjs → owner-CzUycuhd.mjs} +3 -3
- package/dist/{portable-collection-BuBV6xTo.js → portable-collection-COcJxZg_.js} +1 -1
- package/dist/{portable-collection-qskqXJrH.mjs → portable-collection-CTcNJiAa.mjs} +2 -2
- package/dist/{portable-collection-r-mHg1PV.cjs → portable-collection-cPMffcHJ.cjs} +1 -1
- package/dist/{portable-key-woGH_1nn.mjs → portable-key-B4QN3OzR.mjs} +1 -1
- package/dist/{proof-8WiH_-Gq.mjs → proof-nce4pf5S.mjs} +133 -16
- package/dist/{send-JKzZBgPU.mjs → send-4Idpe2z0.mjs} +3 -3
- 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 +19 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +76 -3
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.d.mts +2 -0
- package/dist/sig/verification.test.mjs +310 -0
- package/dist/{temporal-CT3fRaMH.mjs → temporal-BbjmPHYn.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +315 -28
- 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.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/dist/verification-C3954hQh.mjs +5 -0
- package/dist/verification-Dpfcw8ml.mjs +105 -0
- package/package.json +7 -7
|
@@ -2,10 +2,10 @@ import { Temporal } from "temporal-polyfill";
|
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
import { t as __exportAll } from "./rolldown-runtime-BC-Eo5Mj.js";
|
|
4
4
|
import { r as getDefaultActivityTransformers } from "./transformers-DAtXl_Kc.js";
|
|
5
|
-
import { $ as instrumentDocumentLoader, A as hasSignature, At as
|
|
5
|
+
import { $ as instrumentDocumentLoader, A as hasSignature, At as isPortableUri, C as InvalidContextReferenceError, Ct as getGatewayKeyBase, Dt as isCompatibleKeyId, E as compactJsonLd, Et as hasPortableActor, F as verifyCompactJsonLd, G as validateCryptoKey, M as isClearlyMalformedContextReference, N as isInvalidUrlTypeError, Nt as name, O as detachSignature, Ot as isPortableId, P as signJsonLd, Pt as version, Q as getRemoteHost, R as wrapContextLoaderForJsonLd, S as normalizeOutgoingActivityJsonLd, St as getCanonicalPortableId, Tt as getRawCanonicalPortableId, W as importJwk, X as getDurationMs, Z as getFederationMetrics, _ as verifyObject, at as recordCollectionTotalItems, b as verifyPortableObjectProofWithRoot, bt as verificationObservation, c as verifyRequestDetailed, ct as recordInboxActivity, d as InvalidPortableObjectIdError, dt as recordWebFingerHandle, et as isAbortError, f as classifyFep2277CoreType, ft as registerQueueDepthGauge, g as verifyMapLocalProof, h as signObject, i as selectRequestSignature, it as recordCollectionRequest, j as hasSignatureLike, jt as isSameObjectId, k as getNormalizationContextLoader, kt as isPortableKeyId, lt as recordOutboxActivity, m as hasProofLike, mt as formatAcceptSignature, n as listRequestSignatures, nt as recordCollectionDispatchDuration, o as validateMaxSignatures, pt as preloadedOnlyDocumentLoader, q as MAX_PORTABLE_KEY_TTL, r as parseRfc9421SignatureInput, rt as recordCollectionPageItems, s as verifyRequest, st as recordFanoutRecipients, t as doubleKnock, tt as recordCircuitBreakerStateChange, ut as recordOutboxEnqueue, v as verifyPortableObjectProof, vt as observeAttempt, w as assertSafeJsonLd, wt as getPortableDid, xt as getAuthenticationOrigin, y as verifyPortableObjectProofPolicy, yt as snapshotKey, z as exportJwk } from "./http-DRMu1C2T.js";
|
|
6
6
|
import { a as MAX_BODY_SIZE, i as BodyTooLargeError, n as getNodeInfo, o as readBoundedText, t as nodeInfoToJson } from "./types-_S2_MeLF.js";
|
|
7
|
-
import { n as doesActorOwnKey, r as getKeyOwner, t as verifyPortableObject } from "./portable-collection-
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
7
|
+
import { n as doesActorOwnKey, r as getKeyOwner, t as verifyPortableObject } from "./portable-collection-COcJxZg_.js";
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DZLpqhW9.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, PUBLIC_COLLECTION, Tombstone, getTypeId, isActor, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
@@ -100,6 +100,7 @@ var FederationBuilderImpl = class {
|
|
|
100
100
|
featuredTagsCallbacks;
|
|
101
101
|
inboxListeners;
|
|
102
102
|
outboxListeners;
|
|
103
|
+
inboxRequestFinishedHandler;
|
|
103
104
|
inboxErrorHandler;
|
|
104
105
|
outboxListenerErrorHandler;
|
|
105
106
|
outboxAuthorizePredicate;
|
|
@@ -161,6 +162,7 @@ var FederationBuilderImpl = class {
|
|
|
161
162
|
f.featuredTagsCallbacks = this.featuredTagsCallbacks == null ? void 0 : { ...this.featuredTagsCallbacks };
|
|
162
163
|
f.inboxListeners = this.inboxListeners?.clone();
|
|
163
164
|
f.outboxListeners = this.outboxListeners?.clone();
|
|
165
|
+
f.inboxRequestFinishedHandler = this.inboxRequestFinishedHandler;
|
|
164
166
|
f.inboxErrorHandler = this.inboxErrorHandler;
|
|
165
167
|
f.outboxListenerErrorHandler = this.outboxListenerErrorHandler;
|
|
166
168
|
f.outboxAuthorizePredicate = this.outboxAuthorizePredicate;
|
|
@@ -641,6 +643,10 @@ var FederationBuilderImpl = class {
|
|
|
641
643
|
this.inboxErrorHandler = handler;
|
|
642
644
|
return setters;
|
|
643
645
|
},
|
|
646
|
+
onRequestFinished: (handler) => {
|
|
647
|
+
this.inboxRequestFinishedHandler = handler;
|
|
648
|
+
return setters;
|
|
649
|
+
},
|
|
644
650
|
onUnverifiedActivity: (handler) => {
|
|
645
651
|
this.unverifiedActivityHandler = handler;
|
|
646
652
|
return setters;
|
|
@@ -1446,6 +1452,111 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
|
1446
1452
|
return `collectionId="${collectionId}", url="${url}", digest="${encodeHex(await digest(actorIds))}"`;
|
|
1447
1453
|
}
|
|
1448
1454
|
//#endregion
|
|
1455
|
+
//#region src/federation/inbox-observation.ts
|
|
1456
|
+
/** Internal state owned by the ingress boundary, never serialized into queues. */
|
|
1457
|
+
var InboxObservation = class {
|
|
1458
|
+
inbox;
|
|
1459
|
+
payload = { status: "unavailable" };
|
|
1460
|
+
activity = null;
|
|
1461
|
+
authentication = { status: "notDetermined" };
|
|
1462
|
+
hasException = false;
|
|
1463
|
+
stage = "prepare";
|
|
1464
|
+
result = {
|
|
1465
|
+
disposition: "rejected",
|
|
1466
|
+
reason: "recipientNotFound"
|
|
1467
|
+
};
|
|
1468
|
+
verification = {
|
|
1469
|
+
attempts: [],
|
|
1470
|
+
subject: {
|
|
1471
|
+
id: null,
|
|
1472
|
+
pointer: ""
|
|
1473
|
+
},
|
|
1474
|
+
parsedObject: (object) => {
|
|
1475
|
+
if (object instanceof Activity) {
|
|
1476
|
+
this.activity = object;
|
|
1477
|
+
this.verification.subject.id = object.id == null ? null : new URL(object.id.href);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
constructor(inbox) {
|
|
1482
|
+
this.inbox = inbox;
|
|
1483
|
+
}
|
|
1484
|
+
/** Run an ingress operation and project telemetry; fetch owns completion. */
|
|
1485
|
+
async run(operation, span) {
|
|
1486
|
+
let response;
|
|
1487
|
+
let outcome;
|
|
1488
|
+
let threw = false;
|
|
1489
|
+
let originalError;
|
|
1490
|
+
try {
|
|
1491
|
+
response = await operation();
|
|
1492
|
+
outcome = {
|
|
1493
|
+
type: "response",
|
|
1494
|
+
status: response.status,
|
|
1495
|
+
...this.result
|
|
1496
|
+
};
|
|
1497
|
+
} catch (error) {
|
|
1498
|
+
threw = true;
|
|
1499
|
+
this.hasException = true;
|
|
1500
|
+
originalError = error;
|
|
1501
|
+
outcome = {
|
|
1502
|
+
type: "exception",
|
|
1503
|
+
stage: this.stage,
|
|
1504
|
+
error
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
if (span != null) this.project(span, outcome);
|
|
1508
|
+
if (threw) throw originalError;
|
|
1509
|
+
return response;
|
|
1510
|
+
}
|
|
1511
|
+
/** Complete standalone handler calls that have no enclosing fetch boundary. */
|
|
1512
|
+
async runAndFinish(getContext, handler, operation) {
|
|
1513
|
+
let response;
|
|
1514
|
+
try {
|
|
1515
|
+
response = await this.run(operation);
|
|
1516
|
+
} catch (error) {
|
|
1517
|
+
await this.finish(getContext(), handler, {
|
|
1518
|
+
type: "exception",
|
|
1519
|
+
stage: this.stage,
|
|
1520
|
+
error
|
|
1521
|
+
});
|
|
1522
|
+
throw error;
|
|
1523
|
+
}
|
|
1524
|
+
await this.finish(getContext(), handler, {
|
|
1525
|
+
type: "response",
|
|
1526
|
+
status: response.status,
|
|
1527
|
+
...this.result
|
|
1528
|
+
});
|
|
1529
|
+
return response;
|
|
1530
|
+
}
|
|
1531
|
+
project(span, outcome) {
|
|
1532
|
+
span.setAttribute("activitypub.authentication.status", this.authentication.status);
|
|
1533
|
+
if (this.authentication.status === "rejected") span.setAttribute("activitypub.verification.failure_reason", this.authentication.reason.type);
|
|
1534
|
+
span.setAttribute("activitypub.inbox.disposition", outcome?.type === "exception" || this.hasException ? "exception" : this.result.disposition);
|
|
1535
|
+
}
|
|
1536
|
+
async finish(context, handler, outcome) {
|
|
1537
|
+
if (handler == null) return;
|
|
1538
|
+
const report = {
|
|
1539
|
+
inbox: this.inbox,
|
|
1540
|
+
payload: this.payload,
|
|
1541
|
+
activity: this.activity,
|
|
1542
|
+
attempts: this.verification.attempts,
|
|
1543
|
+
authentication: this.authentication,
|
|
1544
|
+
outcome
|
|
1545
|
+
};
|
|
1546
|
+
try {
|
|
1547
|
+
await handler(context, report);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
try {
|
|
1550
|
+
getLogger([
|
|
1551
|
+
"fedify",
|
|
1552
|
+
"federation",
|
|
1553
|
+
"inbox"
|
|
1554
|
+
]).error("An unexpected error occurred in inbox request finished handler:\n{error}", { error });
|
|
1555
|
+
} catch {}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
//#endregion
|
|
1449
1560
|
//#region src/sig/compound-proof.ts
|
|
1450
1561
|
const textEncoder = new TextEncoder();
|
|
1451
1562
|
/**
|
|
@@ -1880,6 +1991,26 @@ function findUnsupportedCompoundProofShape(json) {
|
|
|
1880
1991
|
return findInJsonMaps(json, (map, path) => Object.hasOwn(map, "proof") && !isJsonMap$1(map.proof) ? childPath(path, "proof") : void 0) ?? null;
|
|
1881
1992
|
}
|
|
1882
1993
|
/**
|
|
1994
|
+
* Finds the first embedded map that carries a direct `proof` but not its own
|
|
1995
|
+
* `@context`. The map-local compound-proof profile verifies each proof-bearing
|
|
1996
|
+
* map as a self-contained document, so the inbox rejects such a map with
|
|
1997
|
+
* `missingContext` whatever its proof. An object embedded with its captured
|
|
1998
|
+
* signed representation, e.g., by {@link signObject}, always keeps its own
|
|
1999
|
+
* `@context`; one rebuilt under its parent's context, e.g., an object parsed
|
|
2000
|
+
* from a received document, never does, and its proof no longer covers what
|
|
2001
|
+
* it has become anyway.
|
|
2002
|
+
*
|
|
2003
|
+
* The input must be a finite, already-materialized JSON tree; see
|
|
2004
|
+
* {@link containsCompoundPortableObject}.
|
|
2005
|
+
*
|
|
2006
|
+
* @returns The RFC 6901 JSON Pointer to the map, or `null` if every embedded
|
|
2007
|
+
* proof-bearing map has its own `@context`.
|
|
2008
|
+
* @internal
|
|
2009
|
+
*/
|
|
2010
|
+
function findEmbeddedProofWithoutContext(json) {
|
|
2011
|
+
return findInJsonMaps(json, (map, path) => path !== "" && Object.hasOwn(map, "proof") && !Object.hasOwn(map, "@context") ? path : void 0) ?? null;
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
1883
2014
|
* Discovers direct literal proof-bearing maps in an immutable JSON snapshot.
|
|
1884
2015
|
*
|
|
1885
2016
|
* Discovery is bounded and atomic. Unsupported proof shapes, non-JSON input,
|
|
@@ -1934,7 +2065,23 @@ async function verifyDiscoveredProofDocuments(discovered, options) {
|
|
|
1934
2065
|
verified: false,
|
|
1935
2066
|
reason: { type: "missingContext" }
|
|
1936
2067
|
});
|
|
1937
|
-
const
|
|
2068
|
+
const inherited = options[verificationObservation];
|
|
2069
|
+
let subjectId = null;
|
|
2070
|
+
if (inherited != null && document.id != null) try {
|
|
2071
|
+
subjectId = parseIri(document.id);
|
|
2072
|
+
} catch {}
|
|
2073
|
+
const key = await observeAttempt({ [verificationObservation]: inherited == null ? void 0 : {
|
|
2074
|
+
...inherited,
|
|
2075
|
+
attempt: void 0,
|
|
2076
|
+
check: void 0,
|
|
2077
|
+
subject: {
|
|
2078
|
+
id: subjectId,
|
|
2079
|
+
pointer: document.path
|
|
2080
|
+
}
|
|
2081
|
+
} }, "objectIntegrity", (observation) => verifyMapLocalProof(document.securedDocument, {
|
|
2082
|
+
...options,
|
|
2083
|
+
[verificationObservation]: inherited == null ? void 0 : observation
|
|
2084
|
+
}), (key) => key != null);
|
|
1938
2085
|
return key == null ? Object.freeze({
|
|
1939
2086
|
...metadata,
|
|
1940
2087
|
verified: false,
|
|
@@ -4573,13 +4720,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
|
4573
4720
|
* @param parameters The parameters for handling the collection.
|
|
4574
4721
|
* @returns A promise that resolves to an HTTP response.
|
|
4575
4722
|
*/
|
|
4576
|
-
async function handleCollection(request, { name: name$
|
|
4577
|
-
const spanName = name$
|
|
4723
|
+
async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
4724
|
+
const spanName = name$2.trim().replace(/\s+/g, "_");
|
|
4578
4725
|
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
4579
4726
|
const tracer = tracerProvider.getTracer(name, version);
|
|
4580
4727
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
4581
4728
|
const metricBase = {
|
|
4582
|
-
kind: getCollectionMetricKind(name$
|
|
4729
|
+
kind: getCollectionMetricKind(name$2),
|
|
4583
4730
|
page: cursor != null,
|
|
4584
4731
|
dispatcher: "built_in"
|
|
4585
4732
|
};
|
|
@@ -4625,7 +4772,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
4625
4772
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
4626
4773
|
return await onNotFound(request);
|
|
4627
4774
|
}
|
|
4628
|
-
const items = filterCollectionItems(page.items, name$
|
|
4775
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
4629
4776
|
itemCount = items.length;
|
|
4630
4777
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4631
4778
|
return items;
|
|
@@ -4644,7 +4791,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
4644
4791
|
collection = new OrderedCollection({
|
|
4645
4792
|
id: baseUri,
|
|
4646
4793
|
attribution,
|
|
4647
|
-
totalItems: totalItemCount ?? null,
|
|
4794
|
+
totalItems: totalItemCount ?? (portable == null ? null : itemsOrResponse.length),
|
|
4648
4795
|
items: itemsOrResponse
|
|
4649
4796
|
});
|
|
4650
4797
|
} else {
|
|
@@ -4667,7 +4814,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
4667
4814
|
} else {
|
|
4668
4815
|
const uri = new URL(portable == null ? baseUri : pageBase);
|
|
4669
4816
|
uri.searchParams.set("cursor", cursor);
|
|
4670
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
4817
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
|
|
4671
4818
|
kind: SpanKind.SERVER,
|
|
4672
4819
|
attributes: {
|
|
4673
4820
|
"activitypub.collection.id": uri.href,
|
|
@@ -4683,7 +4830,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
4683
4830
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
4684
4831
|
return await onNotFound(request);
|
|
4685
4832
|
}
|
|
4686
|
-
const items = filterCollectionItems(page.items, name$
|
|
4833
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
4687
4834
|
itemCount = items.length;
|
|
4688
4835
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4689
4836
|
return {
|
|
@@ -5164,6 +5311,16 @@ async function handleMediaUpload(request, { identifier, context: ctx, mediaUploa
|
|
|
5164
5311
|
* @returns A promise that resolves to an HTTP response.
|
|
5165
5312
|
*/
|
|
5166
5313
|
async function handleInbox(request, options) {
|
|
5314
|
+
if (options.observation == null) {
|
|
5315
|
+
const observation = new InboxObservation({
|
|
5316
|
+
kind: options.portableInbox != null ? "portable" : options.recipient == null ? "shared" : "personal",
|
|
5317
|
+
recipient: options.recipient
|
|
5318
|
+
});
|
|
5319
|
+
return await observation.runAndFinish(() => options.context, options.inboxRequestFinishedHandler, () => handleInbox(request, {
|
|
5320
|
+
...options,
|
|
5321
|
+
observation
|
|
5322
|
+
}));
|
|
5323
|
+
}
|
|
5167
5324
|
return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("activitypub.inbox", {
|
|
5168
5325
|
kind: options.queue == null ? SpanKind.SERVER : SpanKind.PRODUCER,
|
|
5169
5326
|
attributes: { "activitypub.shared_inbox": options.recipient == null }
|
|
@@ -5172,12 +5329,14 @@ async function handleInbox(request, options) {
|
|
|
5172
5329
|
try {
|
|
5173
5330
|
return await handleInboxInternal(request, options, span);
|
|
5174
5331
|
} catch (e) {
|
|
5332
|
+
options.observation.hasException = true;
|
|
5175
5333
|
span.setStatus({
|
|
5176
5334
|
code: SpanStatusCode.ERROR,
|
|
5177
5335
|
message: String(e)
|
|
5178
5336
|
});
|
|
5179
5337
|
throw e;
|
|
5180
5338
|
} finally {
|
|
5339
|
+
options.observation.project(span);
|
|
5181
5340
|
span.end();
|
|
5182
5341
|
}
|
|
5183
5342
|
});
|
|
@@ -5192,6 +5351,8 @@ async function handleInbox(request, options) {
|
|
|
5192
5351
|
*/
|
|
5193
5352
|
async function handleInboxInternal(request, parameters, span) {
|
|
5194
5353
|
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, maxHttpSignatures, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider, portableInbox } = parameters;
|
|
5354
|
+
const observation = parameters.observation;
|
|
5355
|
+
const signatureObservation = { [verificationObservation]: observation.verification };
|
|
5195
5356
|
const logger = getLogger([
|
|
5196
5357
|
"fedify",
|
|
5197
5358
|
"federation",
|
|
@@ -5216,6 +5377,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5216
5377
|
}
|
|
5217
5378
|
}
|
|
5218
5379
|
if (request.bodyUsed) {
|
|
5380
|
+
observation.result = {
|
|
5381
|
+
disposition: "failed",
|
|
5382
|
+
reason: "bodyUnavailable"
|
|
5383
|
+
};
|
|
5219
5384
|
logger.error("Request body has already been read.", { recipient });
|
|
5220
5385
|
span.setStatus({
|
|
5221
5386
|
code: SpanStatusCode.ERROR,
|
|
@@ -5226,6 +5391,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5226
5391
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5227
5392
|
});
|
|
5228
5393
|
} else if (request.body?.locked) {
|
|
5394
|
+
observation.result = {
|
|
5395
|
+
disposition: "failed",
|
|
5396
|
+
reason: "bodyUnavailable"
|
|
5397
|
+
};
|
|
5229
5398
|
logger.error("Request body is locked.", { recipient });
|
|
5230
5399
|
span.setStatus({
|
|
5231
5400
|
code: SpanStatusCode.ERROR,
|
|
@@ -5236,11 +5405,20 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5236
5405
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5237
5406
|
});
|
|
5238
5407
|
}
|
|
5408
|
+
observation.stage = "parse";
|
|
5409
|
+
observation.result = {
|
|
5410
|
+
disposition: "rejected",
|
|
5411
|
+
reason: "invalidJson"
|
|
5412
|
+
};
|
|
5239
5413
|
let json;
|
|
5240
5414
|
try {
|
|
5241
5415
|
json = JSON.parse(await readBoundedText(request.clone(), MAX_BODY_SIZE, request.url));
|
|
5242
5416
|
} catch (error) {
|
|
5243
5417
|
if (error instanceof BodyTooLargeError) {
|
|
5418
|
+
observation.result = {
|
|
5419
|
+
disposition: "rejected",
|
|
5420
|
+
reason: "bodyTooLarge"
|
|
5421
|
+
};
|
|
5244
5422
|
request.body?.cancel(error).catch(() => {});
|
|
5245
5423
|
span.setStatus({
|
|
5246
5424
|
code: SpanStatusCode.ERROR,
|
|
@@ -5270,6 +5448,15 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5270
5448
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5271
5449
|
});
|
|
5272
5450
|
}
|
|
5451
|
+
observation.payload = {
|
|
5452
|
+
status: "parsed",
|
|
5453
|
+
value: structuredClone(json)
|
|
5454
|
+
};
|
|
5455
|
+
observation.result = {
|
|
5456
|
+
disposition: "rejected",
|
|
5457
|
+
reason: "invalidActivity"
|
|
5458
|
+
};
|
|
5459
|
+
observation.stage = "verify";
|
|
5273
5460
|
const keyCache = new KvKeyCache(kv, kvPrefixes.publicKey, {
|
|
5274
5461
|
documentLoader: ctx.documentLoader,
|
|
5275
5462
|
contextLoader: ctx.contextLoader,
|
|
@@ -5299,6 +5486,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5299
5486
|
code: SpanStatusCode.ERROR,
|
|
5300
5487
|
message: `Failed to parse activity:\n${error}`
|
|
5301
5488
|
});
|
|
5489
|
+
observation.result = {
|
|
5490
|
+
disposition: "rejected",
|
|
5491
|
+
reason: "invalidActivity"
|
|
5492
|
+
};
|
|
5302
5493
|
return new Response("Invalid activity.", {
|
|
5303
5494
|
status: 400,
|
|
5304
5495
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5316,11 +5507,25 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5316
5507
|
recipient,
|
|
5317
5508
|
error
|
|
5318
5509
|
});
|
|
5510
|
+
observation.result = {
|
|
5511
|
+
disposition: "rejected",
|
|
5512
|
+
reason: "invalidJsonLd"
|
|
5513
|
+
};
|
|
5319
5514
|
return new Response("Invalid JSON-LD.", {
|
|
5320
5515
|
status: 400,
|
|
5321
5516
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5322
5517
|
});
|
|
5323
5518
|
}
|
|
5519
|
+
observation.verification.attempts.push({
|
|
5520
|
+
mechanism: "linkedData",
|
|
5521
|
+
subject: {
|
|
5522
|
+
id: null,
|
|
5523
|
+
pointer: ""
|
|
5524
|
+
},
|
|
5525
|
+
checks: [],
|
|
5526
|
+
status: "error",
|
|
5527
|
+
error
|
|
5528
|
+
});
|
|
5324
5529
|
if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
|
|
5325
5530
|
if (!skipSignatureVerification) deferredLdSignatureError = error;
|
|
5326
5531
|
logger.debug("Failed to normalize JSON-LD for Linked Data Signatures; deferring to another authentication path only if it verifies:\n{error}", {
|
|
@@ -5332,6 +5537,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5332
5537
|
compactedJsonWithoutSig = detachSignature(compactedJson);
|
|
5333
5538
|
try {
|
|
5334
5539
|
ldSigVerified = await verifyCompactJsonLd(compactedJson, {
|
|
5540
|
+
...signatureObservation,
|
|
5335
5541
|
contextLoader: ctx.contextLoader,
|
|
5336
5542
|
documentLoader: ctx.documentLoader,
|
|
5337
5543
|
keyCache,
|
|
@@ -5345,6 +5551,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5345
5551
|
recipient,
|
|
5346
5552
|
error
|
|
5347
5553
|
});
|
|
5554
|
+
observation.result = {
|
|
5555
|
+
disposition: "rejected",
|
|
5556
|
+
reason: "invalidJsonLd"
|
|
5557
|
+
};
|
|
5348
5558
|
return new Response("Invalid JSON-LD.", {
|
|
5349
5559
|
status: 400,
|
|
5350
5560
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5364,6 +5574,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5364
5574
|
recipient,
|
|
5365
5575
|
error: parseError
|
|
5366
5576
|
});
|
|
5577
|
+
observation.result = {
|
|
5578
|
+
disposition: "rejected",
|
|
5579
|
+
reason: "invalidJsonLd"
|
|
5580
|
+
};
|
|
5367
5581
|
return new Response("Invalid JSON-LD.", {
|
|
5368
5582
|
status: 400,
|
|
5369
5583
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5384,7 +5598,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5384
5598
|
json
|
|
5385
5599
|
});
|
|
5386
5600
|
try {
|
|
5387
|
-
activity = await Activity.fromJsonLd(compactedJsonWithoutSig, {
|
|
5601
|
+
activity = observation.activity = await Activity.fromJsonLd(compactedJsonWithoutSig, {
|
|
5388
5602
|
...ctx,
|
|
5389
5603
|
contextLoader: getNormalizationContextLoader(ctx.contextLoader)
|
|
5390
5604
|
});
|
|
@@ -5396,6 +5610,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5396
5610
|
activityVerified = true;
|
|
5397
5611
|
if (!skipSignatureVerification && isPortableActivity(activity)) try {
|
|
5398
5612
|
proofVerified = await verifyObject(Activity, jsonWithoutSig, {
|
|
5613
|
+
...signatureObservation,
|
|
5399
5614
|
contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
|
|
5400
5615
|
documentLoader: ctx.documentLoader,
|
|
5401
5616
|
keyCache,
|
|
@@ -5416,6 +5631,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5416
5631
|
});
|
|
5417
5632
|
try {
|
|
5418
5633
|
activity = await verifyObject(Activity, jsonWithoutSig, {
|
|
5634
|
+
...signatureObservation,
|
|
5419
5635
|
contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
|
|
5420
5636
|
documentLoader: ctx.documentLoader,
|
|
5421
5637
|
keyCache,
|
|
@@ -5451,6 +5667,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5451
5667
|
code: SpanStatusCode.ERROR,
|
|
5452
5668
|
message: `Failed to parse activity:\n${error}`
|
|
5453
5669
|
});
|
|
5670
|
+
observation.result = {
|
|
5671
|
+
disposition: "rejected",
|
|
5672
|
+
reason: "invalidActivity"
|
|
5673
|
+
};
|
|
5454
5674
|
return new Response("Invalid activity.", {
|
|
5455
5675
|
status: 400,
|
|
5456
5676
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5474,6 +5694,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5474
5694
|
if (activity == null) {
|
|
5475
5695
|
if (!skipSignatureVerification) {
|
|
5476
5696
|
const verification = await verifyRequestDetailed(request, {
|
|
5697
|
+
...signatureObservation,
|
|
5477
5698
|
contextLoader: ctx.contextLoader,
|
|
5478
5699
|
documentLoader: ctx.documentLoader,
|
|
5479
5700
|
timeWindow: signatureTimeWindow,
|
|
@@ -5484,6 +5705,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5484
5705
|
});
|
|
5485
5706
|
if (verification.verified === false) {
|
|
5486
5707
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5708
|
+
observation.authentication = {
|
|
5709
|
+
status: "rejected",
|
|
5710
|
+
reason: { type: "verificationFailed" }
|
|
5711
|
+
};
|
|
5712
|
+
observation.result = {
|
|
5713
|
+
disposition: "rejected",
|
|
5714
|
+
reason: "authentication"
|
|
5715
|
+
};
|
|
5487
5716
|
const reason = verification.reason;
|
|
5488
5717
|
const remoteHost = "keyId" in reason && reason.keyId != null ? getRemoteHost(reason.keyId) : void 0;
|
|
5489
5718
|
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure(reason.type, remoteHost);
|
|
@@ -5498,7 +5727,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5498
5727
|
});
|
|
5499
5728
|
if (unverifiedActivityHandler == null) return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5500
5729
|
try {
|
|
5501
|
-
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
5730
|
+
activity = observation.activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
5502
5731
|
} catch (error) {
|
|
5503
5732
|
logger.error("Failed to parse activity:\n{error}", {
|
|
5504
5733
|
recipient,
|
|
@@ -5514,6 +5743,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5514
5743
|
recipient
|
|
5515
5744
|
});
|
|
5516
5745
|
}
|
|
5746
|
+
observation.result = {
|
|
5747
|
+
disposition: "rejected",
|
|
5748
|
+
reason: "invalidActivity"
|
|
5749
|
+
};
|
|
5517
5750
|
return new Response("Invalid activity.", {
|
|
5518
5751
|
status: 400,
|
|
5519
5752
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5536,6 +5769,11 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5536
5769
|
try {
|
|
5537
5770
|
response = await unverifiedActivityHandler(ctx, activity, reason);
|
|
5538
5771
|
} catch (error) {
|
|
5772
|
+
observation.result = {
|
|
5773
|
+
disposition: "failed",
|
|
5774
|
+
reason: "listenerError",
|
|
5775
|
+
error
|
|
5776
|
+
};
|
|
5539
5777
|
logger.error("An unexpected error occurred in unverified activity handler:\n{error}", {
|
|
5540
5778
|
error,
|
|
5541
5779
|
activity: json,
|
|
@@ -5552,7 +5790,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5552
5790
|
}
|
|
5553
5791
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5554
5792
|
}
|
|
5555
|
-
if (response instanceof Response)
|
|
5793
|
+
if (response instanceof Response) {
|
|
5794
|
+
observation.result = { disposition: "customResponse" };
|
|
5795
|
+
return response;
|
|
5796
|
+
}
|
|
5556
5797
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5557
5798
|
} else {
|
|
5558
5799
|
if (inboxChallengePolicy?.enabled && inboxChallengePolicy.requestNonce) pendingNonceLabel = verification.signatureLabel;
|
|
@@ -5562,7 +5803,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5562
5803
|
httpSigKey = verification.key;
|
|
5563
5804
|
}
|
|
5564
5805
|
try {
|
|
5565
|
-
activity = await Activity.fromJsonLd(jsonWithoutSig, {
|
|
5806
|
+
activity = observation.activity = await Activity.fromJsonLd(jsonWithoutSig, {
|
|
5566
5807
|
...ctx,
|
|
5567
5808
|
contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader)
|
|
5568
5809
|
});
|
|
@@ -5571,6 +5812,12 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5571
5812
|
return await respondInvalidActivity(error);
|
|
5572
5813
|
}
|
|
5573
5814
|
}
|
|
5815
|
+
observation.activity = activity;
|
|
5816
|
+
observation.stage = "policy";
|
|
5817
|
+
observation.result = {
|
|
5818
|
+
disposition: "rejected",
|
|
5819
|
+
reason: "authentication"
|
|
5820
|
+
};
|
|
5574
5821
|
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
5575
5822
|
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
|
|
5576
5823
|
span.addEvent("activitypub.activity.received", {
|
|
@@ -5582,6 +5829,13 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5582
5829
|
});
|
|
5583
5830
|
if (!skipSignatureVerification && !proofVerified && (isPortableActivity(activity) || isPortableActorKey(httpSigKey))) {
|
|
5584
5831
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5832
|
+
observation.authentication = {
|
|
5833
|
+
status: "rejected",
|
|
5834
|
+
reason: {
|
|
5835
|
+
type: "proofPolicy",
|
|
5836
|
+
policy: "portableActor"
|
|
5837
|
+
}
|
|
5838
|
+
};
|
|
5585
5839
|
logger.error("The activity {activityId} of the portable actor {actorId} is not authenticated by a valid Object Integrity Proof.", {
|
|
5586
5840
|
activity: json,
|
|
5587
5841
|
recipient,
|
|
@@ -5599,20 +5853,36 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5599
5853
|
});
|
|
5600
5854
|
}
|
|
5601
5855
|
if (!skipSignatureVerification && activity.id != null && isPortableId(activity.id)) {
|
|
5602
|
-
const rejection = await
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5856
|
+
const rejection = await observeAttempt(signatureObservation, "objectIntegrity", async (verification) => {
|
|
5857
|
+
const rejection = await verifyPortableActivityId(activity.id, jsonWithoutSig, {
|
|
5858
|
+
[verificationObservation]: verification,
|
|
5859
|
+
contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
|
|
5860
|
+
documentLoader: ctx.documentLoader,
|
|
5861
|
+
keyCache,
|
|
5862
|
+
meterProvider,
|
|
5863
|
+
tracerProvider
|
|
5864
|
+
});
|
|
5865
|
+
if (rejection != null) verification.attempt.reason = {
|
|
5866
|
+
type: "proofPolicy",
|
|
5867
|
+
reason: rejection
|
|
5868
|
+
};
|
|
5869
|
+
return rejection;
|
|
5870
|
+
}, (value) => value == null);
|
|
5609
5871
|
if (rejection != null) {
|
|
5610
5872
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5873
|
+
observation.authentication = {
|
|
5874
|
+
status: "rejected",
|
|
5875
|
+
reason: {
|
|
5876
|
+
type: "proofPolicy",
|
|
5877
|
+
policy: "portableActivity",
|
|
5878
|
+
detail: rejection
|
|
5879
|
+
}
|
|
5880
|
+
};
|
|
5611
5881
|
logger.error("The portable activity {activityId} is not authenticated by an Object Integrity Proof of its own DID: {reason}", {
|
|
5612
5882
|
activity: json,
|
|
5613
5883
|
recipient,
|
|
5614
5884
|
activityId: activity.id.href,
|
|
5615
|
-
reason: rejection
|
|
5885
|
+
reason: rejection.type
|
|
5616
5886
|
});
|
|
5617
5887
|
span.setStatus({
|
|
5618
5888
|
code: SpanStatusCode.ERROR,
|
|
@@ -5626,6 +5896,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5626
5896
|
}
|
|
5627
5897
|
if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
|
|
5628
5898
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5899
|
+
observation.authentication = {
|
|
5900
|
+
status: "rejected",
|
|
5901
|
+
reason: {
|
|
5902
|
+
type: "actorKeyMismatch",
|
|
5903
|
+
key: snapshotKey(httpSigKey),
|
|
5904
|
+
actorIds: activity.actorIds.map((id) => new URL(id.href))
|
|
5905
|
+
}
|
|
5906
|
+
};
|
|
5629
5907
|
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("actorKeyMismatch", httpSigKey.id == null ? void 0 : getRemoteHost(httpSigKey.id));
|
|
5630
5908
|
logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
|
|
5631
5909
|
activity: json,
|
|
@@ -5645,13 +5923,35 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5645
5923
|
if (!skipSignatureVerification) {
|
|
5646
5924
|
const compoundJson = Array.isArray(json) ? json : jsonWithoutSig;
|
|
5647
5925
|
if (inspectCompoundPortableObjectApplicability(compoundJson, INBOX_COMPOUND_PROOF_LIMITS) !== "absent") {
|
|
5648
|
-
const compoundProof = await
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5926
|
+
const compoundProof = await observeAttempt(signatureObservation, "objectIntegrity", async (verification) => {
|
|
5927
|
+
const result = await verifyCompoundPortableObjectProofs(compoundJson, INBOX_COMPOUND_PROOF_LIMITS, {
|
|
5928
|
+
[verificationObservation]: verification,
|
|
5929
|
+
documentLoader: ctx.documentLoader,
|
|
5930
|
+
keyCache,
|
|
5931
|
+
meterProvider,
|
|
5932
|
+
tracerProvider
|
|
5933
|
+
});
|
|
5934
|
+
if (result.status === "unsupported") verification.attempt.reason = {
|
|
5935
|
+
type: "proofPolicy",
|
|
5936
|
+
reason: result.reason
|
|
5937
|
+
};
|
|
5938
|
+
else if (!result.verified) {
|
|
5939
|
+
const failed = result.portableObjects.find((object) => !object.verified);
|
|
5940
|
+
verification.attempt.reason = {
|
|
5941
|
+
type: "proofPolicy",
|
|
5942
|
+
reason: failed?.verified === false ? failed.reason : { type: "invalidProof" }
|
|
5943
|
+
};
|
|
5944
|
+
}
|
|
5945
|
+
return result;
|
|
5946
|
+
}, (result) => result.status === "ok" && result.verified);
|
|
5654
5947
|
if (compoundProof.status !== "ok" || !compoundProof.verified) {
|
|
5948
|
+
observation.authentication = {
|
|
5949
|
+
status: "rejected",
|
|
5950
|
+
reason: {
|
|
5951
|
+
type: "proofPolicy",
|
|
5952
|
+
policy: "compound"
|
|
5953
|
+
}
|
|
5954
|
+
};
|
|
5655
5955
|
logger.error("Failed to verify compound portable Object Integrity Proofs.", {
|
|
5656
5956
|
recipient,
|
|
5657
5957
|
activity: json,
|
|
@@ -5670,11 +5970,20 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5670
5970
|
}
|
|
5671
5971
|
if (pendingNonceLabel != null) {
|
|
5672
5972
|
if (!await verifySignatureNonce(request, kv, kvPrefixes.acceptSignatureNonce, pendingNonceLabel)) {
|
|
5973
|
+
observation.authentication = {
|
|
5974
|
+
status: "rejected",
|
|
5975
|
+
reason: { type: "invalidNonce" }
|
|
5976
|
+
};
|
|
5673
5977
|
getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("invalidNonce", httpSigKey?.id == null ? void 0 : getRemoteHost(httpSigKey.id));
|
|
5674
5978
|
logger.error("Signature nonce verification failed (missing, expired, or replayed).", { recipient });
|
|
5675
5979
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5676
5980
|
}
|
|
5677
5981
|
}
|
|
5982
|
+
observation.authentication = skipSignatureVerification ? { status: "skipped" } : {
|
|
5983
|
+
status: "verified",
|
|
5984
|
+
attempts: observation.verification.attempts.filter((attempt) => attempt.status === "verified")
|
|
5985
|
+
};
|
|
5986
|
+
observation.stage = "dispatch";
|
|
5678
5987
|
const routeResult = await routeActivity({
|
|
5679
5988
|
context: ctx,
|
|
5680
5989
|
json,
|
|
@@ -5686,7 +5995,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5686
5995
|
inboxListeners,
|
|
5687
5996
|
inboxContextFactory,
|
|
5688
5997
|
listenerInboxContextFactory: ldSigVerified ? inboxContextFactory[rawInboxContextFactorySymbol] : void 0,
|
|
5689
|
-
inboxErrorHandler,
|
|
5998
|
+
inboxErrorHandler: async (context, error) => {
|
|
5999
|
+
observation.result = {
|
|
6000
|
+
disposition: "failed",
|
|
6001
|
+
reason: "listenerError",
|
|
6002
|
+
error
|
|
6003
|
+
};
|
|
6004
|
+
await inboxErrorHandler?.(context, error);
|
|
6005
|
+
},
|
|
5690
6006
|
kv,
|
|
5691
6007
|
kvPrefixes,
|
|
5692
6008
|
queue,
|
|
@@ -5722,30 +6038,44 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5722
6038
|
error
|
|
5723
6039
|
});
|
|
5724
6040
|
}
|
|
5725
|
-
if (routeResult === "alreadyProcessed")
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
})
|
|
5741
|
-
|
|
6041
|
+
if (routeResult === "alreadyProcessed") {
|
|
6042
|
+
observation.result = { disposition: "duplicate" };
|
|
6043
|
+
return new Response(`Activity <${activity.id}> has already been processed.`, {
|
|
6044
|
+
status: 202,
|
|
6045
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6046
|
+
});
|
|
6047
|
+
} else if (routeResult === "missingActor") {
|
|
6048
|
+
observation.result = {
|
|
6049
|
+
disposition: "rejected",
|
|
6050
|
+
reason: "missingActor"
|
|
6051
|
+
};
|
|
6052
|
+
return new Response("Missing actor.", {
|
|
6053
|
+
status: 400,
|
|
6054
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6055
|
+
});
|
|
6056
|
+
} else if (routeResult === "enqueued") {
|
|
6057
|
+
observation.result = { disposition: "enqueued" };
|
|
6058
|
+
return new Response("Activity is enqueued.", {
|
|
6059
|
+
status: 202,
|
|
6060
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6061
|
+
});
|
|
6062
|
+
} else if (routeResult === "unsupportedActivity") {
|
|
6063
|
+
observation.result = { disposition: "unhandled" };
|
|
6064
|
+
return new Response("", {
|
|
6065
|
+
status: 202,
|
|
6066
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6067
|
+
});
|
|
6068
|
+
} else if (routeResult === "error") return new Response("Internal server error.", {
|
|
5742
6069
|
status: 500,
|
|
5743
6070
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5744
6071
|
});
|
|
5745
|
-
else
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
6072
|
+
else {
|
|
6073
|
+
observation.result = { disposition: "processed" };
|
|
6074
|
+
return new Response("", {
|
|
6075
|
+
status: 202,
|
|
6076
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6077
|
+
});
|
|
6078
|
+
}
|
|
5749
6079
|
}
|
|
5750
6080
|
const PORTABLE_INBOX_FORWARDABLE_RESULTS = /* @__PURE__ */ new Set([
|
|
5751
6081
|
"success",
|
|
@@ -5874,8 +6204,8 @@ var CustomCollectionHandler = class {
|
|
|
5874
6204
|
* @param CollectionPage The CollectionPage constructor.
|
|
5875
6205
|
* @param filterPredicate Optional filter predicate for items.
|
|
5876
6206
|
*/
|
|
5877
|
-
constructor(name$
|
|
5878
|
-
this.name = name$
|
|
6207
|
+
constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
|
|
6208
|
+
this.name = name$1;
|
|
5879
6209
|
this.values = values;
|
|
5880
6210
|
this.context = context;
|
|
5881
6211
|
this.callbacks = callbacks;
|
|
@@ -6411,19 +6741,19 @@ function isPortableActivity(activity) {
|
|
|
6411
6741
|
*/
|
|
6412
6742
|
async function verifyPortableActivityId(activityId, jsonLd, options) {
|
|
6413
6743
|
const expectedId = getCanonicalPortableId(activityId);
|
|
6414
|
-
if (expectedId == null) return
|
|
6744
|
+
if (expectedId == null) return { type: "invalidObjectId" };
|
|
6415
6745
|
let verification;
|
|
6416
6746
|
try {
|
|
6417
6747
|
verification = await verifyPortableObjectProofWithRoot(jsonLd, options);
|
|
6418
6748
|
} catch (error) {
|
|
6419
|
-
if (error instanceof InvalidPortableObjectIdError) return
|
|
6749
|
+
if (error instanceof InvalidPortableObjectIdError) return { type: "invalidObjectId" };
|
|
6420
6750
|
if (!isPermanentActivityParseError(error)) throw error;
|
|
6421
|
-
return "
|
|
6751
|
+
return { type: "invalidDocument" };
|
|
6422
6752
|
}
|
|
6423
6753
|
const { result, root } = verification;
|
|
6424
|
-
if (!result.verified) return result.reason
|
|
6754
|
+
if (!result.verified) return result.reason;
|
|
6425
6755
|
const rootId = root?.["@id"];
|
|
6426
|
-
if (typeof rootId !== "string" || getCanonicalPortableId(parseIri(rootId)) !== expectedId) return
|
|
6756
|
+
if (typeof rootId !== "string" || getCanonicalPortableId(parseIri(rootId)) !== expectedId) return { type: "subjectMismatch" };
|
|
6427
6757
|
return null;
|
|
6428
6758
|
}
|
|
6429
6759
|
//#endregion
|
|
@@ -6884,7 +7214,10 @@ function selectPortableActivityKey(activity, activityId, keys) {
|
|
|
6884
7214
|
/**
|
|
6885
7215
|
* Rejects an outgoing activity whose proofs Fedify's own inbox would reject
|
|
6886
7216
|
* as unsupported: a document with portable objects in which some map carries
|
|
6887
|
-
* a proof set or another value that is not a single proof map
|
|
7217
|
+
* a proof set or another value that is not a single proof map, or in which
|
|
7218
|
+
* an embedded map carries a proof but not its own `@context`, e.g., a signed
|
|
7219
|
+
* object parsed from a received document and rebuilt under the activity's
|
|
7220
|
+
* context.
|
|
6888
7221
|
*
|
|
6889
7222
|
* @param jsonLd The compact JSON-LD document about to be delivered.
|
|
6890
7223
|
* @param activityId The activity ID, for the error message.
|
|
@@ -6894,8 +7227,9 @@ function selectPortableActivityKey(activity, activityId, keys) {
|
|
|
6894
7227
|
function assertSupportedCompoundProofShape(jsonLd, activityId) {
|
|
6895
7228
|
if (!containsCompoundPortableObject(jsonLd)) return;
|
|
6896
7229
|
const path = findUnsupportedCompoundProofShape(jsonLd);
|
|
6897
|
-
if (path
|
|
6898
|
-
|
|
7230
|
+
if (path != null) throw new TypeError(`Cannot send the activity ${activityId}: it embeds portable objects, but its proof at the JSON Pointer ${JSON.stringify(path)} is not a single proof map. The map-local compound-proof profile accepts exactly one direct proof per map, so Fedify inboxes would reject the activity. Sign each object with exactly one key.`);
|
|
7231
|
+
const embedded = findEmbeddedProofWithoutContext(jsonLd);
|
|
7232
|
+
if (embedded != null) throw new TypeError(`Cannot send the activity ${activityId}: it embeds portable objects, but the map at the JSON Pointer ${JSON.stringify(embedded)} carries a proof without its own @context. The map-local compound-proof profile verifies each proof-bearing map on its own, so Fedify inboxes would reject the activity. This happens when a signed object parsed from a received document, e.g., a Follow in an inbox listener, is embedded, as it is rebuilt under the activity's context and its proof no longer covers it. Refer to such an object by its ID instead, e.g., new Accept({ object: follow.id }), or embed an object that you signed with signObject().`);
|
|
6899
7233
|
}
|
|
6900
7234
|
//#endregion
|
|
6901
7235
|
//#region src/federation/retry.ts
|
|
@@ -9312,7 +9646,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9312
9646
|
return await withContext({
|
|
9313
9647
|
traceId: spanCtx.traceId,
|
|
9314
9648
|
spanId: spanCtx.spanId
|
|
9315
|
-
}, async () => {
|
|
9649
|
+
}, () => this.#observeInboxFetch(metricState, async () => {
|
|
9316
9650
|
const logger = getLogger([
|
|
9317
9651
|
"fedify",
|
|
9318
9652
|
"federation",
|
|
@@ -9327,8 +9661,14 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9327
9661
|
tracer,
|
|
9328
9662
|
metricState
|
|
9329
9663
|
});
|
|
9664
|
+
if (metricState.inboxCompletion != null) metricState.inboxCompletion.observation.stage = "respond";
|
|
9330
9665
|
if (metricState.endpoint !== "hashlink_media" && acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
|
|
9331
9666
|
} catch (error) {
|
|
9667
|
+
metricState.inboxCompletion?.observation.project(span, {
|
|
9668
|
+
type: "exception",
|
|
9669
|
+
stage: metricState.inboxCompletion.observation.stage,
|
|
9670
|
+
error
|
|
9671
|
+
});
|
|
9332
9672
|
this.metrics.recordHttpServerRequest(request.method, metricState.endpoint ?? "error", getDurationMs(metricStart), { routeTemplate: metricState.routeTemplate });
|
|
9333
9673
|
span.setStatus({
|
|
9334
9674
|
code: SpanStatusCode.ERROR,
|
|
@@ -9367,7 +9707,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9367
9707
|
else if (response.status >= 400) logger.warn(logTpl, values);
|
|
9368
9708
|
else logger.info(logTpl, values);
|
|
9369
9709
|
return response;
|
|
9370
|
-
});
|
|
9710
|
+
}));
|
|
9371
9711
|
});
|
|
9372
9712
|
});
|
|
9373
9713
|
}
|
|
@@ -9409,8 +9749,17 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9409
9749
|
metricState.routeTemplate = route.template;
|
|
9410
9750
|
metricState.endpoint = getEndpointCategory(route.name);
|
|
9411
9751
|
span.updateName(`${request.method} ${route.template}`);
|
|
9412
|
-
let context = this.#createContext(request, contextData);
|
|
9413
9752
|
const routeName = route.name.replace(/:.*$/, "");
|
|
9753
|
+
let context;
|
|
9754
|
+
try {
|
|
9755
|
+
context = this.#createContext(request, contextData);
|
|
9756
|
+
} catch (error) {
|
|
9757
|
+
if (request.method !== "POST" || routeName !== "inbox" && routeName !== "sharedInbox") throw error;
|
|
9758
|
+
return await this.#reportInboxPreparationError(request, contextData, {
|
|
9759
|
+
kind: routeName === "inbox" ? "personal" : "shared",
|
|
9760
|
+
recipient: route.values.identifier ?? null
|
|
9761
|
+
}, error, span, metricState);
|
|
9762
|
+
}
|
|
9414
9763
|
switch (routeName) {
|
|
9415
9764
|
case "webfinger": return await handleWebFinger(request, {
|
|
9416
9765
|
context,
|
|
@@ -9469,6 +9818,28 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9469
9818
|
});
|
|
9470
9819
|
return response;
|
|
9471
9820
|
}
|
|
9821
|
+
if (request.method === "POST" && (routeName === "inbox" || routeName === "sharedInbox")) {
|
|
9822
|
+
const recipient = route.values.identifier ?? null;
|
|
9823
|
+
const observation = new InboxObservation({
|
|
9824
|
+
kind: routeName === "inbox" ? "personal" : "shared",
|
|
9825
|
+
recipient
|
|
9826
|
+
});
|
|
9827
|
+
this.#setInboxCompletion(metricState, observation, () => context);
|
|
9828
|
+
return await observation.run(async () => {
|
|
9829
|
+
if (routeName === "inbox") context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier: recipient }) });
|
|
9830
|
+
else if (this.sharedInboxKeyDispatcher != null) {
|
|
9831
|
+
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
9832
|
+
if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
9833
|
+
}
|
|
9834
|
+
return await this.#handleInbox(request, {
|
|
9835
|
+
recipient,
|
|
9836
|
+
context,
|
|
9837
|
+
contextData,
|
|
9838
|
+
onNotFound,
|
|
9839
|
+
observation
|
|
9840
|
+
});
|
|
9841
|
+
}, span);
|
|
9842
|
+
}
|
|
9472
9843
|
switch (routeName) {
|
|
9473
9844
|
case "actor":
|
|
9474
9845
|
case "actorAlias": {
|
|
@@ -9544,9 +9915,9 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9544
9915
|
onUnauthorized,
|
|
9545
9916
|
onNotFound
|
|
9546
9917
|
});
|
|
9547
|
-
|
|
9918
|
+
throw new Error("Unreachable inbox delivery.");
|
|
9548
9919
|
case "sharedInbox":
|
|
9549
|
-
if (
|
|
9920
|
+
if (this.sharedInboxKeyDispatcher != null) {
|
|
9550
9921
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
9551
9922
|
if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
9552
9923
|
}
|
|
@@ -9839,7 +10210,49 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9839
10210
|
}
|
|
9840
10211
|
return await onNotFound(request);
|
|
9841
10212
|
}
|
|
9842
|
-
|
|
10213
|
+
#setInboxCompletion(metricState, observation, getContext) {
|
|
10214
|
+
const handler = this.inboxRequestFinishedHandler;
|
|
10215
|
+
metricState.inboxCompletion = {
|
|
10216
|
+
observation,
|
|
10217
|
+
finish: (outcome) => observation.finish(getContext(), handler, outcome)
|
|
10218
|
+
};
|
|
10219
|
+
}
|
|
10220
|
+
async #observeInboxFetch(metricState, operation) {
|
|
10221
|
+
let response;
|
|
10222
|
+
try {
|
|
10223
|
+
response = await operation();
|
|
10224
|
+
} catch (error) {
|
|
10225
|
+
const completion = metricState.inboxCompletion;
|
|
10226
|
+
if (completion != null) await completion.finish({
|
|
10227
|
+
type: "exception",
|
|
10228
|
+
stage: completion.observation.stage,
|
|
10229
|
+
error
|
|
10230
|
+
});
|
|
10231
|
+
throw error;
|
|
10232
|
+
}
|
|
10233
|
+
const completion = metricState.inboxCompletion;
|
|
10234
|
+
if (completion != null) await completion.finish({
|
|
10235
|
+
type: "response",
|
|
10236
|
+
status: response.status,
|
|
10237
|
+
...completion.observation.result
|
|
10238
|
+
});
|
|
10239
|
+
return response;
|
|
10240
|
+
}
|
|
10241
|
+
async #reportInboxPreparationError(request, contextData, inbox, error, span, metricState) {
|
|
10242
|
+
const unavailableLoader = () => Promise.reject(error);
|
|
10243
|
+
const context = new RequestContextImpl({
|
|
10244
|
+
url: new URL(request.url),
|
|
10245
|
+
request,
|
|
10246
|
+
federation: this,
|
|
10247
|
+
data: contextData,
|
|
10248
|
+
documentLoader: unavailableLoader,
|
|
10249
|
+
contextLoader: unavailableLoader
|
|
10250
|
+
});
|
|
10251
|
+
const observation = new InboxObservation(inbox);
|
|
10252
|
+
this.#setInboxCompletion(metricState, observation, () => context);
|
|
10253
|
+
return await observation.run(() => Promise.reject(error), span);
|
|
10254
|
+
}
|
|
10255
|
+
async #handleInbox(request, { recipient, context, contextData, onNotFound, portableInbox, observation }) {
|
|
9843
10256
|
if (!this.manuallyStartQueue) this._startQueueInternal(contextData);
|
|
9844
10257
|
const inboxContextFactory = context.toInboxContext.bind(context);
|
|
9845
10258
|
inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
|
|
@@ -9863,7 +10276,8 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9863
10276
|
meterProvider: this.meterProvider,
|
|
9864
10277
|
tracerProvider: this.tracerProvider,
|
|
9865
10278
|
idempotencyStrategy: this.idempotencyStrategy,
|
|
9866
|
-
portableInbox
|
|
10279
|
+
portableInbox,
|
|
10280
|
+
observation
|
|
9867
10281
|
});
|
|
9868
10282
|
}
|
|
9869
10283
|
async #fetchPortableInbox(request, portable, { onNotFound, contextData, span, metricState }) {
|
|
@@ -9892,63 +10306,79 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9892
10306
|
metricState.endpoint = "inbox";
|
|
9893
10307
|
span.updateName(`${request.method} ${metricState.routeTemplate}`);
|
|
9894
10308
|
const identifier = route.values.identifier;
|
|
9895
|
-
let context
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
10309
|
+
let context;
|
|
10310
|
+
try {
|
|
10311
|
+
context = this.#createContext(request, contextData);
|
|
10312
|
+
} catch (error) {
|
|
10313
|
+
return await this.#reportInboxPreparationError(request, contextData, {
|
|
10314
|
+
kind: "portable",
|
|
10315
|
+
recipient: identifier
|
|
10316
|
+
}, error, span, metricState);
|
|
10317
|
+
}
|
|
10318
|
+
const observation = new InboxObservation({
|
|
10319
|
+
kind: "portable",
|
|
10320
|
+
recipient: identifier
|
|
10321
|
+
});
|
|
10322
|
+
this.#setInboxCompletion(metricState, observation, () => context);
|
|
10323
|
+
return await observation.run(async () => {
|
|
10324
|
+
const actor = this.actorCallbacks?.dispatcher == null ? null : await this.actorCallbacks.dispatcher(context, identifier);
|
|
10325
|
+
const localOrigin = context.canonicalOrigin;
|
|
10326
|
+
const resolution = resolvePortableInboxRecipient(actor == null || actor instanceof Tombstone ? null : actor, {
|
|
10327
|
+
authority: portable.portableRequest.authority,
|
|
10328
|
+
canonicalInboxId: portable.canonicalId,
|
|
10329
|
+
localOrigin
|
|
10330
|
+
});
|
|
10331
|
+
if (resolution.status === "rejected") {
|
|
10332
|
+
logger.debug("Not accepting a delivery to the portable inbox {inbox} on behalf of the actor {identifier}: {reason}.", {
|
|
10333
|
+
inbox: portable.canonicalId,
|
|
10334
|
+
identifier,
|
|
10335
|
+
reason: resolution.reason
|
|
10336
|
+
});
|
|
10337
|
+
return await onNotFound(request);
|
|
10338
|
+
}
|
|
10339
|
+
const { recipient } = resolution;
|
|
10340
|
+
if (this.actorCallbacks?.keyPairsDispatcher != null) context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier }) });
|
|
10341
|
+
const excludedOrigins = [localOrigin, new URL(request.url).origin];
|
|
10342
|
+
return await this.#handleInbox(request, {
|
|
10343
|
+
recipient: identifier,
|
|
10344
|
+
observation,
|
|
10345
|
+
context,
|
|
10346
|
+
contextData,
|
|
10347
|
+
onNotFound,
|
|
10348
|
+
portableInbox: {
|
|
10349
|
+
recipient,
|
|
10350
|
+
forward: async (activity, activityId, activityType, signatures) => {
|
|
10351
|
+
if (this.portableInboxForwarding.maxTargets < 1) return;
|
|
10352
|
+
if (this.kv.cas == null) {
|
|
10353
|
+
if (!this.#portableInboxForwardingWarned) {
|
|
10354
|
+
this.#portableInboxForwardingWarned = true;
|
|
10355
|
+
logger.warn("Activities delivered to FEP-ef61 portable inboxes are not forwarded to the other gateways, as the key–value store does not support compare-and-swap (KvStore.cas()), which is needed to forward each activity at most once.");
|
|
10356
|
+
}
|
|
10357
|
+
return;
|
|
9927
10358
|
}
|
|
9928
|
-
|
|
10359
|
+
await forwardPortableInboxActivity({
|
|
10360
|
+
recipient,
|
|
10361
|
+
activity,
|
|
10362
|
+
activityId,
|
|
10363
|
+
activityType,
|
|
10364
|
+
excludedOrigins,
|
|
10365
|
+
baseUrl: context.origin,
|
|
10366
|
+
kv: this.kv,
|
|
10367
|
+
kvPrefix: this.kvPrefixes.portableInboxForwarding,
|
|
10368
|
+
outboxQueue: this.outboxQueue,
|
|
10369
|
+
startQueue: this.manuallyStartQueue ? void 0 : () => this._startQueueInternal(contextData),
|
|
10370
|
+
allowPrivateAddress: this.allowPrivateAddress,
|
|
10371
|
+
getKeys: () => this.#getPortableGatewayKeyPairs(context, identifier, recipient),
|
|
10372
|
+
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock, { specTtl: this.httpMessageSignaturesSpecTtl }),
|
|
10373
|
+
signatures,
|
|
10374
|
+
options: this.portableInboxForwarding,
|
|
10375
|
+
meterProvider: this.meterProvider,
|
|
10376
|
+
tracerProvider: this.tracerProvider
|
|
10377
|
+
});
|
|
9929
10378
|
}
|
|
9930
|
-
await forwardPortableInboxActivity({
|
|
9931
|
-
recipient,
|
|
9932
|
-
activity,
|
|
9933
|
-
activityId,
|
|
9934
|
-
activityType,
|
|
9935
|
-
excludedOrigins,
|
|
9936
|
-
baseUrl: context.origin,
|
|
9937
|
-
kv: this.kv,
|
|
9938
|
-
kvPrefix: this.kvPrefixes.portableInboxForwarding,
|
|
9939
|
-
outboxQueue: this.outboxQueue,
|
|
9940
|
-
startQueue: this.manuallyStartQueue ? void 0 : () => this._startQueueInternal(contextData),
|
|
9941
|
-
allowPrivateAddress: this.allowPrivateAddress,
|
|
9942
|
-
getKeys: () => this.#getPortableGatewayKeyPairs(context, identifier, recipient),
|
|
9943
|
-
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock, { specTtl: this.httpMessageSignaturesSpecTtl }),
|
|
9944
|
-
signatures,
|
|
9945
|
-
options: this.portableInboxForwarding,
|
|
9946
|
-
meterProvider: this.meterProvider,
|
|
9947
|
-
tracerProvider: this.tracerProvider
|
|
9948
|
-
});
|
|
9949
10379
|
}
|
|
9950
|
-
}
|
|
9951
|
-
});
|
|
10380
|
+
});
|
|
10381
|
+
}, span);
|
|
9952
10382
|
}
|
|
9953
10383
|
/**
|
|
9954
10384
|
* Gets this server's gateway key pairs for the portable actor that owns
|