@tinycloud/share-sdk 1.4.2 → 1.4.3-beta.0

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
@@ -2,8 +2,8 @@ export { D as DEFAULT_MAX_CONTENT_BLOB_BYTES, a as DEFAULT_MAX_SEALED_BLOB_BYTES
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-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';
5
+ import { T as TargetPublishOutcome, c as TargetPublishInput, S as ShareTarget } from './addressed-publish-BPFFqD5s.cjs';
6
+ export { d as ADDRESSED_SHARE_TOO_LARGE_MESSAGE, e as AddressedContentSourceError, f as AddressedContentSourceErrorCode, A as AddressedOwnerRootInput, a as AddressedOwnerRootReceipt, g as AddressedPolicyRegistrationInput, h as AddressedPolicyRegistrationReceipt, b as AddressedPublishAuthority, i as AddressedShareBoundInput, j as AddressedShareKeyClient, k as AddressedShareOverLimitError, l as AddressedSharePublishOptions, m as AddressedShareRequest, n as AddressedShareWrites, E as EncryptedAddressedContent, P as PathBoundContentSource, o as PathBoundEnvelope, p as PathBoundPermissionEntry, q as PathBoundSharePlan, r as PreparedAddressedShare, s as TargetPublishAdapter, t as addressedCredentialRequirement, u as addressedShareBodyUpperBound, v as assertAddressedShareFits, w as assertPathBoundContentSource, x as bindPathBoundContentSource, y as defaultEncryptionNetworkForOwner, z as encryptAddressedShareContent, B as normalizeShareTarget, C as pathBoundKeyPath, D as pathBoundSessionPermissions, F as pathBoundStoredBytes, G as planPathBoundShare, H as prepareAddressedShare, I as publishAddressedShare, J as publishTargetShare, K as targetAuthorizationMethod, L as wrappedKeyDigestHex } from './addressed-publish-BPFFqD5s.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
@@ -2,8 +2,8 @@ export { D as DEFAULT_MAX_CONTENT_BLOB_BYTES, a as DEFAULT_MAX_SEALED_BLOB_BYTES
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-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';
5
+ import { T as TargetPublishOutcome, c as TargetPublishInput, S as ShareTarget } from './addressed-publish-DorqWQJ8.js';
6
+ export { d as ADDRESSED_SHARE_TOO_LARGE_MESSAGE, e as AddressedContentSourceError, f as AddressedContentSourceErrorCode, A as AddressedOwnerRootInput, a as AddressedOwnerRootReceipt, g as AddressedPolicyRegistrationInput, h as AddressedPolicyRegistrationReceipt, b as AddressedPublishAuthority, i as AddressedShareBoundInput, j as AddressedShareKeyClient, k as AddressedShareOverLimitError, l as AddressedSharePublishOptions, m as AddressedShareRequest, n as AddressedShareWrites, E as EncryptedAddressedContent, P as PathBoundContentSource, o as PathBoundEnvelope, p as PathBoundPermissionEntry, q as PathBoundSharePlan, r as PreparedAddressedShare, s as TargetPublishAdapter, t as addressedCredentialRequirement, u as addressedShareBodyUpperBound, v as assertAddressedShareFits, w as assertPathBoundContentSource, x as bindPathBoundContentSource, y as defaultEncryptionNetworkForOwner, z as encryptAddressedShareContent, B as normalizeShareTarget, C as pathBoundKeyPath, D as pathBoundSessionPermissions, F as pathBoundStoredBytes, G as planPathBoundShare, H as prepareAddressedShare, I as publishAddressedShare, J as publishTargetShare, K as targetAuthorizationMethod, L as wrappedKeyDigestHex } from './addressed-publish-DorqWQJ8.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
@@ -6419,7 +6419,220 @@ async function parseAddressedEnvelope(value) {
6419
6419
  }
6420
6420
 
6421
6421
  // src/addressed-publish.ts
6422
+ import { sha256 as sha2564 } from "@noble/hashes/sha256";
6423
+
6424
+ // src/addressed-content.ts
6422
6425
  import { sha256 as sha2563 } from "@noble/hashes/sha256";
6426
+ import { keccak_256 } from "@noble/hashes/sha3";
6427
+ var AddressedContentSourceError = class extends TypeError {
6428
+ code;
6429
+ constructor(code3, message) {
6430
+ super(message);
6431
+ this.name = "AddressedContentSourceError";
6432
+ this.code = code3;
6433
+ }
6434
+ };
6435
+ var SHARE_ID = /^[a-z0-9][a-z0-9_-]{15,63}$/;
6436
+ var NETWORK = /^urn:tinycloud:encryption:(.+):default$/;
6437
+ var OWNER_PKH = /^did:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40})$/;
6438
+ var OWNER_KEY = /^did:key:[A-Za-z0-9]+$/;
6439
+ var ENVELOPE_FIELDS = ["v", "networkId", "keyPath", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "aad", "metadata"];
6440
+ var ENVELOPE_ALG = "x25519-aes256gcm/v1";
6441
+ var HEX_DIGEST = /^[0-9a-f]{64}$/;
6442
+ var BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
6443
+ var textEncoder = new TextEncoder();
6444
+ function hex2(bytes3) {
6445
+ return [...bytes3].map((byte) => byte.toString(16).padStart(2, "0")).join("");
6446
+ }
6447
+ function checksumAddress(address) {
6448
+ const lower = address.toLowerCase().slice(2);
6449
+ const hash = keccak_256(textEncoder.encode(lower));
6450
+ let out = "0x";
6451
+ for (let index = 0; index < lower.length; index++) {
6452
+ const nibble = hash[index >> 1] >> (index % 2 === 0 ? 4 : 0) & 15;
6453
+ out += nibble >= 8 ? lower[index].toUpperCase() : lower[index];
6454
+ }
6455
+ return out;
6456
+ }
6457
+ function pathBoundKeyPath(shareId) {
6458
+ if (typeof shareId !== "string" || !SHARE_ID.test(shareId)) {
6459
+ throw new AddressedContentSourceError("share-id-invalid", "addressed share id is invalid: 16 to 64 characters from lowercase letters, digits, '_' and '-', starting with a letter or digit (a lowercase UUID, hyphens included, is valid)");
6460
+ }
6461
+ return `share/${shareId}`;
6462
+ }
6463
+ function defaultEncryptionNetworkForOwner(owner) {
6464
+ let did = owner;
6465
+ const address = typeof owner === "string" ? /^0x[0-9a-fA-F]{40}$/.test(owner) ? owner : void 0 : owner?.address;
6466
+ if (address !== void 0) {
6467
+ const chainId = typeof owner === "string" ? 1 : owner.chainId ?? 1;
6468
+ if (typeof address !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(address) || !Number.isSafeInteger(chainId) || chainId < 1) {
6469
+ throw new AddressedContentSourceError("network-invalid", "owner address is not an Ethereum address");
6470
+ }
6471
+ did = `did:pkh:eip155:${chainId}:${address}`;
6472
+ }
6473
+ const pkh = typeof did === "string" ? OWNER_PKH.exec(did) : null;
6474
+ const normalized = pkh === null ? did : `did:pkh:eip155:${pkh[1]}:${checksumAddress(pkh[2])}`;
6475
+ const network = `urn:tinycloud:encryption:${String(normalized)}:default`;
6476
+ if (typeof normalized !== "string" || !(pkh !== null || OWNER_KEY.test(normalized))) {
6477
+ throw new AddressedContentSourceError("network-invalid", "owner does not name an encryption network");
6478
+ }
6479
+ return network;
6480
+ }
6481
+ function assertEncryptionNetwork(network) {
6482
+ const match = typeof network === "string" ? NETWORK.exec(network) : null;
6483
+ if (match === null) {
6484
+ throw new AddressedContentSourceError("network-invalid", "encryption network must be the owner's default network: urn:tinycloud:encryption:<did>:default");
6485
+ }
6486
+ const owner = match[1];
6487
+ const pkh = OWNER_PKH.exec(owner);
6488
+ if (pkh !== null) {
6489
+ if (pkh[2] !== checksumAddress(pkh[2])) {
6490
+ throw new AddressedContentSourceError("network-not-checksummed", "encryption network address must be EIP-55 checksummed; use defaultEncryptionNetworkForOwner(ownerDid)");
6491
+ }
6492
+ } else if (!OWNER_KEY.test(owner)) {
6493
+ throw new AddressedContentSourceError("network-invalid", "encryption network owner must be a did:pkh:eip155 address or a did:key");
6494
+ }
6495
+ return network;
6496
+ }
6497
+ var assertNetwork = assertEncryptionNetwork;
6498
+ function decodeWrappedKey(encryptedSymmetricKey) {
6499
+ if (typeof encryptedSymmetricKey !== "string" || !BASE64.test(encryptedSymmetricKey)) {
6500
+ throw new AddressedContentSourceError("envelope-invalid", "wrapped content key is not strict base64");
6501
+ }
6502
+ try {
6503
+ return fromBase64Url(encryptedSymmetricKey.replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_"));
6504
+ } catch {
6505
+ throw new AddressedContentSourceError("envelope-invalid", "wrapped content key is not strict base64");
6506
+ }
6507
+ }
6508
+ function decodeBase64(value) {
6509
+ try {
6510
+ return fromBase64Url(value.replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_"));
6511
+ } catch {
6512
+ throw new AddressedContentSourceError("envelope-invalid", "envelope field is not strict base64");
6513
+ }
6514
+ }
6515
+ function wrappedKeyDigestHex(encryptedSymmetricKey) {
6516
+ return hex2(sha2563(decodeWrappedKey(encryptedSymmetricKey)));
6517
+ }
6518
+ function canonicalKeyHashHex(encryptedSymmetricKey) {
6519
+ return hex2(sha2563(textEncoder.encode(canonicalize(encryptedSymmetricKey))));
6520
+ }
6521
+ function pathBoundSessionPermissions(input) {
6522
+ const encryptionNetwork = assertNetwork(input.encryptionNetwork);
6523
+ return [{ service: "tinycloud.encryption", path: `${encryptionNetwork}/share/`, actions: ["tinycloud.encryption/decrypt", "tinycloud.encryption/key.create"] }];
6524
+ }
6525
+ function planPathBoundShare(input) {
6526
+ const keyPath = pathBoundKeyPath(input.shareId);
6527
+ const encryptionNetwork = assertNetwork(input.encryptionNetwork);
6528
+ const keyResource = `${encryptionNetwork}/${keyPath}`;
6529
+ return {
6530
+ shareId: input.shareId,
6531
+ encryptionNetwork,
6532
+ keyPath,
6533
+ keyResource,
6534
+ requiredPermissions: [{ service: "tinycloud.encryption", path: keyResource, actions: ["tinycloud.encryption/key.create"] }],
6535
+ sessionPermissions: pathBoundSessionPermissions({ encryptionNetwork }),
6536
+ registrationPermissions: [{ service: "tinycloud.encryption", path: `${encryptionNetwork}/share/`, actions: ["tinycloud.encryption/decrypt"] }],
6537
+ encryptOptions: (extra) => ({ networkId: encryptionNetwork, ...extra?.metadata === void 0 ? {} : { metadata: { ...extra.metadata } } })
6538
+ };
6539
+ }
6540
+ function pathBoundStoredBytes(envelope) {
6541
+ return textEncoder.encode(canonicalize(envelope));
6542
+ }
6543
+ function assertEnvelope(plan, envelope) {
6544
+ if (envelope === null || typeof envelope !== "object" || envelope.v !== 2) {
6545
+ throw new AddressedContentSourceError("envelope-invalid", "path-bound content must be a v2 named-key envelope (encrypt with keys.encrypt, not encryptToNetwork)");
6546
+ }
6547
+ if (envelope.networkId !== plan.encryptionNetwork || envelope.keyPath !== plan.keyPath) {
6548
+ throw new AddressedContentSourceError("key-path-mismatch", "envelope network or key path does not match the share's plan");
6549
+ }
6550
+ if (!Number.isSafeInteger(envelope.keyVersion) || envelope.keyVersion < 1) {
6551
+ throw new AddressedContentSourceError("envelope-invalid", "envelope key version is invalid");
6552
+ }
6553
+ wrappedKeyDigestHex(envelope.encryptedSymmetricKey);
6554
+ if (envelope.encryptedSymmetricKeyHash !== canonicalKeyHashHex(envelope.encryptedSymmetricKey)) {
6555
+ throw new AddressedContentSourceError("envelope-invalid", "envelope key hash does not match its wrapped key");
6556
+ }
6557
+ const record = envelope;
6558
+ if (Object.keys(record).some((key) => !ENVELOPE_FIELDS.includes(key)) || record.alg !== ENVELOPE_ALG) {
6559
+ throw new AddressedContentSourceError("envelope-invalid", "envelope has a field or algorithm the recipient does not accept");
6560
+ }
6561
+ if (typeof record.ciphertext !== "string" || !BASE64.test(record.ciphertext) || decodeBase64(record.ciphertext).length < 28) {
6562
+ throw new AddressedContentSourceError("envelope-invalid", "envelope ciphertext is not strict base64 of at least 28 bytes");
6563
+ }
6564
+ if (record.aad !== void 0 && (typeof record.aad !== "string" || !BASE64.test(record.aad))) {
6565
+ throw new AddressedContentSourceError("envelope-invalid", "envelope aad is not strict base64");
6566
+ }
6567
+ if (record.metadata !== void 0 && (record.metadata === null || typeof record.metadata !== "object" || Array.isArray(record.metadata) || Object.values(record.metadata).some((entry) => typeof entry !== "string"))) {
6568
+ throw new AddressedContentSourceError("envelope-invalid", "envelope metadata must be an object of strings");
6569
+ }
6570
+ }
6571
+ function bindPathBoundContentSource(input) {
6572
+ const { plan, envelope } = input;
6573
+ assertEnvelope(plan, envelope);
6574
+ const expected = pathBoundStoredBytes(envelope);
6575
+ if (input.storedBytes.length !== expected.length || input.storedBytes.some((byte, index) => byte !== expected[index])) {
6576
+ throw new AddressedContentSourceError("stored-bytes-mismatch", "uploaded bytes are not the canonical v2 envelope; upload pathBoundStoredBytes(envelope)");
6577
+ }
6578
+ return {
6579
+ shareId: plan.shareId,
6580
+ kvResource: input.kvResource,
6581
+ selector: input.selector,
6582
+ encryptionNetwork: plan.encryptionNetwork,
6583
+ encryptionKeyPath: plan.keyPath,
6584
+ encryptedSymmetricKeyDigestHex: wrappedKeyDigestHex(envelope.encryptedSymmetricKey),
6585
+ keyVersion: envelope.keyVersion,
6586
+ mode: "immutable",
6587
+ initialCiphertextDigestHex: hex2(sha2563(expected))
6588
+ };
6589
+ }
6590
+ function isUnauthorized(error) {
6591
+ const value = error;
6592
+ return value !== null && typeof value === "object" && (value.status === 401 || value.status === 403 || value.code === "PERMISSION_DENIED" || value.code === "AUTH_UNAUTHORIZED");
6593
+ }
6594
+ async function encryptAddressedShareContent(input) {
6595
+ const { plan, keys } = input;
6596
+ const byteLength = input.plaintext.byteLength;
6597
+ let receipt;
6598
+ try {
6599
+ receipt = await keys.createKey(plan.keyPath, { networkId: plan.encryptionNetwork });
6600
+ } catch (error) {
6601
+ if (isUnauthorized(error)) {
6602
+ const status = error.status;
6603
+ throw new AddressedContentSourceError(
6604
+ "key-not-authorized",
6605
+ `the owner session does not hold tinycloud.encryption/key.create on ${plan.keyResource} (the node answers "Unauthorized Action: ${plan.keyResource} / tinycloud.encryption/key.create"); hold it either at sign-in (put plan.sessionPermissions in the manifest: key.create on ${plan.encryptionNetwork}/share/) or per share (requestPermissions(plan.requiredPermissions)) before creating the key` + (status === 403 ? "; if the owner signs in through OpenKey, the request must come from a first-party TinyCloud origin" : "")
6606
+ );
6607
+ }
6608
+ throw error;
6609
+ }
6610
+ if (receipt.networkId !== plan.encryptionNetwork || receipt.keyPath !== plan.keyPath || receipt.state !== void 0 && receipt.state !== "active") {
6611
+ throw new AddressedContentSourceError("key-receipt-mismatch", "the created key does not match this share");
6612
+ }
6613
+ const envelope = await keys.encrypt(plan.keyPath, input.plaintext, plan.encryptOptions(input.metadata === void 0 ? void 0 : { metadata: input.metadata }));
6614
+ assertEnvelope(plan, envelope);
6615
+ if (receipt.keyVersion !== void 0 && envelope.keyVersion !== receipt.keyVersion) {
6616
+ throw new AddressedContentSourceError("key-receipt-mismatch", "the envelope key version does not match the created key");
6617
+ }
6618
+ const storedBytes = pathBoundStoredBytes(envelope);
6619
+ return { envelope, storedBytes, byteLength };
6620
+ }
6621
+ function assertPathBoundContentSource(source, shareId) {
6622
+ if (source === void 0 || source === null || typeof source !== "object") {
6623
+ throw new AddressedContentSourceError("key-path-required", "addressed content source is missing");
6624
+ }
6625
+ if (source.encryptionKeyPath === void 0) {
6626
+ throw new AddressedContentSourceError("key-path-required", "addressed content source must name its key (encryptionKeyPath: share/<shareId>); a network-wide source is not accepted");
6627
+ }
6628
+ if (source.encryptionKeyPath !== pathBoundKeyPath(shareId)) {
6629
+ throw new AddressedContentSourceError("key-path-mismatch", "addressed content source key path must be share/<shareId> of this share");
6630
+ }
6631
+ if (typeof source.encryptedSymmetricKeyDigestHex !== "string" || !HEX_DIGEST.test(source.encryptedSymmetricKeyDigestHex)) {
6632
+ throw new AddressedContentSourceError("digest-invalid", "addressed content source needs encryptedSymmetricKeyDigestHex: 64 lowercase hex characters");
6633
+ }
6634
+ assertEncryptionNetwork(source.encryptionNetwork);
6635
+ }
6423
6636
 
6424
6637
  // src/authorization.ts
6425
6638
  async function authorizeShare(input) {
@@ -6917,7 +7130,7 @@ var POLICY_CAPABILITY_V1_DOMAIN2 = "xyz.tinycloud.policy/PolicyCapability/v1\0";
6917
7130
  var CONTENT_SOURCE_V1_DOMAIN2 = "xyz.tinycloud.policy/ContentSource/v1\0";
6918
7131
  var NATIVE_PROJECTION_V1_DOMAIN2 = "xyz.tinycloud.policy/NativeProjection/v1\0";
6919
7132
  var ENVELOPE_V3_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
6920
- var textEncoder = new TextEncoder();
7133
+ var textEncoder2 = new TextEncoder();
6921
7134
  function targetMatcher(target) {
6922
7135
  if (target.kind === "recipientDid") return { kind: "recipientDid", value: target.did };
6923
7136
  if (target.kind === "email") return { kind: "exactEmail", value: target.address };
@@ -6928,17 +7141,18 @@ function targetKind(target) {
6928
7141
  }
6929
7142
  var OWNER_SHARE_ACTIONS = /* @__PURE__ */ new Set(["tinycloud.kv/get", "tinycloud.kv/list", "tinycloud.kv/metadata", "tinycloud.kv/put"]);
6930
7143
  function assertSafeInput(input) {
6931
- if (!/^[A-Za-z0-9_-]{16,128}$/.test(input.shareId)) throw new TypeError("addressed share id is invalid");
7144
+ pathBoundKeyPath(input.shareId);
7145
+ assertPathBoundContentSource(input.contentSource, input.shareId);
6932
7146
  if (!Number.isSafeInteger(input.byteLength) || input.byteLength < 0 || input.byteLength > SHARE_CONTENT_LIMIT) throw new TypeError("addressed content length is invalid");
6933
7147
  if (!Number.isFinite(input.expiresAt.getTime()) || input.expiresAt.getTime() <= Date.now()) throw new TypeError("addressed share expiry must be in the future");
6934
7148
  if (input.artifact === "html" && (input.resource.kind !== "prefix" || !input.actions.includes("read") || !input.actions.includes("list"))) throw new TypeError("html artifacts require a readable prefix");
6935
7149
  if (input.contentSource.shareId !== input.shareId || input.contentSource.selector !== input.resource.kind) throw new TypeError("addressed content source is not bound to the share");
6936
7150
  }
6937
- function hex2(bytes3) {
7151
+ function hex3(bytes3) {
6938
7152
  return [...bytes3].map((byte) => byte.toString(16).padStart(2, "0")).join("");
6939
7153
  }
6940
7154
  function digestHex(value, domain) {
6941
- return hex2(sha2563(textEncoder.encode(`${domain}${canonicalize(value)}`)));
7155
+ return hex3(sha2564(textEncoder2.encode(`${domain}${canonicalize(value)}`)));
6942
7156
  }
6943
7157
  function sortCanonical(values) {
6944
7158
  return [...values].sort((left, right) => canonicalize(left).localeCompare(canonicalize(right)));
@@ -6982,19 +7196,20 @@ async function createPolicy(options, capabilities) {
6982
7196
  const unsigned = options.credentialRequirement === void 0 ? { schema: "xyz.tinycloud.policy/policy/v1", ...fields } : { schema: "xyz.tinycloud.policy/policy/v2", ...fields, credentialRequirement: options.credentialRequirement };
6983
7197
  const domain = options.credentialRequirement === void 0 ? POLICY_V1_DOMAIN : POLICY_V2_DOMAIN;
6984
7198
  const policyDigestHex = digestHex(unsigned, domain);
6985
- const signature = await options.authority.sign(sha2563(textEncoder.encode(`${domain}${canonicalize(unsigned)}`)));
7199
+ const signature = await options.authority.sign(sha2564(textEncoder2.encode(`${domain}${canonicalize(unsigned)}`)));
6986
7200
  if (signature.byteLength !== 64) throw new TypeError("policy signature must be Ed25519");
6987
7201
  const policy = {
6988
7202
  ...unsigned,
6989
7203
  policyId: `pol_${base32Lower2(Uint8Array.from(policyDigestHex.match(/../g), (byte) => Number.parseInt(byte, 16)))}`,
6990
7204
  signature: { suite: "Ed25519", signerDid: options.authority.ownerDid, value: toBase64Url(signature) }
6991
7205
  };
6992
- return { policy, policyCid: await computeCid(textEncoder.encode(canonicalize(policy))), policyDigestHex };
7206
+ return { policy, policyCid: await computeCid(textEncoder2.encode(canonicalize(policy))), policyDigestHex };
6993
7207
  }
6994
7208
  function addressedPlan(options) {
6995
7209
  const capabilities = sortCanonical([
6996
7210
  { kind: "kv", resource: options.contentSource.kvResource, selector: options.resource.kind, actions: [...options.policyActions] },
6997
- { kind: "encryption", resource: options.contentSource.encryptionKeyPath === void 0 ? options.contentSource.encryptionNetwork : `${options.contentSource.encryptionNetwork}/${options.contentSource.encryptionKeyPath}`, action: "tinycloud.encryption/decrypt" }
7211
+ // Only this share's one key: there is no network-scope capability.
7212
+ { kind: "encryption", resource: `${options.contentSource.encryptionNetwork}/${options.contentSource.encryptionKeyPath}`, action: "tinycloud.encryption/decrypt" }
6998
7213
  ]);
6999
7214
  return {
7000
7215
  expiry: rfc3339Seconds(options.expiresAt),
@@ -7082,7 +7297,7 @@ function mailboxCredentialCommitment(target) {
7082
7297
  return {
7083
7298
  type: "TinyCloudPolicyCredentialRequirement",
7084
7299
  version: 1,
7085
- requirementDigest: toBase64Url(sha2563(textEncoder.encode(canonicalize(requirement)))),
7300
+ requirementDigest: toBase64Url(sha2564(textEncoder2.encode(canonicalize(requirement)))),
7086
7301
  descriptorDigest: target.kind === "email" ? EMAIL_DESCRIPTOR_DIGEST : EMAIL_DOMAIN_DESCRIPTOR_DIGEST,
7087
7302
  issuerDid: EMAIL_CREDENTIAL_ISSUER_DID,
7088
7303
  issuerKid: EMAIL_CREDENTIAL_ISSUER_KID,
@@ -7177,11 +7392,11 @@ async function publishAddressedShare(input) {
7177
7392
  expiry
7178
7393
  });
7179
7394
  unsignedShareEnvelopeV3Schema.parse(unsigned);
7180
- const envelopeSignature = await options.authority.sign(sha2563(textEncoder.encode(`${ENVELOPE_V3_DOMAIN}${canonicalize(unsigned)}`)));
7395
+ const envelopeSignature = await options.authority.sign(sha2564(textEncoder2.encode(`${ENVELOPE_V3_DOMAIN}${canonicalize(unsigned)}`)));
7181
7396
  if (envelopeSignature.byteLength !== 64) throw new TypeError("v3 envelope signature must be Ed25519");
7182
7397
  const envelope = signedAddressedEnvelope(unsigned, options.authority.ownerDid, envelopeSignature);
7183
7398
  shareEnvelopeV3Schema.parse(envelope);
7184
- const envelopeBytes = textEncoder.encode(canonicalize(envelope));
7399
+ const envelopeBytes = textEncoder2.encode(canonicalize(envelope));
7185
7400
  const envelopeKeyBytes = generateKey();
7186
7401
  const sealed = await seal(envelopeBytes, envelopeKeyBytes);
7187
7402
  const envelopeKey = toBase64Url(envelopeKeyBytes);
@@ -7261,7 +7476,7 @@ function rootAuthorizationBound(input) {
7261
7476
  nnc: "A".repeat(36),
7262
7477
  prf: []
7263
7478
  };
7264
- return `${toBase64Url(textEncoder.encode(canonicalize(header)))}.${toBase64Url(textEncoder.encode(canonicalize(payload)))}.${"A".repeat(86)}`;
7479
+ return `${toBase64Url(textEncoder2.encode(canonicalize(header)))}.${toBase64Url(textEncoder2.encode(canonicalize(payload)))}.${"A".repeat(86)}`;
7265
7480
  }
7266
7481
  async function sealedBodyUpperBound(options) {
7267
7482
  const plan = addressedPlan(options);
@@ -7309,7 +7524,7 @@ async function sealedBodyUpperBound(options) {
7309
7524
  expiry: plan.expiry
7310
7525
  });
7311
7526
  const envelope = signedAddressedEnvelope(unsigned, options.ownerDid, signature);
7312
- return (await seal(textEncoder.encode(canonicalize(envelope)), generateKey())).blob.length;
7527
+ return (await seal(textEncoder2.encode(canonicalize(envelope)), generateKey())).blob.length;
7313
7528
  }
7314
7529
  async function assertSealedBodyFits(options) {
7315
7530
  if (await sealedBodyUpperBound(options) > await linkBodyLimit()) throw new SharePublishError("max-bytes-exceeded", ADDRESSED_SHARE_TOO_LARGE_MESSAGE);
@@ -7319,12 +7534,15 @@ async function addressedShareBodyUpperBound(input) {
7319
7534
  const deliveryEmail = input.deliveryEmail ?? (target.kind === "email" && isEnvelopeDeliveryEmail(target.address) ? target.address : void 0);
7320
7535
  const artifactStorage = "artifactId" in input.storage ? input.storage : void 0;
7321
7536
  const path = "path" in input.storage ? input.storage.path : `${ARTIFACT_ROOT}/${input.storage.artifactId}/versions/${Number.MAX_SAFE_INTEGER}/${input.storage.storageName}`;
7537
+ const keyPath = pathBoundKeyPath(input.shareId);
7538
+ assertEncryptionNetwork(input.encryptionNetwork);
7539
+ if (input.encryptionKeyPath !== void 0 && input.encryptionKeyPath !== keyPath) throw new AddressedContentSourceError("key-path-mismatch", "addressed content source key path must be share/<shareId> of this share");
7322
7540
  const contentSource = {
7323
7541
  shareId: input.shareId,
7324
7542
  kvResource: `${input.spaceId}/kv/${path}`,
7325
7543
  selector: input.resource.kind,
7326
7544
  encryptionNetwork: input.encryptionNetwork,
7327
- ...input.encryptionKeyPath === void 0 ? {} : { encryptionKeyPath: input.encryptionKeyPath },
7545
+ encryptionKeyPath: keyPath,
7328
7546
  encryptedSymmetricKeyDigestHex: "f".repeat(64),
7329
7547
  keyVersion: Number.MAX_SAFE_INTEGER,
7330
7548
  mode: "immutable",
@@ -7364,13 +7582,13 @@ async function assertAddressedShareFits(input) {
7364
7582
  }
7365
7583
 
7366
7584
  // src/cache.ts
7367
- import { sha256 as sha2564 } from "@noble/hashes/sha256";
7585
+ import { sha256 as sha2565 } from "@noble/hashes/sha256";
7368
7586
  function cacheKey(key) {
7369
7587
  if (!key.cid || !key.expiresAt) throw new Error("share cache keys require CID and expiry");
7370
7588
  return `${key.cid}\0${key.expiresAt}`;
7371
7589
  }
7372
7590
  function digest(bytes3) {
7373
- return sha2564(bytes3);
7591
+ return sha2565(bytes3);
7374
7592
  }
7375
7593
  function equalBytes(left, right) {
7376
7594
  if (left.byteLength !== right.byteLength) return false;
@@ -7426,7 +7644,7 @@ var MemoryShareCache = class {
7426
7644
 
7427
7645
  // src/owner-policy.ts
7428
7646
  import { ed25519 as ed255194 } from "@noble/curves/ed25519";
7429
- import { sha256 as sha2565 } from "@noble/hashes/sha256";
7647
+ import { sha256 as sha2566 } from "@noble/hashes/sha256";
7430
7648
 
7431
7649
  // ../../node_modules/multiformats/dist/src/bases/base32.js
7432
7650
  var base322 = rfc46482({
@@ -7980,10 +8198,10 @@ function fromB64(value) {
7980
8198
  throw new Error("base64url decoding is unavailable");
7981
8199
  }
7982
8200
  function cid(bytesValue) {
7983
- return CID2.createV1(code2, create2(SHA256_CODE2, sha2565(bytesValue))).toString(base322.encoder);
8201
+ return CID2.createV1(code2, create2(SHA256_CODE2, sha2566(bytesValue))).toString(base322.encoder);
7984
8202
  }
7985
8203
  function digest2(bytesValue) {
7986
- return b64(sha2565(bytesValue));
8204
+ return b64(sha2566(bytesValue));
7987
8205
  }
7988
8206
  function didKeyFromEd25519PublicKey2(publicKey) {
7989
8207
  if (publicKey.length !== 32) throw new Error("share key public key must be 32 bytes");
@@ -8655,7 +8873,7 @@ async function showShare(input) {
8655
8873
 
8656
8874
  // src/recipient.ts
8657
8875
  import { ed25519 as ed255195, x25519 } from "@noble/curves/ed25519";
8658
- import { sha256 as sha2566 } from "@noble/hashes/sha256";
8876
+ import { sha256 as sha2567 } from "@noble/hashes/sha256";
8659
8877
 
8660
8878
  // src/protocol.ts
8661
8879
  var SHARE_V2_PROTOCOL = Object.freeze({
@@ -8823,12 +9041,12 @@ function requestedSessionExpiry(expiry) {
8823
9041
  if (!Number.isFinite(seconds) || seconds <= Math.floor(Date.now() / 1e3) + 60) return void 0;
8824
9042
  return new Date(seconds * 1e3).toISOString().replace(/\.\d{3}Z$/, "Z");
8825
9043
  }
8826
- function hex3(value) {
9044
+ function hex4(value) {
8827
9045
  return [...value].map((byte) => byte.toString(16).padStart(2, "0")).join("");
8828
9046
  }
8829
9047
  function matchesInitialCiphertextDigest(bytes3, expected) {
8830
9048
  if (typeof expected !== "string" || !LOWER_SHA256_HEX.test(expected)) return false;
8831
- const actual = sha2566(bytes3);
9049
+ const actual = sha2567(bytes3);
8832
9050
  let mismatch = 0;
8833
9051
  for (let index = 0; index < actual.length; index += 1) {
8834
9052
  mismatch |= actual[index] ^ Number.parseInt(expected.slice(index * 2, index * 2 + 2), 16);
@@ -8836,10 +9054,10 @@ function matchesInitialCiphertextDigest(bytes3, expected) {
8836
9054
  return mismatch === 0;
8837
9055
  }
8838
9056
  function canonicalHashHex(value) {
8839
- return hex3(sha2566(new TextEncoder().encode(canonicalize(value))));
9057
+ return hex4(sha2567(new TextEncoder().encode(canonicalize(value))));
8840
9058
  }
8841
9059
  function registeredWrappedKeyDigest(encryptedSymmetricKey, pathBound) {
8842
- const rawDigest = hex3(sha2566(fromBase64(encryptedSymmetricKey, "v3 encrypted symmetric key")));
9060
+ const rawDigest = hex4(sha2567(fromBase64(encryptedSymmetricKey, "v3 encrypted symmetric key")));
8843
9061
  return pathBound ? rawDigest : canonicalHashHex(encryptedSymmetricKey);
8844
9062
  }
8845
9063
  async function aesGcmDecrypt(key, blob, aad) {
@@ -9008,7 +9226,7 @@ var ShareRecipientClient = class {
9008
9226
  const receiverPublicKey = toBase64(x25519.getPublicKey(receiverPrivateKey));
9009
9227
  const receiverPublicKeyHash = canonicalHashHex(receiverPublicKey);
9010
9228
  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 };
9011
- const bodyHash = hex3(sha2566(new TextEncoder().encode(canonicalize(body))));
9229
+ const bodyHash = hex4(sha2567(new TextEncoder().encode(canonicalize(body))));
9012
9230
  const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
9013
9231
  const now = Math.floor(Date.now() / 1e3);
9014
9232
  const decryptResource = encrypted.keyPath === void 0 ? encrypted.networkId : `${encrypted.networkId}/${encrypted.keyPath}`;
@@ -9029,7 +9247,7 @@ var ShareRecipientClient = class {
9029
9247
  if (!response.ok) throw await recipientHttpError(response, "v3 decrypt invocation rejected");
9030
9248
  const value = object3(await response.json(), "v3 decrypt response");
9031
9249
  const allowed = ["type", "targetNode", "networkId", "invocationCid", "encryptedSymmetricKeyHash", "receiverPublicKeyHash", "wrappedKey", "alg", "keyVersion", "requestHash", "nodeId", "nodeSignature"];
9032
- if (Object.keys(value).length !== allowed.length || Object.keys(value).some((key) => !allowed.includes(key)) || value.type !== "tinycloud.encryption.decrypt-result/v1" || value.targetNode !== body.targetNode || value.nodeId !== body.targetNode || value.networkId !== body.networkId || value.invocationCid !== invocation.cid || value.encryptedSymmetricKeyHash !== body.encryptedSymmetricKeyHash || value.receiverPublicKeyHash !== receiverPublicKeyHash || value.alg !== body.alg || value.keyVersion !== body.keyVersion || value.requestHash !== hex3(sha2566(new TextEncoder().encode(`${invocation.cid}${bodyHash}`))) || typeof value.wrappedKey !== "string" || typeof value.nodeSignature !== "string") throw new Error("v3 decrypt response binding is invalid");
9250
+ if (Object.keys(value).length !== allowed.length || Object.keys(value).some((key) => !allowed.includes(key)) || value.type !== "tinycloud.encryption.decrypt-result/v1" || value.targetNode !== body.targetNode || value.nodeId !== body.targetNode || value.networkId !== body.networkId || value.invocationCid !== invocation.cid || value.encryptedSymmetricKeyHash !== body.encryptedSymmetricKeyHash || value.receiverPublicKeyHash !== receiverPublicKeyHash || value.alg !== body.alg || value.keyVersion !== body.keyVersion || value.requestHash !== hex4(sha2567(new TextEncoder().encode(`${invocation.cid}${bodyHash}`))) || typeof value.wrappedKey !== "string" || typeof value.nodeSignature !== "string") throw new Error("v3 decrypt response binding is invalid");
9033
9251
  const unsigned = { ...value };
9034
9252
  delete unsigned.nodeSignature;
9035
9253
  const signature = fromBase64(value.nodeSignature, "v3 decrypt response signature");
@@ -9194,6 +9412,7 @@ async function openNativeShare(sharing, link) {
9194
9412
  }
9195
9413
  export {
9196
9414
  ADDRESSED_SHARE_TOO_LARGE_MESSAGE,
9415
+ AddressedContentSourceError,
9197
9416
  AddressedShareOverLimitError,
9198
9417
  ArtifactPinError,
9199
9418
  CredentialInvitationError,
@@ -9222,8 +9441,10 @@ export {
9222
9441
  artifactBindingView,
9223
9442
  assertAddressedShareFits,
9224
9443
  assertArtifactBinding,
9444
+ assertPathBoundContentSource,
9225
9445
  authorizationMethodForTarget,
9226
9446
  authorizeShare,
9447
+ bindPathBoundContentSource,
9227
9448
  canonicalOwnerSharePolicy,
9228
9449
  canonicalShareFilename,
9229
9450
  canonicalize,
@@ -9235,7 +9456,9 @@ export {
9235
9456
  createNativeShare,
9236
9457
  createPolicyEnforcementDelegation,
9237
9458
  createShareV2HolderBindingArtifact,
9459
+ defaultEncryptionNetworkForOwner,
9238
9460
  deliverCredentialInvitation,
9461
+ encryptAddressedShareContent,
9239
9462
  fromBase64Url,
9240
9463
  hasUnsafeFilenameCodePoint,
9241
9464
  historyRecordForPublishedShare,
@@ -9246,6 +9469,10 @@ export {
9246
9469
  openNativeShare,
9247
9470
  parseAddressedEnvelope,
9248
9471
  parseNativeShareUrl,
9472
+ pathBoundKeyPath,
9473
+ pathBoundSessionPermissions,
9474
+ pathBoundStoredBytes,
9475
+ planPathBoundShare,
9249
9476
  prepareAddressedShare,
9250
9477
  publishAddressedShare,
9251
9478
  publishPolicyShare,
@@ -9265,6 +9492,7 @@ export {
9265
9492
  toShareErrorInfo,
9266
9493
  validateOwnerSharePolicyRegistration,
9267
9494
  validateOwnerSharePolicyRegistrationBytes,
9268
- verifyArtifactShare
9495
+ verifyArtifactShare,
9496
+ wrappedKeyDigestHex
9269
9497
  };
9270
9498
  //# sourceMappingURL=index.js.map