@tinycloud/sdk-core 3.0.0-beta.0 → 3.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -64,6 +64,7 @@ __export(index_exports, {
64
64
  CREDENTIAL_CONTRACT_VERSION: () => CREDENTIAL_CONTRACT_VERSION,
65
65
  CREDENTIAL_ERROR_CODES: () => CREDENTIAL_ERROR_CODES,
66
66
  CREDENTIAL_FORMAT: () => CREDENTIAL_FORMAT,
67
+ CREDENTIAL_INVITATION_REQUEST_DOMAIN: () => CREDENTIAL_INVITATION_REQUEST_DOMAIN,
67
68
  CREDENTIAL_PROTOCOL: () => CREDENTIAL_ACQUISITION_PROTOCOL,
68
69
  CREDENTIAL_STEP_REGISTRY_VERSION: () => CREDENTIAL_STEP_REGISTRY_VERSION,
69
70
  CapabilityKeyRegistry: () => CapabilityKeyRegistry,
@@ -87,6 +88,7 @@ __export(index_exports, {
87
88
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
88
89
  DEFAULT_TINYCLOUD_FALLBACK_HOST: () => DEFAULT_TINYCLOUD_FALLBACK_HOST,
89
90
  DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL: () => DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
91
+ DELIVERY_ADMISSION_DOMAIN: () => DELIVERY_ADMISSION_DOMAIN,
90
92
  DUCKDB: () => import_bootstrap4.DUCKDB,
91
93
  DataVaultService: () => import_sdk_services8.DataVaultService,
92
94
  DatabaseHandle: () => import_sdk_services8.DatabaseHandle,
@@ -98,6 +100,7 @@ __export(index_exports, {
98
100
  DuckDbService: () => import_sdk_services8.DuckDbService,
99
101
  ED25519_JCS_SIGNATURE_SUITE: () => ED25519_JCS_SIGNATURE_SUITE,
100
102
  EIP191_JCS_SIGNATURE_SUITE: () => EIP191_JCS_SIGNATURE_SUITE,
103
+ EMAIL_CREDENTIAL_MAX_AGE_SECONDS: () => EMAIL_CREDENTIAL_MAX_AGE_SECONDS,
101
104
  ENCRYPTION: () => import_bootstrap4.ENCRYPTION,
102
105
  ENCRYPTION_MANIFEST_SPACE: () => ENCRYPTION_MANIFEST_SPACE,
103
106
  ENCRYPTION_NETWORK_URN_PREFIX: () => import_sdk_services8.ENCRYPTION_NETWORK_URN_PREFIX,
@@ -136,6 +139,8 @@ __export(index_exports, {
136
139
  NETWORK_NAME_PATTERN: () => import_sdk_services8.NETWORK_NAME_PATTERN,
137
140
  NetworkIdError: () => import_sdk_services8.NetworkIdError,
138
141
  OPENKEY_ACTION_ID_SEPARATOR: () => OPENKEY_ACTION_ID_SEPARATOR,
142
+ OWNER_NODE_BINDING_TIMEOUT_MS: () => OWNER_NODE_BINDING_TIMEOUT_MS,
143
+ OpenKeyManageKeyError: () => OpenKeyManageKeyError,
139
144
  POLICY_CAPABILITY_V1_DOMAIN: () => POLICY_CAPABILITY_V1_DOMAIN,
140
145
  POLICY_CAPABILITY_V1_SCHEMA: () => POLICY_CAPABILITY_V1_SCHEMA,
141
146
  POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE: () => POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
@@ -148,6 +153,8 @@ __export(index_exports, {
148
153
  POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA: () => POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA,
149
154
  POLICY_PRESENTATION_V3_DOMAIN: () => POLICY_PRESENTATION_V3_DOMAIN,
150
155
  POLICY_PRESENTATION_V3_SCHEMA: () => POLICY_PRESENTATION_V3_SCHEMA,
156
+ POLICY_PRESENTATION_V4_DOMAIN: () => POLICY_PRESENTATION_V4_DOMAIN,
157
+ POLICY_PRESENTATION_V4_SCHEMA: () => POLICY_PRESENTATION_V4_SCHEMA,
151
158
  POLICY_SCHEMA: () => POLICY_SCHEMA,
152
159
  POLICY_SESSION_UCAN_V1_PROFILE: () => POLICY_SESSION_UCAN_V1_PROFILE,
153
160
  POLICY_STATUS_SCHEMA: () => POLICY_STATUS_SCHEMA,
@@ -172,7 +179,6 @@ __export(index_exports, {
172
179
  SECRET_RECORDS_SCHEMA: () => import_bootstrap4.SECRET_RECORDS_SCHEMA,
173
180
  SERVICE_LONG_TO_SHORT: () => SERVICE_LONG_TO_SHORT,
174
181
  SERVICE_SHORT_TO_LONG: () => SERVICE_SHORT_TO_LONG,
175
- SHARE_DELIVERY_AUTHORIZATION_DOMAIN: () => SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
176
182
  SPACE: () => import_bootstrap4.SPACE,
177
183
  SQL: () => import_bootstrap4.SQL,
178
184
  SQLAction: () => import_sdk_services8.SQLAction,
@@ -206,7 +212,9 @@ __export(index_exports, {
206
212
  SpaceService: () => SpaceService,
207
213
  TINYCLOUD_ACCOUNT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_ACCOUNT_SPACE_MANIFEST,
208
214
  TINYCLOUD_APPLICATIONS_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_APPLICATIONS_SPACE_MANIFEST,
215
+ TINYCLOUD_CANONICAL_IDENTITY_CLAIM: () => TINYCLOUD_CANONICAL_IDENTITY_CLAIM,
209
216
  TINYCLOUD_DEFAULT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_DEFAULT_SPACE_MANIFEST,
217
+ TINYCLOUD_MANAGE_KEY_SCOPE: () => TINYCLOUD_MANAGE_KEY_SCOPE,
210
218
  TINYCLOUD_PUBLIC_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_PUBLIC_SPACE_MANIFEST,
211
219
  TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST: () => import_bootstrap4.TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
212
220
  TinyCloud: () => TinyCloud,
@@ -223,6 +231,7 @@ __export(index_exports, {
223
231
  activateSessionWithHost: () => activateSessionWithHost,
224
232
  addressStorageKey: () => addressStorageKey,
225
233
  admitPolicyCredentialV3: () => admitPolicyCredentialV3,
234
+ admitPolicyCredentialV4: () => admitPolicyCredentialV4,
226
235
  applyPrefix: () => applyPrefix,
227
236
  bootstrapEncryptionNetworkId: () => import_bootstrap4.bootstrapEncryptionNetworkId,
228
237
  bootstrapSpaceId: () => import_bootstrap4.bootstrapSpaceId,
@@ -233,6 +242,7 @@ __export(index_exports, {
233
242
  buildDecryptInvocation: () => import_sdk_services8.buildDecryptInvocation,
234
243
  buildNetworkId: () => import_sdk_services8.buildNetworkId,
235
244
  buildPolicyCredentialPresentationV3: () => buildPolicyCredentialPresentationV3,
245
+ buildPolicyCredentialPresentationV4: () => buildPolicyCredentialPresentationV4,
236
246
  buildSpaceUri: () => buildSpaceUri,
237
247
  canonicalDigest: () => canonicalDigest,
238
248
  canonicalHashHex: () => import_sdk_services8.canonicalHashHex,
@@ -263,10 +273,12 @@ __export(index_exports, {
263
273
  createAndSignSignedObject: () => createAndSignSignedObject,
264
274
  createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
265
275
  createDelegatedShareKey: () => createDelegatedShareKey,
276
+ createEmailCredentialRequirement: () => createEmailCredentialRequirement,
266
277
  createHolderBinding: () => createHolderBinding,
267
278
  createInMemoryLocalNodeIdentityStore: () => createInMemoryLocalNodeIdentityStore,
268
279
  createLocalStorageLocalNodeIdentityStore: () => createLocalStorageLocalNodeIdentityStore,
269
280
  createOpenKeyCallbackSigningStrategy: () => createOpenKeyCallbackSigningStrategy,
281
+ createOpenKeyManageKeySigningStrategy: () => createOpenKeyManageKeySigningStrategy,
270
282
  createPolicyEnforcementDelegation: () => createPolicyEnforcementDelegation,
271
283
  createShareArtifact: () => createShareArtifact,
272
284
  createShareArtifactAsync: () => createShareArtifactAsync,
@@ -325,6 +337,7 @@ __export(index_exports, {
325
337
  generateRandomReceiverKey: () => import_sdk_services8.generateRandomReceiverKey,
326
338
  getPolicyRootStatusV3: () => getPolicyRootStatusV3,
327
339
  getTinyCloudDebugLogs: () => import_sdk_services8.getTinyCloudDebugLogs,
340
+ hasTinyCloudManageKeyScope: () => hasTinyCloudManageKeyScope,
328
341
  hexDecode: () => import_sdk_services8.hexDecode,
329
342
  hexEncode: () => import_sdk_services8.hexEncode,
330
343
  holderBindingCanonicalBytes: () => holderBindingCanonicalBytes,
@@ -339,6 +352,7 @@ __export(index_exports, {
339
352
  isNetworkId: () => import_sdk_services8.isNetworkId,
340
353
  isPlausibleOpenKeyActionId: () => isPlausibleOpenKeyActionId,
341
354
  isV2ShareLink: () => isV2ShareLink,
355
+ issuedCredentialEnvelopeFromVerified: () => issuedCredentialEnvelopeFromVerified,
342
356
  jcsCanonicalize: () => jcsCanonicalize,
343
357
  loadManifest: () => loadManifest,
344
358
  locationPayloadForRecord: () => locationPayloadForRecord,
@@ -357,6 +371,8 @@ __export(index_exports, {
357
371
  openWrappedKey: () => import_sdk_services8.openWrappedKey,
358
372
  parseCanonicalNetworkId: () => parseCanonicalNetworkId,
359
373
  parseCanonicalRecapResource: () => parseCanonicalRecapResource,
374
+ parseCanonicalTinyCloudIdentity: () => parseCanonicalTinyCloudIdentity,
375
+ parseCanonicalTinyCloudIdentityClaims: () => parseCanonicalTinyCloudIdentityClaims,
360
376
  parseCompactUcanAuthorization: () => parseCompactUcanAuthorization,
361
377
  parseExpiry: () => parseExpiry,
362
378
  parseNetworkId: () => import_sdk_services8.parseNetworkId,
@@ -365,6 +381,7 @@ __export(index_exports, {
365
381
  parsePolicySessionUcan: () => parsePolicySessionUcan,
366
382
  parsePortableDelegation: () => parsePortableDelegation,
367
383
  parseRecapCapabilities: () => parseRecapCapabilities,
384
+ parseSecretCatalogKey: () => import_sdk_services8.parseSecretCatalogKey,
368
385
  parseShareArtifact: () => parseShareArtifact,
369
386
  parseShareUrl: () => parseShareUrl,
370
387
  parseSpaceUri: () => parseSpaceUri,
@@ -377,8 +394,11 @@ __export(index_exports, {
377
394
  principalDidEquals: () => principalDidEquals,
378
395
  projectUnifiedPolicyCapabilities: () => projectUnifiedPolicyCapabilities,
379
396
  projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
397
+ publishLocationRecord: () => publishLocationRecord,
380
398
  recapCaveatsEqual: () => recapCaveatsEqual,
399
+ registerPolicyV3: () => registerPolicyV3,
381
400
  renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
401
+ requestTinyCloudManageKeyScope: () => requestTinyCloudManageKeyScope,
382
402
  resolveCloudLocation: () => resolveCloudLocation,
383
403
  resolveManifest: () => resolveManifest,
384
404
  resolveManifestKnowledgeRoot: () => resolveManifestKnowledgeRoot,
@@ -394,10 +414,10 @@ __export(index_exports, {
394
414
  serviceError: () => import_sdk_services8.serviceError,
395
415
  sha256Base64Url: () => sha256Base64Url,
396
416
  shareCapabilityAllows: () => shareCapabilityAllows,
397
- shareDeliveryTrustedKid: () => shareDeliveryTrustedKid,
398
417
  signCompactUcanAuthorization: () => signCompactUcanAuthorization,
399
418
  signCompactUcanRootAuthorization: () => signCompactUcanRootAuthorization,
400
419
  signLocationRecord: () => signLocationRecord,
420
+ signPolicyCredentialPresentationV4: () => signPolicyCredentialPresentationV4,
401
421
  signShareEnvelope: () => signShareEnvelope,
402
422
  signedObjectIdFor: () => signedObjectIdFor,
403
423
  submitHostDelegation: () => submitHostDelegation,
@@ -430,13 +450,14 @@ __export(index_exports, {
430
450
  validatePolicyStatusSignedShape: () => validatePolicyStatusSignedShape,
431
451
  validatePolicyStatusUnsigned: () => validatePolicyStatusUnsigned,
432
452
  validatePolicyUnsigned: () => validatePolicyUnsigned,
433
- validateShareDeliveryAuthorizationBytes: () => validateShareDeliveryAuthorizationBytes,
453
+ validateShareDeliveryAuthorizationV3Bytes: () => validateShareDeliveryAuthorizationV3Bytes,
434
454
  validateUnifiedPolicyV2: () => validateUnifiedPolicyV2,
435
455
  verifyDecryptResponse: () => import_sdk_services8.verifyDecryptResponse,
436
456
  verifyDidKeyEd25519Signature: () => verifyDidKeyEd25519Signature,
437
457
  verifyEip191MessageSignature: () => verifyEip191MessageSignature,
438
458
  verifyIssuedCredential: () => verifyIssuedCredential,
439
459
  verifyLocationRecord: () => verifyLocationRecord,
460
+ verifyOwnerNodeBinding: () => verifyOwnerNodeBinding,
440
461
  verifyPolicy: () => verifyPolicy,
441
462
  verifyPolicyEngineRecord: () => verifyPolicyEngineRecord,
442
463
  verifyPolicyRootRevocationV3: () => verifyPolicyRootRevocationV3,
@@ -1001,6 +1022,17 @@ function isDigest(value) {
1001
1022
 
1002
1023
  // src/credentials/requirement.ts
1003
1024
  var ID2 = /^[a-z0-9][a-z0-9._-]{0,127}(?:\/v1)?$/;
1025
+ var EMAIL_CREDENTIAL_MAX_AGE_SECONDS = 3600;
1026
+ function createEmailCredentialRequirement(input) {
1027
+ return validateCredentialRequirement({
1028
+ type: "TinyCloudCredentialRequirement",
1029
+ version: 1,
1030
+ profile: input.profile,
1031
+ credentialType: input.credentialType,
1032
+ claims: { email: input.email },
1033
+ maxAgeSeconds: EMAIL_CREDENTIAL_MAX_AGE_SECONDS
1034
+ });
1035
+ }
1004
1036
  function validateCredentialRequirement(value) {
1005
1037
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential requirement is invalid");
1006
1038
  const raw = value;
@@ -2215,16 +2247,16 @@ function policyCapabilityContains(authority, request) {
2215
2247
  return caveatsContain(auth.caveats, req.caveats);
2216
2248
  }
2217
2249
  function normalizePolicyCapabilityWithOptions(input, options2) {
2218
- const object3 = expectObject(input, "$");
2219
- assertExactKeys(object3, ["service", "space", "path", "actions", "caveats"], "$");
2220
- const service = requiredString(object3, "service", "$", "policy-capability-malformed-service");
2250
+ const object4 = expectObject(input, "$");
2251
+ assertExactKeys(object4, ["service", "space", "path", "actions", "caveats"], "$");
2252
+ const service = requiredString(object4, "service", "$", "policy-capability-malformed-service");
2221
2253
  if (!CEILING_SERVICES.has(service)) {
2222
2254
  throw new PolicyCapabilityError(
2223
2255
  "policy-capability-malformed-service",
2224
2256
  "$.service is outside the frozen permissionsCeiling vocabulary"
2225
2257
  );
2226
2258
  }
2227
- const space = requiredString(object3, "space", "$", "policy-capability-malformed-space");
2259
+ const space = requiredString(object4, "space", "$", "policy-capability-malformed-space");
2228
2260
  validateConcreteSpace(space);
2229
2261
  const canonicalSpace = space.normalize("NFC");
2230
2262
  if (options2.requireCanonical && canonicalSpace !== space) {
@@ -2233,7 +2265,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
2233
2265
  "$.space must already be canonical NFC"
2234
2266
  );
2235
2267
  }
2236
- const rawPath = requiredString(object3, "path", "$", "policy-capability-malformed-path");
2268
+ const rawPath = requiredString(object4, "path", "$", "policy-capability-malformed-path");
2237
2269
  validateRawPath(rawPath, options2);
2238
2270
  const path = normalizePath(rawPath);
2239
2271
  if (options2.requireCanonical && path !== rawPath) {
@@ -2244,7 +2276,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
2244
2276
  }
2245
2277
  validateRawPath(path, options2);
2246
2278
  const actions = normalizeActions(
2247
- requiredArray(object3, "actions", "$", "policy-capability-malformed-action"),
2279
+ requiredArray(object4, "actions", "$", "policy-capability-malformed-action"),
2248
2280
  service,
2249
2281
  options2
2250
2282
  );
@@ -2253,9 +2285,9 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
2253
2285
  output.space = canonicalSpace;
2254
2286
  output.path = path;
2255
2287
  output.actions = actions;
2256
- if (hasOwn(object3, "caveats")) {
2288
+ if (hasOwn(object4, "caveats")) {
2257
2289
  output.caveats = validateCaveats(
2258
- requiredValue(object3, "caveats", "$", "policy-capability-malformed-caveats"),
2290
+ requiredValue(object4, "caveats", "$", "policy-capability-malformed-caveats"),
2259
2291
  output.service
2260
2292
  );
2261
2293
  }
@@ -2278,9 +2310,9 @@ function expectObject(input, path) {
2278
2310
  );
2279
2311
  }
2280
2312
  }
2281
- function assertExactKeys(object3, allowed, path) {
2313
+ function assertExactKeys(object4, allowed, path) {
2282
2314
  const allowedSet = new Set(allowed);
2283
- for (const key of Object.keys(object3)) {
2315
+ for (const key of Object.keys(object4)) {
2284
2316
  if (!allowedSet.has(key)) {
2285
2317
  throw new PolicyCapabilityError(
2286
2318
  key === "id" || key === "scope" ? "policy-capability-malformed" : "policy-capability-unknown-key",
@@ -2289,21 +2321,21 @@ function assertExactKeys(object3, allowed, path) {
2289
2321
  }
2290
2322
  }
2291
2323
  }
2292
- function requiredValue(object3, key, path, code3) {
2293
- if (!hasOwn(object3, key)) {
2324
+ function requiredValue(object4, key, path, code3) {
2325
+ if (!hasOwn(object4, key)) {
2294
2326
  throw new PolicyCapabilityError(code3, `${path}.${key} is required`);
2295
2327
  }
2296
- return object3[key];
2328
+ return object4[key];
2297
2329
  }
2298
- function requiredString(object3, key, path, code3) {
2299
- const value = requiredValue(object3, key, path, code3);
2330
+ function requiredString(object4, key, path, code3) {
2331
+ const value = requiredValue(object4, key, path, code3);
2300
2332
  if (typeof value !== "string" || value.length === 0) {
2301
2333
  throw new PolicyCapabilityError(code3, `${path}.${key} must be a non-empty string`);
2302
2334
  }
2303
2335
  return value;
2304
2336
  }
2305
- function requiredArray(object3, key, path, code3) {
2306
- const value = requiredValue(object3, key, path, code3);
2337
+ function requiredArray(object4, key, path, code3) {
2338
+ const value = requiredValue(object4, key, path, code3);
2307
2339
  if (!Array.isArray(value)) {
2308
2340
  throw new PolicyCapabilityError(code3, `${path}.${key} must be an array`);
2309
2341
  }
@@ -2448,11 +2480,11 @@ function validateSqlStatement(input, path) {
2448
2480
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
2449
2481
  throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
2450
2482
  }
2451
- const object3 = input;
2452
- assertCaveatKeys(object3, ["name", "sql", "fixedParams"], path);
2453
- caveatString(object3, "name", path);
2454
- caveatString(object3, "sql", path);
2455
- const fixedParams = requiredCaveat(object3, "fixedParams", path);
2483
+ const object4 = input;
2484
+ assertCaveatKeys(object4, ["name", "sql", "fixedParams"], path);
2485
+ caveatString(object4, "name", path);
2486
+ caveatString(object4, "sql", path);
2487
+ const fixedParams = requiredCaveat(object4, "fixedParams", path);
2456
2488
  if (!Array.isArray(fixedParams)) {
2457
2489
  throw new PolicyCapabilityError(
2458
2490
  "policy-capability-malformed-caveats",
@@ -2467,20 +2499,20 @@ function validateFixedParam(input, path) {
2467
2499
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
2468
2500
  throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
2469
2501
  }
2470
- const object3 = input;
2471
- assertCaveatKeys(object3, ["index", "value"], path);
2472
- const index = requiredCaveat(object3, "index", path);
2502
+ const object4 = input;
2503
+ assertCaveatKeys(object4, ["index", "value"], path);
2504
+ const index = requiredCaveat(object4, "index", path);
2473
2505
  if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
2474
2506
  throw new PolicyCapabilityError(
2475
2507
  "policy-capability-malformed-caveats",
2476
2508
  `${path}.index must be a non-negative integer`
2477
2509
  );
2478
2510
  }
2479
- requiredCaveat(object3, "value", path);
2511
+ requiredCaveat(object4, "value", path);
2480
2512
  }
2481
- function assertCaveatKeys(object3, allowed, path) {
2513
+ function assertCaveatKeys(object4, allowed, path) {
2482
2514
  const allowedSet = new Set(allowed);
2483
- for (const key of Object.keys(object3)) {
2515
+ for (const key of Object.keys(object4)) {
2484
2516
  if (!allowedSet.has(key)) {
2485
2517
  throw new PolicyCapabilityError(
2486
2518
  "policy-capability-malformed-caveats",
@@ -2489,17 +2521,17 @@ function assertCaveatKeys(object3, allowed, path) {
2489
2521
  }
2490
2522
  }
2491
2523
  }
2492
- function requiredCaveat(object3, key, path) {
2493
- if (!hasOwn(object3, key)) {
2524
+ function requiredCaveat(object4, key, path) {
2525
+ if (!hasOwn(object4, key)) {
2494
2526
  throw new PolicyCapabilityError(
2495
2527
  "policy-capability-malformed-caveats",
2496
2528
  `${path}.${key} is required`
2497
2529
  );
2498
2530
  }
2499
- return object3[key];
2531
+ return object4[key];
2500
2532
  }
2501
- function caveatString(object3, key, path) {
2502
- const value = requiredCaveat(object3, key, path);
2533
+ function caveatString(object4, key, path) {
2534
+ const value = requiredCaveat(object4, key, path);
2503
2535
  if (typeof value !== "string" || value.length === 0) {
2504
2536
  throw new PolicyCapabilityError(
2505
2537
  "policy-capability-malformed-caveats",
@@ -2530,8 +2562,8 @@ function caveatsContain(authority, request) {
2530
2562
  (statement) => authorityStatements.some((candidate) => jcsCanonicalize(candidate) === jcsCanonicalize(statement))
2531
2563
  );
2532
2564
  }
2533
- function hasOwn(object3, key) {
2534
- return objectHasOwn2(object3, key);
2565
+ function hasOwn(object4, key) {
2566
+ return objectHasOwn2(object4, key);
2535
2567
  }
2536
2568
 
2537
2569
  // src/policy/signed-object.ts
@@ -2859,17 +2891,17 @@ async function createAndSignSignedObject(input, signer) {
2859
2891
  }
2860
2892
  function createAndSignPolicy(input, signer) {
2861
2893
  return createAndSignSignedObject(input, signer).then(
2862
- (object3) => validatePolicySignedShape(object3)
2894
+ (object4) => validatePolicySignedShape(object4)
2863
2895
  );
2864
2896
  }
2865
2897
  function createAndSignPolicyStatus(input, signer) {
2866
2898
  return createAndSignSignedObject(input, signer).then(
2867
- (object3) => validatePolicyStatusSignedShape(object3)
2899
+ (object4) => validatePolicyStatusSignedShape(object4)
2868
2900
  );
2869
2901
  }
2870
2902
  function createAndSignPolicyEngineRecord(input, signer) {
2871
2903
  return createAndSignSignedObject(input, signer).then(
2872
- (object3) => validatePolicyEngineRecordSignedShape(object3)
2904
+ (object4) => validatePolicyEngineRecordSignedShape(object4)
2873
2905
  );
2874
2906
  }
2875
2907
  async function verifySignedObject(input) {
@@ -2973,9 +3005,9 @@ function validateUnsignedForDescriptor(input, descriptor) {
2973
3005
  }
2974
3006
  }
2975
3007
  function validatePolicyShape(input, signed) {
2976
- const object3 = expectJsonObject(normalizeJson(input), "$");
3008
+ const object4 = expectJsonObject(normalizeJson(input), "$");
2977
3009
  assertExactKeys2(
2978
- object3,
3010
+ object4,
2979
3011
  signed ? [
2980
3012
  "schema",
2981
3013
  "policyId",
@@ -3003,30 +3035,30 @@ function validatePolicyShape(input, signed) {
3003
3035
  ],
3004
3036
  "$"
3005
3037
  );
3006
- expectConst(requiredString2(object3, "schema", "$"), POLICY_SCHEMA, "$.schema");
3038
+ expectConst(requiredString2(object4, "schema", "$"), POLICY_SCHEMA, "$.schema");
3007
3039
  if (signed) {
3008
- requiredString2(object3, "policyId", "$");
3009
- validateSignature(requiredValue2(object3, "signature", "$"));
3040
+ requiredString2(object4, "policyId", "$");
3041
+ validateSignature(requiredValue2(object4, "signature", "$"));
3010
3042
  }
3011
- requiredString2(object3, "ownerDid", "$");
3012
- requiredString2(object3, "signingKeyDid", "$");
3013
- requiredDateString(object3, "createdAt", "$");
3014
- optionalDateString(object3, "expiresAt", "$");
3015
- validatePolicyResource(requiredValue2(object3, "resource", "$"), "$.resource");
3016
- validateExpression(requiredValue2(object3, "when", "$"), "$.when");
3017
- validateGrant(requiredValue2(object3, "grant", "$"), "$.grant");
3018
- if (hasOwn2(object3, "disclosure")) {
3019
- validateDisclosure(requiredValue2(object3, "disclosure", "$"), "$.disclosure");
3043
+ requiredString2(object4, "ownerDid", "$");
3044
+ requiredString2(object4, "signingKeyDid", "$");
3045
+ requiredDateString(object4, "createdAt", "$");
3046
+ optionalDateString(object4, "expiresAt", "$");
3047
+ validatePolicyResource(requiredValue2(object4, "resource", "$"), "$.resource");
3048
+ validateExpression(requiredValue2(object4, "when", "$"), "$.when");
3049
+ validateGrant(requiredValue2(object4, "grant", "$"), "$.grant");
3050
+ if (hasOwn2(object4, "disclosure")) {
3051
+ validateDisclosure(requiredValue2(object4, "disclosure", "$"), "$.disclosure");
3020
3052
  }
3021
- if (hasOwn2(object3, "audit")) {
3022
- validateAudit(requiredValue2(object3, "audit", "$"), "$.audit");
3053
+ if (hasOwn2(object4, "audit")) {
3054
+ validateAudit(requiredValue2(object4, "audit", "$"), "$.audit");
3023
3055
  }
3024
- return object3;
3056
+ return object4;
3025
3057
  }
3026
3058
  function validatePolicyStatusShape(input, signed) {
3027
- const object3 = expectJsonObject(normalizeJson(input), "$");
3059
+ const object4 = expectJsonObject(normalizeJson(input), "$");
3028
3060
  assertExactKeys2(
3029
- object3,
3061
+ object4,
3030
3062
  signed ? [
3031
3063
  "schema",
3032
3064
  "statusId",
@@ -3050,28 +3082,28 @@ function validatePolicyStatusShape(input, signed) {
3050
3082
  ],
3051
3083
  "$"
3052
3084
  );
3053
- expectConst(requiredString2(object3, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
3085
+ expectConst(requiredString2(object4, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
3054
3086
  if (signed) {
3055
- requiredString2(object3, "statusId", "$");
3056
- validateSignature(requiredValue2(object3, "signature", "$"));
3087
+ requiredString2(object4, "statusId", "$");
3088
+ validateSignature(requiredValue2(object4, "signature", "$"));
3057
3089
  }
3058
- requiredString2(object3, "policyId", "$");
3059
- requiredString2(object3, "ownerDid", "$");
3060
- requiredInteger(object3, "sequence", "$", 0);
3061
- expectOneOf(requiredString2(object3, "disposition", "$"), [
3090
+ requiredString2(object4, "policyId", "$");
3091
+ requiredString2(object4, "ownerDid", "$");
3092
+ requiredInteger(object4, "sequence", "$", 0);
3093
+ expectOneOf(requiredString2(object4, "disposition", "$"), [
3062
3094
  "active",
3063
3095
  "suspended",
3064
3096
  "revoked"
3065
3097
  ], "$.disposition");
3066
- requiredDateString(object3, "effectiveAt", "$");
3067
- optionalString(object3, "reasonCode", "$");
3068
- requiredString2(object3, "signingKeyDid", "$");
3069
- return object3;
3098
+ requiredDateString(object4, "effectiveAt", "$");
3099
+ optionalString(object4, "reasonCode", "$");
3100
+ requiredString2(object4, "signingKeyDid", "$");
3101
+ return object4;
3070
3102
  }
3071
3103
  function validatePolicyEngineRecordShape(input, signed) {
3072
- const object3 = expectJsonObject(normalizeJson(input), "$");
3104
+ const object4 = expectJsonObject(normalizeJson(input), "$");
3073
3105
  assertExactKeys2(
3074
- object3,
3106
+ object4,
3075
3107
  signed ? [
3076
3108
  "schema",
3077
3109
  "engineRecordId",
@@ -3096,44 +3128,44 @@ function validatePolicyEngineRecordShape(input, signed) {
3096
3128
  "$"
3097
3129
  );
3098
3130
  expectConst(
3099
- requiredString2(object3, "schema", "$"),
3131
+ requiredString2(object4, "schema", "$"),
3100
3132
  POLICY_ENGINE_RECORD_SCHEMA,
3101
3133
  "$.schema"
3102
3134
  );
3103
3135
  if (signed) {
3104
- requiredString2(object3, "engineRecordId", "$");
3105
- validateSignature(requiredValue2(object3, "signature", "$"));
3136
+ requiredString2(object4, "engineRecordId", "$");
3137
+ validateSignature(requiredValue2(object4, "signature", "$"));
3106
3138
  }
3107
- requiredString2(object3, "ownerDid", "$");
3108
- requiredString2(object3, "endpoint", "$");
3109
- requiredString2(object3, "audience", "$");
3139
+ requiredString2(object4, "ownerDid", "$");
3140
+ requiredString2(object4, "endpoint", "$");
3141
+ requiredString2(object4, "audience", "$");
3110
3142
  requiredStringArray(
3111
- object3,
3143
+ object4,
3112
3144
  "supportedPolicyVersions",
3113
3145
  "$",
3114
3146
  (value, path) => expectConst(value, "v0", path)
3115
3147
  );
3116
- requiredStringArray(object3, "supportedEvidenceVerifiers", "$");
3117
- requiredString2(object3, "grantIssuerDid", "$");
3118
- requiredDateString(object3, "expiresAt", "$");
3119
- return object3;
3148
+ requiredStringArray(object4, "supportedEvidenceVerifiers", "$");
3149
+ requiredString2(object4, "grantIssuerDid", "$");
3150
+ requiredDateString(object4, "expiresAt", "$");
3151
+ return object4;
3120
3152
  }
3121
3153
  function validateSignature(input) {
3122
- const object3 = expectJsonObject(input, "$.signature");
3123
- assertExactKeys2(object3, ["suite", "signerDid", "value"], "$.signature");
3124
- const suite = requiredString2(object3, "suite", "$.signature");
3154
+ const object4 = expectJsonObject(input, "$.signature");
3155
+ assertExactKeys2(object4, ["suite", "signerDid", "value"], "$.signature");
3156
+ const suite = requiredString2(object4, "suite", "$.signature");
3125
3157
  assertSupportedSignatureSuite(suite);
3126
- const signerDid = requiredString2(object3, "signerDid", "$.signature");
3127
- const value = requiredString2(object3, "value", "$.signature");
3158
+ const signerDid = requiredString2(object4, "signerDid", "$.signature");
3159
+ const value = requiredString2(object4, "value", "$.signature");
3128
3160
  decodeSignatureValue(value, suite);
3129
3161
  return { suite, signerDid, value };
3130
3162
  }
3131
3163
  function validatePolicyResource(input, path) {
3132
- const object3 = expectJsonObject(input, path);
3133
- assertExactKeys2(object3, ["resourceType", "resourceId", "permissionsCeiling"], path);
3134
- requiredString2(object3, "resourceType", path);
3135
- requiredString2(object3, "resourceId", path);
3136
- const ceiling = requiredArray2(object3, "permissionsCeiling", path, 1);
3164
+ const object4 = expectJsonObject(input, path);
3165
+ assertExactKeys2(object4, ["resourceType", "resourceId", "permissionsCeiling"], path);
3166
+ requiredString2(object4, "resourceType", path);
3167
+ requiredString2(object4, "resourceId", path);
3168
+ const ceiling = requiredArray2(object4, "permissionsCeiling", path, 1);
3137
3169
  for (let index = 0; index < ceiling.length; index++) {
3138
3170
  validatePolicyCapability(ceiling[index], `${path}.permissionsCeiling[${index}]`);
3139
3171
  }
@@ -3149,10 +3181,10 @@ function validatePolicyPermissionsCeilingForSigning(input) {
3149
3181
  }
3150
3182
  }
3151
3183
  function validatePolicyCapability(input, path) {
3152
- const object3 = expectJsonObject(input, path);
3184
+ const object4 = expectJsonObject(input, path);
3153
3185
  try {
3154
- const canonical = canonicalizePolicyCapability(object3);
3155
- if (jcsCanonicalize(object3) !== jcsCanonicalize(canonical)) {
3186
+ const canonical = canonicalizePolicyCapability(object4);
3187
+ if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
3156
3188
  throw new SignedObjectSchemaError(`${path} must be canonical PolicyCapability JSON`);
3157
3189
  }
3158
3190
  } catch (error) {
@@ -3165,10 +3197,10 @@ function validatePolicyCapability(input, path) {
3165
3197
  }
3166
3198
  }
3167
3199
  function validatePolicyCapabilityForSigning(input, path) {
3168
- const object3 = expectJsonObject(input, path);
3200
+ const object4 = expectJsonObject(input, path);
3169
3201
  try {
3170
- const canonical = normalizePolicyCapability(object3);
3171
- if (jcsCanonicalize(object3) !== jcsCanonicalize(canonical)) {
3202
+ const canonical = normalizePolicyCapability(object4);
3203
+ if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
3172
3204
  throw new SignedObjectSchemaError(`${path} must be strict canonical PolicyCapability JSON`);
3173
3205
  }
3174
3206
  } catch (error) {
@@ -3181,43 +3213,43 @@ function validatePolicyCapabilityForSigning(input, path) {
3181
3213
  }
3182
3214
  }
3183
3215
  function validateExpression(input, path) {
3184
- const object3 = expectJsonObject(input, path);
3185
- const keys = Object.keys(object3);
3216
+ const object4 = expectJsonObject(input, path);
3217
+ const keys = Object.keys(object4);
3186
3218
  if (keys.length !== 1) {
3187
3219
  throw new SignedObjectSchemaError(`${path} must have exactly one expression key`);
3188
3220
  }
3189
3221
  const key = keys[0];
3190
3222
  if (key === "allOf" || key === "anyOf") {
3191
- const values = requiredArray2(object3, key, path, 1);
3223
+ const values = requiredArray2(object4, key, path, 1);
3192
3224
  for (let index = 0; index < values.length; index++) {
3193
3225
  validateExpression(values[index], `${path}.${key}[${index}]`);
3194
3226
  }
3195
3227
  return;
3196
3228
  }
3197
3229
  if (key === "subject") {
3198
- const subject = expectJsonObject(requiredValue2(object3, "subject", path), `${path}.subject`);
3230
+ const subject = expectJsonObject(requiredValue2(object4, "subject", path), `${path}.subject`);
3199
3231
  assertExactKeys2(subject, ["did"], `${path}.subject`);
3200
3232
  requiredString2(subject, "did", `${path}.subject`);
3201
3233
  return;
3202
3234
  }
3203
3235
  if (key === "evidence") {
3204
- validateEvidenceRequirement(requiredValue2(object3, "evidence", path), `${path}.evidence`);
3236
+ validateEvidenceRequirement(requiredValue2(object4, "evidence", path), `${path}.evidence`);
3205
3237
  return;
3206
3238
  }
3207
3239
  throw new SignedObjectSchemaError(`${path} has unknown expression key ${key}`);
3208
3240
  }
3209
3241
  function validateEvidenceRequirement(input, path) {
3210
- const object3 = expectJsonObject(input, path);
3242
+ const object4 = expectJsonObject(input, path);
3211
3243
  assertExactKeys2(
3212
- object3,
3244
+ object4,
3213
3245
  ["requirementId", "verifier", "requirements", "authority", "freshness"],
3214
3246
  path
3215
3247
  );
3216
- requiredString2(object3, "requirementId", path);
3217
- requiredString2(object3, "verifier", path);
3218
- requiredValue2(object3, "requirements", path);
3219
- if (hasOwn2(object3, "authority")) {
3220
- const authority = expectJsonObject(requiredValue2(object3, "authority", path), `${path}.authority`);
3248
+ requiredString2(object4, "requirementId", path);
3249
+ requiredString2(object4, "verifier", path);
3250
+ requiredValue2(object4, "requirements", path);
3251
+ if (hasOwn2(object4, "authority")) {
3252
+ const authority = expectJsonObject(requiredValue2(object4, "authority", path), `${path}.authority`);
3221
3253
  assertExactKeys2(
3222
3254
  authority,
3223
3255
  ["profile", "acceptedIssuers", "allowOwnerAuthorizedIssuer"],
@@ -3229,35 +3261,35 @@ function validateEvidenceRequirement(input, path) {
3229
3261
  }
3230
3262
  optionalBoolean(authority, "allowOwnerAuthorizedIssuer", `${path}.authority`);
3231
3263
  }
3232
- if (hasOwn2(object3, "freshness")) {
3233
- const freshness = expectJsonObject(requiredValue2(object3, "freshness", path), `${path}.freshness`);
3264
+ if (hasOwn2(object4, "freshness")) {
3265
+ const freshness = expectJsonObject(requiredValue2(object4, "freshness", path), `${path}.freshness`);
3234
3266
  assertExactKeys2(freshness, ["maxStatusAgeSeconds"], `${path}.freshness`);
3235
3267
  requiredInteger(freshness, "maxStatusAgeSeconds", `${path}.freshness`, 0);
3236
3268
  }
3237
3269
  }
3238
3270
  function validateGrant(input, path) {
3239
- const object3 = expectJsonObject(input, path);
3240
- assertExactKeys2(object3, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
3241
- expectConst(requiredString2(object3, "output", path), "portable-delegation", `${path}.output`);
3242
- requiredInteger(object3, "maxTtlSeconds", path, 1);
3243
- expectOneOf(requiredString2(object3, "delegationMode", path), [
3271
+ const object4 = expectJsonObject(input, path);
3272
+ assertExactKeys2(object4, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
3273
+ expectConst(requiredString2(object4, "output", path), "portable-delegation", `${path}.output`);
3274
+ requiredInteger(object4, "maxTtlSeconds", path, 1);
3275
+ expectOneOf(requiredString2(object4, "delegationMode", path), [
3244
3276
  "terminal",
3245
3277
  "attenuable"
3246
3278
  ], `${path}.delegationMode`);
3247
- expectOneOf(requiredString2(object3, "revocation", path), [
3279
+ expectOneOf(requiredString2(object4, "revocation", path), [
3248
3280
  "refresh_only",
3249
3281
  "active_cutoff"
3250
3282
  ], `${path}.revocation`);
3251
3283
  }
3252
3284
  function validateDisclosure(input, path) {
3253
- const object3 = expectJsonObject(input, path);
3254
- assertExactKeys2(object3, ["denial"], path);
3255
- expectOneOf(requiredString2(object3, "denial", path), ["none", "code", "debug"], `${path}.denial`);
3285
+ const object4 = expectJsonObject(input, path);
3286
+ assertExactKeys2(object4, ["denial"], path);
3287
+ expectOneOf(requiredString2(object4, "denial", path), ["none", "code", "debug"], `${path}.denial`);
3256
3288
  }
3257
3289
  function validateAudit(input, path) {
3258
- const object3 = expectJsonObject(input, path);
3259
- assertExactKeys2(object3, ["issuance"], path);
3260
- expectOneOf(requiredString2(object3, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
3290
+ const object4 = expectJsonObject(input, path);
3291
+ assertExactKeys2(object4, ["issuance"], path);
3292
+ expectOneOf(requiredString2(object4, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
3261
3293
  }
3262
3294
  function materialForUnsigned(unsigned, descriptor) {
3263
3295
  const jcs = jcsCanonicalize(unsigned);
@@ -3406,9 +3438,9 @@ function encodeSignatureValue(value, suite) {
3406
3438
  decodeSignatureValue(encoded, suite);
3407
3439
  return encoded;
3408
3440
  }
3409
- function stripOwnIdAndSignature(object3, descriptor) {
3441
+ function stripOwnIdAndSignature(object4, descriptor) {
3410
3442
  const output = /* @__PURE__ */ Object.create(null);
3411
- for (const [key, value] of Object.entries(object3)) {
3443
+ for (const [key, value] of Object.entries(object4)) {
3412
3444
  if (key === descriptor.idField || key === "signature") {
3413
3445
  continue;
3414
3446
  }
@@ -3417,8 +3449,8 @@ function stripOwnIdAndSignature(object3, descriptor) {
3417
3449
  return output;
3418
3450
  }
3419
3451
  function descriptorForUnsigned(input) {
3420
- const object3 = expectJsonObject(normalizeJson(input), "$");
3421
- return descriptorForSchema(requiredString2(object3, "schema", "$"));
3452
+ const object4 = expectJsonObject(normalizeJson(input), "$");
3453
+ return descriptorForSchema(requiredString2(object4, "schema", "$"));
3422
3454
  }
3423
3455
  function descriptorForSchema(schema) {
3424
3456
  if (schema === POLICY_SCHEMA) return DESCRIPTORS.Policy;
@@ -3432,28 +3464,28 @@ function expectJsonObject(input, path) {
3432
3464
  }
3433
3465
  return input;
3434
3466
  }
3435
- function assertExactKeys2(object3, allowed, path) {
3467
+ function assertExactKeys2(object4, allowed, path) {
3436
3468
  const allowedSet = new Set(allowed);
3437
- for (const key of Object.keys(object3)) {
3469
+ for (const key of Object.keys(object4)) {
3438
3470
  if (!allowedSet.has(key)) {
3439
3471
  throw new SignedObjectSchemaError(`${path} has unknown field ${key}`);
3440
3472
  }
3441
3473
  }
3442
3474
  }
3443
- function requiredValue2(object3, key, path) {
3444
- if (!hasOwn2(object3, key)) {
3475
+ function requiredValue2(object4, key, path) {
3476
+ if (!hasOwn2(object4, key)) {
3445
3477
  throw new SignedObjectSchemaError(`${path}.${key} is required`);
3446
3478
  }
3447
- return object3[key];
3479
+ return object4[key];
3448
3480
  }
3449
- function requiredString2(object3, key, path) {
3450
- return requireStringType(requiredValue2(object3, key, path), `${path}.${key}`);
3481
+ function requiredString2(object4, key, path) {
3482
+ return requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
3451
3483
  }
3452
- function optionalString(object3, key, path) {
3453
- if (!hasOwn2(object3, key)) {
3484
+ function optionalString(object4, key, path) {
3485
+ if (!hasOwn2(object4, key)) {
3454
3486
  return;
3455
3487
  }
3456
- requireStringType(requiredValue2(object3, key, path), `${path}.${key}`);
3488
+ requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
3457
3489
  }
3458
3490
  function requireStringType(value, path) {
3459
3491
  if (typeof value !== "string" || value.length === 0) {
@@ -3461,16 +3493,16 @@ function requireStringType(value, path) {
3461
3493
  }
3462
3494
  return value;
3463
3495
  }
3464
- function optionalBoolean(object3, key, path) {
3465
- if (!hasOwn2(object3, key)) {
3496
+ function optionalBoolean(object4, key, path) {
3497
+ if (!hasOwn2(object4, key)) {
3466
3498
  return;
3467
3499
  }
3468
- if (typeof requiredValue2(object3, key, path) !== "boolean") {
3500
+ if (typeof requiredValue2(object4, key, path) !== "boolean") {
3469
3501
  throw new SignedObjectSchemaError(`${path}.${key} must be a boolean`);
3470
3502
  }
3471
3503
  }
3472
- function requiredInteger(object3, key, path, minimum) {
3473
- const value = requiredValue2(object3, key, path);
3504
+ function requiredInteger(object4, key, path, minimum) {
3505
+ const value = requiredValue2(object4, key, path);
3474
3506
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) {
3475
3507
  throw new SignedObjectSchemaError(
3476
3508
  `${path}.${key} must be an integer >= ${minimum}`
@@ -3478,8 +3510,8 @@ function requiredInteger(object3, key, path, minimum) {
3478
3510
  }
3479
3511
  return value;
3480
3512
  }
3481
- function requiredArray2(object3, key, path, minimum) {
3482
- const value = requiredValue2(object3, key, path);
3513
+ function requiredArray2(object4, key, path, minimum) {
3514
+ const value = requiredValue2(object4, key, path);
3483
3515
  if (!Array.isArray(value) || value.length < minimum) {
3484
3516
  throw new SignedObjectSchemaError(
3485
3517
  `${path}.${key} must be an array with at least ${minimum} item(s)`
@@ -3487,22 +3519,22 @@ function requiredArray2(object3, key, path, minimum) {
3487
3519
  }
3488
3520
  return value;
3489
3521
  }
3490
- function requiredStringArray(object3, key, path, check) {
3491
- const values = requiredArray2(object3, key, path, 0);
3522
+ function requiredStringArray(object4, key, path, check) {
3523
+ const values = requiredArray2(object4, key, path, 0);
3492
3524
  for (let index = 0; index < values.length; index++) {
3493
3525
  const itemPath = `${path}.${key}[${index}]`;
3494
3526
  const value = requireStringType(values[index], itemPath);
3495
3527
  check?.(value, itemPath);
3496
3528
  }
3497
3529
  }
3498
- function requiredDateString(object3, key, path) {
3499
- assertRfc3339(requiredString2(object3, key, path), `${path}.${key}`);
3530
+ function requiredDateString(object4, key, path) {
3531
+ assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
3500
3532
  }
3501
- function optionalDateString(object3, key, path) {
3502
- if (!hasOwn2(object3, key)) {
3533
+ function optionalDateString(object4, key, path) {
3534
+ if (!hasOwn2(object4, key)) {
3503
3535
  return;
3504
3536
  }
3505
- assertRfc3339(requiredString2(object3, key, path), `${path}.${key}`);
3537
+ assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
3506
3538
  }
3507
3539
  function assertRfc3339(value, path) {
3508
3540
  const match = value.match(
@@ -3534,8 +3566,8 @@ function expectOneOf(actual, allowed, path) {
3534
3566
  }
3535
3567
  throw new SignedObjectSchemaError(`${path} has unsupported value ${actual}`);
3536
3568
  }
3537
- function hasOwn2(object3, key) {
3538
- return objectHasOwn3(object3, key);
3569
+ function hasOwn2(object4, key) {
3570
+ return objectHasOwn3(object4, key);
3539
3571
  }
3540
3572
  function assertSupportedSignatureSuite(suite) {
3541
3573
  if (suite !== ED25519_JCS_SIGNATURE_SUITE && suite !== EIP191_JCS_SIGNATURE_SUITE) {
@@ -3762,9 +3794,9 @@ function expectObject2(input, path, code3) {
3762
3794
  );
3763
3795
  }
3764
3796
  }
3765
- function assertExactKeys3(object3, allowed, path) {
3797
+ function assertExactKeys3(object4, allowed, path) {
3766
3798
  const allowedSet = new Set(allowed);
3767
- for (const key of Object.keys(object3)) {
3799
+ for (const key of Object.keys(object4)) {
3768
3800
  if (!allowedSet.has(key)) {
3769
3801
  throw new PolicyAuthoringError(
3770
3802
  "policy-authoring-unknown-key",
@@ -3773,17 +3805,17 @@ function assertExactKeys3(object3, allowed, path) {
3773
3805
  }
3774
3806
  }
3775
3807
  }
3776
- function requiredValue3(object3, key, path) {
3777
- if (!hasOwn3(object3, key)) {
3808
+ function requiredValue3(object4, key, path) {
3809
+ if (!hasOwn3(object4, key)) {
3778
3810
  throw new PolicyAuthoringError(
3779
3811
  "policy-authoring-malformed",
3780
3812
  `${path}.${key} is required`
3781
3813
  );
3782
3814
  }
3783
- return object3[key];
3815
+ return object4[key];
3784
3816
  }
3785
- function requiredString3(object3, key, path) {
3786
- const value = requiredValue3(object3, key, path);
3817
+ function requiredString3(object4, key, path) {
3818
+ const value = requiredValue3(object4, key, path);
3787
3819
  if (typeof value !== "string" || value.length === 0) {
3788
3820
  throw new PolicyAuthoringError(
3789
3821
  "policy-authoring-malformed",
@@ -3792,11 +3824,11 @@ function requiredString3(object3, key, path) {
3792
3824
  }
3793
3825
  return value;
3794
3826
  }
3795
- function fieldString(object3, key, path, code3) {
3796
- if (!hasOwn3(object3, key)) {
3827
+ function fieldString(object4, key, path, code3) {
3828
+ if (!hasOwn3(object4, key)) {
3797
3829
  throw new PolicyAuthoringError(code3, `${path}.${key} is required`);
3798
3830
  }
3799
- const value = object3[key];
3831
+ const value = object4[key];
3800
3832
  if (typeof value !== "string" || value.length === 0) {
3801
3833
  throw new PolicyAuthoringError(
3802
3834
  code3,
@@ -3834,8 +3866,8 @@ function parseStrictRfc3339(value, path) {
3834
3866
  var objectHasOwn4 = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
3835
3867
  Object.prototype.hasOwnProperty
3836
3868
  );
3837
- function hasOwn3(object3, key) {
3838
- return objectHasOwn4(object3, key);
3869
+ function hasOwn3(object4, key) {
3870
+ return objectHasOwn4(object4, key);
3839
3871
  }
3840
3872
 
3841
3873
  // src/policy/unified.ts
@@ -3862,9 +3894,11 @@ var MAX_LEGACY_ENVELOPE_EXPIRES_AT = "2026-12-29T00:00:00Z";
3862
3894
  var LAST_V2_READ_AT = "2027-01-05T00:00:00Z";
3863
3895
  async function requestPolicyChallengeV3(input) {
3864
3896
  const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
3865
- const response = await fetchFn(new URL("/share/v3/policy/challenges", input.nodeOrigin), {
3897
+ const policyRuntimePath = policyRuntimeBasePath(input.policyRuntimePath);
3898
+ const response = await fetchFn(new URL(`${policyRuntimePath}/challenges`, input.nodeOrigin), {
3866
3899
  method: "POST",
3867
3900
  redirect: "error",
3901
+ signal: input.signal,
3868
3902
  headers: { accept: "application/json", "content-type": "application/json" },
3869
3903
  body: JSON.stringify({ policyCid: input.policyCid, recipientDid: input.recipientDid, requestedCapabilities: input.requestedCapabilities.map(normalizeUnifiedPolicyCapability) })
3870
3904
  });
@@ -3874,6 +3908,13 @@ async function requestPolicyChallengeV3(input) {
3874
3908
  throw new Error("policy challenge binding is invalid");
3875
3909
  return value;
3876
3910
  }
3911
+ function policyRuntimeBasePath(value) {
3912
+ const path = value ?? "/policy/v3";
3913
+ if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
3914
+ throw new Error("policy runtime path is invalid");
3915
+ }
3916
+ return path;
3917
+ }
3877
3918
  function compactAttenuationForPolicyCapabilities(input) {
3878
3919
  const attenuation = {};
3879
3920
  for (const raw of input) {
@@ -3919,7 +3960,7 @@ function verifyPolicyRootStatusCheckpointV3(input) {
3919
3960
  }
3920
3961
  async function getPolicyRootStatusV3(input) {
3921
3962
  const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
3922
- const response = await fetchFn(new URL(`/share/v3/policy/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
3963
+ const response = await fetchFn(new URL(`/policy/v3/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
3923
3964
  method: "GET",
3924
3965
  redirect: "error",
3925
3966
  headers: { accept: "application/json" }
@@ -3978,7 +4019,7 @@ async function renewPolicyRootStatusV3(input) {
3978
4019
  };
3979
4020
  const signature = await input.sign((0, import_sha256.sha256)(new TextEncoder().encode(ROOT_STATUS_RENEWAL_V1_DOMAIN + jcsCanonicalize(unsigned))));
3980
4021
  if (signature.length !== 64) throw new Error("policy root renewal signature must be Ed25519");
3981
- const response = await fetchFn(new URL("/share/v3/policy/status", input.nodeOrigin), {
4022
+ const response = await fetchFn(new URL("/policy/v3/status", input.nodeOrigin), {
3982
4023
  method: "POST",
3983
4024
  redirect: "error",
3984
4025
  headers: { accept: "application/json", "content-type": "application/json" },
@@ -4034,6 +4075,11 @@ var POLICY_SESSION_FACT_KEYS = [
4034
4075
  "issuanceAuditDigestHex",
4035
4076
  "remainingRedelegationDepth"
4036
4077
  ];
4078
+ var POLICY_SESSION_V4_AUDIT_FACT_KEYS = [
4079
+ "credentialIdAuditDigestHex",
4080
+ "presentationJtiAuditDigestHex"
4081
+ ];
4082
+ var LOWER_SHA256_HEX = /^[0-9a-f]{64}$/;
4037
4083
  var KV_ACTIONS = /* @__PURE__ */ new Set([
4038
4084
  "tinycloud.kv/get",
4039
4085
  "tinycloud.kv/list",
@@ -4228,6 +4274,133 @@ function contentSourceDigestHex(input) {
4228
4274
  )
4229
4275
  );
4230
4276
  }
4277
+ function exactObject(value, keys, label) {
4278
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
4279
+ throw new Error(`${label} is invalid`);
4280
+ }
4281
+ const record2 = value;
4282
+ if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) {
4283
+ throw new Error(`${label} is invalid`);
4284
+ }
4285
+ return record2;
4286
+ }
4287
+ function didKeyEd25519PublicKey(value) {
4288
+ const principal = value.split("#", 1)[0];
4289
+ if (!principal.startsWith("did:key:")) {
4290
+ throw new Error("policy enforcer binding signer is invalid");
4291
+ }
4292
+ const material = base58btc.decode(principal.slice("did:key:".length));
4293
+ if (material.length !== 34 || material[0] !== 237 || material[1] !== 1) {
4294
+ throw new Error("policy enforcer binding signer is invalid");
4295
+ }
4296
+ return material.slice(2);
4297
+ }
4298
+ async function registerPolicyV3(input) {
4299
+ const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
4300
+ const rootExpiresAt = new Date(input.rootExpiresAt);
4301
+ const canonicalRootExpiresAt = new Date(
4302
+ Math.floor(rootExpiresAt.getTime() / 1e3) * 1e3
4303
+ ).toISOString().replace(/\.000Z$/, "Z");
4304
+ if (!Number.isFinite(rootExpiresAt.getTime()) || canonicalRootExpiresAt !== input.rootExpiresAt || rootExpiresAt.getTime() <= Date.now()) {
4305
+ throw new Error("policy root expiry is invalid");
4306
+ }
4307
+ const bindingResponse = await fetchFn(
4308
+ new URL("/policy/v3/enforcer-bindings", input.nodeOrigin),
4309
+ {
4310
+ method: "POST",
4311
+ redirect: "error",
4312
+ signal: input.signal,
4313
+ headers: { accept: "application/json", "content-type": "application/json" },
4314
+ body: JSON.stringify({
4315
+ rootExpiresAt: input.rootExpiresAt,
4316
+ enforcerDid: input.enforcerDid
4317
+ })
4318
+ }
4319
+ );
4320
+ if (!bindingResponse.ok) {
4321
+ throw new Error(`policy enforcer binding rejected (${bindingResponse.status})`);
4322
+ }
4323
+ const binding = exactObject(
4324
+ await bindingResponse.json(),
4325
+ [
4326
+ "schema",
4327
+ "enforcerDid",
4328
+ "nodeAudience",
4329
+ "attestationBindingDigestHex",
4330
+ "issuedAt",
4331
+ "expiresAt",
4332
+ "signature"
4333
+ ],
4334
+ "policy enforcer binding"
4335
+ );
4336
+ const signature = exactObject(
4337
+ binding.signature,
4338
+ ["suite", "signerDid", "value"],
4339
+ "policy enforcer binding signature"
4340
+ );
4341
+ const expectedBindingDigestHex = hex(
4342
+ (0, import_sha256.sha256)(
4343
+ new TextEncoder().encode(
4344
+ jcsCanonicalize({
4345
+ enforcerDid: input.enforcerDid,
4346
+ nodeAudience: input.expectedNodeAudience
4347
+ })
4348
+ )
4349
+ )
4350
+ );
4351
+ if (binding.schema !== ATTESTED_ENFORCER_BINDING_V2_SCHEMA || binding.enforcerDid !== input.enforcerDid || binding.nodeAudience !== input.expectedNodeAudience || binding.attestationBindingDigestHex !== expectedBindingDigestHex || binding.expiresAt !== input.rootExpiresAt || typeof binding.issuedAt !== "string" || Date.parse(binding.issuedAt) > Date.now() || signature.suite !== "Ed25519" || signature.signerDid !== input.expectedNodeAudience || typeof signature.value !== "string") {
4352
+ throw new Error("policy enforcer binding is invalid");
4353
+ }
4354
+ const { signature: _signature, ...unsignedBinding } = binding;
4355
+ const signedBinding = (0, import_sha256.sha256)(
4356
+ new TextEncoder().encode(
4357
+ `xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0${jcsCanonicalize(unsignedBinding)}`
4358
+ )
4359
+ );
4360
+ if (!import_ed255193.ed25519.verify(
4361
+ decodeBase64Url2(signature.value),
4362
+ signedBinding,
4363
+ didKeyEd25519PublicKey(input.expectedNodeAudience),
4364
+ { zip215: false }
4365
+ )) {
4366
+ throw new Error("policy enforcer binding signature is invalid");
4367
+ }
4368
+ const response = await fetchFn(
4369
+ new URL("/policy/v3/policies", input.nodeOrigin),
4370
+ {
4371
+ method: "POST",
4372
+ redirect: "error",
4373
+ signal: input.signal,
4374
+ headers: { accept: "application/json", "content-type": "application/json" },
4375
+ body: JSON.stringify({
4376
+ policyCid: input.policyCid,
4377
+ policy: input.policy,
4378
+ policyRoot: input.policyRoot.authorization.replace(/^Bearer\s+/i, ""),
4379
+ enforcementRoot: input.enforcementRoot.authorization.replace(/^Bearer\s+/i, ""),
4380
+ contentSourceDigestHex: input.contentSourceDigestHex,
4381
+ nativeProjectionHashHex: input.nativeProjectionHashHex,
4382
+ attestedEnforcerBinding: binding
4383
+ })
4384
+ }
4385
+ );
4386
+ if (!response.ok) {
4387
+ throw new Error(`policy registration rejected (${response.status})`);
4388
+ }
4389
+ const receipt = exactObject(
4390
+ await response.json(),
4391
+ ["policyCid", "policyRootCid", "enforcementRootCid"],
4392
+ "policy registration response"
4393
+ );
4394
+ if (receipt.policyCid !== input.policyCid || receipt.policyRootCid !== input.policyRoot.cid || receipt.enforcementRootCid !== input.enforcementRoot.cid) {
4395
+ throw new Error("policy registration response binding is invalid");
4396
+ }
4397
+ return {
4398
+ policyCid: input.policyCid,
4399
+ policyRootCid: input.policyRoot.cid,
4400
+ enforcementRootCid: input.enforcementRoot.cid,
4401
+ attestedEnforcerBinding: binding
4402
+ };
4403
+ }
4231
4404
  function policyDigestHex(policy) {
4232
4405
  const canonicalPolicy = {
4233
4406
  ...policy,
@@ -4426,9 +4599,13 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
4426
4599
  if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
4427
4600
  throw new Error("policy session UCAN ordered proofs do not match");
4428
4601
  const factKeys = Object.keys(fact);
4429
- if (factKeys.length !== POLICY_SESSION_FACT_KEYS.length || factKeys.some((key) => !POLICY_SESSION_FACT_KEYS.includes(key)) || POLICY_SESSION_FACT_KEYS.some((key) => !(key in fact)))
4602
+ const hasLegacyFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact);
4603
+ const hasV4AuditFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length + POLICY_SESSION_V4_AUDIT_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key) || POLICY_SESSION_V4_AUDIT_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact) && POLICY_SESSION_V4_AUDIT_FACT_KEYS.every((key) => key in fact);
4604
+ if (!hasLegacyFactShape && !hasV4AuditFactShape)
4430
4605
  throw new Error("policy session UCAN fact is incomplete");
4431
- if (fact.profile !== POLICY_SESSION_UCAN_V1_PROFILE || typeof fact.remainingRedelegationDepth !== "number" || !Number.isInteger(fact.remainingRedelegationDepth) || fact.remainingRedelegationDepth < 0 || fact.remainingRedelegationDepth > 8 || parsed.payload.exp - parsed.payload.nbf > 60 || parsed.payload.iss.split("#", 1)[0] !== fact.enforcerDid || parsed.payload.aud !== fact.recipientDid)
4606
+ if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
4607
+ throw new Error("policy session UCAN v4 audit facts are invalid");
4608
+ if (fact.profile !== POLICY_SESSION_UCAN_V1_PROFILE || typeof fact.remainingRedelegationDepth !== "number" || !Number.isInteger(fact.remainingRedelegationDepth) || fact.remainingRedelegationDepth < 0 || fact.remainingRedelegationDepth > 8 || parsed.payload.exp - parsed.payload.nbf > 60 || parsed.payload.iss.split("#", 1)[0] !== fact.nodeAudience || parsed.payload.aud !== fact.recipientDid)
4432
4609
  throw new Error("policy session UCAN fact is invalid");
4433
4610
  return {
4434
4611
  authorization,
@@ -4487,6 +4664,8 @@ var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
4487
4664
  var POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE = "TinyCloudPolicyCredentialRequirement";
4488
4665
  var POLICY_PRESENTATION_V3_SCHEMA = "xyz.tinycloud.policy/presentation/v3";
4489
4666
  var POLICY_PRESENTATION_V3_DOMAIN = "xyz.tinycloud.policy/Presentation/v3\0";
4667
+ var POLICY_PRESENTATION_V4_SCHEMA = "xyz.tinycloud.policy/presentation/v4";
4668
+ var POLICY_PRESENTATION_V4_DOMAIN = "xyz.tinycloud.policy/Presentation/v4\0";
4490
4669
  function record(value, label) {
4491
4670
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
4492
4671
  throw new Error(`${label} must be an object`);
@@ -4661,6 +4840,97 @@ function envelopeFromVerified(verified) {
4661
4840
  } = verified;
4662
4841
  return envelope;
4663
4842
  }
4843
+ function issuedCredentialEnvelopeFromVerified(verified) {
4844
+ return envelopeFromVerified(verified);
4845
+ }
4846
+ async function signPolicyCredentialPresentationV4(value, sign) {
4847
+ const item = record(value, "Policy presentation v4");
4848
+ exactKeys2(item, [
4849
+ "schema",
4850
+ "jti",
4851
+ "challengeId",
4852
+ "nonce",
4853
+ "policyCid",
4854
+ "nodeAudience",
4855
+ "holderDid",
4856
+ "subjectDid",
4857
+ "credentialDigest",
4858
+ "requirementDigest",
4859
+ "descriptorDigest",
4860
+ "requestedCapabilities",
4861
+ "issuedAt",
4862
+ "expiresAt"
4863
+ ], "Policy presentation v4");
4864
+ if (item.schema !== POLICY_PRESENTATION_V4_SCHEMA || item.holderDid !== item.subjectDid || !Array.isArray(item.requestedCapabilities)) throw new Error("Policy presentation v4 is invalid");
4865
+ const holderDid = nonEmpty(item.holderDid, "Policy presentation v4 holder DID");
4866
+ didKeyBytes(holderDid);
4867
+ const unsigned = Object.freeze({
4868
+ schema: POLICY_PRESENTATION_V4_SCHEMA,
4869
+ jti: nonEmpty(item.jti, "Policy presentation v4 jti"),
4870
+ challengeId: nonEmpty(item.challengeId, "Policy presentation v4 challenge"),
4871
+ nonce: nonEmpty(item.nonce, "Policy presentation v4 nonce"),
4872
+ policyCid: nonEmpty(item.policyCid, "Policy presentation v4 policy CID"),
4873
+ nodeAudience: nonEmpty(item.nodeAudience, "Policy presentation v4 audience"),
4874
+ holderDid,
4875
+ subjectDid: holderDid,
4876
+ credentialDigest: nonEmpty(item.credentialDigest, "Policy presentation v4 credential digest"),
4877
+ requirementDigest: nonEmpty(item.requirementDigest, "Policy presentation v4 requirement digest"),
4878
+ descriptorDigest: nonEmpty(item.descriptorDigest, "Policy presentation v4 descriptor digest"),
4879
+ // The low-level signer preserves the frozen wire bytes exactly. The
4880
+ // policy-aware builder performs capability normalization and containment.
4881
+ requestedCapabilities: Object.freeze(item.requestedCapabilities),
4882
+ issuedAt: date(item.issuedAt, "Policy presentation v4 issuedAt"),
4883
+ expiresAt: date(item.expiresAt, "Policy presentation v4 expiresAt")
4884
+ });
4885
+ const digest4 = (0, import_sha2562.sha256)(new TextEncoder().encode(
4886
+ POLICY_PRESENTATION_V4_DOMAIN + jcsCanonicalize(unsigned)
4887
+ ));
4888
+ const signature = await sign(digest4);
4889
+ if (signature.length !== 64 || !import_ed255194.ed25519.verify(signature, digest4, didKeyBytes(holderDid))) throw new Error("policy presentation signature is invalid");
4890
+ return Object.freeze({
4891
+ ...unsigned,
4892
+ signature: Object.freeze({
4893
+ suite: "Ed25519",
4894
+ signerDid: holderDid,
4895
+ value: encodeBase64Url(signature)
4896
+ })
4897
+ });
4898
+ }
4899
+ async function buildPolicyCredentialPresentationV4(input) {
4900
+ const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
4901
+ const requirement = validateCredentialRequirement(input.requirement);
4902
+ const commitment = policy.credentialRequirement;
4903
+ const requirementDigest = await credentialRequirementDigest(requirement);
4904
+ const credentialDigest = await sha256Base64Url(input.credential.credential);
4905
+ if (requirementDigest !== commitment.requirementDigest || input.credential.descriptorDigest !== commitment.descriptorDigest || input.credential.credentialDigest !== credentialDigest || input.credential.issuerDid !== commitment.issuerDid || input.credential.issuerKid !== commitment.issuerKid || jcsCanonicalize(input.credential.profile) !== jcsCanonicalize(commitment.profile) || jcsCanonicalize(input.credential.credentialType) !== jcsCanonicalize(commitment.credentialType)) throw new Error("credential does not match the signed Policy/v2 requirement");
4906
+ const requestedCapabilities = input.requestedCapabilities.map(normalizeUnifiedPolicyCapability);
4907
+ if (requestedCapabilities.length === 0 || requestedCapabilities.some((requested) => !policy.capabilityCeiling.some((authority) => unifiedPolicyCapabilityContains(authority, requested)))) throw new Error("requested capabilities exceed the signed Policy/v2 ceiling");
4908
+ const holderDid = input.credential.holderDid;
4909
+ if (input.challenge.policyCid !== input.policyCid || input.challenge.recipientDid !== holderDid || input.credential.subjectDid !== holderDid || typeof input.challenge.nodeAudience !== "string" || input.challenge.nodeAudience.length === 0 || typeof input.challenge.expiresAt !== "string" || !Number.isFinite(Date.parse(input.challenge.expiresAt))) throw new Error("policy challenge binding is invalid");
4910
+ const now = input.now ?? /* @__PURE__ */ new Date();
4911
+ const expiresAtMs = Math.min(
4912
+ now.getTime() + 6e4,
4913
+ Date.parse(input.challenge.expiresAt),
4914
+ Date.parse(input.credential.expiresAt)
4915
+ );
4916
+ if (expiresAtMs <= now.getTime()) throw new Error("policy challenge is expired");
4917
+ return signPolicyCredentialPresentationV4(Object.freeze({
4918
+ schema: POLICY_PRESENTATION_V4_SCHEMA,
4919
+ jti: input.jti ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
4920
+ challengeId: input.challenge.challengeId,
4921
+ nonce: input.challenge.nonce,
4922
+ policyCid: input.policyCid,
4923
+ nodeAudience: input.challenge.nodeAudience,
4924
+ holderDid,
4925
+ subjectDid: holderDid,
4926
+ credentialDigest,
4927
+ requirementDigest,
4928
+ descriptorDigest: input.credential.descriptorDigest,
4929
+ requestedCapabilities: Object.freeze(requestedCapabilities),
4930
+ issuedAt: rfc3339Seconds(now),
4931
+ expiresAt: rfc3339Seconds(new Date(expiresAtMs))
4932
+ }), input.sign);
4933
+ }
4664
4934
  async function buildPolicyCredentialPresentationV3(input) {
4665
4935
  const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
4666
4936
  const requirement = validateCredentialRequirement(input.requirement);
@@ -4747,10 +5017,12 @@ async function admitPolicyCredentialV3(input) {
4747
5017
  const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
4748
5018
  const challenge = await requestPolicyChallengeV3({
4749
5019
  nodeOrigin,
5020
+ policyRuntimePath: input.policyRuntimePath,
4750
5021
  policyCid: input.policyCid,
4751
5022
  recipientDid: input.credential.holderDid,
4752
5023
  requestedCapabilities: input.requestedCapabilities,
4753
- fetch: fetchFn
5024
+ fetch: fetchFn,
5025
+ signal: input.signal
4754
5026
  });
4755
5027
  const presentation = await buildPolicyCredentialPresentationV3({
4756
5028
  ...input,
@@ -4758,10 +5030,11 @@ async function admitPolicyCredentialV3(input) {
4758
5030
  challenge
4759
5031
  });
4760
5032
  const response = await fetchFn(
4761
- new URL("/share/v3/policy/delegations", nodeOrigin),
5033
+ new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin),
4762
5034
  {
4763
5035
  method: "POST",
4764
5036
  redirect: "error",
5037
+ signal: input.signal,
4765
5038
  headers: { accept: "application/json", "content-type": "application/json" },
4766
5039
  body: JSON.stringify({
4767
5040
  policyCid: input.policyCid,
@@ -4790,8 +5063,74 @@ async function admitPolicyCredentialV3(input) {
4790
5063
  if (session.cid !== value.sessionCid || session.aud !== input.credential.holderDid || session.fact.policyCid !== input.policyCid || session.fact.recipientDid !== input.credential.holderDid || session.fact.policyDelegationCid !== input.policyRootCid || session.fact.enforcementDelegationCid !== input.enforcementRootCid || jcsCanonicalize(session.att) !== jcsCanonicalize(expectedAttenuation)) {
4791
5064
  throw new Error("policy delegation signed binding is invalid");
4792
5065
  }
5066
+ input.signal?.throwIfAborted();
4793
5067
  return Object.freeze({ challenge, presentation, session });
4794
5068
  }
5069
+ function validatePolicyCredentialAdmissionV4Authority(session, expected) {
5070
+ if (session.iss.split("#", 1)[0] !== expected.nodeAudience || session.fact.nodeAudience !== expected.nodeAudience || session.fact.enforcerDid !== expected.enforcerDid) throw new Error("policy delegation authority binding is invalid");
5071
+ }
5072
+ async function admitPolicyCredentialV4(input) {
5073
+ const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
5074
+ const nodeOrigin = policyNodeOrigin(input.nodeOrigin);
5075
+ const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
5076
+ const challenge = await requestPolicyChallengeV3({
5077
+ nodeOrigin,
5078
+ policyRuntimePath: input.policyRuntimePath,
5079
+ policyCid: input.policyCid,
5080
+ recipientDid: input.credential.holderDid,
5081
+ requestedCapabilities: input.requestedCapabilities,
5082
+ fetch: fetchFn,
5083
+ signal: input.signal
5084
+ });
5085
+ const presentation = await buildPolicyCredentialPresentationV4({
5086
+ ...input,
5087
+ policy,
5088
+ challenge
5089
+ });
5090
+ const response = await fetchFn(new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin), {
5091
+ method: "POST",
5092
+ redirect: "error",
5093
+ signal: input.signal,
5094
+ headers: { accept: "application/json", "content-type": "application/json" },
5095
+ body: JSON.stringify({
5096
+ policyCid: input.policyCid,
5097
+ challengeId: challenge.challengeId,
5098
+ nonce: challenge.nonce,
5099
+ requirement: validateCredentialRequirement(input.requirement),
5100
+ credential: envelopeFromVerified(input.credential),
5101
+ presentation
5102
+ })
5103
+ });
5104
+ if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
5105
+ const value = record(await response.json(), "policy delegation");
5106
+ if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string") throw new Error("policy delegation response is invalid");
5107
+ const session = parsePolicySessionUcan(value.authorization, [
5108
+ input.policyRootCid,
5109
+ input.enforcementRootCid
5110
+ ]);
5111
+ const expectedAttenuation = compactAttenuationForPolicyCapabilities(input.requestedCapabilities);
5112
+ validatePolicyCredentialAdmissionV4Authority(session, {
5113
+ nodeAudience: input.expectedNodeAudience,
5114
+ enforcerDid: input.expectedEnforcerDid
5115
+ });
5116
+ if (session.cid !== value.sessionCid || session.aud !== input.credential.holderDid || session.fact.policyCid !== input.policyCid || session.fact.recipientDid !== input.credential.holderDid || session.fact.policyDelegationCid !== input.policyRootCid || session.fact.enforcementDelegationCid !== input.enforcementRootCid || jcsCanonicalize(session.att) !== jcsCanonicalize(expectedAttenuation)) throw new Error("policy delegation signed binding is invalid");
5117
+ const importResponse = await fetchFn(new URL("/delegate", nodeOrigin), {
5118
+ method: "POST",
5119
+ redirect: "error",
5120
+ signal: input.signal,
5121
+ headers: { Authorization: session.authorization }
5122
+ });
5123
+ if (!importResponse.ok) throw new Error(`delegation import rejected (${importResponse.status})`);
5124
+ input.signal?.throwIfAborted();
5125
+ return Object.freeze({ challenge, presentation, session, delegationImported: true });
5126
+ }
5127
+ function policyRuntimeBasePath2(value) {
5128
+ const path = value ?? "/policy/v3";
5129
+ if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
5130
+ throw new Error("policy runtime path is invalid");
5131
+ }
5132
+ return path;
5133
+ }
4795
5134
 
4796
5135
  // src/requester/index.ts
4797
5136
  var REQUESTER_NEAR_EXPIRY_SECONDS = 30;
@@ -8198,13 +8537,13 @@ function canonicalizeRecapCaveats(caveats) {
8198
8537
  }
8199
8538
  if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
8200
8539
  if (typeof value === "object") {
8201
- const object3 = value;
8202
- const prototype = Object.getPrototypeOf(object3);
8540
+ const object4 = value;
8541
+ const prototype = Object.getPrototypeOf(object4);
8203
8542
  if (prototype !== Object.prototype && prototype !== null) {
8204
8543
  throw new Error("ReCap caveats must contain only JSON values.");
8205
8544
  }
8206
- return `{${Object.keys(object3).sort().map(
8207
- (key) => `${JSON.stringify(key)}:${canonicalize(object3[key])}`
8545
+ return `{${Object.keys(object4).sort().map(
8546
+ (key) => `${JSON.stringify(key)}:${canonicalize(object4[key])}`
8208
8547
  ).join(",")}}`;
8209
8548
  }
8210
8549
  throw new Error("ReCap caveats must contain only JSON values.");
@@ -8288,7 +8627,6 @@ var import_ed255195 = require("@noble/curves/ed25519");
8288
8627
  var import_blake33 = require("@noble/hashes/blake3");
8289
8628
  var import_sha2563 = require("@noble/hashes/sha256");
8290
8629
  var ENVELOPE_SCHEMA = "xyz.tinycloud.share/envelope";
8291
- var INLINE_PREFIX = "tc2:";
8292
8630
  var RAW_CID_CODEC = 85;
8293
8631
  var BLAKE3_256_CODE = 30;
8294
8632
  var DEFAULT_MAX_INLINE_BYTES = 256 * 1024;
@@ -8335,8 +8673,8 @@ function canonicalJson(value) {
8335
8673
  }
8336
8674
  if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
8337
8675
  if (typeof value === "object") {
8338
- const object3 = value;
8339
- return `{${Object.keys(object3).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object3[key])}`).join(",")}}`;
8676
+ const object4 = value;
8677
+ return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object4[key])}`).join(",")}}`;
8340
8678
  }
8341
8679
  throw new ShareEnvelopeError("invalid-envelope", "unsupported JSON value");
8342
8680
  }
@@ -8993,25 +9331,8 @@ function parsePublishedShareLink(link, options2 = {}) {
8993
9331
  }
8994
9332
  const origin2 = normalizeOrigin(url.origin);
8995
9333
  if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
8996
- if (url.search !== "" || url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
8997
- if (url.pathname === "/s/inline" && url.hash.startsWith("#v=2&p=")) {
8998
- let payload;
8999
- try {
9000
- const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
9001
- if (bytes2.length > (options2.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES) * 2) throw new Error("inline payload too large");
9002
- const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
9003
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
9004
- payload = value;
9005
- } catch (error) {
9006
- throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
9007
- }
9008
- if (payload.v !== 2 || typeof payload.c !== "string" || typeof payload.cid !== "string" || payload.k !== void 0 && typeof payload.k !== "string" || Object.keys(payload).some((key3) => !["v", "c", "cid", "k"].includes(key3))) throw new ShareEnvelopeError("invalid-link", "inline payload fields are invalid");
9009
- const sealed = base64UrlToBytes(payload.c);
9010
- if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
9011
- const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
9012
- if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
9013
- return { origin: origin2, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
9014
- }
9334
+ if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
9335
+ if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
9015
9336
  const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
9016
9337
  if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
9017
9338
  const key = base64UrlToBytes(url.hash.slice(3));
@@ -9031,13 +9352,10 @@ function parseShareUrl(link, options2 = {}) {
9031
9352
  } catch {
9032
9353
  throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
9033
9354
  }
9034
- if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
9035
9355
  const origin2 = normalizeOrigin(url.origin);
9036
9356
  if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
9037
- if (url.pathname === "/s/inline" && url.hash.startsWith("#v=2&p=")) {
9038
- const published = parsePublishedShareLink(link, options2);
9039
- return { kind: "inline", origin: origin2, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
9040
- }
9357
+ if (url.pathname === "/viewer") throw new ShareEnvelopeError("invalid-link", "retired viewer links are not accepted");
9358
+ if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
9041
9359
  const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
9042
9360
  if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
9043
9361
  const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
@@ -9060,9 +9378,10 @@ function parseShareUrl(link, options2 = {}) {
9060
9378
  }
9061
9379
  function isV2ShareLink(link) {
9062
9380
  try {
9063
- return new URL(link).pathname.startsWith("/s/");
9381
+ const url = new URL(link);
9382
+ return url.search === "" && url.pathname.startsWith("/s/");
9064
9383
  } catch {
9065
- return link.startsWith(INLINE_PREFIX);
9384
+ return false;
9066
9385
  }
9067
9386
  }
9068
9387
  async function encryptShareBytes(bytes2, key) {
@@ -9518,8 +9837,8 @@ function canonicalJsonForProof(value) {
9518
9837
  }
9519
9838
  if (Array.isArray(value)) return `[${value.map(canonicalJsonForProof).join(",")}]`;
9520
9839
  if (typeof value === "object") {
9521
- const object3 = value;
9522
- return `{${Object.keys(object3).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object3[key])}`).join(",")}}`;
9840
+ const object4 = value;
9841
+ return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object4[key])}`).join(",")}}`;
9523
9842
  }
9524
9843
  throw new ShareAccessError("SHARE_SESSION_INVALID");
9525
9844
  }
@@ -10228,12 +10547,12 @@ var ShareRecipientClient = class {
10228
10547
  this.assertDetachedProof(request.proof);
10229
10548
  }
10230
10549
  parseSessionCapability(value) {
10231
- const object3 = this.requireObject(value, "session capability");
10232
- if (typeof object3.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
10233
- if (!Array.isArray(object3.actions) || object3.actions.some((action) => !["read", "list", "edit"].includes(action))) throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability actions are invalid");
10234
- const resource = this.requireObject(object3.resource, "session capability resource");
10550
+ const object4 = this.requireObject(value, "session capability");
10551
+ if (typeof object4.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
10552
+ if (!Array.isArray(object4.actions) || object4.actions.some((action) => !["read", "list", "edit"].includes(action))) throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability actions are invalid");
10553
+ const resource = this.requireObject(object4.resource, "session capability resource");
10235
10554
  if (resource.kind !== "exact" && resource.kind !== "prefix" || typeof resource.path !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability resource is invalid");
10236
- return { spaceId: object3.spaceId, actions: [...new Set(object3.actions)], resource: { kind: resource.kind, path: resource.path } };
10555
+ return { spaceId: object4.spaceId, actions: [...new Set(object4.actions)], resource: { kind: resource.kind, path: resource.path } };
10237
10556
  }
10238
10557
  assertProvidedPolicySession(session, envelope) {
10239
10558
  const sessionExpiry = dateValue(session.expiresAt);
@@ -11873,62 +12192,21 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
11873
12192
  return validateOwnerSharePolicyRegistration(value, expected);
11874
12193
  }
11875
12194
 
11876
- // src/delegations/share-delivery.ts
12195
+ // src/delegations/share-delivery-v3.ts
11877
12196
  var import_ed255199 = require("@noble/curves/ed25519");
11878
- var SHARE_DELIVERY_AUTHORIZATION_DOMAIN = "xyz.tinycloud.share/delivery-authorization/v2\0";
11879
- function isCanonicalHttpsOrigin(value) {
11880
- let url;
11881
- try {
11882
- url = new URL(value);
11883
- } catch {
11884
- return false;
11885
- }
11886
- return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
11887
- }
11888
- var AUTHORIZATION_KEYS = [
11889
- "type",
11890
- "version",
11891
- "jti",
11892
- "shareCid",
11893
- "shareId",
11894
- "registrationCid",
11895
- "delegationCid",
11896
- "enforcementDelegationCid",
11897
- "envelopeCid",
11898
- "policyCid",
11899
- "nodeAudience",
11900
- "targetOrigin",
11901
- "openCredentialsAudience",
11902
- "holder",
11903
- "recipientMatcher",
11904
- "deliveryEmail",
11905
- "shareUrl",
11906
- "returnOrigin",
11907
- "documentName",
11908
- "senderDid",
11909
- "senderTrust",
11910
- "contentSource",
11911
- "contentSourceDigest",
11912
- "shareExpiresAt",
11913
- "issuedAt",
11914
- "reportAbuseToken",
11915
- "actions",
11916
- "resource",
11917
- "authorityMaterialHandle",
11918
- "authorityMaterialDigest",
11919
- "requestBodyDigest",
11920
- "idempotencyKey",
11921
- "expiresAt",
11922
- "dataAuthority"
11923
- ];
11924
- function assertObject2(value, keys, label) {
12197
+ var import_sha2567 = require("@noble/hashes/sha256");
12198
+ var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
12199
+ var DELIVERY_ADMISSION_DOMAIN = "xyz.tinycloud.policy/delivery-admission/v0\0";
12200
+ var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
12201
+ var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
12202
+ function exactObject2(value, keys, label) {
11925
12203
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
11926
12204
  const record2 = value;
11927
12205
  if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) throw new Error(`${label} has unknown or missing fields`);
11928
12206
  return record2;
11929
12207
  }
11930
- function fromB642(value) {
11931
- if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
12208
+ function decodeBase64Url3(value) {
12209
+ if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
11932
12210
  if (typeof atob === "function") {
11933
12211
  const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
11934
12212
  return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
@@ -11936,42 +12214,45 @@ function fromB642(value) {
11936
12214
  if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
11937
12215
  throw new Error("base64url decoding is unavailable");
11938
12216
  }
11939
- function validateShareDeliveryAuthorizationBytes(bytesValue, expected) {
11940
- let value;
12217
+ function object3(value, label) {
12218
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
12219
+ return value;
12220
+ }
12221
+ function didKeyBytes2(did) {
12222
+ if (!did.startsWith("did:key:z") || /[#/?]/.test(did.slice("did:key:".length))) throw new Error("v3 delivery admission signer is invalid");
12223
+ const decoded = base58btc.decode(did.slice("did:key:".length));
12224
+ if (decoded.length !== 34 || decoded[0] !== 237 || decoded[1] !== 1) throw new Error("v3 delivery admission signer is invalid");
12225
+ return decoded.slice(2);
12226
+ }
12227
+ function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
12228
+ let parsed;
11941
12229
  try {
11942
- value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytesValue));
12230
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
11943
12231
  } catch {
11944
- throw new Error("share delivery authorization response is not valid UTF-8 JSON");
11945
- }
11946
- const root = assertObject2(value, ["authorization", "proof"], "share delivery authorization response");
11947
- const authorization = assertObject2(root.authorization, AUTHORIZATION_KEYS, "share delivery authorization");
11948
- const proof = assertObject2(root.proof, ["alg", "kid", "signature"], "share delivery authorization proof");
11949
- const request = expected.request;
11950
- if (authorization.type !== "TinyCloudShareDeliveryAuthorization" || authorization.version !== 2 || authorization.dataAuthority !== false || authorization.jti !== request.jti || authorization.shareCid !== request.shareCid || authorization.shareId !== request.shareId || authorization.registrationCid !== request.registrationCid || authorization.delegationCid !== request.delegationCid || authorization.enforcementDelegationCid !== request.enforcementDelegationCid || authorization.envelopeCid !== request.envelopeCid || authorization.policyCid !== request.policyCid || authorization.deliveryEmail !== request.recipientEmail || authorization.shareUrl !== request.shareUrl || authorization.documentName !== request.documentName || authorization.requestBodyDigest !== request.requestBodyDigest || authorization.expiresAt !== request.expiresAt || authorization.idempotencyKey !== request.idempotencyKey || authorization.reportAbuseToken !== request.jti || authorization.authorityMaterialHandle !== request.registrationCid) {
11951
- throw new Error("share delivery authorization is not bound to the submitted request");
11952
- }
11953
- if (typeof authorization.issuedAt !== "string" || Date.parse(authorization.issuedAt) > Date.now() + 6e4) throw new Error("share delivery authorization issuedAt is invalid");
11954
- if (new Date(authorization.expiresAt).toISOString() !== authorization.expiresAt) throw new Error("share delivery authorization expiresAt is non-canonical");
11955
- if (typeof authorization.openCredentialsAudience !== "string" || !isCanonicalHttpsOrigin(authorization.openCredentialsAudience) || authorization.openCredentialsAudience !== expected.credentialsAudience || authorization.openCredentialsAudience === authorization.nodeAudience || authorization.openCredentialsAudience === authorization.returnOrigin) {
11956
- throw new Error("share delivery authorization openCredentialsAudience is missing or untrusted");
11957
- }
11958
- if (proof.alg !== "EdDSA" || typeof proof.kid !== "string" || typeof proof.signature !== "string") throw new Error("share delivery authorization proof is invalid");
11959
- if (proof.kid !== expected.nodeProof.kid) throw new Error("share delivery authorization proof key is not trusted");
11960
- const encodedKid = expected.nodeProof.publicKey;
11961
- const publicKey = encodedKid.length === 34 && encodedKid[0] === 237 && encodedKid[1] === 1 ? encodedKid.slice(2) : encodedKid;
11962
- if (publicKey.length !== 32) throw new Error("share delivery authorization proof key is invalid");
11963
- const signatureBytes = fromB642(proof.signature);
11964
- const signedBytes = new TextEncoder().encode(`${SHARE_DELIVERY_AUTHORIZATION_DOMAIN}${canonicalizeSignedObjectUnsigned(authorization)}`);
11965
- if (signatureBytes.length !== 64 || !import_ed255199.ed25519.verify(signatureBytes, signedBytes, publicKey)) throw new Error("share delivery authorization proof signature is invalid");
11966
- return { authorization, proof };
11967
- }
11968
- function shareDeliveryTrustedKid(publicKey) {
11969
- if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
11970
- const prefixed = new Uint8Array(34);
11971
- prefixed.set([237, 1]);
11972
- prefixed.set(publicKey, 2);
11973
- const did = `did:key:${base58btc.encode(prefixed)}`;
11974
- return `${did}#${did.slice("did:key:".length)}`;
12232
+ throw new Error("v3 share delivery response is not valid UTF-8 JSON");
12233
+ }
12234
+ const root = exactObject2(parsed, ["request", "admission"], "v3 share delivery response");
12235
+ const request = exactObject2(root.request, REQUEST_KEYS, "credential invitation request");
12236
+ const admission = exactObject2(root.admission, ADMISSION_KEYS, "delivery admission");
12237
+ const signature = exactObject2(admission.signature, ["suite", "signerDid", "value"], "delivery admission signature");
12238
+ const envelope = object3(expected.request.envelope, "v3 share envelope");
12239
+ const policy = object3(envelope.policy, "v3 share policy");
12240
+ const requirement = object3(policy.credentialRequirement, "v3 credential requirement");
12241
+ const credentialType = object3(requirement.credentialType, "v3 credential type");
12242
+ const contentSource = object3(envelope.contentSource, "v3 content source");
12243
+ const envelopeSignature = object3(envelope.signature, "v3 envelope signature");
12244
+ const target = object3(envelope.target, "v3 target");
12245
+ const binding = object3(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
12246
+ const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
12247
+ if (request.schema !== "xyz.tinycloud.credentials/invitation-request/v1" || admission.schema !== "xyz.tinycloud.policy/delivery-admission/v0" || fields.some((field) => request[field] !== admission[field]) || request.policyId !== envelope.policyCid || request.recipient !== expected.request.recipientEmail || request.resource !== contentSource.kvResource || request.credentialType !== credentialType.id || request.credentialType !== "opencredentials.email/v1" || request.returnLink !== expected.request.shareUrl || request.envelopeRef !== expected.request.shareCid || request.label !== expected.request.documentName || request.shareExpiresAt !== envelope.expiry || request.audience !== expected.deliveryAudience || request.expiresAt !== expected.request.expiresAt || request.nonce !== expected.request.jti || admission.ownerDid !== envelopeSignature.signerDid || admission.senderKeyDid !== expected.senderKeyDid || canonicalizeSignedObjectUnsigned(admission.actions) !== canonicalizeSignedObjectUnsigned(["tinycloud.kv/get"]) || signature.suite !== "eddsa-ed25519-sha256-jcs-v1" || target.nodeAudience !== binding.nodeAudience || signature.signerDid !== binding.nodeAudience) throw new Error("v3 share delivery authorization is not bound to the submitted request");
12248
+ const issuedAt = Date.parse(String(request.issuedAt));
12249
+ const expiresAt = Date.parse(String(request.expiresAt));
12250
+ if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
12251
+ const admissionSignature = decodeBase64Url3(signature.value);
12252
+ const { signature: _signature, ...unsignedAdmission } = admission;
12253
+ const digest4 = (0, import_sha2567.sha256)(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
12254
+ if (admissionSignature.length !== 64 || !import_ed255199.ed25519.verify(admissionSignature, digest4, didKeyBytes2(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
12255
+ return { request, admission };
11975
12256
  }
11976
12257
 
11977
12258
  // src/delegations/sender-share-store.ts
@@ -14694,8 +14975,8 @@ function stableJson(value) {
14694
14975
  if (Array.isArray(value)) {
14695
14976
  return `[${value.map(stableJson).join(",")}]`;
14696
14977
  }
14697
- const object3 = value;
14698
- return `{${Object.keys(object3).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object3[key])}`).join(",")}}`;
14978
+ const object4 = value;
14979
+ return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object4[key])}`).join(",")}}`;
14699
14980
  }
14700
14981
  function indexedDelegationFromRow(row) {
14701
14982
  const [
@@ -15367,6 +15648,236 @@ function createOpenKeyCallbackSigningStrategy(options2) {
15367
15648
  }
15368
15649
  var defaultSignStrategy = { type: "auto-sign" };
15369
15650
 
15651
+ // src/authorization/manage-key.ts
15652
+ var TINYCLOUD_MANAGE_KEY_SCOPE = "tinycloud:manage-key";
15653
+ var TINYCLOUD_CANONICAL_IDENTITY_CLAIM = "https://tinycloud.xyz/canonical_identity";
15654
+ function requestTinyCloudManageKeyScope(scopes = []) {
15655
+ const requested = typeof scopes === "string" ? scopes.split(/\s+/u) : scopes;
15656
+ return [
15657
+ ...new Set([...requested, TINYCLOUD_MANAGE_KEY_SCOPE].filter(Boolean))
15658
+ ].sort().join(" ");
15659
+ }
15660
+ function hasTinyCloudManageKeyScope(scopes) {
15661
+ const values = Array.isArray(scopes) ? scopes : typeof scopes === "string" ? scopes.split(/\s+/u) : [];
15662
+ return values.includes(TINYCLOUD_MANAGE_KEY_SCOPE);
15663
+ }
15664
+ var OpenKeyManageKeyError = class extends Error {
15665
+ constructor(code3, message, approvalUrl, retryable = false) {
15666
+ super(message);
15667
+ this.code = code3;
15668
+ this.approvalUrl = approvalUrl;
15669
+ this.retryable = retryable;
15670
+ this.name = "OpenKeyManageKeyError";
15671
+ }
15672
+ };
15673
+ function isRecord2(value) {
15674
+ return value !== null && typeof value === "object" && !Array.isArray(value);
15675
+ }
15676
+ function rejected(message) {
15677
+ return new OpenKeyManageKeyError("MESSAGE_REJECTED", message);
15678
+ }
15679
+ function identityMismatch(message) {
15680
+ return new OpenKeyManageKeyError("IDENTITY_MISMATCH", message);
15681
+ }
15682
+ function parseCanonicalTinyCloudIdentity(value) {
15683
+ if (!isRecord2(value))
15684
+ throw rejected("Canonical TinyCloud identity is missing");
15685
+ const expectedKeys = [
15686
+ "address",
15687
+ "chainId",
15688
+ "did",
15689
+ "keyId",
15690
+ "spaceId",
15691
+ "version"
15692
+ ];
15693
+ const actualKeys = Object.keys(value).sort();
15694
+ if (actualKeys.length !== expectedKeys.length || actualKeys.some((key, index) => key !== expectedKeys[index])) {
15695
+ throw rejected("Canonical TinyCloud identity has an invalid shape");
15696
+ }
15697
+ if (value.version !== "v1")
15698
+ throw rejected("Canonical TinyCloud identity version is unsupported");
15699
+ if (typeof value.keyId !== "string" || value.keyId.trim() === "") {
15700
+ throw rejected("Canonical TinyCloud identity key ID is invalid");
15701
+ }
15702
+ if (typeof value.address !== "string") {
15703
+ throw rejected("Canonical TinyCloud identity address is invalid");
15704
+ }
15705
+ let address;
15706
+ try {
15707
+ address = canonicalizeAddress(value.address);
15708
+ } catch {
15709
+ throw rejected("Canonical TinyCloud identity address is invalid");
15710
+ }
15711
+ if (address !== value.address) {
15712
+ throw rejected(
15713
+ "Canonical TinyCloud identity address must use EIP-55 checksum casing"
15714
+ );
15715
+ }
15716
+ if (typeof value.chainId !== "number" || !Number.isSafeInteger(value.chainId) || value.chainId <= 0) {
15717
+ throw rejected("Canonical TinyCloud identity chain ID is invalid");
15718
+ }
15719
+ const did = pkhDid(address, value.chainId);
15720
+ const spacePrefix = `tinycloud:pkh:eip155:${value.chainId}:${address}:`;
15721
+ if (typeof value.spaceId !== "string" || !value.spaceId.startsWith(spacePrefix)) {
15722
+ throw rejected("Canonical TinyCloud identity space is invalid");
15723
+ }
15724
+ const spaceName = value.spaceId.slice(spacePrefix.length);
15725
+ let spaceId;
15726
+ try {
15727
+ spaceId = makePkhSpaceId(address, value.chainId, spaceName);
15728
+ } catch {
15729
+ throw rejected("Canonical TinyCloud identity space is invalid");
15730
+ }
15731
+ if (value.did !== did || value.spaceId !== spaceId) {
15732
+ throw rejected("Canonical TinyCloud identity does not match its address");
15733
+ }
15734
+ return {
15735
+ version: "v1",
15736
+ keyId: value.keyId,
15737
+ address,
15738
+ chainId: value.chainId,
15739
+ did,
15740
+ spaceId
15741
+ };
15742
+ }
15743
+ function parseCanonicalTinyCloudIdentityClaims(claims) {
15744
+ if (!isRecord2(claims)) throw rejected("OAuth claims are missing");
15745
+ return parseCanonicalTinyCloudIdentity(
15746
+ claims[TINYCLOUD_CANONICAL_IDENTITY_CLAIM]
15747
+ );
15748
+ }
15749
+ function identitiesEqual(left, right) {
15750
+ return left.version === right.version && left.keyId === right.keyId && left.address === right.address && left.chainId === right.chainId && left.did === right.did && left.spaceId === right.spaceId;
15751
+ }
15752
+ function errorForResponse(body, status) {
15753
+ const message = body?.reason ?? body?.error ?? `OpenKey manage-key signing failed with HTTP ${status}`;
15754
+ switch (body?.code ?? body?.error) {
15755
+ case "missing_scope":
15756
+ case "consent_required":
15757
+ return new OpenKeyManageKeyError("CONSENT_REQUIRED", message, body?.approvalUrl);
15758
+ case "client_disabled":
15759
+ case "signing_disabled":
15760
+ case "grant_disabled":
15761
+ return new OpenKeyManageKeyError("GRANT_DISABLED", message, body?.approvalUrl);
15762
+ case "user_exclusive":
15763
+ return new OpenKeyManageKeyError("USER_EXCLUSIVE", message, body?.approvalUrl);
15764
+ case "token_expired":
15765
+ case "token_too_old":
15766
+ case "invalid_token":
15767
+ case "expired_token":
15768
+ return new OpenKeyManageKeyError("TOKEN_EXPIRED", message, body?.approvalUrl);
15769
+ case "canonical_key_unavailable":
15770
+ case "signer_failed":
15771
+ return new OpenKeyManageKeyError(
15772
+ "SIGNER_UNAVAILABLE",
15773
+ "The canonical key signer is temporarily unavailable",
15774
+ void 0,
15775
+ true
15776
+ );
15777
+ default:
15778
+ return new OpenKeyManageKeyError(
15779
+ status === 401 ? "TOKEN_EXPIRED" : "MESSAGE_REJECTED",
15780
+ message,
15781
+ body?.approvalUrl
15782
+ );
15783
+ }
15784
+ }
15785
+ async function resolveToken(token) {
15786
+ const value = typeof token === "function" ? await token() : token;
15787
+ if (typeof value !== "string" || value.trim() === "") {
15788
+ throw new OpenKeyManageKeyError(
15789
+ "CONSENT_REQUIRED",
15790
+ "A manage-key OAuth bearer token is required"
15791
+ );
15792
+ }
15793
+ return value;
15794
+ }
15795
+ function normalizeEndpoint(value) {
15796
+ let endpoint;
15797
+ try {
15798
+ endpoint = new URL(value);
15799
+ } catch {
15800
+ throw rejected("OpenKey manage-key signing endpoint must be an absolute URL");
15801
+ }
15802
+ const rawHttpAuthority = value.match(/^http:\/\/([^/?#]*)/iu)?.[1];
15803
+ const loopback = rawHttpAuthority !== void 0 && /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/u.test(rawHttpAuthority);
15804
+ if (endpoint.protocol !== "https:" && !(endpoint.protocol === "http:" && loopback) || endpoint.username !== "" || endpoint.password !== "" || endpoint.search !== "" || endpoint.hash !== "") {
15805
+ throw rejected("OpenKey manage-key signing endpoint must use HTTPS or exact loopback HTTP");
15806
+ }
15807
+ return endpoint.href;
15808
+ }
15809
+ function assertSignInRequest(request, identity3) {
15810
+ if (request.type !== "siwe" || request.purpose !== "sign-in" || request.chainId !== identity3.chainId || request.address !== identity3.address || typeof request.message !== "string" || request.message.length === 0) {
15811
+ throw rejected(
15812
+ "Manage-key signing only permits the canonical sign-in SIWE request"
15813
+ );
15814
+ }
15815
+ }
15816
+ function createOpenKeyManageKeySigningStrategy(options2) {
15817
+ const identity3 = parseCanonicalTinyCloudIdentity(options2.identity);
15818
+ const endpoint = normalizeEndpoint(options2.endpoint);
15819
+ return {
15820
+ type: "callback",
15821
+ openKeyAutoSign: true,
15822
+ handler: async (request) => {
15823
+ assertSignInRequest(request, identity3);
15824
+ const fetchImpl = options2.fetch ?? globalThis.fetch;
15825
+ if (!fetchImpl)
15826
+ throw rejected("OpenKey manage-key signing requires fetch");
15827
+ if (!hasTinyCloudManageKeyScope(options2.scopes)) {
15828
+ throw new OpenKeyManageKeyError(
15829
+ "CONSENT_REQUIRED",
15830
+ "The OAuth bearer token was not granted tinycloud:manage-key"
15831
+ );
15832
+ }
15833
+ const token = await resolveToken(options2.token);
15834
+ const response = await fetchImpl(endpoint, {
15835
+ method: "POST",
15836
+ credentials: "omit",
15837
+ headers: {
15838
+ authorization: `Bearer ${token}`,
15839
+ "content-type": "application/json"
15840
+ },
15841
+ body: JSON.stringify({
15842
+ address: request.address,
15843
+ chainId: request.chainId,
15844
+ message: request.message,
15845
+ type: "siwe"
15846
+ })
15847
+ });
15848
+ let body;
15849
+ try {
15850
+ const value = await response.json();
15851
+ body = isRecord2(value) ? value : void 0;
15852
+ } catch {
15853
+ body = void 0;
15854
+ }
15855
+ if (!response.ok || body?.approved !== true)
15856
+ throw errorForResponse(body, response.status);
15857
+ if (typeof body.signature !== "string")
15858
+ throw rejected("OpenKey manage-key signer did not return a signature");
15859
+ const responseIdentity = parseCanonicalTinyCloudIdentity(
15860
+ body.canonicalIdentity
15861
+ );
15862
+ if (!identitiesEqual(identity3, responseIdentity)) {
15863
+ throw identityMismatch(
15864
+ "OpenKey manage-key signer returned a different canonical identity"
15865
+ );
15866
+ }
15867
+ if (!await verifyEip191MessageSignature(
15868
+ request.message,
15869
+ body.signature,
15870
+ identity3.address
15871
+ )) {
15872
+ throw rejected(
15873
+ "OpenKey manage-key signer returned an invalid signature"
15874
+ );
15875
+ }
15876
+ return { approved: true, signature: body.signature };
15877
+ }
15878
+ };
15879
+ }
15880
+
15370
15881
  // src/authorization/spaceCreation.ts
15371
15882
  var AutoApproveSpaceCreationHandler = class {
15372
15883
  /**
@@ -17373,6 +17884,7 @@ var DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
17373
17884
  var LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
17374
17885
  var LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
17375
17886
  var LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
17887
+ var OWNER_NODE_BINDING_TIMEOUT_MS = 5e3;
17376
17888
  function createInMemoryLocalNodeIdentityStore() {
17377
17889
  const pins = /* @__PURE__ */ new Map();
17378
17890
  return {
@@ -17505,21 +18017,80 @@ async function verifyLocationRecord(input) {
17505
18017
  }
17506
18018
  return false;
17507
18019
  }
17508
- async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch) {
18020
+ async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch, signal) {
17509
18021
  const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
17510
- const response = await fetchFn(url);
18022
+ const response = await fetchFn(url, {
18023
+ redirect: "error",
18024
+ headers: { accept: "application/json" },
18025
+ ...signal === void 0 ? {} : { signal }
18026
+ });
17511
18027
  if (response.status === 404) {
17512
18028
  return null;
17513
18029
  }
17514
18030
  if (!response.ok) {
17515
18031
  throw new Error(`location registry returned HTTP ${response.status}`);
17516
18032
  }
17517
- const body = await response.json();
18033
+ const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
18034
+ let body;
18035
+ try {
18036
+ body = JSON.parse(text2);
18037
+ } catch {
18038
+ throw new LocationRecordValidationError("registry response is not JSON");
18039
+ }
17518
18040
  if (body.record === void 0) {
17519
18041
  throw new LocationRecordValidationError("registry response missing record");
17520
18042
  }
17521
18043
  return validateLocationRecord(body.record);
17522
18044
  }
18045
+ async function publishLocationRecord(options2) {
18046
+ validateSubject(options2.subject);
18047
+ const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
18048
+ const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
18049
+ const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
18050
+ const current = await fetchLocationRecord(registryUrl, options2.subject, fetchFn);
18051
+ if (current !== null) {
18052
+ if (current.subject !== options2.subject || !await verifyLocationRecord(current)) {
18053
+ throw new LocationRecordValidationError("existing location record signature is invalid");
18054
+ }
18055
+ if (publishedHttpOrigins(current).includes(nodeOrigin)) {
18056
+ return current;
18057
+ }
18058
+ }
18059
+ const record2 = await signLocationRecord({
18060
+ version: 1,
18061
+ subject: options2.subject,
18062
+ multiaddrs: [...current?.multiaddrs ?? [], httpUrlToMultiaddr(nodeOrigin)],
18063
+ updated_at: (options2.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
18064
+ sequence: (current?.sequence ?? -1) + 1
18065
+ }, options2.signer);
18066
+ const response = await fetchFn(
18067
+ `${registryUrl}/v1/locations/${encodeURIComponent(options2.subject)}`,
18068
+ {
18069
+ method: "PUT",
18070
+ redirect: "error",
18071
+ headers: { accept: "application/json", "content-type": "application/json" },
18072
+ body: JSON.stringify(record2)
18073
+ }
18074
+ );
18075
+ if (!response.ok) {
18076
+ throw new Error(`location registry publish returned HTTP ${response.status}`);
18077
+ }
18078
+ const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
18079
+ let body;
18080
+ try {
18081
+ body = JSON.parse(text2);
18082
+ } catch {
18083
+ throw new LocationRecordValidationError("registry publish response is not JSON");
18084
+ }
18085
+ if (body.record === void 0) {
18086
+ throw new LocationRecordValidationError("registry publish response missing record");
18087
+ }
18088
+ const published = validateLocationRecord(body.record);
18089
+ if (canonicalLocationPayload(locationPayloadForRecord(published)) !== canonicalLocationPayload(locationPayloadForRecord(record2)) || published.signature !== record2.signature || !await verifyLocationRecord(published)) {
18090
+ throw new LocationRecordValidationError("registry publish response does not match the signed record");
18091
+ }
18092
+ return published;
18093
+ }
17523
18094
  async function resolveCloudLocation(subject, options2 = {}) {
17524
18095
  validateSubject(subject);
17525
18096
  const verifyRecords = options2.verifyRecords ?? true;
@@ -17770,6 +18341,123 @@ function multiaddrToHttpUrl(input) {
17770
18341
  }
17771
18342
  return uri;
17772
18343
  }
18344
+ function exactHttpOrigin(value, label) {
18345
+ let url;
18346
+ try {
18347
+ url = new URL(value);
18348
+ } catch {
18349
+ throw new LocationRecordValidationError(`${label} must be an absolute URL origin`);
18350
+ }
18351
+ const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
18352
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
18353
+ throw new LocationRecordValidationError(`${label} must use HTTPS (or loopback HTTP)`);
18354
+ }
18355
+ if (url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
18356
+ throw new LocationRecordValidationError(`${label} must be an origin without credentials, path, query, or fragment`);
18357
+ }
18358
+ return url.origin;
18359
+ }
18360
+ function publishedHttpOrigins(record2) {
18361
+ const origins = [];
18362
+ for (const address of record2.multiaddrs) {
18363
+ try {
18364
+ origins.push(exactHttpOrigin(multiaddrToHttpUrl(address), "published node URL"));
18365
+ } catch {
18366
+ }
18367
+ }
18368
+ return origins;
18369
+ }
18370
+ async function boundedResponseText(response, label, maxBytes) {
18371
+ const declaredLength = response.headers.get("content-length");
18372
+ if (declaredLength !== null && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > maxBytes)) {
18373
+ throw new LocationRecordValidationError(`${label} response is too large`);
18374
+ }
18375
+ if (response.body === null) return "";
18376
+ const reader = response.body.getReader();
18377
+ const chunks = [];
18378
+ let total = 0;
18379
+ try {
18380
+ while (true) {
18381
+ const { done, value } = await reader.read();
18382
+ if (done) break;
18383
+ if (value === void 0) continue;
18384
+ total += value.byteLength;
18385
+ if (total > maxBytes) {
18386
+ await reader.cancel().catch(() => void 0);
18387
+ throw new LocationRecordValidationError(`${label} response is too large`);
18388
+ }
18389
+ chunks.push(value);
18390
+ }
18391
+ } finally {
18392
+ reader.releaseLock();
18393
+ }
18394
+ const bytes2 = new Uint8Array(total);
18395
+ let offset = 0;
18396
+ for (const chunk of chunks) {
18397
+ bytes2.set(chunk, offset);
18398
+ offset += chunk.byteLength;
18399
+ }
18400
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
18401
+ }
18402
+ function boundedSignal(parent, timeoutMs) {
18403
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
18404
+ throw new LocationRecordValidationError("owner-node binding timeout must be positive");
18405
+ }
18406
+ const controller = new AbortController();
18407
+ const abortFromParent = () => controller.abort(parent?.reason);
18408
+ if (parent?.aborted) abortFromParent();
18409
+ else parent?.addEventListener("abort", abortFromParent, { once: true });
18410
+ const timer = setTimeout(() => controller.abort(new DOMException("owner-node binding timed out", "TimeoutError")), timeoutMs);
18411
+ return {
18412
+ signal: controller.signal,
18413
+ dispose() {
18414
+ clearTimeout(timer);
18415
+ parent?.removeEventListener("abort", abortFromParent);
18416
+ }
18417
+ };
18418
+ }
18419
+ async function verifyOwnerNodeBinding(options2) {
18420
+ validateSubject(options2.ownerDid);
18421
+ validateSubject(options2.nodeDid);
18422
+ const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
18423
+ const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
18424
+ const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
18425
+ const lookup = boundedSignal(options2.signal, options2.timeoutMs ?? OWNER_NODE_BINDING_TIMEOUT_MS);
18426
+ try {
18427
+ const record2 = await fetchLocationRecord(registryUrl, options2.ownerDid, fetchFn, lookup.signal);
18428
+ if (record2 === null) {
18429
+ throw new LocationRecordValidationError("owner has no published location record");
18430
+ }
18431
+ if (record2.subject !== options2.ownerDid || !await verifyLocationRecord(record2)) {
18432
+ throw new LocationRecordValidationError("owner location record signature is invalid");
18433
+ }
18434
+ const publishedOrigins = publishedHttpOrigins(record2);
18435
+ if (!publishedOrigins.includes(nodeOrigin)) {
18436
+ throw new LocationRecordValidationError("share target is not in the owner's signed location record");
18437
+ }
18438
+ const response = await fetchFn(new URL("/info", nodeOrigin), {
18439
+ redirect: "error",
18440
+ headers: { accept: "application/json" },
18441
+ signal: lookup.signal
18442
+ });
18443
+ if (!response.ok) {
18444
+ throw new LocationRecordValidationError(`target node /info returned HTTP ${response.status}`);
18445
+ }
18446
+ const text2 = await boundedResponseText(response, "target node /info", 16 * 1024);
18447
+ let body;
18448
+ try {
18449
+ body = JSON.parse(text2);
18450
+ } catch {
18451
+ throw new LocationRecordValidationError("target node /info response is not JSON");
18452
+ }
18453
+ if (body === null || typeof body !== "object" || Array.isArray(body) || body.nodeId !== options2.nodeDid) {
18454
+ throw new LocationRecordValidationError("target node identity does not match the share attestation");
18455
+ }
18456
+ return Object.freeze({ record: record2, nodeOrigin, nodeDid: options2.nodeDid });
18457
+ } finally {
18458
+ lookup.dispose();
18459
+ }
18460
+ }
17773
18461
  function httpUrlToMultiaddr(input) {
17774
18462
  const url = new URL(input);
17775
18463
  if (url.protocol !== "http:" && url.protocol !== "https:") {
@@ -17931,7 +18619,7 @@ async function verifyPkhSignature(did, payload, signature) {
17931
18619
  }
17932
18620
  function verifyDidKeySignature(did, payload, signature) {
17933
18621
  const publicKey = ed25519PublicKeyFromDidKey2(did);
17934
- const signatureBytes = decodeBase64Url3(signature);
18622
+ const signatureBytes = decodeBase64Url4(signature);
17935
18623
  if (signatureBytes.length !== 64) {
17936
18624
  throw new LocationRecordValidationError(
17937
18625
  "did:key signature must be a base64url Ed25519 signature"
@@ -17984,7 +18672,7 @@ function base64UrlEncode3(bytes2) {
17984
18672
  }
17985
18673
  return output;
17986
18674
  }
17987
- function decodeBase64Url3(value) {
18675
+ function decodeBase64Url4(value) {
17988
18676
  const alphabet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
17989
18677
  const bytes2 = [];
17990
18678
  let buffer = 0;