@fedify/fedify 2.4.0-dev.2256 → 2.4.0-dev.2259
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-CBNa1Uw2.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-CMPv4UK0.mjs} +20 -3
- 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-C8XaXmPB.mjs} +1 -1
- package/dist/{docloader-CIP_SG39.mjs → docloader-CpdFLwAg.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 +5 -5
- 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 +4 -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-D6giSGUT.cjs} +524 -118
- package/dist/{http-DkqWu-sm.mjs → http-BqSbUEiQ.mjs} +141 -98
- package/dist/{http-1fZ83jj7.cjs → http-Cki-npuA.cjs} +447 -110
- package/dist/{http-BGb3QioZ.js → http-OMtKlbx1.js} +430 -111
- 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/{key-CEsu7oaD.mjs → key-DecAtABm.mjs} +30 -3
- package/dist/{kv-cache-CJS4mImS.mjs → kv-cache-CWWlMv-s.mjs} +1 -1
- package/dist/{kv-cache-BEWuZSB4.cjs → kv-cache-D73yVcE5.cjs} +1 -1
- package/dist/{kv-cache-CGhEgt98.js → kv-cache-J0v3UbUn.js} +1 -1
- package/dist/{ld-Cf4CPWN_.mjs → ld-UsN-TIPh.mjs} +40 -5
- package/dist/{metrics-CocsZP0E.mjs → metrics-DWCn4VXX.mjs} +2 -1
- package/dist/{middleware-BfYke3YQ.js → middleware-BqzfIlUO.js} +524 -118
- package/dist/{middleware-Dxn0E9ES.mjs → middleware-CkjwiEBd.mjs} +512 -127
- package/dist/{middleware-Cne_jMZo.mjs → middleware-DHn2q0L1.mjs} +1 -1
- 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-CEBn81LD.mjs} +3 -3
- package/dist/{portable-collection-r-mHg1PV.cjs → portable-collection-DqTUBKY7.cjs} +1 -1
- package/dist/{portable-collection-BuBV6xTo.js → portable-collection-ZkUNjtb7.js} +1 -1
- package/dist/{portable-collection-qskqXJrH.mjs → portable-collection-qFSYw4TG.mjs} +2 -2
- package/dist/{portable-key-woGH_1nn.mjs → portable-key-LlMkP-vz.mjs} +1 -1
- package/dist/{proof-8WiH_-Gq.mjs → proof-BHRf_jMG.mjs} +133 -16
- package/dist/{send-JKzZBgPU.mjs → send-DjG8DDU_.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-BRFH_dte.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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
3
|
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-Cki-npuA.cjs");
|
|
5
5
|
const require_types = require("./types-poIao40f.cjs");
|
|
6
|
-
const require_portable_collection = require("./portable-collection-
|
|
7
|
-
const require_kv_cache = require("./kv-cache-
|
|
6
|
+
const require_portable_collection = require("./portable-collection-DqTUBKY7.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-D73yVcE5.cjs");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
9
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
10
10
|
let _fedify_vocab = require("@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;
|
|
@@ -1447,6 +1453,111 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
|
1447
1453
|
return `collectionId="${collectionId}", url="${url}", digest="${(0, byte_encodings_hex.encodeHex)(await digest(actorIds))}"`;
|
|
1448
1454
|
}
|
|
1449
1455
|
//#endregion
|
|
1456
|
+
//#region src/federation/inbox-observation.ts
|
|
1457
|
+
/** Internal state owned by the ingress boundary, never serialized into queues. */
|
|
1458
|
+
var InboxObservation = class {
|
|
1459
|
+
inbox;
|
|
1460
|
+
payload = { status: "unavailable" };
|
|
1461
|
+
activity = null;
|
|
1462
|
+
authentication = { status: "notDetermined" };
|
|
1463
|
+
hasException = false;
|
|
1464
|
+
stage = "prepare";
|
|
1465
|
+
result = {
|
|
1466
|
+
disposition: "rejected",
|
|
1467
|
+
reason: "recipientNotFound"
|
|
1468
|
+
};
|
|
1469
|
+
verification = {
|
|
1470
|
+
attempts: [],
|
|
1471
|
+
subject: {
|
|
1472
|
+
id: null,
|
|
1473
|
+
pointer: ""
|
|
1474
|
+
},
|
|
1475
|
+
parsedObject: (object) => {
|
|
1476
|
+
if (object instanceof _fedify_vocab.Activity) {
|
|
1477
|
+
this.activity = object;
|
|
1478
|
+
this.verification.subject.id = object.id == null ? null : new URL(object.id.href);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
constructor(inbox) {
|
|
1483
|
+
this.inbox = inbox;
|
|
1484
|
+
}
|
|
1485
|
+
/** Run an ingress operation and project telemetry; fetch owns completion. */
|
|
1486
|
+
async run(operation, span) {
|
|
1487
|
+
let response;
|
|
1488
|
+
let outcome;
|
|
1489
|
+
let threw = false;
|
|
1490
|
+
let originalError;
|
|
1491
|
+
try {
|
|
1492
|
+
response = await operation();
|
|
1493
|
+
outcome = {
|
|
1494
|
+
type: "response",
|
|
1495
|
+
status: response.status,
|
|
1496
|
+
...this.result
|
|
1497
|
+
};
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
threw = true;
|
|
1500
|
+
this.hasException = true;
|
|
1501
|
+
originalError = error;
|
|
1502
|
+
outcome = {
|
|
1503
|
+
type: "exception",
|
|
1504
|
+
stage: this.stage,
|
|
1505
|
+
error
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
if (span != null) this.project(span, outcome);
|
|
1509
|
+
if (threw) throw originalError;
|
|
1510
|
+
return response;
|
|
1511
|
+
}
|
|
1512
|
+
/** Complete standalone handler calls that have no enclosing fetch boundary. */
|
|
1513
|
+
async runAndFinish(getContext, handler, operation) {
|
|
1514
|
+
let response;
|
|
1515
|
+
try {
|
|
1516
|
+
response = await this.run(operation);
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
await this.finish(getContext(), handler, {
|
|
1519
|
+
type: "exception",
|
|
1520
|
+
stage: this.stage,
|
|
1521
|
+
error
|
|
1522
|
+
});
|
|
1523
|
+
throw error;
|
|
1524
|
+
}
|
|
1525
|
+
await this.finish(getContext(), handler, {
|
|
1526
|
+
type: "response",
|
|
1527
|
+
status: response.status,
|
|
1528
|
+
...this.result
|
|
1529
|
+
});
|
|
1530
|
+
return response;
|
|
1531
|
+
}
|
|
1532
|
+
project(span, outcome) {
|
|
1533
|
+
span.setAttribute("activitypub.authentication.status", this.authentication.status);
|
|
1534
|
+
if (this.authentication.status === "rejected") span.setAttribute("activitypub.verification.failure_reason", this.authentication.reason.type);
|
|
1535
|
+
span.setAttribute("activitypub.inbox.disposition", outcome?.type === "exception" || this.hasException ? "exception" : this.result.disposition);
|
|
1536
|
+
}
|
|
1537
|
+
async finish(context, handler, outcome) {
|
|
1538
|
+
if (handler == null) return;
|
|
1539
|
+
const report = {
|
|
1540
|
+
inbox: this.inbox,
|
|
1541
|
+
payload: this.payload,
|
|
1542
|
+
activity: this.activity,
|
|
1543
|
+
attempts: this.verification.attempts,
|
|
1544
|
+
authentication: this.authentication,
|
|
1545
|
+
outcome
|
|
1546
|
+
};
|
|
1547
|
+
try {
|
|
1548
|
+
await handler(context, report);
|
|
1549
|
+
} catch (error) {
|
|
1550
|
+
try {
|
|
1551
|
+
(0, _logtape_logtape.getLogger)([
|
|
1552
|
+
"fedify",
|
|
1553
|
+
"federation",
|
|
1554
|
+
"inbox"
|
|
1555
|
+
]).error("An unexpected error occurred in inbox request finished handler:\n{error}", { error });
|
|
1556
|
+
} catch {}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
//#endregion
|
|
1450
1561
|
//#region src/sig/compound-proof.ts
|
|
1451
1562
|
const textEncoder = new TextEncoder();
|
|
1452
1563
|
/**
|
|
@@ -1935,7 +2046,23 @@ async function verifyDiscoveredProofDocuments(discovered, options) {
|
|
|
1935
2046
|
verified: false,
|
|
1936
2047
|
reason: { type: "missingContext" }
|
|
1937
2048
|
});
|
|
1938
|
-
const
|
|
2049
|
+
const inherited = options[require_http.verificationObservation];
|
|
2050
|
+
let subjectId = null;
|
|
2051
|
+
if (inherited != null && document.id != null) try {
|
|
2052
|
+
subjectId = (0, _fedify_vocab_runtime.parseIri)(document.id);
|
|
2053
|
+
} catch {}
|
|
2054
|
+
const key = await require_http.observeAttempt({ [require_http.verificationObservation]: inherited == null ? void 0 : {
|
|
2055
|
+
...inherited,
|
|
2056
|
+
attempt: void 0,
|
|
2057
|
+
check: void 0,
|
|
2058
|
+
subject: {
|
|
2059
|
+
id: subjectId,
|
|
2060
|
+
pointer: document.path
|
|
2061
|
+
}
|
|
2062
|
+
} }, "objectIntegrity", (observation) => require_http.verifyMapLocalProof(document.securedDocument, {
|
|
2063
|
+
...options,
|
|
2064
|
+
[require_http.verificationObservation]: inherited == null ? void 0 : observation
|
|
2065
|
+
}), (key) => key != null);
|
|
1939
2066
|
return key == null ? Object.freeze({
|
|
1940
2067
|
...metadata,
|
|
1941
2068
|
verified: false,
|
|
@@ -4579,13 +4706,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
|
4579
4706
|
* @param parameters The parameters for handling the collection.
|
|
4580
4707
|
* @returns A promise that resolves to an HTTP response.
|
|
4581
4708
|
*/
|
|
4582
|
-
async function handleCollection(request, { name: name$
|
|
4583
|
-
const spanName = name$
|
|
4709
|
+
async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
4710
|
+
const spanName = name$2.trim().replace(/\s+/g, "_");
|
|
4584
4711
|
tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
|
|
4585
4712
|
const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
|
|
4586
4713
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
4587
4714
|
const metricBase = {
|
|
4588
|
-
kind: getCollectionMetricKind(name$
|
|
4715
|
+
kind: getCollectionMetricKind(name$2),
|
|
4589
4716
|
page: cursor != null,
|
|
4590
4717
|
dispatcher: "built_in"
|
|
4591
4718
|
};
|
|
@@ -4631,7 +4758,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
4631
4758
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
4632
4759
|
return await onNotFound(request);
|
|
4633
4760
|
}
|
|
4634
|
-
const items = filterCollectionItems(page.items, name$
|
|
4761
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
4635
4762
|
itemCount = items.length;
|
|
4636
4763
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4637
4764
|
return items;
|
|
@@ -4673,7 +4800,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
4673
4800
|
} else {
|
|
4674
4801
|
const uri = new URL(portable == null ? baseUri : pageBase);
|
|
4675
4802
|
uri.searchParams.set("cursor", cursor);
|
|
4676
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
4803
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
|
|
4677
4804
|
kind: _opentelemetry_api.SpanKind.SERVER,
|
|
4678
4805
|
attributes: {
|
|
4679
4806
|
"activitypub.collection.id": uri.href,
|
|
@@ -4689,7 +4816,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
4689
4816
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
4690
4817
|
return await onNotFound(request);
|
|
4691
4818
|
}
|
|
4692
|
-
const items = filterCollectionItems(page.items, name$
|
|
4819
|
+
const items = filterCollectionItems(page.items, name$2, filterPredicate);
|
|
4693
4820
|
itemCount = items.length;
|
|
4694
4821
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4695
4822
|
return {
|
|
@@ -5170,6 +5297,16 @@ async function handleMediaUpload(request, { identifier, context: ctx, mediaUploa
|
|
|
5170
5297
|
* @returns A promise that resolves to an HTTP response.
|
|
5171
5298
|
*/
|
|
5172
5299
|
async function handleInbox(request, options) {
|
|
5300
|
+
if (options.observation == null) {
|
|
5301
|
+
const observation = new InboxObservation({
|
|
5302
|
+
kind: options.portableInbox != null ? "portable" : options.recipient == null ? "shared" : "personal",
|
|
5303
|
+
recipient: options.recipient
|
|
5304
|
+
});
|
|
5305
|
+
return await observation.runAndFinish(() => options.context, options.inboxRequestFinishedHandler, () => handleInbox(request, {
|
|
5306
|
+
...options,
|
|
5307
|
+
observation
|
|
5308
|
+
}));
|
|
5309
|
+
}
|
|
5173
5310
|
return await (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.inbox", {
|
|
5174
5311
|
kind: options.queue == null ? _opentelemetry_api.SpanKind.SERVER : _opentelemetry_api.SpanKind.PRODUCER,
|
|
5175
5312
|
attributes: { "activitypub.shared_inbox": options.recipient == null }
|
|
@@ -5178,12 +5315,14 @@ async function handleInbox(request, options) {
|
|
|
5178
5315
|
try {
|
|
5179
5316
|
return await handleInboxInternal(request, options, span);
|
|
5180
5317
|
} catch (e) {
|
|
5318
|
+
options.observation.hasException = true;
|
|
5181
5319
|
span.setStatus({
|
|
5182
5320
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
5183
5321
|
message: String(e)
|
|
5184
5322
|
});
|
|
5185
5323
|
throw e;
|
|
5186
5324
|
} finally {
|
|
5325
|
+
options.observation.project(span);
|
|
5187
5326
|
span.end();
|
|
5188
5327
|
}
|
|
5189
5328
|
});
|
|
@@ -5198,6 +5337,8 @@ async function handleInbox(request, options) {
|
|
|
5198
5337
|
*/
|
|
5199
5338
|
async function handleInboxInternal(request, parameters, span) {
|
|
5200
5339
|
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, publicKeyTtl, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, maxHttpSignatures, skipSignatureVerification, inboxChallengePolicy, meterProvider, tracerProvider, portableInbox } = parameters;
|
|
5340
|
+
const observation = parameters.observation;
|
|
5341
|
+
const signatureObservation = { [require_http.verificationObservation]: observation.verification };
|
|
5201
5342
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
5202
5343
|
"fedify",
|
|
5203
5344
|
"federation",
|
|
@@ -5222,6 +5363,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5222
5363
|
}
|
|
5223
5364
|
}
|
|
5224
5365
|
if (request.bodyUsed) {
|
|
5366
|
+
observation.result = {
|
|
5367
|
+
disposition: "failed",
|
|
5368
|
+
reason: "bodyUnavailable"
|
|
5369
|
+
};
|
|
5225
5370
|
logger.error("Request body has already been read.", { recipient });
|
|
5226
5371
|
span.setStatus({
|
|
5227
5372
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -5232,6 +5377,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5232
5377
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5233
5378
|
});
|
|
5234
5379
|
} else if (request.body?.locked) {
|
|
5380
|
+
observation.result = {
|
|
5381
|
+
disposition: "failed",
|
|
5382
|
+
reason: "bodyUnavailable"
|
|
5383
|
+
};
|
|
5235
5384
|
logger.error("Request body is locked.", { recipient });
|
|
5236
5385
|
span.setStatus({
|
|
5237
5386
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -5242,11 +5391,20 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5242
5391
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5243
5392
|
});
|
|
5244
5393
|
}
|
|
5394
|
+
observation.stage = "parse";
|
|
5395
|
+
observation.result = {
|
|
5396
|
+
disposition: "rejected",
|
|
5397
|
+
reason: "invalidJson"
|
|
5398
|
+
};
|
|
5245
5399
|
let json;
|
|
5246
5400
|
try {
|
|
5247
5401
|
json = JSON.parse(await require_types.readBoundedText(request.clone(), require_types.MAX_BODY_SIZE, request.url));
|
|
5248
5402
|
} catch (error) {
|
|
5249
5403
|
if (error instanceof require_types.BodyTooLargeError) {
|
|
5404
|
+
observation.result = {
|
|
5405
|
+
disposition: "rejected",
|
|
5406
|
+
reason: "bodyTooLarge"
|
|
5407
|
+
};
|
|
5250
5408
|
request.body?.cancel(error).catch(() => {});
|
|
5251
5409
|
span.setStatus({
|
|
5252
5410
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -5276,6 +5434,15 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5276
5434
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5277
5435
|
});
|
|
5278
5436
|
}
|
|
5437
|
+
observation.payload = {
|
|
5438
|
+
status: "parsed",
|
|
5439
|
+
value: structuredClone(json)
|
|
5440
|
+
};
|
|
5441
|
+
observation.result = {
|
|
5442
|
+
disposition: "rejected",
|
|
5443
|
+
reason: "invalidActivity"
|
|
5444
|
+
};
|
|
5445
|
+
observation.stage = "verify";
|
|
5279
5446
|
const keyCache = new KvKeyCache(kv, kvPrefixes.publicKey, {
|
|
5280
5447
|
documentLoader: ctx.documentLoader,
|
|
5281
5448
|
contextLoader: ctx.contextLoader,
|
|
@@ -5305,6 +5472,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5305
5472
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
5306
5473
|
message: `Failed to parse activity:\n${error}`
|
|
5307
5474
|
});
|
|
5475
|
+
observation.result = {
|
|
5476
|
+
disposition: "rejected",
|
|
5477
|
+
reason: "invalidActivity"
|
|
5478
|
+
};
|
|
5308
5479
|
return new Response("Invalid activity.", {
|
|
5309
5480
|
status: 400,
|
|
5310
5481
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5322,11 +5493,25 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5322
5493
|
recipient,
|
|
5323
5494
|
error
|
|
5324
5495
|
});
|
|
5496
|
+
observation.result = {
|
|
5497
|
+
disposition: "rejected",
|
|
5498
|
+
reason: "invalidJsonLd"
|
|
5499
|
+
};
|
|
5325
5500
|
return new Response("Invalid JSON-LD.", {
|
|
5326
5501
|
status: 400,
|
|
5327
5502
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5328
5503
|
});
|
|
5329
5504
|
}
|
|
5505
|
+
observation.verification.attempts.push({
|
|
5506
|
+
mechanism: "linkedData",
|
|
5507
|
+
subject: {
|
|
5508
|
+
id: null,
|
|
5509
|
+
pointer: ""
|
|
5510
|
+
},
|
|
5511
|
+
checks: [],
|
|
5512
|
+
status: "error",
|
|
5513
|
+
error
|
|
5514
|
+
});
|
|
5330
5515
|
if (!canAttemptAlternateAuthAfterLdSignatureFailure) throw error;
|
|
5331
5516
|
if (!skipSignatureVerification) deferredLdSignatureError = error;
|
|
5332
5517
|
logger.debug("Failed to normalize JSON-LD for Linked Data Signatures; deferring to another authentication path only if it verifies:\n{error}", {
|
|
@@ -5338,6 +5523,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5338
5523
|
compactedJsonWithoutSig = require_http.detachSignature(compactedJson);
|
|
5339
5524
|
try {
|
|
5340
5525
|
ldSigVerified = await require_http.verifyCompactJsonLd(compactedJson, {
|
|
5526
|
+
...signatureObservation,
|
|
5341
5527
|
contextLoader: ctx.contextLoader,
|
|
5342
5528
|
documentLoader: ctx.documentLoader,
|
|
5343
5529
|
keyCache,
|
|
@@ -5351,6 +5537,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5351
5537
|
recipient,
|
|
5352
5538
|
error
|
|
5353
5539
|
});
|
|
5540
|
+
observation.result = {
|
|
5541
|
+
disposition: "rejected",
|
|
5542
|
+
reason: "invalidJsonLd"
|
|
5543
|
+
};
|
|
5354
5544
|
return new Response("Invalid JSON-LD.", {
|
|
5355
5545
|
status: 400,
|
|
5356
5546
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5370,6 +5560,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5370
5560
|
recipient,
|
|
5371
5561
|
error: parseError
|
|
5372
5562
|
});
|
|
5563
|
+
observation.result = {
|
|
5564
|
+
disposition: "rejected",
|
|
5565
|
+
reason: "invalidJsonLd"
|
|
5566
|
+
};
|
|
5373
5567
|
return new Response("Invalid JSON-LD.", {
|
|
5374
5568
|
status: 400,
|
|
5375
5569
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5390,7 +5584,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5390
5584
|
json
|
|
5391
5585
|
});
|
|
5392
5586
|
try {
|
|
5393
|
-
activity = await _fedify_vocab.Activity.fromJsonLd(compactedJsonWithoutSig, {
|
|
5587
|
+
activity = observation.activity = await _fedify_vocab.Activity.fromJsonLd(compactedJsonWithoutSig, {
|
|
5394
5588
|
...ctx,
|
|
5395
5589
|
contextLoader: require_http.getNormalizationContextLoader(ctx.contextLoader)
|
|
5396
5590
|
});
|
|
@@ -5402,6 +5596,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5402
5596
|
activityVerified = true;
|
|
5403
5597
|
if (!skipSignatureVerification && isPortableActivity(activity)) try {
|
|
5404
5598
|
proofVerified = await require_http.verifyObject(_fedify_vocab.Activity, jsonWithoutSig, {
|
|
5599
|
+
...signatureObservation,
|
|
5405
5600
|
contextLoader: require_http.wrapContextLoaderForJsonLd(ctx.contextLoader),
|
|
5406
5601
|
documentLoader: ctx.documentLoader,
|
|
5407
5602
|
keyCache,
|
|
@@ -5422,6 +5617,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5422
5617
|
});
|
|
5423
5618
|
try {
|
|
5424
5619
|
activity = await require_http.verifyObject(_fedify_vocab.Activity, jsonWithoutSig, {
|
|
5620
|
+
...signatureObservation,
|
|
5425
5621
|
contextLoader: require_http.wrapContextLoaderForJsonLd(ctx.contextLoader),
|
|
5426
5622
|
documentLoader: ctx.documentLoader,
|
|
5427
5623
|
keyCache,
|
|
@@ -5457,6 +5653,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5457
5653
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
5458
5654
|
message: `Failed to parse activity:\n${error}`
|
|
5459
5655
|
});
|
|
5656
|
+
observation.result = {
|
|
5657
|
+
disposition: "rejected",
|
|
5658
|
+
reason: "invalidActivity"
|
|
5659
|
+
};
|
|
5460
5660
|
return new Response("Invalid activity.", {
|
|
5461
5661
|
status: 400,
|
|
5462
5662
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5480,6 +5680,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5480
5680
|
if (activity == null) {
|
|
5481
5681
|
if (!skipSignatureVerification) {
|
|
5482
5682
|
const verification = await require_http.verifyRequestDetailed(request, {
|
|
5683
|
+
...signatureObservation,
|
|
5483
5684
|
contextLoader: ctx.contextLoader,
|
|
5484
5685
|
documentLoader: ctx.documentLoader,
|
|
5485
5686
|
timeWindow: signatureTimeWindow,
|
|
@@ -5490,6 +5691,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5490
5691
|
});
|
|
5491
5692
|
if (verification.verified === false) {
|
|
5492
5693
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5694
|
+
observation.authentication = {
|
|
5695
|
+
status: "rejected",
|
|
5696
|
+
reason: { type: "verificationFailed" }
|
|
5697
|
+
};
|
|
5698
|
+
observation.result = {
|
|
5699
|
+
disposition: "rejected",
|
|
5700
|
+
reason: "authentication"
|
|
5701
|
+
};
|
|
5493
5702
|
const reason = verification.reason;
|
|
5494
5703
|
const remoteHost = "keyId" in reason && reason.keyId != null ? require_http.getRemoteHost(reason.keyId) : void 0;
|
|
5495
5704
|
require_http.getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure(reason.type, remoteHost);
|
|
@@ -5504,7 +5713,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5504
5713
|
});
|
|
5505
5714
|
if (unverifiedActivityHandler == null) return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5506
5715
|
try {
|
|
5507
|
-
activity = await _fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
5716
|
+
activity = observation.activity = await _fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
5508
5717
|
} catch (error) {
|
|
5509
5718
|
logger.error("Failed to parse activity:\n{error}", {
|
|
5510
5719
|
recipient,
|
|
@@ -5520,6 +5729,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5520
5729
|
recipient
|
|
5521
5730
|
});
|
|
5522
5731
|
}
|
|
5732
|
+
observation.result = {
|
|
5733
|
+
disposition: "rejected",
|
|
5734
|
+
reason: "invalidActivity"
|
|
5735
|
+
};
|
|
5523
5736
|
return new Response("Invalid activity.", {
|
|
5524
5737
|
status: 400,
|
|
5525
5738
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
@@ -5542,6 +5755,11 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5542
5755
|
try {
|
|
5543
5756
|
response = await unverifiedActivityHandler(ctx, activity, reason);
|
|
5544
5757
|
} catch (error) {
|
|
5758
|
+
observation.result = {
|
|
5759
|
+
disposition: "failed",
|
|
5760
|
+
reason: "listenerError",
|
|
5761
|
+
error
|
|
5762
|
+
};
|
|
5545
5763
|
logger.error("An unexpected error occurred in unverified activity handler:\n{error}", {
|
|
5546
5764
|
error,
|
|
5547
5765
|
activity: json,
|
|
@@ -5558,7 +5776,10 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5558
5776
|
}
|
|
5559
5777
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5560
5778
|
}
|
|
5561
|
-
if (response instanceof Response)
|
|
5779
|
+
if (response instanceof Response) {
|
|
5780
|
+
observation.result = { disposition: "customResponse" };
|
|
5781
|
+
return response;
|
|
5782
|
+
}
|
|
5562
5783
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5563
5784
|
} else {
|
|
5564
5785
|
if (inboxChallengePolicy?.enabled && inboxChallengePolicy.requestNonce) pendingNonceLabel = verification.signatureLabel;
|
|
@@ -5568,7 +5789,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5568
5789
|
httpSigKey = verification.key;
|
|
5569
5790
|
}
|
|
5570
5791
|
try {
|
|
5571
|
-
activity = await _fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, {
|
|
5792
|
+
activity = observation.activity = await _fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, {
|
|
5572
5793
|
...ctx,
|
|
5573
5794
|
contextLoader: require_http.wrapContextLoaderForJsonLd(ctx.contextLoader)
|
|
5574
5795
|
});
|
|
@@ -5577,6 +5798,12 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5577
5798
|
return await respondInvalidActivity(error);
|
|
5578
5799
|
}
|
|
5579
5800
|
}
|
|
5801
|
+
observation.activity = activity;
|
|
5802
|
+
observation.stage = "policy";
|
|
5803
|
+
observation.result = {
|
|
5804
|
+
disposition: "rejected",
|
|
5805
|
+
reason: "authentication"
|
|
5806
|
+
};
|
|
5580
5807
|
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
5581
5808
|
span.setAttribute("activitypub.activity.type", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
5582
5809
|
span.addEvent("activitypub.activity.received", {
|
|
@@ -5588,6 +5815,13 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5588
5815
|
});
|
|
5589
5816
|
if (!skipSignatureVerification && !proofVerified && (isPortableActivity(activity) || isPortableActorKey(httpSigKey))) {
|
|
5590
5817
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5818
|
+
observation.authentication = {
|
|
5819
|
+
status: "rejected",
|
|
5820
|
+
reason: {
|
|
5821
|
+
type: "proofPolicy",
|
|
5822
|
+
policy: "portableActor"
|
|
5823
|
+
}
|
|
5824
|
+
};
|
|
5591
5825
|
logger.error("The activity {activityId} of the portable actor {actorId} is not authenticated by a valid Object Integrity Proof.", {
|
|
5592
5826
|
activity: json,
|
|
5593
5827
|
recipient,
|
|
@@ -5605,20 +5839,36 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5605
5839
|
});
|
|
5606
5840
|
}
|
|
5607
5841
|
if (!skipSignatureVerification && activity.id != null && require_http.isPortableId(activity.id)) {
|
|
5608
|
-
const rejection = await
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5842
|
+
const rejection = await require_http.observeAttempt(signatureObservation, "objectIntegrity", async (verification) => {
|
|
5843
|
+
const rejection = await verifyPortableActivityId(activity.id, jsonWithoutSig, {
|
|
5844
|
+
[require_http.verificationObservation]: verification,
|
|
5845
|
+
contextLoader: require_http.wrapContextLoaderForJsonLd(ctx.contextLoader),
|
|
5846
|
+
documentLoader: ctx.documentLoader,
|
|
5847
|
+
keyCache,
|
|
5848
|
+
meterProvider,
|
|
5849
|
+
tracerProvider
|
|
5850
|
+
});
|
|
5851
|
+
if (rejection != null) verification.attempt.reason = {
|
|
5852
|
+
type: "proofPolicy",
|
|
5853
|
+
reason: rejection
|
|
5854
|
+
};
|
|
5855
|
+
return rejection;
|
|
5856
|
+
}, (value) => value == null);
|
|
5615
5857
|
if (rejection != null) {
|
|
5616
5858
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5859
|
+
observation.authentication = {
|
|
5860
|
+
status: "rejected",
|
|
5861
|
+
reason: {
|
|
5862
|
+
type: "proofPolicy",
|
|
5863
|
+
policy: "portableActivity",
|
|
5864
|
+
detail: rejection
|
|
5865
|
+
}
|
|
5866
|
+
};
|
|
5617
5867
|
logger.error("The portable activity {activityId} is not authenticated by an Object Integrity Proof of its own DID: {reason}", {
|
|
5618
5868
|
activity: json,
|
|
5619
5869
|
recipient,
|
|
5620
5870
|
activityId: activity.id.href,
|
|
5621
|
-
reason: rejection
|
|
5871
|
+
reason: rejection.type
|
|
5622
5872
|
});
|
|
5623
5873
|
span.setStatus({
|
|
5624
5874
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -5632,6 +5882,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5632
5882
|
}
|
|
5633
5883
|
if (httpSigKey != null && !await require_portable_collection.doesActorOwnKey(activity, httpSigKey, ctx)) {
|
|
5634
5884
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
5885
|
+
observation.authentication = {
|
|
5886
|
+
status: "rejected",
|
|
5887
|
+
reason: {
|
|
5888
|
+
type: "actorKeyMismatch",
|
|
5889
|
+
key: require_http.snapshotKey(httpSigKey),
|
|
5890
|
+
actorIds: activity.actorIds.map((id) => new URL(id.href))
|
|
5891
|
+
}
|
|
5892
|
+
};
|
|
5635
5893
|
require_http.getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("actorKeyMismatch", httpSigKey.id == null ? void 0 : require_http.getRemoteHost(httpSigKey.id));
|
|
5636
5894
|
logger.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
|
|
5637
5895
|
activity: json,
|
|
@@ -5651,13 +5909,35 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5651
5909
|
if (!skipSignatureVerification) {
|
|
5652
5910
|
const compoundJson = Array.isArray(json) ? json : jsonWithoutSig;
|
|
5653
5911
|
if (inspectCompoundPortableObjectApplicability(compoundJson, INBOX_COMPOUND_PROOF_LIMITS) !== "absent") {
|
|
5654
|
-
const compoundProof = await
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5912
|
+
const compoundProof = await require_http.observeAttempt(signatureObservation, "objectIntegrity", async (verification) => {
|
|
5913
|
+
const result = await verifyCompoundPortableObjectProofs(compoundJson, INBOX_COMPOUND_PROOF_LIMITS, {
|
|
5914
|
+
[require_http.verificationObservation]: verification,
|
|
5915
|
+
documentLoader: ctx.documentLoader,
|
|
5916
|
+
keyCache,
|
|
5917
|
+
meterProvider,
|
|
5918
|
+
tracerProvider
|
|
5919
|
+
});
|
|
5920
|
+
if (result.status === "unsupported") verification.attempt.reason = {
|
|
5921
|
+
type: "proofPolicy",
|
|
5922
|
+
reason: result.reason
|
|
5923
|
+
};
|
|
5924
|
+
else if (!result.verified) {
|
|
5925
|
+
const failed = result.portableObjects.find((object) => !object.verified);
|
|
5926
|
+
verification.attempt.reason = {
|
|
5927
|
+
type: "proofPolicy",
|
|
5928
|
+
reason: failed?.verified === false ? failed.reason : { type: "invalidProof" }
|
|
5929
|
+
};
|
|
5930
|
+
}
|
|
5931
|
+
return result;
|
|
5932
|
+
}, (result) => result.status === "ok" && result.verified);
|
|
5660
5933
|
if (compoundProof.status !== "ok" || !compoundProof.verified) {
|
|
5934
|
+
observation.authentication = {
|
|
5935
|
+
status: "rejected",
|
|
5936
|
+
reason: {
|
|
5937
|
+
type: "proofPolicy",
|
|
5938
|
+
policy: "compound"
|
|
5939
|
+
}
|
|
5940
|
+
};
|
|
5661
5941
|
logger.error("Failed to verify compound portable Object Integrity Proofs.", {
|
|
5662
5942
|
recipient,
|
|
5663
5943
|
activity: json,
|
|
@@ -5676,11 +5956,20 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5676
5956
|
}
|
|
5677
5957
|
if (pendingNonceLabel != null) {
|
|
5678
5958
|
if (!await verifySignatureNonce(request, kv, kvPrefixes.acceptSignatureNonce, pendingNonceLabel)) {
|
|
5959
|
+
observation.authentication = {
|
|
5960
|
+
status: "rejected",
|
|
5961
|
+
reason: { type: "invalidNonce" }
|
|
5962
|
+
};
|
|
5679
5963
|
require_http.getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("invalidNonce", httpSigKey?.id == null ? void 0 : require_http.getRemoteHost(httpSigKey.id));
|
|
5680
5964
|
logger.error("Signature nonce verification failed (missing, expired, or replayed).", { recipient });
|
|
5681
5965
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
5682
5966
|
}
|
|
5683
5967
|
}
|
|
5968
|
+
observation.authentication = skipSignatureVerification ? { status: "skipped" } : {
|
|
5969
|
+
status: "verified",
|
|
5970
|
+
attempts: observation.verification.attempts.filter((attempt) => attempt.status === "verified")
|
|
5971
|
+
};
|
|
5972
|
+
observation.stage = "dispatch";
|
|
5684
5973
|
const routeResult = await routeActivity({
|
|
5685
5974
|
context: ctx,
|
|
5686
5975
|
json,
|
|
@@ -5692,7 +5981,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5692
5981
|
inboxListeners,
|
|
5693
5982
|
inboxContextFactory,
|
|
5694
5983
|
listenerInboxContextFactory: ldSigVerified ? inboxContextFactory[rawInboxContextFactorySymbol] : void 0,
|
|
5695
|
-
inboxErrorHandler,
|
|
5984
|
+
inboxErrorHandler: async (context, error) => {
|
|
5985
|
+
observation.result = {
|
|
5986
|
+
disposition: "failed",
|
|
5987
|
+
reason: "listenerError",
|
|
5988
|
+
error
|
|
5989
|
+
};
|
|
5990
|
+
await inboxErrorHandler?.(context, error);
|
|
5991
|
+
},
|
|
5696
5992
|
kv,
|
|
5697
5993
|
kvPrefixes,
|
|
5698
5994
|
queue,
|
|
@@ -5728,30 +6024,44 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
5728
6024
|
error
|
|
5729
6025
|
});
|
|
5730
6026
|
}
|
|
5731
|
-
if (routeResult === "alreadyProcessed")
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
})
|
|
5747
|
-
|
|
6027
|
+
if (routeResult === "alreadyProcessed") {
|
|
6028
|
+
observation.result = { disposition: "duplicate" };
|
|
6029
|
+
return new Response(`Activity <${activity.id}> has already been processed.`, {
|
|
6030
|
+
status: 202,
|
|
6031
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6032
|
+
});
|
|
6033
|
+
} else if (routeResult === "missingActor") {
|
|
6034
|
+
observation.result = {
|
|
6035
|
+
disposition: "rejected",
|
|
6036
|
+
reason: "missingActor"
|
|
6037
|
+
};
|
|
6038
|
+
return new Response("Missing actor.", {
|
|
6039
|
+
status: 400,
|
|
6040
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6041
|
+
});
|
|
6042
|
+
} else if (routeResult === "enqueued") {
|
|
6043
|
+
observation.result = { disposition: "enqueued" };
|
|
6044
|
+
return new Response("Activity is enqueued.", {
|
|
6045
|
+
status: 202,
|
|
6046
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6047
|
+
});
|
|
6048
|
+
} else if (routeResult === "unsupportedActivity") {
|
|
6049
|
+
observation.result = { disposition: "unhandled" };
|
|
6050
|
+
return new Response("", {
|
|
6051
|
+
status: 202,
|
|
6052
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6053
|
+
});
|
|
6054
|
+
} else if (routeResult === "error") return new Response("Internal server error.", {
|
|
5748
6055
|
status: 500,
|
|
5749
6056
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
5750
6057
|
});
|
|
5751
|
-
else
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
6058
|
+
else {
|
|
6059
|
+
observation.result = { disposition: "processed" };
|
|
6060
|
+
return new Response("", {
|
|
6061
|
+
status: 202,
|
|
6062
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6063
|
+
});
|
|
6064
|
+
}
|
|
5755
6065
|
}
|
|
5756
6066
|
const PORTABLE_INBOX_FORWARDABLE_RESULTS = /* @__PURE__ */ new Set([
|
|
5757
6067
|
"success",
|
|
@@ -5880,8 +6190,8 @@ var CustomCollectionHandler = class {
|
|
|
5880
6190
|
* @param CollectionPage The CollectionPage constructor.
|
|
5881
6191
|
* @param filterPredicate Optional filter predicate for items.
|
|
5882
6192
|
*/
|
|
5883
|
-
constructor(name$
|
|
5884
|
-
this.name = name$
|
|
6193
|
+
constructor(name$1, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
|
|
6194
|
+
this.name = name$1;
|
|
5885
6195
|
this.values = values;
|
|
5886
6196
|
this.context = context;
|
|
5887
6197
|
this.callbacks = callbacks;
|
|
@@ -6417,19 +6727,19 @@ function isPortableActivity(activity) {
|
|
|
6417
6727
|
*/
|
|
6418
6728
|
async function verifyPortableActivityId(activityId, jsonLd, options) {
|
|
6419
6729
|
const expectedId = require_http.getCanonicalPortableId(activityId);
|
|
6420
|
-
if (expectedId == null) return
|
|
6730
|
+
if (expectedId == null) return { type: "invalidObjectId" };
|
|
6421
6731
|
let verification;
|
|
6422
6732
|
try {
|
|
6423
6733
|
verification = await require_http.verifyPortableObjectProofWithRoot(jsonLd, options);
|
|
6424
6734
|
} catch (error) {
|
|
6425
|
-
if (error instanceof require_http.InvalidPortableObjectIdError) return
|
|
6735
|
+
if (error instanceof require_http.InvalidPortableObjectIdError) return { type: "invalidObjectId" };
|
|
6426
6736
|
if (!isPermanentActivityParseError(error)) throw error;
|
|
6427
|
-
return "
|
|
6737
|
+
return { type: "invalidDocument" };
|
|
6428
6738
|
}
|
|
6429
6739
|
const { result, root } = verification;
|
|
6430
|
-
if (!result.verified) return result.reason
|
|
6740
|
+
if (!result.verified) return result.reason;
|
|
6431
6741
|
const rootId = root?.["@id"];
|
|
6432
|
-
if (typeof rootId !== "string" || require_http.getCanonicalPortableId((0, _fedify_vocab_runtime.parseIri)(rootId)) !== expectedId) return
|
|
6742
|
+
if (typeof rootId !== "string" || require_http.getCanonicalPortableId((0, _fedify_vocab_runtime.parseIri)(rootId)) !== expectedId) return { type: "subjectMismatch" };
|
|
6433
6743
|
return null;
|
|
6434
6744
|
}
|
|
6435
6745
|
//#endregion
|
|
@@ -9409,7 +9719,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9409
9719
|
return await (0, _logtape_logtape.withContext)({
|
|
9410
9720
|
traceId: spanCtx.traceId,
|
|
9411
9721
|
spanId: spanCtx.spanId
|
|
9412
|
-
}, async () => {
|
|
9722
|
+
}, () => this.#observeInboxFetch(metricState, async () => {
|
|
9413
9723
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
9414
9724
|
"fedify",
|
|
9415
9725
|
"federation",
|
|
@@ -9424,8 +9734,14 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9424
9734
|
tracer,
|
|
9425
9735
|
metricState
|
|
9426
9736
|
});
|
|
9737
|
+
if (metricState.inboxCompletion != null) metricState.inboxCompletion.observation.stage = "respond";
|
|
9427
9738
|
if (metricState.endpoint !== "hashlink_media" && acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
|
|
9428
9739
|
} catch (error) {
|
|
9740
|
+
metricState.inboxCompletion?.observation.project(span, {
|
|
9741
|
+
type: "exception",
|
|
9742
|
+
stage: metricState.inboxCompletion.observation.stage,
|
|
9743
|
+
error
|
|
9744
|
+
});
|
|
9429
9745
|
this.metrics.recordHttpServerRequest(request.method, metricState.endpoint ?? "error", require_http.getDurationMs(metricStart), { routeTemplate: metricState.routeTemplate });
|
|
9430
9746
|
span.setStatus({
|
|
9431
9747
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -9464,7 +9780,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9464
9780
|
else if (response.status >= 400) logger.warn(logTpl, values);
|
|
9465
9781
|
else logger.info(logTpl, values);
|
|
9466
9782
|
return response;
|
|
9467
|
-
});
|
|
9783
|
+
}));
|
|
9468
9784
|
});
|
|
9469
9785
|
});
|
|
9470
9786
|
}
|
|
@@ -9506,8 +9822,17 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9506
9822
|
metricState.routeTemplate = route.template;
|
|
9507
9823
|
metricState.endpoint = getEndpointCategory(route.name);
|
|
9508
9824
|
span.updateName(`${request.method} ${route.template}`);
|
|
9509
|
-
let context = this.#createContext(request, contextData);
|
|
9510
9825
|
const routeName = route.name.replace(/:.*$/, "");
|
|
9826
|
+
let context;
|
|
9827
|
+
try {
|
|
9828
|
+
context = this.#createContext(request, contextData);
|
|
9829
|
+
} catch (error) {
|
|
9830
|
+
if (request.method !== "POST" || routeName !== "inbox" && routeName !== "sharedInbox") throw error;
|
|
9831
|
+
return await this.#reportInboxPreparationError(request, contextData, {
|
|
9832
|
+
kind: routeName === "inbox" ? "personal" : "shared",
|
|
9833
|
+
recipient: route.values.identifier ?? null
|
|
9834
|
+
}, error, span, metricState);
|
|
9835
|
+
}
|
|
9511
9836
|
switch (routeName) {
|
|
9512
9837
|
case "webfinger": return await handleWebFinger(request, {
|
|
9513
9838
|
context,
|
|
@@ -9566,6 +9891,28 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9566
9891
|
});
|
|
9567
9892
|
return response;
|
|
9568
9893
|
}
|
|
9894
|
+
if (request.method === "POST" && (routeName === "inbox" || routeName === "sharedInbox")) {
|
|
9895
|
+
const recipient = route.values.identifier ?? null;
|
|
9896
|
+
const observation = new InboxObservation({
|
|
9897
|
+
kind: routeName === "inbox" ? "personal" : "shared",
|
|
9898
|
+
recipient
|
|
9899
|
+
});
|
|
9900
|
+
this.#setInboxCompletion(metricState, observation, () => context);
|
|
9901
|
+
return await observation.run(async () => {
|
|
9902
|
+
if (routeName === "inbox") context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier: recipient }) });
|
|
9903
|
+
else if (this.sharedInboxKeyDispatcher != null) {
|
|
9904
|
+
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
9905
|
+
if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
9906
|
+
}
|
|
9907
|
+
return await this.#handleInbox(request, {
|
|
9908
|
+
recipient,
|
|
9909
|
+
context,
|
|
9910
|
+
contextData,
|
|
9911
|
+
onNotFound,
|
|
9912
|
+
observation
|
|
9913
|
+
});
|
|
9914
|
+
}, span);
|
|
9915
|
+
}
|
|
9569
9916
|
switch (routeName) {
|
|
9570
9917
|
case "actor":
|
|
9571
9918
|
case "actorAlias": {
|
|
@@ -9641,9 +9988,9 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9641
9988
|
onUnauthorized,
|
|
9642
9989
|
onNotFound
|
|
9643
9990
|
});
|
|
9644
|
-
|
|
9991
|
+
throw new Error("Unreachable inbox delivery.");
|
|
9645
9992
|
case "sharedInbox":
|
|
9646
|
-
if (
|
|
9993
|
+
if (this.sharedInboxKeyDispatcher != null) {
|
|
9647
9994
|
const identity = await this.sharedInboxKeyDispatcher(context);
|
|
9648
9995
|
if (identity != null) context = this.#createContext(request, contextData, { documentLoader: "identifier" in identity || "username" in identity ? await context.getDocumentLoader(identity) : context.getDocumentLoader(identity) });
|
|
9649
9996
|
}
|
|
@@ -9936,7 +10283,49 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9936
10283
|
}
|
|
9937
10284
|
return await onNotFound(request);
|
|
9938
10285
|
}
|
|
9939
|
-
|
|
10286
|
+
#setInboxCompletion(metricState, observation, getContext) {
|
|
10287
|
+
const handler = this.inboxRequestFinishedHandler;
|
|
10288
|
+
metricState.inboxCompletion = {
|
|
10289
|
+
observation,
|
|
10290
|
+
finish: (outcome) => observation.finish(getContext(), handler, outcome)
|
|
10291
|
+
};
|
|
10292
|
+
}
|
|
10293
|
+
async #observeInboxFetch(metricState, operation) {
|
|
10294
|
+
let response;
|
|
10295
|
+
try {
|
|
10296
|
+
response = await operation();
|
|
10297
|
+
} catch (error) {
|
|
10298
|
+
const completion = metricState.inboxCompletion;
|
|
10299
|
+
if (completion != null) await completion.finish({
|
|
10300
|
+
type: "exception",
|
|
10301
|
+
stage: completion.observation.stage,
|
|
10302
|
+
error
|
|
10303
|
+
});
|
|
10304
|
+
throw error;
|
|
10305
|
+
}
|
|
10306
|
+
const completion = metricState.inboxCompletion;
|
|
10307
|
+
if (completion != null) await completion.finish({
|
|
10308
|
+
type: "response",
|
|
10309
|
+
status: response.status,
|
|
10310
|
+
...completion.observation.result
|
|
10311
|
+
});
|
|
10312
|
+
return response;
|
|
10313
|
+
}
|
|
10314
|
+
async #reportInboxPreparationError(request, contextData, inbox, error, span, metricState) {
|
|
10315
|
+
const unavailableLoader = () => Promise.reject(error);
|
|
10316
|
+
const context = new RequestContextImpl({
|
|
10317
|
+
url: new URL(request.url),
|
|
10318
|
+
request,
|
|
10319
|
+
federation: this,
|
|
10320
|
+
data: contextData,
|
|
10321
|
+
documentLoader: unavailableLoader,
|
|
10322
|
+
contextLoader: unavailableLoader
|
|
10323
|
+
});
|
|
10324
|
+
const observation = new InboxObservation(inbox);
|
|
10325
|
+
this.#setInboxCompletion(metricState, observation, () => context);
|
|
10326
|
+
return await observation.run(() => Promise.reject(error), span);
|
|
10327
|
+
}
|
|
10328
|
+
async #handleInbox(request, { recipient, context, contextData, onNotFound, portableInbox, observation }) {
|
|
9940
10329
|
if (!this.manuallyStartQueue) this._startQueueInternal(contextData);
|
|
9941
10330
|
const inboxContextFactory = context.toInboxContext.bind(context);
|
|
9942
10331
|
inboxContextFactory[rawInboxContextFactorySymbol] = context.toInboxContext.bind(context);
|
|
@@ -9960,7 +10349,8 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9960
10349
|
meterProvider: this.meterProvider,
|
|
9961
10350
|
tracerProvider: this.tracerProvider,
|
|
9962
10351
|
idempotencyStrategy: this.idempotencyStrategy,
|
|
9963
|
-
portableInbox
|
|
10352
|
+
portableInbox,
|
|
10353
|
+
observation
|
|
9964
10354
|
});
|
|
9965
10355
|
}
|
|
9966
10356
|
async #fetchPortableInbox(request, portable, { onNotFound, contextData, span, metricState }) {
|
|
@@ -9989,63 +10379,79 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9989
10379
|
metricState.endpoint = "inbox";
|
|
9990
10380
|
span.updateName(`${request.method} ${metricState.routeTemplate}`);
|
|
9991
10381
|
const identifier = route.values.identifier;
|
|
9992
|
-
let context
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10382
|
+
let context;
|
|
10383
|
+
try {
|
|
10384
|
+
context = this.#createContext(request, contextData);
|
|
10385
|
+
} catch (error) {
|
|
10386
|
+
return await this.#reportInboxPreparationError(request, contextData, {
|
|
10387
|
+
kind: "portable",
|
|
10388
|
+
recipient: identifier
|
|
10389
|
+
}, error, span, metricState);
|
|
10390
|
+
}
|
|
10391
|
+
const observation = new InboxObservation({
|
|
10392
|
+
kind: "portable",
|
|
10393
|
+
recipient: identifier
|
|
10394
|
+
});
|
|
10395
|
+
this.#setInboxCompletion(metricState, observation, () => context);
|
|
10396
|
+
return await observation.run(async () => {
|
|
10397
|
+
const actor = this.actorCallbacks?.dispatcher == null ? null : await this.actorCallbacks.dispatcher(context, identifier);
|
|
10398
|
+
const localOrigin = context.canonicalOrigin;
|
|
10399
|
+
const resolution = resolvePortableInboxRecipient(actor == null || actor instanceof _fedify_vocab.Tombstone ? null : actor, {
|
|
10400
|
+
authority: portable.portableRequest.authority,
|
|
10401
|
+
canonicalInboxId: portable.canonicalId,
|
|
10402
|
+
localOrigin
|
|
10403
|
+
});
|
|
10404
|
+
if (resolution.status === "rejected") {
|
|
10405
|
+
logger.debug("Not accepting a delivery to the portable inbox {inbox} on behalf of the actor {identifier}: {reason}.", {
|
|
10406
|
+
inbox: portable.canonicalId,
|
|
10407
|
+
identifier,
|
|
10408
|
+
reason: resolution.reason
|
|
10409
|
+
});
|
|
10410
|
+
return await onNotFound(request);
|
|
10411
|
+
}
|
|
10412
|
+
const { recipient } = resolution;
|
|
10413
|
+
if (this.actorCallbacks?.keyPairsDispatcher != null) context = this.#createContext(request, contextData, { documentLoader: await context.getDocumentLoader({ identifier }) });
|
|
10414
|
+
const excludedOrigins = [localOrigin, new URL(request.url).origin];
|
|
10415
|
+
return await this.#handleInbox(request, {
|
|
10416
|
+
recipient: identifier,
|
|
10417
|
+
observation,
|
|
10418
|
+
context,
|
|
10419
|
+
contextData,
|
|
10420
|
+
onNotFound,
|
|
10421
|
+
portableInbox: {
|
|
10422
|
+
recipient,
|
|
10423
|
+
forward: async (activity, activityId, activityType, signatures) => {
|
|
10424
|
+
if (this.portableInboxForwarding.maxTargets < 1) return;
|
|
10425
|
+
if (this.kv.cas == null) {
|
|
10426
|
+
if (!this.#portableInboxForwardingWarned) {
|
|
10427
|
+
this.#portableInboxForwardingWarned = true;
|
|
10428
|
+
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.");
|
|
10429
|
+
}
|
|
10430
|
+
return;
|
|
10024
10431
|
}
|
|
10025
|
-
|
|
10432
|
+
await forwardPortableInboxActivity({
|
|
10433
|
+
recipient,
|
|
10434
|
+
activity,
|
|
10435
|
+
activityId,
|
|
10436
|
+
activityType,
|
|
10437
|
+
excludedOrigins,
|
|
10438
|
+
baseUrl: context.origin,
|
|
10439
|
+
kv: this.kv,
|
|
10440
|
+
kvPrefix: this.kvPrefixes.portableInboxForwarding,
|
|
10441
|
+
outboxQueue: this.outboxQueue,
|
|
10442
|
+
startQueue: this.manuallyStartQueue ? void 0 : () => this._startQueueInternal(contextData),
|
|
10443
|
+
allowPrivateAddress: this.allowPrivateAddress,
|
|
10444
|
+
getKeys: () => this.#getPortableGatewayKeyPairs(context, identifier, recipient),
|
|
10445
|
+
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock, { specTtl: this.httpMessageSignaturesSpecTtl }),
|
|
10446
|
+
signatures,
|
|
10447
|
+
options: this.portableInboxForwarding,
|
|
10448
|
+
meterProvider: this.meterProvider,
|
|
10449
|
+
tracerProvider: this.tracerProvider
|
|
10450
|
+
});
|
|
10026
10451
|
}
|
|
10027
|
-
await forwardPortableInboxActivity({
|
|
10028
|
-
recipient,
|
|
10029
|
-
activity,
|
|
10030
|
-
activityId,
|
|
10031
|
-
activityType,
|
|
10032
|
-
excludedOrigins,
|
|
10033
|
-
baseUrl: context.origin,
|
|
10034
|
-
kv: this.kv,
|
|
10035
|
-
kvPrefix: this.kvPrefixes.portableInboxForwarding,
|
|
10036
|
-
outboxQueue: this.outboxQueue,
|
|
10037
|
-
startQueue: this.manuallyStartQueue ? void 0 : () => this._startQueueInternal(contextData),
|
|
10038
|
-
allowPrivateAddress: this.allowPrivateAddress,
|
|
10039
|
-
getKeys: () => this.#getPortableGatewayKeyPairs(context, identifier, recipient),
|
|
10040
|
-
specDeterminer: new KvSpecDeterminer(this.kv, this.kvPrefixes.httpMessageSignaturesSpec, this.firstKnock, { specTtl: this.httpMessageSignaturesSpecTtl }),
|
|
10041
|
-
signatures,
|
|
10042
|
-
options: this.portableInboxForwarding,
|
|
10043
|
-
meterProvider: this.meterProvider,
|
|
10044
|
-
tracerProvider: this.tracerProvider
|
|
10045
|
-
});
|
|
10046
10452
|
}
|
|
10047
|
-
}
|
|
10048
|
-
});
|
|
10453
|
+
});
|
|
10454
|
+
}, span);
|
|
10049
10455
|
}
|
|
10050
10456
|
/**
|
|
10051
10457
|
* Gets this server's gateway key pairs for the portable actor that owns
|