@tinycloud/share-sdk 1.4.1-beta.0 → 1.4.1-beta.1

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.
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- export { D as DEFAULT_MAX_CONTENT_BLOB_BYTES, a as DEFAULT_MAX_SEALED_BLOB_BYTES, S as SHARE_RESULT_VERSION, b as ShareErrorCode, c as ShareErrorInfo, d as ShareFetchOptions, e as ShareInspection, f as ShareMetadata, g as ShareNodeTrust, h as SharePolicyChallenge, i as SharePolicyDelegationLink, j as SharePolicySession, k as SharePresentationMaterial, l as ShareReceiveAuthorizationRequired, m as ShareReceiveError, n as ShareReceiveOutcome, o as ShareReceiveResult, p as ShareRecipientClient, q as ShareRecipientClientOptions, r as createAddressedAuthorization, s as inspectShare, t as receiveShare, u as toShareErrorInfo } from './web-recipient-entry-CiUzdSQs.cjs';
1
+ export { D as DEFAULT_MAX_CONTENT_BLOB_BYTES, a as DEFAULT_MAX_SEALED_BLOB_BYTES, S as SHARE_RESULT_VERSION, b as ShareErrorCode, c as ShareErrorInfo, d as ShareFetchOptions, e as ShareInspection, f as ShareMetadata, g as ShareNodeTrust, h as SharePolicyAuthority, i as SharePolicyChallenge, j as SharePolicyDelegationLink, k as SharePolicyEvidence, l as SharePolicySession, m as SharePresentationMaterial, n as ShareReceiveAuthorizationRequired, o as ShareReceiveError, p as ShareReceiveOutcome, q as ShareReceiveResult, r as ShareRecipientClient, s as ShareRecipientClientOptions, t as createAddressedAuthorization, u as inspectShare, v as receiveShare, w as toShareErrorInfo } from './web-recipient-entry-Cy1fXl_5.cjs';
2
2
  export { C as CreateOwnerDelegationParams, h as DEFAULT_SHARE_LIFETIME_MS, D as DelegatedShareKey, M as MAX_CONTENT_BYTES, i as OWNER_SHARE_REGISTRATION_DOMAIN, j as OwnerDelegationPermission, g as OwnerDelegationReceipt, O as OwnerShareAction, k as OwnerShareDecryption, a as OwnerShareMatcher, l as OwnerSharePolicyRegistration, b as OwnerSharePolicyRegistrationReceipt, m as OwnerSharePolicyV2, c as PublishedShare, P as PublishedShareDeliveryMaterial, n as PublishedShareMetadata, R as RegisterOwnerSharePolicyParams, o as SHARE_CONTENT_LIMIT, p as SHARE_PUBLISH_RESULT_VERSION, f as SharePublishError, q as SharePublishErrorCode, e as SharePublishOptions, d as SharePublishTarget, S as SignedDelegation, r as canonicalOwnerSharePolicy, s as computeOwnerShareRegistrationCid, t as createDelegatedShareKey, u as createPolicyEnforcementDelegation, v as redactPublishedShare, w as restoreDelegatedShareKey, x as validateOwnerSharePolicyRegistration, y as validateOwnerSharePolicyRegistrationBytes } from './owner-policy-aPmXIR3e.cjs';
3
3
  import { ShareEnvelopeV2, ArtifactPin, UnsignedShareEnvelopeV3, ShareEnvelopeV3 } from '@tinycloud/share-envelope';
4
4
  export { CheckBearerDelegationOptions, DelegationCheckResult, canonicalize, checkBearerDelegation, fromBase64Url, requiredResourceUri, resourceUriCovers, toBase64Url } from '@tinycloud/share-envelope';
5
- import { T as TargetPublishOutcome, c as TargetPublishInput, S as ShareTarget } from './addressed-publish-BP6nsgRq.cjs';
6
- export { d as ADDRESSED_SHARE_TOO_LARGE_MESSAGE, A as AddressedOwnerRootInput, a as AddressedOwnerRootReceipt, e as AddressedPolicyRegistrationInput, f as AddressedPolicyRegistrationReceipt, b as AddressedPublishAuthority, g as AddressedShareBoundInput, h as AddressedShareOverLimitError, i as AddressedSharePublishOptions, j as AddressedShareRequest, k as AddressedShareWrites, P as PreparedAddressedShare, l as TargetPublishAdapter, m as addressedCredentialRequirement, n as addressedShareBodyUpperBound, o as assertAddressedShareFits, p as normalizeShareTarget, q as prepareAddressedShare, r as publishAddressedShare, s as publishTargetShare, t as targetAuthorizationMethod } from './addressed-publish-BP6nsgRq.cjs';
5
+ import { T as TargetPublishOutcome, c as TargetPublishInput, S as ShareTarget } from './addressed-publish-BcvisG4a.cjs';
6
+ export { d as ADDRESSED_SHARE_TOO_LARGE_MESSAGE, A as AddressedOwnerRootInput, a as AddressedOwnerRootReceipt, e as AddressedPolicyRegistrationInput, f as AddressedPolicyRegistrationReceipt, b as AddressedPublishAuthority, g as AddressedShareBoundInput, h as AddressedShareOverLimitError, i as AddressedSharePublishOptions, j as AddressedShareRequest, k as AddressedShareWrites, P as PreparedAddressedShare, l as TargetPublishAdapter, m as addressedCredentialRequirement, n as addressedShareBodyUpperBound, o as assertAddressedShareFits, p as normalizeShareTarget, q as prepareAddressedShare, r as publishAddressedShare, s as publishTargetShare, t as targetAuthorizationMethod } from './addressed-publish-BcvisG4a.cjs';
7
7
  import { S as SenderShareRecord, a as SenderShareRecordStorage } from './history-Bg25JIsi.cjs';
8
8
  export { E as EncryptedSenderShareHistory, b as EncryptedShareHistoryStorage, M as MemoryEncryptedShareHistoryStorage, c as MemorySenderShareKeyStorage, d as MemorySenderShareRecordStorage, e as SenderShareKeyStorage, f as SenderShareRecordProofs, g as SenderShareRevocationScope, h as SenderShareStore, i as historyRecordForPublishedShare } from './history-Bg25JIsi.cjs';
9
9
  import { b as ShareAuthorizationResult, c as ShareAuthorizationAdapter } from './authorization-CmP1skO8.cjs';
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { D as DEFAULT_MAX_CONTENT_BLOB_BYTES, a as DEFAULT_MAX_SEALED_BLOB_BYTES, S as SHARE_RESULT_VERSION, b as ShareErrorCode, c as ShareErrorInfo, d as ShareFetchOptions, e as ShareInspection, f as ShareMetadata, g as ShareNodeTrust, h as SharePolicyChallenge, i as SharePolicyDelegationLink, j as SharePolicySession, k as SharePresentationMaterial, l as ShareReceiveAuthorizationRequired, m as ShareReceiveError, n as ShareReceiveOutcome, o as ShareReceiveResult, p as ShareRecipientClient, q as ShareRecipientClientOptions, r as createAddressedAuthorization, s as inspectShare, t as receiveShare, u as toShareErrorInfo } from './web-recipient-entry-DVyTQBl0.js';
1
+ export { D as DEFAULT_MAX_CONTENT_BLOB_BYTES, a as DEFAULT_MAX_SEALED_BLOB_BYTES, S as SHARE_RESULT_VERSION, b as ShareErrorCode, c as ShareErrorInfo, d as ShareFetchOptions, e as ShareInspection, f as ShareMetadata, g as ShareNodeTrust, h as SharePolicyAuthority, i as SharePolicyChallenge, j as SharePolicyDelegationLink, k as SharePolicyEvidence, l as SharePolicySession, m as SharePresentationMaterial, n as ShareReceiveAuthorizationRequired, o as ShareReceiveError, p as ShareReceiveOutcome, q as ShareReceiveResult, r as ShareRecipientClient, s as ShareRecipientClientOptions, t as createAddressedAuthorization, u as inspectShare, v as receiveShare, w as toShareErrorInfo } from './web-recipient-entry-CPntAP0H.js';
2
2
  export { C as CreateOwnerDelegationParams, h as DEFAULT_SHARE_LIFETIME_MS, D as DelegatedShareKey, M as MAX_CONTENT_BYTES, i as OWNER_SHARE_REGISTRATION_DOMAIN, j as OwnerDelegationPermission, g as OwnerDelegationReceipt, O as OwnerShareAction, k as OwnerShareDecryption, a as OwnerShareMatcher, l as OwnerSharePolicyRegistration, b as OwnerSharePolicyRegistrationReceipt, m as OwnerSharePolicyV2, c as PublishedShare, P as PublishedShareDeliveryMaterial, n as PublishedShareMetadata, R as RegisterOwnerSharePolicyParams, o as SHARE_CONTENT_LIMIT, p as SHARE_PUBLISH_RESULT_VERSION, f as SharePublishError, q as SharePublishErrorCode, e as SharePublishOptions, d as SharePublishTarget, S as SignedDelegation, r as canonicalOwnerSharePolicy, s as computeOwnerShareRegistrationCid, t as createDelegatedShareKey, u as createPolicyEnforcementDelegation, v as redactPublishedShare, w as restoreDelegatedShareKey, x as validateOwnerSharePolicyRegistration, y as validateOwnerSharePolicyRegistrationBytes } from './owner-policy-aPmXIR3e.js';
3
3
  import { ShareEnvelopeV2, ArtifactPin, UnsignedShareEnvelopeV3, ShareEnvelopeV3 } from '@tinycloud/share-envelope';
4
4
  export { CheckBearerDelegationOptions, DelegationCheckResult, canonicalize, checkBearerDelegation, fromBase64Url, requiredResourceUri, resourceUriCovers, toBase64Url } from '@tinycloud/share-envelope';
5
- import { T as TargetPublishOutcome, c as TargetPublishInput, S as ShareTarget } from './addressed-publish-CpkwV-Au.js';
6
- export { d as ADDRESSED_SHARE_TOO_LARGE_MESSAGE, A as AddressedOwnerRootInput, a as AddressedOwnerRootReceipt, e as AddressedPolicyRegistrationInput, f as AddressedPolicyRegistrationReceipt, b as AddressedPublishAuthority, g as AddressedShareBoundInput, h as AddressedShareOverLimitError, i as AddressedSharePublishOptions, j as AddressedShareRequest, k as AddressedShareWrites, P as PreparedAddressedShare, l as TargetPublishAdapter, m as addressedCredentialRequirement, n as addressedShareBodyUpperBound, o as assertAddressedShareFits, p as normalizeShareTarget, q as prepareAddressedShare, r as publishAddressedShare, s as publishTargetShare, t as targetAuthorizationMethod } from './addressed-publish-CpkwV-Au.js';
5
+ import { T as TargetPublishOutcome, c as TargetPublishInput, S as ShareTarget } from './addressed-publish-CwTTn9Jn.js';
6
+ export { d as ADDRESSED_SHARE_TOO_LARGE_MESSAGE, A as AddressedOwnerRootInput, a as AddressedOwnerRootReceipt, e as AddressedPolicyRegistrationInput, f as AddressedPolicyRegistrationReceipt, b as AddressedPublishAuthority, g as AddressedShareBoundInput, h as AddressedShareOverLimitError, i as AddressedSharePublishOptions, j as AddressedShareRequest, k as AddressedShareWrites, P as PreparedAddressedShare, l as TargetPublishAdapter, m as addressedCredentialRequirement, n as addressedShareBodyUpperBound, o as assertAddressedShareFits, p as normalizeShareTarget, q as prepareAddressedShare, r as publishAddressedShare, s as publishTargetShare, t as targetAuthorizationMethod } from './addressed-publish-CwTTn9Jn.js';
7
7
  import { S as SenderShareRecord, a as SenderShareRecordStorage } from './history-kwJxrZgq.js';
8
8
  export { E as EncryptedSenderShareHistory, b as EncryptedShareHistoryStorage, M as MemoryEncryptedShareHistoryStorage, c as MemorySenderShareKeyStorage, d as MemorySenderShareRecordStorage, e as SenderShareKeyStorage, f as SenderShareRecordProofs, g as SenderShareRevocationScope, h as SenderShareStore, i as historyRecordForPublishedShare } from './history-kwJxrZgq.js';
9
9
  import { b as ShareAuthorizationResult, c as ShareAuthorizationAdapter } from './authorization-CmP1skO8.js';
package/dist/index.js CHANGED
@@ -5261,6 +5261,23 @@ var unifiedEncryptionNetworkSchema = external_exports.string().refine((value) =>
5261
5261
  const network = separator < 0 ? "" : rest.slice(separator + 1);
5262
5262
  return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
5263
5263
  }, { message: "expected canonical TinyCloud encryption network" });
5264
+ var unifiedEncryptionResourceSchema = external_exports.string().refine((value) => {
5265
+ const prefix = "urn:tinycloud:encryption:";
5266
+ if (!value.startsWith(prefix)) return false;
5267
+ const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
5268
+ if (match === null) return false;
5269
+ const path = match[3];
5270
+ return path === void 0 || path.split("/").every(
5271
+ (segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
5272
+ );
5273
+ }, { message: "expected a canonical TinyCloud encryption resource" });
5274
+ var unifiedEncryptionKeyPathSchema = external_exports.string().regex(/^share\/[^/]+$/).refine(
5275
+ (value) => {
5276
+ const segment = value.slice("share/".length);
5277
+ return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
5278
+ },
5279
+ { message: "expected an exact share key path" }
5280
+ );
5264
5281
  var unifiedKvCapabilitySchema = external_exports.object({
5265
5282
  kind: external_exports.literal("kv"),
5266
5283
  resource: unifiedResourceSchema,
@@ -5274,7 +5291,7 @@ var unifiedKvCapabilitySchema = external_exports.object({
5274
5291
  }).strict();
5275
5292
  var unifiedEncryptionCapabilitySchema = external_exports.object({
5276
5293
  kind: external_exports.literal("encryption"),
5277
- resource: unifiedEncryptionNetworkSchema,
5294
+ resource: unifiedEncryptionResourceSchema,
5278
5295
  action: external_exports.literal("tinycloud.encryption/decrypt")
5279
5296
  }).strict();
5280
5297
  var unifiedCapabilitySchema = external_exports.union([unifiedKvCapabilitySchema, unifiedEncryptionCapabilitySchema]);
@@ -5283,6 +5300,7 @@ var unifiedContentSourceSchema = external_exports.object({
5283
5300
  kvResource: unifiedResourceSchema,
5284
5301
  selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
5285
5302
  encryptionNetwork: unifiedEncryptionNetworkSchema,
5303
+ encryptionKeyPath: unifiedEncryptionKeyPathSchema.optional(),
5286
5304
  encryptedSymmetricKeyDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
5287
5305
  keyVersion: external_exports.number().int().positive(),
5288
5306
  mode: external_exports.union([external_exports.literal("mutable"), external_exports.literal("immutable")]),
@@ -5400,16 +5418,21 @@ function validateV3Invariants(value, ctx) {
5400
5418
  if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
5401
5419
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
5402
5420
  }
5403
- if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
5421
+ const encryptionKeyPath = value.contentSource.encryptionKeyPath;
5422
+ if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
5423
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
5424
+ }
5425
+ if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptionKeyPath !== encryptionKeyPath || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
5404
5426
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
5405
5427
  }
5406
5428
  if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
5407
5429
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
5408
5430
  }
5409
5431
  const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
5410
- const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === value.encryptionNetwork && capability.action === "tinycloud.encryption/decrypt");
5432
+ const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
5433
+ const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
5411
5434
  if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
5412
- ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt network" });
5435
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
5413
5436
  }
5414
5437
  }
5415
5438
  var unsignedShareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.superRefine(validateV3Invariants);
@@ -5597,6 +5620,7 @@ var SEALED_BLOB_VERSION = 1;
5597
5620
  var AAD = utf8Bytes(ENVELOPE_AAD_LABEL);
5598
5621
  var KEY_LENGTH = 32;
5599
5622
  var NONCE_LENGTH = 12;
5623
+ var TAG_LENGTH = 16;
5600
5624
  var HEADER_LENGTH = 1;
5601
5625
  function assertKey(key32) {
5602
5626
  if (key32.length !== KEY_LENGTH) {
@@ -5624,6 +5648,17 @@ async function seal(plaintextBytes, key32) {
5624
5648
  blob.set(ciphertext, HEADER_LENGTH + nonce.length);
5625
5649
  return { blob, cid: await computeCid(blob) };
5626
5650
  }
5651
+ async function open(blob, key32) {
5652
+ if (blob.length < HEADER_LENGTH + NONCE_LENGTH + TAG_LENGTH) {
5653
+ throw new TypeError(`sealed blob too short: ${blob.length} bytes`);
5654
+ }
5655
+ if (blob[0] !== SEALED_BLOB_VERSION) {
5656
+ throw new TypeError(`unknown sealed blob version: ${blob[0]}`);
5657
+ }
5658
+ const nonce = blob.subarray(HEADER_LENGTH, HEADER_LENGTH + NONCE_LENGTH);
5659
+ const ciphertext = blob.subarray(HEADER_LENGTH + NONCE_LENGTH);
5660
+ return decryptEnvelope(nonce, ciphertext, key32);
5661
+ }
5627
5662
  async function encryptEnvelope(plaintextBytes, key32) {
5628
5663
  const key = await importAesKey(key32, "encrypt");
5629
5664
  const nonce = globalThis.crypto.getRandomValues(new Uint8Array(NONCE_LENGTH));
@@ -5636,6 +5671,19 @@ async function encryptEnvelope(plaintextBytes, key32) {
5636
5671
  );
5637
5672
  return { nonce, ciphertext };
5638
5673
  }
5674
+ async function decryptEnvelope(nonce, ciphertext, key32) {
5675
+ if (nonce.length !== NONCE_LENGTH) {
5676
+ throw new TypeError(`nonce must be ${NONCE_LENGTH} bytes, got ${nonce.length}`);
5677
+ }
5678
+ const key = await importAesKey(key32, "decrypt");
5679
+ return new Uint8Array(
5680
+ await globalThis.crypto.subtle.decrypt(
5681
+ { name: "AES-GCM", iv: nonce, additionalData: AAD },
5682
+ key,
5683
+ ciphertext
5684
+ )
5685
+ );
5686
+ }
5639
5687
  function isPlainRecord(value) {
5640
5688
  const proto = Object.getPrototypeOf(value);
5641
5689
  return proto === Object.prototype || proto === null;
@@ -5781,6 +5829,7 @@ function equal(left, right) {
5781
5829
  return left.length === right.length && left.every((byte, index) => byte === right[index]);
5782
5830
  }
5783
5831
  var ED25519_VERIFY_OPTS2 = { zip215: false };
5832
+ var ENVELOPE_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
5784
5833
  var ENVELOPE_V3_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
5785
5834
  var POLICY_V1_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
5786
5835
  var POLICY_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
@@ -5788,6 +5837,14 @@ var CONTENT_SOURCE_V1_DOMAIN = "xyz.tinycloud.policy/ContentSource/v1\0";
5788
5837
  var POLICY_CAPABILITY_V1_DOMAIN = "xyz.tinycloud.policy/PolicyCapability/v1\0";
5789
5838
  var NATIVE_PROJECTION_V1_DOMAIN = "xyz.tinycloud.policy/NativeProjection/v1\0";
5790
5839
  var ATTESTED_ENFORCER_V2_DOMAIN = "xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0";
5840
+ function signingBytesV2(unsigned) {
5841
+ const domain = utf8Bytes(ENVELOPE_V2_SIGNATURE_DOMAIN);
5842
+ const body = utf8Bytes(canonicalize(unsigned));
5843
+ const bytes3 = new Uint8Array(domain.length + body.length);
5844
+ bytes3.set(domain);
5845
+ bytes3.set(body, domain.length);
5846
+ return bytes3;
5847
+ }
5791
5848
  function signingBytesV3(unsigned) {
5792
5849
  const domain = utf8Bytes(ENVELOPE_V3_SIGNATURE_DOMAIN);
5793
5850
  const body = utf8Bytes(canonicalize(unsigned));
@@ -5796,6 +5853,17 @@ function signingBytesV3(unsigned) {
5796
5853
  bytes3.set(body, domain.length);
5797
5854
  return bytes3;
5798
5855
  }
5856
+ function verifyEnvelopeV2SignatureOnly(envelope) {
5857
+ const parsed = shareEnvelopeV2Schema.parse(envelope);
5858
+ const { signature, ...unsigned } = parsed;
5859
+ return ed255193.verify(fromBase64Url(signature.value), signingBytesV2(unsigned), ed25519PublicKeyFromDidKey(signature.signerDid), ED25519_VERIFY_OPTS2);
5860
+ }
5861
+ async function verifyEnvelopeV2(envelope, options) {
5862
+ const parsed = shareEnvelopeV2Schema.parse(envelope);
5863
+ if (parsed.signature.signerDid !== options.expectedSignerDid || !verifyEnvelopeV2SignatureOnly(parsed)) return false;
5864
+ if (parsed.authorizationTarget.kind === "policy") return await computeCid(fromBase64Url(parsed.authorizationTarget.policyBytes)) === parsed.authorizationTarget.policyCid;
5865
+ return true;
5866
+ }
5799
5867
  function verifyEnvelopeV3SignatureOnly(envelope) {
5800
5868
  const parsed = shareEnvelopeV3Schema.parse(envelope);
5801
5869
  const { signature, ...unsigned } = parsed;
@@ -5882,6 +5950,52 @@ function base32Lower(bytes3) {
5882
5950
  if (bits > 0) output += alphabet[buffer << 5 - bits & 31];
5883
5951
  return output;
5884
5952
  }
5953
+ var KEY_LENGTH2 = 32;
5954
+ function assertCanonicalCid(cidString) {
5955
+ const cid2 = CID.parse(cidString);
5956
+ if (cid2.version !== 1 || cid2.code !== code || cid2.multihash.code !== 18 || // sha2-256 (0x12) only, at the link layer too
5957
+ cid2.toString() !== cidString) {
5958
+ throw new TypeError(`not a canonical CIDv1 raw sha2-256 base32 CID: ${cidString}`);
5959
+ }
5960
+ }
5961
+ function parseShareUrl(url, options = {}) {
5962
+ const parsed = new URL(url);
5963
+ if (parsed.protocol !== "https:") {
5964
+ throw new TypeError(`share URL must be https, got ${parsed.protocol}`);
5965
+ }
5966
+ if (parsed.username !== "" || parsed.password !== "") {
5967
+ throw new TypeError("share URL must not carry userinfo");
5968
+ }
5969
+ if (parsed.search !== "") {
5970
+ throw new TypeError("share URL must not have a query string");
5971
+ }
5972
+ if (options.expectedOrigin !== void 0) {
5973
+ if (!isCanonicalHttpsOrigin(options.expectedOrigin)) {
5974
+ throw new TypeError(
5975
+ `expectedOrigin must be a canonical https origin, got ${options.expectedOrigin}`
5976
+ );
5977
+ }
5978
+ if (parsed.origin !== options.expectedOrigin) {
5979
+ throw new TypeError(
5980
+ `share URL origin ${parsed.origin} does not match expected ${options.expectedOrigin}`
5981
+ );
5982
+ }
5983
+ }
5984
+ const match = /^\/s\/([a-z2-7]+)$/.exec(parsed.pathname);
5985
+ if (!match || match[1] === void 0) {
5986
+ throw new TypeError(`not a share URL path: ${parsed.pathname}`);
5987
+ }
5988
+ const ciphertextCid = match[1];
5989
+ assertCanonicalCid(ciphertextCid);
5990
+ if (!parsed.hash.startsWith("#k=")) {
5991
+ throw new TypeError("share URL is missing the #k= key fragment");
5992
+ }
5993
+ const key32 = fromBase64Url(parsed.hash.slice("#k=".length));
5994
+ if (key32.length !== KEY_LENGTH2) {
5995
+ throw new TypeError(`fragment key must be ${KEY_LENGTH2} bytes, got ${key32.length}`);
5996
+ }
5997
+ return { ciphertextCid, key32 };
5998
+ }
5885
5999
 
5886
6000
  // src/artifact-core.ts
5887
6001
  var ARTIFACT_ROOT = "artifacts";
@@ -5983,7 +6097,7 @@ function toShareErrorInfo(error) {
5983
6097
  return { protocol: "tinycloud-share", version: SHARE_RESULT_VERSION, error: { code: "fetch-failed" } };
5984
6098
  }
5985
6099
  function metadataFor(envelope, origin) {
5986
- const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : "emailDomain";
6100
+ const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : envelope.recipientMatcher.kind === "emailDomain" ? "emailDomain" : "bearer";
5987
6101
  return {
5988
6102
  protocol: "tinycloud-share",
5989
6103
  version: 1,
@@ -5998,7 +6112,7 @@ function metadataFor(envelope, origin) {
5998
6112
  ...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
5999
6113
  ...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
6000
6114
  },
6001
- ...envelope.artifactPin === void 0 ? {} : { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } }
6115
+ ...envelope.version === 3 && envelope.artifactPin !== void 0 ? { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } } : {}
6002
6116
  };
6003
6117
  }
6004
6118
  function receiveErrorFor(error) {
@@ -6096,14 +6210,165 @@ async function resolvePolicyShare(link, options) {
6096
6210
  options.onResolvedAddressedEnvelope?.(envelope, cid2);
6097
6211
  return { envelope, origin: url.origin, cid: cid2 };
6098
6212
  }
6213
+ async function resolveLegacyV2(link, options) {
6214
+ let parsed;
6215
+ try {
6216
+ parsed = parseShareUrl(link, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
6217
+ } catch {
6218
+ throw new ShareReceiveError("invalid-link", "legacy share link format is invalid");
6219
+ }
6220
+ try {
6221
+ options.signal?.throwIfAborted();
6222
+ const origin = new URL(link).origin;
6223
+ const limit = options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES;
6224
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope limit is invalid");
6225
+ let sealed;
6226
+ if (options.fetchBlob !== void 0) {
6227
+ try {
6228
+ sealed = await options.fetchBlob({ origin, cid: parsed.ciphertextCid, ...options.signal === void 0 ? {} : { signal: options.signal } });
6229
+ } catch {
6230
+ throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
6231
+ }
6232
+ } else {
6233
+ if (options.registryBaseUrl === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry is required");
6234
+ const response = await (options.fetch ?? globalThis.fetch)(`${options.registryBaseUrl.replace(/\/+$/, "")}/ipfs/${parsed.ciphertextCid}?format=raw`, {
6235
+ headers: { accept: "application/vnd.ipld.raw" },
6236
+ redirect: "error",
6237
+ ...options.signal === void 0 ? {} : { signal: options.signal }
6238
+ });
6239
+ if (!response.ok) throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
6240
+ if (Number(response.headers.get("content-length")) > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
6241
+ const reader = response.body?.getReader();
6242
+ if (reader === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry returned no body");
6243
+ const chunks = [];
6244
+ let length3 = 0;
6245
+ for (; ; ) {
6246
+ const part = await reader.read();
6247
+ if (part.done) break;
6248
+ length3 += part.value.length;
6249
+ if (length3 > limit) {
6250
+ await reader.cancel();
6251
+ throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
6252
+ }
6253
+ chunks.push(part.value);
6254
+ }
6255
+ sealed = new Uint8Array(length3);
6256
+ let offset = 0;
6257
+ for (const chunk of chunks) {
6258
+ sealed.set(chunk, offset);
6259
+ offset += chunk.length;
6260
+ }
6261
+ }
6262
+ options.signal?.throwIfAborted();
6263
+ if (!(sealed instanceof Uint8Array) || sealed.length > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
6264
+ if (await computeCid(sealed) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "legacy envelope CID does not match");
6265
+ let plaintext;
6266
+ try {
6267
+ plaintext = await open(sealed, parsed.key32);
6268
+ } catch {
6269
+ throw new ShareReceiveError("decrypt-failed", "legacy envelope could not be opened");
6270
+ }
6271
+ let envelope;
6272
+ try {
6273
+ const encoded = new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
6274
+ const value = JSON.parse(encoded);
6275
+ if (canonicalize(value) !== encoded) throw new Error("non-canonical envelope");
6276
+ envelope = shareEnvelopeV2Schema.parse(value);
6277
+ } catch {
6278
+ throw new ShareReceiveError("envelope-invalid", "legacy envelope is invalid");
6279
+ }
6280
+ return { envelope, origin, cid: parsed.ciphertextCid };
6281
+ } finally {
6282
+ parsed.key32.fill(0);
6283
+ }
6284
+ }
6285
+ function evidenceMatchesV2(envelope, evidence) {
6286
+ const actions = new Set(evidence.actions.map((action) => action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : action === "edit" ? "tinycloud.kv/put" : action));
6287
+ const resourceCovers = evidence.resource.kind === "exact" ? envelope.resource.kind === "exact" && evidence.resource.path === envelope.resource.path : envelope.resource.path === evidence.resource.path || envelope.resource.path.startsWith(evidence.resource.path.endsWith("/") ? evidence.resource.path : `${evidence.resource.path}/`);
6288
+ return envelope.authorizationTarget.kind === "policy" && evidence.policyCid === envelope.authorizationTarget.policyCid && evidence.signerDid === envelope.signature.signerDid && evidence.registrationCid.length > 0 && evidence.shareId === envelope.shareId && canonicalize(evidence.recipientMatcher) === canonicalize(envelope.recipientMatcher) && canonicalize(evidence.target) === canonicalize(envelope.target) && resourceCovers && envelope.actions.every((action) => actions.has(action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : "tinycloud.kv/put")) && canonicalize(evidence.contentSource) === canonicalize(envelope.contentSource) && evidence.contentSourceDigest === envelope.contentSourceDigest && evidence.delegationCid === envelope.delegationCid && evidence.authorityMaterialHandle === envelope.authorityMaterialHandle && evidence.authorityMaterialDigest === envelope.authorityMaterialDigest && evidence.expiresAt === envelope.expiry && Number.isFinite(Date.parse(evidence.expiresAt)) && Date.parse(evidence.expiresAt) === Date.parse(envelope.expiry);
6289
+ }
6290
+ async function verifyLegacyV2(envelope, options) {
6291
+ try {
6292
+ if (!await verifyEnvelopeV2(envelope, { expectedSignerDid: envelope.signature.signerDid })) throw new Error("signature");
6293
+ } catch {
6294
+ throw new ShareReceiveError("signature-invalid", "legacy envelope signature is invalid");
6295
+ }
6296
+ const expiry = Date.parse(envelope.expiry);
6297
+ if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "legacy share expiry is invalid");
6298
+ if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "legacy share has expired", { expiresAt: envelope.expiry });
6299
+ if (envelope.authorizationTarget.kind === "policy") {
6300
+ if (options.trustedPolicyAuthority === void 0) throw new ShareReceiveError("envelope-invalid", "a legacy policy authority is required");
6301
+ let evidence;
6302
+ try {
6303
+ evidence = await options.trustedPolicyAuthority.resolve({ policyCid: envelope.authorizationTarget.policyCid, envelope });
6304
+ } catch {
6305
+ }
6306
+ if (evidence === void 0 || !evidenceMatchesV2(envelope, evidence)) throw new ShareReceiveError("capability-invalid", "legacy policy evidence does not match");
6307
+ if (options.trustedSignerDid !== void 0 && options.trustedSignerDid !== evidence.signerDid) throw new ShareReceiveError("signature-invalid", "legacy signer is not trusted");
6308
+ } else if (options.trustedSignerDid === void 0 || options.trustedSignerDid !== envelope.signature.signerDid || envelope.authorizationTarget.kind === "recipientDid" && envelope.recipientMatcher.kind === "recipientDid" && envelope.authorizationTarget.did !== envelope.recipientMatcher.value) {
6309
+ throw new ShareReceiveError("capability-invalid", "legacy recipient authority does not match");
6310
+ }
6311
+ }
6099
6312
  async function inspectShare(link, options = {}) {
6313
+ if (isLegacyV2Link(link)) {
6314
+ const legacy = await resolveLegacyV2(link, options);
6315
+ await verifyLegacyV2(legacy.envelope, options);
6316
+ options.onResolvedAddressedEnvelope?.(legacy.envelope, legacy.cid);
6317
+ return { metadata: metadataFor(legacy.envelope, legacy.origin), link: { origin: legacy.origin, cid: legacy.cid, kind: "compact" } };
6318
+ }
6100
6319
  const resolved = await resolvePolicyShare(link, options);
6101
6320
  return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "policy" } };
6102
6321
  }
6103
6322
  async function receiveShare(link, options = {}) {
6323
+ if (isLegacyV2Link(link)) return receiveLegacyV2(link, options);
6104
6324
  const resolved = await resolvePolicyShare(link, options);
6105
6325
  return { state: "authorization-required", method: resolved.envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim" };
6106
6326
  }
6327
+ function isLegacyV2Link(link) {
6328
+ try {
6329
+ const url = new URL(link);
6330
+ return /^\/s\/bafkrei[a-z2-7]{52}$/.test(url.pathname) && url.hash.startsWith("#k=");
6331
+ } catch {
6332
+ return false;
6333
+ }
6334
+ }
6335
+ async function receiveLegacyV2(link, options) {
6336
+ const resolved = await resolveLegacyV2(link, options);
6337
+ const { envelope } = resolved;
6338
+ await verifyLegacyV2(envelope, options);
6339
+ options.onResolvedAddressedEnvelope?.(envelope, resolved.cid);
6340
+ const method = envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim";
6341
+ if (options.authorization === void 0) return { state: "authorization-required", method };
6342
+ const result = options.authorizationResumeToken === void 0 ? await options.authorization.begin({ envelope, method }) : await options.authorization.resume({
6343
+ envelope,
6344
+ method,
6345
+ resumeToken: options.authorizationResumeToken,
6346
+ ...options.authorizationProof === void 0 ? {} : { proof: options.authorizationProof }
6347
+ });
6348
+ if (result.state === "authorization-required") return result;
6349
+ if (result.state === "denied") throw new ShareReceiveError("authorization-denied", "legacy share authorization was denied");
6350
+ if (result.value === null || typeof result.value !== "object" || !(result.value.bytes instanceof Uint8Array) || result.value.proof === void 0 || options.authorization.verifyResult === void 0) {
6351
+ throw new ShareReceiveError("content-integrity-failed", "legacy content lacks a verifiable proof");
6352
+ }
6353
+ const authorized = result.value;
6354
+ try {
6355
+ if (!await options.authorization.verifyResult({ envelope, value: authorized, proof: authorized.proof })) throw new Error("proof");
6356
+ } catch {
6357
+ throw new ShareReceiveError("content-integrity-failed", "legacy content proof is invalid");
6358
+ }
6359
+ const bytes3 = authorized.bytes.slice();
6360
+ if (bytes3.length > (options.maxContentBlobBytes ?? DEFAULT_MAX_CONTENT_BLOB_BYTES)) throw new ShareReceiveError("max-bytes-exceeded", "legacy content exceeds the size limit");
6361
+ const bodyDigest = toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes3)));
6362
+ if (authorized.bodyDigest !== bodyDigest || authorized.contentSourceDigest !== envelope.contentSourceDigest || authorized.binding.shareId !== envelope.shareId || authorized.binding.delegationCid !== envelope.delegationCid || authorized.binding.authorityMaterialHandle !== envelope.authorityMaterialHandle || authorized.binding.authorityMaterialDigest !== envelope.authorityMaterialDigest || authorized.binding.resource.kind !== envelope.resource.kind || authorized.binding.resource.path !== envelope.resource.path || authorized.binding.action !== void 0 && !envelope.actions.some((action) => action === authorized.binding.action || `tinycloud.kv/${action}` === authorized.binding.action) || envelope.metadata.byteLength !== void 0 && envelope.metadata.byteLength !== bytes3.length) {
6363
+ throw new ShareReceiveError("content-integrity-failed", "legacy content binding does not match");
6364
+ }
6365
+ let text;
6366
+ try {
6367
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes3);
6368
+ } catch {
6369
+ }
6370
+ return { metadata: metadataFor(envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "compact" }, bytes: bytes3, ...text === void 0 ? {} : { text } };
6371
+ }
6107
6372
 
6108
6373
  // src/publish.ts
6109
6374
  var SHARE_CONTENT_LIMIT = 100 * 1024 * 1024;
@@ -6638,7 +6903,10 @@ async function publishTargetShare(input) {
6638
6903
  expiresAt,
6639
6904
  expiryWasExplicit: input.expiryWasExplicit ?? input.expiresAt !== void 0,
6640
6905
  origin: input.origin,
6641
- ...input.notify === void 0 ? {} : { notify: input.notify }
6906
+ ...input.notify === void 0 ? {} : { notify: input.notify },
6907
+ ...input.credentialDescriptor === void 0 ? {} : { credentialDescriptor: input.credentialDescriptor },
6908
+ ...input.issuerDid === void 0 ? {} : { issuerDid: input.issuerDid },
6909
+ ...input.issuerKid === void 0 ? {} : { issuerKid: input.issuerKid }
6642
6910
  });
6643
6911
  }
6644
6912
 
@@ -6726,7 +6994,7 @@ async function createPolicy(options, capabilities) {
6726
6994
  function addressedPlan(options) {
6727
6995
  const capabilities = sortCanonical([
6728
6996
  { kind: "kv", resource: options.contentSource.kvResource, selector: options.resource.kind, actions: [...options.policyActions] },
6729
- { kind: "encryption", resource: options.contentSource.encryptionNetwork, action: "tinycloud.encryption/decrypt" }
6997
+ { kind: "encryption", resource: options.contentSource.encryptionKeyPath === void 0 ? options.contentSource.encryptionNetwork : `${options.contentSource.encryptionNetwork}/${options.contentSource.encryptionKeyPath}`, action: "tinycloud.encryption/decrypt" }
6730
6998
  ]);
6731
6999
  return {
6732
7000
  expiry: rfc3339Seconds(options.expiresAt),
@@ -7056,6 +7324,7 @@ async function addressedShareBodyUpperBound(input) {
7056
7324
  kvResource: `${input.spaceId}/kv/${path}`,
7057
7325
  selector: input.resource.kind,
7058
7326
  encryptionNetwork: input.encryptionNetwork,
7327
+ ...input.encryptionKeyPath === void 0 ? {} : { encryptionKeyPath: input.encryptionKeyPath },
7059
7328
  encryptedSymmetricKeyDigestHex: "f".repeat(64),
7060
7329
  keyVersion: Number.MAX_SAFE_INTEGER,
7061
7330
  mode: "immutable",
@@ -8616,8 +8885,8 @@ function parseV3InlineEncryptedEnvelope(bytes3, expected) {
8616
8885
  }
8617
8886
  if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("encrypted content envelope is malformed");
8618
8887
  const record = value;
8619
- const allowed = ["v", "networkId", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "metadata"];
8620
- if (Object.keys(record).some((key) => !allowed.includes(key)) || record.v !== 1 || record.networkId !== expected.encryptionNetwork || record.alg !== "x25519-aes256gcm/v1" || record.keyVersion !== expected.contentSource.keyVersion || typeof record.encryptedSymmetricKey !== "string" || typeof record.encryptedSymmetricKeyHash !== "string" || record.encryptedSymmetricKeyHash !== expected.contentSource.encryptedSymmetricKeyDigestHex || record.encryptedSymmetricKeyHash !== canonicalHashHex(record.encryptedSymmetricKey) || typeof record.ciphertext !== "string" || record.metadata !== void 0 && (record.metadata === null || typeof record.metadata !== "object" || Array.isArray(record.metadata) || Object.values(record.metadata).some((entry) => typeof entry !== "string"))) {
8888
+ const allowed = ["v", "networkId", "keyPath", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "metadata"];
8889
+ if (Object.keys(record).some((key) => !allowed.includes(key)) || record.v !== 1 || record.networkId !== expected.encryptionNetwork || record.keyPath !== expected.contentSource.encryptionKeyPath || record.keyPath !== void 0 && record.keyPath !== `share/${expected.shareId}` || record.alg !== "x25519-aes256gcm/v1" || record.keyVersion !== expected.contentSource.keyVersion || typeof record.encryptedSymmetricKey !== "string" || typeof record.encryptedSymmetricKeyHash !== "string" || record.encryptedSymmetricKeyHash !== expected.contentSource.encryptedSymmetricKeyDigestHex || record.encryptedSymmetricKeyHash !== canonicalHashHex(record.encryptedSymmetricKey) || typeof record.ciphertext !== "string" || record.metadata !== void 0 && (record.metadata === null || typeof record.metadata !== "object" || Array.isArray(record.metadata) || Object.values(record.metadata).some((entry) => typeof entry !== "string"))) {
8621
8890
  throw new Error("encrypted content envelope binding is invalid");
8622
8891
  }
8623
8892
  return record;
@@ -8749,15 +9018,16 @@ var ShareRecipientClient = class {
8749
9018
  const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
8750
9019
  const receiverPublicKey = toBase64(x25519.getPublicKey(receiverPrivateKey));
8751
9020
  const receiverPublicKeyHash = canonicalHashHex(receiverPublicKey);
8752
- const body = { type: "tinycloud.encryption.decrypt/v1", targetNode: this.v3NodeAudience, networkId: encrypted.networkId, alg: encrypted.alg, keyVersion: encrypted.keyVersion, encryptedSymmetricKey: encrypted.encryptedSymmetricKey, encryptedSymmetricKeyHash: encrypted.encryptedSymmetricKeyHash, receiverPublicKey, receiverPublicKeyHash };
9021
+ const body = { type: "tinycloud.encryption.decrypt/v1", targetNode: this.v3NodeAudience, networkId: encrypted.networkId, ...encrypted.keyPath === void 0 ? {} : { keyPath: encrypted.keyPath }, alg: encrypted.alg, keyVersion: encrypted.keyVersion, encryptedSymmetricKey: encrypted.encryptedSymmetricKey, encryptedSymmetricKeyHash: encrypted.encryptedSymmetricKeyHash, receiverPublicKey, receiverPublicKeyHash };
8753
9022
  const bodyHash = hex3(sha2566(new TextEncoder().encode(canonicalize(body))));
8754
9023
  const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
8755
9024
  const now = Math.floor(Date.now() / 1e3);
9025
+ const decryptResource = encrypted.keyPath === void 0 ? encrypted.networkId : `${encrypted.networkId}/${encrypted.keyPath}`;
8756
9026
  const invocation = await signCompactUcanAuthorization({
8757
9027
  issuerDid: this.options.holderDid,
8758
9028
  audienceDid: this.v3NodeAudience,
8759
- attenuation: { [encrypted.networkId]: { "tinycloud.encryption/decrypt": [{}] } },
8760
- facts: [{ type: body.type, targetNode: body.targetNode, networkId: body.networkId, bodyHash, encryptedSymmetricKeyHash: body.encryptedSymmetricKeyHash, receiverPublicKeyHash, alg: body.alg, keyVersion: body.keyVersion }],
9029
+ attenuation: { [decryptResource]: { "tinycloud.encryption/decrypt": [{}] } },
9030
+ facts: [{ type: body.type, targetNode: body.targetNode, networkId: body.networkId, ...encrypted.keyPath === void 0 ? {} : { keyPath: encrypted.keyPath }, bodyHash, encryptedSymmetricKeyHash: body.encryptedSymmetricKeyHash, receiverPublicKeyHash, alg: body.alg, keyVersion: body.keyVersion }],
8761
9031
  proofs: [session.cid],
8762
9032
  notBefore: now,
8763
9033
  expiresAt: Math.min(now + 60, session.payload.exp),