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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6058,7 +6058,7 @@ async function runCas(key, policy, attempt) {
6058
6058
  }
6059
6059
 
6060
6060
  // src/share-index/delivery.ts
6061
- import { parseLink as parseLink2 } from "@tinycloud/share-envelope/links";
6061
+ import { buildFullLink, checkLinkRecord, linkSignatureV1Schema, openLinkRecord, parseLink as parseLink2, validateLinkHeader } from "@tinycloud/share-envelope/links";
6062
6062
  var STORED_DELIVERY_MATERIAL_KEYS = ["shareCid", "sealedEnvelope", "envelopeKey"];
6063
6063
  var DeliveryMaterialError = class extends Error {
6064
6064
  constructor(message) {
@@ -6066,6 +6066,16 @@ var DeliveryMaterialError = class extends Error {
6066
6066
  this.name = "DeliveryMaterialError";
6067
6067
  }
6068
6068
  };
6069
+ function isObject(value) {
6070
+ return value !== null && typeof value === "object" && !Array.isArray(value);
6071
+ }
6072
+ function checkStoredFullShareLink(value) {
6073
+ if (!isObject(value) || Object.keys(value).length !== 3 || !["origin", "header", "signature"].every((key) => Object.hasOwn(value, key)) || typeof value.origin !== "string" || !isCanonicalHttpsOrigin(value.origin)) throw new DeliveryMaterialError("full link metadata is invalid");
6074
+ const header = validateLinkHeader(value.header, { mode: "signed" });
6075
+ const signature = linkSignatureV1Schema.parse(value.signature);
6076
+ if (header.type !== "share") throw new DeliveryMaterialError("full link is not a share");
6077
+ return { origin: value.origin, header, signature };
6078
+ }
6069
6079
  function sealedParts(material) {
6070
6080
  let sealed;
6071
6081
  let key;
@@ -6082,18 +6092,24 @@ async function openDeliveryEnvelope(material) {
6082
6092
  const { sealed, key } = sealedParts(material);
6083
6093
  if (await computeCid(sealed) !== material.shareCid) throw new DeliveryMaterialError("sealed envelope does not match shareCid");
6084
6094
  let text;
6085
- try {
6086
- text = new TextDecoder("utf-8", { fatal: true }).decode(await open(sealed, key));
6087
- } catch {
6088
- throw new DeliveryMaterialError("sealed envelope does not open under its key");
6089
- }
6090
6095
  let value;
6091
6096
  try {
6092
- value = JSON.parse(text);
6093
- } catch {
6094
- throw new DeliveryMaterialError("sealed envelope is not JSON");
6097
+ if (material.fullLink === void 0) {
6098
+ text = new TextDecoder("utf-8", { fatal: true }).decode(await open(sealed, key));
6099
+ value = JSON.parse(text);
6100
+ } else {
6101
+ const metadata = checkStoredFullShareLink(material.fullLink);
6102
+ if (metadata.header.bodyCid !== material.shareCid) throw new DeliveryMaterialError("full link names another sealed body");
6103
+ const opened = await openLinkRecord({ header: metadata.header, signature: metadata.signature, body: material.sealedEnvelope }, key, { now: Date.parse(metadata.header.issuedAt) });
6104
+ text = new TextDecoder("utf-8", { fatal: true }).decode(opened.plaintext);
6105
+ value = opened.value;
6106
+ if (typeof opened.value.expiry !== "string" || Date.parse(opened.value.expiry) !== Date.parse(metadata.header.expiresAt)) throw new DeliveryMaterialError("full link expiry differs from the sealed envelope");
6107
+ }
6108
+ } catch (error) {
6109
+ if (error instanceof DeliveryMaterialError) throw error;
6110
+ throw new DeliveryMaterialError(material.fullLink === void 0 ? "sealed envelope does not open under its key" : "original full link is invalid");
6095
6111
  }
6096
- if (value === null || typeof value !== "object" || Array.isArray(value) || canonicalize(value) !== text) throw new DeliveryMaterialError("sealed envelope is not canonical JSON");
6112
+ if (!isObject(value) || canonicalize(value) !== text) throw new DeliveryMaterialError("sealed envelope is not canonical JSON");
6097
6113
  return value;
6098
6114
  }
6099
6115
  function assertLinkBoundToMaterial(link, material) {
@@ -6112,21 +6128,58 @@ async function compactShareRecord(record2) {
6112
6128
  const material = record2.deliveryMaterial;
6113
6129
  if (material === void 0) return record2;
6114
6130
  if (typeof material.shareCid !== "string" || typeof material.sealedEnvelope !== "string" || typeof material.envelopeKey !== "string") throw new DeliveryMaterialError("delivery material is incomplete");
6115
- const stored = { shareCid: material.shareCid, sealedEnvelope: material.sealedEnvelope, envelopeKey: material.envelopeKey };
6116
- const opened = await openDeliveryEnvelope(stored);
6117
- if (material.envelope !== void 0 && canonicalize(opened) !== canonicalize(material.envelope)) throw new DeliveryMaterialError("envelope is not the sealed envelope");
6131
+ let fullLink = "fullLink" in material ? checkStoredFullShareLink(material.fullLink) : void 0;
6118
6132
  const { link, deliveryMaterial: _material, ...rest } = record2;
6119
- const keptLink = link !== void 0 && assertLinkBoundToMaterial(link, stored) === "short" ? link : void 0;
6133
+ let keptLink;
6134
+ if (link !== void 0) {
6135
+ if (assertLinkBoundToMaterial(link, material) === "short") keptLink = link;
6136
+ else {
6137
+ const parsed = parseLink2(link, { trustedOrigins: [new URL(link).origin] });
6138
+ if (parsed.form !== "full" || !isObject(parsed.record)) throw new DeliveryMaterialError("full link record is invalid");
6139
+ const header = validateLinkHeader(parsed.record.header, { mode: "signed" });
6140
+ const checked = await checkLinkRecord(parsed.record, Date.parse(header.issuedAt));
6141
+ if (checked.signature === void 0) throw new DeliveryMaterialError("full link signature is missing");
6142
+ const original = checkStoredFullShareLink({ origin: parsed.origin, header: checked.header, signature: checked.signature });
6143
+ if (fullLink !== void 0 && canonicalize(fullLink) !== canonicalize(original)) throw new DeliveryMaterialError("full link metadata differs from the original link");
6144
+ fullLink = original;
6145
+ }
6146
+ }
6147
+ const stored = { shareCid: material.shareCid, sealedEnvelope: material.sealedEnvelope, envelopeKey: material.envelopeKey, ...fullLink === void 0 ? {} : { fullLink } };
6148
+ const opened = await openDeliveryEnvelope(stored);
6149
+ if ("envelope" in material && material.envelope !== void 0 && canonicalize(opened) !== canonicalize(material.envelope)) throw new DeliveryMaterialError("envelope is not the sealed envelope");
6150
+ if (fullLink !== void 0 && Date.parse(fullLink.header.expiresAt) !== Date.parse(record2.expiresAt)) throw new DeliveryMaterialError("full link expiry differs from the record");
6120
6151
  return { ...rest, ...keptLink === void 0 ? {} : { link: keptLink }, deliveryMaterial: stored };
6121
6152
  }
6122
6153
  function equalBytes(left, right) {
6123
6154
  return left.length === right.length && left.every((byte, index) => byte === right[index]);
6124
6155
  }
6125
- async function expandShareRecord(record2) {
6156
+ function recordLink(record2, shareOrigins) {
6126
6157
  const material = record2.deliveryMaterial;
6127
- if (material === void 0) return record2;
6158
+ if (material?.fullLink === void 0) return record2.link;
6159
+ const metadata = checkStoredFullShareLink(material.fullLink);
6160
+ if (!shareOrigins.includes(metadata.origin)) throw new DeliveryMaterialError("full link origin is not trusted");
6161
+ if (Date.parse(metadata.header.expiresAt) !== Date.parse(record2.expiresAt)) throw new DeliveryMaterialError("full link expiry differs from the record");
6162
+ const full = buildFullLink({ origin: metadata.origin, key: sealedParts(material).key, record: { header: metadata.header, signature: metadata.signature, body: material.sealedEnvelope } });
6163
+ if (record2.link !== void 0) {
6164
+ parseLink2(record2.link, { trustedOrigins: shareOrigins });
6165
+ assertLinkBoundToMaterial(record2.link, material);
6166
+ }
6167
+ return record2.link ?? full;
6168
+ }
6169
+ async function restoreShareRecordLink(record2, shareOrigins) {
6170
+ if (record2.deliveryMaterial?.fullLink !== void 0) await openDeliveryEnvelope(record2.deliveryMaterial);
6171
+ return recordLink(record2, shareOrigins);
6172
+ }
6173
+ async function expandShareRecord(record2, options = {}) {
6174
+ const material = record2.deliveryMaterial;
6175
+ if (material === void 0) {
6176
+ const { deliveryMaterial: _material, ...withoutMaterial } = record2;
6177
+ return withoutMaterial;
6178
+ }
6128
6179
  const envelope = await openDeliveryEnvelope(material);
6129
- return { ...record2, deliveryMaterial: { ...material, envelope } };
6180
+ const origins = options.shareOrigins ?? (material.fullLink === void 0 ? [] : [checkStoredFullShareLink(material.fullLink).origin]);
6181
+ const link = recordLink(record2, origins);
6182
+ return { ...record2, ...link === void 0 ? {} : { link }, deliveryMaterial: { ...material, envelope } };
6130
6183
  }
6131
6184
 
6132
6185
  // src/share-index/record-store.ts
@@ -6298,6 +6351,14 @@ var AgentShareRecordStore = class {
6298
6351
  }
6299
6352
  };
6300
6353
 
6354
+ // src/artifact-core.ts
6355
+ var ARTIFACT_ROOT = "artifacts";
6356
+ function parseArtifactVersionPath(path) {
6357
+ const match = /^artifacts\/([a-z0-9][a-z0-9_-]{2,62})\/versions\/([1-9][0-9]{0,14})\/([^/]+)$/.exec(path);
6358
+ if (match === null) return void 0;
6359
+ return { artifactId: match[1], version: Number(match[2]), file: match[3] };
6360
+ }
6361
+
6301
6362
  // src/share-index/schema.ts
6302
6363
  var INDEX_SCHEMA_VERSION = 1;
6303
6364
  function emptyIndexDocument(input) {
@@ -6314,7 +6375,7 @@ function emptyIndexDocument(input) {
6314
6375
  rows: {}
6315
6376
  };
6316
6377
  }
6317
- function isObject(value) {
6378
+ function isObject2(value) {
6318
6379
  return value !== null && typeof value === "object" && !Array.isArray(value);
6319
6380
  }
6320
6381
  function isIsoTime(value) {
@@ -6323,25 +6384,25 @@ function isIsoTime(value) {
6323
6384
  return Number.isFinite(parsed) && new Date(parsed).toISOString() === `${match[1]}.${(match[2] ?? "").padEnd(3, "0").slice(0, 3)}Z`;
6324
6385
  }
6325
6386
  function isRow(value) {
6326
- if (!isObject(value)) return false;
6327
- return typeof value.n === "string" && (value.k === "file" || value.k === "folder") && typeof value.ak === "string" && typeof value.al === "string" && Array.isArray(value.a) && value.a.every((item) => typeof item === "string") && typeof value.c === "string" && typeof value.x === "string" && (value.r === void 0 || isObject(value.r) && typeof value.r.at === "string" && typeof value.r.confirmed === "boolean") && typeof value.rk === "string" && (value.cid === void 0 || typeof value.cid === "string") && (value.cc === void 0 || Array.isArray(value.cc) && value.cc.every((item) => typeof item === "string")) && typeof value.src === "string";
6387
+ if (!isObject2(value)) return false;
6388
+ return typeof value.n === "string" && (value.k === "file" || value.k === "folder") && typeof value.ak === "string" && typeof value.al === "string" && Array.isArray(value.a) && value.a.every((item) => typeof item === "string") && typeof value.c === "string" && typeof value.x === "string" && (value.r === void 0 || isObject2(value.r) && typeof value.r.at === "string" && typeof value.r.confirmed === "boolean") && typeof value.rk === "string" && (value.cid === void 0 || typeof value.cid === "string") && (value.cc === void 0 || Array.isArray(value.cc) && value.cc.every((item) => typeof item === "string")) && typeof value.src === "string";
6328
6389
  }
6329
6390
  function isAgentEntry(value) {
6330
- return isObject(value) && (value.did === null || typeof value.did === "string") && Number.isSafeInteger(value.rows) && isObject(value.rejected) && Object.values(value.rejected).every((count) => Number.isSafeInteger(count)) && Array.isArray(value.rejectedKeys) && value.rejectedKeys.every((item) => typeof item === "string") && (value.newerKeys === void 0 || Array.isArray(value.newerKeys) && value.newerKeys.every((item) => typeof item === "string")) && (value.paused === false || isObject(value.paused) && typeof value.paused.reason === "string" && typeof value.paused.at === "string") && typeof value.firstSeen === "string";
6391
+ return isObject2(value) && (value.did === null || typeof value.did === "string") && Number.isSafeInteger(value.rows) && isObject2(value.rejected) && Object.values(value.rejected).every((count) => Number.isSafeInteger(count)) && Array.isArray(value.rejectedKeys) && value.rejectedKeys.every((item) => typeof item === "string") && (value.newerKeys === void 0 || Array.isArray(value.newerKeys) && value.newerKeys.every((item) => typeof item === "string")) && (value.paused === false || isObject2(value.paused) && typeof value.paused.reason === "string" && typeof value.paused.at === "string") && typeof value.firstSeen === "string";
6331
6392
  }
6332
6393
  function parseIndexDocument(value) {
6333
- if (!isObject(value) || !Number.isSafeInteger(value.v)) return { status: "invalid" };
6394
+ if (!isObject2(value) || !Number.isSafeInteger(value.v)) return { status: "invalid" };
6334
6395
  if (value.v > INDEX_SCHEMA_VERSION) return { status: "newer", v: value.v };
6335
6396
  if (value.v !== INDEX_SCHEMA_VERSION || typeof value.ownerDid !== "string" || typeof value.spaceId !== "string" || typeof value.updatedAt !== "string" || typeof value.updatedBy !== "string") return { status: "invalid" };
6336
- if (!Array.isArray(value.archives) || !value.archives.every((item) => isObject(item) && typeof item.name === "string" && Array.isArray(item.ids) && item.ids.every((id) => typeof id === "string"))) return { status: "invalid" };
6337
- if (!Array.isArray(value.tombstones) || !value.tombstones.every((item) => isObject(item) && typeof item.key === "string" && typeof item.at === "string")) return { status: "invalid" };
6338
- if (!isObject(value.reconcile) || value.reconcile.cursor !== void 0 && typeof value.reconcile.cursor !== "string") return { status: "invalid" };
6339
- if (!isObject(value.agents) || !Object.values(value.agents).every(isAgentEntry)) return { status: "invalid" };
6340
- if (!isObject(value.rows) || !Object.values(value.rows).every(isRow)) return { status: "invalid" };
6397
+ if (!Array.isArray(value.archives) || !value.archives.every((item) => isObject2(item) && typeof item.name === "string" && Array.isArray(item.ids) && item.ids.every((id) => typeof id === "string"))) return { status: "invalid" };
6398
+ if (!Array.isArray(value.tombstones) || !value.tombstones.every((item) => isObject2(item) && typeof item.key === "string" && typeof item.at === "string")) return { status: "invalid" };
6399
+ if (!isObject2(value.reconcile) || value.reconcile.cursor !== void 0 && typeof value.reconcile.cursor !== "string") return { status: "invalid" };
6400
+ if (!isObject2(value.agents) || !Object.values(value.agents).every(isAgentEntry)) return { status: "invalid" };
6401
+ if (!isObject2(value.rows) || !Object.values(value.rows).every(isRow)) return { status: "invalid" };
6341
6402
  return { status: "ok", doc: value };
6342
6403
  }
6343
6404
  function parseArchiveDocument(value) {
6344
- if (!isObject(value) || value.v !== INDEX_SCHEMA_VERSION || !isObject(value.rows) || !Object.values(value.rows).every(isRow)) return void 0;
6405
+ if (!isObject2(value) || value.v !== INDEX_SCHEMA_VERSION || !isObject2(value.rows) || !Object.values(value.rows).every(isRow)) return void 0;
6345
6406
  return value;
6346
6407
  }
6347
6408
  var UNSAFE_TEXT = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u2028-\u202e\u2060-\u206f\ufeff\ufff9-\ufffb]/g;
@@ -6403,7 +6464,7 @@ var FIELDS_BY_RECORD_V = {
6403
6464
  };
6404
6465
  function isShareResourcePath(path) {
6405
6466
  if (typeof path !== "string" || path.length === 0 || path.length > 1024 || /[\\%\u0000-\u001f]/.test(path)) return false;
6406
- if (!SHARE_RESOURCE_PREFIXES.some((prefix) => path.startsWith(prefix) && path.length > prefix.length)) return false;
6467
+ if (!SHARE_RESOURCE_PREFIXES.some((prefix) => path.startsWith(prefix) && path.length > prefix.length) && parseArtifactVersionPath(path) === void 0) return false;
6407
6468
  return path.split("/").every((segment, index, all) => (segment !== "" || index === all.length - 1) && segment !== "." && segment !== "..");
6408
6469
  }
6409
6470
  function isSpaceOwnedBy(spaceId, ownerDid) {
@@ -6438,7 +6499,7 @@ function checkAgentRecordSchema(value, context) {
6438
6499
  throw new ShareRecordSchemaError("schema");
6439
6500
  };
6440
6501
  const fields = FIELDS_BY_RECORD_V[context.recordV];
6441
- if (fields === void 0 || !isObject(value)) return fail2();
6502
+ if (fields === void 0 || !isObject2(value)) return fail2();
6442
6503
  const keys = Object.keys(value);
6443
6504
  if (!fields.required.every((key) => keys.includes(key)) || !keys.every((key) => fields.required.includes(key) || fields.optional.includes(key))) return fail2();
6444
6505
  const record2 = value;
@@ -6447,13 +6508,15 @@ function checkAgentRecordSchema(value, context) {
6447
6508
  if (record2.shareId !== context.shareId) return fail2();
6448
6509
  const target = record2.target;
6449
6510
  const targetKeys = Object.keys(target ?? {});
6450
- if (!isObject(target) || !targetKeys.every((key) => ["origin", "nodeAudience", "spaceId", "kind"].includes(key)) || typeof target.origin !== "string" || typeof target.nodeAudience !== "string" || typeof target.spaceId !== "string" || target.kind !== void 0 && (typeof target.kind !== "string" || !["bearer", "recipientDid", "email", "emailDomain"].includes(target.kind))) return fail2();
6511
+ if (!isObject2(target) || !targetKeys.every((key) => ["origin", "nodeAudience", "spaceId", "kind"].includes(key)) || typeof target.origin !== "string" || typeof target.nodeAudience !== "string" || typeof target.spaceId !== "string" || target.kind !== void 0 && (typeof target.kind !== "string" || !["bearer", "recipientDid", "email", "emailDomain"].includes(target.kind))) return fail2();
6451
6512
  if (!isSpaceOwnedBy(target.spaceId, context.accountOwnerDid)) return fail2();
6452
6513
  const resource = record2.resource;
6453
- if (!isObject(resource) || Object.keys(resource).length !== 2 || resource.kind !== "exact" && resource.kind !== "prefix" || !isShareResourcePath(resource.path)) return fail2();
6514
+ if (!isObject2(resource) || Object.keys(resource).length !== 2 || resource.kind !== "exact" && resource.kind !== "prefix" || !isShareResourcePath(resource.path)) return fail2();
6454
6515
  if (!Array.isArray(record2.actions) || record2.actions.length === 0 || new Set(record2.actions).size !== record2.actions.length || !record2.actions.every((action) => typeof action === "string" && SHARE_ACTIONS.includes(action))) return fail2();
6516
+ const artifact = parseArtifactVersionPath(resource.path) !== void 0;
6517
+ if (artifact && (resource.kind !== "exact" || record2.actions.some((action) => action !== "tinycloud.kv/get" && action !== "tinycloud.kv/metadata"))) return fail2();
6455
6518
  const matcher = record2.recipientMatcher;
6456
- if (!isObject(matcher) || !(matcher.kind === "bearer" ? Object.keys(matcher).length === 1 : ["exactEmail", "emailDomain", "recipientDid"].includes(matcher.kind) && Object.keys(matcher).length === 2 && typeof matcher.value === "string")) return fail2();
6519
+ if (!isObject2(matcher) || !(matcher.kind === "bearer" ? Object.keys(matcher).length === 1 : ["exactEmail", "emailDomain", "recipientDid"].includes(matcher.kind) && Object.keys(matcher).length === 2 && typeof matcher.value === "string")) return fail2();
6457
6520
  if (!isIsoTime(record2.registeredAt) || !isIsoTime(record2.expiresAt) || Date.parse(record2.registeredAt) >= Date.parse(record2.expiresAt)) return fail2();
6458
6521
  for (const key of ["registrationCid", "policyCid", "ownerDelegationCid", "enforcementDelegationCid", "envelopeCid", "shareCid"]) {
6459
6522
  if (record2[key] !== void 0 && (typeof record2[key] !== "string" || !/^[a-z0-9]{8,128}$/.test(record2[key]))) return fail2();
@@ -6468,13 +6531,27 @@ function checkAgentRecordSchema(value, context) {
6468
6531
  if (record2.filename !== void 0 && (typeof record2.filename !== "string" || record2.filename.length > 512)) return fail2();
6469
6532
  if (record2.deliveryMaterial !== void 0) {
6470
6533
  const material = record2.deliveryMaterial;
6471
- if (!isObject(material) || Object.keys(material).length !== STORED_DELIVERY_MATERIAL_KEYS.length || !STORED_DELIVERY_MATERIAL_KEYS.every((key) => typeof material[key] === "string")) return fail2();
6534
+ if (!isObject2(material) || !STORED_DELIVERY_MATERIAL_KEYS.every((key) => typeof material[key] === "string") || Object.keys(material).some((key) => !STORED_DELIVERY_MATERIAL_KEYS.includes(key) && key !== "fullLink")) return fail2();
6535
+ if (Object.hasOwn(material, "fullLink")) {
6536
+ try {
6537
+ checkStoredFullShareLink(material.fullLink);
6538
+ } catch {
6539
+ return fail2();
6540
+ }
6541
+ }
6472
6542
  if (typeof record2.link === "string" && carriesSealedRecord(record2.link)) return fail2();
6473
6543
  }
6474
6544
  if (record2.deliveredRecipients !== void 0 && (!Array.isArray(record2.deliveredRecipients) || !record2.deliveredRecipients.every((item) => typeof item === "string"))) return fail2();
6475
6545
  if (record2.proofs !== void 0) {
6476
6546
  const proofs = record2.proofs;
6477
- if (!isObject(proofs) || !Object.keys(proofs).every((key) => PROOF_STRING_FIELDS.includes(key) ? typeof proofs[key] === "string" : PROOF_OBJECT_FIELDS.includes(key) && isObject(proofs[key]))) return fail2();
6547
+ if (!isObject2(proofs) || !Object.keys(proofs).every((key) => PROOF_STRING_FIELDS.includes(key) ? typeof proofs[key] === "string" : PROOF_OBJECT_FIELDS.includes(key) && isObject2(proofs[key]))) return fail2();
6548
+ }
6549
+ if (artifact) {
6550
+ const proofs = record2.proofs;
6551
+ if (isObject2(proofs) && proofs.ownerPolicy !== void 0) return fail2();
6552
+ if (record2.deliveryMaterial !== void 0) {
6553
+ if (record2.targetKind === void 0 || record2.targetKind === "bearer" || isObject2(proofs) && proofs.link !== void 0) return fail2();
6554
+ } else if (record2.targetKind !== void 0 && record2.targetKind !== "bearer") return fail2();
6478
6555
  }
6479
6556
  return record2;
6480
6557
  }
@@ -7921,6 +7998,76 @@ async function verifyAgentRecordChain(record2, context) {
7921
7998
  }
7922
7999
  }
7923
8000
 
8001
+ // src/artifact-pin.ts
8002
+ var ArtifactPinError = class extends Error {
8003
+ code;
8004
+ /** The code is the message: stable, short, and what callers and tests match on. */
8005
+ constructor(code3) {
8006
+ super(code3);
8007
+ this.name = "ArtifactPinError";
8008
+ this.code = code3;
8009
+ }
8010
+ };
8011
+ function artifactBindingView(envelope) {
8012
+ return {
8013
+ ...envelope.artifactPin === void 0 ? {} : { artifactPin: envelope.artifactPin },
8014
+ target: envelope.target,
8015
+ contentSource: envelope.contentSource,
8016
+ resource: envelope.resource,
8017
+ actions: envelope.actions,
8018
+ policyKvActions: envelope.policy.capabilityCeiling.flatMap((capability) => capability.kind === "kv" ? capability.actions : [])
8019
+ };
8020
+ }
8021
+ function sourcePath(view) {
8022
+ const resource = view.contentSource.kvResource;
8023
+ const marker = "/kv/";
8024
+ const at = resource.indexOf(marker);
8025
+ if (at <= 0 || resource.slice(0, at) !== view.target.spaceId) {
8026
+ throw new ArtifactPinError("pin-source-mismatch");
8027
+ }
8028
+ return resource.slice(at + marker.length);
8029
+ }
8030
+ function assertArtifactBinding(view, expected = {}) {
8031
+ const pin = view.artifactPin;
8032
+ const path = sourcePath(view);
8033
+ const inArtifacts = path === ARTIFACT_ROOT || path.startsWith(`${ARTIFACT_ROOT}/`);
8034
+ if (!inArtifacts) {
8035
+ if (pin !== void 0) throw new ArtifactPinError("pin-source-mismatch");
8036
+ return void 0;
8037
+ }
8038
+ if (pin === void 0) throw new ArtifactPinError("missing-pin");
8039
+ const parsed = parseArtifactVersionPath(path);
8040
+ if (parsed === void 0) throw new ArtifactPinError("unsupported-artifact-path");
8041
+ const file = pin.files[0];
8042
+ const source = view.contentSource;
8043
+ if (
8044
+ // The stored ciphertext of one file: bounded like content (100 MiB) plus the envelope's own framing.
8045
+ file.bytes > 128 * 1024 * 1024 || pin.artifactId !== parsed.artifactId || pin.version !== parsed.version || file.path !== parsed.file || view.resource.kind !== "exact" || view.resource.path !== path || source.selector !== "exact" || source.mode !== "immutable" || source.initialCiphertextDigestHex !== file.sha256
8046
+ ) {
8047
+ throw new ArtifactPinError("pin-source-mismatch");
8048
+ }
8049
+ if (view.actions.includes("edit") || view.policyKvActions.includes("tinycloud.kv/put")) {
8050
+ throw new ArtifactPinError("pinned-share-writable");
8051
+ }
8052
+ if (expected.artifactId !== void 0 && expected.artifactId !== pin.artifactId || expected.version !== void 0 && expected.version !== pin.version) {
8053
+ throw new ArtifactPinError("expectation-mismatch");
8054
+ }
8055
+ return pin;
8056
+ }
8057
+ function verifyArtifactShare(envelope, options) {
8058
+ if (envelope.signature.signerDid !== options.expectedOwnerDid) {
8059
+ throw new ArtifactPinError("wrong-signer");
8060
+ }
8061
+ let valid;
8062
+ try {
8063
+ valid = verifyEnvelopeV3SignatureOnly(envelope) && envelope.policy.ownerDid === options.expectedOwnerDid;
8064
+ } catch {
8065
+ valid = false;
8066
+ }
8067
+ if (!valid) throw new ArtifactPinError("bad-signature");
8068
+ return assertArtifactBinding(artifactBindingView(envelope), options.expected);
8069
+ }
8070
+
7924
8071
  // src/share-index/sender-share-index.ts
7925
8072
  var DAY_MS = 24 * 60 * 60 * 1e3;
7926
8073
  var DEFAULT_SHARE_INDEX_LIMITS = {
@@ -7966,13 +8113,13 @@ function basename(key) {
7966
8113
  function agentOf(row) {
7967
8114
  return row.src.startsWith("agent:") ? row.src.slice("agent:".length) : void 0;
7968
8115
  }
7969
- function isObject2(item) {
8116
+ function isObject3(item) {
7970
8117
  return item !== null && typeof item === "object" && !Array.isArray(item);
7971
8118
  }
7972
8119
  function isWebRecord(value) {
7973
- if (!isObject2(value)) return false;
8120
+ if (!isObject3(value)) return false;
7974
8121
  const { shareId, target, resource, actions, recipientMatcher, registeredAt, expiresAt } = value;
7975
- return typeof shareId === "string" && isObject2(target) && typeof target.spaceId === "string" && isObject2(resource) && typeof resource.path === "string" && Array.isArray(actions) && actions.every((item) => typeof item === "string") && isObject2(recipientMatcher) && typeof recipientMatcher.kind === "string" && typeof registeredAt === "string" && typeof expiresAt === "string";
8122
+ return typeof shareId === "string" && isObject3(target) && typeof target.spaceId === "string" && isObject3(resource) && typeof resource.path === "string" && Array.isArray(actions) && actions.every((item) => typeof item === "string") && isObject3(recipientMatcher) && typeof recipientMatcher.kind === "string" && typeof registeredAt === "string" && typeof expiresAt === "string";
7976
8123
  }
7977
8124
  function unreadableRow(now) {
7978
8125
  const at = now.toISOString();
@@ -8206,10 +8353,16 @@ var SenderShareIndex = class {
8206
8353
  } catch (error) {
8207
8354
  return { kind: "reject", reason: error instanceof ShareRecordSchemaError && error.reason === "provenance" ? "provenance" : "schema" };
8208
8355
  }
8209
- if (record2.link !== void 0 && !validateShareLink(record2.link, this.options.shareOrigins).ok) return { kind: "reject", reason: "link" };
8210
- if (record2.link !== void 0 && record2.deliveryMaterial !== void 0) {
8356
+ let link;
8357
+ try {
8358
+ link = await restoreShareRecordLink(record2, this.options.shareOrigins);
8359
+ } catch {
8360
+ return { kind: "reject", reason: "link" };
8361
+ }
8362
+ if (link !== void 0 && !validateShareLink(link, this.options.shareOrigins).ok) return { kind: "reject", reason: "link" };
8363
+ if (link !== void 0 && record2.deliveryMaterial !== void 0) {
8211
8364
  try {
8212
- assertLinkBoundToMaterial(record2.link, record2.deliveryMaterial);
8365
+ assertLinkBoundToMaterial(link, record2.deliveryMaterial);
8213
8366
  } catch {
8214
8367
  return { kind: "reject", reason: "link" };
8215
8368
  }
@@ -8222,6 +8375,14 @@ var SenderShareIndex = class {
8222
8375
  ...this.options.registrationKey === void 0 ? {} : { registrationKey: this.options.registrationKey }
8223
8376
  });
8224
8377
  if (!verdict.ok) return { kind: "reject", reason: "proof" };
8378
+ if (parseArtifactVersionPath(record2.resource.path) !== void 0 && record2.deliveryMaterial !== void 0) {
8379
+ try {
8380
+ const envelope = shareEnvelopeV3Schema.parse(await openDeliveryEnvelope(record2.deliveryMaterial));
8381
+ if (verifyArtifactShare(envelope, { expectedOwnerDid: envelope.policy.ownerDid }) === void 0) return { kind: "reject", reason: "proof" };
8382
+ } catch {
8383
+ return { kind: "reject", reason: "proof" };
8384
+ }
8385
+ }
8225
8386
  const window = input.source === "migrated" ? { ...verdict.issued.latest === void 0 ? {} : { latest: verdict.issued.latest } } : verdict.issued;
8226
8387
  if (!registeredAtFits(record2.registeredAt, window, this.now())) return { kind: "reject", reason: "schema" };
8227
8388
  const row = project(record2, { key: input.key, src: input.src, claims: verdict.claims, proof: { state: verdict.state, expired: verdict.expired } });
@@ -8234,7 +8395,7 @@ var SenderShareIndex = class {
8234
8395
  peekRecordV(bytes) {
8235
8396
  try {
8236
8397
  const value = JSON.parse(new TextDecoder().decode(bytes));
8237
- const recordV = isObject2(value) && isObject2(value.hdr) ? value.hdr.recordV : void 0;
8398
+ const recordV = isObject3(value) && isObject3(value.hdr) ? value.hdr.recordV : void 0;
8238
8399
  return typeof recordV === "number" && Number.isSafeInteger(recordV) ? recordV : SUPPORTED_AGENT_RECORD_V + 1;
8239
8400
  } catch {
8240
8401
  return SUPPORTED_AGENT_RECORD_V + 1;
@@ -8320,7 +8481,7 @@ var SenderShareIndex = class {
8320
8481
  if (classified.kind === "web") {
8321
8482
  fetches++;
8322
8483
  const read3 = await this.backend.readVault(key);
8323
- if (read3.status === "ok" && isObject2(read3.value) && read3.value.provenance === "migrated") {
8484
+ if (read3.status === "ok" && isObject3(read3.value) && read3.value.provenance === "migrated") {
8324
8485
  const checked = await this.checkRecord(read3.value, { key, shareId: classified.shareId, src: "web", source: "migrated", agentDid: "", recordV: SUPPORTED_AGENT_RECORD_V }, archivedOnce);
8325
8486
  findings.push({ kind: "web", key, row: checked.kind === "admit" ? checked.row : { ...unreadableRow(this.now()), pv: "migrated" } });
8326
8487
  } else if (read3.status === "ok") {
@@ -8595,6 +8756,7 @@ export {
8595
8756
  agentRecordPrefix,
8596
8757
  assertLinkBoundToMaterial,
8597
8758
  attenuationCovers,
8759
+ checkAgentRecordSchema,
8598
8760
  checkRegistrationReceipt,
8599
8761
  claimRank,
8600
8762
  classifyRecordKey,
@@ -8615,6 +8777,8 @@ export {
8615
8777
  parseSignedUcan,
8616
8778
  parseStoredAgentRecord,
8617
8779
  project,
8780
+ registeredAtFits,
8781
+ restoreShareRecordLink,
8618
8782
  sealAgentRecord,
8619
8783
  validateShareLink,
8620
8784
  verifyAgentRecordChain,