@fedify/fedify 2.5.0-dev.2446 → 2.5.0-dev.2492

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.
Files changed (141) hide show
  1. package/README.md +3 -0
  2. package/dist/{activity-listener-C-BSbKI7.mjs → activity-listener-CELFAodc.mjs} +8 -4
  3. package/dist/{builder-Da8b9O6v.mjs → builder-DphK9v7f.mjs} +5 -5
  4. package/dist/{client-D-XP_Ccb.d.ts → client-3InDZbba.d.cts} +0 -1
  5. package/dist/{client-D-XP_Ccb.d.cts → client-3InDZbba.d.ts} +0 -1
  6. package/dist/{client-CEnNz8O7.mjs → client-5SIFzW3q.mjs} +113 -64
  7. package/dist/compat/mod.d.cts +1 -1
  8. package/dist/compat/mod.d.ts +1 -1
  9. package/dist/compat/transformers.test.mjs +1 -1
  10. package/dist/{compound-proof-D1djO5fa.mjs → compound-proof-cYtvlTgF.mjs} +2 -2
  11. package/dist/{context-8Tu9kea0.mjs → context-COHd1Rsh.mjs} +2 -0
  12. package/dist/{context-BdB54V-A.d.ts → context-DQJE9YfH.d.ts} +332 -6
  13. package/dist/{context-CQ9H8vNW.d.cts → context-DSuL3YWx.d.cts} +332 -6
  14. package/dist/{deno-eNpfj4oR.mjs → deno-iOAA7iRN.mjs} +1 -1
  15. package/dist/docloader-DCZkiTeF.mjs +136 -0
  16. package/dist/{exporter-DThFfe7P.mjs → exporter-DVG68ozH.mjs} +36 -30
  17. package/dist/federation/builder.test.mjs +1 -1
  18. package/dist/federation/compound-fanout.test.mjs +6 -3
  19. package/dist/federation/handler-compound-proof.test.mjs +5 -5
  20. package/dist/federation/handler.test.mjs +22 -7
  21. package/dist/federation/hashlink-media.test.mjs +2 -2
  22. package/dist/federation/idempotency.test.mjs +2 -2
  23. package/dist/federation/inbox-actorless.test.d.mts +2 -0
  24. package/dist/federation/inbox-actorless.test.mjs +487 -0
  25. package/dist/federation/inbox-authentication.test.d.mts +2 -0
  26. package/dist/federation/inbox-authentication.test.mjs +116 -0
  27. package/dist/federation/inbox-report.test.mjs +5 -5
  28. package/dist/federation/inbox-view.test.d.mts +2 -0
  29. package/dist/federation/inbox-view.test.mjs +369 -0
  30. package/dist/federation/inbox.test.mjs +22 -12
  31. package/dist/federation/metrics.test.mjs +1 -1
  32. package/dist/federation/middleware.test.mjs +470 -35
  33. package/dist/federation/mod.cjs +2 -1
  34. package/dist/federation/mod.d.cts +4 -4
  35. package/dist/federation/mod.d.ts +4 -4
  36. package/dist/federation/mod.js +8 -2
  37. package/dist/federation/mq.test.mjs +36 -1
  38. package/dist/federation/outgoing-proof.test.mjs +9 -6
  39. package/dist/federation/portable-actor.test.mjs +3 -3
  40. package/dist/federation/portable-chain.test.mjs +3 -3
  41. package/dist/federation/portable-collection.test.mjs +4 -4
  42. package/dist/federation/portable-delivery.test.mjs +2 -2
  43. package/dist/federation/portable-gateway-keys.test.mjs +13 -7
  44. package/dist/federation/portable-inbox.test.mjs +4 -4
  45. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  46. package/dist/federation/portable-route-activity.test.mjs +2 -2
  47. package/dist/federation/portable.test.mjs +3 -3
  48. package/dist/federation/send.test.mjs +502 -93
  49. package/dist/federation/tasks/enqueue.test.mjs +3 -3
  50. package/dist/federation/tasks/tasks.test.mjs +4 -4
  51. package/dist/federation/temporal.test.mjs +1 -1
  52. package/dist/federation/webfinger.test.mjs +2 -2
  53. package/dist/{federation-BVH7drJA.cjs → federation-D0AZw_aw.cjs} +1415 -236
  54. package/dist/{http-BUJ5SFh6.mjs → http-BoJCMnjY.mjs} +204 -106
  55. package/dist/{http-DlNxxW_5.js → http-ChXUU_3c.js} +319 -123
  56. package/dist/{http-BfW3wyQ4.cjs → http-kv1LZw-r.cjs} +339 -125
  57. package/dist/inbox-BjihTKLo.mjs +312 -0
  58. package/dist/inbox-authentication-DflPYlLL.mjs +171 -0
  59. package/dist/inbox-view-D-skypg-.mjs +385 -0
  60. package/dist/{key-nYGY4QB9.d.cts → key-DbLNT-vA.d.ts} +1 -2
  61. package/dist/{key-DhsBxxZN.d.ts → key-DlHT4R6k.d.cts} +1 -2
  62. package/dist/{kv-cache-D_m3A241.mjs → kv-cache-Cmsyke4d.mjs} +1 -1
  63. package/dist/{kv-cache-7015lbrx.js → kv-cache-DQcte7ES.js} +73 -28
  64. package/dist/{kv-cache-kkBB0vbI.cjs → kv-cache-Ddh5sMxH.cjs} +72 -27
  65. package/dist/ld-CWVpo_NM.d.cts +250 -0
  66. package/dist/{ld-CD2CsPxq.mjs → ld-CdvcOEXQ.mjs} +52 -9
  67. package/dist/ld-Inagww4R.d.ts +250 -0
  68. package/dist/{metrics-D3LHk6Z4.mjs → metrics-CpxvUrBz.mjs} +1 -1
  69. package/dist/{middleware-N9eM16XE.js → middleware-BHb5toqA.js} +1413 -247
  70. package/dist/{middleware-CCDXvXOG.mjs → middleware-BKs2DLUF.mjs} +1 -1
  71. package/dist/{middleware-CzoWvrdZ.mjs → middleware-CoBLrTKX.mjs} +533 -132
  72. package/dist/{mod-B0dGgQsg.d.cts → mod-6TQ4UQZb.d.cts} +1 -1
  73. package/dist/mod-B4kBw1-8.d.ts +154 -0
  74. package/dist/{mod-DkhfZC1u.d.ts → mod-B7j30IQf.d.ts} +2 -9
  75. package/dist/mod-C1nT74IG.d.cts +154 -0
  76. package/dist/{mod-D0bxB8jS.d.ts → mod-CIeBgsOC.d.ts} +1 -1
  77. package/dist/{mod-B5-wdzmJ.d.cts → mod-YvA4Rk8J.d.cts} +2 -9
  78. package/dist/mod.cjs +6 -5
  79. package/dist/mod.d.cts +9 -9
  80. package/dist/mod.d.ts +9 -9
  81. package/dist/mod.js +6 -6
  82. package/dist/{mq-Dm1Wmtm-.mjs → mq-eF96QxUT.mjs} +6 -0
  83. package/dist/nodeinfo/client.test.mjs +337 -19
  84. package/dist/nodeinfo/handler.test.mjs +2 -2
  85. package/dist/nodeinfo/mod.cjs +1 -1
  86. package/dist/nodeinfo/mod.d.cts +1 -1
  87. package/dist/nodeinfo/mod.d.ts +1 -1
  88. package/dist/nodeinfo/mod.js +1 -1
  89. package/dist/otel/exporter.test.mjs +257 -1
  90. package/dist/otel/mod.cjs +36 -30
  91. package/dist/otel/mod.d.cts +13 -0
  92. package/dist/otel/mod.d.ts +13 -0
  93. package/dist/otel/mod.js +36 -30
  94. package/dist/{owner-C5-FVX6t.mjs → owner-DTnVpUKA.mjs} +3 -3
  95. package/dist/{portable-collection-Cony3_LC.mjs → portable-collection-CJeYTL61.mjs} +2 -2
  96. package/dist/{portable-collection-C-Z1SKDy.cjs → portable-collection-DciwBIia.cjs} +1 -1
  97. package/dist/{portable-collection-CC2zNAje.js → portable-collection-TmtpzD4E.js} +1 -1
  98. package/dist/{portable-key-DNc_flH-.mjs → portable-key-DcYok4ki.mjs} +1 -1
  99. package/dist/{proof-Cryb1c70.mjs → proof-GH7Poos_.mjs} +98 -10
  100. package/dist/{send-AF25pWaJ.mjs → send-BgGJh6pv.mjs} +174 -76
  101. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  102. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  103. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  104. package/dist/sig/compound-proof.test.mjs +2 -2
  105. package/dist/sig/http.test.mjs +553 -2
  106. package/dist/sig/key.test.mjs +1 -1
  107. package/dist/sig/ld.test.mjs +1 -1
  108. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  109. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  110. package/dist/sig/mod.cjs +2 -2
  111. package/dist/sig/mod.d.cts +3 -3
  112. package/dist/sig/mod.d.ts +3 -3
  113. package/dist/sig/mod.js +2 -2
  114. package/dist/sig/owner.test.mjs +1 -1
  115. package/dist/sig/portable-collection.test.mjs +1 -1
  116. package/dist/sig/portable-interop.test.mjs +3 -3
  117. package/dist/sig/portable-key.test.mjs +5 -5
  118. package/dist/sig/proof.test.mjs +45 -5
  119. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  120. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  121. package/dist/sig/verification.test.mjs +3 -3
  122. package/dist/{temporal-BsAeuUJj.mjs → temporal-gJINzcL4.mjs} +1 -1
  123. package/dist/testing/kv.test.mjs +1 -1
  124. package/dist/testing/mod.d.mts +288 -2
  125. package/dist/testing/mod.mjs +2 -2
  126. package/dist/{testing-t8xDeJZ5.mjs → testing-Dbr21vZk.mjs} +1 -1
  127. package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
  128. package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
  129. package/dist/utils/docloader.test.mjs +344 -36
  130. package/dist/utils/kv-cache.test.mjs +1 -1
  131. package/dist/utils/mod.cjs +1 -1
  132. package/dist/utils/mod.d.cts +1 -1
  133. package/dist/utils/mod.d.ts +1 -1
  134. package/dist/utils/mod.js +1 -1
  135. package/package.json +7 -7
  136. package/dist/docloader-B-0nqOB1.mjs +0 -91
  137. package/dist/inbox-CHvMaIFX.mjs +0 -188
  138. package/dist/mod-BRpER-B0.d.ts +0 -320
  139. package/dist/mod-xAfaLS4T.d.cts +0 -320
  140. package/dist/owner-BF0L9cMt.d.cts +0 -78
  141. package/dist/owner-BSzr5-I8.d.ts +0 -78
@@ -7,7 +7,7 @@ import { t as assertExists } from "../assert_exists-cffSpJ7X.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
9
  import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
10
- import { r as createFederationBuilder } from "../builder-Da8b9O6v.mjs";
10
+ import { r as createFederationBuilder } from "../builder-DphK9v7f.mjs";
11
11
  import { DisallowedOperatorError, DisallowedVarSpecModifierError, DuplicateRouteVariableError, RouteTemplateOptionsNotMatchedError, RouterError } from "@fedify/uri-template";
12
12
  import { Activity, Note, Person } from "@fedify/vocab";
13
13
  import { test } from "@fedify/fixture";
@@ -6,11 +6,11 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assert } from "../assert-D1Eafmd_.mjs";
9
- import { y as exportJwk } from "../ld-CD2CsPxq.mjs";
9
+ import { x as exportJwk } from "../ld-CdvcOEXQ.mjs";
10
10
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
11
- import { o as signObject, p as verifyProof } from "../proof-Cryb1c70.mjs";
11
+ import { h as verifyProof, s as signObject } from "../proof-GH7Poos_.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
13
- import { n as FederationImpl } from "../middleware-CzoWvrdZ.mjs";
13
+ import { n as FederationImpl } from "../middleware-CoBLrTKX.mjs";
14
14
  import { Create, DataIntegrityProof, Note } from "@fedify/vocab";
15
15
  import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
16
16
  import { mockDocumentLoader, test } from "@fedify/fixture";
@@ -68,6 +68,7 @@ test("fanout delivery keeps an embedded signed child intact", async () => {
68
68
  kv: new MemoryKvStore(),
69
69
  contextLoaderFactory: () => mockDocumentLoader
70
70
  });
71
+ federation.allowPrivateAddress = true;
71
72
  federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
72
73
  const message = {
73
74
  type: "fanout",
@@ -139,6 +140,7 @@ test("forced fanout signs before serializing so the child survives", async () =>
139
140
  manuallyStartQueue: true,
140
141
  contextLoaderFactory: () => mockDocumentLoader
141
142
  });
143
+ federation.allowPrivateAddress = true;
142
144
  federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
143
145
  await federation.createContext(new URL("https://example.com/")).sendActivity({
144
146
  keyId: childKeyId,
@@ -203,6 +205,7 @@ test("an already signed activity keeps its single proof through fanout", async (
203
205
  manuallyStartQueue: true,
204
206
  contextLoaderFactory: () => mockDocumentLoader
205
207
  });
208
+ federation.allowPrivateAddress = true;
206
209
  federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
207
210
  await federation.createContext(new URL("https://example.com/")).sendActivity({
208
211
  keyId: childKeyId,
@@ -1,15 +1,15 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { r as createRequestContext, t as createInboxContext } from "../context-8Tu9kea0.mjs";
4
+ import { r as createRequestContext, t as createInboxContext } from "../context-COHd1Rsh.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
- import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-CD2CsPxq.mjs";
9
- import { l as signRequest } from "../http-BUJ5SFh6.mjs";
8
+ import { a as compactJsonLd, h as verifyCompactJsonLd, m as signJsonLd } from "../ld-CdvcOEXQ.mjs";
9
+ import { u as signRequest } from "../http-BoJCMnjY.mjs";
10
10
  import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
11
- import { f as handleInbox, o as createFederation } from "../middleware-CzoWvrdZ.mjs";
12
- import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
11
+ import { f as handleInbox, o as createFederation } from "../middleware-CoBLrTKX.mjs";
12
+ import { t as ActivityListenerSet } from "../activity-listener-CELFAodc.mjs";
13
13
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
14
14
  import { Create, CryptographicKey, Multikey, Person, Update } from "@fedify/vocab";
15
15
  import { encodeMultibase, exportDidKey, parseIri } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-8Tu9kea0.mjs";
4
+ import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-COHd1Rsh.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import { t as assertGreaterOrEqual } from "../assert_greater_or_equal-BlafOF4-.mjs";
@@ -11,13 +11,13 @@ import "../std__assert-DvMDRPes.mjs";
11
11
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
12
12
  import { t as assert } from "../assert-D1Eafmd_.mjs";
13
13
  import { r as parseAcceptSignature } from "../accept-CPVqki78.mjs";
14
- import { C as generateCryptoKeyPair, a as compactJsonLd, p as signJsonLd } from "../ld-CD2CsPxq.mjs";
15
- import { o as signObject } from "../proof-Cryb1c70.mjs";
16
- import { l as signRequest } from "../http-BUJ5SFh6.mjs";
14
+ import { T as generateCryptoKeyPair, a as compactJsonLd, m as signJsonLd } from "../ld-CdvcOEXQ.mjs";
15
+ import { s as signObject } from "../proof-GH7Poos_.mjs";
16
+ import { u as signRequest } from "../http-BoJCMnjY.mjs";
17
17
  import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
18
- import { d as handleCustomCollection, f as handleInbox, g as respondWithObjectIfAcceptable, h as respondWithObject, l as handleActor, m as handleOutbox, o as createFederation, p as handleObject, u as handleCollection } from "../middleware-CzoWvrdZ.mjs";
19
- import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
20
- import { t as FedifySpanExporter } from "../exporter-DThFfe7P.mjs";
18
+ import { d as handleCustomCollection, f as handleInbox, g as respondWithObjectIfAcceptable, h as respondWithObject, l as handleActor, m as handleOutbox, o as createFederation, p as handleObject, u as handleCollection } from "../middleware-CoBLrTKX.mjs";
19
+ import { t as ActivityListenerSet } from "../activity-listener-CELFAodc.mjs";
20
+ import { t as FedifySpanExporter } from "../exporter-DVG68ozH.mjs";
21
21
  import { configure, getLogger, reset, withContext } from "@logtape/logtape";
22
22
  import { Activity, Create, CryptographicKey, Multikey, Note, Person, Tombstone } from "@fedify/vocab";
23
23
  import { FetchError, parseIri } from "@fedify/vocab-runtime";
@@ -3246,11 +3246,26 @@ test("handleInbox() caches normalizedActivity for queued signature-bearing fallb
3246
3246
  activity: unsignedBody,
3247
3247
  normalizedActivity: await compactJsonLd(unsignedBody, sourceContextLoader),
3248
3248
  ldSignatureVerified: false,
3249
+ verifiedView: inboxMessage.verifiedView,
3250
+ authentication: {
3251
+ verified: true,
3252
+ requestSigners: [{
3253
+ kind: "actor",
3254
+ id: "https://example.com/person2",
3255
+ mechanism: "httpSignatures",
3256
+ keyId: "https://example.com/person2#key3"
3257
+ }],
3258
+ documentSigners: [],
3259
+ originAuthenticated: []
3260
+ },
3249
3261
  started: inboxMessage.started,
3250
3262
  attempt: 0,
3251
3263
  identifier: "someone",
3252
3264
  traceContext: inboxMessage.traceContext
3253
3265
  });
3266
+ assertEquals(inboxMessage.verifiedView?.policy, "standard");
3267
+ assertEquals(inboxMessage.verifiedView?.source, "activity");
3268
+ assert(inboxMessage.verifiedView?.contexts.some((entry) => entry.url === "https://remote.example/contexts/ext" && "response" in entry));
3254
3269
  });
3255
3270
  test("respondWithObject()", async () => {
3256
3271
  const response = await respondWithObject(new Note({
@@ -10,8 +10,8 @@ import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
11
11
  import { t as assert } from "../assert-D1Eafmd_.mjs";
12
12
  import { r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
13
- import { o as createFederation, x as parseHashlinkGatewayRequest } from "../middleware-CzoWvrdZ.mjs";
14
- import { r as createFederationBuilder } from "../builder-Da8b9O6v.mjs";
13
+ import { o as createFederation, x as parseHashlinkGatewayRequest } from "../middleware-CoBLrTKX.mjs";
14
+ import { r as createFederationBuilder } from "../builder-DphK9v7f.mjs";
15
15
  import { n as RouterError } from "../router-u5O5KCQr.mjs";
16
16
  import { Note } from "@fedify/vocab";
17
17
  import { computeDigestMultibase, encodeMultibase, exportDidKey } from "@fedify/vocab-runtime";
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import "../std__assert-DvMDRPes.mjs";
7
- import { o as signObject } from "../proof-Cryb1c70.mjs";
7
+ import { s as signObject } from "../proof-GH7Poos_.mjs";
8
8
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
9
- import { o as createFederation } from "../middleware-CzoWvrdZ.mjs";
9
+ import { o as createFederation } from "../middleware-CoBLrTKX.mjs";
10
10
  import { Create, Follow, Person } from "@fedify/vocab";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  //#region src/federation/idempotency.test.ts
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,487 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { a as isActorAuthenticated } from "../inbox-authentication-DflPYlLL.mjs";
5
+ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
+ import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
8
+ import "../std__assert-DvMDRPes.mjs";
9
+ import { t as assert } from "../assert-D1Eafmd_.mjs";
10
+ import { m as signJsonLd } from "../ld-CdvcOEXQ.mjs";
11
+ import { s as signObject } from "../proof-GH7Poos_.mjs";
12
+ import { u as signRequest } from "../http-BoJCMnjY.mjs";
13
+ import { a as rsaPrivateKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
14
+ import { o as createFederation } from "../middleware-CoBLrTKX.mjs";
15
+ import { c as expandDocument } from "../inbox-view-D-skypg-.mjs";
16
+ import { Create, FeatureRequest, Person } from "@fedify/vocab";
17
+ import { FetchError, encodeMultibase, preloadedContexts } from "@fedify/vocab-runtime";
18
+ import { mockDocumentLoader, test } from "@fedify/fixture";
19
+ import serialize from "json-canon";
20
+ //#region src/federation/inbox-actorless.test.ts
21
+ const SIGNER = new URL("https://example.com/person2");
22
+ const RSA_KEY = new URL("https://example.com/person2#key3");
23
+ const ED25519_KEY = new URL("https://example.com/person2#key4");
24
+ const TARGET = new URL("https://example.com/users/alice");
25
+ const COLLECTION = new URL("https://example.com/person2/featured/1");
26
+ const MUTABLE_CONTEXT = "https://remote.example/contexts/mutable";
27
+ const RELAY = new URL("https://example.com/relay");
28
+ const RELAY_KEY = new URL("https://example.com/relay#main-key");
29
+ const { publicKeyPem: RELAY_PEM } = await rsaPublicKey2.toJsonLd({ contextLoader: mockDocumentLoader });
30
+ /** The document Mastodon sends, which declares no actor. */
31
+ function featureRequestDocument(extra = {}) {
32
+ return {
33
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/fep/7aa9"],
34
+ id: "https://example.com/person2/featured/1/requests/1",
35
+ type: "FeatureRequest",
36
+ object: TARGET.href,
37
+ instrument: COLLECTION.href,
38
+ ...extra
39
+ };
40
+ }
41
+ var CapturingQueue = class {
42
+ messages = [];
43
+ maxMessageSize;
44
+ constructor(maxMessageSize) {
45
+ this.maxMessageSize = maxMessageSize;
46
+ }
47
+ enqueue(message) {
48
+ this.messages.push(structuredClone(message));
49
+ return Promise.resolve();
50
+ }
51
+ listen() {
52
+ return new Promise(() => {});
53
+ }
54
+ };
55
+ function setup(options = {}) {
56
+ const received = [];
57
+ let mutableLoads = 0;
58
+ const loader = async (url, loaderOptions) => {
59
+ options.contextLoads?.push(url);
60
+ if (options.unavailable?.includes(url)) throw new FetchError(url, "Service unavailable", new Response(null, { status: 503 }));
61
+ if (url === RELAY.href || url === RELAY_KEY.href) return {
62
+ contextUrl: null,
63
+ documentUrl: RELAY.href,
64
+ document: {
65
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
66
+ id: RELAY.href,
67
+ type: "Service",
68
+ publicKey: {
69
+ id: RELAY_KEY.href,
70
+ type: "CryptographicKey",
71
+ owner: RELAY.href,
72
+ publicKeyPem: RELAY_PEM
73
+ }
74
+ }
75
+ };
76
+ if (url === MUTABLE_CONTEXT) {
77
+ mutableLoads++;
78
+ return {
79
+ contextUrl: null,
80
+ documentUrl: url,
81
+ document: { "@context": { tgt: mutableLoads % 2 === 1 ? {
82
+ "@id": "https://www.w3.org/ns/activitystreams#object",
83
+ "@type": "@id"
84
+ } : {
85
+ "@id": "https://www.w3.org/ns/activitystreams#target",
86
+ "@type": "@id"
87
+ } } }
88
+ };
89
+ }
90
+ return await mockDocumentLoader(url, loaderOptions);
91
+ };
92
+ const federation = createFederation({
93
+ kv: new MemoryKvStore(),
94
+ documentLoaderFactory: () => loader,
95
+ contextLoaderFactory: () => loader,
96
+ authenticatedDocumentLoaderFactory: () => loader,
97
+ queue: options.queue,
98
+ manuallyStartQueue: true
99
+ });
100
+ federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => new Person({
101
+ id: ctx.getActorUri(identifier),
102
+ inbox: ctx.getInboxUri(identifier)
103
+ })).setKeyPairsDispatcher(() => [{
104
+ privateKey: rsaPrivateKey2,
105
+ publicKey: rsaPublicKey2.publicKey
106
+ }]);
107
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(FeatureRequest, (ctx, activity) => {
108
+ if (options.failOnce != null && !options.failOnce.failed) {
109
+ options.failOnce.failed = true;
110
+ throw new Error("Temporary failure.");
111
+ }
112
+ received.push({
113
+ activity,
114
+ authentication: ctx.authentication,
115
+ rawActivity: ctx.activity
116
+ });
117
+ }, { allowActorless: options.allowActorless ?? true }).on(Create, () => {});
118
+ return {
119
+ federation,
120
+ received
121
+ };
122
+ }
123
+ function post(body) {
124
+ return new Request("https://example.com/users/alice/inbox", {
125
+ method: "POST",
126
+ headers: { "Content-Type": "application/activity+json" },
127
+ body: JSON.stringify(body)
128
+ });
129
+ }
130
+ async function postSigned(body) {
131
+ return await signRequest(post(body), rsaPrivateKey3, RSA_KEY);
132
+ }
133
+ async function withLds(document) {
134
+ return await signJsonLd(document, rsaPrivateKey3, RSA_KEY, { contextLoader: mockDocumentLoader });
135
+ }
136
+ async function withProof(document) {
137
+ return await (await signObject(await FeatureRequest.fromJsonLd(document, {
138
+ documentLoader: mockDocumentLoader,
139
+ contextLoader: mockDocumentLoader
140
+ }), ed25519PrivateKey, ED25519_KEY, { contextLoader: mockDocumentLoader })).toJsonLd({
141
+ format: "compact",
142
+ contextLoader: mockDocumentLoader
143
+ });
144
+ }
145
+ /**
146
+ * Signs a JSON document as is with an `eddsa-jcs-2022` proof, so that values
147
+ * typed parsing would drop are covered by the proof as well.
148
+ */
149
+ async function withRawProof(document, proofProperty = "proof") {
150
+ const contexts = Array.isArray(document["@context"]) ? document["@context"] : [document["@context"]];
151
+ const unsecured = {
152
+ ...document,
153
+ "@context": [
154
+ ...contexts,
155
+ "https://w3id.org/security/data-integrity/v1",
156
+ ...proofProperty === "proof" ? [] : [{ [proofProperty]: {
157
+ "@id": "https://w3id.org/security#proof",
158
+ "@type": "@id",
159
+ "@container": "@graph"
160
+ } }]
161
+ ]
162
+ };
163
+ const proofConfig = {
164
+ "@context": unsecured["@context"],
165
+ type: "DataIntegrityProof",
166
+ cryptosuite: "eddsa-jcs-2022",
167
+ verificationMethod: ED25519_KEY.href,
168
+ proofPurpose: "assertionMethod",
169
+ created: "2026-10-01T00:00:00Z"
170
+ };
171
+ const digest = async (value) => new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(serialize(value))));
172
+ const signed = new Uint8Array(await crypto.subtle.sign("Ed25519", ed25519PrivateKey, new Uint8Array([...await digest(proofConfig), ...await digest(unsecured)])));
173
+ const { "@context": _, ...proof } = proofConfig;
174
+ return {
175
+ ...unsecured,
176
+ [proofProperty]: {
177
+ ...proof,
178
+ proofValue: new TextDecoder().decode(encodeMultibase("base58btc", signed))
179
+ }
180
+ };
181
+ }
182
+ async function processQueue(federation, queue) {
183
+ while (queue.messages.length > 0) {
184
+ const message = queue.messages.shift();
185
+ await federation.processQueuedTask(void 0, message);
186
+ }
187
+ }
188
+ test("Actorless FeatureRequest authenticated by HTTP Signatures", async () => {
189
+ const { federation, received } = setup();
190
+ assertEquals((await federation.fetch(await postSigned(featureRequestDocument()), { contextData: void 0 })).status, 202);
191
+ assertEquals(received.length, 1);
192
+ const [{ activity, authentication, rawActivity }] = received;
193
+ assertEquals(activity.actorId, null);
194
+ assertEquals(activity.instrumentId, COLLECTION);
195
+ assertEquals(authentication.verified, true);
196
+ assertEquals(authentication.documentSigners, []);
197
+ assertEquals(authentication.requestSigners.map((p) => [
198
+ p.kind,
199
+ p.id.href,
200
+ p.mechanism
201
+ ]), [[
202
+ "actor",
203
+ SIGNER.href,
204
+ "httpSignatures"
205
+ ]]);
206
+ assert(isActorAuthenticated(authentication, SIGNER));
207
+ assertFalse(isActorAuthenticated(authentication, SIGNER, { scope: "document" }));
208
+ assertEquals(rawActivity, featureRequestDocument());
209
+ });
210
+ test("Actorless FeatureRequest authenticated by Linked Data Signatures", async () => {
211
+ const { federation, received } = setup();
212
+ const document = await withLds(featureRequestDocument());
213
+ assertEquals((await federation.fetch(post(document), { contextData: void 0 })).status, 202);
214
+ assertEquals(received.length, 1);
215
+ assertEquals(received[0].authentication.documentSigners.map((p) => [p.id.href, p.mechanism]), [[SIGNER.href, "linkedDataSignatures"]]);
216
+ assertEquals(received[0].rawActivity, document);
217
+ });
218
+ test("Actorless FeatureRequest authenticated by Object Integrity Proofs", async () => {
219
+ const { federation, received } = setup();
220
+ const document = await withProof(featureRequestDocument());
221
+ assertEquals((await federation.fetch(post(document), { contextData: void 0 })).status, 202);
222
+ assertEquals(received.length, 1);
223
+ assertEquals(received[0].authentication.documentSigners.map((p) => [
224
+ p.kind,
225
+ p.id.href,
226
+ p.mechanism,
227
+ p.keyId?.href
228
+ ]), [[
229
+ "actor",
230
+ SIGNER.href,
231
+ "objectIntegrityProofs",
232
+ ED25519_KEY.href
233
+ ]]);
234
+ });
235
+ test("Actorless activities need a listener that allows them", async () => {
236
+ const { federation, received } = setup({ allowActorless: false });
237
+ const response = await federation.fetch(await postSigned(featureRequestDocument()), { contextData: void 0 });
238
+ assertEquals(response.status, 400);
239
+ assertEquals(await response.text(), "Missing actor.");
240
+ assertEquals(received, []);
241
+ });
242
+ test("Actorless activities need an authenticated principal", async () => {
243
+ const { federation, received } = setup();
244
+ assertEquals((await federation.fetch(post(featureRequestDocument()), { contextData: void 0 })).status, 401);
245
+ assertEquals(received, []);
246
+ });
247
+ test("Actorless activities need the same origin as their signer", async () => {
248
+ const { federation, received } = setup();
249
+ assertEquals((await federation.fetch(await postSigned(featureRequestDocument({ id: "https://other.example/requests/1" })), { contextData: void 0 })).status, 401);
250
+ assertEquals(received, []);
251
+ });
252
+ for (const [label, actor] of [
253
+ ["an anonymous actor", {
254
+ type: "Person",
255
+ name: "Anonymous"
256
+ }],
257
+ ["an untyped actor", { name: "Anonymous" }],
258
+ ["a blank node actor", { id: "_:b0" }]
259
+ ]) for (const mechanism of [
260
+ "http",
261
+ "lds",
262
+ "proof"
263
+ ]) test(`Activities with ${label} are not actorless (${mechanism})`, async () => {
264
+ const queue = new CapturingQueue();
265
+ const { federation, received } = setup({ queue });
266
+ const document = featureRequestDocument({ actor });
267
+ const response = await federation.fetch(mechanism === "http" ? await postSigned(document) : post(mechanism === "lds" ? await withLds(document) : await withRawProof(document)), { contextData: void 0 });
268
+ assert(response.status >= 400, `${response.status}`);
269
+ await processQueue(federation, queue);
270
+ assertEquals(received, []);
271
+ });
272
+ test("Queued actorless activities keep their interpretation and evidence", async () => {
273
+ const queue = new CapturingQueue();
274
+ const contextLoads = [];
275
+ const failOnce = { failed: false };
276
+ const { federation, received } = setup({
277
+ queue,
278
+ contextLoads,
279
+ failOnce
280
+ });
281
+ const document = featureRequestDocument({
282
+ "@context": [
283
+ "https://www.w3.org/ns/activitystreams",
284
+ "https://w3id.org/fep/7aa9",
285
+ MUTABLE_CONTEXT
286
+ ],
287
+ object: void 0,
288
+ tgt: TARGET.href
289
+ });
290
+ delete document.object;
291
+ assertEquals((await federation.fetch(await postSigned(document), { contextData: void 0 })).status, 202);
292
+ assertEquals(queue.messages.length, 1);
293
+ const message = queue.messages[0];
294
+ assertEquals(message.activity, document);
295
+ assertEquals(message.verifiedView?.source, "activity");
296
+ const loadsBefore = contextLoads.filter((url) => url === MUTABLE_CONTEXT).length;
297
+ await processQueue(federation, queue);
298
+ assertEquals(contextLoads.filter((url) => url === MUTABLE_CONTEXT).length, loadsBefore);
299
+ assert(failOnce.failed);
300
+ assertEquals(received.length, 1);
301
+ assertEquals(received[0].activity.objectId, TARGET);
302
+ assertEquals(received[0].activity.targetId, null);
303
+ assertEquals(received[0].authentication.requestSigners.map((p) => p.id.href), [SIGNER.href]);
304
+ assertEquals(received[0].authentication.expandedActivity.length, 1);
305
+ });
306
+ test("Queued actorless activities are dropped without evidence", async () => {
307
+ const queue = new CapturingQueue();
308
+ const { federation, received } = setup({ queue });
309
+ assertEquals((await federation.fetch(await postSigned(featureRequestDocument()), { contextData: void 0 })).status, 202);
310
+ const message = queue.messages.shift();
311
+ const { authentication: _, verifiedView: __, ...legacy } = message;
312
+ await federation.processQueuedTask(void 0, legacy);
313
+ assertEquals(received, []);
314
+ await federation.processQueuedTask(void 0, {
315
+ ...message,
316
+ authentication: {
317
+ verified: true,
318
+ requestSigners: [],
319
+ documentSigners: [],
320
+ originAuthenticated: []
321
+ }
322
+ });
323
+ assertEquals(received, []);
324
+ await federation.processQueuedTask(void 0, {
325
+ ...message,
326
+ verifiedView: { version: 2 }
327
+ });
328
+ assertEquals(received, []);
329
+ await federation.processQueuedTask(void 0, message);
330
+ assertEquals(received.length, 1);
331
+ });
332
+ test("Actorless activities too large to enqueue are processed immediately", async () => {
333
+ const queue = new CapturingQueue(512);
334
+ const { federation, received } = setup({ queue });
335
+ assertEquals((await federation.fetch(await postSigned(featureRequestDocument()), { contextData: void 0 })).status, 202);
336
+ assertEquals(queue.messages, []);
337
+ assertEquals(received.length, 1);
338
+ });
339
+ test("Activities processed by older queue workers are not processed again", async () => {
340
+ const kv = new MemoryKvStore();
341
+ const document = featureRequestDocument({ actor: SIGNER.href });
342
+ await kv.set([
343
+ "_fedify",
344
+ "activityIdempotence",
345
+ "https://example.com",
346
+ document.id
347
+ ], true);
348
+ const federation = createFederation({
349
+ kv,
350
+ documentLoaderFactory: () => mockDocumentLoader,
351
+ contextLoaderFactory: () => mockDocumentLoader,
352
+ authenticatedDocumentLoaderFactory: () => mockDocumentLoader
353
+ });
354
+ federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => new Person({ id: ctx.getActorUri(identifier) })).setKeyPairsDispatcher(() => [{
355
+ privateKey: rsaPrivateKey2,
356
+ publicKey: rsaPublicKey2.publicKey
357
+ }]);
358
+ let calls = 0;
359
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(FeatureRequest, () => {
360
+ calls++;
361
+ });
362
+ assertEquals((await federation.fetch(await postSigned(document), { contextData: void 0 })).status, 202);
363
+ assertEquals(calls, 0);
364
+ });
365
+ test("Activities with an actor expose their evidence", async () => {
366
+ const { federation, received } = setup();
367
+ assertEquals((await federation.fetch(await postSigned(featureRequestDocument({ actor: SIGNER.href })), { contextData: void 0 })).status, 202);
368
+ assertEquals(received.length, 1);
369
+ assertEquals(received[0].authentication.requestSigners.map((p) => [
370
+ p.id.href,
371
+ p.mechanism,
372
+ p.keyId?.href
373
+ ]), [[
374
+ SIGNER.href,
375
+ "httpSignatures",
376
+ RSA_KEY.href
377
+ ]]);
378
+ });
379
+ test("An author's proof is preferred to a relay's Linked Data Signature", async () => {
380
+ const { federation, received } = setup();
381
+ const relayKey = RELAY_KEY;
382
+ const document = await signJsonLd(await withRawProof(featureRequestDocument()), rsaPrivateKey2, relayKey, { contextLoader: mockDocumentLoader });
383
+ assertEquals((await federation.fetch(post(document), { contextData: void 0 })).status, 202);
384
+ assertEquals(received.length, 1);
385
+ assertEquals(received[0].authentication.documentSigners.map((p) => [p.id.href, p.mechanism]), [[SIGNER.href, "objectIntegrityProofs"]]);
386
+ });
387
+ test("A proof that cannot be verified for now is not superseded", async () => {
388
+ const { federation, received } = setup({ unavailable: ["https://example.com/person2", "https://example.com/person2#key4"] });
389
+ const relayKey = RELAY_KEY;
390
+ const document = await signJsonLd(await withRawProof(featureRequestDocument()), rsaPrivateKey2, relayKey, { contextLoader: mockDocumentLoader });
391
+ let status;
392
+ try {
393
+ status = (await federation.fetch(post(document), { contextData: void 0 })).status;
394
+ } catch {}
395
+ assert(status == null || status >= 500, `${status}`);
396
+ assertEquals(received, []);
397
+ });
398
+ test("Context.routeActivity() exposes the evidence of verified proofs", async () => {
399
+ const { federation, received } = setup();
400
+ const document = await withRawProof(featureRequestDocument({ actor: SIGNER.href }));
401
+ const activity = await FeatureRequest.fromJsonLd(document, {
402
+ documentLoader: mockDocumentLoader,
403
+ contextLoader: mockDocumentLoader
404
+ });
405
+ assert(await federation.createContext(new URL("https://example.com/"), void 0).routeActivity("alice", activity));
406
+ assertEquals(received.length, 1);
407
+ assertEquals(received[0].authentication.documentSigners.map((p) => [p.id.href, p.mechanism]), [[SIGNER.href, "objectIntegrityProofs"]]);
408
+ assertEquals(received[0].authentication.originAuthenticated, []);
409
+ });
410
+ test("An author's aliased proof is preferred to a relay's signature", async () => {
411
+ const { federation, received } = setup();
412
+ const document = await signJsonLd(await withRawProof(featureRequestDocument(), "integrity"), rsaPrivateKey2, RELAY_KEY, { contextLoader: mockDocumentLoader });
413
+ assertEquals((await federation.fetch(post(document), { contextData: void 0 })).status, 202);
414
+ assertEquals(received.length, 1);
415
+ assertEquals(received[0].authentication.documentSigners.map((p) => [p.id.href, p.mechanism]), [[SIGNER.href, "objectIntegrityProofs"]]);
416
+ });
417
+ for (const queued of [false, true]) test(`Context.routeActivity() keeps the fetched interpretation (queued: ${queued})`, async () => {
418
+ const queue = queued ? new CapturingQueue() : void 0;
419
+ const { federation, received } = setup({ queue });
420
+ const activityId = "https://example.com/person2/activities/fetched";
421
+ const fetchedDocument = {
422
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/fep/7aa9"],
423
+ id: activityId,
424
+ type: "FeatureRequest",
425
+ actor: SIGNER.href,
426
+ object: TARGET.href,
427
+ instrument: COLLECTION.href
428
+ };
429
+ const overriding = async (url, options) => {
430
+ if (url === "https://www.w3.org/ns/activitystreams") return {
431
+ contextUrl: null,
432
+ documentUrl: url,
433
+ document: { "@context": {
434
+ ...preloadedContexts[url]["@context"],
435
+ object: {
436
+ "@id": "https://www.w3.org/ns/activitystreams#target",
437
+ "@type": "@id"
438
+ }
439
+ } }
440
+ };
441
+ return await mockDocumentLoader(url, options);
442
+ };
443
+ const context = federation.createContext(new URL("https://example.com/"), void 0);
444
+ const activity = await FeatureRequest.fromJsonLd(fetchedDocument, {
445
+ documentLoader: mockDocumentLoader,
446
+ contextLoader: mockDocumentLoader
447
+ });
448
+ assert(await context.routeActivity("alice", activity, {
449
+ contextLoader: overriding,
450
+ documentLoader: async (url, options) => url === activityId ? {
451
+ contextUrl: null,
452
+ documentUrl: url,
453
+ document: structuredClone(fetchedDocument)
454
+ } : await mockDocumentLoader(url, options)
455
+ }));
456
+ if (queue != null) await processQueue(federation, queue);
457
+ assertEquals(received.length, 1);
458
+ const [{ activity: dispatched, authentication }] = received;
459
+ const root = authentication.expandedActivity[0];
460
+ assertEquals(root["https://www.w3.org/ns/activitystreams#object"]?.[0]?.["@id"], dispatched.objectId?.href);
461
+ assertEquals(dispatched.objectId, TARGET);
462
+ assertEquals(authentication.originAuthenticated.map((p) => p.id.href), [SIGNER.href]);
463
+ });
464
+ test("Activities whose contexts cannot be kept are processed immediately", async () => {
465
+ const queue = new CapturingQueue();
466
+ const { federation, received } = setup({ queue });
467
+ const cached = "https://remote.example/contexts/static-shared";
468
+ await expandDocument({
469
+ "@context": cached,
470
+ "z": "z"
471
+ }, (url, options) => url === cached ? Promise.resolve({
472
+ contextUrl: null,
473
+ documentUrl: url,
474
+ document: { "@context": { z: "https://example.com/z" } },
475
+ tag: "static"
476
+ }) : mockDocumentLoader(url, options));
477
+ const document = featureRequestDocument({ "@context": [
478
+ "https://www.w3.org/ns/activitystreams",
479
+ "https://w3id.org/fep/7aa9",
480
+ cached
481
+ ] });
482
+ assertEquals((await federation.fetch(await postSigned(document), { contextData: void 0 })).status, 202);
483
+ assertEquals(queue.messages, []);
484
+ assertEquals(received.length, 1);
485
+ });
486
+ //#endregion
487
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";