@fedify/fedify 2.4.0-dev.2063 → 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-BzS4brdy.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-BrSXP5so.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-CVZgG4sc.mjs → deno-B-NtYZVW.mjs} +1 -1
  9. package/dist/{docloader-DBb8z7_l.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-BrsIzNK5.cjs → federation-CUiHc7dP.cjs} +182 -76
  32. package/dist/http-BoWBwC0g.js +5087 -0
  33. package/dist/{http-CVjwUCCu.cjs → http-CuLWIJdf.cjs} +2684 -48
  34. package/dist/{http-znjq5j6a.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-BnMA6vpl.mjs → key-lfcx_NaE.mjs} +147 -7
  38. package/dist/{kv-cache-aijDx_jN.js → kv-cache-BdnmnAVc.js} +1 -1
  39. package/dist/{kv-cache-D2O0JspA.cjs → kv-cache-DE8kSNQU.cjs} +1 -1
  40. package/dist/{kv-cache--REpvHgG.mjs → kv-cache-d8EjO29d.mjs} +1 -1
  41. package/dist/{ld-BBVP8ock.mjs → ld-jyjdrWjG.mjs} +3 -3
  42. package/dist/{metrics-B5spFq82.mjs → metrics-B5Rei-HC.mjs} +1 -1
  43. package/dist/{middleware-CzYM4cJI.js → middleware-CAObohey.js} +149 -43
  44. package/dist/{middleware-Cw8EWwAT.mjs → middleware-CPx4ZWwt.mjs} +155 -53
  45. package/dist/{middleware-ClrGS1xy.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-Hc0hvbbX.mjs → owner-ByqNQVaa.mjs} +45 -2
  60. package/dist/portable-key-Cw_ow5vn.mjs +194 -0
  61. package/dist/{proof-B69bJaSn.mjs → proof-B6gwdhN5.mjs} +5 -5
  62. package/dist/{send-BB5rBhvN.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 +76 -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-B8WDBV-h.js → sig-BR_Z6mpY.js} +2 -2
  80. package/dist/{sig-dut4BzEW.cjs → sig-TTn9wYrE.cjs} +4 -4
  81. package/dist/{temporal-CcRkOx3C.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 +7 -7
  91. package/dist/http-99KVDtMr.js +0 -2647
  92. package/dist/owner-CHBQ0sSj.cjs +0 -2446
  93. package/dist/owner-Cu_KMvQN.js +0 -2276
@@ -1,10 +1,11 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-CVZgG4sc.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-B5spFq82.mjs";
4
+ import { n as version, t as name } from "./deno-B-NtYZVW.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-B5Rei-HC.mjs";
6
6
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
7
- import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-BnMA6vpl.mjs";
7
+ import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-lfcx_NaE.mjs";
8
+ import { n as verifyPortableGatewayKeyDocument } from "./portable-key-Cw_ow5vn.mjs";
8
9
  import { getLogger } from "@logtape/logtape";
9
10
  import { CryptographicKey } from "@fedify/vocab";
10
11
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -570,7 +571,14 @@ async function verifyRequestDraft(request, span, metricsContext, { documentLoade
570
571
  contextLoader,
571
572
  keyCache,
572
573
  tracerProvider,
573
- meterProvider
574
+ meterProvider,
575
+ portableGatewayKeyResolver: (document, actor, keyId) => verifyPortableGatewayKeyDocument(document, actor, keyId, {
576
+ documentLoader,
577
+ contextLoader,
578
+ keyCache,
579
+ tracerProvider,
580
+ meterProvider
581
+ })
574
582
  }));
575
583
  if (fetchError != null) return keyFetchErrorResult(keyIdUrl, fetchError);
576
584
  if (key == null) return invalidSignatureResult(keyIdUrl);
@@ -786,7 +794,14 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
786
794
  contextLoader,
787
795
  keyCache,
788
796
  tracerProvider,
789
- meterProvider
797
+ meterProvider,
798
+ portableGatewayKeyResolver: (document, actor, gatewayKeyId) => verifyPortableGatewayKeyDocument(document, actor, gatewayKeyId, {
799
+ documentLoader,
800
+ contextLoader,
801
+ keyCache,
802
+ tracerProvider,
803
+ meterProvider
804
+ })
790
805
  }));
791
806
  if (fetchError != null) {
792
807
  setFailure(keyFetchErrorResult(keyId, fetchError));
@@ -3,6 +3,28 @@ import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
3
3
  import { MeterProvider, TracerProvider } from "@opentelemetry/api";
4
4
  import { DocumentLoader } from "@fedify/vocab-runtime";
5
5
 
6
+ //#region src/sig/portable-key.d.ts
7
+ /**
8
+ * The result of resolving a gateway key of a portable actor.
9
+ * @internal
10
+ */
11
+ type PortableGatewayKeyResolution = {
12
+ /**
13
+ * The document at the key ID is not a portable actor, so the key is
14
+ * resolved as usual.
15
+ */
16
+ readonly type: "legacy";
17
+ } | {
18
+ /** The document is a portable actor, but it does not vouch for the key. */readonly type: "rejected";
19
+ readonly reason: string;
20
+ } | {
21
+ /** The portable actor's signed document vouches for the key. */readonly type: "verified";
22
+ readonly actor: Actor;
23
+ readonly key: CryptographicKey & {
24
+ publicKey: CryptoKey;
25
+ };
26
+ };
27
+ //#endregion
6
28
  //#region src/sig/key.d.ts
7
29
  /**
8
30
  * Generates a key pair which is appropriate for Fedify.
@@ -91,6 +113,18 @@ interface FetchKeyOptions {
91
113
  * @internal
92
114
  */
93
115
  keyIdBoundByCaller?: boolean;
116
+ /**
117
+ * Resolves a gateway key of an [FEP-ef61] portable actor, i.e., a key whose
118
+ * ID is a compatible identifier and dereferences to a portable actor's
119
+ * document. Such a key is resolved only through this function, and is
120
+ * rejected if it is omitted, so that only HTTP Signature verification,
121
+ * which passes it, accepts gateway keys. Object Integrity Proofs and
122
+ * Linked Data Signatures never do.
123
+ *
124
+ * [FEP-ef61]: https://w3id.org/fep/ef61
125
+ * @internal
126
+ */
127
+ portableGatewayKeyResolver?: (document: unknown, actor: Actor, keyId: URL) => Promise<PortableGatewayKeyResolution>;
94
128
  }
95
129
  /**
96
130
  * The result of {@link fetchKey}.
@@ -139,7 +173,15 @@ type FetchableKeyClass<T extends CryptographicKey | Multikey> = (new (...args: a
139
173
  /**
140
174
  * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL.
141
175
  * If the given URL contains an {@link Actor} object, it tries to find
142
- * the corresponding key in the `publicKey` or `assertionMethod` property.
176
+ * the corresponding key in the `publicKey` or `assertionMethod` property. *
177
+ * Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
178
+ * compatible identifiers that dereference to portable actor documents, are
179
+ * not resolved by this function, as they only authenticate HTTP requests;
180
+ * use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
181
+ * whose IDs are compatible identifiers are never read from or written to
182
+ * the key cache.
183
+ *
184
+ * [FEP-ef61]: https://w3id.org/fep/ef61
143
185
  * @template T The type of the key to fetch. Either {@link CryptographicKey}
144
186
  * or {@link Multikey}.
145
187
  * @param keyId The URL of the key.
@@ -153,7 +195,15 @@ declare function fetchKey<T extends CryptographicKey | Multikey>(keyId: URL | st
153
195
  /**
154
196
  * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL,
155
197
  * preserving transport-level fetch failures for callers that need to inspect
156
- * why the key could not be loaded.
198
+ * why the key could not be loaded. *
199
+ * Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
200
+ * compatible identifiers that dereference to portable actor documents, are
201
+ * not resolved by this function, as they only authenticate HTTP requests;
202
+ * use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
203
+ * whose IDs are compatible identifiers are never read from or written to
204
+ * the key cache.
205
+ *
206
+ * [FEP-ef61]: https://w3id.org/fep/ef61
157
207
  *
158
208
  * @template T The type of the key to fetch. Either {@link CryptographicKey}
159
209
  * or {@link Multikey}.
@@ -3,6 +3,28 @@ import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
3
3
  import { DocumentLoader } from "@fedify/vocab-runtime";
4
4
  import { MeterProvider, TracerProvider } from "@opentelemetry/api";
5
5
 
6
+ //#region src/sig/portable-key.d.ts
7
+ /**
8
+ * The result of resolving a gateway key of a portable actor.
9
+ * @internal
10
+ */
11
+ type PortableGatewayKeyResolution = {
12
+ /**
13
+ * The document at the key ID is not a portable actor, so the key is
14
+ * resolved as usual.
15
+ */
16
+ readonly type: "legacy";
17
+ } | {
18
+ /** The document is a portable actor, but it does not vouch for the key. */readonly type: "rejected";
19
+ readonly reason: string;
20
+ } | {
21
+ /** The portable actor's signed document vouches for the key. */readonly type: "verified";
22
+ readonly actor: Actor;
23
+ readonly key: CryptographicKey & {
24
+ publicKey: CryptoKey;
25
+ };
26
+ };
27
+ //#endregion
6
28
  //#region src/sig/key.d.ts
7
29
  /**
8
30
  * Generates a key pair which is appropriate for Fedify.
@@ -91,6 +113,18 @@ interface FetchKeyOptions {
91
113
  * @internal
92
114
  */
93
115
  keyIdBoundByCaller?: boolean;
116
+ /**
117
+ * Resolves a gateway key of an [FEP-ef61] portable actor, i.e., a key whose
118
+ * ID is a compatible identifier and dereferences to a portable actor's
119
+ * document. Such a key is resolved only through this function, and is
120
+ * rejected if it is omitted, so that only HTTP Signature verification,
121
+ * which passes it, accepts gateway keys. Object Integrity Proofs and
122
+ * Linked Data Signatures never do.
123
+ *
124
+ * [FEP-ef61]: https://w3id.org/fep/ef61
125
+ * @internal
126
+ */
127
+ portableGatewayKeyResolver?: (document: unknown, actor: Actor, keyId: URL) => Promise<PortableGatewayKeyResolution>;
94
128
  }
95
129
  /**
96
130
  * The result of {@link fetchKey}.
@@ -139,7 +173,15 @@ type FetchableKeyClass<T extends CryptographicKey | Multikey> = (new (...args: a
139
173
  /**
140
174
  * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL.
141
175
  * If the given URL contains an {@link Actor} object, it tries to find
142
- * the corresponding key in the `publicKey` or `assertionMethod` property.
176
+ * the corresponding key in the `publicKey` or `assertionMethod` property. *
177
+ * Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
178
+ * compatible identifiers that dereference to portable actor documents, are
179
+ * not resolved by this function, as they only authenticate HTTP requests;
180
+ * use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
181
+ * whose IDs are compatible identifiers are never read from or written to
182
+ * the key cache.
183
+ *
184
+ * [FEP-ef61]: https://w3id.org/fep/ef61
143
185
  * @template T The type of the key to fetch. Either {@link CryptographicKey}
144
186
  * or {@link Multikey}.
145
187
  * @param keyId The URL of the key.
@@ -153,7 +195,15 @@ declare function fetchKey<T extends CryptographicKey | Multikey>(keyId: URL | st
153
195
  /**
154
196
  * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL,
155
197
  * preserving transport-level fetch failures for callers that need to inspect
156
- * why the key could not be loaded.
198
+ * why the key could not be loaded. *
199
+ * Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
200
+ * compatible identifiers that dereference to portable actor documents, are
201
+ * not resolved by this function, as they only authenticate HTTP requests;
202
+ * use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
203
+ * whose IDs are compatible identifiers are never read from or written to
204
+ * the key cache.
205
+ *
206
+ * [FEP-ef61]: https://w3id.org/fep/ef61
157
207
  *
158
208
  * @template T The type of the key to fetch. Either {@link CryptographicKey}
159
209
  * or {@link Multikey}.
@@ -1,12 +1,105 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-CVZgG4sc.mjs";
5
- import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-B5spFq82.mjs";
4
+ import { n as version, t as name } from "./deno-B-NtYZVW.mjs";
5
+ import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-B5Rei-HC.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { CryptographicKey, Multikey, Object as Object$1, isActor } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
- import { FetchError, getDocumentLoader, parseDidKeyVerificationMethod } from "@fedify/vocab-runtime";
9
+ import { FetchError, canonicalizePortableUri, formatIri, fromCompatibleEf61Id, getDocumentLoader, parseDidKeyVerificationMethod, toCompatibleEf61Id } from "@fedify/vocab-runtime";
10
+ //#region src/sig/portable-key-id.ts
11
+ const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
12
+ /**
13
+ * Checks whether the URL is an `ap:` or `ap+ef61:` URI.
14
+ * @internal
15
+ */
16
+ function isPortableUri(url) {
17
+ return url.protocol === "ap:" || url.protocol === "ap+ef61:";
18
+ }
19
+ /**
20
+ * Checks whether an activity is performed by an FEP-ef61 portable actor,
21
+ * i.e., any of its actors has an `ap:` or `ap+ef61:` ID.
22
+ * @internal
23
+ */
24
+ function hasPortableActor(activity) {
25
+ return activity.actorIds.some(isPortableUri);
26
+ }
27
+ /**
28
+ * Checks whether a key ID looks like an [FEP-ef61] compatible identifier,
29
+ * i.e., an HTTP(S) URL under a gateway's `/.well-known/apgateway/did:` path.
30
+ * Such a key ID names a key that a gateway holds for a portable actor, so it
31
+ * is resolved only as a gateway key, and never cached in a key cache.
32
+ *
33
+ * [FEP-ef61]: https://w3id.org/fep/ef61
34
+ * @internal
35
+ */
36
+ function isCompatibleKeyId(keyId) {
37
+ return (keyId.protocol === "http:" || keyId.protocol === "https:") && COMPATIBLE_KEY_ID_PATH_PATTERN.test(keyId.pathname);
38
+ }
39
+ /**
40
+ * Gets the canonical portable ID of an actor ID that is either an `ap:` or
41
+ * `ap+ef61:` URI or a compatible identifier.
42
+ * @returns The canonical portable ID, or `null` if the ID is neither, or is
43
+ * malformed.
44
+ * @internal
45
+ */
46
+ function getCanonicalPortableId(id) {
47
+ try {
48
+ if (isPortableUri(id)) return canonicalizePortableUri(formatIri(id));
49
+ const portable = fromCompatibleEf61Id(id);
50
+ if (portable == null) return null;
51
+ return canonicalizePortableUri(formatIri(portable));
52
+ } catch (error) {
53
+ if (error instanceof TypeError) return null;
54
+ throw error;
55
+ }
56
+ }
57
+ /**
58
+ * Builds the base of the key IDs of a gateway's keys for a portable actor:
59
+ * the actor's compatible identifier on the gateway.
60
+ * @param actorId The portable actor's ID, either an `ap:` or `ap+ef61:` URI
61
+ * or a compatible identifier. It must not have a fragment.
62
+ * @param gateway The gateway's origin, e.g., `https://example.com`.
63
+ * @returns The compatible identifier without query and fragment.
64
+ * @throws {TypeError} If the actor ID is not a portable ID, or has
65
+ * a fragment.
66
+ * @internal
67
+ */
68
+ function getGatewayKeyBase(actorId, gateway) {
69
+ let portable;
70
+ if (isPortableUri(actorId)) portable = actorId;
71
+ else try {
72
+ portable = fromCompatibleEf61Id(actorId);
73
+ } catch (error) {
74
+ if (!(error instanceof TypeError)) throw error;
75
+ portable = null;
76
+ }
77
+ if (portable == null) throw new TypeError(`The portable actor ID ${actorId.href} must be an ap: or ap+ef61: URI, or an FEP-ef61 compatible identifier.`);
78
+ if (portable.hash !== "") throw new TypeError(`The portable actor ID ${actorId.href} must not have a fragment.`);
79
+ const base = toCompatibleEf61Id(portable, gateway);
80
+ base.search = "";
81
+ base.hash = "";
82
+ return base;
83
+ }
84
+ /**
85
+ * Tells whether a parsed document at a compatible key ID is a portable actor,
86
+ * which makes the key a gateway key that only
87
+ * {@link verifyPortableGatewayKeyDocument} can vouch for.
88
+ * @internal
89
+ */
90
+ function isPortableActorDocument(object) {
91
+ return isActor(object) && object.id != null && isPortableUri(object.id);
92
+ }
93
+ /**
94
+ * Compares two public keys by their key material.
95
+ * @internal
96
+ */
97
+ async function isSamePublicKey(a, b) {
98
+ if (a.algorithm.name !== b.algorithm.name) return false;
99
+ const [x, y] = await Promise.all([crypto.subtle.exportKey("jwk", a), crypto.subtle.exportKey("jwk", b)]);
100
+ return x.kty === y.kty && x.crv === y.crv && x.n === y.n && x.e === y.e && x.x === y.x && x.y === y.y;
101
+ }
102
+ //#endregion
10
103
  //#region src/sig/key.ts
11
104
  /**
12
105
  * Checks if the given key is valid and supported. No-op if the key is valid,
@@ -230,7 +323,15 @@ async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
230
323
  /**
231
324
  * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL.
232
325
  * If the given URL contains an {@link Actor} object, it tries to find
233
- * the corresponding key in the `publicKey` or `assertionMethod` property.
326
+ * the corresponding key in the `publicKey` or `assertionMethod` property. *
327
+ * Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
328
+ * compatible identifiers that dereference to portable actor documents, are
329
+ * not resolved by this function, as they only authenticate HTTP requests;
330
+ * use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
331
+ * whose IDs are compatible identifiers are never read from or written to
332
+ * the key cache.
333
+ *
334
+ * [FEP-ef61]: https://w3id.org/fep/ef61
234
335
  * @template T The type of the key to fetch. Either {@link CryptographicKey}
235
336
  * or {@link Multikey}.
236
337
  * @param keyId The URL of the key.
@@ -247,7 +348,15 @@ function fetchKey(keyId, cls, options = {}) {
247
348
  /**
248
349
  * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL,
249
350
  * preserving transport-level fetch failures for callers that need to inspect
250
- * why the key could not be loaded.
351
+ * why the key could not be loaded. *
352
+ * Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
353
+ * compatible identifiers that dereference to portable actor documents, are
354
+ * not resolved by this function, as they only authenticate HTTP requests;
355
+ * use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
356
+ * whose IDs are compatible identifiers are never read from or written to
357
+ * the key cache.
358
+ *
359
+ * [FEP-ef61]: https://w3id.org/fep/ef61
251
360
  *
252
361
  * @template T The type of the key to fetch. Either {@link CryptographicKey}
253
362
  * or {@link Multikey}.
@@ -398,7 +507,7 @@ async function resolveDidKey(cacheKey, cls, keyCache, logger) {
398
507
  cached: false
399
508
  };
400
509
  }
401
- async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider, keyIdBoundByCaller }, logger) {
510
+ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider, keyIdBoundByCaller, portableGatewayKeyResolver }, logger) {
402
511
  let object;
403
512
  try {
404
513
  object = await Object$1.fromJsonLd(document, {
@@ -427,6 +536,33 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
427
536
  throw e;
428
537
  }
429
538
  }
539
+ if (isCompatibleKeyId(cacheKey) && isPortableActorDocument(object)) {
540
+ if (portableGatewayKeyResolver == null || cls !== CryptographicKey) {
541
+ logger.debug("Failed to verify; key {keyId} is a gateway key of the portable actor {actorId}, which is only accepted for HTTP Signatures.", {
542
+ keyId,
543
+ actorId: object.id?.href
544
+ });
545
+ return {
546
+ key: null,
547
+ cached: false
548
+ };
549
+ }
550
+ const resolution = await portableGatewayKeyResolver(document, object, cacheKey);
551
+ if (resolution.type !== "verified") {
552
+ logger.debug("Failed to verify; the portable actor {actorId} does not vouch for its gateway key {keyId}.", {
553
+ keyId,
554
+ actorId: object.id?.href
555
+ });
556
+ return {
557
+ key: null,
558
+ cached: false
559
+ };
560
+ }
561
+ return {
562
+ key: resolution.key,
563
+ cached: false
564
+ };
565
+ }
430
566
  let key = null;
431
567
  let ownerDocument = null;
432
568
  if (object instanceof cls && (object.id == null || object.id.href === keyId)) key = object.id == null ? object.clone({ id: cacheKey }) : object;
@@ -546,6 +682,10 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
546
682
  "key"
547
683
  ]);
548
684
  const keyId = cacheKey.href;
685
+ if (isCompatibleKeyId(cacheKey)) options = {
686
+ ...options,
687
+ keyCache: void 0
688
+ };
549
689
  const keyCache = options.keyCache;
550
690
  const didKey = await resolveDidKey(cacheKey, cls, keyCache, logger);
551
691
  if (didKey != null) {
@@ -612,4 +752,4 @@ async function fetchKeyInternal(keyId, cls, options = {}) {
612
752
  });
613
753
  }
614
754
  //#endregion
615
- export { generateCryptoKeyPair as a, verifyKeyOwnership as c, fetchKeyDetailed as i, fetchActorDocument as n, importJwk as o, fetchKey as r, validateCryptoKey as s, exportJwk as t };
755
+ export { generateCryptoKeyPair as a, verifyKeyOwnership as c, hasPortableActor as d, isCompatibleKeyId as f, isSamePublicKey as h, fetchKeyDetailed as i, getCanonicalPortableId as l, isPortableUri as m, fetchActorDocument as n, importJwk as o, isPortableActorDocument as p, fetchKey as r, validateCryptoKey as s, exportJwk as t, getGatewayKeyBase as u };
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-99KVDtMr.js";
3
+ import { R as validateCryptoKey, nt as recordDocumentCache, t as doubleKnock } from "./http-BoWBwC0g.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
6
6
  import { curry } from "es-toolkit";
@@ -1,5 +1,5 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-CVjwUCCu.cjs");
2
+ const require_http = require("./http-CuLWIJdf.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
4
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
5
5
  let es_toolkit = require("es-toolkit");
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { p as recordDocumentCache } from "./metrics-B5spFq82.mjs";
4
+ import { p as recordDocumentCache } from "./metrics-B5Rei-HC.mjs";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import { preloadedContexts } from "@fedify/vocab-runtime";
7
7
  //#region src/utils/kv-cache.ts
@@ -1,9 +1,9 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-CVZgG4sc.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-B5spFq82.mjs";
6
- import { r as fetchKey, s as validateCryptoKey } from "./key-BnMA6vpl.mjs";
4
+ import { n as version, t as name } from "./deno-B-NtYZVW.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-B5Rei-HC.mjs";
6
+ import { r as fetchKey, s as validateCryptoKey } from "./key-lfcx_NaE.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
9
9
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,7 +1,7 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-CVZgG4sc.mjs";
4
+ import { n as version, t as name } from "./deno-B-NtYZVW.mjs";
5
5
  import { metrics } from "@opentelemetry/api";
6
6
  import { FetchError } from "@fedify/vocab-runtime";
7
7
  //#region src/federation/metrics.ts