@tinycloud/cli 1.0.0-beta.10 → 1.0.0-beta.12

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.js CHANGED
@@ -17579,6 +17579,68 @@ function validateV3Invariants(value, ctx) {
17579
17579
  ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt network" });
17580
17580
  }
17581
17581
  }
17582
+ function assertKey(key32) {
17583
+ if (key32.length !== KEY_LENGTH) {
17584
+ throw new TypeError(`key must be ${KEY_LENGTH} bytes, got ${key32.length}`);
17585
+ }
17586
+ }
17587
+ async function importAesKey(key32, usage) {
17588
+ assertKey(key32);
17589
+ return globalThis.crypto.subtle.importKey(
17590
+ "raw",
17591
+ key32,
17592
+ "AES-GCM",
17593
+ false,
17594
+ [usage]
17595
+ );
17596
+ }
17597
+ function generateKey() {
17598
+ return globalThis.crypto.getRandomValues(new Uint8Array(KEY_LENGTH));
17599
+ }
17600
+ async function seal(plaintextBytes, key32) {
17601
+ const { nonce, ciphertext } = await encryptEnvelope(plaintextBytes, key32);
17602
+ const blob = new Uint8Array(HEADER_LENGTH + nonce.length + ciphertext.length);
17603
+ blob[0] = SEALED_BLOB_VERSION;
17604
+ blob.set(nonce, HEADER_LENGTH);
17605
+ blob.set(ciphertext, HEADER_LENGTH + nonce.length);
17606
+ return { blob, cid: await computeCid(blob) };
17607
+ }
17608
+ async function open(blob, key32) {
17609
+ if (blob.length < HEADER_LENGTH + NONCE_LENGTH + TAG_LENGTH) {
17610
+ throw new TypeError(`sealed blob too short: ${blob.length} bytes`);
17611
+ }
17612
+ if (blob[0] !== SEALED_BLOB_VERSION) {
17613
+ throw new TypeError(`unknown sealed blob version: ${blob[0]}`);
17614
+ }
17615
+ const nonce = blob.subarray(HEADER_LENGTH, HEADER_LENGTH + NONCE_LENGTH);
17616
+ const ciphertext = blob.subarray(HEADER_LENGTH + NONCE_LENGTH);
17617
+ return decryptEnvelope(nonce, ciphertext, key32);
17618
+ }
17619
+ async function encryptEnvelope(plaintextBytes, key32) {
17620
+ const key = await importAesKey(key32, "encrypt");
17621
+ const nonce = globalThis.crypto.getRandomValues(new Uint8Array(NONCE_LENGTH));
17622
+ const ciphertext = new Uint8Array(
17623
+ await globalThis.crypto.subtle.encrypt(
17624
+ { name: "AES-GCM", iv: nonce, additionalData: AAD },
17625
+ key,
17626
+ plaintextBytes
17627
+ )
17628
+ );
17629
+ return { nonce, ciphertext };
17630
+ }
17631
+ async function decryptEnvelope(nonce, ciphertext, key32) {
17632
+ if (nonce.length !== NONCE_LENGTH) {
17633
+ throw new TypeError(`nonce must be ${NONCE_LENGTH} bytes, got ${nonce.length}`);
17634
+ }
17635
+ const key = await importAesKey(key32, "decrypt");
17636
+ return new Uint8Array(
17637
+ await globalThis.crypto.subtle.decrypt(
17638
+ { name: "AES-GCM", iv: nonce, additionalData: AAD },
17639
+ key,
17640
+ ciphertext
17641
+ )
17642
+ );
17643
+ }
17582
17644
  function isPlainRecord(value) {
17583
17645
  const proto = Object.getPrototypeOf(value);
17584
17646
  return proto === Object.prototype || proto === null;
@@ -17778,62 +17840,61 @@ function assertCanonicalCid(cidString) {
17778
17840
  throw new TypeError(`not a canonical CIDv1 raw sha2-256 base32 CID: ${cidString}`);
17779
17841
  }
17780
17842
  }
17781
- async function encodePublicInlineShareUrl(parts) {
17843
+ async function encodeSealedInlineShareUrl(parts) {
17782
17844
  if (!isCanonicalHttpsOrigin(parts.origin)) throw new TypeError("origin must be a canonical https origin");
17783
- if (parts.plaintext.byteLength === 0 || parts.plaintext.byteLength > MAX_INLINE_BYTES) throw new RangeError("public inline envelope is outside the allowed size");
17784
- const ciphertextCid = await computeCid(parts.plaintext);
17785
- const payloadBytes = new TextEncoder().encode(canonicalize2({
17845
+ if (parts.ciphertext.byteLength === 0 || parts.ciphertext.byteLength > MAX_INLINE_BYTES) throw new RangeError("inline ciphertext is outside the allowed size");
17846
+ if (parts.key32.byteLength !== KEY_LENGTH2) throw new TypeError("inline key must be 32 bytes");
17847
+ const ciphertextCid = await computeCid(parts.ciphertext);
17848
+ const payload = canonicalize2({
17786
17849
  v: 2,
17787
- c: toBase64Url(parts.plaintext),
17788
- cid: ciphertextCid
17789
- }));
17850
+ c: toBase64Url(parts.ciphertext),
17851
+ cid: ciphertextCid,
17852
+ k: toBase64Url(parts.key32)
17853
+ });
17854
+ const payloadBytes = new TextEncoder().encode(payload);
17790
17855
  if (payloadBytes.byteLength > MAX_INLINE_BYTES * 2) throw new RangeError("inline URL is too large");
17791
- return `${parts.origin}/viewer?${PUBLIC_INLINE_PARAMETER}=${toBase64Url(payloadBytes)}`;
17856
+ return `${parts.origin}/s/inline#v=2&p=${toBase64Url(payloadBytes)}`;
17792
17857
  }
17793
- function parseInlineShareUrl(url, options = {}) {
17858
+ async function parseSealedInlineShareUrl(url, options = {}) {
17794
17859
  const parsed = new URL(url);
17795
- if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "") throw new TypeError("inline share URL must be canonical HTTPS without userinfo");
17796
- if (!isCanonicalHttpsOrigin(parsed.origin) || options.expectedOrigin !== void 0 && parsed.origin !== options.expectedOrigin) throw new TypeError("inline share URL origin is not trusted");
17797
- if (parsed.pathname !== "/viewer") throw new TypeError("not a TinyCloud policy share URL");
17798
- const secretPayload = parsed.search === "" && parsed.hash.startsWith(INLINE_PREFIX) ? parsed.hash.slice(INLINE_PREFIX.length) : void 0;
17799
- const publicPayload = parsed.hash === "" && parsed.searchParams.size === 1 ? parsed.searchParams.get(PUBLIC_INLINE_PARAMETER) ?? void 0 : void 0;
17800
- if (secretPayload === void 0 && publicPayload === void 0) throw new TypeError("not a canonical TinyCloud policy share URL");
17801
- if (publicPayload !== void 0 && parsed.search !== `?${PUBLIC_INLINE_PARAMETER}=${publicPayload}`) throw new TypeError("public inline URL is not canonical");
17860
+ if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "") throw new TypeError("sealed inline share URL must be canonical HTTPS without userinfo");
17861
+ if (!isCanonicalHttpsOrigin(parsed.origin) || options.expectedOrigin !== void 0 && parsed.origin !== options.expectedOrigin) throw new TypeError("sealed inline share URL origin is not trusted");
17862
+ if (parsed.pathname !== "/s/inline" || parsed.search !== "") throw new TypeError("not a Node sealed-inline share URL");
17863
+ if (url !== `${parsed.origin}/s/inline${parsed.hash}`) throw new TypeError("sealed inline share URL is not lexically canonical");
17864
+ const prefix = "#v=2&p=";
17865
+ if (!parsed.hash.startsWith(prefix)) throw new TypeError("sealed inline URL is missing its canonical fragment");
17866
+ const encoded = parsed.hash.slice(prefix.length);
17867
+ if (encoded.length === 0 || parsed.hash !== `${prefix}${encoded}`) throw new TypeError("sealed inline URL fragment is not canonical");
17802
17868
  let payloadBytes;
17803
17869
  try {
17804
- payloadBytes = fromBase64Url(secretPayload ?? publicPayload);
17870
+ payloadBytes = fromBase64Url(encoded);
17805
17871
  } catch {
17806
- throw new TypeError("inline payload is not canonical base64url");
17872
+ throw new TypeError("sealed inline payload is not canonical base64url");
17807
17873
  }
17808
- if (payloadBytes.byteLength === 0 || payloadBytes.byteLength > MAX_INLINE_BYTES * 2) throw new TypeError("inline payload is too large");
17874
+ if (payloadBytes.byteLength === 0 || payloadBytes.byteLength > MAX_INLINE_BYTES * 2) throw new TypeError("sealed inline payload is too large");
17875
+ const payloadText = new TextDecoder("utf-8", { fatal: true }).decode(payloadBytes);
17809
17876
  let value;
17810
17877
  try {
17811
- value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payloadBytes));
17878
+ value = JSON.parse(payloadText);
17812
17879
  } catch {
17813
- throw new TypeError("inline payload is not valid JSON");
17880
+ throw new TypeError("sealed inline payload is not valid JSON");
17814
17881
  }
17815
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("inline payload must be an object");
17816
- if (canonicalize2(value) !== new TextDecoder("utf-8").decode(payloadBytes)) throw new TypeError("inline payload is not canonical JSON");
17882
+ if (typeof value !== "object" || value === null || Array.isArray(value) || canonicalize2(value) !== payloadText) throw new TypeError("sealed inline payload is not canonical JSON");
17817
17883
  const record = value;
17818
- const keys = Object.keys(record);
17819
- if (!keys.every((key) => key === "v" || key === "c" || key === "cid" || key === "k") || record.v !== 2 || typeof record.c !== "string" || typeof record.cid !== "string" || publicPayload !== void 0 && (keys.length !== 3 || record.k !== void 0)) throw new TypeError("inline payload has invalid fields");
17884
+ if (Object.keys(record).length !== 4 || record.v !== 2 || typeof record.c !== "string" || typeof record.cid !== "string" || typeof record.k !== "string") throw new TypeError("sealed inline payload has invalid fields");
17820
17885
  let ciphertext;
17886
+ let key32;
17821
17887
  try {
17822
17888
  ciphertext = fromBase64Url(record.c);
17889
+ key32 = fromBase64Url(record.k);
17823
17890
  } catch {
17824
- throw new TypeError("inline ciphertext is not canonical base64url");
17891
+ throw new TypeError("sealed inline payload has invalid base64url material");
17825
17892
  }
17826
- if (ciphertext.byteLength === 0 || ciphertext.byteLength > MAX_INLINE_BYTES) throw new TypeError("inline ciphertext is outside the allowed size");
17893
+ if (ciphertext.byteLength === 0 || ciphertext.byteLength > MAX_INLINE_BYTES) throw new TypeError("sealed inline ciphertext is outside the allowed size");
17894
+ if (key32.byteLength !== KEY_LENGTH2) throw new TypeError("sealed inline key must be 32 bytes");
17827
17895
  assertCanonicalCid(record.cid);
17828
- if (record.k !== void 0 && typeof record.k !== "string") throw new TypeError("inline key must be base64url");
17829
- let key32;
17830
- try {
17831
- key32 = record.k === void 0 ? void 0 : fromBase64Url(record.k);
17832
- } catch {
17833
- throw new TypeError("inline key is not canonical base64url");
17834
- }
17835
- if (key32 !== void 0 && key32.length !== KEY_LENGTH2) throw new TypeError("inline key must be 32 bytes");
17836
- return { kind: "inline", ciphertextCid: record.cid, ciphertext, ...key32 === void 0 ? {} : { key32 } };
17896
+ if (await computeCid(ciphertext) !== record.cid) throw new TypeError("sealed inline ciphertext does not match its CID");
17897
+ return { kind: "inline", ciphertextCid: record.cid, ciphertext, key32 };
17837
17898
  }
17838
17899
  function metadataFor(envelope, origin) {
17839
17900
  const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : "emailDomain";
@@ -17859,19 +17920,17 @@ async function resolvePolicyShare(link2, options) {
17859
17920
  let parsed;
17860
17921
  try {
17861
17922
  url = new URL(link2);
17862
- if (url.pathname !== "/viewer" || url.hash !== "" || url.searchParams.size !== 1 || !url.searchParams.has("tc2")) throw new Error("not public Policy/v3");
17863
- parsed = parseInlineShareUrl(link2, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
17864
- if (parsed.key32 !== void 0) throw new Error("secret inline transport is retired");
17923
+ parsed = await parseSealedInlineShareUrl(link2, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
17865
17924
  } catch {
17866
17925
  throw new ShareReceiveError("invalid-link", "share link format is invalid");
17867
17926
  }
17868
17927
  const limit = options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES;
17869
- if (!Number.isSafeInteger(limit) || limit <= 0 || parsed.ciphertext.byteLength > limit) throw new ShareReceiveError("max-bytes-exceeded", "public policy envelope exceeds the configured byte limit");
17870
- if (await computeCid(parsed.ciphertext) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "public envelope bytes do not match the link CID");
17928
+ if (!Number.isSafeInteger(limit) || limit <= 0 || parsed.ciphertext.byteLength > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed policy envelope exceeds the configured byte limit");
17929
+ if (await computeCid(parsed.ciphertext) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "sealed envelope bytes do not match the link CID");
17871
17930
  options.signal?.throwIfAborted();
17872
17931
  let envelope;
17873
17932
  try {
17874
- const encoded = new TextDecoder("utf-8", { fatal: true }).decode(parsed.ciphertext);
17933
+ const encoded = new TextDecoder("utf-8", { fatal: true }).decode(await open(parsed.ciphertext, parsed.key32));
17875
17934
  const value = JSON.parse(encoded);
17876
17935
  if (canonicalize2(value) !== encoded) throw new Error("non-canonical envelope");
17877
17936
  envelope = shareEnvelopeV3Schema.parse(value);
@@ -18455,14 +18514,25 @@ async function publishAddressedShare(options) {
18455
18514
  const envelope = { ...unsigned, signature: { signerDid: options.authority.ownerDid, algorithm: "Ed25519", value: toBase64Url(envelopeSignature) } };
18456
18515
  shareEnvelopeV3Schema.parse(envelope);
18457
18516
  const envelopeBytes = textEncoder.encode(canonicalize2(envelope));
18458
- const envelopeCid = await computeCid(envelopeBytes);
18459
- const url = await encodePublicInlineShareUrl({ origin: options.shareOrigin, plaintext: envelopeBytes });
18460
- const deliveryMaterial = { envelope, shareCid: envelopeCid };
18517
+ const envelopeKeyBytes = generateKey();
18518
+ const sealed = await seal(envelopeBytes, envelopeKeyBytes);
18519
+ const envelopeKey = toBase64Url(envelopeKeyBytes);
18520
+ const url = await encodeSealedInlineShareUrl({
18521
+ origin: options.shareOrigin,
18522
+ ciphertext: sealed.blob,
18523
+ key32: envelopeKeyBytes
18524
+ });
18525
+ const deliveryMaterial = {
18526
+ envelope,
18527
+ shareCid: sealed.cid,
18528
+ sealedEnvelope: toBase64Url(sealed.blob),
18529
+ envelopeKey
18530
+ };
18461
18531
  options.onDeliveryMaterial?.(deliveryMaterial);
18462
18532
  return publicationResult({
18463
18533
  options,
18464
18534
  url,
18465
- envelopeCid,
18535
+ envelopeCid: sealed.cid,
18466
18536
  matcher,
18467
18537
  policyCid: created.policyCid,
18468
18538
  policyRootCid: policyRoot.cid,
@@ -18695,6 +18765,110 @@ async function defaultIdempotencyKey(shareId, recipient) {
18695
18765
  ));
18696
18766
  return `tinycloud-share:${shareId}:${toBase64Url(digest3)}`;
18697
18767
  }
18768
+ function canonicalOrigin(value) {
18769
+ let url;
18770
+ try {
18771
+ url = new URL(value);
18772
+ } catch {
18773
+ throw new CredentialInvitationError(
18774
+ "invalid-origin",
18775
+ "credential invitation origin is invalid"
18776
+ );
18777
+ }
18778
+ if (url.origin !== value || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || url.protocol !== "https:")
18779
+ throw new CredentialInvitationError(
18780
+ "invalid-origin",
18781
+ "credential invitation origin is invalid"
18782
+ );
18783
+ return url.origin;
18784
+ }
18785
+ function object2(value) {
18786
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18787
+ }
18788
+ async function validateReceipt(receipt, shareUrl) {
18789
+ if (!object2(receipt) || !object2(receipt.request) || !object2(receipt.admission) || !object2(receipt.proof)) {
18790
+ throw new CredentialInvitationError(
18791
+ "invalid-receipt",
18792
+ "credential invitation receipt is invalid"
18793
+ );
18794
+ }
18795
+ if (receipt.request.returnLink !== shareUrl) {
18796
+ throw new CredentialInvitationError(
18797
+ "invalid-receipt",
18798
+ "credential invitation is not bound to the share link"
18799
+ );
18800
+ }
18801
+ let link2;
18802
+ try {
18803
+ link2 = new URL(shareUrl);
18804
+ } catch {
18805
+ throw new CredentialInvitationError(
18806
+ "invalid-receipt",
18807
+ "credential invitation share link is invalid"
18808
+ );
18809
+ }
18810
+ if (link2.search !== "" || link2.pathname !== "/s/inline") {
18811
+ throw new CredentialInvitationError(
18812
+ "invalid-receipt",
18813
+ "credential invitation share link must be sealed inline"
18814
+ );
18815
+ }
18816
+ try {
18817
+ await parseSealedInlineShareUrl(shareUrl);
18818
+ } catch {
18819
+ throw new CredentialInvitationError(
18820
+ "invalid-receipt",
18821
+ "credential invitation share link is invalid"
18822
+ );
18823
+ }
18824
+ }
18825
+ async function deliverCredentialInvitation(input) {
18826
+ const credentialsOrigin = canonicalOrigin(input.credentialsOrigin);
18827
+ await validateReceipt(input.receipt, input.shareUrl);
18828
+ const fetchFn = input.fetchFn ?? globalThis.fetch;
18829
+ let response;
18830
+ try {
18831
+ response = await fetchFn(`${credentialsOrigin}/v1/credential-invitations`, {
18832
+ method: "POST",
18833
+ credentials: "omit",
18834
+ redirect: "error",
18835
+ referrerPolicy: "no-referrer",
18836
+ headers: {
18837
+ accept: "application/json",
18838
+ "content-type": "application/json"
18839
+ },
18840
+ body: JSON.stringify(input.receipt),
18841
+ ...input.signal === void 0 ? {} : { signal: input.signal }
18842
+ });
18843
+ } catch {
18844
+ throw new CredentialInvitationError(
18845
+ "transport",
18846
+ "credential invitation delivery is unavailable"
18847
+ );
18848
+ }
18849
+ if (response.status !== 202) {
18850
+ throw new CredentialInvitationError(
18851
+ "rejected",
18852
+ "credential invitation delivery was not accepted"
18853
+ );
18854
+ }
18855
+ let body;
18856
+ try {
18857
+ body = await response.json();
18858
+ } catch {
18859
+ throw new CredentialInvitationError(
18860
+ "invalid-response",
18861
+ "credential invitation response is invalid"
18862
+ );
18863
+ }
18864
+ if (!object2(body) || Object.keys(body).length !== 1 || body.status !== "accepted") {
18865
+ throw new CredentialInvitationError(
18866
+ "invalid-response",
18867
+ "credential invitation response is invalid"
18868
+ );
18869
+ }
18870
+ return { status: "accepted" };
18871
+ }
18698
18872
  function targetKind2(record) {
18699
18873
  if (record.targetKind !== void 0) return record.targetKind;
18700
18874
  return record.recipientMatcher.kind === "exactEmail" ? "email" : record.recipientMatcher.kind === "emailDomain" ? "emailDomain" : record.recipientMatcher.kind === "recipientDid" ? "recipientDid" : "bearer";
@@ -18777,7 +18951,7 @@ function parseNativeShareUrl(value) {
18777
18951
  if (!token || fragment.size !== 1) throw new TypeError("missing native share fragment");
18778
18952
  return token;
18779
18953
  }
18780
- var __defProp2, __export2, external_exports2, util2, objectUtil2, ZodParsedType2, getParsedType2, ZodIssueCode2, quotelessJson2, ZodError2, errorMap2, en_default2, overrideErrorMap2, makeIssue2, EMPTY_PATH2, ParseStatus2, INVALID2, DIRTY2, OK2, isAborted2, isDirty2, isValid2, isAsync2, errorUtil2, ParseInputLazyPath2, handleResult2, ZodType2, cuidRegex2, cuid2Regex2, ulidRegex2, uuidRegex2, nanoidRegex2, jwtRegex2, durationRegex2, emailRegex2, _emojiRegex2, emojiRegex2, ipv4Regex2, ipv4CidrRegex2, ipv6Regex2, ipv6CidrRegex2, base64Regex2, base64urlRegex2, dateRegexSource2, dateRegex2, ZodString2, ZodNumber2, ZodBigInt2, ZodBoolean2, ZodDate2, ZodSymbol2, ZodUndefined2, ZodNull2, ZodAny2, ZodUnknown2, ZodNever2, ZodVoid2, ZodArray2, ZodObject2, ZodUnion2, getDiscriminator2, ZodDiscriminatedUnion2, ZodIntersection2, ZodTuple2, ZodRecord2, ZodMap2, ZodSet2, ZodFunction2, ZodLazy2, ZodLiteral2, ZodEnum2, ZodNativeEnum2, ZodPromise2, ZodEffects2, ZodOptional2, ZodNullable2, ZodDefault2, ZodCatch2, ZodNaN2, BRAND2, ZodBranded2, ZodPipeline2, ZodReadonly2, late2, ZodFirstPartyTypeKind2, instanceOfType2, stringType2, numberType2, nanType2, bigIntType2, booleanType2, dateType2, symbolType2, undefinedType2, nullType2, anyType2, unknownType2, neverType2, voidType2, arrayType2, objectType2, strictObjectType2, unionType2, discriminatedUnionType2, intersectionType2, tupleType2, recordType2, mapType2, setType2, functionType2, lazyType2, literalType2, enumType2, nativeEnumType2, promiseType2, effectsType2, optionalType2, nullableType2, preprocessType2, pipelineType2, ostring2, onumber2, oboolean2, coerce2, NEVER2, empty, src, _brrp__multiformats_scope_baseX, base_x_default, Encoder, Decoder, ComposedDecoder, Codec, base32, base32upper, base32pad, base32padupper, base32hex, base32hexupper, base32hexpad, base32hexpadupper, base32z, base36, base36upper, base58btc, base58flickr, encode_1, MSB, REST, MSBALL, INT, decode2, MSB$1, REST$1, N1, N2, N3, N4, N5, N6, N7, N8, N9, length, varint, _brrp_varint, varint_default, Digest, cache, CID, DAG_PB_CODE, SHA_256_CODE, cidSymbol, code, SHA256_CODE, base64, base64pad, base64url, base64urlpad, ED25519_MULTICODEC_PREFIX, PUBLIC_KEY_LENGTH, base64UrlString, sessionJwkCommonFields, okpPrivateJwkSchema, ecPrivateJwkSchema, sessionJwkSchema, policyTargetSchema, bearerKeyTargetSchema, recipientDidTargetSchema, authorizationTargetSchema, resourceSelectorSchema, targetSchema, displaySchema, contentPointerSchema, signatureSchema, unsignedShareEnvelopeSchema, shareEnvelopeSchema, recipientMatcherSchema, shareActionSchema, kvContentSourceSchema, sqlContentSourceSchema, contentSourceSchema, v2TargetSchema, shareDecryptionSchema, ownerAuthoritySchema, contentMetadataSchema, unsignedShareEnvelopeV2BaseSchema, unsignedShareEnvelopeV2Schema, shareEnvelopeV2Schema, unifiedResourceSchema, unifiedEncryptionNetworkSchema, unifiedKvCapabilitySchema, unifiedEncryptionCapabilitySchema, unifiedCapabilitySchema, unifiedContentSourceSchema, unifiedPolicyV1Schema, policyCredentialRequirementV1Schema, unifiedPolicyV2Schema, unifiedPolicySchema, unifiedRootSchema, attestedEnforcerBindingV2Schema, v3TargetSchema, unsignedShareEnvelopeV3BaseSchema, unsignedShareEnvelopeV3Schema, shareEnvelopeV3Schema, ENVELOPE_AAD_LABEL, AAD, ED25519_VERIFY_OPTS2, ENVELOPE_V3_SIGNATURE_DOMAIN, POLICY_V1_SIGNATURE_DOMAIN, POLICY_V2_SIGNATURE_DOMAIN, CONTENT_SOURCE_V1_DOMAIN, POLICY_CAPABILITY_V1_DOMAIN, NATIVE_PROJECTION_V1_DOMAIN, ATTESTED_ENFORCER_V2_DOMAIN, KEY_LENGTH2, INLINE_PREFIX, PUBLIC_INLINE_PARAMETER, MAX_INLINE_BYTES, SHARE_RESULT_VERSION, DEFAULT_MAX_SEALED_BLOB_BYTES, ShareReceiveError, SHARE_CONTENT_LIMIT, SHARE_PUBLISH_RESULT_VERSION, DEFAULT_SHARE_LIFETIME_MS, SharePublishError, empty2, src2, _brrp__multiformats_scope_baseX2, base_x_default2, Encoder2, Decoder2, ComposedDecoder2, Codec2, base58btc2, base58flickr2, POLICY_V1_DOMAIN, POLICY_V2_DOMAIN, POLICY_CAPABILITY_V1_DOMAIN2, CONTENT_SOURCE_V1_DOMAIN2, NATIVE_PROJECTION_V1_DOMAIN2, ENVELOPE_V3_DOMAIN, textEncoder, base322, base32upper2, base32pad2, base32padupper2, base32hex2, base32hexupper2, base32hexpad2, base32hexpadupper2, base32z2, base362, base36upper2, encode_12, MSB2, REST2, MSBALL2, INT2, decode6, MSB$12, REST$12, N12, N22, N32, N42, N52, N62, N72, N82, N92, length2, varint2, _brrp_varint2, varint_default2, Digest2, cache2, CID2, DAG_PB_CODE2, SHA_256_CODE2, cidSymbol2, MAX_CONTENT_BYTES, ShareNotifyError, SHARE_V2_PROTOCOL, DOMAIN, PRESENTATION_DOMAIN, SESSION_DOMAIN, INVOCATION_DOMAIN, NATIVE_SHARE_FRAGMENT_PARAMETER;
18954
+ var __defProp2, __export2, external_exports2, util2, objectUtil2, ZodParsedType2, getParsedType2, ZodIssueCode2, quotelessJson2, ZodError2, errorMap2, en_default2, overrideErrorMap2, makeIssue2, EMPTY_PATH2, ParseStatus2, INVALID2, DIRTY2, OK2, isAborted2, isDirty2, isValid2, isAsync2, errorUtil2, ParseInputLazyPath2, handleResult2, ZodType2, cuidRegex2, cuid2Regex2, ulidRegex2, uuidRegex2, nanoidRegex2, jwtRegex2, durationRegex2, emailRegex2, _emojiRegex2, emojiRegex2, ipv4Regex2, ipv4CidrRegex2, ipv6Regex2, ipv6CidrRegex2, base64Regex2, base64urlRegex2, dateRegexSource2, dateRegex2, ZodString2, ZodNumber2, ZodBigInt2, ZodBoolean2, ZodDate2, ZodSymbol2, ZodUndefined2, ZodNull2, ZodAny2, ZodUnknown2, ZodNever2, ZodVoid2, ZodArray2, ZodObject2, ZodUnion2, getDiscriminator2, ZodDiscriminatedUnion2, ZodIntersection2, ZodTuple2, ZodRecord2, ZodMap2, ZodSet2, ZodFunction2, ZodLazy2, ZodLiteral2, ZodEnum2, ZodNativeEnum2, ZodPromise2, ZodEffects2, ZodOptional2, ZodNullable2, ZodDefault2, ZodCatch2, ZodNaN2, BRAND2, ZodBranded2, ZodPipeline2, ZodReadonly2, late2, ZodFirstPartyTypeKind2, instanceOfType2, stringType2, numberType2, nanType2, bigIntType2, booleanType2, dateType2, symbolType2, undefinedType2, nullType2, anyType2, unknownType2, neverType2, voidType2, arrayType2, objectType2, strictObjectType2, unionType2, discriminatedUnionType2, intersectionType2, tupleType2, recordType2, mapType2, setType2, functionType2, lazyType2, literalType2, enumType2, nativeEnumType2, promiseType2, effectsType2, optionalType2, nullableType2, preprocessType2, pipelineType2, ostring2, onumber2, oboolean2, coerce2, NEVER2, empty, src, _brrp__multiformats_scope_baseX, base_x_default, Encoder, Decoder, ComposedDecoder, Codec, base32, base32upper, base32pad, base32padupper, base32hex, base32hexupper, base32hexpad, base32hexpadupper, base32z, base36, base36upper, base58btc, base58flickr, encode_1, MSB, REST, MSBALL, INT, decode2, MSB$1, REST$1, N1, N2, N3, N4, N5, N6, N7, N8, N9, length, varint, _brrp_varint, varint_default, Digest, cache, CID, DAG_PB_CODE, SHA_256_CODE, cidSymbol, code, SHA256_CODE, base64, base64pad, base64url, base64urlpad, ED25519_MULTICODEC_PREFIX, PUBLIC_KEY_LENGTH, base64UrlString, sessionJwkCommonFields, okpPrivateJwkSchema, ecPrivateJwkSchema, sessionJwkSchema, policyTargetSchema, bearerKeyTargetSchema, recipientDidTargetSchema, authorizationTargetSchema, resourceSelectorSchema, targetSchema, displaySchema, contentPointerSchema, signatureSchema, unsignedShareEnvelopeSchema, shareEnvelopeSchema, recipientMatcherSchema, shareActionSchema, kvContentSourceSchema, sqlContentSourceSchema, contentSourceSchema, v2TargetSchema, shareDecryptionSchema, ownerAuthoritySchema, contentMetadataSchema, unsignedShareEnvelopeV2BaseSchema, unsignedShareEnvelopeV2Schema, shareEnvelopeV2Schema, unifiedResourceSchema, unifiedEncryptionNetworkSchema, unifiedKvCapabilitySchema, unifiedEncryptionCapabilitySchema, unifiedCapabilitySchema, unifiedContentSourceSchema, unifiedPolicyV1Schema, policyCredentialRequirementV1Schema, unifiedPolicyV2Schema, unifiedPolicySchema, unifiedRootSchema, attestedEnforcerBindingV2Schema, v3TargetSchema, unsignedShareEnvelopeV3BaseSchema, unsignedShareEnvelopeV3Schema, shareEnvelopeV3Schema, ENVELOPE_AAD_LABEL, SEALED_BLOB_VERSION, AAD, KEY_LENGTH, NONCE_LENGTH, TAG_LENGTH, HEADER_LENGTH, ED25519_VERIFY_OPTS2, ENVELOPE_V3_SIGNATURE_DOMAIN, POLICY_V1_SIGNATURE_DOMAIN, POLICY_V2_SIGNATURE_DOMAIN, CONTENT_SOURCE_V1_DOMAIN, POLICY_CAPABILITY_V1_DOMAIN, NATIVE_PROJECTION_V1_DOMAIN, ATTESTED_ENFORCER_V2_DOMAIN, KEY_LENGTH2, MAX_INLINE_BYTES, SHARE_RESULT_VERSION, DEFAULT_MAX_SEALED_BLOB_BYTES, ShareReceiveError, SHARE_CONTENT_LIMIT, SHARE_PUBLISH_RESULT_VERSION, DEFAULT_SHARE_LIFETIME_MS, SharePublishError, empty2, src2, _brrp__multiformats_scope_baseX2, base_x_default2, Encoder2, Decoder2, ComposedDecoder2, Codec2, base58btc2, base58flickr2, POLICY_V1_DOMAIN, POLICY_V2_DOMAIN, POLICY_CAPABILITY_V1_DOMAIN2, CONTENT_SOURCE_V1_DOMAIN2, NATIVE_PROJECTION_V1_DOMAIN2, ENVELOPE_V3_DOMAIN, textEncoder, base322, base32upper2, base32pad2, base32padupper2, base32hex2, base32hexupper2, base32hexpad2, base32hexpadupper2, base32z2, base362, base36upper2, encode_12, MSB2, REST2, MSBALL2, INT2, decode6, MSB$12, REST$12, N12, N22, N32, N42, N52, N62, N72, N82, N92, length2, varint2, _brrp_varint2, varint_default2, Digest2, cache2, CID2, DAG_PB_CODE2, SHA_256_CODE2, cidSymbol2, MAX_CONTENT_BYTES, ShareNotifyError, CredentialInvitationError, SHARE_V2_PROTOCOL, DOMAIN, PRESENTATION_DOMAIN, SESSION_DOMAIN, INVOCATION_DOMAIN, NATIVE_SHARE_FRAGMENT_PARAMETER;
18781
18955
  var init_dist3 = __esm({
18782
18956
  "../share-sdk/dist/index.js"() {
18783
18957
  "use strict";
@@ -18932,10 +19106,10 @@ var init_dist3 = __esm({
18932
19106
  return obj[e];
18933
19107
  });
18934
19108
  };
18935
- util22.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object3) => {
19109
+ util22.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object4) => {
18936
19110
  const keys = [];
18937
- for (const key in object3) {
18938
- if (Object.prototype.hasOwnProperty.call(object3, key)) {
19111
+ for (const key in object4) {
19112
+ if (Object.prototype.hasOwnProperty.call(object4, key)) {
18939
19113
  keys.push(key);
18940
19114
  }
18941
19115
  }
@@ -23394,7 +23568,12 @@ var init_dist3 = __esm({
23394
23568
  unsignedShareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.superRefine(validateV3Invariants);
23395
23569
  shareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.extend({ signature: signatureSchema }).strict().superRefine(validateV3Invariants);
23396
23570
  ENVELOPE_AAD_LABEL = "tinycloud-share-envelope-v1";
23571
+ SEALED_BLOB_VERSION = 1;
23397
23572
  AAD = utf8Bytes(ENVELOPE_AAD_LABEL);
23573
+ KEY_LENGTH = 32;
23574
+ NONCE_LENGTH = 12;
23575
+ TAG_LENGTH = 16;
23576
+ HEADER_LENGTH = 1;
23398
23577
  ED25519_VERIFY_OPTS2 = { zip215: false };
23399
23578
  ENVELOPE_V3_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
23400
23579
  POLICY_V1_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
@@ -23404,8 +23583,6 @@ var init_dist3 = __esm({
23404
23583
  NATIVE_PROJECTION_V1_DOMAIN = "xyz.tinycloud.policy/NativeProjection/v1\0";
23405
23584
  ATTESTED_ENFORCER_V2_DOMAIN = "xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0";
23406
23585
  KEY_LENGTH2 = 32;
23407
- INLINE_PREFIX = "#tc2=";
23408
- PUBLIC_INLINE_PARAMETER = "tc2";
23409
23586
  MAX_INLINE_BYTES = 256 * 1024;
23410
23587
  SHARE_RESULT_VERSION = 1;
23411
23588
  DEFAULT_MAX_SEALED_BLOB_BYTES = 100 * 1024 * 1024 + 29;
@@ -23906,6 +24083,13 @@ var init_dist3 = __esm({
23906
24083
  this.name = "ShareNotifyError";
23907
24084
  }
23908
24085
  };
24086
+ CredentialInvitationError = class extends Error {
24087
+ constructor(code32, message) {
24088
+ super(message);
24089
+ this.code = code32;
24090
+ this.name = "CredentialInvitationError";
24091
+ }
24092
+ };
23909
24093
  SHARE_V2_PROTOCOL = Object.freeze({
23910
24094
  challengeDomain: "xyz.tinycloud.share/policy-challenge/v2\0",
23911
24095
  sessionDomain: "xyz.tinycloud.share/policy-session/v2\0",
@@ -24571,9 +24755,9 @@ async function revokePolicyRootV3(input) {
24571
24755
  body: JSON.stringify({ revocation: { ...unsigned, signature: { suite: "Ed25519", signerDid: input.issuerDid, value: encodeBase64Url2(signature) } } })
24572
24756
  });
24573
24757
  if (!response.ok) throw new Error(`policy root revocation rejected (${response.status})`);
24574
- return object2(await response.json(), "policy root revocation");
24758
+ return object22(await response.json(), "policy root revocation");
24575
24759
  }
24576
- function object2(value, label) {
24760
+ function object22(value, label) {
24577
24761
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
24578
24762
  throw new Error(`${label} must be an object`);
24579
24763
  }
@@ -29076,7 +29260,7 @@ function receiveJson(result, path) {
29076
29260
 
29077
29261
  // src/share/io.ts
29078
29262
  import { constants } from "fs";
29079
- import { lstat, mkdir as mkdir2, mkdtemp, open, readFile as readFile2, realpath, stat as stat2, link, rename as rename2, rm as rm2, unlink } from "fs/promises";
29263
+ import { lstat, mkdir as mkdir2, mkdtemp, open as open2, readFile as readFile2, realpath, stat as stat2, link, rename as rename2, rm as rm2, unlink } from "fs/promises";
29080
29264
  import { randomBytes as randomBytes2 } from "crypto";
29081
29265
  import { basename as basename2, join as join4, resolve, sep } from "path";
29082
29266
  var MAX_SHARE_STDIN_BYTES = 100 * 1024 * 1024;
@@ -29144,7 +29328,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
29144
29328
  const outputDirectory = resolve(directory);
29145
29329
  await assertDirectory(outputDirectory);
29146
29330
  const safeName = safeFilename(filename);
29147
- const directoryHandle = await open(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
29331
+ const directoryHandle = await open2(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
29148
29332
  const stableDirectory = await realpath(outputDirectory);
29149
29333
  const outputPath = join4(stableDirectory, safeName);
29150
29334
  const directoryIdentity = await directoryHandle.stat();
@@ -29168,7 +29352,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
29168
29352
  if (error.code !== "ENOENT") throw error;
29169
29353
  }
29170
29354
  temporaryPath = stagingPath;
29171
- handle = await open(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
29355
+ handle = await open2(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
29172
29356
  await handle.writeFile(bytes);
29173
29357
  await handle.close();
29174
29358
  handle = void 0;
@@ -30819,18 +31003,6 @@ var MAX_INLINE_BYTES2 = 256 * 1024;
30819
31003
  // src/share/adapters.ts
30820
31004
  init_dist4();
30821
31005
  var DEFAULT_SHARE_ORIGIN = "https://share.tinycloud.xyz";
30822
- async function postAddressedShareDelivery(input) {
30823
- if (input.receipt.request.returnLink !== input.shareUrl) throw new Error("credential invitation is not bound to the share link");
30824
- return input.fetchFn(`${input.emailOrigin}/v1/email`, {
30825
- method: "POST",
30826
- credentials: "omit",
30827
- redirect: "error",
30828
- referrerPolicy: "no-referrer",
30829
- headers: { accept: "application/json", "content-type": "application/json" },
30830
- body: JSON.stringify(input.receipt),
30831
- signal: input.signal
30832
- });
30833
- }
30834
31006
  function createEncryptedSessionHistory() {
30835
31007
  const records = /* @__PURE__ */ new Map();
30836
31008
  let keyPromise;
@@ -30958,7 +31130,7 @@ function createEncryptedProfileHistory(profileName, sessionSigner) {
30958
31130
  function createShareAuthorityAdapters(input = {}) {
30959
31131
  const origin = input.origin ?? DEFAULT_SHARE_ORIGIN;
30960
31132
  const fetchFn = input.fetchFn ?? globalThis.fetch;
30961
- const canonicalOrigin = (value, label) => {
31133
+ const canonicalOrigin2 = (value, label) => {
30962
31134
  if (typeof value !== "string") throw new Error(`share ${label} is unavailable`);
30963
31135
  const parsed = new URL(value);
30964
31136
  const loopback = parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost";
@@ -30979,9 +31151,9 @@ function createShareAuthorityAdapters(input = {}) {
30979
31151
  const object3 = value;
30980
31152
  if (object3.version !== "tinycloud.share/config-v2") throw new Error("share public config version is unsupported");
30981
31153
  return {
30982
- shareOrigin: canonicalOrigin(object3.shareOrigin, "origin"),
30983
- registryOrigin: canonicalOrigin(object3.registryOrigin, "registry origin"),
30984
- emailOrigin: canonicalOrigin(input.emailOrigin ?? object3.emailOrigin, "email origin")
31154
+ shareOrigin: canonicalOrigin2(object3.shareOrigin, "origin"),
31155
+ registryOrigin: canonicalOrigin2(object3.registryOrigin, "registry origin"),
31156
+ credentialsOrigin: canonicalOrigin2(input.credentialsOrigin ?? object3.credentialsOrigin, "credentials origin")
30985
31157
  };
30986
31158
  })();
30987
31159
  let nodePromise;
@@ -31088,27 +31260,34 @@ function createShareAuthorityAdapters(input = {}) {
31088
31260
  } };
31089
31261
  const delivery = { deliver: input.deliver ?? (async (request) => {
31090
31262
  const record = request.record;
31091
- if (record === void 0 || record.link === void 0 || record.deliveryMaterial === void 0) throw new Error("share delivery history is incomplete");
31263
+ if (record === void 0 || record.link === void 0 || record.deliveryMaterial === void 0 || request.idempotencyKey === void 0) throw new Error("share delivery history is incomplete");
31092
31264
  const [config, node] = await Promise.all([publicConfig(), authenticatedNode()]);
31265
+ const authorizationExpiresAt = new Date(Math.min(
31266
+ Date.parse(record.expiresAt),
31267
+ Date.parse(record.registeredAt) + 5 * 60 * 1e3
31268
+ )).toISOString();
31269
+ if (Date.parse(authorizationExpiresAt) <= Date.now()) throw new Error("share delivery authorization retry window has expired");
31093
31270
  const receipt = await node.authorizeShareDeliveryV3({
31094
31271
  envelope: record.deliveryMaterial.envelope,
31272
+ sealedEnvelope: record.deliveryMaterial.sealedEnvelope,
31273
+ envelopeKey: record.deliveryMaterial.envelopeKey,
31095
31274
  shareCid: record.deliveryMaterial.shareCid,
31096
31275
  resourcePath: record.resource.path,
31097
31276
  recipientEmail: request.recipient,
31098
31277
  shareUrl: record.link,
31099
31278
  documentName: record.filename ?? "share.md",
31100
- expiresAt: new Date(Math.min(Date.parse(record.expiresAt), Date.now() + 5 * 60 * 1e3)).toISOString(),
31101
- deliveryAudience: config.emailOrigin
31279
+ expiresAt: authorizationExpiresAt,
31280
+ deliveryAudience: config.credentialsOrigin,
31281
+ idempotencyKey: request.idempotencyKey
31102
31282
  });
31103
- const response = await postAddressedShareDelivery({
31104
- emailOrigin: config.emailOrigin,
31283
+ await deliverCredentialInvitation({
31284
+ credentialsOrigin: config.credentialsOrigin,
31105
31285
  receipt,
31106
31286
  shareUrl: record.link,
31107
31287
  fetchFn,
31108
31288
  signal: request.signal
31109
31289
  });
31110
- if (!response.ok) throw new Error("share delivery was not accepted");
31111
- return response.status === 208 ? "already-delivered" : "delivered";
31290
+ return "delivered";
31112
31291
  }) };
31113
31292
  const revocation = {
31114
31293
  revokeDelegation: input.revokeDelegation ?? (async (request) => {
@@ -31139,7 +31318,7 @@ function createShareAuthorityAdapters(input = {}) {
31139
31318
  const decoder = new TinyCloudNode2({ autoDiscoverLocalNode: false });
31140
31319
  const decoded = decoder.sharing.decodeLink(token);
31141
31320
  if (typeof decoded.host !== "string") throw new Error("native share has no owner Node");
31142
- const client = new TinyCloudNode2({ host: canonicalOrigin(decoded.host, "owner node origin"), autoDiscoverLocalNode: false });
31321
+ const client = new TinyCloudNode2({ host: canonicalOrigin2(decoded.host, "owner node origin"), autoDiscoverLocalNode: false });
31143
31322
  const received = await client.sharing.receive(token, { autoSubdelegate: false, useSessionKey: false });
31144
31323
  if (!received.ok) throw new Error("native share could not be verified");
31145
31324
  const value = await received.data.kv.get("", { binary: true });