@fedify/fedify 2.4.0-dev.2065 → 2.4.0-dev.2071

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 (93) hide show
  1. package/dist/{builder-CMkrnYbY.mjs → builder-DVQ0yhKJ.mjs} +6 -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-Dj3DCsoj.mjs → compound-proof-KmXDfWFp.mjs} +1 -1
  6. package/dist/{context-b36TpPoG.d.cts → context-DijrgHDe.d.cts} +45 -2
  7. package/dist/{context-D_gg4M0n.d.ts → context-DsB9kF6q.d.ts} +45 -2
  8. package/dist/{deno-BuRFI7wk.mjs → deno-B-NtYZVW.mjs} +1 -1
  9. package/dist/{docloader-_aSNLA90.mjs → docloader-C6yOVoaF.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -1
  11. package/dist/federation/compound-fanout.test.mjs +4 -4
  12. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  13. package/dist/federation/handler.test.mjs +5 -5
  14. package/dist/federation/hashlink-media.test.mjs +2 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/metrics.test.mjs +1 -1
  17. package/dist/federation/middleware.test.mjs +8 -8
  18. package/dist/federation/mod.cjs +1 -1
  19. package/dist/federation/mod.d.cts +3 -3
  20. package/dist/federation/mod.d.ts +3 -3
  21. package/dist/federation/mod.js +1 -1
  22. package/dist/federation/outgoing-proof.test.mjs +6 -6
  23. package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
  24. package/dist/federation/portable-gateway-keys.test.mjs +363 -0
  25. package/dist/federation/portable.test.mjs +3 -3
  26. package/dist/federation/send.test.mjs +3 -3
  27. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  28. package/dist/federation/tasks/tasks.test.mjs +2 -2
  29. package/dist/federation/temporal.test.mjs +1 -1
  30. package/dist/federation/webfinger.test.mjs +1 -1
  31. package/dist/{federation-Coi_bg12.cjs → federation-CUiHc7dP.cjs} +182 -76
  32. package/dist/http-BoWBwC0g.js +5087 -0
  33. package/dist/{http-BGShIsd6.cjs → http-CuLWIJdf.cjs} +2684 -48
  34. package/dist/{http-f1qaBtOh.mjs → http-DKWi8RjI.mjs} +20 -5
  35. package/dist/{http-65PYi9jY.d.ts → http-n2v6DPxu.d.ts} +52 -2
  36. package/dist/{http-CP-7OdSq.d.cts → http-xlG-jRdB.d.cts} +52 -2
  37. package/dist/{key-BxHsA1s9.mjs → key-lfcx_NaE.mjs} +147 -7
  38. package/dist/{kv-cache-Cgj-kpQS.js → kv-cache-BdnmnAVc.js} +1 -1
  39. package/dist/{kv-cache-CY6pDNoS.cjs → kv-cache-DE8kSNQU.cjs} +1 -1
  40. package/dist/{kv-cache-C9IY-FMN.mjs → kv-cache-d8EjO29d.mjs} +1 -1
  41. package/dist/{ld-CqJq5erX.mjs → ld-jyjdrWjG.mjs} +3 -3
  42. package/dist/{metrics-DOqknlZd.mjs → metrics-B5Rei-HC.mjs} +1 -1
  43. package/dist/{middleware-1h_LgwXC.js → middleware-CAObohey.js} +149 -43
  44. package/dist/{middleware-s-AZzx4r.mjs → middleware-CPx4ZWwt.mjs} +155 -53
  45. package/dist/{middleware-VOCZN3SO.mjs → middleware-CdVnCDXD.mjs} +1 -1
  46. package/dist/{mod-Dhm7SqyE.d.ts → mod-C9WSpZWm.d.ts} +1 -1
  47. package/dist/{mod-C0BYc0OU.d.ts → mod-CVZrXSdm.d.ts} +1 -1
  48. package/dist/{mod-BhMvsgdF.d.ts → mod-DMkflQdF.d.ts} +1 -1
  49. package/dist/{mod-Bfenkg_c.d.cts → mod-DTAaBwiY.d.cts} +1 -1
  50. package/dist/{mod-pLbeNwcC.d.cts → mod-HDSvS-ww.d.cts} +1 -1
  51. package/dist/{mod-CyiSjRkk.d.cts → mod-xGFCHqq_.d.cts} +1 -1
  52. package/dist/mod.cjs +18 -18
  53. package/dist/mod.d.cts +7 -7
  54. package/dist/mod.d.ts +7 -7
  55. package/dist/mod.js +5 -5
  56. package/dist/nodeinfo/handler.test.mjs +1 -1
  57. package/dist/owner-1TFakcTF.js +215 -0
  58. package/dist/owner-BXufwAzm.cjs +225 -0
  59. package/dist/{owner-D_F22lfM.mjs → owner-ByqNQVaa.mjs} +45 -2
  60. package/dist/portable-key-Cw_ow5vn.mjs +194 -0
  61. package/dist/{proof-B03JXhrk.mjs → proof-B6gwdhN5.mjs} +5 -5
  62. package/dist/{send-NNkfKtVN.mjs → send-DsK7xevO.mjs} +3 -3
  63. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  64. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  65. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  66. package/dist/sig/compound-proof.test.mjs +3 -3
  67. package/dist/sig/http.test.mjs +2 -2
  68. package/dist/sig/key.test.mjs +1 -1
  69. package/dist/sig/ld.test.mjs +2 -2
  70. package/dist/sig/mod.cjs +16 -16
  71. package/dist/sig/mod.d.cts +2 -2
  72. package/dist/sig/mod.d.ts +2 -2
  73. package/dist/sig/mod.js +3 -3
  74. package/dist/sig/owner.test.mjs +1 -1
  75. package/dist/sig/portable-collection.test.mjs +3 -3
  76. package/dist/sig/portable-key.test.d.mts +2 -0
  77. package/dist/sig/portable-key.test.mjs +275 -0
  78. package/dist/sig/proof.test.mjs +4 -4
  79. package/dist/{sig-Bb20Pyrd.js → sig-BR_Z6mpY.js} +2 -2
  80. package/dist/{sig-B1E2uyhQ.cjs → sig-TTn9wYrE.cjs} +4 -4
  81. package/dist/{temporal-VVVSuruq.mjs → temporal-Bw5jtrBY.mjs} +1 -1
  82. package/dist/testing/kv.test.mjs +1 -1
  83. package/dist/testing/mod.d.mts +43 -0
  84. package/dist/utils/docloader.test.mjs +2 -2
  85. package/dist/utils/kv-cache.test.mjs +1 -1
  86. package/dist/utils/mod.cjs +1 -1
  87. package/dist/utils/mod.d.cts +1 -1
  88. package/dist/utils/mod.d.ts +1 -1
  89. package/dist/utils/mod.js +1 -1
  90. package/package.json +6 -6
  91. package/dist/http-z5n7chWw.js +0 -2647
  92. package/dist/owner-BLBWXCNt.js +0 -2276
  93. package/dist/owner-D8ZpRusc.cjs +0 -2446
@@ -0,0 +1,275 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { r as fetchKey } from "../key-lfcx_NaE.mjs";
5
+ import { l as verifyRequest, s as signRequest } from "../http-DKWi8RjI.mjs";
6
+ import { c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
7
+ import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-ByqNQVaa.mjs";
8
+ import { Create, CryptographicKey, Multikey, Person } from "@fedify/vocab";
9
+ import { test } from "@fedify/fixture";
10
+ import { FetchError, encodeMultibase, exportDidKey, parseIri, preloadedContexts } from "@fedify/vocab-runtime";
11
+ import serialize from "json-canon";
12
+ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
13
+ //#region src/sig/portable-key.test.ts
14
+ const did = await exportDidKey(ed25519PublicKey.publicKey);
15
+ const didKeyId = `${did}#${did.slice(8)}`;
16
+ const otherKeyPair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
17
+ const otherDid = await exportDidKey(otherKeyPair.publicKey);
18
+ const actorId = `ap+ef61://${did}/actor`;
19
+ const gw1 = "https://gw1.example";
20
+ const gw2 = "https://gw2.example";
21
+ function compatibleId(gateway, id = actorId) {
22
+ return `${gateway}/.well-known/apgateway/${id.replace(/^ap\+ef61:\/\//, "")}`;
23
+ }
24
+ const keyId = new URL(`${compatibleId(gw1)}#main-key`);
25
+ const gatewayPublicKey = rsaPublicKey2.publicKey;
26
+ const contextLoader = (url) => {
27
+ const document = preloadedContexts[url];
28
+ if (document == null) return Promise.reject(/* @__PURE__ */ new Error(`No context: ${url}`));
29
+ return Promise.resolve({
30
+ contextUrl: null,
31
+ documentUrl: url,
32
+ document
33
+ });
34
+ };
35
+ function createLoader(responses) {
36
+ const fetched = [];
37
+ const loader = (url) => {
38
+ fetched.push(url);
39
+ const document = responses[url.replace(/#.*$/, "")];
40
+ if (document == null) return Promise.reject(new FetchError(url, "HTTP 404", new Response(null, { status: 404 })));
41
+ return Promise.resolve({
42
+ contextUrl: null,
43
+ documentUrl: url,
44
+ document: structuredClone(document)
45
+ });
46
+ };
47
+ return Object.assign(loader, { fetched });
48
+ }
49
+ async function sign(document, privateKey = ed25519PrivateKey, verificationMethod = didKeyId) {
50
+ const proofConfig = {
51
+ "@context": document["@context"],
52
+ type: "DataIntegrityProof",
53
+ cryptosuite: "eddsa-jcs-2022",
54
+ verificationMethod,
55
+ proofPurpose: "assertionMethod",
56
+ created: "2023-02-24T23:36:38Z"
57
+ };
58
+ const encoder = new TextEncoder();
59
+ const proofDigest = await crypto.subtle.digest("SHA-256", encoder.encode(serialize(proofConfig)));
60
+ const messageDigest = await crypto.subtle.digest("SHA-256", encoder.encode(serialize(document)));
61
+ const digest = /* @__PURE__ */ new Uint8Array(64);
62
+ digest.set(new Uint8Array(proofDigest), 0);
63
+ digest.set(new Uint8Array(messageDigest), 32);
64
+ const signature = await crypto.subtle.sign("Ed25519", privateKey, digest);
65
+ return {
66
+ ...document,
67
+ proof: {
68
+ ...proofConfig,
69
+ proofValue: new TextDecoder().decode(encodeMultibase("base58btc", new Uint8Array(signature)))
70
+ }
71
+ };
72
+ }
73
+ async function actorJson(options = {}) {
74
+ const id = parseIri(options.id ?? actorId);
75
+ const key = options.key ?? keyId;
76
+ const json = await new Person({
77
+ id,
78
+ preferredUsername: "alice",
79
+ inbox: parseIri(`${options.id ?? actorId}/inbox`),
80
+ gateways: (options.gateways ?? [gw1, gw2]).map((g) => new URL(g)),
81
+ assertionMethods: options.assertionMethod === false ? [] : [new Multikey({
82
+ id: key,
83
+ controller: id,
84
+ publicKey: gatewayPublicKey
85
+ })],
86
+ publicKeys: options.publicKey === null ? [] : [new CryptographicKey({
87
+ id: key,
88
+ owner: id,
89
+ publicKey: options.publicKey ?? gatewayPublicKey
90
+ })]
91
+ }).toJsonLd({
92
+ format: "compact",
93
+ contextLoader
94
+ });
95
+ const context = json["@context"];
96
+ if (!context.includes("https://w3id.org/security/data-integrity/v1")) json["@context"] = [...context, "https://w3id.org/security/data-integrity/v1"];
97
+ return json;
98
+ }
99
+ async function signedRequest(spec, key = keyId) {
100
+ return await signRequest(new Request("https://recipient.example/inbox", {
101
+ method: "POST",
102
+ body: "{}",
103
+ headers: { "Content-Type": "application/activity+json" }
104
+ }), rsaPrivateKey2, key, { spec });
105
+ }
106
+ test("verifyRequest() accepts gateway keys vouched for by portable actors", async () => {
107
+ const documentLoader = createLoader({ [compatibleId(gw1)]: await sign(await actorJson()) });
108
+ for (const spec of ["draft-cavage-http-signatures-12", "rfc9421"]) {
109
+ const key = await verifyRequest(await signedRequest(spec), {
110
+ documentLoader,
111
+ contextLoader,
112
+ spec
113
+ });
114
+ ok(key != null, spec);
115
+ strictEqual(key.id?.href, keyId.href);
116
+ strictEqual(key.ownerId?.href, parseIri(actorId).href);
117
+ }
118
+ });
119
+ test("verifyRequest() accepts gateway keys only in assertionMethod", async () => {
120
+ const documentLoader = createLoader({ [compatibleId(gw1)]: await sign(await actorJson({ publicKey: null })) });
121
+ strictEqual((await verifyRequest(await signedRequest(), {
122
+ documentLoader,
123
+ contextLoader
124
+ }))?.id?.href, keyId.href);
125
+ });
126
+ test("verifyRequest() rejects gateway keys that portable actors do not vouch for", async () => {
127
+ const unsigned = await actorJson();
128
+ const cases = {
129
+ "an unsigned actor document": unsigned,
130
+ "an actor document signed by another DID": await sign(unsigned, otherKeyPair.privateKey, `${otherDid}#${otherDid.slice(8)}`),
131
+ "an unlisted gateway": await sign(await actorJson({ gateways: [gw2] })),
132
+ "a key missing from assertionMethod": await sign(await actorJson({ assertionMethod: false })),
133
+ "a key referred to by URL": await sign({
134
+ ...await actorJson({
135
+ publicKey: null,
136
+ assertionMethod: false
137
+ }),
138
+ assertionMethod: [keyId.href]
139
+ }),
140
+ "a publicKey entry with other key material": await sign(await actorJson({ publicKey: rsaPublicKey3.publicKey })),
141
+ "a key ID for another actor": await sign(await actorJson({ key: new URL(`${compatibleId(gw1, `${actorId}/other`)}#main-key`) }))
142
+ };
143
+ for (const [name, document] of Object.entries(cases)) {
144
+ const documentLoader = createLoader({ [compatibleId(gw1)]: document });
145
+ strictEqual(await verifyRequest(await signedRequest(), {
146
+ documentLoader,
147
+ contextLoader
148
+ }), null, name);
149
+ }
150
+ const otherActorKey = new URL(`${compatibleId(gw1, `${actorId}/other`)}#main-key`);
151
+ const documentLoader = createLoader({ [compatibleId(gw1, `${actorId}/other`)]: await sign(await actorJson()) });
152
+ strictEqual(await verifyRequest(await signedRequest(void 0, otherActorKey), {
153
+ documentLoader,
154
+ contextLoader
155
+ }), null);
156
+ });
157
+ test("verifyRequest() keeps web-origin trust for compatible-ID actors", async () => {
158
+ const id = compatibleId(gw1);
159
+ const documentLoader = createLoader({ [id]: await actorJson({
160
+ id,
161
+ gateways: [gw1]
162
+ }) });
163
+ const key = await verifyRequest(await signedRequest(), {
164
+ documentLoader,
165
+ contextLoader
166
+ });
167
+ strictEqual(key?.id?.href, keyId.href);
168
+ strictEqual(key?.ownerId?.href, id);
169
+ });
170
+ test("gateway keys are neither cached nor accepted outside HTTP Signatures", async () => {
171
+ const calls = [];
172
+ const keyCache = {
173
+ get(id) {
174
+ calls.push(`get ${id.href}`);
175
+ return Promise.resolve(void 0);
176
+ },
177
+ set(id) {
178
+ calls.push(`set ${id.href}`);
179
+ return Promise.resolve();
180
+ }
181
+ };
182
+ const documentLoader = createLoader({ [compatibleId(gw1)]: await sign(await actorJson()) });
183
+ ok(await verifyRequest(await signedRequest(), {
184
+ documentLoader,
185
+ contextLoader,
186
+ keyCache
187
+ }) != null);
188
+ const fetchOptions = {
189
+ documentLoader,
190
+ contextLoader,
191
+ keyCache
192
+ };
193
+ strictEqual((await fetchKey(keyId, CryptographicKey, fetchOptions)).key, null);
194
+ strictEqual((await fetchKey(keyId, Multikey, fetchOptions)).key, null);
195
+ deepStrictEqual(calls.filter((c) => c.includes(keyId.origin)), []);
196
+ });
197
+ test("doesActorOwnKey() and getKeyOwner() resolve gateway keys to portable actors", async () => {
198
+ const options = {
199
+ documentLoader: createLoader({ [compatibleId(gw1)]: await sign(await actorJson()) }),
200
+ contextLoader
201
+ };
202
+ const key = await verifyRequest(await signedRequest(), options);
203
+ ok(key != null);
204
+ const activity = (actor) => new Create({
205
+ id: parseIri(`ap+ef61://${did}/activities/1`),
206
+ actor: parseIri(actor)
207
+ });
208
+ ok(await doesActorOwnKey(activity(actorId), key, options));
209
+ ok(await doesActorOwnKey(activity(compatibleId(gw2)), key, options));
210
+ ok(!await doesActorOwnKey(activity(`${actorId}/other`), key, options));
211
+ strictEqual((await getKeyOwner(keyId, options))?.id?.href, parseIri(actorId).href);
212
+ strictEqual((await getKeyOwner(key, options))?.id?.href, key.ownerId?.href);
213
+ const forged = key.clone({ publicKey: rsaPublicKey3.publicKey });
214
+ strictEqual(await getKeyOwner(forged, options), null);
215
+ ok(!await doesActorOwnKey(activity(actorId), forged, options));
216
+ });
217
+ test("doesActorOwnKey() and getKeyOwner() do not fall back for rejected gateway keys", async () => {
218
+ const options = {
219
+ documentLoader: createLoader({ [compatibleId(gw1)]: await actorJson() }),
220
+ contextLoader
221
+ };
222
+ const key = new CryptographicKey({
223
+ id: keyId,
224
+ owner: parseIri(actorId),
225
+ publicKey: gatewayPublicKey
226
+ });
227
+ ok(!await doesActorOwnKey(new Create({
228
+ id: parseIri(`ap+ef61://${did}/activities/1`),
229
+ actor: parseIri(actorId)
230
+ }), key, options));
231
+ strictEqual(await getKeyOwner(keyId, options), null);
232
+ strictEqual(await getKeyOwner(key, options), null);
233
+ });
234
+ test("verifyRequest() binds gateway keys to the actor whose document is signed", async () => {
235
+ const attackerDid = otherDid;
236
+ const attackerActorId = `ap+ef61://${attackerDid}/actor`;
237
+ const shiftingContext = "https://attacker.example/context";
238
+ let loads = 0;
239
+ const shiftingContextLoader = (url) => {
240
+ if (url !== shiftingContext) return contextLoader(url);
241
+ loads++;
242
+ const document = { "@context": loads <= 2 ? {
243
+ victimId: "@id",
244
+ attackerId: "https://attacker.example/ns#a"
245
+ } : {
246
+ victimId: "https://attacker.example/ns#v",
247
+ attackerId: "@id"
248
+ } };
249
+ return Promise.resolve({
250
+ contextUrl: null,
251
+ documentUrl: url,
252
+ document
253
+ });
254
+ };
255
+ const { id: _, ...victim } = await actorJson({ gateways: [gw1] });
256
+ const document = await sign({
257
+ ...victim,
258
+ "@context": [...victim["@context"], shiftingContext],
259
+ victimId: actorId,
260
+ attackerId: attackerActorId
261
+ }, otherKeyPair.privateKey, `${attackerDid}#${attackerDid.slice(8)}`);
262
+ const documentLoader = createLoader({ [compatibleId(gw1)]: document });
263
+ strictEqual(await verifyRequest(await signedRequest(), {
264
+ documentLoader,
265
+ contextLoader: shiftingContextLoader
266
+ }), null);
267
+ ok(loads > 2, "the shifting context was loaded for the proof");
268
+ loads = 0;
269
+ strictEqual(await getKeyOwner(keyId, {
270
+ documentLoader,
271
+ contextLoader: shiftingContextLoader
272
+ }), null);
273
+ });
274
+ //#endregion
275
+ export {};
@@ -9,11 +9,11 @@ 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 { l as verifyRequest } from "../http-f1qaBtOh.mjs";
13
- import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
14
- import { t as getAuthenticatedDocumentLoader } from "../docloader-_aSNLA90.mjs";
15
12
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
16
- import { a as verifyObject, l as verifyProof, n as hasProofLike, o as verifyPortableObjectProof, r as signObject, t as createProof } from "../proof-B03JXhrk.mjs";
13
+ import { a as verifyObject, l as verifyProof, n as hasProofLike, o as verifyPortableObjectProof, r as signObject, t as createProof } from "../proof-B6gwdhN5.mjs";
14
+ import { l as verifyRequest } from "../http-DKWi8RjI.mjs";
15
+ 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-C6yOVoaF.mjs";
17
17
  import { Create, DataIntegrityProof, Document, Multikey, Note, PUBLIC_COLLECTION, Place, Translation } from "@fedify/vocab";
18
18
  import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
19
19
  import { decodeMultibase, encodeMultibase, exportDidKey, exportMultibaseKey, formatIri, importMultibaseKey, parseIri } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { I as name, L as version } from "./http-z5n7chWw.js";
4
- import { c as verifyPortableObjectProof, u as verifyPortableObjectProofWithRoot } from "./owner-BLBWXCNt.js";
3
+ import { f as verifyPortableObjectProof, ht as version, m as verifyPortableObjectProofWithRoot, mt as name } from "./http-BoWBwC0g.js";
4
+ import "./owner-1TFakcTF.js";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import { CollectionPage, Object as Object$1, isActor } from "@fedify/vocab";
7
7
  import { trace } from "@opentelemetry/api";
@@ -1,6 +1,6 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-BGShIsd6.cjs");
3
- const require_owner = require("./owner-D8ZpRusc.cjs");
2
+ const require_http = require("./http-CuLWIJdf.cjs");
3
+ require("./owner-BXufwAzm.cjs");
4
4
  let _logtape_logtape = require("@logtape/logtape");
5
5
  let _fedify_vocab = require("@fedify/vocab");
6
6
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -236,7 +236,7 @@ async function fetchOwner(candidate, options) {
236
236
  contextLoader,
237
237
  tracerProvider,
238
238
  gateways,
239
- verifyPortableObject: (document, verifierOptions) => require_owner.verifyPortableObjectProof(document, {
239
+ verifyPortableObject: (document, verifierOptions) => require_http.verifyPortableObjectProof(document, {
240
240
  ...options,
241
241
  ...verifierOptions
242
242
  }),
@@ -340,7 +340,7 @@ function getSourceGateway(node, documentUrl) {
340
340
  * @since 2.4.0
341
341
  */
342
342
  async function verifyPortableObject(jsonLd, options = {}) {
343
- const { result, root, objectType } = await require_owner.verifyPortableObjectProofWithRoot(jsonLd, options);
343
+ const { result, root, objectType } = await require_http.verifyPortableObjectProofWithRoot(jsonLd, options);
344
344
  if (result.verified) {
345
345
  const node = objectType === "collection" && root != null ? describeRoot(root) : null;
346
346
  const chain = node == null ? null : resolveChain(node, options.referrer);
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { c as getNormalizationContextLoader } from "./ld-CqJq5erX.mjs";
4
+ import { c as getNormalizationContextLoader } from "./ld-jyjdrWjG.mjs";
5
5
  import jsonld from "@fedify/vocab-runtime/jsonld";
6
6
  //#region src/federation/temporal.ts
7
7
  function isPlainObject(value) {
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as ManualClockKvStore } from "../kv-DBRWVx3Q.mjs";
7
7
  import { n as baseOptions, o as stringSchema, t as MockQueue } from "../tasks-dbCShlCb.mjs";
8
8
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
9
- import { o as createFederation } from "../middleware-s-AZzx4r.mjs";
9
+ import { o as createFederation } from "../middleware-CPx4ZWwt.mjs";
10
10
  //#region src/testing/kv.test.ts
11
11
  test("ManualClockKvStore keeps an entry at its exact expiration instant", async () => {
12
12
  const kv = new ManualClockKvStore();
@@ -348,6 +348,25 @@ type ActorKeyPairsDispatcher<TContextData> = (context: Context<TContextData>, id
348
348
  * @since 0.15.0
349
349
  */
350
350
  type ActorHandleMapper<TContextData> = (context: Context<TContextData>, username: string) => string | null | Promise<string | null>;
351
+ /**
352
+ * A callback that maps an actor's internal identifier to the ID of the
353
+ * [FEP-ef61] portable actor it dispatches, or `null` if the actor is not
354
+ * portable.
355
+ *
356
+ * The returned ID is an `ap:` or `ap+ef61:` URI, e.g.,
357
+ * `ap+ef61://did:key:z6Mk.../actors/alice`, or its compatible identifier.
358
+ * It must be the same ID that the actor dispatcher puts in the actor's `id`,
359
+ * and must not have a fragment.
360
+ *
361
+ * [FEP-ef61]: https://w3id.org/fep/ef61
362
+ *
363
+ * @template TContextData The context data to pass to the {@link Context}.
364
+ * @param context The context.
365
+ * @param identifier The actor's internal identifier.
366
+ * @returns The portable actor's ID, or `null` if the actor is not portable.
367
+ * @since 2.4.0
368
+ */
369
+ type PortableActorIdMapper<TContextData> = (context: Context<TContextData>, identifier: string) => URL | null | Promise<URL | null>;
351
370
  /**
352
371
  * A callback that maps a WebFinger query to the corresponding actor's
353
372
  * internal identifier or username, or `null` if the query is not found.
@@ -2365,6 +2384,30 @@ interface ActorCallbackSetters<TContextData> {
2365
2384
  * @since 1.4.0
2366
2385
  */
2367
2386
  mapAlias(mapper: ActorAliasMapper<TContextData>): ActorCallbackSetters<TContextData>;
2387
+ /**
2388
+ * Sets the callback function that maps an actor's identifier to the ID of
2389
+ * the [FEP-ef61] portable actor it dispatches. If the callback returns
2390
+ * a portable ID for an identifier, the key pairs of that actor are treated
2391
+ * as this server's *gateway keys* for the portable actor:
2392
+ * {@link Context.getActorKeyPairs} derives their key IDs from the actor's
2393
+ * compatible identifier on this server, e.g.,
2394
+ * `https://example.com/.well-known/apgateway/did:key:z6Mk.../actors/alice#main-key`,
2395
+ * and makes the portable actor their owner, so that requests this server
2396
+ * makes on behalf of the actor, such as activity deliveries and signed
2397
+ * fetches, are signed with them. Gateway keys are only used for HTTP
2398
+ * Signatures; they never make Object Integrity Proofs or Linked Data
2399
+ * Signatures.
2400
+ *
2401
+ * If it's omitted, or it returns `null`, the actor's keys are derived from
2402
+ * {@link Context.getActorUri} as usual.
2403
+ *
2404
+ * [FEP-ef61]: https://w3id.org/fep/ef61
2405
+ * @param mapper A callback that maps an actor's identifier to its portable
2406
+ * ID, or `null` if the actor is not portable.
2407
+ * @returns The setters object so that settings can be chained.
2408
+ * @since 2.4.0
2409
+ */
2410
+ mapPortableActorId(mapper: PortableActorIdMapper<TContextData>): ActorCallbackSetters<TContextData>;
2368
2411
  /**
2369
2412
  * Maps a fixed path to a sentinel identifier. It is useful for exposing
2370
2413
  * a single, instance-level actor at a fixed path, such as `/actor` for
@@ -5,9 +5,9 @@ import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import "../std__assert-DvMDRPes.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
8
- import { l as verifyRequest } from "../http-f1qaBtOh.mjs";
8
+ import { l as verifyRequest } from "../http-DKWi8RjI.mjs";
9
9
  import { i as rsaPrivateKey2 } from "../keys-DIJNHW-Y.mjs";
10
- import { t as getAuthenticatedDocumentLoader } from "../docloader-_aSNLA90.mjs";
10
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-C6yOVoaF.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
13
13
  //#region src/utils/docloader.test.ts
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as kvCache, t as MockKvStore } from "../kv-cache-C9IY-FMN.mjs";
4
+ import { n as kvCache, t as MockKvStore } from "../kv-cache-d8EjO29d.mjs";
5
5
  import { deepStrictEqual, throws } from "node:assert";
6
6
  import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
7
7
  import { preloadedContexts } from "@fedify/vocab-runtime";
@@ -1,5 +1,5 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_kv_cache = require("../kv-cache-CY6pDNoS.cjs");
3
+ const require_kv_cache = require("../kv-cache-DE8kSNQU.cjs");
4
4
  exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
5
5
  exports.kvCache = require_kv_cache.kvCache;
@@ -1,3 +1,3 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-CyiSjRkk.cjs";
2
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-xGFCHqq_.cjs";
3
3
  export { getAuthenticatedDocumentLoader, kvCache };
@@ -1,3 +1,3 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-C0BYc0OU.js";
2
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-CVZrXSdm.js";
3
3
  export { getAuthenticatedDocumentLoader, kvCache };
package/dist/utils/mod.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-Cgj-kpQS.js";
3
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-BdnmnAVc.js";
4
4
  export { getAuthenticatedDocumentLoader, kvCache };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/fedify",
3
- "version": "2.4.0-dev.2065+b09fdd33",
3
+ "version": "2.4.0-dev.2071+8b9dccb9",
4
4
  "description": "An ActivityPub server framework",
5
5
  "keywords": [
6
6
  "ActivityPub",
@@ -151,10 +151,10 @@
151
151
  "structured-field-values": "^2.0.4",
152
152
  "temporal-polyfill": "^1.0.1",
153
153
  "urlpattern-polyfill": "^10.1.0",
154
- "@fedify/vocab": "2.4.0-dev.2065+b09fdd33",
155
- "@fedify/uri-template": "2.4.0-dev.2065+b09fdd33",
156
- "@fedify/webfinger": "2.4.0-dev.2065+b09fdd33",
157
- "@fedify/vocab-runtime": "2.4.0-dev.2065+b09fdd33"
154
+ "@fedify/uri-template": "2.4.0-dev.2071+8b9dccb9",
155
+ "@fedify/vocab": "2.4.0-dev.2071+8b9dccb9",
156
+ "@fedify/vocab-runtime": "2.4.0-dev.2071+8b9dccb9",
157
+ "@fedify/webfinger": "2.4.0-dev.2071+8b9dccb9"
158
158
  },
159
159
  "devDependencies": {
160
160
  "@std/assert": "npm:@jsr/std__assert@^0.226.0",
@@ -167,7 +167,7 @@
167
167
  "typescript": "^6.0.0",
168
168
  "wrangler": "^4.17.0",
169
169
  "@fedify/fixture": "2.0.0",
170
- "@fedify/vocab-tools": "^2.4.0-dev.2065+b09fdd33"
170
+ "@fedify/vocab-tools": "^2.4.0-dev.2071+8b9dccb9"
171
171
  },
172
172
  "scripts": {
173
173
  "build:self": "tsdown",