@fedify/fedify 2.4.0-dev.2084 → 2.4.0-dev.2093

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 (75) hide show
  1. package/dist/{builder-BGlpUocK.mjs → builder-Ld4Y9ymR.mjs} +34 -3
  2. package/dist/compat/transformers.test.mjs +1 -1
  3. package/dist/{compound-proof-B6Aj2kGY.mjs → compound-proof-CTx_lewQ.mjs} +191 -13
  4. package/dist/{deno-Dn-py9Zy.mjs → deno-8t8k_6Yv.mjs} +1 -1
  5. package/dist/{docloader-BUq5bYJr.mjs → docloader-DJKcsAiF.mjs} +2 -2
  6. package/dist/federation/builder.test.mjs +1 -1
  7. package/dist/federation/compound-fanout.test.mjs +3 -3
  8. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  9. package/dist/federation/handler.test.mjs +5 -5
  10. package/dist/federation/hashlink-media.test.mjs +2 -2
  11. package/dist/federation/idempotency.test.mjs +2 -2
  12. package/dist/federation/metrics.test.mjs +1 -1
  13. package/dist/federation/middleware.test.mjs +7 -7
  14. package/dist/federation/mod.cjs +1 -1
  15. package/dist/federation/mod.js +1 -1
  16. package/dist/federation/outgoing-proof.test.mjs +254 -5
  17. package/dist/federation/portable-gateway-keys.test.mjs +72 -11
  18. package/dist/federation/portable-inbox.test.mjs +116 -4
  19. package/dist/federation/portable.test.mjs +182 -4
  20. package/dist/federation/send.test.mjs +3 -3
  21. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  22. package/dist/federation/tasks/tasks.test.mjs +2 -2
  23. package/dist/federation/temporal.test.mjs +1 -1
  24. package/dist/federation/webfinger.test.mjs +89 -1
  25. package/dist/{federation-BLGx8JUH.cjs → federation-aSHO04ob.cjs} +360 -65
  26. package/dist/{http-B0VRNipE.mjs → http-B2ftY0Ha.mjs} +4 -4
  27. package/dist/{http-B5q1CHtL.cjs → http-DSaSAN7X.cjs} +1609 -1457
  28. package/dist/{http-4PCCvhsU.js → http-DyT7DP1Y.js} +1518 -1390
  29. package/dist/{key-BJrp524f.mjs → key-Dwm4VswT.mjs} +39 -99
  30. package/dist/{kv-cache-Dhhhrryv.cjs → kv-cache-BSJAlP4C.cjs} +1 -1
  31. package/dist/{kv-cache-C4C3LyYW.mjs → kv-cache-D0yYukqk.mjs} +1 -1
  32. package/dist/{kv-cache-1KUmAPH7.js → kv-cache-DmroAz3M.js} +1 -1
  33. package/dist/{ld-BmFz5NLA.mjs → ld-BYZOoDzZ.mjs} +3 -3
  34. package/dist/{metrics-Beod6tzO.mjs → metrics-B7YwKTFr.mjs} +1 -1
  35. package/dist/{middleware-B7CNsyqt.mjs → middleware-BNeqKSsL.mjs} +1 -1
  36. package/dist/{middleware-D_9pNYe9.js → middleware-Blq0882d.js} +368 -73
  37. package/dist/{middleware-BKeQYPm0.mjs → middleware-E_0IH72m.mjs} +166 -71
  38. package/dist/{mod-HDSvS-ww.d.cts → mod-C8LieBzF.d.cts} +16 -3
  39. package/dist/{mod-C9WSpZWm.d.ts → mod-Coacostr.d.ts} +16 -3
  40. package/dist/mod.cjs +5 -5
  41. package/dist/mod.d.cts +1 -1
  42. package/dist/mod.d.ts +1 -1
  43. package/dist/mod.js +5 -5
  44. package/dist/nodeinfo/handler.test.mjs +1 -1
  45. package/dist/{owner-CsKVgMzk.mjs → owner-Cgx2Dc7I.mjs} +5 -4
  46. package/dist/{owner-_pbbgLJK.js → owner-DAGSC5gX.js} +2 -2
  47. package/dist/{owner-BQFZtFV4.cjs → owner-szIZl4qI.cjs} +2 -2
  48. package/dist/{portable-key-B4R-CiIV.mjs → portable-key-Cn1WJSN1.mjs} +60 -41
  49. package/dist/portable-key-id-Bva5iT3W.mjs +136 -0
  50. package/dist/{proof-ozG4HfcQ.mjs → proof-DVXC6nYz.mjs} +59 -18
  51. package/dist/{send-ClPt8nYu.mjs → send-D-hOvYKy.mjs} +3 -3
  52. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  53. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  54. package/dist/sig/compound-proof-verification.test.mjs +160 -3
  55. package/dist/sig/compound-proof.test.mjs +2 -2
  56. package/dist/sig/http.test.mjs +2 -2
  57. package/dist/sig/key.test.mjs +1 -1
  58. package/dist/sig/ld.test.mjs +2 -2
  59. package/dist/sig/mod.cjs +3 -3
  60. package/dist/sig/mod.d.cts +1 -1
  61. package/dist/sig/mod.d.ts +1 -1
  62. package/dist/sig/mod.js +3 -3
  63. package/dist/sig/owner.test.mjs +1 -1
  64. package/dist/sig/portable-collection.test.mjs +69 -5
  65. package/dist/sig/portable-key.test.mjs +176 -10
  66. package/dist/sig/proof.test.mjs +107 -4
  67. package/dist/{sig-DhdomAN0.js → sig-B5Ell7pO.js} +19 -3
  68. package/dist/{sig-k3C8sF7g.cjs → sig-CXOKN4Br.cjs} +19 -3
  69. package/dist/{temporal-Ba0EAZAI.mjs → temporal-CqpKGEPk.mjs} +1 -1
  70. package/dist/testing/kv.test.mjs +1 -1
  71. package/dist/utils/docloader.test.mjs +2 -2
  72. package/dist/utils/kv-cache.test.mjs +1 -1
  73. package/dist/utils/mod.cjs +1 -1
  74. package/dist/utils/mod.js +1 -1
  75. package/package.json +7 -7
@@ -1,27 +1,28 @@
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-Dn-py9Zy.mjs";
5
- import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-Beod6tzO.mjs";
4
+ import { n as version, t as name } from "./deno-8t8k_6Yv.mjs";
5
+ import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-B7YwKTFr.mjs";
6
6
  import { t as formatAcceptSignature } from "./accept-CPVqki78.mjs";
7
- import { d as hasPortableActor, f as isCompatibleKeyId, l as getCanonicalPortableId, m as isPortableUri, o as importJwk, s as validateCryptoKey, t as exportJwk, u as getGatewayKeyBase } from "./key-BJrp524f.mjs";
7
+ import { a as isCompatibleKeyId, c as isPortableUri, i as hasPortableActor, n as getGatewayKeyBase, r as getPortableDid, s as isPortableId, t as getCanonicalPortableId } from "./portable-key-id-Bva5iT3W.mjs";
8
+ import { o as importJwk, s as validateCryptoKey, t as exportJwk } from "./key-Dwm4VswT.mjs";
8
9
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-4IJqPYos.mjs";
9
- import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-BmFz5NLA.mjs";
10
- import { a as verifyObject, n as hasProofLike, o as verifyPortableObjectProof, r as signObject } from "./proof-ozG4HfcQ.mjs";
11
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-B0VRNipE.mjs";
12
- import { t as getAuthenticatedDocumentLoader } from "./docloader-BUq5bYJr.mjs";
13
- import { n as kvCache } from "./kv-cache-C4C3LyYW.mjs";
10
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-BYZOoDzZ.mjs";
11
+ import { i as signObject, l as verifyPortableObjectProofWithRoot, o as verifyObject, r as hasProofLike, s as verifyPortableObjectProof, t as InvalidPortableObjectIdError } from "./proof-DVXC6nYz.mjs";
12
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-B2ftY0Ha.mjs";
13
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-DJKcsAiF.mjs";
14
+ import { n as kvCache } from "./kv-cache-D0yYukqk.mjs";
14
15
  import { d as readBoundedText, l as BodyTooLargeError, t as getNodeInfo, u as MAX_BODY_SIZE } from "./client-DpnpgjyX.mjs";
15
16
  import { t as nodeInfoToJson } from "./types-DE_LV1JB.mjs";
16
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-CsKVgMzk.mjs";
17
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-ClPt8nYu.mjs";
18
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-BGlpUocK.mjs";
17
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-Cgx2Dc7I.mjs";
18
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-D-hOvYKy.mjs";
19
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-Ld4Y9ymR.mjs";
19
20
  import { t as CircuitBreaker } from "./circuit-breaker-C5NqoLBJ.mjs";
20
21
  import { t as buildCollectionSynchronizationHeader } from "./collection-CnnP1UAv.mjs";
21
- import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "./compound-proof-B6Aj2kGY.mjs";
22
+ import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "./compound-proof-CTx_lewQ.mjs";
22
23
  import { t as KvKeyCache } from "./keycache-BUDHB5rY.mjs";
23
24
  import { t as acceptsJsonLd } from "./negotiation-CCocWT_N.mjs";
24
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-Ba0EAZAI.mjs";
25
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-CqpKGEPk.mjs";
25
26
  import { t as createExponentialBackoffPolicy } from "./retry-zX5xw5e0.mjs";
26
27
  import { t as TaskCodec } from "./codec-C6rTrpIp.mjs";
27
28
  import { getLogger, withContext } from "@logtape/logtape";
@@ -30,8 +31,9 @@ import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey,
30
31
  import { lookupWebFinger } from "@fedify/webfinger";
31
32
  import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
32
33
  import { uniq } from "es-toolkit";
33
- import { FetchError, canonicalizePortableUri, decodeMultibase, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, haveSameFe34Origin, isGatewayUrl, parseDigestMultibase, parseHashlink, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
34
+ import { FetchError, canonicalizePortableUri, decodeMultibase, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, isGatewayUrl, parseDigestMultibase, parseHashlink, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
34
35
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
36
+ import { isCompatibleEf61Iri } from "@fedify/vocab-runtime/internal/portable-dereference";
35
37
  import jsonld from "@fedify/vocab-runtime/jsonld";
36
38
  import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
37
39
  import { domainToASCII } from "node:url";
@@ -789,14 +791,7 @@ async function handlePortableObject(request, { values, context, objectDispatcher
789
791
  } });
790
792
  }
791
793
  function isRequestedPortableObject(object, canonicalId) {
792
- if (object.id == null) return false;
793
- if (object.id.protocol !== "ap:" && object.id.protocol !== "ap+ef61:") return false;
794
- try {
795
- return canonicalizePortableUri(formatIri(object.id)) === canonicalId;
796
- } catch (error) {
797
- if (error instanceof TypeError) return false;
798
- throw error;
799
- }
794
+ return object.id != null && getCanonicalPortableId(object.id) === canonicalId;
800
795
  }
801
796
  async function getJsonLdRootId(jsonLd, contextLoader) {
802
797
  const expanded = await jsonld.expand(jsonLd, {
@@ -808,6 +803,7 @@ async function getJsonLdRootId(jsonLd, contextLoader) {
808
803
  return typeof id === "string" ? id : null;
809
804
  }
810
805
  function isRequestedPortableId(id, canonicalId) {
806
+ if (!/^ap(?:\+ef61)?:\/\//i.test(id)) return URL.canParse(id) && getCanonicalPortableId(new URL(id)) === canonicalId;
811
807
  try {
812
808
  return canonicalizePortableUri(id) === canonicalId;
813
809
  } catch (error) {
@@ -858,13 +854,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
858
854
  * @param parameters The parameters for handling the collection.
859
855
  * @returns A promise that resolves to an HTTP response.
860
856
  */
861
- async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
862
- const spanName = name$1.trim().replace(/\s+/g, "_");
857
+ async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
858
+ const spanName = name$2.trim().replace(/\s+/g, "_");
863
859
  tracerProvider = tracerProvider ?? trace.getTracerProvider();
864
860
  const tracer = tracerProvider.getTracer(name, version);
865
861
  const cursor = new URL(request.url).searchParams.get("cursor");
866
862
  const metricBase = {
867
- kind: getCollectionMetricKind(name$1),
863
+ kind: getCollectionMetricKind(name$2),
868
864
  page: cursor != null,
869
865
  dispatcher: "built_in"
870
866
  };
@@ -905,7 +901,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
905
901
  span.setStatus({ code: SpanStatusCode.ERROR });
906
902
  return await onNotFound(request);
907
903
  }
908
- const items = filterCollectionItems(page.items, name$1, filterPredicate);
904
+ const items = filterCollectionItems(page.items, name$2, filterPredicate);
909
905
  itemCount = items.length;
910
906
  span.setAttribute("fedify.collection.items", itemCount);
911
907
  return items;
@@ -945,7 +941,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
945
941
  } else {
946
942
  const uri = new URL(baseUri);
947
943
  uri.searchParams.set("cursor", cursor);
948
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
944
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
949
945
  kind: SpanKind.SERVER,
950
946
  attributes: {
951
947
  "activitypub.collection.id": uri.href,
@@ -961,7 +957,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
961
957
  span.setStatus({ code: SpanStatusCode.ERROR });
962
958
  return await onNotFound(request);
963
959
  }
964
- const items = filterCollectionItems(page.items, name$1, filterPredicate);
960
+ const items = filterCollectionItems(page.items, name$2, filterPredicate);
965
961
  itemCount = items.length;
966
962
  span.setAttribute("fedify.collection.items", itemCount);
967
963
  return {
@@ -1670,7 +1666,7 @@ async function handleInboxInternal(request, parameters, span) {
1670
1666
  return await respondInvalidActivity(error);
1671
1667
  }
1672
1668
  activityVerified = true;
1673
- if (!skipSignatureVerification && hasPortableActor(activity)) try {
1669
+ if (!skipSignatureVerification && isPortableActivity(activity)) try {
1674
1670
  proofVerified = await verifyObject(Activity, jsonWithoutSig, {
1675
1671
  contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
1676
1672
  documentLoader: ctx.documentLoader,
@@ -1854,7 +1850,7 @@ async function handleInboxInternal(request, parameters, span) {
1854
1850
  "http_signatures.verified": httpSigKey != null,
1855
1851
  "http_signatures.key_id": httpSigKey?.id?.href ?? ""
1856
1852
  });
1857
- if (!skipSignatureVerification && !proofVerified && (hasPortableActor(activity) || isPortableGatewayKey(httpSigKey))) {
1853
+ if (!skipSignatureVerification && !proofVerified && (isPortableActivity(activity) || isPortableGatewayKey(httpSigKey))) {
1858
1854
  if (deferredLdSignatureError != null) throw deferredLdSignatureError;
1859
1855
  logger.error("The activity {activityId} of the portable actor {actorId} is not authenticated by a valid Object Integrity Proof.", {
1860
1856
  activity: json,
@@ -1872,6 +1868,32 @@ async function handleInboxInternal(request, parameters, span) {
1872
1868
  headers: { "Content-Type": "text/plain; charset=utf-8" }
1873
1869
  });
1874
1870
  }
1871
+ if (!skipSignatureVerification && activity.id != null && isPortableId(activity.id)) {
1872
+ const rejection = await verifyPortableActivityId(activity.id, jsonWithoutSig, {
1873
+ contextLoader: wrapContextLoaderForJsonLd(ctx.contextLoader),
1874
+ documentLoader: ctx.documentLoader,
1875
+ keyCache,
1876
+ meterProvider,
1877
+ tracerProvider
1878
+ });
1879
+ if (rejection != null) {
1880
+ if (deferredLdSignatureError != null) throw deferredLdSignatureError;
1881
+ logger.error("The portable activity {activityId} is not authenticated by an Object Integrity Proof of its own DID: {reason}", {
1882
+ activity: json,
1883
+ recipient,
1884
+ activityId: activity.id.href,
1885
+ reason: rejection
1886
+ });
1887
+ span.setStatus({
1888
+ code: SpanStatusCode.ERROR,
1889
+ message: `The portable activity (${activity.id.href}) is not authenticated by an Object Integrity Proof of its own DID.`
1890
+ });
1891
+ return new Response("Portable activities must have valid Object Integrity Proofs made by their own DIDs.", {
1892
+ status: 401,
1893
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
1894
+ });
1895
+ }
1896
+ }
1875
1897
  if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
1876
1898
  if (deferredLdSignatureError != null) throw deferredLdSignatureError;
1877
1899
  getFederationMetrics(parameters.meterProvider).recordSignatureVerificationFailure("actorKeyMismatch", httpSigKey.id == null ? void 0 : getRemoteHost(httpSigKey.id));
@@ -2109,8 +2131,8 @@ var CustomCollectionHandler = class {
2109
2131
  * @param CollectionPage The CollectionPage constructor.
2110
2132
  * @param filterPredicate Optional filter predicate for items.
2111
2133
  */
2112
- constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
2113
- this.name = name$2;
2134
+ constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
2135
+ this.name = name$1;
2114
2136
  this.values = values;
2115
2137
  this.context = context;
2116
2138
  this.callbacks = callbacks;
@@ -2601,7 +2623,41 @@ const MIN_COMPONENTS = [
2601
2623
  * establish.
2602
2624
  */
2603
2625
  function isPortableGatewayKey(key) {
2604
- return key?.id != null && isCompatibleKeyId(key.id) && key.ownerId != null && isPortableUri(key.ownerId);
2626
+ return key?.id != null && isCompatibleKeyId(key.id) && key.ownerId != null && isPortableId(key.ownerId);
2627
+ }
2628
+ /**
2629
+ * Checks whether an activity needs an Object Integrity Proof by its DID:
2630
+ * either it is performed by an [FEP-ef61] portable actor, or its own ID is
2631
+ * portable. Both `ap:` URIs and compatible identifiers count as portable.
2632
+ *
2633
+ * [FEP-ef61]: https://w3id.org/fep/ef61
2634
+ */
2635
+ function isPortableActivity(activity) {
2636
+ return hasPortableActor(activity) || activity.id != null && isPortableId(activity.id);
2637
+ }
2638
+ /**
2639
+ * Verifies that a portable activity's own ID belongs to the DID that signed
2640
+ * it, on the JSON-LD document received. The ID the proof policy checks has
2641
+ * to be the parsed activity's ID, so that a remote context that changes
2642
+ * between loads cannot make the two name different objects.
2643
+ * @returns `null` if the ID is verified, or the reason why it is not.
2644
+ */
2645
+ async function verifyPortableActivityId(activityId, jsonLd, options) {
2646
+ const expectedId = getCanonicalPortableId(activityId);
2647
+ if (expectedId == null) return "malformed portable ID";
2648
+ let verification;
2649
+ try {
2650
+ verification = await verifyPortableObjectProofWithRoot(jsonLd, options);
2651
+ } catch (error) {
2652
+ if (error instanceof InvalidPortableObjectIdError) return "malformed portable ID";
2653
+ if (!isPermanentActivityParseError(error)) throw error;
2654
+ return "malformed document";
2655
+ }
2656
+ const { result, root } = verification;
2657
+ if (!result.verified) return result.reason.type;
2658
+ const rootId = root?.["@id"];
2659
+ if (typeof rootId !== "string" || getCanonicalPortableId(parseIri(rootId)) !== expectedId) return "the verified document has another ID";
2660
+ return null;
2605
2661
  }
2606
2662
  //#endregion
2607
2663
  //#region src/federation/portable-inbox.ts
@@ -2649,16 +2705,17 @@ function resolvePortableInboxRecipient(actor, { authority, canonicalInboxId, loc
2649
2705
  reason: "notFound"
2650
2706
  };
2651
2707
  const id = actor.id;
2652
- if (id == null || id.protocol !== "ap:" && id.protocol !== "ap+ef61:") return {
2708
+ const did = id == null || !isPortableId(id) ? null : getPortableDid(id);
2709
+ if (id == null || did == null) return {
2653
2710
  status: "rejected",
2654
2711
  reason: "notPortable"
2655
2712
  };
2656
- if (!isSameAuthority(id, authority)) return {
2713
+ if (did !== authority) return {
2657
2714
  status: "rejected",
2658
2715
  reason: "authorityMismatch"
2659
2716
  };
2660
- const inboxId = actor.inboxId;
2661
- if (inboxId == null || canonicalize(inboxId) !== canonicalInboxId) return {
2717
+ const inboxId = actor.inboxId == null ? null : toPortableUri(actor.inboxId);
2718
+ if (inboxId == null || getCanonicalPortableId(inboxId) !== canonicalInboxId) return {
2662
2719
  status: "rejected",
2663
2720
  reason: "inboxMismatch"
2664
2721
  };
@@ -2677,17 +2734,15 @@ function resolvePortableInboxRecipient(actor, { authority, canonicalInboxId, loc
2677
2734
  }
2678
2735
  };
2679
2736
  }
2680
- function isSameAuthority(id, authority) {
2681
- try {
2682
- return getFe34Origin(id) === authority;
2683
- } catch (error) {
2684
- if (error instanceof TypeError) return false;
2685
- throw error;
2686
- }
2687
- }
2688
- function canonicalize(iri) {
2737
+ /**
2738
+ * Gets the portable form of an `ap:` or `ap+ef61:` URI or a compatible
2739
+ * identifier, keeping its query.
2740
+ * @returns The portable URI, or `null` if the ID is neither, or is malformed.
2741
+ */
2742
+ function toPortableUri(id) {
2743
+ if (isPortableUri(id)) return id;
2689
2744
  try {
2690
- return canonicalizePortableUri(typeof iri === "string" ? iri : formatIri(iri));
2745
+ return fromCompatibleEf61Id(id);
2691
2746
  } catch (error) {
2692
2747
  if (error instanceof TypeError) return null;
2693
2748
  throw error;
@@ -2721,7 +2776,7 @@ async function forwardPortableInboxActivity(parameters) {
2721
2776
  const { recipient, activityId, excludedOrigins, kv, kvPrefix, outboxQueue } = parameters;
2722
2777
  const { maxTargets, ttl } = parameters.options ?? resolvePortableInboxForwardingOptions();
2723
2778
  if (kv.cas == null || maxTargets < 1) return [];
2724
- const canonicalActivityId = canonicalize(activityId) ?? activityId.href;
2779
+ const canonicalActivityId = getCanonicalPortableId(activityId) ?? activityId.href;
2725
2780
  const targets = getForwardingTargets(recipient, excludedOrigins);
2726
2781
  if (targets.length > maxTargets) {
2727
2782
  logger.warn("The portable actor that owns the inbox {inbox} has more than {max} other gateways; the activity {activityId} is forwarded only to the first {max} of them.", {
@@ -2908,34 +2963,50 @@ async function enqueueForwarding(parameters, outboxQueue, inboxes) {
2908
2963
  }
2909
2964
  //#endregion
2910
2965
  //#region src/federation/outgoing-proof.ts
2911
- function isPortableUrl(url) {
2912
- return url.protocol === "ap:" || url.protocol === "ap+ef61:";
2913
- }
2914
2966
  function formatKeyIds(candidates) {
2915
2967
  return candidates.map((c) => c.verificationMethod.href).join(", ");
2916
2968
  }
2917
2969
  /**
2918
2970
  * Rejects an activity of a portable actor whose ID cannot be authenticated by
2919
2971
  * the actor's DID: FEP-ef61 requires the activity to be a portable object
2920
- * signed by that DID, so its ID has to be a portable ID with the same DID.
2972
+ * signed by that DID, so its ID has to be a portable ID, i.e., an `ap:` or
2973
+ * `ap+ef61:` URI or a compatible identifier, with the same DID. A portable
2974
+ * actor is one whose ID is such an ID as well.
2921
2975
  * @throws {TypeError} If the activity is performed by a portable actor, but
2922
- * its ID is not a portable ID of the actor's DID, or its
2923
- * portable actors do not share a DID.
2976
+ * its ID is not a portable ID of the actor's DID, its
2977
+ * portable actors do not share a DID, or the ID of one of
2978
+ * them is a malformed compatible identifier.
2924
2979
  * @internal
2925
2980
  */
2926
2981
  function assertPortableActorActivity(activity) {
2927
- const portableActors = activity.actorIds.filter(isPortableUrl);
2982
+ const portableActors = activity.actorIds.filter((id) => isPortableId(id));
2928
2983
  if (portableActors.length < 1) return;
2929
- const did = getFe34Origin(portableActors[0]);
2930
- if (portableActors.some((actorId) => getFe34Origin(actorId) !== did)) throw new TypeError(`The activity ${activity.id?.href} has portable actors with different DIDs (${portableActors.map((a) => a.href).join(", ")}); a portable activity is signed by a single DID.`);
2931
- if (activity.id == null || !isPortableUrl(activity.id) || getFe34Origin(activity.id) !== did) throw new TypeError(`The activity ${activity.id?.href} is performed by the portable actor ${portableActors[0].href}, so its ID has to be an ap: or ap+ef61: URI with the same DID (${did}), as FEP-ef61 requires it to carry an Object Integrity Proof made by that DID.`);
2984
+ const dids = portableActors.map((actorId) => {
2985
+ const did = getPortableDid(actorId);
2986
+ if (did == null) throw new TypeError(`The activity ${activity.id?.href} is performed by the actor ${actorId.href}, whose ID is a malformed FEP-ef61 portable ID. A compatible identifier must have a valid DID and an object path, and must not have credentials or @gateway location hints.`);
2987
+ return did;
2988
+ });
2989
+ const did = dids[0];
2990
+ if (dids.some((d) => d !== did)) throw new TypeError(`The activity ${activity.id?.href} has portable actors with different DIDs (${portableActors.map((a) => a.href).join(", ")}); a portable activity is signed by a single DID.`);
2991
+ if (activity.id == null || !isPortableId(activity.id) || getPortableDid(activity.id) !== did) throw new TypeError(`The activity ${activity.id?.href} is performed by the portable actor ${portableActors[0].href}, so its ID has to be an ap: or ap+ef61: URI, or an FEP-ef61 compatible identifier, with the same DID (${did}), as FEP-ef61 requires it to carry an Object Integrity Proof made by that DID.`);
2992
+ if (!isCompatibleEf61Iri(activity.id)) return;
2993
+ const mismatched = portableActors.find((actorId) => isCompatibleEf61Iri(actorId) && actorId.origin !== activity.id?.origin);
2994
+ if (mismatched == null) return;
2995
+ getLogger([
2996
+ "fedify",
2997
+ "federation",
2998
+ "outbox"
2999
+ ]).warn("The activity {activityId} and its actor {actorId} are identified by FEP-ef61 compatible identifiers on different gateways. FEP-ef61 requires publishers to use the first gateway in the actor's gateways when constructing compatible identifiers.", {
3000
+ activityId: activity.id.href,
3001
+ actorId: mismatched.href
3002
+ });
2932
3003
  }
2933
3004
  /**
2934
3005
  * Creates the Object Integrity Proofs Fedify attaches to an outgoing activity.
2935
3006
  *
2936
3007
  * Keys whose IDs are FEP-ef61 compatible identifiers are gateway keys, which
2937
3008
  * never sign proofs. A portable activity, i.e., one with an `ap:` or
2938
- * `ap+ef61:` ID, that carries no proof is signed only by the one Ed25519 key
3009
+ * `ap+ef61:` ID or a compatible identifier, that carries no proof is signed only by the one Ed25519 key
2939
3010
  * whose verification method is a DID URL for its DID, even if it is the only
2940
3011
  * key; if no key or more than one key qualifies, this function throws.
2941
3012
  *
@@ -2974,7 +3045,7 @@ async function signOutgoingActivity(activity, candidates, options) {
2974
3045
  break;
2975
3046
  }
2976
3047
  const keys = candidates.filter((c) => c.privateKey.algorithm.name === "Ed25519" && !isCompatibleKeyId(c.verificationMethod));
2977
- if (!hasProof && activity.id != null && isPortableUrl(activity.id)) return await sign(selectPortableActivityKey(activity, activity.id, keys));
3048
+ if (!hasProof && activity.id != null && isPortableId(activity.id)) return await sign(selectPortableActivityKey(activity, activity.id, keys));
2978
3049
  if (keys.length < 1 || hasProof && !options.appendToExistingProofs) return {
2979
3050
  activity,
2980
3051
  hasProof,
@@ -3007,8 +3078,9 @@ async function signOutgoingActivity(activity, candidates, options) {
3007
3078
  * @throws {TypeError} If no key or more than one key qualifies.
3008
3079
  */
3009
3080
  function selectPortableActivityKey(activity, activityId, keys) {
3010
- const activityDid = getFe34Origin(activityId);
3011
- const eligible = keys.filter((c) => c.verificationMethod.protocol === "did:" && haveSameFe34Origin(activityId, c.verificationMethod));
3081
+ const activityDid = getPortableDid(activityId);
3082
+ if (activityDid == null) throw new TypeError(`Cannot sign the portable activity ${activityId.href}, as its ID is a malformed FEP-ef61 portable ID. A compatible identifier must have a valid DID and an object path, and must not have credentials or @gateway location hints.`);
3083
+ const eligible = keys.filter((c) => c.verificationMethod.protocol === "did:" && getPortableDid(c.verificationMethod) === activityDid);
3012
3084
  if (eligible.length !== 1) throw new TypeError(`Cannot choose an Object Integrity Proof key for the portable activity ${activityId.href}: ` + (eligible.length < 1 ? `none of its ${keys.length} Ed25519 keys (${formatKeyIds(keys)}) ` : `${eligible.length} of its Ed25519 keys (${formatKeyIds(eligible)}) `) + `${eligible.length < 1 ? "is" : "are"} a DID URL verification method for ${activityDid}. FEP-ef61 requires a portable activity's proof to be made by its DID, and the map-local compound-proof profile accepts exactly one direct proof per map. Gateway keys, whose IDs are compatible identifiers, never sign proofs. Supply exactly one Ed25519 key whose key ID is a DID URL for ${activityDid}, or sign the activity with signObject() before sending it.`);
3013
3085
  getLogger([
3014
3086
  "fedify",
@@ -3255,6 +3327,27 @@ function getWebFingerSubjectAndAliases({ resourceUrl, actorUri, preferredUsernam
3255
3327
  };
3256
3328
  }
3257
3329
  /**
3330
+ * Gets the compatible identifier that a WebFinger response links to for
3331
+ * an FEP-ef61 portable actor. An actor identified by an `ap:` or `ap+ef61:`
3332
+ * URI is linked through its compatible identifier on the given gateway, while
3333
+ * an actor identified by a compatible identifier is linked through its ID as
3334
+ * is, so that software without FEP-ef61 support finds the same ID in the actor
3335
+ * document.
3336
+ * @param actorId The portable actor's ID.
3337
+ * @param gateway The actor's first gateway.
3338
+ * @returns The compatible identifier, or `null` if the actor ID is malformed
3339
+ * or cannot be represented as a compatible identifier.
3340
+ */
3341
+ function getCompatibleActorId(actorId, gateway) {
3342
+ try {
3343
+ if (isPortableUri(actorId)) return toCompatibleEf61Id(actorId, gateway);
3344
+ return fromCompatibleEf61Id(actorId) == null ? null : actorId;
3345
+ } catch (error) {
3346
+ if (error instanceof TypeError) return null;
3347
+ throw error;
3348
+ }
3349
+ }
3350
+ /**
3258
3351
  * Computes the subject and aliases for an FEP-ef61 portable actor, whose
3259
3352
  * WebFinger address takes its domain from the first gateway of the actor
3260
3353
  * rather than from the actor ID. The canonical `ap:` ID is not emitted,
@@ -3418,21 +3511,23 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
3418
3511
  });
3419
3512
  let actorUri = context.getActorUri(identifier);
3420
3513
  let portable;
3421
- if (actor.id != null && (actor.id.protocol === "ap:" || actor.id.protocol === "ap+ef61:")) {
3514
+ if (actor.id != null && isPortableId(actor.id)) {
3422
3515
  const gateway = actor.gateway;
3423
- let compatibleId;
3424
- if (gateway != null && isGatewayUrl(gateway)) try {
3425
- compatibleId = toCompatibleEf61Id(actor.id, gateway);
3426
- } catch (error) {
3427
- if (!(error instanceof TypeError)) throw error;
3428
- }
3429
- if (gateway == null || compatibleId == null) {
3516
+ if (gateway == null || !isGatewayUrl(gateway)) {
3430
3517
  logger.error("The portable actor {actorId} (identifier: {identifier}) needs an HTTP(S) origin as the first item of its gateways property to be discovered through WebFinger.", {
3431
3518
  actorId: formatIri(actor.id),
3432
3519
  identifier
3433
3520
  });
3434
3521
  return await onNotFound(request);
3435
3522
  }
3523
+ const compatibleId = getCompatibleActorId(actor.id, gateway);
3524
+ if (compatibleId == null) {
3525
+ logger.error("The portable actor {actorId} (identifier: {identifier}) cannot be discovered through WebFinger, as its ID is malformed or cannot be represented as an FEP-ef61 compatible identifier.", {
3526
+ actorId: formatIri(actor.id),
3527
+ identifier
3528
+ });
3529
+ return await onNotFound(request);
3530
+ }
3436
3531
  actorUri = compatibleId;
3437
3532
  portable = {
3438
3533
  compatibleId,
@@ -6667,4 +6762,4 @@ function getRequestId(request) {
6667
6762
  return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
6668
6763
  }
6669
6764
  //#endregion
6670
- export { actorDehydrator as C, handleNodeInfoJrd as S, respondWithObjectIfAcceptable as _, OutboxContextImpl as a, handleBenchmarkTrigger as b, forwardPortableInboxActivity as c, handleCollection as d, handleCustomCollection as f, respondWithObject as g, handleOutbox as h, KvSpecDeterminer as i, resolvePortableInboxForwardingOptions as l, handleObject as m, FederationImpl as n, createFederation as o, handleInbox as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, handleActor as u, PORTABLE_OBJECT_CONTENT_TYPE as v, autoIdAssigner as w, handleNodeInfo as x, parseHashlinkGatewayRequest as y };
6765
+ export { handleNodeInfoJrd as C, handleNodeInfo as S, autoIdAssigner as T, respondWithObject as _, OutboxContextImpl as a, parseHashlinkGatewayRequest as b, assertPortableActorActivity as c, handleActor as d, handleCollection as f, handleOutbox as g, handleObject as h, KvSpecDeterminer as i, forwardPortableInboxActivity as l, handleInbox as m, FederationImpl as n, createFederation as o, handleCustomCollection as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, resolvePortableInboxForwardingOptions as u, respondWithObjectIfAcceptable as v, actorDehydrator as w, handleBenchmarkTrigger as x, PORTABLE_OBJECT_CONTENT_TYPE as y };
@@ -289,7 +289,11 @@ interface VerifyPortableObjectProofOptions extends VerifyProofOptions {}
289
289
  * @since 2.4.0
290
290
  */
291
291
  type VerifyPortableObjectProofFailureReason = {
292
- /** The document does not have a portable `ap:` or `ap+ef61:` ID. */readonly type: "notPortableObject";
292
+ /**
293
+ * The document has neither a portable `ap:` or `ap+ef61:` ID nor an
294
+ * FEP-ef61 compatible identifier.
295
+ */
296
+ readonly type: "notPortableObject";
293
297
  } | {
294
298
  /**
295
299
  * The document is a portable collection without an Object Integrity
@@ -347,15 +351,24 @@ declare function verifyProof(jsonLd: unknown, proof: DataIntegrityProof, options
347
351
  * collection without a proof is reported separately so a caller can apply a
348
352
  * gateway trust policy. Embedded portable objects are not traversed.
349
353
  *
354
+ * A portable object is a document whose ID is an `ap:` or `ap+ef61:` URI, or
355
+ * an [FEP-ef61] compatible identifier such as
356
+ * `https://gw.example/.well-known/apgateway/did:key:z6Mk…/actor`, which
357
+ * stands for the portable ID `ap+ef61://did:key:z6Mk…/actor`.
358
+ *
350
359
  * Every proof must use a DID URL whose DID matches the portable object's
351
- * authority, and every proof must pass {@link verifyProof}.
360
+ * authority, i.e., the DID of its canonical portable ID, and every proof must
361
+ * pass {@link verifyProof}. The proofs are verified over the document as
362
+ * given; a compatible identifier is never rewritten into a portable ID.
363
+ *
364
+ * [FEP-ef61]: https://w3id.org/fep/ef61
352
365
  *
353
366
  * @param jsonLd The JSON-LD document to verify.
354
367
  * @param options Additional options. See also
355
368
  * {@link VerifyPortableObjectProofOptions}.
356
369
  * @returns The detailed portable proof-policy result.
357
370
  * @throws {TypeError} If the input is not a single JSON-LD object or has a
358
- * malformed portable ID.
371
+ * malformed portable ID or compatible identifier.
359
372
  * @since 2.4.0
360
373
  */
361
374
  declare function verifyPortableObjectProof(jsonLd: unknown, options?: VerifyPortableObjectProofOptions): Promise<VerifyPortableObjectProofResult>;
@@ -289,7 +289,11 @@ interface VerifyPortableObjectProofOptions extends VerifyProofOptions {}
289
289
  * @since 2.4.0
290
290
  */
291
291
  type VerifyPortableObjectProofFailureReason = {
292
- /** The document does not have a portable `ap:` or `ap+ef61:` ID. */readonly type: "notPortableObject";
292
+ /**
293
+ * The document has neither a portable `ap:` or `ap+ef61:` ID nor an
294
+ * FEP-ef61 compatible identifier.
295
+ */
296
+ readonly type: "notPortableObject";
293
297
  } | {
294
298
  /**
295
299
  * The document is a portable collection without an Object Integrity
@@ -347,15 +351,24 @@ declare function verifyProof(jsonLd: unknown, proof: DataIntegrityProof, options
347
351
  * collection without a proof is reported separately so a caller can apply a
348
352
  * gateway trust policy. Embedded portable objects are not traversed.
349
353
  *
354
+ * A portable object is a document whose ID is an `ap:` or `ap+ef61:` URI, or
355
+ * an [FEP-ef61] compatible identifier such as
356
+ * `https://gw.example/.well-known/apgateway/did:key:z6Mk…/actor`, which
357
+ * stands for the portable ID `ap+ef61://did:key:z6Mk…/actor`.
358
+ *
350
359
  * Every proof must use a DID URL whose DID matches the portable object's
351
- * authority, and every proof must pass {@link verifyProof}.
360
+ * authority, i.e., the DID of its canonical portable ID, and every proof must
361
+ * pass {@link verifyProof}. The proofs are verified over the document as
362
+ * given; a compatible identifier is never rewritten into a portable ID.
363
+ *
364
+ * [FEP-ef61]: https://w3id.org/fep/ef61
352
365
  *
353
366
  * @param jsonLd The JSON-LD document to verify.
354
367
  * @param options Additional options. See also
355
368
  * {@link VerifyPortableObjectProofOptions}.
356
369
  * @returns The detailed portable proof-policy result.
357
370
  * @throws {TypeError} If the input is not a single JSON-LD object or has a
358
- * malformed portable ID.
371
+ * malformed portable ID or compatible identifier.
359
372
  * @since 2.4.0
360
373
  */
361
374
  declare function verifyPortableObjectProof(jsonLd: unknown, options?: VerifyPortableObjectProofOptions): Promise<VerifyPortableObjectProofResult>;
package/dist/mod.cjs CHANGED
@@ -3,14 +3,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  require("./rolldown-runtime-Dmm3t92W.cjs");
4
4
  const require_transformers = require("./transformers-BalCrSsr.cjs");
5
5
  require("./compat/mod.cjs");
6
- const require_http = require("./http-B5q1CHtL.cjs");
7
- const require_federation = require("./federation-BLGx8JUH.cjs");
6
+ const require_http = require("./http-DSaSAN7X.cjs");
7
+ const require_federation = require("./federation-aSHO04ob.cjs");
8
8
  const require_types = require("./types-Be2zbyeG.cjs");
9
- const require_owner = require("./owner-BQFZtFV4.cjs");
10
- const require_kv_cache = require("./kv-cache-Dhhhrryv.cjs");
9
+ const require_owner = require("./owner-szIZl4qI.cjs");
10
+ const require_kv_cache = require("./kv-cache-BSJAlP4C.cjs");
11
11
  require("./nodeinfo/mod.cjs");
12
12
  require("./runtime/mod.cjs");
13
- const require_sig = require("./sig-k3C8sF7g.cjs");
13
+ const require_sig = require("./sig-CXOKN4Br.cjs");
14
14
  require("./utils/mod.cjs");
15
15
  //#endregion
16
16
  exports.CircuitBreaker = require_federation.CircuitBreaker;
package/dist/mod.d.cts CHANGED
@@ -6,7 +6,7 @@ import { $ as InProcessMessageQueueOptions, A as ObjectCallbackSetters, At as In
6
6
  import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-BhNbN8Ed.cjs";
7
7
  import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.cjs";
8
8
  import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-JzkaEXsi.cjs";
9
- import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-HDSvS-ww.cjs";
9
+ import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-C8LieBzF.cjs";
10
10
  import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-xGFCHqq_.cjs";
11
11
  export * from "@fedify/vocab-runtime";
12
12
 
package/dist/mod.d.ts CHANGED
@@ -6,7 +6,7 @@ import { $ as InProcessMessageQueueOptions, A as ObjectCallbackSetters, At as In
6
6
  import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-BhNbN8Ed.js";
7
7
  import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.js";
8
8
  import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-B2h2KY0q.js";
9
- import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-C9WSpZWm.js";
9
+ import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-Coacostr.js";
10
10
  import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-CVZrXSdm.js";
11
11
  export * from "@fedify/vocab-runtime";
12
12
 
package/dist/mod.js CHANGED
@@ -3,15 +3,15 @@ import { URLPattern } from "urlpattern-polyfill";
3
3
  import "./rolldown-runtime-BC-Eo5Mj.js";
4
4
  import { n as autoIdAssigner, r as getDefaultActivityTransformers, t as actorDehydrator } from "./transformers-DAtXl_Kc.js";
5
5
  import "./compat/mod.js";
6
- import { A as verifySignature, D as signJsonLd, F as fetchKeyDetailed, I as generateCryptoKeyPair, L as importJwk, M as exportJwk, P as fetchKey, a as verifyRequestDetailed, b as createSignature, c as hasProofLike, d as verifyObject, dt as fulfillAcceptSignature, f as verifyPortableObjectProof, ft as parseAcceptSignature, h as verifyProof, i as verifyRequest, k as verifyJsonLd, l as signObject, pt as validateAcceptSignature, r as signRequest, s as createProof, ut as formatAcceptSignature, v as attachSignature, w as hasSignatureLike, x as detachSignature } from "./http-4PCCvhsU.js";
7
- import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-D_9pNYe9.js";
6
+ import { C as detachSignature, E as hasSignatureLike, I as fetchKey, L as fetchKeyDetailed, M as verifySignature, P as exportJwk, R as generateCryptoKeyPair, S as createSignature, a as verifyRequestDetailed, b as attachSignature, c as createProof, ct as parseAcceptSignature, f as verifyObject, g as verifyProof, i as verifyRequest, j as verifyJsonLd, k as signJsonLd, l as hasProofLike, lt as validateAcceptSignature, ot as formatAcceptSignature, p as verifyPortableObjectProof, r as signRequest, st as fulfillAcceptSignature, u as signObject, z as importJwk } from "./http-DyT7DP1Y.js";
7
+ import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-Blq0882d.js";
8
8
  import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-C_cNlNVK.js";
9
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-_pbbgLJK.js";
10
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-1KUmAPH7.js";
9
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DAGSC5gX.js";
10
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DmroAz3M.js";
11
11
  import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue, Router, RouterError } from "./federation/mod.js";
12
12
  import "./nodeinfo/mod.js";
13
13
  import "./runtime/mod.js";
14
- import { t as verifyPortableObject } from "./sig-DhdomAN0.js";
14
+ import { t as verifyPortableObject } from "./sig-B5Ell7pO.js";
15
15
  import "./utils/mod.js";
16
16
  export * from "@fedify/vocab-runtime";
17
17
  //#endregion
@@ -5,7 +5,7 @@ import { r as createRequestContext } from "../context-DlZhxdFT.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
- import { S as handleNodeInfoJrd, o as createFederation, x as handleNodeInfo } from "../middleware-BKeQYPm0.mjs";
8
+ import { C as handleNodeInfoJrd, S as handleNodeInfo, o as createFederation } from "../middleware-E_0IH72m.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  //#region src/nodeinfo/handler.test.ts
11
11
  test("handleNodeInfo()", async () => {
@@ -1,9 +1,10 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Dn-py9Zy.mjs";
5
- import { c as verifyKeyOwnership, f as isCompatibleKeyId, h as isSamePublicKey$1, l as getCanonicalPortableId, n as fetchActorDocument, t as exportJwk } from "./key-BJrp524f.mjs";
6
- import { t as fetchPortableGatewayKey } from "./portable-key-B4R-CiIV.mjs";
4
+ import { n as version, t as name } from "./deno-8t8k_6Yv.mjs";
5
+ import { a as isCompatibleKeyId, l as isSamePublicKey$1, s as isPortableId, t as getCanonicalPortableId } from "./portable-key-id-Bva5iT3W.mjs";
6
+ import { c as verifyKeyOwnership, n as fetchActorDocument, t as exportJwk } from "./key-Dwm4VswT.mjs";
7
+ import { t as fetchPortableGatewayKey } from "./portable-key-Cn1WJSN1.mjs";
7
8
  import { getLogger } from "@logtape/logtape";
8
9
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
9
10
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -176,7 +177,7 @@ async function getKeyOwner(keyId, options) {
176
177
  return await verifyKeyOwnership(object.id == null ? object.clone({ id: keyId }) : object, fetchOptions);
177
178
  }
178
179
  if (!isActor(object)) return null;
179
- if (object.id == null || object.id.origin !== documentUrl.origin) {
180
+ if (object.id == null || isPortableId(object.id) || object.id.origin !== documentUrl.origin) {
180
181
  logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as the owner of key {keyId}.", {
181
182
  documentUrl: documentUrl.href,
182
183
  actorId: object.id?.href,