@tinycloud/cli 1.5.0-beta.22 → 1.5.0-beta.24

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.
@@ -1901,11 +1901,13 @@ function expandEncryptionPermissionEntry(entry) {
1901
1901
  `tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
1902
1902
  );
1903
1903
  }
1904
- if (entry.path.includes("/")) {
1904
+ const sharePrefix = entry.path.endsWith("/share/");
1905
+ if (entry.path.includes("/") && !sharePrefix) {
1905
1906
  throw new ManifestValidationError("named keys go in manifest.keys");
1906
1907
  }
1907
1908
  try {
1908
- parseNetworkId(entry.path);
1909
+ const network = parseNetworkId(sharePrefix ? entry.path.slice(0, -"/share/".length) : entry.path);
1910
+ if (sharePrefix && network.name !== "default") throw new Error("share prefix requires default network");
1909
1911
  } catch {
1910
1912
  throw new ManifestValidationError("tinycloud.encryption entries require a valid networkId URN");
1911
1913
  }
@@ -1940,6 +1942,9 @@ function expandEncryptionPermissionEntry(entry) {
1940
1942
  seen.add(a);
1941
1943
  }
1942
1944
  }
1945
+ if (sharePrefix && dedupedActions.some((action) => action !== "tinycloud.encryption/decrypt")) {
1946
+ throw new ManifestValidationError("share prefix only supports decrypt");
1947
+ }
1943
1948
  return [
1944
1949
  {
1945
1950
  service: ENCRYPTION_PERMISSION_SERVICE,
@@ -2555,6 +2560,9 @@ function isRawEncryptionPermission(permission) {
2555
2560
  function isVerifiedRawEncryptionPermission(permission) {
2556
2561
  return isRawEncryptionPermission(permission) && (permission.space === void 0 || permission.space === "encryption");
2557
2562
  }
2563
+ function isSharePublishingPrefixDecrypt(permission) {
2564
+ return permission.service === ENCRYPTION_PERMISSION_SERVICE && permission.space === "default" && permission.path === "share/" && permission.actions.length === 1 && permission.actions[0] === "tinycloud.encryption/decrypt" && !permission.caveats?.length;
2565
+ }
2558
2566
  function isDeviceDefaultDecrypt(permission) {
2559
2567
  return permission.service === ENCRYPTION_PERMISSION_SERVICE && permission.space === "default" && permission.path === "" && permission.actions.length === 1 && permission.actions[0] === "tinycloud.encryption/decrypt" && !permission.caveats?.length;
2560
2568
  }
@@ -2579,7 +2587,7 @@ function canonicalNetworkUrn(path) {
2579
2587
  return match ? `urn:tinycloud:encryption:${canonicalOwnerDid(match[1])}:${match[2]}` : path;
2580
2588
  }
2581
2589
  function rawEncryptionOwnerMatches(path, ownerDid) {
2582
- const match = /^urn:tinycloud:encryption:(did:pkh:eip155:.+):([a-z0-9][a-z0-9-]*)$/.exec(path);
2590
+ const match = /^urn:tinycloud:encryption:(did:pkh:eip155:[1-9]\d*:0x[0-9a-fA-F]{40}):([a-z0-9][a-z0-9-]*)(?:\/share\/(?:[0-9a-f]{32})?)?$/.exec(path);
2583
2591
  if (!match) return false;
2584
2592
  try {
2585
2593
  return canonicalOwnerDid(match[1]) === canonicalOwnerDid(ownerDid);
@@ -2671,7 +2679,7 @@ var init_publishing_manifest = __esm({
2671
2679
  },
2672
2680
  {
2673
2681
  service: "tinycloud.encryption",
2674
- path: "",
2682
+ path: "share/",
2675
2683
  skipPrefix: true,
2676
2684
  actions: ["tinycloud.encryption/decrypt"]
2677
2685
  }
@@ -3053,6 +3061,10 @@ async function resolvePermissionSpaces(entries, profile, options = {}) {
3053
3061
  resolved.push({ ...raw, service, space: ENCRYPTION_MANIFEST_SPACE, actions });
3054
3062
  continue;
3055
3063
  }
3064
+ if (isSharePublishingPrefixDecrypt({ ...entry, service, actions })) {
3065
+ resolved.push({ service, space: ENCRYPTION_MANIFEST_SPACE, path: `${await defaultSecretsNetworkId(profile, options.ownerDid)}/share/`, actions });
3066
+ continue;
3067
+ }
3056
3068
  if (isDeviceDefaultDecrypt({ ...entry, service, actions })) {
3057
3069
  resolved.push({ service, space: ENCRYPTION_MANIFEST_SPACE, path: await defaultSecretsNetworkId(profile, options.ownerDid), actions });
3058
3070
  continue;
@@ -21938,6 +21950,23 @@ var unifiedEncryptionNetworkSchema = external_exports.string().refine((value) =>
21938
21950
  const network = separator < 0 ? "" : rest.slice(separator + 1);
21939
21951
  return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
21940
21952
  }, { message: "expected canonical TinyCloud encryption network" });
21953
+ var unifiedEncryptionResourceSchema = external_exports.string().refine((value) => {
21954
+ const prefix = "urn:tinycloud:encryption:";
21955
+ if (!value.startsWith(prefix)) return false;
21956
+ const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
21957
+ if (match === null) return false;
21958
+ const path = match[3];
21959
+ return path === void 0 || path.split("/").every(
21960
+ (segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
21961
+ );
21962
+ }, { message: "expected a canonical TinyCloud encryption resource" });
21963
+ var unifiedEncryptionKeyPathSchema = external_exports.string().regex(/^share\/[^/]+$/).refine(
21964
+ (value) => {
21965
+ const segment = value.slice("share/".length);
21966
+ return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
21967
+ },
21968
+ { message: "expected an exact share key path" }
21969
+ );
21941
21970
  var unifiedKvCapabilitySchema = external_exports.object({
21942
21971
  kind: external_exports.literal("kv"),
21943
21972
  resource: unifiedResourceSchema,
@@ -21951,7 +21980,7 @@ var unifiedKvCapabilitySchema = external_exports.object({
21951
21980
  }).strict();
21952
21981
  var unifiedEncryptionCapabilitySchema = external_exports.object({
21953
21982
  kind: external_exports.literal("encryption"),
21954
- resource: unifiedEncryptionNetworkSchema,
21983
+ resource: unifiedEncryptionResourceSchema,
21955
21984
  action: external_exports.literal("tinycloud.encryption/decrypt")
21956
21985
  }).strict();
21957
21986
  var unifiedCapabilitySchema = external_exports.union([unifiedKvCapabilitySchema, unifiedEncryptionCapabilitySchema]);
@@ -21960,6 +21989,7 @@ var unifiedContentSourceSchema = external_exports.object({
21960
21989
  kvResource: unifiedResourceSchema,
21961
21990
  selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
21962
21991
  encryptionNetwork: unifiedEncryptionNetworkSchema,
21992
+ encryptionKeyPath: unifiedEncryptionKeyPathSchema.optional(),
21963
21993
  encryptedSymmetricKeyDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
21964
21994
  keyVersion: external_exports.number().int().positive(),
21965
21995
  mode: external_exports.union([external_exports.literal("mutable"), external_exports.literal("immutable")]),
@@ -22077,16 +22107,21 @@ function validateV3Invariants(value, ctx) {
22077
22107
  if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
22078
22108
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
22079
22109
  }
22080
- 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) {
22110
+ const encryptionKeyPath = value.contentSource.encryptionKeyPath;
22111
+ if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
22112
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
22113
+ }
22114
+ 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) {
22081
22115
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
22082
22116
  }
22083
22117
  if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
22084
22118
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
22085
22119
  }
22086
22120
  const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
22087
- const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === value.encryptionNetwork && capability.action === "tinycloud.encryption/decrypt");
22121
+ const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
22122
+ const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
22088
22123
  if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
22089
- ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt network" });
22124
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
22090
22125
  }
22091
22126
  }
22092
22127
  var unsignedShareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.superRefine(validateV3Invariants);
@@ -23377,6 +23412,23 @@ var unifiedEncryptionNetworkSchema2 = external_exports2.string().refine((value)
23377
23412
  const network = separator < 0 ? "" : rest.slice(separator + 1);
23378
23413
  return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
23379
23414
  }, { message: "expected canonical TinyCloud encryption network" });
23415
+ var unifiedEncryptionResourceSchema2 = external_exports2.string().refine((value) => {
23416
+ const prefix = "urn:tinycloud:encryption:";
23417
+ if (!value.startsWith(prefix)) return false;
23418
+ const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
23419
+ if (match === null) return false;
23420
+ const path = match[3];
23421
+ return path === void 0 || path.split("/").every(
23422
+ (segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
23423
+ );
23424
+ }, { message: "expected a canonical TinyCloud encryption resource" });
23425
+ var unifiedEncryptionKeyPathSchema2 = external_exports2.string().regex(/^share\/[^/]+$/).refine(
23426
+ (value) => {
23427
+ const segment = value.slice("share/".length);
23428
+ return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
23429
+ },
23430
+ { message: "expected an exact share key path" }
23431
+ );
23380
23432
  var unifiedKvCapabilitySchema2 = external_exports2.object({
23381
23433
  kind: external_exports2.literal("kv"),
23382
23434
  resource: unifiedResourceSchema2,
@@ -23390,7 +23442,7 @@ var unifiedKvCapabilitySchema2 = external_exports2.object({
23390
23442
  }).strict();
23391
23443
  var unifiedEncryptionCapabilitySchema2 = external_exports2.object({
23392
23444
  kind: external_exports2.literal("encryption"),
23393
- resource: unifiedEncryptionNetworkSchema2,
23445
+ resource: unifiedEncryptionResourceSchema2,
23394
23446
  action: external_exports2.literal("tinycloud.encryption/decrypt")
23395
23447
  }).strict();
23396
23448
  var unifiedCapabilitySchema2 = external_exports2.union([unifiedKvCapabilitySchema2, unifiedEncryptionCapabilitySchema2]);
@@ -23399,6 +23451,7 @@ var unifiedContentSourceSchema2 = external_exports2.object({
23399
23451
  kvResource: unifiedResourceSchema2,
23400
23452
  selector: external_exports2.union([external_exports2.literal("exact"), external_exports2.literal("prefix")]),
23401
23453
  encryptionNetwork: unifiedEncryptionNetworkSchema2,
23454
+ encryptionKeyPath: unifiedEncryptionKeyPathSchema2.optional(),
23402
23455
  encryptedSymmetricKeyDigestHex: external_exports2.string().regex(/^[0-9a-f]{64}$/),
23403
23456
  keyVersion: external_exports2.number().int().positive(),
23404
23457
  mode: external_exports2.union([external_exports2.literal("mutable"), external_exports2.literal("immutable")]),
@@ -23516,16 +23569,21 @@ function validateV3Invariants2(value, ctx) {
23516
23569
  if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
23517
23570
  ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
23518
23571
  }
23519
- 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) {
23572
+ const encryptionKeyPath = value.contentSource.encryptionKeyPath;
23573
+ if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
23574
+ ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
23575
+ }
23576
+ 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) {
23520
23577
  ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
23521
23578
  }
23522
23579
  if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
23523
23580
  ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
23524
23581
  }
23525
23582
  const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
23526
- const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === value.encryptionNetwork && capability.action === "tinycloud.encryption/decrypt");
23583
+ const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
23584
+ const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
23527
23585
  if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
23528
- ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt network" });
23586
+ ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
23529
23587
  }
23530
23588
  }
23531
23589
  var unsignedShareEnvelopeV3Schema2 = unsignedShareEnvelopeV3BaseSchema2.superRefine(validateV3Invariants2);
@@ -23574,53 +23632,13 @@ function shortLinkId(link4) {
23574
23632
  }
23575
23633
  }
23576
23634
 
23577
- // src/share/errors.ts
23578
- var SharePublishAuthorityError = class extends Error {
23579
- constructor(failure, options) {
23580
- super(failure.kind, options);
23581
- this.failure = failure;
23582
- this.name = "SharePublishAuthorityError";
23583
- }
23584
- };
23585
- var ShareHistoryRetryError = class extends Error {
23586
- constructor(profile) {
23587
- super("sender history changed during the operation");
23588
- this.profile = profile;
23589
- this.name = "ShareHistoryRetryError";
23590
- }
23591
- code = "SHARE_HISTORY_RETRY";
23592
- };
23593
- var ShareHistoryUnreadableError = class extends Error {
23594
- constructor() {
23595
- super("share history is unavailable");
23596
- this.name = "ShareHistoryUnreadableError";
23597
- }
23598
- };
23599
- var ShareHistoryReadError = class extends Error {
23600
- constructor() {
23601
- super("share history could not be read");
23602
- this.name = "ShareHistoryReadError";
23603
- }
23604
- };
23605
- var HISTORY_WRITE_ERROR_CODES = { EACCES: true, EPERM: true, EROFS: true, ENOSPC: true, EDQUOT: true, EIO: true };
23606
- function shareHistoryReason(error) {
23607
- if (error instanceof ShareHistoryUnreadableError) return "HISTORY_UNREADABLE";
23608
- if (error instanceof ShareHistoryReadError) return "HISTORY_READ_FAILED";
23609
- const code5 = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
23610
- return typeof code5 === "string" && HISTORY_WRITE_ERROR_CODES[code5] === true ? "HISTORY_WRITE_FAILED" : "HISTORY_UNAVAILABLE";
23611
- }
23612
-
23613
- // src/share/adapters.ts
23614
- init_storage2();
23615
- init_profiles();
23616
- init_constants();
23617
- init_errors();
23618
- init_session_expired();
23619
- import { chmod as chmod4, link as link2, open as open8, readFile as readFile8, stat as stat4, unlink as unlink4 } from "fs/promises";
23620
- import { dirname as dirname5, join as join12 } from "path";
23621
- import { createHash } from "crypto";
23622
- import { writeJsonAtomic as writeJsonAtomic2 } from "@tinycloud/operations/state";
23623
- import { withoutBearer as withoutBearer2 } from "@tinycloud/operations/delegation-binding";
23635
+ // src/share/email-publish-v3.ts
23636
+ import {
23637
+ canonicalDigest,
23638
+ createEmailCredentialRequirement,
23639
+ credentialRequirementDigest,
23640
+ validateCredentialFlowDescriptor
23641
+ } from "@tinycloud/sdk-core";
23624
23642
 
23625
23643
  // ../share-sdk/dist/index.js
23626
23644
  init_sha2();
@@ -28840,6 +28858,23 @@ var unifiedEncryptionNetworkSchema3 = external_exports3.string().refine((value)
28840
28858
  const network = separator < 0 ? "" : rest.slice(separator + 1);
28841
28859
  return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
28842
28860
  }, { message: "expected canonical TinyCloud encryption network" });
28861
+ var unifiedEncryptionResourceSchema3 = external_exports3.string().refine((value) => {
28862
+ const prefix = "urn:tinycloud:encryption:";
28863
+ if (!value.startsWith(prefix)) return false;
28864
+ const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
28865
+ if (match === null) return false;
28866
+ const path = match[3];
28867
+ return path === void 0 || path.split("/").every(
28868
+ (segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
28869
+ );
28870
+ }, { message: "expected a canonical TinyCloud encryption resource" });
28871
+ var unifiedEncryptionKeyPathSchema3 = external_exports3.string().regex(/^share\/[^/]+$/).refine(
28872
+ (value) => {
28873
+ const segment = value.slice("share/".length);
28874
+ return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
28875
+ },
28876
+ { message: "expected an exact share key path" }
28877
+ );
28843
28878
  var unifiedKvCapabilitySchema3 = external_exports3.object({
28844
28879
  kind: external_exports3.literal("kv"),
28845
28880
  resource: unifiedResourceSchema3,
@@ -28853,7 +28888,7 @@ var unifiedKvCapabilitySchema3 = external_exports3.object({
28853
28888
  }).strict();
28854
28889
  var unifiedEncryptionCapabilitySchema3 = external_exports3.object({
28855
28890
  kind: external_exports3.literal("encryption"),
28856
- resource: unifiedEncryptionNetworkSchema3,
28891
+ resource: unifiedEncryptionResourceSchema3,
28857
28892
  action: external_exports3.literal("tinycloud.encryption/decrypt")
28858
28893
  }).strict();
28859
28894
  var unifiedCapabilitySchema3 = external_exports3.union([unifiedKvCapabilitySchema3, unifiedEncryptionCapabilitySchema3]);
@@ -28862,6 +28897,7 @@ var unifiedContentSourceSchema3 = external_exports3.object({
28862
28897
  kvResource: unifiedResourceSchema3,
28863
28898
  selector: external_exports3.union([external_exports3.literal("exact"), external_exports3.literal("prefix")]),
28864
28899
  encryptionNetwork: unifiedEncryptionNetworkSchema3,
28900
+ encryptionKeyPath: unifiedEncryptionKeyPathSchema3.optional(),
28865
28901
  encryptedSymmetricKeyDigestHex: external_exports3.string().regex(/^[0-9a-f]{64}$/),
28866
28902
  keyVersion: external_exports3.number().int().positive(),
28867
28903
  mode: external_exports3.union([external_exports3.literal("mutable"), external_exports3.literal("immutable")]),
@@ -28979,22 +29015,71 @@ function validateV3Invariants3(value, ctx) {
28979
29015
  if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
28980
29016
  ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
28981
29017
  }
28982
- 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) {
29018
+ const encryptionKeyPath = value.contentSource.encryptionKeyPath;
29019
+ if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
29020
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
29021
+ }
29022
+ 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) {
28983
29023
  ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
28984
29024
  }
28985
29025
  if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
28986
29026
  ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
28987
29027
  }
28988
29028
  const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
28989
- const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === value.encryptionNetwork && capability.action === "tinycloud.encryption/decrypt");
29029
+ const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
29030
+ const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
28990
29031
  if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
28991
- ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt network" });
29032
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
28992
29033
  }
28993
29034
  }
28994
29035
  var unsignedShareEnvelopeV3Schema3 = unsignedShareEnvelopeV3BaseSchema3.superRefine(validateV3Invariants3);
28995
29036
  var shareEnvelopeV3Schema3 = unsignedShareEnvelopeV3BaseSchema3.extend({ signature: signatureSchema3 }).strict().superRefine(validateV3Invariants3);
28996
29037
  var ENVELOPE_AAD_LABEL4 = "tinycloud-share-envelope-v1";
29038
+ var SEALED_BLOB_VERSION2 = 1;
28997
29039
  var AAD4 = utf8Bytes4(ENVELOPE_AAD_LABEL4);
29040
+ var KEY_LENGTH2 = 32;
29041
+ var NONCE_LENGTH2 = 12;
29042
+ var TAG_LENGTH2 = 16;
29043
+ var HEADER_LENGTH2 = 1;
29044
+ function assertKey3(key32) {
29045
+ if (key32.length !== KEY_LENGTH2) {
29046
+ throw new TypeError(`key must be ${KEY_LENGTH2} bytes, got ${key32.length}`);
29047
+ }
29048
+ }
29049
+ async function importAesKey2(key32, usage3) {
29050
+ assertKey3(key32);
29051
+ return globalThis.crypto.subtle.importKey(
29052
+ "raw",
29053
+ key32,
29054
+ "AES-GCM",
29055
+ false,
29056
+ [usage3]
29057
+ );
29058
+ }
29059
+ async function open8(blob, key32) {
29060
+ if (blob.length < HEADER_LENGTH2 + NONCE_LENGTH2 + TAG_LENGTH2) {
29061
+ throw new TypeError(`sealed blob too short: ${blob.length} bytes`);
29062
+ }
29063
+ if (blob[0] !== SEALED_BLOB_VERSION2) {
29064
+ throw new TypeError(`unknown sealed blob version: ${blob[0]}`);
29065
+ }
29066
+ const nonce = blob.subarray(HEADER_LENGTH2, HEADER_LENGTH2 + NONCE_LENGTH2);
29067
+ const ciphertext = blob.subarray(HEADER_LENGTH2 + NONCE_LENGTH2);
29068
+ return decryptEnvelope2(nonce, ciphertext, key32);
29069
+ }
29070
+ async function decryptEnvelope2(nonce, ciphertext, key32) {
29071
+ if (nonce.length !== NONCE_LENGTH2) {
29072
+ throw new TypeError(`nonce must be ${NONCE_LENGTH2} bytes, got ${nonce.length}`);
29073
+ }
29074
+ const key = await importAesKey2(key32, "decrypt");
29075
+ return new Uint8Array(
29076
+ await globalThis.crypto.subtle.decrypt(
29077
+ { name: "AES-GCM", iv: nonce, additionalData: AAD4 },
29078
+ key,
29079
+ ciphertext
29080
+ )
29081
+ );
29082
+ }
28998
29083
  function isPlainRecord2(value) {
28999
29084
  const proto = Object.getPrototypeOf(value);
29000
29085
  return proto === Object.prototype || proto === null;
@@ -29109,6 +29194,7 @@ function equal(left, right) {
29109
29194
  return left.length === right.length && left.every((byte, index) => byte === right[index]);
29110
29195
  }
29111
29196
  var ED25519_VERIFY_OPTS2 = { zip215: false };
29197
+ var ENVELOPE_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
29112
29198
  var ENVELOPE_V3_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
29113
29199
  var POLICY_V1_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
29114
29200
  var POLICY_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
@@ -29116,6 +29202,14 @@ var CONTENT_SOURCE_V1_DOMAIN = "xyz.tinycloud.policy/ContentSource/v1\0";
29116
29202
  var POLICY_CAPABILITY_V1_DOMAIN = "xyz.tinycloud.policy/PolicyCapability/v1\0";
29117
29203
  var NATIVE_PROJECTION_V1_DOMAIN = "xyz.tinycloud.policy/NativeProjection/v1\0";
29118
29204
  var ATTESTED_ENFORCER_V2_DOMAIN = "xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0";
29205
+ function signingBytesV2(unsigned) {
29206
+ const domain = utf8Bytes4(ENVELOPE_V2_SIGNATURE_DOMAIN);
29207
+ const body = utf8Bytes4(canonicalize2(unsigned));
29208
+ const bytes3 = new Uint8Array(domain.length + body.length);
29209
+ bytes3.set(domain);
29210
+ bytes3.set(body, domain.length);
29211
+ return bytes3;
29212
+ }
29119
29213
  function signingBytesV3(unsigned) {
29120
29214
  const domain = utf8Bytes4(ENVELOPE_V3_SIGNATURE_DOMAIN);
29121
29215
  const body = utf8Bytes4(canonicalize2(unsigned));
@@ -29124,6 +29218,17 @@ function signingBytesV3(unsigned) {
29124
29218
  bytes3.set(body, domain.length);
29125
29219
  return bytes3;
29126
29220
  }
29221
+ function verifyEnvelopeV2SignatureOnly(envelope) {
29222
+ const parsed = shareEnvelopeV2Schema3.parse(envelope);
29223
+ const { signature, ...unsigned } = parsed;
29224
+ return ed25519.verify(fromBase64Url4(signature.value), signingBytesV2(unsigned), ed25519PublicKeyFromDidKey2(signature.signerDid), ED25519_VERIFY_OPTS2);
29225
+ }
29226
+ async function verifyEnvelopeV2(envelope, options) {
29227
+ const parsed = shareEnvelopeV2Schema3.parse(envelope);
29228
+ if (parsed.signature.signerDid !== options.expectedSignerDid || !verifyEnvelopeV2SignatureOnly(parsed)) return false;
29229
+ if (parsed.authorizationTarget.kind === "policy") return await computeCid2(fromBase64Url4(parsed.authorizationTarget.policyBytes)) === parsed.authorizationTarget.policyCid;
29230
+ return true;
29231
+ }
29127
29232
  function verifyEnvelopeV3SignatureOnly(envelope) {
29128
29233
  const parsed = shareEnvelopeV3Schema3.parse(envelope);
29129
29234
  const { signature, ...unsigned } = parsed;
@@ -29210,6 +29315,52 @@ function base32Lower(bytes3) {
29210
29315
  if (bits > 0) output2 += alphabet[buffer << 5 - bits & 31];
29211
29316
  return output2;
29212
29317
  }
29318
+ var KEY_LENGTH22 = 32;
29319
+ function assertCanonicalCid(cidString) {
29320
+ const cid2 = CID4.parse(cidString);
29321
+ if (cid2.version !== 1 || cid2.code !== code4 || cid2.multihash.code !== 18 || // sha2-256 (0x12) only, at the link layer too
29322
+ cid2.toString() !== cidString) {
29323
+ throw new TypeError(`not a canonical CIDv1 raw sha2-256 base32 CID: ${cidString}`);
29324
+ }
29325
+ }
29326
+ function parseShareUrl(url, options = {}) {
29327
+ const parsed = new URL(url);
29328
+ if (parsed.protocol !== "https:") {
29329
+ throw new TypeError(`share URL must be https, got ${parsed.protocol}`);
29330
+ }
29331
+ if (parsed.username !== "" || parsed.password !== "") {
29332
+ throw new TypeError("share URL must not carry userinfo");
29333
+ }
29334
+ if (parsed.search !== "") {
29335
+ throw new TypeError("share URL must not have a query string");
29336
+ }
29337
+ if (options.expectedOrigin !== void 0) {
29338
+ if (!isCanonicalHttpsOrigin3(options.expectedOrigin)) {
29339
+ throw new TypeError(
29340
+ `expectedOrigin must be a canonical https origin, got ${options.expectedOrigin}`
29341
+ );
29342
+ }
29343
+ if (parsed.origin !== options.expectedOrigin) {
29344
+ throw new TypeError(
29345
+ `share URL origin ${parsed.origin} does not match expected ${options.expectedOrigin}`
29346
+ );
29347
+ }
29348
+ }
29349
+ const match = /^\/s\/([a-z2-7]+)$/.exec(parsed.pathname);
29350
+ if (!match || match[1] === void 0) {
29351
+ throw new TypeError(`not a share URL path: ${parsed.pathname}`);
29352
+ }
29353
+ const ciphertextCid = match[1];
29354
+ assertCanonicalCid(ciphertextCid);
29355
+ if (!parsed.hash.startsWith("#k=")) {
29356
+ throw new TypeError("share URL is missing the #k= key fragment");
29357
+ }
29358
+ const key32 = fromBase64Url4(parsed.hash.slice("#k=".length));
29359
+ if (key32.length !== KEY_LENGTH22) {
29360
+ throw new TypeError(`fragment key must be ${KEY_LENGTH22} bytes, got ${key32.length}`);
29361
+ }
29362
+ return { ciphertextCid, key32 };
29363
+ }
29213
29364
  var ARTIFACT_ROOT2 = "artifacts";
29214
29365
  function parseArtifactVersionPath2(path) {
29215
29366
  const match = /^artifacts\/([a-z0-9][a-z0-9_-]{2,62})\/versions\/([1-9][0-9]{0,14})\/([^/]+)$/.exec(path);
@@ -29286,6 +29437,7 @@ function verifyArtifactShare(envelope, options) {
29286
29437
  }
29287
29438
  var SHARE_RESULT_VERSION = 1;
29288
29439
  var DEFAULT_MAX_SEALED_BLOB_BYTES = 100 * 1024 * 1024 + 29;
29440
+ var DEFAULT_MAX_CONTENT_BLOB_BYTES = DEFAULT_MAX_SEALED_BLOB_BYTES;
29289
29441
  var ShareReceiveError = class extends Error {
29290
29442
  code;
29291
29443
  details;
@@ -29300,7 +29452,7 @@ var ShareReceiveError = class extends Error {
29300
29452
  }
29301
29453
  };
29302
29454
  function metadataFor(envelope, origin) {
29303
- const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : "emailDomain";
29455
+ const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : envelope.recipientMatcher.kind === "emailDomain" ? "emailDomain" : "bearer";
29304
29456
  return {
29305
29457
  protocol: "tinycloud-share",
29306
29458
  version: 1,
@@ -29315,7 +29467,7 @@ function metadataFor(envelope, origin) {
29315
29467
  ...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
29316
29468
  ...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
29317
29469
  },
29318
- ...envelope.artifactPin === void 0 ? {} : { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } }
29470
+ ...envelope.version === 3 && envelope.artifactPin !== void 0 ? { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } } : {}
29319
29471
  };
29320
29472
  }
29321
29473
  function receiveErrorFor(error) {
@@ -29413,14 +29565,165 @@ async function resolvePolicyShare(link4, options) {
29413
29565
  options.onResolvedAddressedEnvelope?.(envelope, cid2);
29414
29566
  return { envelope, origin: url.origin, cid: cid2 };
29415
29567
  }
29568
+ async function resolveLegacyV2(link4, options) {
29569
+ let parsed;
29570
+ try {
29571
+ parsed = parseShareUrl(link4, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
29572
+ } catch {
29573
+ throw new ShareReceiveError("invalid-link", "legacy share link format is invalid");
29574
+ }
29575
+ try {
29576
+ options.signal?.throwIfAborted();
29577
+ const origin = new URL(link4).origin;
29578
+ const limit = options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES;
29579
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope limit is invalid");
29580
+ let sealed;
29581
+ if (options.fetchBlob !== void 0) {
29582
+ try {
29583
+ sealed = await options.fetchBlob({ origin, cid: parsed.ciphertextCid, ...options.signal === void 0 ? {} : { signal: options.signal } });
29584
+ } catch {
29585
+ throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
29586
+ }
29587
+ } else {
29588
+ if (options.registryBaseUrl === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry is required");
29589
+ const response = await (options.fetch ?? globalThis.fetch)(`${options.registryBaseUrl.replace(/\/+$/, "")}/ipfs/${parsed.ciphertextCid}?format=raw`, {
29590
+ headers: { accept: "application/vnd.ipld.raw" },
29591
+ redirect: "error",
29592
+ ...options.signal === void 0 ? {} : { signal: options.signal }
29593
+ });
29594
+ if (!response.ok) throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
29595
+ if (Number(response.headers.get("content-length")) > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
29596
+ const reader = response.body?.getReader();
29597
+ if (reader === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry returned no body");
29598
+ const chunks = [];
29599
+ let length32 = 0;
29600
+ for (; ; ) {
29601
+ const part = await reader.read();
29602
+ if (part.done) break;
29603
+ length32 += part.value.length;
29604
+ if (length32 > limit) {
29605
+ await reader.cancel();
29606
+ throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
29607
+ }
29608
+ chunks.push(part.value);
29609
+ }
29610
+ sealed = new Uint8Array(length32);
29611
+ let offset = 0;
29612
+ for (const chunk of chunks) {
29613
+ sealed.set(chunk, offset);
29614
+ offset += chunk.length;
29615
+ }
29616
+ }
29617
+ options.signal?.throwIfAborted();
29618
+ if (!(sealed instanceof Uint8Array) || sealed.length > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
29619
+ if (await computeCid2(sealed) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "legacy envelope CID does not match");
29620
+ let plaintext;
29621
+ try {
29622
+ plaintext = await open8(sealed, parsed.key32);
29623
+ } catch {
29624
+ throw new ShareReceiveError("decrypt-failed", "legacy envelope could not be opened");
29625
+ }
29626
+ let envelope;
29627
+ try {
29628
+ const encoded = new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
29629
+ const value = JSON.parse(encoded);
29630
+ if (canonicalize2(value) !== encoded) throw new Error("non-canonical envelope");
29631
+ envelope = shareEnvelopeV2Schema3.parse(value);
29632
+ } catch {
29633
+ throw new ShareReceiveError("envelope-invalid", "legacy envelope is invalid");
29634
+ }
29635
+ return { envelope, origin, cid: parsed.ciphertextCid };
29636
+ } finally {
29637
+ parsed.key32.fill(0);
29638
+ }
29639
+ }
29640
+ function evidenceMatchesV2(envelope, evidence) {
29641
+ const actions = new Set(evidence.actions.map((action) => action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : action === "edit" ? "tinycloud.kv/put" : action));
29642
+ 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}/`);
29643
+ return envelope.authorizationTarget.kind === "policy" && evidence.policyCid === envelope.authorizationTarget.policyCid && evidence.signerDid === envelope.signature.signerDid && evidence.registrationCid.length > 0 && evidence.shareId === envelope.shareId && canonicalize2(evidence.recipientMatcher) === canonicalize2(envelope.recipientMatcher) && canonicalize2(evidence.target) === canonicalize2(envelope.target) && resourceCovers && envelope.actions.every((action) => actions.has(action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : "tinycloud.kv/put")) && canonicalize2(evidence.contentSource) === canonicalize2(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);
29644
+ }
29645
+ async function verifyLegacyV2(envelope, options) {
29646
+ try {
29647
+ if (!await verifyEnvelopeV2(envelope, { expectedSignerDid: envelope.signature.signerDid })) throw new Error("signature");
29648
+ } catch {
29649
+ throw new ShareReceiveError("signature-invalid", "legacy envelope signature is invalid");
29650
+ }
29651
+ const expiry = Date.parse(envelope.expiry);
29652
+ if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "legacy share expiry is invalid");
29653
+ if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "legacy share has expired", { expiresAt: envelope.expiry });
29654
+ if (envelope.authorizationTarget.kind === "policy") {
29655
+ if (options.trustedPolicyAuthority === void 0) throw new ShareReceiveError("envelope-invalid", "a legacy policy authority is required");
29656
+ let evidence;
29657
+ try {
29658
+ evidence = await options.trustedPolicyAuthority.resolve({ policyCid: envelope.authorizationTarget.policyCid, envelope });
29659
+ } catch {
29660
+ }
29661
+ if (evidence === void 0 || !evidenceMatchesV2(envelope, evidence)) throw new ShareReceiveError("capability-invalid", "legacy policy evidence does not match");
29662
+ if (options.trustedSignerDid !== void 0 && options.trustedSignerDid !== evidence.signerDid) throw new ShareReceiveError("signature-invalid", "legacy signer is not trusted");
29663
+ } 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) {
29664
+ throw new ShareReceiveError("capability-invalid", "legacy recipient authority does not match");
29665
+ }
29666
+ }
29416
29667
  async function inspectShare(link4, options = {}) {
29668
+ if (isLegacyV2Link(link4)) {
29669
+ const legacy = await resolveLegacyV2(link4, options);
29670
+ await verifyLegacyV2(legacy.envelope, options);
29671
+ options.onResolvedAddressedEnvelope?.(legacy.envelope, legacy.cid);
29672
+ return { metadata: metadataFor(legacy.envelope, legacy.origin), link: { origin: legacy.origin, cid: legacy.cid, kind: "compact" } };
29673
+ }
29417
29674
  const resolved = await resolvePolicyShare(link4, options);
29418
29675
  return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "policy" } };
29419
29676
  }
29420
29677
  async function receiveShare(link4, options = {}) {
29678
+ if (isLegacyV2Link(link4)) return receiveLegacyV2(link4, options);
29421
29679
  const resolved = await resolvePolicyShare(link4, options);
29422
29680
  return { state: "authorization-required", method: resolved.envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim" };
29423
29681
  }
29682
+ function isLegacyV2Link(link4) {
29683
+ try {
29684
+ const url = new URL(link4);
29685
+ return /^\/s\/bafkrei[a-z2-7]{52}$/.test(url.pathname) && url.hash.startsWith("#k=");
29686
+ } catch {
29687
+ return false;
29688
+ }
29689
+ }
29690
+ async function receiveLegacyV2(link4, options) {
29691
+ const resolved = await resolveLegacyV2(link4, options);
29692
+ const { envelope } = resolved;
29693
+ await verifyLegacyV2(envelope, options);
29694
+ options.onResolvedAddressedEnvelope?.(envelope, resolved.cid);
29695
+ const method = envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim";
29696
+ if (options.authorization === void 0) return { state: "authorization-required", method };
29697
+ const result = options.authorizationResumeToken === void 0 ? await options.authorization.begin({ envelope, method }) : await options.authorization.resume({
29698
+ envelope,
29699
+ method,
29700
+ resumeToken: options.authorizationResumeToken,
29701
+ ...options.authorizationProof === void 0 ? {} : { proof: options.authorizationProof }
29702
+ });
29703
+ if (result.state === "authorization-required") return result;
29704
+ if (result.state === "denied") throw new ShareReceiveError("authorization-denied", "legacy share authorization was denied");
29705
+ if (result.value === null || typeof result.value !== "object" || !(result.value.bytes instanceof Uint8Array) || result.value.proof === void 0 || options.authorization.verifyResult === void 0) {
29706
+ throw new ShareReceiveError("content-integrity-failed", "legacy content lacks a verifiable proof");
29707
+ }
29708
+ const authorized = result.value;
29709
+ try {
29710
+ if (!await options.authorization.verifyResult({ envelope, value: authorized, proof: authorized.proof })) throw new Error("proof");
29711
+ } catch {
29712
+ throw new ShareReceiveError("content-integrity-failed", "legacy content proof is invalid");
29713
+ }
29714
+ const bytes3 = authorized.bytes.slice();
29715
+ if (bytes3.length > (options.maxContentBlobBytes ?? DEFAULT_MAX_CONTENT_BLOB_BYTES)) throw new ShareReceiveError("max-bytes-exceeded", "legacy content exceeds the size limit");
29716
+ const bodyDigest = toBase64Url2(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes3)));
29717
+ 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) {
29718
+ throw new ShareReceiveError("content-integrity-failed", "legacy content binding does not match");
29719
+ }
29720
+ let text;
29721
+ try {
29722
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes3);
29723
+ } catch {
29724
+ }
29725
+ return { metadata: metadataFor(envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "compact" }, bytes: bytes3, ...text === void 0 ? {} : { text } };
29726
+ }
29424
29727
  var SHARE_CONTENT_LIMIT = 100 * 1024 * 1024;
29425
29728
  var SHARE_PUBLISH_RESULT_VERSION = 1;
29426
29729
  var DEFAULT_SHARE_LIFETIME_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -29908,7 +30211,10 @@ async function publishTargetShare(input) {
29908
30211
  expiresAt,
29909
30212
  expiryWasExplicit: input.expiryWasExplicit ?? input.expiresAt !== void 0,
29910
30213
  origin: input.origin,
29911
- ...input.notify === void 0 ? {} : { notify: input.notify }
30214
+ ...input.notify === void 0 ? {} : { notify: input.notify },
30215
+ ...input.credentialDescriptor === void 0 ? {} : { credentialDescriptor: input.credentialDescriptor },
30216
+ ...input.issuerDid === void 0 ? {} : { issuerDid: input.issuerDid },
30217
+ ...input.issuerKid === void 0 ? {} : { issuerKid: input.issuerKid }
29912
30218
  });
29913
30219
  }
29914
30220
  var textEncoder = new TextEncoder();
@@ -30642,7 +30948,53 @@ function parseNativeShareUrl(value) {
30642
30948
  return token;
30643
30949
  }
30644
30950
 
30951
+ // src/share/errors.ts
30952
+ var SharePublishAuthorityError = class extends Error {
30953
+ constructor(failure, options) {
30954
+ super(failure.kind, options);
30955
+ this.failure = failure;
30956
+ this.name = "SharePublishAuthorityError";
30957
+ }
30958
+ };
30959
+ var ShareHistoryRetryError = class extends Error {
30960
+ constructor(profile) {
30961
+ super("sender history changed during the operation");
30962
+ this.profile = profile;
30963
+ this.name = "ShareHistoryRetryError";
30964
+ }
30965
+ code = "SHARE_HISTORY_RETRY";
30966
+ };
30967
+ var ShareHistoryUnreadableError = class extends Error {
30968
+ constructor() {
30969
+ super("share history is unavailable");
30970
+ this.name = "ShareHistoryUnreadableError";
30971
+ }
30972
+ };
30973
+ var ShareHistoryReadError = class extends Error {
30974
+ constructor() {
30975
+ super("share history could not be read");
30976
+ this.name = "ShareHistoryReadError";
30977
+ }
30978
+ };
30979
+ var HISTORY_WRITE_ERROR_CODES = { EACCES: true, EPERM: true, EROFS: true, ENOSPC: true, EDQUOT: true, EIO: true };
30980
+ function shareHistoryReason(error) {
30981
+ if (error instanceof ShareHistoryUnreadableError) return "HISTORY_UNREADABLE";
30982
+ if (error instanceof ShareHistoryReadError) return "HISTORY_READ_FAILED";
30983
+ const code5 = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
30984
+ return typeof code5 === "string" && HISTORY_WRITE_ERROR_CODES[code5] === true ? "HISTORY_WRITE_FAILED" : "HISTORY_UNAVAILABLE";
30985
+ }
30986
+
30645
30987
  // src/share/adapters.ts
30988
+ init_storage2();
30989
+ init_profiles();
30990
+ init_constants();
30991
+ init_errors();
30992
+ init_session_expired();
30993
+ import { chmod as chmod4, link as link2, open as open9, readFile as readFile8, stat as stat4, unlink as unlink4 } from "fs/promises";
30994
+ import { dirname as dirname5, join as join12 } from "path";
30995
+ import { createHash } from "crypto";
30996
+ import { writeJsonAtomic as writeJsonAtomic2 } from "@tinycloud/operations/state";
30997
+ import { withoutBearer as withoutBearer2 } from "@tinycloud/operations/delegation-binding";
30646
30998
  import {
30647
30999
  LocationRecordValidationError,
30648
31000
  LocationRegistryHttpError,
@@ -30855,7 +31207,7 @@ function createEncryptedProfileHistory(profileName2, sessionSigner) {
30855
31207
  await link2(found.file, target);
30856
31208
  await chmod4(target, PRIVATE_HISTORY_MODE);
30857
31209
  await unlink4(found.file);
30858
- const directory = await open8(dirname5(found.file), "r");
31210
+ const directory = await open9(dirname5(found.file), "r");
30859
31211
  try {
30860
31212
  await directory.sync();
30861
31213
  } finally {
@@ -31228,8 +31580,8 @@ async function callbackFlow(did, options = {}, env = {}) {
31228
31580
  }
31229
31581
  if (openBrowser) {
31230
31582
  try {
31231
- const open15 = (await import("open")).default;
31232
- await open15(authUrl);
31583
+ const open16 = (await import("open")).default;
31584
+ await open16(authUrl);
31233
31585
  } catch {
31234
31586
  settle({ error: new Error("Failed to open browser") });
31235
31587
  return;
@@ -32582,7 +32934,7 @@ init_errors();
32582
32934
  import { randomBytes as randomBytes6, timingSafeEqual } from "crypto";
32583
32935
  import { createServer as createServer2 } from "http";
32584
32936
  import { createInterface as createInterface2 } from "readline";
32585
- import open9 from "open";
32937
+ import open10 from "open";
32586
32938
  init_owner_did();
32587
32939
  var MAX_BODY_BYTES = 1024 * 1024;
32588
32940
  var RESULT_FIELDS = ["address", "permissions", "protocolVersion", "selectedActionKeys", "signature", "signedMessage"];
@@ -32787,7 +33139,7 @@ async function startPreparedOpenKeyHandoff(options) {
32787
33139
  console.error(`Open this URL in a browser to authenticate: ${url}`);
32788
33140
  if (!options.noPopup && !options.paste) {
32789
33141
  try {
32790
- await open9(url);
33142
+ await open10(url);
32791
33143
  } catch {
32792
33144
  console.error("Could not open browser. Use the printed URL or paste the signed code.");
32793
33145
  }
@@ -37473,7 +37825,7 @@ init_storage();
37473
37825
  init_errors();
37474
37826
  init_constants();
37475
37827
  import { randomUUID as randomUUID2 } from "crypto";
37476
- import { lstat as lstat8, open as open10, rename as rename3, rm as rm5 } from "fs/promises";
37828
+ import { lstat as lstat8, open as open11, rename as rename3, rm as rm5 } from "fs/promises";
37477
37829
  import { basename as basename2, dirname as dirname7, join as join15 } from "path";
37478
37830
  async function validatePrivateOutput(path, label = "output") {
37479
37831
  try {
@@ -37500,7 +37852,7 @@ async function writePrivateOutput(path, value, label = "output") {
37500
37852
  const temp = join15(parentPath, `.${basename2(path)}.${randomUUID2()}.tmp`);
37501
37853
  let created = false;
37502
37854
  try {
37503
- const handle = await open10(temp, "wx", PRIVATE_FILE_MODE);
37855
+ const handle = await open11(temp, "wx", PRIVATE_FILE_MODE);
37504
37856
  created = true;
37505
37857
  try {
37506
37858
  await handle.writeFile(value);
@@ -37515,7 +37867,7 @@ async function writePrivateOutput(path, value, label = "output") {
37515
37867
  throw new CLIError("ERROR", `Could not write ${label.toLowerCase()} "${path}"${code5}.`, ExitCode.ERROR);
37516
37868
  }
37517
37869
  try {
37518
- const parent = await open10(parentPath, "r");
37870
+ const parent = await open11(parentPath, "r");
37519
37871
  try {
37520
37872
  await parent.sync();
37521
37873
  } finally {
@@ -38166,7 +38518,7 @@ var sql = (actions) => actions.map((action) => `tinycloud.sql/${action}`);
38166
38518
  var ONBOARD_STEPS = [
38167
38519
  {
38168
38520
  space: "applications",
38169
- asks: "Read and change your apps' data for 30 days.",
38521
+ asks: "Read, change and delete your apps' data for 30 days.",
38170
38522
  permissions: [
38171
38523
  { service: "tinycloud.kv", space: "applications", path: "", actions: kv(["get", "list", "put", "del", "metadata"]) },
38172
38524
  { service: "tinycloud.sql", space: "applications", path: "", actions: sql(["read", "write"]) }
@@ -38188,7 +38540,7 @@ var ApprovalNeeded = class extends Error {
38188
38540
  }
38189
38541
  };
38190
38542
  function registerOnboardCommand(program) {
38191
- program.command("onboard").description(`Set up this computer to act for its owner: a profile, the owner's OpenKey sign-in (or new account), and a starting grant for 30 days (read and change the applications space; read which spaces and apps exist). Each run does the next approval: it prints the approval link and exits ${ExitCode.PENDING}; run it again with the code OpenKey shows. Exits 0 when onboarding is complete.`).option("--code <code>", "The code OpenKey showed after the owner approved the last link").action(async (options, cmd) => {
38543
+ program.command("onboard").description(`Set up this computer to act for its owner: a profile, the owner's OpenKey sign-in (or new account), and a starting grant for 30 days (read, change and delete in the applications space; read which spaces and apps exist). Each run does the next approval: it prints the approval link and exits ${ExitCode.PENDING}; run it again with the code OpenKey shows. Exits 0 when onboarding is complete.`).option("--code <code>", "The code OpenKey showed after the owner approved the last link").action(async (options, cmd) => {
38192
38544
  try {
38193
38545
  const globalOpts = cmd.optsWithGlobals();
38194
38546
  const ctx = await ProfileManager.resolveContext(globalOpts, { storedSession: "unused" });
@@ -38499,7 +38851,7 @@ init_profile_replicas();
38499
38851
  import { CLI_NAME as CLI_NAME31 } from "@tinycloud/operations/cli-name";
38500
38852
  import { randomBytes as randomBytes7 } from "crypto";
38501
38853
  import { constants as fsConstants5 } from "fs";
38502
- import { mkdir as mkdir5, open as open11, readdir as readdir6, rename as rename4, unlink as unlink5, lstat as lstat9 } from "fs/promises";
38854
+ import { mkdir as mkdir5, open as open12, readdir as readdir6, rename as rename4, unlink as unlink5, lstat as lstat9 } from "fs/promises";
38503
38855
  import { basename as basename3, dirname as dirname8, join as join16, resolve as resolve5 } from "path";
38504
38856
 
38505
38857
  // src/lib/replication-report.ts
@@ -38966,7 +39318,7 @@ function describeStatus(status) {
38966
39318
  async function writeFileAtomic(path, bytes) {
38967
39319
  const target = resolve5(path);
38968
39320
  const temp = join16(dirname8(target), `.${basename3(target)}.${randomBytes7(6).toString("hex")}.tmp`);
38969
- const handle = await open11(temp, fsConstants5.O_CREAT | fsConstants5.O_EXCL | fsConstants5.O_WRONLY, 384);
39321
+ const handle = await open12(temp, fsConstants5.O_CREAT | fsConstants5.O_EXCL | fsConstants5.O_WRONLY, 384);
38970
39322
  try {
38971
39323
  await handle.writeFile(bytes);
38972
39324
  await handle.sync();
@@ -39015,7 +39367,7 @@ async function withPinnedLocalDirectory(profile, parent, name, action) {
39015
39367
  });
39016
39368
  if (!before) return null;
39017
39369
  if (before.isSymbolicLink() || !before.isDirectory()) throw localPathChanged(profile);
39018
- const handle = await open11(path, fsConstants5.O_RDONLY | fsConstants5.O_DIRECTORY | fsConstants5.O_NOFOLLOW).catch((error) => {
39370
+ const handle = await open12(path, fsConstants5.O_RDONLY | fsConstants5.O_DIRECTORY | fsConstants5.O_NOFOLLOW).catch((error) => {
39019
39371
  if (error.code === "ELOOP" || error.code === "ENOTDIR" || error.code === "ENOENT") throw localPathChanged(profile);
39020
39372
  throw error;
39021
39373
  });
@@ -39044,7 +39396,7 @@ async function readReportFile(profile, parent, name) {
39044
39396
  });
39045
39397
  if (!before) return null;
39046
39398
  if (before.isSymbolicLink() || !before.isFile()) throw localPathChanged(profile);
39047
- const handle = await open11(path, fsConstants5.O_RDONLY | fsConstants5.O_NOFOLLOW).catch((error) => {
39399
+ const handle = await open12(path, fsConstants5.O_RDONLY | fsConstants5.O_NOFOLLOW).catch((error) => {
39048
39400
  if (error.code === "ELOOP" || error.code === "ENOTDIR" || error.code === "ENOENT") throw localPathChanged(profile);
39049
39401
  throw error;
39050
39402
  });
@@ -39422,12 +39774,12 @@ init_space();
39422
39774
  // src/lib/browser.ts
39423
39775
  init_formatter();
39424
39776
  init_theme();
39425
- import open12 from "open";
39777
+ import open13 from "open";
39426
39778
  var LAUNCH_CHECK_MS = 1e3;
39427
39779
  async function tryOpenBrowser(url) {
39428
39780
  let launcher;
39429
39781
  try {
39430
- launcher = await open12(url);
39782
+ launcher = await open13(url);
39431
39783
  } catch {
39432
39784
  return false;
39433
39785
  }
@@ -41239,6 +41591,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
41239
41591
 
41240
41592
  // src/commands/share.ts
41241
41593
  import { Option } from "commander";
41594
+ import { readFile as readFile16 } from "fs/promises";
41242
41595
  import { CLI_NAME as CLI_NAME34 } from "@tinycloud/operations/cli-name";
41243
41596
  import { ProfileLockTimeoutError as ProfileLockTimeoutError3 } from "@tinycloud/operations/state";
41244
41597
  init_errors();
@@ -41334,7 +41687,7 @@ function receiveJson(result, path) {
41334
41687
 
41335
41688
  // src/share/io.ts
41336
41689
  import { constants } from "fs";
41337
- import { lstat as lstat10, mkdir as mkdir6, mkdtemp, open as open13, readFile as readFile15, realpath as realpath2, stat as stat6, link as link3, rename as rename5, rm as rm6, unlink as unlink6 } from "fs/promises";
41690
+ import { lstat as lstat10, mkdir as mkdir6, mkdtemp, open as open14, readFile as readFile15, realpath as realpath2, stat as stat6, link as link3, rename as rename5, rm as rm6, unlink as unlink6 } from "fs/promises";
41338
41691
  import { randomBytes as randomBytes8 } from "crypto";
41339
41692
  import { basename as basename4, join as join18, resolve as resolve6, sep as sep3 } from "path";
41340
41693
  init_errors();
@@ -41410,7 +41763,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
41410
41763
  const outputDirectory = resolve6(directory);
41411
41764
  await assertDirectory(outputDirectory);
41412
41765
  const safeName = safeFilename(filename);
41413
- const directoryHandle = await open13(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
41766
+ const directoryHandle = await open14(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
41414
41767
  const stableDirectory = await realpath2(outputDirectory);
41415
41768
  const outputPath = join18(stableDirectory, safeName);
41416
41769
  const directoryIdentity = await directoryHandle.stat();
@@ -41434,7 +41787,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
41434
41787
  if (error.code !== "ENOENT") throw error;
41435
41788
  }
41436
41789
  temporaryPath = stagingPath;
41437
- handle = await open13(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
41790
+ handle = await open14(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
41438
41791
  await handle.writeFile(bytes);
41439
41792
  await handle.close();
41440
41793
  handle = void 0;
@@ -41467,7 +41820,7 @@ init_profiles();
41467
41820
 
41468
41821
  // src/share/html-folder.ts
41469
41822
  import { constants as constants2, existsSync as existsSync2 } from "fs";
41470
- import { lstat as lstat11, open as open14, readdir as readdir7, realpath as realpath3, stat as stat7 } from "fs/promises";
41823
+ import { lstat as lstat11, open as open15, readdir as readdir7, realpath as realpath3, stat as stat7 } from "fs/promises";
41471
41824
  import { basename as basename5, dirname as dirname9, isAbsolute, join as join19, relative as relativePath, resolve as resolve7, sep as sep4 } from "path";
41472
41825
 
41473
41826
  // ../share-sdk/dist/html-bundle.js
@@ -41822,9 +42175,9 @@ function scanMarkup(html, options) {
41822
42175
  if (comments > MAX_ARTIFACT_COMMENTS) fail("limit", "artifact page has too many comments");
41823
42176
  };
41824
42177
  while (index < length5) {
41825
- const open15 = html.indexOf("<", index);
41826
- if (open15 === -1) return;
41827
- index = open15 + 1;
42178
+ const open16 = html.indexOf("<", index);
42179
+ if (open16 === -1) return;
42180
+ index = open16 + 1;
41828
42181
  const next = html[index] ?? "";
41829
42182
  if (html.startsWith("!--", index)) {
41830
42183
  countComment();
@@ -42425,7 +42778,7 @@ async function readRegularFile(root, path, relative, remaining, limit, hooks = {
42425
42778
  await hooks.beforeOpen?.();
42426
42779
  before = await lstat11(path);
42427
42780
  await hooks.afterLstat?.();
42428
- handle = await open14(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0));
42781
+ handle = await open15(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0));
42429
42782
  } catch (error) {
42430
42783
  if (error.code === "ELOOP") return problem("symbolic link");
42431
42784
  if (isRaceError(error)) return problem(CHANGED);
@@ -42484,7 +42837,7 @@ async function listFolderDirectory(root, absolute, relative, hooks = {}) {
42484
42837
  if (before.isSymbolicLink()) return problem("symbolic link");
42485
42838
  if (!before.isDirectory()) return problem(CHANGED);
42486
42839
  await hooks.afterLstat?.();
42487
- const handle = await open14(absolute, constants2.O_RDONLY | (constants2.O_DIRECTORY ?? 0) | (constants2.O_NOFOLLOW ?? 0));
42840
+ const handle = await open15(absolute, constants2.O_RDONLY | (constants2.O_DIRECTORY ?? 0) | (constants2.O_NOFOLLOW ?? 0));
42488
42841
  try {
42489
42842
  await hooks.afterOpen?.();
42490
42843
  const info = await handle.stat();
@@ -42908,7 +43261,7 @@ function assertAggregateInputLimit(inputs, maxBytes) {
42908
43261
  }
42909
43262
  function registerShareCommand(program) {
42910
43263
  const share = program.command("share").description("Publish and consume TinyCloud Share links");
42911
- share.command("publish <files...>").description("Publish one or more bounded files as a Share, or a folder with an index.html as one self-contained page (anyone links only)").option("--name <filename>", "Filename for stdin input, or the published .html name of a folder").option("--to <target>", "Share target", "anyone").option("--notify", "Email an exact-email recipient, or a domain mailbox on node 1.17.3+").option("--notify-to <address>", "Mailbox to invite for --to domain:<name> --notify (node 1.17.3+)").option("--expires <duration>", "Share lifetime").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--artifact <id>", 'Publish as the next write-once version of an artifact ("new" starts one); the share is pinned to that version').addOption(new Option("--ongoing").hideHelp()).option("--note <text>", "Note stored with the artifact version").option("--json", "Print versioned redacted JSON").option("--full-link", "Print the full self-contained link instead of a short link (addressed shares)").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN2).action(async (files, options, command) => {
43264
+ share.command("publish <files...>").description("Publish one or more bounded files as a Share, or a folder with an index.html as one self-contained page (anyone links only)").option("--name <filename>", "Filename for stdin input, or the published .html name of a folder").option("--to <target>", "Share target", "anyone").option("--credential-descriptor <path>", "Email credential flow descriptor JSON").option("--issuer-did <did>", "Trusted email credential issuer DID").option("--issuer-kid <kid>", "Trusted email credential issuer key ID").option("--notify", "Email an exact-email recipient, or a domain mailbox on node 1.17.3+").option("--notify-to <address>", "Mailbox to invite for --to domain:<name> --notify (node 1.17.3+)").option("--expires <duration>", "Share lifetime").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--artifact <id>", 'Publish as the next write-once version of an artifact ("new" starts one); the share is pinned to that version').addOption(new Option("--ongoing").hideHelp()).option("--note <text>", "Note stored with the artifact version").option("--json", "Print versioned redacted JSON").option("--full-link", "Print the full self-contained link instead of a short link (addressed shares)").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN2).action(async (files, options, command) => {
42912
43265
  try {
42913
43266
  const json = jsonOutput(options, command);
42914
43267
  if (options.ongoing === true) throw new CLIError("UNSUPPORTED_TARGET", ONGOING_UNAVAILABLE, 2);
@@ -42916,6 +43269,15 @@ function registerShareCommand(program) {
42916
43269
  if (files.length === 0 || files.includes("-") && files.length > 1) throw new CLIError("INVALID_ARGUMENT", "stdin must be the only publish input", 2);
42917
43270
  const name = files.length === 1 ? options.name : void 0;
42918
43271
  const target = parseShareTarget(options.to);
43272
+ const actions = requestedActions(options.action);
43273
+ if (options.notify === true && !actions.includes("read")) throw new CLIError("INVALID_ARGUMENT", "--notify requires the read action; nothing was shared", 2);
43274
+ if (target.kind === "email" && (!options.credentialDescriptor || !options.issuerDid || !options.issuerKid)) {
43275
+ throw new CLIError("INVALID_ARGUMENT", "email publication requires --credential-descriptor, --issuer-did, and --issuer-kid", 2);
43276
+ }
43277
+ if (target.kind !== "email" && (options.credentialDescriptor || options.issuerDid || options.issuerKid)) {
43278
+ throw new CLIError("INVALID_ARGUMENT", "credential options require an exact email target", 2);
43279
+ }
43280
+ const credentialDescriptor = target.kind === "email" ? JSON.parse(await readFile16(options.credentialDescriptor, "utf8")) : void 0;
42919
43281
  const folder = files.length === 1 && target.kind === "bearer" && options.prefix !== true && await isDirectoryInput(files[0]);
42920
43282
  let inputs;
42921
43283
  if (folder) {
@@ -42932,13 +43294,11 @@ function registerShareCommand(program) {
42932
43294
  inputs = await Promise.all(files.map((file) => readShareInput(file, name, maxBytes)));
42933
43295
  }
42934
43296
  assertAggregateInputLimit(inputs, maxBytes);
42935
- const actions = requestedActions(options.action);
42936
43297
  if (options.prefix && target.kind === "bearer") throw new CLIError("INVALID_ARGUMENT", "--prefix requires an addressed target", 2);
42937
43298
  if (target.kind === "bearer" && actions.includes("edit")) throw new CLIError("INVALID_ARGUMENT", `--action edit is not available with --to ${options.to}: a link can only be read; address a recipient to allow edits`, 2);
42938
43299
  if (inputs.length > 1 && target.kind === "bearer") throw new CLIError("UNSUPPORTED_LINK", "multiple files require an addressed target", 2);
42939
43300
  const artifact = artifactRequest(options, actions, inputs.length);
42940
43301
  if (inputs.length > 1 && !options.prefix) throw new CLIError("INVALID_ARGUMENT", "multiple files require --prefix", 2);
42941
- if (options.notify === true && !actions.includes("read")) throw new CLIError("INVALID_ARGUMENT", "--notify requires the read action; nothing was shared", 2);
42942
43302
  if (options.notifyTo !== void 0 && (options.notify !== true || target.kind !== "emailDomain")) {
42943
43303
  throw new CLIError("INVALID_ARGUMENT", "--notify-to requires --to domain:<name> --notify", 2);
42944
43304
  }
@@ -42968,6 +43328,7 @@ function registerShareCommand(program) {
42968
43328
  origin: options.viewerOrigin,
42969
43329
  ...maxBytes === void 0 ? {} : { maxBytes },
42970
43330
  notify: options.notify === true,
43331
+ ...credentialDescriptor === void 0 ? {} : { credentialDescriptor, issuerDid: options.issuerDid, issuerKid: options.issuerKid },
42971
43332
  // The artifact request rides on the adapter call rather than through `publishTargetShare`, whose code is part of
42972
43333
  // the browser bundles (the web-sdk size ceiling counts every byte it carries).
42973
43334
  targetAdapter: artifact === void 0 || shareServices.targetAdapter === void 0 ? shareServices.targetAdapter : { publish: (request) => shareServices.targetAdapter.publish({ ...request, artifact }) }
@@ -44103,7 +44464,7 @@ function registerUpgradeCommand(program) {
44103
44464
  }
44104
44465
 
44105
44466
  // src/commands/vault.ts
44106
- import { readFile as readFile16 } from "fs/promises";
44467
+ import { readFile as readFile17 } from "fs/promises";
44107
44468
  init_profiles();
44108
44469
  init_formatter();
44109
44470
  init_errors();
@@ -44165,7 +44526,7 @@ function registerVaultCommand(program) {
44165
44526
  throw new CLIError("USAGE_ERROR", "Provide only one of: value argument, --file, or --stdin", ExitCode.USAGE_ERROR);
44166
44527
  }
44167
44528
  if (options.file) {
44168
- putValue = new Uint8Array(await readFile16(options.file));
44529
+ putValue = new Uint8Array(await readFile17(options.file));
44169
44530
  } else if (options.stdin) {
44170
44531
  putValue = new Uint8Array(await readStdin5());
44171
44532
  } else {
@@ -44280,7 +44641,7 @@ function registerVaultCommand(program) {
44280
44641
  }
44281
44642
 
44282
44643
  // src/commands/vars.ts
44283
- import { readFile as readFile17 } from "fs/promises";
44644
+ import { readFile as readFile18 } from "fs/promises";
44284
44645
  init_profiles();
44285
44646
  init_formatter();
44286
44647
  init_errors();
@@ -44385,7 +44746,7 @@ function registerVarsCommand(program) {
44385
44746
  throw new CLIError("USAGE_ERROR", "Provide only one of: value argument, --file, or --stdin", ExitCode.USAGE_ERROR);
44386
44747
  }
44387
44748
  if (options.file) {
44388
- varValue = await readFile17(options.file, "utf-8");
44749
+ varValue = await readFile18(options.file, "utf-8");
44389
44750
  } else if (options.stdin) {
44390
44751
  varValue = (await readStdin6()).toString("utf-8");
44391
44752
  } else {