@fedify/fedify 2.4.0-dev.2253 → 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.
Files changed (110) hide show
  1. package/dist/{builder-DdUWFCwL.mjs → builder-CBNa1Uw2.mjs} +8 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{compound-proof-CcWDD5VB.mjs → compound-proof-CMPv4UK0.mjs} +20 -3
  6. package/dist/{context-CtI307_A.d.ts → context-7NyiqIcg.d.ts} +111 -2
  7. package/dist/{context-B6X-t8gI.d.cts → context-tsYPJ2Ew.d.cts} +111 -2
  8. package/dist/{deno-D9uhdIuD.mjs → deno-C8XaXmPB.mjs} +1 -1
  9. package/dist/{docloader-fbmPhCdS.mjs → docloader-CpdFLwAg.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -1
  11. package/dist/federation/compound-fanout.test.mjs +3 -3
  12. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  13. package/dist/federation/handler.test.mjs +76 -5
  14. package/dist/federation/hashlink-media.test.mjs +2 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/inbox-report.test.d.mts +2 -0
  17. package/dist/federation/inbox-report.test.mjs +475 -0
  18. package/dist/federation/metrics.test.mjs +1 -1
  19. package/dist/federation/middleware.test.mjs +7 -7
  20. package/dist/federation/mod.cjs +1 -1
  21. package/dist/federation/mod.d.cts +4 -3
  22. package/dist/federation/mod.d.ts +4 -3
  23. package/dist/federation/mod.js +1 -1
  24. package/dist/federation/outgoing-proof.test.mjs +5 -5
  25. package/dist/federation/portable-actor.test.mjs +3 -3
  26. package/dist/federation/portable-chain.test.mjs +3 -3
  27. package/dist/federation/portable-collection.test.mjs +4 -4
  28. package/dist/federation/portable-delivery.test.mjs +2 -2
  29. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  30. package/dist/federation/portable-inbox.test.mjs +67 -6
  31. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  32. package/dist/federation/portable-route-activity.test.mjs +2 -2
  33. package/dist/federation/portable.test.mjs +3 -3
  34. package/dist/federation/send.test.mjs +3 -3
  35. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  36. package/dist/federation/tasks/tasks.test.mjs +2 -2
  37. package/dist/federation/temporal.test.mjs +1 -1
  38. package/dist/federation/webfinger.test.mjs +1 -1
  39. package/dist/{federation-B858-Ylk.cjs → federation-D6giSGUT.cjs} +524 -118
  40. package/dist/{http-CUyBaVsQ.mjs → http-BqSbUEiQ.mjs} +141 -98
  41. package/dist/{http-Bp0MKAK3.cjs → http-Cki-npuA.cjs} +456 -116
  42. package/dist/{http-BoGE-nk7.js → http-OMtKlbx1.js} +439 -117
  43. package/dist/{http-DGx0KOws.d.ts → key-C1nv7_jD.d.ts} +638 -229
  44. package/dist/{http-BrwKINQn.d.cts → key-DYxuyd-s.d.cts} +638 -229
  45. package/dist/{key-BlTaKABr.mjs → key-DecAtABm.mjs} +30 -3
  46. package/dist/{kv-cache-Dmqd7s2a.mjs → kv-cache-CWWlMv-s.mjs} +1 -1
  47. package/dist/{kv-cache-BnWemxH2.cjs → kv-cache-D73yVcE5.cjs} +1 -1
  48. package/dist/{kv-cache-DhMzUXEE.js → kv-cache-J0v3UbUn.js} +1 -1
  49. package/dist/{ld-Dx8r_qJU.mjs → ld-UsN-TIPh.mjs} +40 -5
  50. package/dist/{metrics-DFkZkLrO.mjs → metrics-DWCn4VXX.mjs} +2 -1
  51. package/dist/{middleware-DC018B4u.js → middleware-BqzfIlUO.js} +524 -118
  52. package/dist/{middleware-B0RRI88k.mjs → middleware-CkjwiEBd.mjs} +512 -127
  53. package/dist/{middleware-BNhF-Ayx.mjs → middleware-DHn2q0L1.mjs} +1 -1
  54. package/dist/{mod-C7_2j7VT.d.cts → mod-BojP2Bw3.d.cts} +1 -1
  55. package/dist/{mod-Cy9VNHVw.d.ts → mod-CavhAgLw.d.cts} +5 -253
  56. package/dist/{mod-DoTWIc7F.d.cts → mod-CtpF2BU5.d.ts} +5 -253
  57. package/dist/{mod-B8MI1mpi.d.ts → mod-DEZXwV6J.d.ts} +1 -1
  58. package/dist/{mod-DFL6IxYQ.d.cts → mod-Dw-nPxQh.d.cts} +1 -1
  59. package/dist/{mod-BhTaDR5H.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
  60. package/dist/mod.cjs +4 -4
  61. package/dist/mod.d.cts +7 -7
  62. package/dist/mod.d.ts +7 -7
  63. package/dist/mod.js +4 -4
  64. package/dist/nodeinfo/handler.test.mjs +1 -1
  65. package/dist/{owner-B61jjp5K.mjs → owner-CEBn81LD.mjs} +3 -3
  66. package/dist/{portable-collection-DhEA_nWT.cjs → portable-collection-DqTUBKY7.cjs} +1 -1
  67. package/dist/{portable-collection-BnktjQ04.js → portable-collection-ZkUNjtb7.js} +1 -1
  68. package/dist/{portable-collection-CyBJDEzs.mjs → portable-collection-qFSYw4TG.mjs} +2 -2
  69. package/dist/{portable-key-CPN0QZSA.mjs → portable-key-LlMkP-vz.mjs} +1 -1
  70. package/dist/{proof-CZrjvoJ3.mjs → proof-BHRf_jMG.mjs} +142 -22
  71. package/dist/{send-enxOnmZo.mjs → send-DjG8DDU_.mjs} +3 -3
  72. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  73. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  74. package/dist/sig/compound-proof-verification.test.mjs +19 -2
  75. package/dist/sig/compound-proof.test.mjs +2 -2
  76. package/dist/sig/http.test.mjs +2 -2
  77. package/dist/sig/key.test.mjs +1 -1
  78. package/dist/sig/ld.test.mjs +2 -2
  79. package/dist/sig/mitra-receive-interop.test.d.mts +2 -0
  80. package/dist/sig/mitra-receive-interop.test.mjs +260 -0
  81. package/dist/sig/mitra-send-interop.test.d.mts +2 -0
  82. package/dist/sig/mitra-send-interop.test.mjs +461 -0
  83. package/dist/sig/mod.cjs +2 -2
  84. package/dist/sig/mod.d.cts +2 -2
  85. package/dist/sig/mod.d.ts +2 -2
  86. package/dist/sig/mod.js +2 -2
  87. package/dist/sig/owner.test.mjs +1 -1
  88. package/dist/sig/portable-collection.test.mjs +1 -1
  89. package/dist/sig/portable-interop.test.d.mts +2 -0
  90. package/dist/sig/portable-interop.test.mjs +156 -0
  91. package/dist/sig/portable-key.test.mjs +6 -6
  92. package/dist/sig/proof.test.mjs +161 -3
  93. package/dist/sig/tootik-request-interop.test.d.mts +2 -0
  94. package/dist/sig/tootik-request-interop.test.mjs +190 -0
  95. package/dist/sig/tootik-send-interop.test.d.mts +2 -0
  96. package/dist/sig/tootik-send-interop.test.mjs +331 -0
  97. package/dist/sig/verification.test.d.mts +2 -0
  98. package/dist/sig/verification.test.mjs +310 -0
  99. package/dist/{temporal-CBVv0xRB.mjs → temporal-BRFH_dte.mjs} +1 -1
  100. package/dist/testing/kv.test.mjs +1 -1
  101. package/dist/testing/mod.d.mts +315 -28
  102. package/dist/utils/docloader.test.mjs +2 -2
  103. package/dist/utils/kv-cache.test.mjs +1 -1
  104. package/dist/utils/mod.cjs +1 -1
  105. package/dist/utils/mod.d.cts +1 -1
  106. package/dist/utils/mod.d.ts +1 -1
  107. package/dist/utils/mod.js +1 -1
  108. package/dist/verification-C3954hQh.mjs +5 -0
  109. package/dist/verification-Dpfcw8ml.mjs +105 -0
  110. package/package.json +6 -6
@@ -0,0 +1,156 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
+ import "../std__assert-DvMDRPes.mjs";
6
+ import { t as assert } from "../assert-D1Eafmd_.mjs";
7
+ import { c as verifyPortableObjectProof, d as verifyProof } from "../proof-BHRf_jMG.mjs";
8
+ import { n as getKeyOwner } from "../owner-CEBn81LD.mjs";
9
+ import { t as verifyPortableObject } from "../portable-collection-qFSYw4TG.mjs";
10
+ import { Application, DataIntegrityProof, lookupObject } from "@fedify/vocab";
11
+ import { mockDocumentLoader, test } from "@fedify/fixture";
12
+ //#region test-vectors/fep-ef61/tootik-v0.25.4/actor.json
13
+ var actor_default = {
14
+ "@context": [
15
+ "https://www.w3.org/ns/activitystreams",
16
+ "https://w3id.org/security/data-integrity/v1",
17
+ "https://w3id.org/security/v1"
18
+ ],
19
+ id: "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor",
20
+ type: "Application",
21
+ inbox: "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor/inbox",
22
+ outbox: "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor/outbox",
23
+ preferredUsername: "actor",
24
+ followers: "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor/followers",
25
+ publicKey: {
26
+ "id": "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor#main-key",
27
+ "owner": "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor",
28
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr70sLuCyvDBKblca6Jsk\nJW5ZmTpmyFK4gY6dpDkjvsokBjTR6oUO5GF7QGT4xGaGDtnzhd+K0/IE2Jo2cbBu\nTAK92xo3qB4nL+0XIfqXK/ju/pIo4ClZ1oHYNeJnmjGDAJmZmaf9/b0sd0osfTPX\nWSqAkgZuxZYrWSXKs6WYuEJof5YfTHgH5PBzwtQGyexZKYkumyrq5lazeEvqBLyx\nyNc5u6LdkkHP+HR9DJIQ3CDv6d1yQ7ELiP5pXvKRV9jJKlBQ1TK1vlMGC8GcBlDy\nQT0UsbM/1lRbxnbbqiwY6rFlT6FpHd5cl9tblGU6sF9gyCDATCHAdZ/2laUzParD\n+QIDAQAB\n-----END PUBLIC KEY-----\n"
29
+ },
30
+ icon: [{
31
+ "type": "Image",
32
+ "mediaType": "image/gif",
33
+ "url": "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor/icon.gif"
34
+ }],
35
+ manuallyApprovesFollowers: false,
36
+ assertionMethod: [{
37
+ "id": "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor#ed25519-key",
38
+ "type": "Multikey",
39
+ "controller": "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor",
40
+ "publicKeyMultibase": "z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE"
41
+ }, {
42
+ "id": "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor#ml-dsa-44-key",
43
+ "type": "Multikey",
44
+ "controller": "https://tootik.example/.well-known/apgateway/did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE/actor",
45
+ "publicKeyMultibase": "ukCRpmFGeqQvqCAsbrRIKdfH1ZD2Y-2NiDr5tmgYFceiWCipFyeJKL65fEBcHa3gIBJbuzwvTfFeiMppDxvQySHhXFMf12o2mZ1d9Rw5fP9JKxSgCH7TyjeXEsvN6Zv_3q3ignCU3UxQKL5q2Yks9bfQkDxPJdDvzgyP5D0lDWulx1I9wyQS_f0SXG-Muph2rHBIT_u30ZuN7PHNNk7VAv4AWn7DlbJ6h2871YgFD7YHmtZiq_YCWerBe9TPBAoIOYEY1LV_nPVEe5xDIBlQ2EXYcfJviKQ1rB1iP4c0uoxdGm1RLDOXd65EDmtzil_R1Y5Le4s5td6AV2iFanIQtoJabH1IfL3Ju9n1nM-X3T8YM5FCngvoFME7UxhUHUggZ7vbWy_hy99tRvlaPG3U2OogtyPELx1o91itRjVH276UB8frLb06oJj6RzHE8pwfImH7POETjxEMPzfX4RA7BQVge6TU3fiBx4V4wmofJ3eiMu6yo497mUjJMiFjnt4AC1xiY8_XzdAauJag55HjKDf1xKNHIpQe2IE_n2zJvjfh__coWwl5qJLyszAVBZTn-MTg8nS--PTZMZPaDJSwk9NAT6qdCAnaP5PPkoWlTedCVbDNE-az45x9rKs3sob1OVoJW-2Ogf0S37noPe3L2lfUidr32k5fXPCsg3GfB0F5ZS9sQ-ZBzQvgchArohI1DnX9cbObTj4bGrJu_N9SPFLp0jYQb5bujeIDdpiVu46PVbv4fV4gxQAt4XPZLExZq_WQhKVVn3A89NU6qwxKsAp1LWoouehQSSNaC-Wif7ev5vaCSE0eeTOCO0Ip1URirY7hKhjbp-hfAMx_Vnoj5gQeiRQkUW2oxkgXEpfdzJDvVoeaTmJ_k3soJlJYjp9ZJAc9qn8ygsFpRKFMUYZQL3Wcj7vFxjlX_42a-5p0_0UHA7JjK3UlvE_nG9cIeoqU-WgygEU9rf1CCJflppbwXMFmBQvJ1oMDoutbZTuDIdnRO5DVQ4uNEPyT3p_kv0smEGU3lzZERTYHPM-CNEF5Pk2rrVea9WxpC0TG2njHxmHsO0nCWmUgoB3cAMWpby-YBaen1xvXt5po7ge5KD3SC_cXMSLAJem_ZMVWtR070zXCJdsoB-GN8nFTchyjPpI6SLxXcVwWXNEzI_dEvcx0YTEwlI9OEDuF2rizU4UzZMIT_Ec5OgEehh-o0MLpgLRvLo15TOz0FFZfaKYWfVS6CEBxHg0g3XwmvbrhA47ZJNfLYniPV4K7ELTp3nsoe9xZo2yORy8KiYUV9AETu90oZd-ZnZG_J1IXY7HmzT3DQ5rXWRMHx1Uym_SU7TCk0hIvFpta012lS_Hv8OZ2cnXC70VUx8_LS27ICE4k6xJbwxZCZZ0oLZ_XSicvg9vOP_N7KWbR_Jc7sxjpIDc2IY9EUNn0FiGs6IDy-YJJ5kq31N0Di28uYuX910sAbymPGl--SD1feo4-SBXA9qejrfq85iHcgKWpOVmID1HxulHKZRcKM-T_TfEVlHUPvkiPo61xda2ak1ni05GpPc_iknAFUbM1D6eIh4mFSxdnLTDXVkvNByPb8QCaZCwOSiN5bDy-CvHseH7uuDrwilnqgMruLQmVj0oj2HM1vsjQqXMdVWi6dtfaj1qqyf8Cti_bt7m0qPKe4B6Mc9ALBFtk6gGLjAuygg4_WSCAr8WPnDjrLEI7Xmo_uFvPRsDEfQUCvXVP_vapaCr6vSPvQ-hJmXy2-u5qe"
46
+ }],
47
+ "implements": [
48
+ {
49
+ "href": "https://datatracker.ietf.org/doc/html/rfc9421",
50
+ "name": "RFC-9421: HTTP Message Signatures"
51
+ },
52
+ {
53
+ "href": "https://datatracker.ietf.org/doc/html/rfc9421#name-eddsa-using-curve-edwards25",
54
+ "name": "RFC-9421 signatures using the Ed25519 algorithm"
55
+ },
56
+ {
57
+ "href": "https://c2sp.org/httpsig-pq@v1.0.0#ml-dsa-44",
58
+ "name": "RFC-9421 signatures using the ml-dsa-44 algorithm"
59
+ }
60
+ ],
61
+ gateways: ["https://tootik.example"],
62
+ proof: {
63
+ "@context": [
64
+ "https://www.w3.org/ns/activitystreams",
65
+ "https://w3id.org/security/data-integrity/v1",
66
+ "https://w3id.org/security/v1"
67
+ ],
68
+ "type": "DataIntegrityProof",
69
+ "cryptosuite": "eddsa-jcs-2022",
70
+ "verificationMethod": "did:key:z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE#z6MknxT9xX8cmnsEqBk2u74aYaGQNjVQL1eHxhn1qb27p5vE",
71
+ "proofPurpose": "assertionMethod",
72
+ "proofValue": "zjGdNqPpqgx45ecMAz8jY3gVRrTphG5Cn7v9EiBKn7WyeHtdvTYPSkzyvWqZqfQuw92Kn3pD58qXw6wFTLAvkWee",
73
+ "created": "2026-09-30T11:57:45Z"
74
+ }
75
+ };
76
+ //#endregion
77
+ //#region src/sig/portable-interop.test.ts
78
+ const options = {
79
+ contextLoader: mockDocumentLoader,
80
+ documentLoader() {
81
+ throw new TypeError("The captured did:key proof must resolve locally");
82
+ }
83
+ };
84
+ test("tootik v0.25.4 actor has a valid DID proof", async () => {
85
+ const key = await verifyProof(actor_default, await DataIntegrityProof.fromJsonLd(actor_default.proof, options), options);
86
+ assert(key != null);
87
+ assertEquals(key.id?.href, actor_default.proof.verificationMethod);
88
+ });
89
+ test("tootik v0.25.4 actor proof rejects tampering", async () => {
90
+ const proof = await DataIntegrityProof.fromJsonLd(actor_default.proof, options);
91
+ assertEquals(await verifyProof({
92
+ ...actor_default,
93
+ preferredUsername: "tampered"
94
+ }, proof, options), null);
95
+ });
96
+ test("tootik v0.25.4 actor retains its unmapped gateways", async () => {
97
+ const original = structuredClone(actor_default);
98
+ assertEquals(actor_default.gateways, ["https://tootik.example"]);
99
+ const parsed = await Application.fromJsonLd(actor_default, options);
100
+ assertEquals(parsed.id?.href, actor_default.id);
101
+ assertEquals(parsed.gateways, [new URL("https://tootik.example")]);
102
+ assert((await verifyPortableObjectProof(actor_default, options)).verified);
103
+ assertEquals(actor_default, original);
104
+ });
105
+ const loadedOptions = {
106
+ contextLoader: mockDocumentLoader,
107
+ documentLoader(url) {
108
+ assertEquals(url.split("#")[0], actor_default.id);
109
+ return Promise.resolve({
110
+ document: structuredClone(actor_default),
111
+ documentUrl: actor_default.id,
112
+ contextUrl: null
113
+ });
114
+ }
115
+ };
116
+ test("tootik v0.25.4 actor can be looked up and own a gateway key", async () => {
117
+ const parsed = await lookupObject(actor_default.id, {
118
+ ...loadedOptions,
119
+ verifyPortableObject: verifyPortableObjectProof
120
+ });
121
+ assert(parsed instanceof Application);
122
+ assertEquals(parsed.gateways, [new URL("https://tootik.example")]);
123
+ const owner = await getKeyOwner(new URL(actor_default.publicKey.id), loadedOptions);
124
+ assert(owner != null);
125
+ assertEquals(owner.id?.href, actor_default.id);
126
+ assertEquals(owner.gateways, [new URL("https://tootik.example")]);
127
+ });
128
+ test("tootik v0.25.4 actor authenticates its gateway for collections", async () => {
129
+ const result = await verifyPortableObject({
130
+ "@context": "https://www.w3.org/ns/activitystreams",
131
+ id: actor_default.outbox,
132
+ type: "OrderedCollection",
133
+ attributedTo: actor_default.id,
134
+ orderedItems: []
135
+ }, {
136
+ ...loadedOptions,
137
+ documentUrl: new URL(actor_default.outbox)
138
+ });
139
+ assert(result.verified);
140
+ assertEquals(result.method, "gateway");
141
+ if (result.method === "gateway") assertEquals(result.gateway, new URL("https://tootik.example"));
142
+ });
143
+ test("tootik v0.25.4 actor rejects tampered gateways", async () => {
144
+ assertEquals(await verifyPortableObjectProof({
145
+ ...actor_default,
146
+ gateways: ["https://attacker.example"]
147
+ }, options), {
148
+ verified: false,
149
+ reason: {
150
+ type: "invalidProof",
151
+ proofIndex: 0
152
+ }
153
+ });
154
+ });
155
+ //#endregion
156
+ export {};
@@ -2,13 +2,13 @@ import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
- import { a as fetchKeyDetailed, i as fetchKey } from "../key-BlTaKABr.mjs";
6
- import { h as verifyJsonLd, p as signJsonLd } from "../ld-Dx8r_qJU.mjs";
7
- import { d as verifyProof, r as createProof } from "../proof-CZrjvoJ3.mjs";
8
- import { n as resolvePortableActorKey } from "../portable-key-CPN0QZSA.mjs";
9
- import { f as verifyRequest, l as signRequest, p as verifyRequestDetailed } from "../http-CUyBaVsQ.mjs";
5
+ import { a as fetchKeyDetailed, i as fetchKey } from "../key-DecAtABm.mjs";
6
+ import { h as verifyJsonLd, p as signJsonLd } from "../ld-UsN-TIPh.mjs";
7
+ import { d as verifyProof, r as createProof } from "../proof-BHRf_jMG.mjs";
8
+ import { n as resolvePortableActorKey } from "../portable-key-LlMkP-vz.mjs";
9
+ import { f as verifyRequest, l as signRequest, p as verifyRequestDetailed } from "../http-BqSbUEiQ.mjs";
10
10
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
11
- import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-B61jjp5K.mjs";
11
+ import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-CEBn81LD.mjs";
12
12
  import { t as KvKeyCache } from "../keycache-Da8A8mZa.mjs";
13
13
  import { Create, CryptographicKey, Multikey, Note, Person } from "@fedify/vocab";
14
14
  import { FetchError, encodeMultibase, exportDidKey, exportSpki, parseIri, preloadedContexts } from "@fedify/vocab-runtime";
@@ -9,15 +9,17 @@ import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
9
9
  import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
11
11
  import { t as assert } from "../assert-D1Eafmd_.mjs";
12
+ import { a as verificationObservation } from "../verification-Dpfcw8ml.mjs";
12
13
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
13
- import { a as signObject, c as verifyPortableObjectProof, d as verifyProof, i as hasProofLike, r as createProof, s as verifyObject } from "../proof-CZrjvoJ3.mjs";
14
- import { f as verifyRequest } from "../http-CUyBaVsQ.mjs";
14
+ import { a as signObject, c as verifyPortableObjectProof, d as verifyProof, i as hasProofLike, o as verifyMapLocalProof, r as createProof, s as verifyObject } from "../proof-BHRf_jMG.mjs";
15
+ import { f as verifyRequest } from "../http-BqSbUEiQ.mjs";
15
16
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
16
- import { t as getAuthenticatedDocumentLoader } from "../docloader-fbmPhCdS.mjs";
17
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-CpdFLwAg.mjs";
17
18
  import { Create, DataIntegrityProof, Document, Multikey, Note, PUBLIC_COLLECTION, Place, Translation } from "@fedify/vocab";
18
19
  import { decodeMultibase, encodeMultibase, exportDidKey, exportMultibaseKey, formatIri, importMultibaseKey, parseIri } from "@fedify/vocab-runtime";
19
20
  import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
20
21
  import { decodeHex } from "byte-encodings/hex";
22
+ import jsonld from "@fedify/vocab-runtime/jsonld";
21
23
  import serialize from "json-canon";
22
24
  //#region src/sig/proof.test.ts
23
25
  const fep8b32TestVectorPrivateKey = await crypto.subtle.importKey("jwk", {
@@ -797,6 +799,77 @@ test("verifyProof() records verification duration metric", async (t) => {
797
799
  assertFalse("object_integrity_proofs.cryptosuite" in measurements[0].attributes);
798
800
  });
799
801
  });
802
+ test("proof verification processes raw declaration contexts only for observers", async (t) => {
803
+ const options = {
804
+ documentLoader: mockDocumentLoader,
805
+ contextLoader: mockDocumentLoader
806
+ };
807
+ const proofUrl = `ap://did:key:${portableDidMethod}/proofs/diagnostics`;
808
+ const signed = await signPortableJsonLd({
809
+ "@context": portableContext,
810
+ id: `ap://did:key:${portableDidMethod}/objects/diagnostics`,
811
+ type: "Note",
812
+ attributedTo: `ap://did:key:${portableDidMethod}/actor`,
813
+ content: "Observed proof"
814
+ }, { proofOptions: { id: proofUrl } });
815
+ const rawProof = signed.proof;
816
+ const proof = await DataIntegrityProof.fromJsonLd(rawProof, options);
817
+ const referenced = { ...signed };
818
+ delete referenced.proof;
819
+ referenced["https://w3id.org/security#proof"] = { "@graph": [{ "@id": proofUrl }] };
820
+ const cases = [
821
+ {
822
+ name: "object",
823
+ verify: async (options) => await verifyObject(Note, structuredClone(signed), options) != null
824
+ },
825
+ {
826
+ name: "portable",
827
+ verify: async (options) => (await verifyPortableObjectProof(structuredClone(signed), options)).verified
828
+ },
829
+ {
830
+ name: "map-local",
831
+ verify: async (options) => await verifyMapLocalProof(structuredClone(signed), options) != null
832
+ },
833
+ {
834
+ name: "proof",
835
+ verify: async (options) => await verifyProof(structuredClone(signed), proof, options) != null
836
+ },
837
+ {
838
+ name: "remote proof",
839
+ verify: async (options) => await verifyObject(Note, structuredClone(referenced), {
840
+ ...options,
841
+ documentLoader: (url) => Promise.resolve({
842
+ contextUrl: null,
843
+ documentUrl: url,
844
+ document: structuredClone(rawProof)
845
+ })
846
+ }) != null
847
+ }
848
+ ];
849
+ for (const { name, verify } of cases) await t.step(name, async () => {
850
+ const original = jsonld.processContext;
851
+ let calls = 0;
852
+ jsonld.processContext = (...args) => {
853
+ calls++;
854
+ return original(...args);
855
+ };
856
+ try {
857
+ assert(await verify(options));
858
+ const unobservedCalls = calls;
859
+ calls = 0;
860
+ assert(await verify({
861
+ ...options,
862
+ [verificationObservation]: {
863
+ attempts: [],
864
+ attempt: { checks: [] }
865
+ }
866
+ }));
867
+ assert(calls > unobservedCalls, "Raw declaration processing should be absent from unobserved verification");
868
+ } finally {
869
+ jsonld.processContext = original;
870
+ }
871
+ });
872
+ });
800
873
  test("verifyPortableObjectProof()", async (t) => {
801
874
  const options = {
802
875
  documentLoader() {
@@ -1032,6 +1105,91 @@ test("verifyPortableObjectProof()", async (t) => {
1032
1105
  reason: { type: "invalidGateways" }
1033
1106
  });
1034
1107
  });
1108
+ await t.step("verifies JCS-signed actors with unmapped gateways", async () => {
1109
+ for (const id of [`ap+ef61://${portableDid}/actor`, `https://gateway.example/.well-known/apgateway/${portableDid}/actor`]) {
1110
+ const signed = await signPortableJsonLd({
1111
+ "@context": portableContext,
1112
+ id,
1113
+ type: "Person",
1114
+ inbox: `${id}/inbox`,
1115
+ outbox: `${id}/outbox`,
1116
+ gateways: ["https://gateway.example", "https://other.example"]
1117
+ });
1118
+ const original = structuredClone(signed);
1119
+ assert((await verifyPortableObjectProof(signed, options)).verified);
1120
+ assertEquals(signed, original);
1121
+ assertEquals(await verifyPortableObjectProof({
1122
+ ...signed,
1123
+ gateways: ["https://attacker.example"]
1124
+ }, options), {
1125
+ verified: false,
1126
+ reason: {
1127
+ type: "invalidProof",
1128
+ proofIndex: 0
1129
+ }
1130
+ });
1131
+ }
1132
+ });
1133
+ await t.step("rejects malformed unmapped actor gateways", async () => {
1134
+ const id = `ap+ef61://${portableDid}/actor`;
1135
+ for (const gateways of [
1136
+ [],
1137
+ ["https://gateway.example", 42],
1138
+ [{ bad: "https://gateway.example" }],
1139
+ ["ftp://gateway.example"],
1140
+ ["https://gateway.example/path"],
1141
+ ["https://gateway.example/?query"],
1142
+ ["https://gateway.example/#fragment"],
1143
+ ["https://user:password@gateway.example"]
1144
+ ]) assertEquals(await verifyPortableObjectProof(await signPortableJsonLd({
1145
+ "@context": portableContext,
1146
+ id,
1147
+ type: "Person",
1148
+ inbox: `${id}/inbox`,
1149
+ outbox: `${id}/outbox`,
1150
+ gateways
1151
+ }), options), {
1152
+ verified: false,
1153
+ reason: { type: "invalidGateways" }
1154
+ }, JSON.stringify(gateways));
1155
+ });
1156
+ await t.step("does not replace context-defined gateway semantics", async () => {
1157
+ const id = `ap+ef61://${portableDid}/actor`;
1158
+ const actor = {
1159
+ "@context": portableContext,
1160
+ id,
1161
+ type: "Person",
1162
+ inbox: `${id}/inbox`,
1163
+ outbox: `${id}/outbox`,
1164
+ gateways: ["https://gateway.example"]
1165
+ };
1166
+ const property = "https://w3id.org/fep/ef61/gateways";
1167
+ for (const extra of [
1168
+ { [property]: { "@list": [] } },
1169
+ { [property]: { "@list": [{ "@id": "https://bad.example/path" }] } },
1170
+ { "@context": [...portableContext, { gateways: null }] },
1171
+ { "@context": [...portableContext, { gateways: "https://other.example/gateways" }] },
1172
+ { "@context": [...portableContext, { Person: {
1173
+ "@id": "https://www.w3.org/ns/activitystreams#Person",
1174
+ "@context": { gateways: null }
1175
+ } }] }
1176
+ ]) assertEquals(await verifyPortableObjectProof(await signPortableJsonLd({
1177
+ ...actor,
1178
+ ...extra
1179
+ }), options), {
1180
+ verified: false,
1181
+ reason: { type: "invalidGateways" }
1182
+ }, JSON.stringify(extra));
1183
+ assert((await verifyPortableObjectProof(await signPortableJsonLd({
1184
+ ...actor,
1185
+ gateways: ["https://bad.example/path"],
1186
+ [property]: { "@list": [{ "@id": "https://canonical.example" }] }
1187
+ }), options)).verified);
1188
+ assertEquals(await verifyPortableObjectProof(await signPortableJsonLd(actor, { proofOptions: { cryptosuite: "eddsa-rdfc-2022" } }), options), {
1189
+ verified: false,
1190
+ reason: { type: "invalidGateways" }
1191
+ });
1192
+ });
1035
1193
  await t.step("checks gateways only for FEP-2277 actors", async () => {
1036
1194
  assert((await verifyPortableObjectProof(await signPortableJsonLd({
1037
1195
  "@context": portableContext,
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,190 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
+ import "../std__assert-DvMDRPes.mjs";
7
+ import { t as assert } from "../assert-D1Eafmd_.mjs";
8
+ import { c as verifyPortableObjectProof } from "../proof-BHRf_jMG.mjs";
9
+ import { p as verifyRequestDetailed } from "../http-BqSbUEiQ.mjs";
10
+ import { o as createFederation } from "../middleware-CkjwiEBd.mjs";
11
+ import { t as doesActorOwnKey } from "../owner-CEBn81LD.mjs";
12
+ import { Follow, Person } from "@fedify/vocab";
13
+ import { mockDocumentLoader, test } from "@fedify/fixture";
14
+ import { encodeHex } from "byte-encodings/hex";
15
+ //#region test-vectors/fep-ef61/tootik-v0.25.4/follow/actor.json
16
+ var actor_default = {
17
+ "@context": [
18
+ "https://www.w3.org/ns/activitystreams",
19
+ "https://w3id.org/security/data-integrity/v1",
20
+ "https://w3id.org/security/v1"
21
+ ],
22
+ id: "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor",
23
+ type: "Person",
24
+ inbox: "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/inbox",
25
+ outbox: "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/outbox",
26
+ preferredUsername: "alice",
27
+ followers: "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/followers",
28
+ publicKey: {
29
+ "id": "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor#main-key",
30
+ "owner": "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor",
31
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyLpYZnhnVih6ToB4UThp\nEZIcxbBcGng71XeEfiR42v+cCyUL+jzQm12x/I0a3FNh4dzIicMvtbmLewQ/HscR\nUNIxAFHF8GqshIjg2tDAmGswTQDsvTzBsaEixXtL0qTTX8X0tFzVKJEsMJJ99jzg\nodERW73G55WwpJYyI4v1RT3G5tU3p0tmD52hhCLCRLRCPKy75iz416nCwDt5sA8B\nowVE5n/J+tjqG3umnnI3gJpTtuJxTyB/SVjLjnC604RKowwQQTHGAmcl3E5F3IrK\nfnmfqdXNCrNbfvJFFAWB+XNXFB0I+fJbGUZAOYMfjhMGEpPzzkKI8cI1eR+Z5DeZ\nGQIDAQAB\n-----END PUBLIC KEY-----\n"
32
+ },
33
+ icon: [{
34
+ "type": "Image",
35
+ "mediaType": "image/gif",
36
+ "url": "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/icon.gif"
37
+ }],
38
+ manuallyApprovesFollowers: false,
39
+ assertionMethod: [{
40
+ "id": "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor#ed25519-key",
41
+ "type": "Multikey",
42
+ "controller": "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor",
43
+ "publicKeyMultibase": "z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR"
44
+ }, {
45
+ "id": "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor#ml-dsa-44-key",
46
+ "type": "Multikey",
47
+ "controller": "https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor",
48
+ "publicKeyMultibase": "ukCQjRdBDVD0QfnNnj4wFAJE82TmNXoNQ80Rf6s3ivFXlFCzb5_tZHVhfsR5JysgCbOqYPnXYyXIgXAm_klgaj1a4_MB-KLhmueucnnMKUjl60ORt4RG2fDAjZoCzBy-m7PMX5hByOfuDTXbAhSFhG44xvBumN2x877PLudpMqhe9hhBDwgL4NTxUWOP3xJR4KhO0GMzjA6r4HGw0F2Mw3cYQNJs2t40k9w_Yt1qiVfyr4Qk0pk5vnZEkNFF71OBm-SUUSPtrHQEo02HhElN7uFE8wgYmziC_RqzrvyWAi9XQofgDGps2d-xXQew28NkyhAjv-7yIwd3ukjm_0UxjslLFpcjRAW7hNkrIm6oxV_Wv3ynpUVII7uc6v3Q3hkTtJxL4bcsl-oOpZKAqJide5A09G0XE_PvPrnVU3n9hH1HIhs4aWUeo3xda_gLfA5yoavGhqh1oOKnT3yiZYysjKt4GbdcLE_AMCDvWAqLdHTywnl5CG_9gUa3GlgjEsrJK8-SWCXoVgjQmGtdLWWMXTwzAwNZZwkoiR72v0uVOAzE54U_0oz6_Fowdgr29o0qu8TTb6NY70fF34XMWDDlKOWpgo7s5ejL15jfsrzcLhzP1xQlcBqQRZIsSQ_e0z_6mPJlYMu-UjsJIu4SM1__mn48TAQbw__B7VtClD_wGpedwF8gPpATziNsmOVvK8LKtYt983i2bzLYlV3Um31R4r-0i13JY6tBDC3SXkbOyOW3HkfecC1EJ11ffqoBwYT6KJGYH1pd8YR7cvwQn6C4kItLQnoUcIPBY7hKmw6B78e0ZexMMZlmQkW_JEHpgRi0Fb9atgSfGs8IIHTZGOkU5MGUUH1Evqz995dqFjoUxZaCGNKE2Rh-c0F4UdVToex7ZniQOFL1-D16mTloNwPgA9tvd7SUnSY2fr_E4Vyt-Rvcy_azBWBZWPzKdOy9u0OEW8uLeX680qp1Qbx97zDwCsiNAoTRMPyS_azqB12RThb3L-o28UXw5cPyeW9NSSN3G4yaweaIDmQ16vB1biKknrOU9XNVwkwZ4ilzYoESkGmQoCOomrNjGclx99d42S_7laMBlOkQcy846EyxhaHpkOj7t5LIU4Vs1TKeXXDV08wZQHz_r557uwtly8ltLHCg1pWm6MJNRgkhcGBhlE7Jt3gybA9HkYrg_x3NTR-uQocckeUbZZI1Q0aQuMa_F6_qC8WJOAkGDfERlFs7-gVjqYtTSHFLky3B_CgZukp_YZ-HLDlbxAO2qw_itfcn3fn95LLPUxiOZvB49xyha_z2r4rHt9R0BymvDM9ufp-pg2CiLBe3cPiy1Ck_BE7kvA7TyjShi3YyQIGfUcwAoWXOj4lKwejeqUmsjJ-gpD2iVJ3frWao9Cu846ubamb6wQZf5faAx6yTJsHLYdeDBqqJRCMSVEnOYHf6SaNfzdE8EFqCJ7LP2IYDnSFaiZovhja6BJBM4fphH27mnkrQ10whn35XX843QVAOLJa5fn4GoIwkSFY0PSL1J4MT1MobKfA0lROfF5fE29sKLRt-wbnHveAFE_CRiyzW6IFQO9flnSmn83VxigN2hv_xD51KX6p8dCzX1QLl0S9fWF1K9iHkq4ulz6slvypfacesLLGvbVnAWX491PPq9LIOHjCLoZbr1Ap8vkLw61-XvDFSBHUxGKvrlpPOKO5V3zop0sT_QiEymx9DpGAGrbYiyDEC3LhNmD14DxRdRqQLG3wW6HFTMJtl6"
49
+ }],
50
+ gateways: ["https://tootik.example"],
51
+ proof: {
52
+ "@context": [
53
+ "https://www.w3.org/ns/activitystreams",
54
+ "https://w3id.org/security/data-integrity/v1",
55
+ "https://w3id.org/security/v1"
56
+ ],
57
+ "type": "DataIntegrityProof",
58
+ "cryptosuite": "eddsa-jcs-2022",
59
+ "verificationMethod": "did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR#z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR",
60
+ "proofPurpose": "assertionMethod",
61
+ "proofValue": "z2G5haKKAJk3rfL8njG9V2jybnPuHrXjR8ipPBjDSFim9cFuJ9xEMEWBLQLdBmoQKXmHhqXfheadf8EaNW3cEpX8s",
62
+ "created": "2026-09-30T13:25:15Z"
63
+ }
64
+ };
65
+ //#endregion
66
+ //#region test-vectors/fep-ef61/tootik-v0.25.4/follow/request.json
67
+ var url = "https://fedify.example:18444/users/alice/inbox";
68
+ var method = "POST";
69
+ var headers = [
70
+ ["accept-encoding", "gzip"],
71
+ ["content-length", "1033"],
72
+ ["content-type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""],
73
+ ["date", "Wed, 30 Sep 2026 13:30:43 GMT"],
74
+ ["digest", "SHA-256=ZlDZCndYkYcCvPMpFmRZJE+QT1SVtwope+KPaV5U/r8="],
75
+ ["host", "fedify.example:18444"],
76
+ ["signature", "keyId=\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date content-type digest\",signature=\"Rqd+mtn7knlPY5SXUE2Zz93ICDgBeamFu5v8SagsII8vYcBTX1gan+S0oded4+f4gLB0cs+xdlaqeAUBj3BtWYBJ/cEcCWDFp+bB0xhq/grE/xt4fkdgCCfXWSU2EnJuFCEmUoB1zRemNvTOHwZV4wDrkLVwQsIxrUIjPbMFTVGyMjnpY71Ydta3rdia7VW0VWK3gEC4FJ6YweFxV52dmcOTmA3Qj1h0riHHVEZXs5UOzPJLL6FJQoz8ezyjE7LanxSoXpeOSHa5K1fAo9i35eJr1/eQdFpHt7ftabpv3+6u6MhCFAt2lJs3XJbJZL8DYNkYwJNAYi9fqmH8JOVuMw==\""],
77
+ ["user-agent", "tootik/0.25.4"]
78
+ ];
79
+ var capturedAt$1 = "2026-09-30T13:30:43.325Z";
80
+ //#endregion
81
+ //#region test-vectors/fep-ef61/tootik-v0.25.4/follow/metadata.json
82
+ var capturedAt = "2026-09-30T13:30:43.325Z";
83
+ var requestBodySha256 = "6650d90a7758918702bcf329166459244f904f5495b70a297be28f695e54febf";
84
+ var actorBodySha256 = "a58dad10d1051aed670bd79ee7dbfb9d3606cc8e6f231a9a742edd9317b28cd8";
85
+ //#endregion
86
+ //#region test-vectors/fep-ef61/tootik-v0.25.4/follow/raw-bodies.json
87
+ var requestBody = "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/data-integrity/v1\",\"https://w3id.org/security/v1\"],\"id\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/follow/01a0f282-9cba-7125-af77-e8c7210b7b65\",\"type\":\"Follow\",\"actor\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor\",\"object\":\"https://fedify.example:18444/users/alice\",\"to\":[\"https://fedify.example:18444/users/alice\"],\"cc\":[],\"proof\":{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/data-integrity/v1\",\"https://w3id.org/security/v1\"],\"type\":\"DataIntegrityProof\",\"cryptosuite\":\"eddsa-jcs-2022\",\"verificationMethod\":\"did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR#z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR\",\"proofPurpose\":\"assertionMethod\",\"proofValue\":\"z4yttRN3ak6xdXqdkg6KF9UyiVLv85tbcEvGFEcAKRSg5hpzhMFZzK2A8dp3o3wnJq5CA9t6nc47umjJkjmT5Kpmk\",\"created\":\"2026-09-30T13:30:41Z\"}}";
88
+ var actorBody = "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/data-integrity/v1\",\"https://w3id.org/security/v1\"],\"id\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor\",\"type\":\"Person\",\"inbox\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/inbox\",\"outbox\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/outbox\",\"preferredUsername\":\"alice\",\"followers\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/followers\",\"publicKey\":{\"id\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor#main-key\",\"owner\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor\",\"publicKeyPem\":\"-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyLpYZnhnVih6ToB4UThp\\nEZIcxbBcGng71XeEfiR42v+cCyUL+jzQm12x/I0a3FNh4dzIicMvtbmLewQ/HscR\\nUNIxAFHF8GqshIjg2tDAmGswTQDsvTzBsaEixXtL0qTTX8X0tFzVKJEsMJJ99jzg\\nodERW73G55WwpJYyI4v1RT3G5tU3p0tmD52hhCLCRLRCPKy75iz416nCwDt5sA8B\\nowVE5n/J+tjqG3umnnI3gJpTtuJxTyB/SVjLjnC604RKowwQQTHGAmcl3E5F3IrK\\nfnmfqdXNCrNbfvJFFAWB+XNXFB0I+fJbGUZAOYMfjhMGEpPzzkKI8cI1eR+Z5DeZ\\nGQIDAQAB\\n-----END PUBLIC KEY-----\\n\"},\"icon\":[{\"type\":\"Image\",\"mediaType\":\"image/gif\",\"url\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor/icon.gif\"}],\"manuallyApprovesFollowers\":false,\"assertionMethod\":[{\"id\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor#ed25519-key\",\"type\":\"Multikey\",\"controller\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor\",\"publicKeyMultibase\":\"z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR\"},{\"id\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor#ml-dsa-44-key\",\"type\":\"Multikey\",\"controller\":\"https://tootik.example/.well-known/apgateway/did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR/actor\",\"publicKeyMultibase\":\"ukCQjRdBDVD0QfnNnj4wFAJE82TmNXoNQ80Rf6s3ivFXlFCzb5_tZHVhfsR5JysgCbOqYPnXYyXIgXAm_klgaj1a4_MB-KLhmueucnnMKUjl60ORt4RG2fDAjZoCzBy-m7PMX5hByOfuDTXbAhSFhG44xvBumN2x877PLudpMqhe9hhBDwgL4NTxUWOP3xJR4KhO0GMzjA6r4HGw0F2Mw3cYQNJs2t40k9w_Yt1qiVfyr4Qk0pk5vnZEkNFF71OBm-SUUSPtrHQEo02HhElN7uFE8wgYmziC_RqzrvyWAi9XQofgDGps2d-xXQew28NkyhAjv-7yIwd3ukjm_0UxjslLFpcjRAW7hNkrIm6oxV_Wv3ynpUVII7uc6v3Q3hkTtJxL4bcsl-oOpZKAqJide5A09G0XE_PvPrnVU3n9hH1HIhs4aWUeo3xda_gLfA5yoavGhqh1oOKnT3yiZYysjKt4GbdcLE_AMCDvWAqLdHTywnl5CG_9gUa3GlgjEsrJK8-SWCXoVgjQmGtdLWWMXTwzAwNZZwkoiR72v0uVOAzE54U_0oz6_Fowdgr29o0qu8TTb6NY70fF34XMWDDlKOWpgo7s5ejL15jfsrzcLhzP1xQlcBqQRZIsSQ_e0z_6mPJlYMu-UjsJIu4SM1__mn48TAQbw__B7VtClD_wGpedwF8gPpATziNsmOVvK8LKtYt983i2bzLYlV3Um31R4r-0i13JY6tBDC3SXkbOyOW3HkfecC1EJ11ffqoBwYT6KJGYH1pd8YR7cvwQn6C4kItLQnoUcIPBY7hKmw6B78e0ZexMMZlmQkW_JEHpgRi0Fb9atgSfGs8IIHTZGOkU5MGUUH1Evqz995dqFjoUxZaCGNKE2Rh-c0F4UdVToex7ZniQOFL1-D16mTloNwPgA9tvd7SUnSY2fr_E4Vyt-Rvcy_azBWBZWPzKdOy9u0OEW8uLeX680qp1Qbx97zDwCsiNAoTRMPyS_azqB12RThb3L-o28UXw5cPyeW9NSSN3G4yaweaIDmQ16vB1biKknrOU9XNVwkwZ4ilzYoESkGmQoCOomrNjGclx99d42S_7laMBlOkQcy846EyxhaHpkOj7t5LIU4Vs1TKeXXDV08wZQHz_r557uwtly8ltLHCg1pWm6MJNRgkhcGBhlE7Jt3gybA9HkYrg_x3NTR-uQocckeUbZZI1Q0aQuMa_F6_qC8WJOAkGDfERlFs7-gVjqYtTSHFLky3B_CgZukp_YZ-HLDlbxAO2qw_itfcn3fn95LLPUxiOZvB49xyha_z2r4rHt9R0BymvDM9ufp-pg2CiLBe3cPiy1Ck_BE7kvA7TyjShi3YyQIGfUcwAoWXOj4lKwejeqUmsjJ-gpD2iVJ3frWao9Cu846ubamb6wQZf5faAx6yTJsHLYdeDBqqJRCMSVEnOYHf6SaNfzdE8EFqCJ7LP2IYDnSFaiZovhja6BJBM4fphH27mnkrQ10whn35XX843QVAOLJa5fn4GoIwkSFY0PSL1J4MT1MobKfA0lROfF5fE29sKLRt-wbnHveAFE_CRiyzW6IFQO9flnSmn83VxigN2hv_xD51KX6p8dCzX1QLl0S9fWF1K9iHkq4ulz6slvypfacesLLGvbVnAWX491PPq9LIOHjCLoZbr1Ap8vkLw61-XvDFSBHUxGKvrlpPOKO5V3zop0sT_QiEymx9DpGAGrbYiyDEC3LhNmD14DxRdRqQLG3wW6HFTMJtl6\"}],\"gateways\":[\"https://tootik.example\"],\"proof\":{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/data-integrity/v1\",\"https://w3id.org/security/v1\"],\"type\":\"DataIntegrityProof\",\"cryptosuite\":\"eddsa-jcs-2022\",\"verificationMethod\":\"did:key:z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR#z6MkpWqhz4YAuBfC4ddjU49mGfs6CAuYcm243K5hW2YEGAyR\",\"proofPurpose\":\"assertionMethod\",\"proofValue\":\"z2G5haKKAJk3rfL8njG9V2jybnPuHrXjR8ipPBjDSFim9cFuJ9xEMEWBLQLdBmoQKXmHhqXfheadf8EaNW3cEpX8s\",\"created\":\"2026-09-30T13:25:15Z\"}}";
89
+ //#endregion
90
+ //#region src/sig/tootik-request-interop.test.ts
91
+ const body = requestBody;
92
+ const activity = JSON.parse(body);
93
+ async function sha256(value) {
94
+ return encodeHex(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value))));
95
+ }
96
+ function loader(document = actor_default) {
97
+ return (url) => {
98
+ assertEquals(url.split("#")[0], actor_default.id);
99
+ return Promise.resolve({
100
+ document: structuredClone(document),
101
+ documentUrl: actor_default.id,
102
+ contextUrl: null
103
+ });
104
+ };
105
+ }
106
+ const options = {
107
+ documentLoader: loader(),
108
+ contextLoader: mockDocumentLoader,
109
+ currentTime: Temporal.Instant.from(capturedAt$1)
110
+ };
111
+ function request(requestBody = body, url$1 = url, headers$2 = new Headers(headers)) {
112
+ return new Request(url$1, {
113
+ method,
114
+ headers: headers$2,
115
+ body: requestBody
116
+ });
117
+ }
118
+ async function deliver(requestBody) {
119
+ const federation = createFederation({
120
+ kv: new MemoryKvStore(),
121
+ documentLoaderFactory: () => loader(),
122
+ contextLoaderFactory: () => mockDocumentLoader,
123
+ signatureTimeWindow: false
124
+ });
125
+ federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => identifier === "alice" ? new Person({
126
+ id: ctx.getActorUri(identifier),
127
+ inbox: ctx.getInboxUri(identifier)
128
+ }) : null).setKeyPairsDispatcher(() => []);
129
+ const received = [];
130
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Follow, (_ctx, follow) => {
131
+ received.push(follow);
132
+ });
133
+ return {
134
+ response: await federation.fetch(request(requestBody), { contextData: void 0 }),
135
+ received
136
+ };
137
+ }
138
+ test("tootik v0.25.4 captured Follow verifies its proof and HTTP signature", async () => {
139
+ const actorBody$1 = actorBody;
140
+ assertEquals(await sha256(body), requestBodySha256);
141
+ assertEquals(await sha256(actorBody$1), actorBodySha256);
142
+ assertEquals(JSON.parse(actorBody$1), actor_default);
143
+ assertEquals(activity.type, "Follow");
144
+ assertEquals(actor_default.type, "Person");
145
+ assertEquals(activity.actor, actor_default.id);
146
+ assert((await verifyPortableObjectProof(activity, options)).verified);
147
+ const verified = await verifyRequestDetailed(request(), options);
148
+ assert(verified.verified);
149
+ assertEquals(verified.key.id?.href, actor_default.publicKey.id);
150
+ assert(await doesActorOwnKey(await Follow.fromJsonLd(activity, options), verified.key, options));
151
+ assertEquals(capturedAt$1, capturedAt);
152
+ });
153
+ test("tootik v0.25.4 captured Follow reaches the Fedify inbox listener", async () => {
154
+ const { response, received } = await deliver(body);
155
+ assertEquals(response.status, 202);
156
+ assertEquals(received.length, 1);
157
+ assertEquals(received[0].id?.href, activity.id);
158
+ assertEquals(received[0].actorId?.href, actor_default.id);
159
+ });
160
+ test("tootik v0.25.4 captured Follow rejects body tampering", async () => {
161
+ const tampered = {
162
+ ...activity,
163
+ object: "https://fedify.example:18444/users/mallory"
164
+ };
165
+ assertEquals((await verifyPortableObjectProof(tampered, options)).verified, false);
166
+ const changedBody = JSON.stringify(tampered);
167
+ assertEquals((await verifyRequestDetailed(request(changedBody), options)).verified, false);
168
+ const { response, received } = await deliver(changedBody);
169
+ assertEquals(response.status, 401);
170
+ assertEquals(received.length, 0);
171
+ });
172
+ test("tootik v0.25.4 captured Follow rejects signed header and path tampering", async () => {
173
+ const headers$1 = new Headers(headers);
174
+ headers$1.set("Content-Type", "application/json");
175
+ assertEquals((await verifyRequestDetailed(request(body, url, headers$1), options)).verified, false);
176
+ assertEquals((await verifyRequestDetailed(request(body, `${url}/tampered`), options)).verified, false);
177
+ });
178
+ test("tootik v0.25.4 captured Follow rejects a tampered actor proof", async () => {
179
+ const tampered = {
180
+ ...actor_default,
181
+ preferredUsername: "mallory"
182
+ };
183
+ assertEquals((await verifyPortableObjectProof(tampered, options)).verified, false);
184
+ assertEquals((await verifyRequestDetailed(request(), {
185
+ ...options,
186
+ documentLoader: loader(tampered)
187
+ })).verified, false);
188
+ });
189
+ //#endregion
190
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";