@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.
@@ -46,6 +46,7 @@ __export(share_index_exports, {
46
46
  agentRecordPrefix: () => agentRecordPrefix,
47
47
  assertLinkBoundToMaterial: () => assertLinkBoundToMaterial,
48
48
  attenuationCovers: () => attenuationCovers,
49
+ checkAgentRecordSchema: () => checkAgentRecordSchema,
49
50
  checkRegistrationReceipt: () => checkRegistrationReceipt,
50
51
  claimRank: () => claimRank,
51
52
  classifyRecordKey: () => classifyRecordKey,
@@ -66,6 +67,8 @@ __export(share_index_exports, {
66
67
  parseSignedUcan: () => parseSignedUcan,
67
68
  parseStoredAgentRecord: () => parseStoredAgentRecord,
68
69
  project: () => project,
70
+ registeredAtFits: () => registeredAtFits,
71
+ restoreShareRecordLink: () => restoreShareRecordLink,
69
72
  sealAgentRecord: () => sealAgentRecord,
70
73
  validateShareLink: () => validateShareLink,
71
74
  verifyAgentRecordChain: () => verifyAgentRecordChain,
@@ -6137,6 +6140,16 @@ var DeliveryMaterialError = class extends Error {
6137
6140
  this.name = "DeliveryMaterialError";
6138
6141
  }
6139
6142
  };
6143
+ function isObject(value) {
6144
+ return value !== null && typeof value === "object" && !Array.isArray(value);
6145
+ }
6146
+ function checkStoredFullShareLink(value) {
6147
+ 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");
6148
+ const header = (0, import_links2.validateLinkHeader)(value.header, { mode: "signed" });
6149
+ const signature = import_links2.linkSignatureV1Schema.parse(value.signature);
6150
+ if (header.type !== "share") throw new DeliveryMaterialError("full link is not a share");
6151
+ return { origin: value.origin, header, signature };
6152
+ }
6140
6153
  function sealedParts(material) {
6141
6154
  let sealed;
6142
6155
  let key;
@@ -6153,18 +6166,24 @@ async function openDeliveryEnvelope(material) {
6153
6166
  const { sealed, key } = sealedParts(material);
6154
6167
  if (await computeCid(sealed) !== material.shareCid) throw new DeliveryMaterialError("sealed envelope does not match shareCid");
6155
6168
  let text;
6156
- try {
6157
- text = new TextDecoder("utf-8", { fatal: true }).decode(await open(sealed, key));
6158
- } catch {
6159
- throw new DeliveryMaterialError("sealed envelope does not open under its key");
6160
- }
6161
6169
  let value;
6162
6170
  try {
6163
- value = JSON.parse(text);
6164
- } catch {
6165
- throw new DeliveryMaterialError("sealed envelope is not JSON");
6171
+ if (material.fullLink === void 0) {
6172
+ text = new TextDecoder("utf-8", { fatal: true }).decode(await open(sealed, key));
6173
+ value = JSON.parse(text);
6174
+ } else {
6175
+ const metadata = checkStoredFullShareLink(material.fullLink);
6176
+ if (metadata.header.bodyCid !== material.shareCid) throw new DeliveryMaterialError("full link names another sealed body");
6177
+ const opened = await (0, import_links2.openLinkRecord)({ header: metadata.header, signature: metadata.signature, body: material.sealedEnvelope }, key, { now: Date.parse(metadata.header.issuedAt) });
6178
+ text = new TextDecoder("utf-8", { fatal: true }).decode(opened.plaintext);
6179
+ value = opened.value;
6180
+ 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");
6181
+ }
6182
+ } catch (error) {
6183
+ if (error instanceof DeliveryMaterialError) throw error;
6184
+ throw new DeliveryMaterialError(material.fullLink === void 0 ? "sealed envelope does not open under its key" : "original full link is invalid");
6166
6185
  }
6167
- if (value === null || typeof value !== "object" || Array.isArray(value) || canonicalize(value) !== text) throw new DeliveryMaterialError("sealed envelope is not canonical JSON");
6186
+ if (!isObject(value) || canonicalize(value) !== text) throw new DeliveryMaterialError("sealed envelope is not canonical JSON");
6168
6187
  return value;
6169
6188
  }
6170
6189
  function assertLinkBoundToMaterial(link, material) {
@@ -6183,21 +6202,58 @@ async function compactShareRecord(record2) {
6183
6202
  const material = record2.deliveryMaterial;
6184
6203
  if (material === void 0) return record2;
6185
6204
  if (typeof material.shareCid !== "string" || typeof material.sealedEnvelope !== "string" || typeof material.envelopeKey !== "string") throw new DeliveryMaterialError("delivery material is incomplete");
6186
- const stored = { shareCid: material.shareCid, sealedEnvelope: material.sealedEnvelope, envelopeKey: material.envelopeKey };
6187
- const opened = await openDeliveryEnvelope(stored);
6188
- if (material.envelope !== void 0 && canonicalize(opened) !== canonicalize(material.envelope)) throw new DeliveryMaterialError("envelope is not the sealed envelope");
6205
+ let fullLink = "fullLink" in material ? checkStoredFullShareLink(material.fullLink) : void 0;
6189
6206
  const { link, deliveryMaterial: _material, ...rest } = record2;
6190
- const keptLink = link !== void 0 && assertLinkBoundToMaterial(link, stored) === "short" ? link : void 0;
6207
+ let keptLink;
6208
+ if (link !== void 0) {
6209
+ if (assertLinkBoundToMaterial(link, material) === "short") keptLink = link;
6210
+ else {
6211
+ const parsed = (0, import_links2.parseLink)(link, { trustedOrigins: [new URL(link).origin] });
6212
+ if (parsed.form !== "full" || !isObject(parsed.record)) throw new DeliveryMaterialError("full link record is invalid");
6213
+ const header = (0, import_links2.validateLinkHeader)(parsed.record.header, { mode: "signed" });
6214
+ const checked = await (0, import_links2.checkLinkRecord)(parsed.record, Date.parse(header.issuedAt));
6215
+ if (checked.signature === void 0) throw new DeliveryMaterialError("full link signature is missing");
6216
+ const original = checkStoredFullShareLink({ origin: parsed.origin, header: checked.header, signature: checked.signature });
6217
+ if (fullLink !== void 0 && canonicalize(fullLink) !== canonicalize(original)) throw new DeliveryMaterialError("full link metadata differs from the original link");
6218
+ fullLink = original;
6219
+ }
6220
+ }
6221
+ const stored = { shareCid: material.shareCid, sealedEnvelope: material.sealedEnvelope, envelopeKey: material.envelopeKey, ...fullLink === void 0 ? {} : { fullLink } };
6222
+ const opened = await openDeliveryEnvelope(stored);
6223
+ if ("envelope" in material && material.envelope !== void 0 && canonicalize(opened) !== canonicalize(material.envelope)) throw new DeliveryMaterialError("envelope is not the sealed envelope");
6224
+ if (fullLink !== void 0 && Date.parse(fullLink.header.expiresAt) !== Date.parse(record2.expiresAt)) throw new DeliveryMaterialError("full link expiry differs from the record");
6191
6225
  return { ...rest, ...keptLink === void 0 ? {} : { link: keptLink }, deliveryMaterial: stored };
6192
6226
  }
6193
6227
  function equalBytes(left, right) {
6194
6228
  return left.length === right.length && left.every((byte, index) => byte === right[index]);
6195
6229
  }
6196
- async function expandShareRecord(record2) {
6230
+ function recordLink(record2, shareOrigins) {
6197
6231
  const material = record2.deliveryMaterial;
6198
- if (material === void 0) return record2;
6232
+ if (material?.fullLink === void 0) return record2.link;
6233
+ const metadata = checkStoredFullShareLink(material.fullLink);
6234
+ if (!shareOrigins.includes(metadata.origin)) throw new DeliveryMaterialError("full link origin is not trusted");
6235
+ if (Date.parse(metadata.header.expiresAt) !== Date.parse(record2.expiresAt)) throw new DeliveryMaterialError("full link expiry differs from the record");
6236
+ const full = (0, import_links2.buildFullLink)({ origin: metadata.origin, key: sealedParts(material).key, record: { header: metadata.header, signature: metadata.signature, body: material.sealedEnvelope } });
6237
+ if (record2.link !== void 0) {
6238
+ (0, import_links2.parseLink)(record2.link, { trustedOrigins: shareOrigins });
6239
+ assertLinkBoundToMaterial(record2.link, material);
6240
+ }
6241
+ return record2.link ?? full;
6242
+ }
6243
+ async function restoreShareRecordLink(record2, shareOrigins) {
6244
+ if (record2.deliveryMaterial?.fullLink !== void 0) await openDeliveryEnvelope(record2.deliveryMaterial);
6245
+ return recordLink(record2, shareOrigins);
6246
+ }
6247
+ async function expandShareRecord(record2, options = {}) {
6248
+ const material = record2.deliveryMaterial;
6249
+ if (material === void 0) {
6250
+ const { deliveryMaterial: _material, ...withoutMaterial } = record2;
6251
+ return withoutMaterial;
6252
+ }
6199
6253
  const envelope = await openDeliveryEnvelope(material);
6200
- return { ...record2, deliveryMaterial: { ...material, envelope } };
6254
+ const origins = options.shareOrigins ?? (material.fullLink === void 0 ? [] : [checkStoredFullShareLink(material.fullLink).origin]);
6255
+ const link = recordLink(record2, origins);
6256
+ return { ...record2, ...link === void 0 ? {} : { link }, deliveryMaterial: { ...material, envelope } };
6201
6257
  }
6202
6258
 
6203
6259
  // src/share-index/record-store.ts
@@ -6369,6 +6425,14 @@ var AgentShareRecordStore = class {
6369
6425
  }
6370
6426
  };
6371
6427
 
6428
+ // src/artifact-core.ts
6429
+ var ARTIFACT_ROOT = "artifacts";
6430
+ function parseArtifactVersionPath(path) {
6431
+ const match = /^artifacts\/([a-z0-9][a-z0-9_-]{2,62})\/versions\/([1-9][0-9]{0,14})\/([^/]+)$/.exec(path);
6432
+ if (match === null) return void 0;
6433
+ return { artifactId: match[1], version: Number(match[2]), file: match[3] };
6434
+ }
6435
+
6372
6436
  // src/share-index/schema.ts
6373
6437
  var INDEX_SCHEMA_VERSION = 1;
6374
6438
  function emptyIndexDocument(input) {
@@ -6385,7 +6449,7 @@ function emptyIndexDocument(input) {
6385
6449
  rows: {}
6386
6450
  };
6387
6451
  }
6388
- function isObject(value) {
6452
+ function isObject2(value) {
6389
6453
  return value !== null && typeof value === "object" && !Array.isArray(value);
6390
6454
  }
6391
6455
  function isIsoTime(value) {
@@ -6394,25 +6458,25 @@ function isIsoTime(value) {
6394
6458
  return Number.isFinite(parsed) && new Date(parsed).toISOString() === `${match[1]}.${(match[2] ?? "").padEnd(3, "0").slice(0, 3)}Z`;
6395
6459
  }
6396
6460
  function isRow(value) {
6397
- if (!isObject(value)) return false;
6398
- 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";
6461
+ if (!isObject2(value)) return false;
6462
+ 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";
6399
6463
  }
6400
6464
  function isAgentEntry(value) {
6401
- 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";
6465
+ 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";
6402
6466
  }
6403
6467
  function parseIndexDocument(value) {
6404
- if (!isObject(value) || !Number.isSafeInteger(value.v)) return { status: "invalid" };
6468
+ if (!isObject2(value) || !Number.isSafeInteger(value.v)) return { status: "invalid" };
6405
6469
  if (value.v > INDEX_SCHEMA_VERSION) return { status: "newer", v: value.v };
6406
6470
  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" };
6407
- 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" };
6408
- if (!Array.isArray(value.tombstones) || !value.tombstones.every((item) => isObject(item) && typeof item.key === "string" && typeof item.at === "string")) return { status: "invalid" };
6409
- if (!isObject(value.reconcile) || value.reconcile.cursor !== void 0 && typeof value.reconcile.cursor !== "string") return { status: "invalid" };
6410
- if (!isObject(value.agents) || !Object.values(value.agents).every(isAgentEntry)) return { status: "invalid" };
6411
- if (!isObject(value.rows) || !Object.values(value.rows).every(isRow)) return { status: "invalid" };
6471
+ 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" };
6472
+ if (!Array.isArray(value.tombstones) || !value.tombstones.every((item) => isObject2(item) && typeof item.key === "string" && typeof item.at === "string")) return { status: "invalid" };
6473
+ if (!isObject2(value.reconcile) || value.reconcile.cursor !== void 0 && typeof value.reconcile.cursor !== "string") return { status: "invalid" };
6474
+ if (!isObject2(value.agents) || !Object.values(value.agents).every(isAgentEntry)) return { status: "invalid" };
6475
+ if (!isObject2(value.rows) || !Object.values(value.rows).every(isRow)) return { status: "invalid" };
6412
6476
  return { status: "ok", doc: value };
6413
6477
  }
6414
6478
  function parseArchiveDocument(value) {
6415
- if (!isObject(value) || value.v !== INDEX_SCHEMA_VERSION || !isObject(value.rows) || !Object.values(value.rows).every(isRow)) return void 0;
6479
+ if (!isObject2(value) || value.v !== INDEX_SCHEMA_VERSION || !isObject2(value.rows) || !Object.values(value.rows).every(isRow)) return void 0;
6416
6480
  return value;
6417
6481
  }
6418
6482
  var UNSAFE_TEXT = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u2028-\u202e\u2060-\u206f\ufeff\ufff9-\ufffb]/g;
@@ -6474,7 +6538,7 @@ var FIELDS_BY_RECORD_V = {
6474
6538
  };
6475
6539
  function isShareResourcePath(path) {
6476
6540
  if (typeof path !== "string" || path.length === 0 || path.length > 1024 || /[\\%\u0000-\u001f]/.test(path)) return false;
6477
- if (!SHARE_RESOURCE_PREFIXES.some((prefix) => path.startsWith(prefix) && path.length > prefix.length)) return false;
6541
+ if (!SHARE_RESOURCE_PREFIXES.some((prefix) => path.startsWith(prefix) && path.length > prefix.length) && parseArtifactVersionPath(path) === void 0) return false;
6478
6542
  return path.split("/").every((segment, index, all) => (segment !== "" || index === all.length - 1) && segment !== "." && segment !== "..");
6479
6543
  }
6480
6544
  function isSpaceOwnedBy(spaceId, ownerDid) {
@@ -6509,7 +6573,7 @@ function checkAgentRecordSchema(value, context) {
6509
6573
  throw new ShareRecordSchemaError("schema");
6510
6574
  };
6511
6575
  const fields = FIELDS_BY_RECORD_V[context.recordV];
6512
- if (fields === void 0 || !isObject(value)) return fail2();
6576
+ if (fields === void 0 || !isObject2(value)) return fail2();
6513
6577
  const keys = Object.keys(value);
6514
6578
  if (!fields.required.every((key) => keys.includes(key)) || !keys.every((key) => fields.required.includes(key) || fields.optional.includes(key))) return fail2();
6515
6579
  const record2 = value;
@@ -6518,13 +6582,15 @@ function checkAgentRecordSchema(value, context) {
6518
6582
  if (record2.shareId !== context.shareId) return fail2();
6519
6583
  const target = record2.target;
6520
6584
  const targetKeys = Object.keys(target ?? {});
6521
- 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();
6585
+ 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();
6522
6586
  if (!isSpaceOwnedBy(target.spaceId, context.accountOwnerDid)) return fail2();
6523
6587
  const resource = record2.resource;
6524
- if (!isObject(resource) || Object.keys(resource).length !== 2 || resource.kind !== "exact" && resource.kind !== "prefix" || !isShareResourcePath(resource.path)) return fail2();
6588
+ if (!isObject2(resource) || Object.keys(resource).length !== 2 || resource.kind !== "exact" && resource.kind !== "prefix" || !isShareResourcePath(resource.path)) return fail2();
6525
6589
  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();
6590
+ const artifact = parseArtifactVersionPath(resource.path) !== void 0;
6591
+ if (artifact && (resource.kind !== "exact" || record2.actions.some((action) => action !== "tinycloud.kv/get" && action !== "tinycloud.kv/metadata"))) return fail2();
6526
6592
  const matcher = record2.recipientMatcher;
6527
- 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();
6593
+ 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();
6528
6594
  if (!isIsoTime(record2.registeredAt) || !isIsoTime(record2.expiresAt) || Date.parse(record2.registeredAt) >= Date.parse(record2.expiresAt)) return fail2();
6529
6595
  for (const key of ["registrationCid", "policyCid", "ownerDelegationCid", "enforcementDelegationCid", "envelopeCid", "shareCid"]) {
6530
6596
  if (record2[key] !== void 0 && (typeof record2[key] !== "string" || !/^[a-z0-9]{8,128}$/.test(record2[key]))) return fail2();
@@ -6539,13 +6605,27 @@ function checkAgentRecordSchema(value, context) {
6539
6605
  if (record2.filename !== void 0 && (typeof record2.filename !== "string" || record2.filename.length > 512)) return fail2();
6540
6606
  if (record2.deliveryMaterial !== void 0) {
6541
6607
  const material = record2.deliveryMaterial;
6542
- if (!isObject(material) || Object.keys(material).length !== STORED_DELIVERY_MATERIAL_KEYS.length || !STORED_DELIVERY_MATERIAL_KEYS.every((key) => typeof material[key] === "string")) return fail2();
6608
+ 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();
6609
+ if (Object.hasOwn(material, "fullLink")) {
6610
+ try {
6611
+ checkStoredFullShareLink(material.fullLink);
6612
+ } catch {
6613
+ return fail2();
6614
+ }
6615
+ }
6543
6616
  if (typeof record2.link === "string" && carriesSealedRecord(record2.link)) return fail2();
6544
6617
  }
6545
6618
  if (record2.deliveredRecipients !== void 0 && (!Array.isArray(record2.deliveredRecipients) || !record2.deliveredRecipients.every((item) => typeof item === "string"))) return fail2();
6546
6619
  if (record2.proofs !== void 0) {
6547
6620
  const proofs = record2.proofs;
6548
- 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();
6621
+ 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();
6622
+ }
6623
+ if (artifact) {
6624
+ const proofs = record2.proofs;
6625
+ if (isObject2(proofs) && proofs.ownerPolicy !== void 0) return fail2();
6626
+ if (record2.deliveryMaterial !== void 0) {
6627
+ if (record2.targetKind === void 0 || record2.targetKind === "bearer" || isObject2(proofs) && proofs.link !== void 0) return fail2();
6628
+ } else if (record2.targetKind !== void 0 && record2.targetKind !== "bearer") return fail2();
6549
6629
  }
6550
6630
  return record2;
6551
6631
  }
@@ -7992,6 +8072,76 @@ async function verifyAgentRecordChain(record2, context) {
7992
8072
  }
7993
8073
  }
7994
8074
 
8075
+ // src/artifact-pin.ts
8076
+ var ArtifactPinError = class extends Error {
8077
+ code;
8078
+ /** The code is the message: stable, short, and what callers and tests match on. */
8079
+ constructor(code3) {
8080
+ super(code3);
8081
+ this.name = "ArtifactPinError";
8082
+ this.code = code3;
8083
+ }
8084
+ };
8085
+ function artifactBindingView(envelope) {
8086
+ return {
8087
+ ...envelope.artifactPin === void 0 ? {} : { artifactPin: envelope.artifactPin },
8088
+ target: envelope.target,
8089
+ contentSource: envelope.contentSource,
8090
+ resource: envelope.resource,
8091
+ actions: envelope.actions,
8092
+ policyKvActions: envelope.policy.capabilityCeiling.flatMap((capability) => capability.kind === "kv" ? capability.actions : [])
8093
+ };
8094
+ }
8095
+ function sourcePath(view) {
8096
+ const resource = view.contentSource.kvResource;
8097
+ const marker = "/kv/";
8098
+ const at = resource.indexOf(marker);
8099
+ if (at <= 0 || resource.slice(0, at) !== view.target.spaceId) {
8100
+ throw new ArtifactPinError("pin-source-mismatch");
8101
+ }
8102
+ return resource.slice(at + marker.length);
8103
+ }
8104
+ function assertArtifactBinding(view, expected = {}) {
8105
+ const pin = view.artifactPin;
8106
+ const path = sourcePath(view);
8107
+ const inArtifacts = path === ARTIFACT_ROOT || path.startsWith(`${ARTIFACT_ROOT}/`);
8108
+ if (!inArtifacts) {
8109
+ if (pin !== void 0) throw new ArtifactPinError("pin-source-mismatch");
8110
+ return void 0;
8111
+ }
8112
+ if (pin === void 0) throw new ArtifactPinError("missing-pin");
8113
+ const parsed = parseArtifactVersionPath(path);
8114
+ if (parsed === void 0) throw new ArtifactPinError("unsupported-artifact-path");
8115
+ const file = pin.files[0];
8116
+ const source = view.contentSource;
8117
+ if (
8118
+ // The stored ciphertext of one file: bounded like content (100 MiB) plus the envelope's own framing.
8119
+ 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
8120
+ ) {
8121
+ throw new ArtifactPinError("pin-source-mismatch");
8122
+ }
8123
+ if (view.actions.includes("edit") || view.policyKvActions.includes("tinycloud.kv/put")) {
8124
+ throw new ArtifactPinError("pinned-share-writable");
8125
+ }
8126
+ if (expected.artifactId !== void 0 && expected.artifactId !== pin.artifactId || expected.version !== void 0 && expected.version !== pin.version) {
8127
+ throw new ArtifactPinError("expectation-mismatch");
8128
+ }
8129
+ return pin;
8130
+ }
8131
+ function verifyArtifactShare(envelope, options) {
8132
+ if (envelope.signature.signerDid !== options.expectedOwnerDid) {
8133
+ throw new ArtifactPinError("wrong-signer");
8134
+ }
8135
+ let valid;
8136
+ try {
8137
+ valid = verifyEnvelopeV3SignatureOnly(envelope) && envelope.policy.ownerDid === options.expectedOwnerDid;
8138
+ } catch {
8139
+ valid = false;
8140
+ }
8141
+ if (!valid) throw new ArtifactPinError("bad-signature");
8142
+ return assertArtifactBinding(artifactBindingView(envelope), options.expected);
8143
+ }
8144
+
7995
8145
  // src/share-index/sender-share-index.ts
7996
8146
  var DAY_MS = 24 * 60 * 60 * 1e3;
7997
8147
  var DEFAULT_SHARE_INDEX_LIMITS = {
@@ -8037,13 +8187,13 @@ function basename(key) {
8037
8187
  function agentOf(row) {
8038
8188
  return row.src.startsWith("agent:") ? row.src.slice("agent:".length) : void 0;
8039
8189
  }
8040
- function isObject2(item) {
8190
+ function isObject3(item) {
8041
8191
  return item !== null && typeof item === "object" && !Array.isArray(item);
8042
8192
  }
8043
8193
  function isWebRecord(value) {
8044
- if (!isObject2(value)) return false;
8194
+ if (!isObject3(value)) return false;
8045
8195
  const { shareId, target, resource, actions, recipientMatcher, registeredAt, expiresAt } = value;
8046
- 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";
8196
+ 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";
8047
8197
  }
8048
8198
  function unreadableRow(now) {
8049
8199
  const at = now.toISOString();
@@ -8277,10 +8427,16 @@ var SenderShareIndex = class {
8277
8427
  } catch (error) {
8278
8428
  return { kind: "reject", reason: error instanceof ShareRecordSchemaError && error.reason === "provenance" ? "provenance" : "schema" };
8279
8429
  }
8280
- if (record2.link !== void 0 && !validateShareLink(record2.link, this.options.shareOrigins).ok) return { kind: "reject", reason: "link" };
8281
- if (record2.link !== void 0 && record2.deliveryMaterial !== void 0) {
8430
+ let link;
8431
+ try {
8432
+ link = await restoreShareRecordLink(record2, this.options.shareOrigins);
8433
+ } catch {
8434
+ return { kind: "reject", reason: "link" };
8435
+ }
8436
+ if (link !== void 0 && !validateShareLink(link, this.options.shareOrigins).ok) return { kind: "reject", reason: "link" };
8437
+ if (link !== void 0 && record2.deliveryMaterial !== void 0) {
8282
8438
  try {
8283
- assertLinkBoundToMaterial(record2.link, record2.deliveryMaterial);
8439
+ assertLinkBoundToMaterial(link, record2.deliveryMaterial);
8284
8440
  } catch {
8285
8441
  return { kind: "reject", reason: "link" };
8286
8442
  }
@@ -8293,6 +8449,14 @@ var SenderShareIndex = class {
8293
8449
  ...this.options.registrationKey === void 0 ? {} : { registrationKey: this.options.registrationKey }
8294
8450
  });
8295
8451
  if (!verdict.ok) return { kind: "reject", reason: "proof" };
8452
+ if (parseArtifactVersionPath(record2.resource.path) !== void 0 && record2.deliveryMaterial !== void 0) {
8453
+ try {
8454
+ const envelope = shareEnvelopeV3Schema.parse(await openDeliveryEnvelope(record2.deliveryMaterial));
8455
+ if (verifyArtifactShare(envelope, { expectedOwnerDid: envelope.policy.ownerDid }) === void 0) return { kind: "reject", reason: "proof" };
8456
+ } catch {
8457
+ return { kind: "reject", reason: "proof" };
8458
+ }
8459
+ }
8296
8460
  const window = input.source === "migrated" ? { ...verdict.issued.latest === void 0 ? {} : { latest: verdict.issued.latest } } : verdict.issued;
8297
8461
  if (!registeredAtFits(record2.registeredAt, window, this.now())) return { kind: "reject", reason: "schema" };
8298
8462
  const row = project(record2, { key: input.key, src: input.src, claims: verdict.claims, proof: { state: verdict.state, expired: verdict.expired } });
@@ -8305,7 +8469,7 @@ var SenderShareIndex = class {
8305
8469
  peekRecordV(bytes) {
8306
8470
  try {
8307
8471
  const value = JSON.parse(new TextDecoder().decode(bytes));
8308
- const recordV = isObject2(value) && isObject2(value.hdr) ? value.hdr.recordV : void 0;
8472
+ const recordV = isObject3(value) && isObject3(value.hdr) ? value.hdr.recordV : void 0;
8309
8473
  return typeof recordV === "number" && Number.isSafeInteger(recordV) ? recordV : SUPPORTED_AGENT_RECORD_V + 1;
8310
8474
  } catch {
8311
8475
  return SUPPORTED_AGENT_RECORD_V + 1;
@@ -8391,7 +8555,7 @@ var SenderShareIndex = class {
8391
8555
  if (classified.kind === "web") {
8392
8556
  fetches++;
8393
8557
  const read3 = await this.backend.readVault(key);
8394
- if (read3.status === "ok" && isObject2(read3.value) && read3.value.provenance === "migrated") {
8558
+ if (read3.status === "ok" && isObject3(read3.value) && read3.value.provenance === "migrated") {
8395
8559
  const checked = await this.checkRecord(read3.value, { key, shareId: classified.shareId, src: "web", source: "migrated", agentDid: "", recordV: SUPPORTED_AGENT_RECORD_V }, archivedOnce);
8396
8560
  findings.push({ kind: "web", key, row: checked.kind === "admit" ? checked.row : { ...unreadableRow(this.now()), pv: "migrated" } });
8397
8561
  } else if (read3.status === "ok") {
@@ -8667,6 +8831,7 @@ var SenderShareIndex = class {
8667
8831
  agentRecordPrefix,
8668
8832
  assertLinkBoundToMaterial,
8669
8833
  attenuationCovers,
8834
+ checkAgentRecordSchema,
8670
8835
  checkRegistrationReceipt,
8671
8836
  claimRank,
8672
8837
  classifyRecordKey,
@@ -8687,6 +8852,8 @@ var SenderShareIndex = class {
8687
8852
  parseSignedUcan,
8688
8853
  parseStoredAgentRecord,
8689
8854
  project,
8855
+ registeredAtFits,
8856
+ restoreShareRecordLink,
8690
8857
  sealAgentRecord,
8691
8858
  validateShareLink,
8692
8859
  verifyAgentRecordChain,