@tinycloud/sdk-core 3.0.0-beta.1 → 3.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -311,6 +311,7 @@ var CREDENTIAL_ERROR_CODES = [
311
311
  "ISSUER_UNREADY",
312
312
  "OFFLINE",
313
313
  "POPUP_BLOCKED",
314
+ "PROOF_REJECTED",
314
315
  "REQUEST_EXPIRED",
315
316
  "REQUEST_SUBSTITUTED",
316
317
  "SIGNATURE_REJECTED",
@@ -324,6 +325,7 @@ var RECOVERABLE = /* @__PURE__ */ new Set([
324
325
  "ISSUER_UNREADY",
325
326
  "OFFLINE",
326
327
  "POPUP_BLOCKED",
328
+ "PROOF_REJECTED",
327
329
  "REQUEST_EXPIRED",
328
330
  "SIGNATURE_REJECTED",
329
331
  "UNSUPPORTED_PROFILE",
@@ -565,6 +567,17 @@ function isDigest(value) {
565
567
 
566
568
  // src/credentials/requirement.ts
567
569
  var ID2 = /^[a-z0-9][a-z0-9._-]{0,127}(?:\/v1)?$/;
570
+ var EMAIL_CREDENTIAL_MAX_AGE_SECONDS = 3600;
571
+ function createEmailCredentialRequirement(input) {
572
+ return validateCredentialRequirement({
573
+ type: "TinyCloudCredentialRequirement",
574
+ version: 1,
575
+ profile: input.profile,
576
+ credentialType: input.credentialType,
577
+ claims: { email: input.email },
578
+ maxAgeSeconds: EMAIL_CREDENTIAL_MAX_AGE_SECONDS
579
+ });
580
+ }
568
581
  function validateCredentialRequirement(value) {
569
582
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential requirement is invalid");
570
583
  const raw = value;
@@ -1779,16 +1792,16 @@ function policyCapabilityContains(authority, request) {
1779
1792
  return caveatsContain(auth.caveats, req.caveats);
1780
1793
  }
1781
1794
  function normalizePolicyCapabilityWithOptions(input, options2) {
1782
- const object3 = expectObject(input, "$");
1783
- assertExactKeys(object3, ["service", "space", "path", "actions", "caveats"], "$");
1784
- const service = requiredString(object3, "service", "$", "policy-capability-malformed-service");
1795
+ const object4 = expectObject(input, "$");
1796
+ assertExactKeys(object4, ["service", "space", "path", "actions", "caveats"], "$");
1797
+ const service = requiredString(object4, "service", "$", "policy-capability-malformed-service");
1785
1798
  if (!CEILING_SERVICES.has(service)) {
1786
1799
  throw new PolicyCapabilityError(
1787
1800
  "policy-capability-malformed-service",
1788
1801
  "$.service is outside the frozen permissionsCeiling vocabulary"
1789
1802
  );
1790
1803
  }
1791
- const space = requiredString(object3, "space", "$", "policy-capability-malformed-space");
1804
+ const space = requiredString(object4, "space", "$", "policy-capability-malformed-space");
1792
1805
  validateConcreteSpace(space);
1793
1806
  const canonicalSpace = space.normalize("NFC");
1794
1807
  if (options2.requireCanonical && canonicalSpace !== space) {
@@ -1797,7 +1810,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
1797
1810
  "$.space must already be canonical NFC"
1798
1811
  );
1799
1812
  }
1800
- const rawPath = requiredString(object3, "path", "$", "policy-capability-malformed-path");
1813
+ const rawPath = requiredString(object4, "path", "$", "policy-capability-malformed-path");
1801
1814
  validateRawPath(rawPath, options2);
1802
1815
  const path = normalizePath(rawPath);
1803
1816
  if (options2.requireCanonical && path !== rawPath) {
@@ -1808,7 +1821,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
1808
1821
  }
1809
1822
  validateRawPath(path, options2);
1810
1823
  const actions = normalizeActions(
1811
- requiredArray(object3, "actions", "$", "policy-capability-malformed-action"),
1824
+ requiredArray(object4, "actions", "$", "policy-capability-malformed-action"),
1812
1825
  service,
1813
1826
  options2
1814
1827
  );
@@ -1817,9 +1830,9 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
1817
1830
  output.space = canonicalSpace;
1818
1831
  output.path = path;
1819
1832
  output.actions = actions;
1820
- if (hasOwn(object3, "caveats")) {
1833
+ if (hasOwn(object4, "caveats")) {
1821
1834
  output.caveats = validateCaveats(
1822
- requiredValue(object3, "caveats", "$", "policy-capability-malformed-caveats"),
1835
+ requiredValue(object4, "caveats", "$", "policy-capability-malformed-caveats"),
1823
1836
  output.service
1824
1837
  );
1825
1838
  }
@@ -1842,9 +1855,9 @@ function expectObject(input, path) {
1842
1855
  );
1843
1856
  }
1844
1857
  }
1845
- function assertExactKeys(object3, allowed, path) {
1858
+ function assertExactKeys(object4, allowed, path) {
1846
1859
  const allowedSet = new Set(allowed);
1847
- for (const key of Object.keys(object3)) {
1860
+ for (const key of Object.keys(object4)) {
1848
1861
  if (!allowedSet.has(key)) {
1849
1862
  throw new PolicyCapabilityError(
1850
1863
  key === "id" || key === "scope" ? "policy-capability-malformed" : "policy-capability-unknown-key",
@@ -1853,21 +1866,21 @@ function assertExactKeys(object3, allowed, path) {
1853
1866
  }
1854
1867
  }
1855
1868
  }
1856
- function requiredValue(object3, key, path, code3) {
1857
- if (!hasOwn(object3, key)) {
1869
+ function requiredValue(object4, key, path, code3) {
1870
+ if (!hasOwn(object4, key)) {
1858
1871
  throw new PolicyCapabilityError(code3, `${path}.${key} is required`);
1859
1872
  }
1860
- return object3[key];
1873
+ return object4[key];
1861
1874
  }
1862
- function requiredString(object3, key, path, code3) {
1863
- const value = requiredValue(object3, key, path, code3);
1875
+ function requiredString(object4, key, path, code3) {
1876
+ const value = requiredValue(object4, key, path, code3);
1864
1877
  if (typeof value !== "string" || value.length === 0) {
1865
1878
  throw new PolicyCapabilityError(code3, `${path}.${key} must be a non-empty string`);
1866
1879
  }
1867
1880
  return value;
1868
1881
  }
1869
- function requiredArray(object3, key, path, code3) {
1870
- const value = requiredValue(object3, key, path, code3);
1882
+ function requiredArray(object4, key, path, code3) {
1883
+ const value = requiredValue(object4, key, path, code3);
1871
1884
  if (!Array.isArray(value)) {
1872
1885
  throw new PolicyCapabilityError(code3, `${path}.${key} must be an array`);
1873
1886
  }
@@ -2012,11 +2025,11 @@ function validateSqlStatement(input, path) {
2012
2025
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
2013
2026
  throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
2014
2027
  }
2015
- const object3 = input;
2016
- assertCaveatKeys(object3, ["name", "sql", "fixedParams"], path);
2017
- caveatString(object3, "name", path);
2018
- caveatString(object3, "sql", path);
2019
- const fixedParams = requiredCaveat(object3, "fixedParams", path);
2028
+ const object4 = input;
2029
+ assertCaveatKeys(object4, ["name", "sql", "fixedParams"], path);
2030
+ caveatString(object4, "name", path);
2031
+ caveatString(object4, "sql", path);
2032
+ const fixedParams = requiredCaveat(object4, "fixedParams", path);
2020
2033
  if (!Array.isArray(fixedParams)) {
2021
2034
  throw new PolicyCapabilityError(
2022
2035
  "policy-capability-malformed-caveats",
@@ -2031,20 +2044,20 @@ function validateFixedParam(input, path) {
2031
2044
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
2032
2045
  throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
2033
2046
  }
2034
- const object3 = input;
2035
- assertCaveatKeys(object3, ["index", "value"], path);
2036
- const index = requiredCaveat(object3, "index", path);
2047
+ const object4 = input;
2048
+ assertCaveatKeys(object4, ["index", "value"], path);
2049
+ const index = requiredCaveat(object4, "index", path);
2037
2050
  if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
2038
2051
  throw new PolicyCapabilityError(
2039
2052
  "policy-capability-malformed-caveats",
2040
2053
  `${path}.index must be a non-negative integer`
2041
2054
  );
2042
2055
  }
2043
- requiredCaveat(object3, "value", path);
2056
+ requiredCaveat(object4, "value", path);
2044
2057
  }
2045
- function assertCaveatKeys(object3, allowed, path) {
2058
+ function assertCaveatKeys(object4, allowed, path) {
2046
2059
  const allowedSet = new Set(allowed);
2047
- for (const key of Object.keys(object3)) {
2060
+ for (const key of Object.keys(object4)) {
2048
2061
  if (!allowedSet.has(key)) {
2049
2062
  throw new PolicyCapabilityError(
2050
2063
  "policy-capability-malformed-caveats",
@@ -2053,17 +2066,17 @@ function assertCaveatKeys(object3, allowed, path) {
2053
2066
  }
2054
2067
  }
2055
2068
  }
2056
- function requiredCaveat(object3, key, path) {
2057
- if (!hasOwn(object3, key)) {
2069
+ function requiredCaveat(object4, key, path) {
2070
+ if (!hasOwn(object4, key)) {
2058
2071
  throw new PolicyCapabilityError(
2059
2072
  "policy-capability-malformed-caveats",
2060
2073
  `${path}.${key} is required`
2061
2074
  );
2062
2075
  }
2063
- return object3[key];
2076
+ return object4[key];
2064
2077
  }
2065
- function caveatString(object3, key, path) {
2066
- const value = requiredCaveat(object3, key, path);
2078
+ function caveatString(object4, key, path) {
2079
+ const value = requiredCaveat(object4, key, path);
2067
2080
  if (typeof value !== "string" || value.length === 0) {
2068
2081
  throw new PolicyCapabilityError(
2069
2082
  "policy-capability-malformed-caveats",
@@ -2094,8 +2107,8 @@ function caveatsContain(authority, request) {
2094
2107
  (statement) => authorityStatements.some((candidate) => jcsCanonicalize(candidate) === jcsCanonicalize(statement))
2095
2108
  );
2096
2109
  }
2097
- function hasOwn(object3, key) {
2098
- return objectHasOwn2(object3, key);
2110
+ function hasOwn(object4, key) {
2111
+ return objectHasOwn2(object4, key);
2099
2112
  }
2100
2113
 
2101
2114
  // src/policy/signed-object.ts
@@ -2423,17 +2436,17 @@ async function createAndSignSignedObject(input, signer) {
2423
2436
  }
2424
2437
  function createAndSignPolicy(input, signer) {
2425
2438
  return createAndSignSignedObject(input, signer).then(
2426
- (object3) => validatePolicySignedShape(object3)
2439
+ (object4) => validatePolicySignedShape(object4)
2427
2440
  );
2428
2441
  }
2429
2442
  function createAndSignPolicyStatus(input, signer) {
2430
2443
  return createAndSignSignedObject(input, signer).then(
2431
- (object3) => validatePolicyStatusSignedShape(object3)
2444
+ (object4) => validatePolicyStatusSignedShape(object4)
2432
2445
  );
2433
2446
  }
2434
2447
  function createAndSignPolicyEngineRecord(input, signer) {
2435
2448
  return createAndSignSignedObject(input, signer).then(
2436
- (object3) => validatePolicyEngineRecordSignedShape(object3)
2449
+ (object4) => validatePolicyEngineRecordSignedShape(object4)
2437
2450
  );
2438
2451
  }
2439
2452
  async function verifySignedObject(input) {
@@ -2537,9 +2550,9 @@ function validateUnsignedForDescriptor(input, descriptor) {
2537
2550
  }
2538
2551
  }
2539
2552
  function validatePolicyShape(input, signed) {
2540
- const object3 = expectJsonObject(normalizeJson(input), "$");
2553
+ const object4 = expectJsonObject(normalizeJson(input), "$");
2541
2554
  assertExactKeys2(
2542
- object3,
2555
+ object4,
2543
2556
  signed ? [
2544
2557
  "schema",
2545
2558
  "policyId",
@@ -2567,30 +2580,30 @@ function validatePolicyShape(input, signed) {
2567
2580
  ],
2568
2581
  "$"
2569
2582
  );
2570
- expectConst(requiredString2(object3, "schema", "$"), POLICY_SCHEMA, "$.schema");
2583
+ expectConst(requiredString2(object4, "schema", "$"), POLICY_SCHEMA, "$.schema");
2571
2584
  if (signed) {
2572
- requiredString2(object3, "policyId", "$");
2573
- validateSignature(requiredValue2(object3, "signature", "$"));
2585
+ requiredString2(object4, "policyId", "$");
2586
+ validateSignature(requiredValue2(object4, "signature", "$"));
2574
2587
  }
2575
- requiredString2(object3, "ownerDid", "$");
2576
- requiredString2(object3, "signingKeyDid", "$");
2577
- requiredDateString(object3, "createdAt", "$");
2578
- optionalDateString(object3, "expiresAt", "$");
2579
- validatePolicyResource(requiredValue2(object3, "resource", "$"), "$.resource");
2580
- validateExpression(requiredValue2(object3, "when", "$"), "$.when");
2581
- validateGrant(requiredValue2(object3, "grant", "$"), "$.grant");
2582
- if (hasOwn2(object3, "disclosure")) {
2583
- validateDisclosure(requiredValue2(object3, "disclosure", "$"), "$.disclosure");
2588
+ requiredString2(object4, "ownerDid", "$");
2589
+ requiredString2(object4, "signingKeyDid", "$");
2590
+ requiredDateString(object4, "createdAt", "$");
2591
+ optionalDateString(object4, "expiresAt", "$");
2592
+ validatePolicyResource(requiredValue2(object4, "resource", "$"), "$.resource");
2593
+ validateExpression(requiredValue2(object4, "when", "$"), "$.when");
2594
+ validateGrant(requiredValue2(object4, "grant", "$"), "$.grant");
2595
+ if (hasOwn2(object4, "disclosure")) {
2596
+ validateDisclosure(requiredValue2(object4, "disclosure", "$"), "$.disclosure");
2584
2597
  }
2585
- if (hasOwn2(object3, "audit")) {
2586
- validateAudit(requiredValue2(object3, "audit", "$"), "$.audit");
2598
+ if (hasOwn2(object4, "audit")) {
2599
+ validateAudit(requiredValue2(object4, "audit", "$"), "$.audit");
2587
2600
  }
2588
- return object3;
2601
+ return object4;
2589
2602
  }
2590
2603
  function validatePolicyStatusShape(input, signed) {
2591
- const object3 = expectJsonObject(normalizeJson(input), "$");
2604
+ const object4 = expectJsonObject(normalizeJson(input), "$");
2592
2605
  assertExactKeys2(
2593
- object3,
2606
+ object4,
2594
2607
  signed ? [
2595
2608
  "schema",
2596
2609
  "statusId",
@@ -2614,28 +2627,28 @@ function validatePolicyStatusShape(input, signed) {
2614
2627
  ],
2615
2628
  "$"
2616
2629
  );
2617
- expectConst(requiredString2(object3, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
2630
+ expectConst(requiredString2(object4, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
2618
2631
  if (signed) {
2619
- requiredString2(object3, "statusId", "$");
2620
- validateSignature(requiredValue2(object3, "signature", "$"));
2632
+ requiredString2(object4, "statusId", "$");
2633
+ validateSignature(requiredValue2(object4, "signature", "$"));
2621
2634
  }
2622
- requiredString2(object3, "policyId", "$");
2623
- requiredString2(object3, "ownerDid", "$");
2624
- requiredInteger(object3, "sequence", "$", 0);
2625
- expectOneOf(requiredString2(object3, "disposition", "$"), [
2635
+ requiredString2(object4, "policyId", "$");
2636
+ requiredString2(object4, "ownerDid", "$");
2637
+ requiredInteger(object4, "sequence", "$", 0);
2638
+ expectOneOf(requiredString2(object4, "disposition", "$"), [
2626
2639
  "active",
2627
2640
  "suspended",
2628
2641
  "revoked"
2629
2642
  ], "$.disposition");
2630
- requiredDateString(object3, "effectiveAt", "$");
2631
- optionalString(object3, "reasonCode", "$");
2632
- requiredString2(object3, "signingKeyDid", "$");
2633
- return object3;
2643
+ requiredDateString(object4, "effectiveAt", "$");
2644
+ optionalString(object4, "reasonCode", "$");
2645
+ requiredString2(object4, "signingKeyDid", "$");
2646
+ return object4;
2634
2647
  }
2635
2648
  function validatePolicyEngineRecordShape(input, signed) {
2636
- const object3 = expectJsonObject(normalizeJson(input), "$");
2649
+ const object4 = expectJsonObject(normalizeJson(input), "$");
2637
2650
  assertExactKeys2(
2638
- object3,
2651
+ object4,
2639
2652
  signed ? [
2640
2653
  "schema",
2641
2654
  "engineRecordId",
@@ -2660,44 +2673,44 @@ function validatePolicyEngineRecordShape(input, signed) {
2660
2673
  "$"
2661
2674
  );
2662
2675
  expectConst(
2663
- requiredString2(object3, "schema", "$"),
2676
+ requiredString2(object4, "schema", "$"),
2664
2677
  POLICY_ENGINE_RECORD_SCHEMA,
2665
2678
  "$.schema"
2666
2679
  );
2667
2680
  if (signed) {
2668
- requiredString2(object3, "engineRecordId", "$");
2669
- validateSignature(requiredValue2(object3, "signature", "$"));
2681
+ requiredString2(object4, "engineRecordId", "$");
2682
+ validateSignature(requiredValue2(object4, "signature", "$"));
2670
2683
  }
2671
- requiredString2(object3, "ownerDid", "$");
2672
- requiredString2(object3, "endpoint", "$");
2673
- requiredString2(object3, "audience", "$");
2684
+ requiredString2(object4, "ownerDid", "$");
2685
+ requiredString2(object4, "endpoint", "$");
2686
+ requiredString2(object4, "audience", "$");
2674
2687
  requiredStringArray(
2675
- object3,
2688
+ object4,
2676
2689
  "supportedPolicyVersions",
2677
2690
  "$",
2678
2691
  (value, path) => expectConst(value, "v0", path)
2679
2692
  );
2680
- requiredStringArray(object3, "supportedEvidenceVerifiers", "$");
2681
- requiredString2(object3, "grantIssuerDid", "$");
2682
- requiredDateString(object3, "expiresAt", "$");
2683
- return object3;
2693
+ requiredStringArray(object4, "supportedEvidenceVerifiers", "$");
2694
+ requiredString2(object4, "grantIssuerDid", "$");
2695
+ requiredDateString(object4, "expiresAt", "$");
2696
+ return object4;
2684
2697
  }
2685
2698
  function validateSignature(input) {
2686
- const object3 = expectJsonObject(input, "$.signature");
2687
- assertExactKeys2(object3, ["suite", "signerDid", "value"], "$.signature");
2688
- const suite = requiredString2(object3, "suite", "$.signature");
2699
+ const object4 = expectJsonObject(input, "$.signature");
2700
+ assertExactKeys2(object4, ["suite", "signerDid", "value"], "$.signature");
2701
+ const suite = requiredString2(object4, "suite", "$.signature");
2689
2702
  assertSupportedSignatureSuite(suite);
2690
- const signerDid = requiredString2(object3, "signerDid", "$.signature");
2691
- const value = requiredString2(object3, "value", "$.signature");
2703
+ const signerDid = requiredString2(object4, "signerDid", "$.signature");
2704
+ const value = requiredString2(object4, "value", "$.signature");
2692
2705
  decodeSignatureValue(value, suite);
2693
2706
  return { suite, signerDid, value };
2694
2707
  }
2695
2708
  function validatePolicyResource(input, path) {
2696
- const object3 = expectJsonObject(input, path);
2697
- assertExactKeys2(object3, ["resourceType", "resourceId", "permissionsCeiling"], path);
2698
- requiredString2(object3, "resourceType", path);
2699
- requiredString2(object3, "resourceId", path);
2700
- const ceiling = requiredArray2(object3, "permissionsCeiling", path, 1);
2709
+ const object4 = expectJsonObject(input, path);
2710
+ assertExactKeys2(object4, ["resourceType", "resourceId", "permissionsCeiling"], path);
2711
+ requiredString2(object4, "resourceType", path);
2712
+ requiredString2(object4, "resourceId", path);
2713
+ const ceiling = requiredArray2(object4, "permissionsCeiling", path, 1);
2701
2714
  for (let index = 0; index < ceiling.length; index++) {
2702
2715
  validatePolicyCapability(ceiling[index], `${path}.permissionsCeiling[${index}]`);
2703
2716
  }
@@ -2713,10 +2726,10 @@ function validatePolicyPermissionsCeilingForSigning(input) {
2713
2726
  }
2714
2727
  }
2715
2728
  function validatePolicyCapability(input, path) {
2716
- const object3 = expectJsonObject(input, path);
2729
+ const object4 = expectJsonObject(input, path);
2717
2730
  try {
2718
- const canonical = canonicalizePolicyCapability(object3);
2719
- if (jcsCanonicalize(object3) !== jcsCanonicalize(canonical)) {
2731
+ const canonical = canonicalizePolicyCapability(object4);
2732
+ if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
2720
2733
  throw new SignedObjectSchemaError(`${path} must be canonical PolicyCapability JSON`);
2721
2734
  }
2722
2735
  } catch (error) {
@@ -2729,10 +2742,10 @@ function validatePolicyCapability(input, path) {
2729
2742
  }
2730
2743
  }
2731
2744
  function validatePolicyCapabilityForSigning(input, path) {
2732
- const object3 = expectJsonObject(input, path);
2745
+ const object4 = expectJsonObject(input, path);
2733
2746
  try {
2734
- const canonical = normalizePolicyCapability(object3);
2735
- if (jcsCanonicalize(object3) !== jcsCanonicalize(canonical)) {
2747
+ const canonical = normalizePolicyCapability(object4);
2748
+ if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
2736
2749
  throw new SignedObjectSchemaError(`${path} must be strict canonical PolicyCapability JSON`);
2737
2750
  }
2738
2751
  } catch (error) {
@@ -2745,43 +2758,43 @@ function validatePolicyCapabilityForSigning(input, path) {
2745
2758
  }
2746
2759
  }
2747
2760
  function validateExpression(input, path) {
2748
- const object3 = expectJsonObject(input, path);
2749
- const keys = Object.keys(object3);
2761
+ const object4 = expectJsonObject(input, path);
2762
+ const keys = Object.keys(object4);
2750
2763
  if (keys.length !== 1) {
2751
2764
  throw new SignedObjectSchemaError(`${path} must have exactly one expression key`);
2752
2765
  }
2753
2766
  const key = keys[0];
2754
2767
  if (key === "allOf" || key === "anyOf") {
2755
- const values = requiredArray2(object3, key, path, 1);
2768
+ const values = requiredArray2(object4, key, path, 1);
2756
2769
  for (let index = 0; index < values.length; index++) {
2757
2770
  validateExpression(values[index], `${path}.${key}[${index}]`);
2758
2771
  }
2759
2772
  return;
2760
2773
  }
2761
2774
  if (key === "subject") {
2762
- const subject = expectJsonObject(requiredValue2(object3, "subject", path), `${path}.subject`);
2775
+ const subject = expectJsonObject(requiredValue2(object4, "subject", path), `${path}.subject`);
2763
2776
  assertExactKeys2(subject, ["did"], `${path}.subject`);
2764
2777
  requiredString2(subject, "did", `${path}.subject`);
2765
2778
  return;
2766
2779
  }
2767
2780
  if (key === "evidence") {
2768
- validateEvidenceRequirement(requiredValue2(object3, "evidence", path), `${path}.evidence`);
2781
+ validateEvidenceRequirement(requiredValue2(object4, "evidence", path), `${path}.evidence`);
2769
2782
  return;
2770
2783
  }
2771
2784
  throw new SignedObjectSchemaError(`${path} has unknown expression key ${key}`);
2772
2785
  }
2773
2786
  function validateEvidenceRequirement(input, path) {
2774
- const object3 = expectJsonObject(input, path);
2787
+ const object4 = expectJsonObject(input, path);
2775
2788
  assertExactKeys2(
2776
- object3,
2789
+ object4,
2777
2790
  ["requirementId", "verifier", "requirements", "authority", "freshness"],
2778
2791
  path
2779
2792
  );
2780
- requiredString2(object3, "requirementId", path);
2781
- requiredString2(object3, "verifier", path);
2782
- requiredValue2(object3, "requirements", path);
2783
- if (hasOwn2(object3, "authority")) {
2784
- const authority = expectJsonObject(requiredValue2(object3, "authority", path), `${path}.authority`);
2793
+ requiredString2(object4, "requirementId", path);
2794
+ requiredString2(object4, "verifier", path);
2795
+ requiredValue2(object4, "requirements", path);
2796
+ if (hasOwn2(object4, "authority")) {
2797
+ const authority = expectJsonObject(requiredValue2(object4, "authority", path), `${path}.authority`);
2785
2798
  assertExactKeys2(
2786
2799
  authority,
2787
2800
  ["profile", "acceptedIssuers", "allowOwnerAuthorizedIssuer"],
@@ -2793,35 +2806,35 @@ function validateEvidenceRequirement(input, path) {
2793
2806
  }
2794
2807
  optionalBoolean(authority, "allowOwnerAuthorizedIssuer", `${path}.authority`);
2795
2808
  }
2796
- if (hasOwn2(object3, "freshness")) {
2797
- const freshness = expectJsonObject(requiredValue2(object3, "freshness", path), `${path}.freshness`);
2809
+ if (hasOwn2(object4, "freshness")) {
2810
+ const freshness = expectJsonObject(requiredValue2(object4, "freshness", path), `${path}.freshness`);
2798
2811
  assertExactKeys2(freshness, ["maxStatusAgeSeconds"], `${path}.freshness`);
2799
2812
  requiredInteger(freshness, "maxStatusAgeSeconds", `${path}.freshness`, 0);
2800
2813
  }
2801
2814
  }
2802
2815
  function validateGrant(input, path) {
2803
- const object3 = expectJsonObject(input, path);
2804
- assertExactKeys2(object3, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
2805
- expectConst(requiredString2(object3, "output", path), "portable-delegation", `${path}.output`);
2806
- requiredInteger(object3, "maxTtlSeconds", path, 1);
2807
- expectOneOf(requiredString2(object3, "delegationMode", path), [
2816
+ const object4 = expectJsonObject(input, path);
2817
+ assertExactKeys2(object4, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
2818
+ expectConst(requiredString2(object4, "output", path), "portable-delegation", `${path}.output`);
2819
+ requiredInteger(object4, "maxTtlSeconds", path, 1);
2820
+ expectOneOf(requiredString2(object4, "delegationMode", path), [
2808
2821
  "terminal",
2809
2822
  "attenuable"
2810
2823
  ], `${path}.delegationMode`);
2811
- expectOneOf(requiredString2(object3, "revocation", path), [
2824
+ expectOneOf(requiredString2(object4, "revocation", path), [
2812
2825
  "refresh_only",
2813
2826
  "active_cutoff"
2814
2827
  ], `${path}.revocation`);
2815
2828
  }
2816
2829
  function validateDisclosure(input, path) {
2817
- const object3 = expectJsonObject(input, path);
2818
- assertExactKeys2(object3, ["denial"], path);
2819
- expectOneOf(requiredString2(object3, "denial", path), ["none", "code", "debug"], `${path}.denial`);
2830
+ const object4 = expectJsonObject(input, path);
2831
+ assertExactKeys2(object4, ["denial"], path);
2832
+ expectOneOf(requiredString2(object4, "denial", path), ["none", "code", "debug"], `${path}.denial`);
2820
2833
  }
2821
2834
  function validateAudit(input, path) {
2822
- const object3 = expectJsonObject(input, path);
2823
- assertExactKeys2(object3, ["issuance"], path);
2824
- expectOneOf(requiredString2(object3, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
2835
+ const object4 = expectJsonObject(input, path);
2836
+ assertExactKeys2(object4, ["issuance"], path);
2837
+ expectOneOf(requiredString2(object4, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
2825
2838
  }
2826
2839
  function materialForUnsigned(unsigned, descriptor) {
2827
2840
  const jcs = jcsCanonicalize(unsigned);
@@ -2970,9 +2983,9 @@ function encodeSignatureValue(value, suite) {
2970
2983
  decodeSignatureValue(encoded, suite);
2971
2984
  return encoded;
2972
2985
  }
2973
- function stripOwnIdAndSignature(object3, descriptor) {
2986
+ function stripOwnIdAndSignature(object4, descriptor) {
2974
2987
  const output = /* @__PURE__ */ Object.create(null);
2975
- for (const [key, value] of Object.entries(object3)) {
2988
+ for (const [key, value] of Object.entries(object4)) {
2976
2989
  if (key === descriptor.idField || key === "signature") {
2977
2990
  continue;
2978
2991
  }
@@ -2981,8 +2994,8 @@ function stripOwnIdAndSignature(object3, descriptor) {
2981
2994
  return output;
2982
2995
  }
2983
2996
  function descriptorForUnsigned(input) {
2984
- const object3 = expectJsonObject(normalizeJson(input), "$");
2985
- return descriptorForSchema(requiredString2(object3, "schema", "$"));
2997
+ const object4 = expectJsonObject(normalizeJson(input), "$");
2998
+ return descriptorForSchema(requiredString2(object4, "schema", "$"));
2986
2999
  }
2987
3000
  function descriptorForSchema(schema) {
2988
3001
  if (schema === POLICY_SCHEMA) return DESCRIPTORS.Policy;
@@ -2996,28 +3009,28 @@ function expectJsonObject(input, path) {
2996
3009
  }
2997
3010
  return input;
2998
3011
  }
2999
- function assertExactKeys2(object3, allowed, path) {
3012
+ function assertExactKeys2(object4, allowed, path) {
3000
3013
  const allowedSet = new Set(allowed);
3001
- for (const key of Object.keys(object3)) {
3014
+ for (const key of Object.keys(object4)) {
3002
3015
  if (!allowedSet.has(key)) {
3003
3016
  throw new SignedObjectSchemaError(`${path} has unknown field ${key}`);
3004
3017
  }
3005
3018
  }
3006
3019
  }
3007
- function requiredValue2(object3, key, path) {
3008
- if (!hasOwn2(object3, key)) {
3020
+ function requiredValue2(object4, key, path) {
3021
+ if (!hasOwn2(object4, key)) {
3009
3022
  throw new SignedObjectSchemaError(`${path}.${key} is required`);
3010
3023
  }
3011
- return object3[key];
3024
+ return object4[key];
3012
3025
  }
3013
- function requiredString2(object3, key, path) {
3014
- return requireStringType(requiredValue2(object3, key, path), `${path}.${key}`);
3026
+ function requiredString2(object4, key, path) {
3027
+ return requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
3015
3028
  }
3016
- function optionalString(object3, key, path) {
3017
- if (!hasOwn2(object3, key)) {
3029
+ function optionalString(object4, key, path) {
3030
+ if (!hasOwn2(object4, key)) {
3018
3031
  return;
3019
3032
  }
3020
- requireStringType(requiredValue2(object3, key, path), `${path}.${key}`);
3033
+ requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
3021
3034
  }
3022
3035
  function requireStringType(value, path) {
3023
3036
  if (typeof value !== "string" || value.length === 0) {
@@ -3025,16 +3038,16 @@ function requireStringType(value, path) {
3025
3038
  }
3026
3039
  return value;
3027
3040
  }
3028
- function optionalBoolean(object3, key, path) {
3029
- if (!hasOwn2(object3, key)) {
3041
+ function optionalBoolean(object4, key, path) {
3042
+ if (!hasOwn2(object4, key)) {
3030
3043
  return;
3031
3044
  }
3032
- if (typeof requiredValue2(object3, key, path) !== "boolean") {
3045
+ if (typeof requiredValue2(object4, key, path) !== "boolean") {
3033
3046
  throw new SignedObjectSchemaError(`${path}.${key} must be a boolean`);
3034
3047
  }
3035
3048
  }
3036
- function requiredInteger(object3, key, path, minimum) {
3037
- const value = requiredValue2(object3, key, path);
3049
+ function requiredInteger(object4, key, path, minimum) {
3050
+ const value = requiredValue2(object4, key, path);
3038
3051
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) {
3039
3052
  throw new SignedObjectSchemaError(
3040
3053
  `${path}.${key} must be an integer >= ${minimum}`
@@ -3042,8 +3055,8 @@ function requiredInteger(object3, key, path, minimum) {
3042
3055
  }
3043
3056
  return value;
3044
3057
  }
3045
- function requiredArray2(object3, key, path, minimum) {
3046
- const value = requiredValue2(object3, key, path);
3058
+ function requiredArray2(object4, key, path, minimum) {
3059
+ const value = requiredValue2(object4, key, path);
3047
3060
  if (!Array.isArray(value) || value.length < minimum) {
3048
3061
  throw new SignedObjectSchemaError(
3049
3062
  `${path}.${key} must be an array with at least ${minimum} item(s)`
@@ -3051,22 +3064,22 @@ function requiredArray2(object3, key, path, minimum) {
3051
3064
  }
3052
3065
  return value;
3053
3066
  }
3054
- function requiredStringArray(object3, key, path, check) {
3055
- const values = requiredArray2(object3, key, path, 0);
3067
+ function requiredStringArray(object4, key, path, check) {
3068
+ const values = requiredArray2(object4, key, path, 0);
3056
3069
  for (let index = 0; index < values.length; index++) {
3057
3070
  const itemPath = `${path}.${key}[${index}]`;
3058
3071
  const value = requireStringType(values[index], itemPath);
3059
3072
  check?.(value, itemPath);
3060
3073
  }
3061
3074
  }
3062
- function requiredDateString(object3, key, path) {
3063
- assertRfc3339(requiredString2(object3, key, path), `${path}.${key}`);
3075
+ function requiredDateString(object4, key, path) {
3076
+ assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
3064
3077
  }
3065
- function optionalDateString(object3, key, path) {
3066
- if (!hasOwn2(object3, key)) {
3078
+ function optionalDateString(object4, key, path) {
3079
+ if (!hasOwn2(object4, key)) {
3067
3080
  return;
3068
3081
  }
3069
- assertRfc3339(requiredString2(object3, key, path), `${path}.${key}`);
3082
+ assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
3070
3083
  }
3071
3084
  function assertRfc3339(value, path) {
3072
3085
  const match = value.match(
@@ -3098,8 +3111,8 @@ function expectOneOf(actual, allowed, path) {
3098
3111
  }
3099
3112
  throw new SignedObjectSchemaError(`${path} has unsupported value ${actual}`);
3100
3113
  }
3101
- function hasOwn2(object3, key) {
3102
- return objectHasOwn3(object3, key);
3114
+ function hasOwn2(object4, key) {
3115
+ return objectHasOwn3(object4, key);
3103
3116
  }
3104
3117
  function assertSupportedSignatureSuite(suite) {
3105
3118
  if (suite !== ED25519_JCS_SIGNATURE_SUITE && suite !== EIP191_JCS_SIGNATURE_SUITE) {
@@ -3326,9 +3339,9 @@ function expectObject2(input, path, code3) {
3326
3339
  );
3327
3340
  }
3328
3341
  }
3329
- function assertExactKeys3(object3, allowed, path) {
3342
+ function assertExactKeys3(object4, allowed, path) {
3330
3343
  const allowedSet = new Set(allowed);
3331
- for (const key of Object.keys(object3)) {
3344
+ for (const key of Object.keys(object4)) {
3332
3345
  if (!allowedSet.has(key)) {
3333
3346
  throw new PolicyAuthoringError(
3334
3347
  "policy-authoring-unknown-key",
@@ -3337,17 +3350,17 @@ function assertExactKeys3(object3, allowed, path) {
3337
3350
  }
3338
3351
  }
3339
3352
  }
3340
- function requiredValue3(object3, key, path) {
3341
- if (!hasOwn3(object3, key)) {
3353
+ function requiredValue3(object4, key, path) {
3354
+ if (!hasOwn3(object4, key)) {
3342
3355
  throw new PolicyAuthoringError(
3343
3356
  "policy-authoring-malformed",
3344
3357
  `${path}.${key} is required`
3345
3358
  );
3346
3359
  }
3347
- return object3[key];
3360
+ return object4[key];
3348
3361
  }
3349
- function requiredString3(object3, key, path) {
3350
- const value = requiredValue3(object3, key, path);
3362
+ function requiredString3(object4, key, path) {
3363
+ const value = requiredValue3(object4, key, path);
3351
3364
  if (typeof value !== "string" || value.length === 0) {
3352
3365
  throw new PolicyAuthoringError(
3353
3366
  "policy-authoring-malformed",
@@ -3356,11 +3369,11 @@ function requiredString3(object3, key, path) {
3356
3369
  }
3357
3370
  return value;
3358
3371
  }
3359
- function fieldString(object3, key, path, code3) {
3360
- if (!hasOwn3(object3, key)) {
3372
+ function fieldString(object4, key, path, code3) {
3373
+ if (!hasOwn3(object4, key)) {
3361
3374
  throw new PolicyAuthoringError(code3, `${path}.${key} is required`);
3362
3375
  }
3363
- const value = object3[key];
3376
+ const value = object4[key];
3364
3377
  if (typeof value !== "string" || value.length === 0) {
3365
3378
  throw new PolicyAuthoringError(
3366
3379
  code3,
@@ -3398,8 +3411,8 @@ function parseStrictRfc3339(value, path) {
3398
3411
  var objectHasOwn4 = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
3399
3412
  Object.prototype.hasOwnProperty
3400
3413
  );
3401
- function hasOwn3(object3, key) {
3402
- return objectHasOwn4(object3, key);
3414
+ function hasOwn3(object4, key) {
3415
+ return objectHasOwn4(object4, key);
3403
3416
  }
3404
3417
 
3405
3418
  // src/policy/unified.ts
@@ -3426,9 +3439,11 @@ var MAX_LEGACY_ENVELOPE_EXPIRES_AT = "2026-12-29T00:00:00Z";
3426
3439
  var LAST_V2_READ_AT = "2027-01-05T00:00:00Z";
3427
3440
  async function requestPolicyChallengeV3(input) {
3428
3441
  const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
3429
- const response = await fetchFn(new URL("/share/v3/policy/challenges", input.nodeOrigin), {
3442
+ const policyRuntimePath = policyRuntimeBasePath(input.policyRuntimePath);
3443
+ const response = await fetchFn(new URL(`${policyRuntimePath}/challenges`, input.nodeOrigin), {
3430
3444
  method: "POST",
3431
3445
  redirect: "error",
3446
+ signal: input.signal,
3432
3447
  headers: { accept: "application/json", "content-type": "application/json" },
3433
3448
  body: JSON.stringify({ policyCid: input.policyCid, recipientDid: input.recipientDid, requestedCapabilities: input.requestedCapabilities.map(normalizeUnifiedPolicyCapability) })
3434
3449
  });
@@ -3438,6 +3453,13 @@ async function requestPolicyChallengeV3(input) {
3438
3453
  throw new Error("policy challenge binding is invalid");
3439
3454
  return value;
3440
3455
  }
3456
+ function policyRuntimeBasePath(value) {
3457
+ const path = value ?? "/policy/v3";
3458
+ if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
3459
+ throw new Error("policy runtime path is invalid");
3460
+ }
3461
+ return path;
3462
+ }
3441
3463
  function compactAttenuationForPolicyCapabilities(input) {
3442
3464
  const attenuation = {};
3443
3465
  for (const raw of input) {
@@ -3483,7 +3505,7 @@ function verifyPolicyRootStatusCheckpointV3(input) {
3483
3505
  }
3484
3506
  async function getPolicyRootStatusV3(input) {
3485
3507
  const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
3486
- const response = await fetchFn(new URL(`/share/v3/policy/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
3508
+ const response = await fetchFn(new URL(`/policy/v3/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
3487
3509
  method: "GET",
3488
3510
  redirect: "error",
3489
3511
  headers: { accept: "application/json" }
@@ -3542,7 +3564,7 @@ async function renewPolicyRootStatusV3(input) {
3542
3564
  };
3543
3565
  const signature = await input.sign(sha2564(new TextEncoder().encode(ROOT_STATUS_RENEWAL_V1_DOMAIN + jcsCanonicalize(unsigned))));
3544
3566
  if (signature.length !== 64) throw new Error("policy root renewal signature must be Ed25519");
3545
- const response = await fetchFn(new URL("/share/v3/policy/status", input.nodeOrigin), {
3567
+ const response = await fetchFn(new URL("/policy/v3/status", input.nodeOrigin), {
3546
3568
  method: "POST",
3547
3569
  redirect: "error",
3548
3570
  headers: { accept: "application/json", "content-type": "application/json" },
@@ -3598,6 +3620,11 @@ var POLICY_SESSION_FACT_KEYS = [
3598
3620
  "issuanceAuditDigestHex",
3599
3621
  "remainingRedelegationDepth"
3600
3622
  ];
3623
+ var POLICY_SESSION_V4_AUDIT_FACT_KEYS = [
3624
+ "credentialIdAuditDigestHex",
3625
+ "presentationJtiAuditDigestHex"
3626
+ ];
3627
+ var LOWER_SHA256_HEX = /^[0-9a-f]{64}$/;
3601
3628
  var KV_ACTIONS = /* @__PURE__ */ new Set([
3602
3629
  "tinycloud.kv/get",
3603
3630
  "tinycloud.kv/list",
@@ -3792,6 +3819,133 @@ function contentSourceDigestHex(input) {
3792
3819
  )
3793
3820
  );
3794
3821
  }
3822
+ function exactObject(value, keys, label) {
3823
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
3824
+ throw new Error(`${label} is invalid`);
3825
+ }
3826
+ const record2 = value;
3827
+ if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) {
3828
+ throw new Error(`${label} is invalid`);
3829
+ }
3830
+ return record2;
3831
+ }
3832
+ function didKeyEd25519PublicKey(value) {
3833
+ const principal = value.split("#", 1)[0];
3834
+ if (!principal.startsWith("did:key:")) {
3835
+ throw new Error("policy enforcer binding signer is invalid");
3836
+ }
3837
+ const material = base58btc.decode(principal.slice("did:key:".length));
3838
+ if (material.length !== 34 || material[0] !== 237 || material[1] !== 1) {
3839
+ throw new Error("policy enforcer binding signer is invalid");
3840
+ }
3841
+ return material.slice(2);
3842
+ }
3843
+ async function registerPolicyV3(input) {
3844
+ const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
3845
+ const rootExpiresAt = new Date(input.rootExpiresAt);
3846
+ const canonicalRootExpiresAt = new Date(
3847
+ Math.floor(rootExpiresAt.getTime() / 1e3) * 1e3
3848
+ ).toISOString().replace(/\.000Z$/, "Z");
3849
+ if (!Number.isFinite(rootExpiresAt.getTime()) || canonicalRootExpiresAt !== input.rootExpiresAt || rootExpiresAt.getTime() <= Date.now()) {
3850
+ throw new Error("policy root expiry is invalid");
3851
+ }
3852
+ const bindingResponse = await fetchFn(
3853
+ new URL("/policy/v3/enforcer-bindings", input.nodeOrigin),
3854
+ {
3855
+ method: "POST",
3856
+ redirect: "error",
3857
+ signal: input.signal,
3858
+ headers: { accept: "application/json", "content-type": "application/json" },
3859
+ body: JSON.stringify({
3860
+ rootExpiresAt: input.rootExpiresAt,
3861
+ enforcerDid: input.enforcerDid
3862
+ })
3863
+ }
3864
+ );
3865
+ if (!bindingResponse.ok) {
3866
+ throw new Error(`policy enforcer binding rejected (${bindingResponse.status})`);
3867
+ }
3868
+ const binding = exactObject(
3869
+ await bindingResponse.json(),
3870
+ [
3871
+ "schema",
3872
+ "enforcerDid",
3873
+ "nodeAudience",
3874
+ "attestationBindingDigestHex",
3875
+ "issuedAt",
3876
+ "expiresAt",
3877
+ "signature"
3878
+ ],
3879
+ "policy enforcer binding"
3880
+ );
3881
+ const signature = exactObject(
3882
+ binding.signature,
3883
+ ["suite", "signerDid", "value"],
3884
+ "policy enforcer binding signature"
3885
+ );
3886
+ const expectedBindingDigestHex = hex(
3887
+ sha2564(
3888
+ new TextEncoder().encode(
3889
+ jcsCanonicalize({
3890
+ enforcerDid: input.enforcerDid,
3891
+ nodeAudience: input.expectedNodeAudience
3892
+ })
3893
+ )
3894
+ )
3895
+ );
3896
+ 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") {
3897
+ throw new Error("policy enforcer binding is invalid");
3898
+ }
3899
+ const { signature: _signature, ...unsignedBinding } = binding;
3900
+ const signedBinding = sha2564(
3901
+ new TextEncoder().encode(
3902
+ `xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0${jcsCanonicalize(unsignedBinding)}`
3903
+ )
3904
+ );
3905
+ if (!ed255193.verify(
3906
+ decodeBase64Url2(signature.value),
3907
+ signedBinding,
3908
+ didKeyEd25519PublicKey(input.expectedNodeAudience),
3909
+ { zip215: false }
3910
+ )) {
3911
+ throw new Error("policy enforcer binding signature is invalid");
3912
+ }
3913
+ const response = await fetchFn(
3914
+ new URL("/policy/v3/policies", input.nodeOrigin),
3915
+ {
3916
+ method: "POST",
3917
+ redirect: "error",
3918
+ signal: input.signal,
3919
+ headers: { accept: "application/json", "content-type": "application/json" },
3920
+ body: JSON.stringify({
3921
+ policyCid: input.policyCid,
3922
+ policy: input.policy,
3923
+ policyRoot: input.policyRoot.authorization.replace(/^Bearer\s+/i, ""),
3924
+ enforcementRoot: input.enforcementRoot.authorization.replace(/^Bearer\s+/i, ""),
3925
+ contentSourceDigestHex: input.contentSourceDigestHex,
3926
+ nativeProjectionHashHex: input.nativeProjectionHashHex,
3927
+ attestedEnforcerBinding: binding
3928
+ })
3929
+ }
3930
+ );
3931
+ if (!response.ok) {
3932
+ throw new Error(`policy registration rejected (${response.status})`);
3933
+ }
3934
+ const receipt = exactObject(
3935
+ await response.json(),
3936
+ ["policyCid", "policyRootCid", "enforcementRootCid"],
3937
+ "policy registration response"
3938
+ );
3939
+ if (receipt.policyCid !== input.policyCid || receipt.policyRootCid !== input.policyRoot.cid || receipt.enforcementRootCid !== input.enforcementRoot.cid) {
3940
+ throw new Error("policy registration response binding is invalid");
3941
+ }
3942
+ return {
3943
+ policyCid: input.policyCid,
3944
+ policyRootCid: input.policyRoot.cid,
3945
+ enforcementRootCid: input.enforcementRoot.cid,
3946
+ attestedEnforcerBinding: binding
3947
+ };
3948
+ }
3795
3949
  function policyDigestHex(policy) {
3796
3950
  const canonicalPolicy = {
3797
3951
  ...policy,
@@ -3990,9 +4144,13 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
3990
4144
  if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
3991
4145
  throw new Error("policy session UCAN ordered proofs do not match");
3992
4146
  const factKeys = Object.keys(fact);
3993
- 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)))
4147
+ 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);
4148
+ 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);
4149
+ if (!hasLegacyFactShape && !hasV4AuditFactShape)
3994
4150
  throw new Error("policy session UCAN fact is incomplete");
3995
- 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)
4151
+ if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
4152
+ throw new Error("policy session UCAN v4 audit facts are invalid");
4153
+ 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)
3996
4154
  throw new Error("policy session UCAN fact is invalid");
3997
4155
  return {
3998
4156
  authorization,
@@ -4051,6 +4209,8 @@ var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
4051
4209
  var POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE = "TinyCloudPolicyCredentialRequirement";
4052
4210
  var POLICY_PRESENTATION_V3_SCHEMA = "xyz.tinycloud.policy/presentation/v3";
4053
4211
  var POLICY_PRESENTATION_V3_DOMAIN = "xyz.tinycloud.policy/Presentation/v3\0";
4212
+ var POLICY_PRESENTATION_V4_SCHEMA = "xyz.tinycloud.policy/presentation/v4";
4213
+ var POLICY_PRESENTATION_V4_DOMAIN = "xyz.tinycloud.policy/Presentation/v4\0";
4054
4214
  function record(value, label) {
4055
4215
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
4056
4216
  throw new Error(`${label} must be an object`);
@@ -4225,6 +4385,97 @@ function envelopeFromVerified(verified) {
4225
4385
  } = verified;
4226
4386
  return envelope;
4227
4387
  }
4388
+ function issuedCredentialEnvelopeFromVerified(verified) {
4389
+ return envelopeFromVerified(verified);
4390
+ }
4391
+ async function signPolicyCredentialPresentationV4(value, sign) {
4392
+ const item = record(value, "Policy presentation v4");
4393
+ exactKeys2(item, [
4394
+ "schema",
4395
+ "jti",
4396
+ "challengeId",
4397
+ "nonce",
4398
+ "policyCid",
4399
+ "nodeAudience",
4400
+ "holderDid",
4401
+ "subjectDid",
4402
+ "credentialDigest",
4403
+ "requirementDigest",
4404
+ "descriptorDigest",
4405
+ "requestedCapabilities",
4406
+ "issuedAt",
4407
+ "expiresAt"
4408
+ ], "Policy presentation v4");
4409
+ if (item.schema !== POLICY_PRESENTATION_V4_SCHEMA || item.holderDid !== item.subjectDid || !Array.isArray(item.requestedCapabilities)) throw new Error("Policy presentation v4 is invalid");
4410
+ const holderDid = nonEmpty(item.holderDid, "Policy presentation v4 holder DID");
4411
+ didKeyBytes(holderDid);
4412
+ const unsigned = Object.freeze({
4413
+ schema: POLICY_PRESENTATION_V4_SCHEMA,
4414
+ jti: nonEmpty(item.jti, "Policy presentation v4 jti"),
4415
+ challengeId: nonEmpty(item.challengeId, "Policy presentation v4 challenge"),
4416
+ nonce: nonEmpty(item.nonce, "Policy presentation v4 nonce"),
4417
+ policyCid: nonEmpty(item.policyCid, "Policy presentation v4 policy CID"),
4418
+ nodeAudience: nonEmpty(item.nodeAudience, "Policy presentation v4 audience"),
4419
+ holderDid,
4420
+ subjectDid: holderDid,
4421
+ credentialDigest: nonEmpty(item.credentialDigest, "Policy presentation v4 credential digest"),
4422
+ requirementDigest: nonEmpty(item.requirementDigest, "Policy presentation v4 requirement digest"),
4423
+ descriptorDigest: nonEmpty(item.descriptorDigest, "Policy presentation v4 descriptor digest"),
4424
+ // The low-level signer preserves the frozen wire bytes exactly. The
4425
+ // policy-aware builder performs capability normalization and containment.
4426
+ requestedCapabilities: Object.freeze(item.requestedCapabilities),
4427
+ issuedAt: date(item.issuedAt, "Policy presentation v4 issuedAt"),
4428
+ expiresAt: date(item.expiresAt, "Policy presentation v4 expiresAt")
4429
+ });
4430
+ const digest4 = sha2565(new TextEncoder().encode(
4431
+ POLICY_PRESENTATION_V4_DOMAIN + jcsCanonicalize(unsigned)
4432
+ ));
4433
+ const signature = await sign(digest4);
4434
+ if (signature.length !== 64 || !ed255194.verify(signature, digest4, didKeyBytes(holderDid))) throw new Error("policy presentation signature is invalid");
4435
+ return Object.freeze({
4436
+ ...unsigned,
4437
+ signature: Object.freeze({
4438
+ suite: "Ed25519",
4439
+ signerDid: holderDid,
4440
+ value: encodeBase64Url(signature)
4441
+ })
4442
+ });
4443
+ }
4444
+ async function buildPolicyCredentialPresentationV4(input) {
4445
+ const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
4446
+ const requirement = validateCredentialRequirement(input.requirement);
4447
+ const commitment = policy.credentialRequirement;
4448
+ const requirementDigest = await credentialRequirementDigest(requirement);
4449
+ const credentialDigest = await sha256Base64Url(input.credential.credential);
4450
+ 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");
4451
+ const requestedCapabilities = input.requestedCapabilities.map(normalizeUnifiedPolicyCapability);
4452
+ 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");
4453
+ const holderDid = input.credential.holderDid;
4454
+ 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");
4455
+ const now = input.now ?? /* @__PURE__ */ new Date();
4456
+ const expiresAtMs = Math.min(
4457
+ now.getTime() + 6e4,
4458
+ Date.parse(input.challenge.expiresAt),
4459
+ Date.parse(input.credential.expiresAt)
4460
+ );
4461
+ if (expiresAtMs <= now.getTime()) throw new Error("policy challenge is expired");
4462
+ return signPolicyCredentialPresentationV4(Object.freeze({
4463
+ schema: POLICY_PRESENTATION_V4_SCHEMA,
4464
+ jti: input.jti ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
4465
+ challengeId: input.challenge.challengeId,
4466
+ nonce: input.challenge.nonce,
4467
+ policyCid: input.policyCid,
4468
+ nodeAudience: input.challenge.nodeAudience,
4469
+ holderDid,
4470
+ subjectDid: holderDid,
4471
+ credentialDigest,
4472
+ requirementDigest,
4473
+ descriptorDigest: input.credential.descriptorDigest,
4474
+ requestedCapabilities: Object.freeze(requestedCapabilities),
4475
+ issuedAt: rfc3339Seconds(now),
4476
+ expiresAt: rfc3339Seconds(new Date(expiresAtMs))
4477
+ }), input.sign);
4478
+ }
4228
4479
  async function buildPolicyCredentialPresentationV3(input) {
4229
4480
  const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
4230
4481
  const requirement = validateCredentialRequirement(input.requirement);
@@ -4311,10 +4562,12 @@ async function admitPolicyCredentialV3(input) {
4311
4562
  const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
4312
4563
  const challenge = await requestPolicyChallengeV3({
4313
4564
  nodeOrigin,
4565
+ policyRuntimePath: input.policyRuntimePath,
4314
4566
  policyCid: input.policyCid,
4315
4567
  recipientDid: input.credential.holderDid,
4316
4568
  requestedCapabilities: input.requestedCapabilities,
4317
- fetch: fetchFn
4569
+ fetch: fetchFn,
4570
+ signal: input.signal
4318
4571
  });
4319
4572
  const presentation = await buildPolicyCredentialPresentationV3({
4320
4573
  ...input,
@@ -4322,10 +4575,11 @@ async function admitPolicyCredentialV3(input) {
4322
4575
  challenge
4323
4576
  });
4324
4577
  const response = await fetchFn(
4325
- new URL("/share/v3/policy/delegations", nodeOrigin),
4578
+ new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin),
4326
4579
  {
4327
4580
  method: "POST",
4328
4581
  redirect: "error",
4582
+ signal: input.signal,
4329
4583
  headers: { accept: "application/json", "content-type": "application/json" },
4330
4584
  body: JSON.stringify({
4331
4585
  policyCid: input.policyCid,
@@ -4354,8 +4608,74 @@ async function admitPolicyCredentialV3(input) {
4354
4608
  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)) {
4355
4609
  throw new Error("policy delegation signed binding is invalid");
4356
4610
  }
4611
+ input.signal?.throwIfAborted();
4357
4612
  return Object.freeze({ challenge, presentation, session });
4358
4613
  }
4614
+ function validatePolicyCredentialAdmissionV4Authority(session, expected) {
4615
+ 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");
4616
+ }
4617
+ async function admitPolicyCredentialV4(input) {
4618
+ const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
4619
+ const nodeOrigin = policyNodeOrigin(input.nodeOrigin);
4620
+ const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
4621
+ const challenge = await requestPolicyChallengeV3({
4622
+ nodeOrigin,
4623
+ policyRuntimePath: input.policyRuntimePath,
4624
+ policyCid: input.policyCid,
4625
+ recipientDid: input.credential.holderDid,
4626
+ requestedCapabilities: input.requestedCapabilities,
4627
+ fetch: fetchFn,
4628
+ signal: input.signal
4629
+ });
4630
+ const presentation = await buildPolicyCredentialPresentationV4({
4631
+ ...input,
4632
+ policy,
4633
+ challenge
4634
+ });
4635
+ const response = await fetchFn(new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin), {
4636
+ method: "POST",
4637
+ redirect: "error",
4638
+ signal: input.signal,
4639
+ headers: { accept: "application/json", "content-type": "application/json" },
4640
+ body: JSON.stringify({
4641
+ policyCid: input.policyCid,
4642
+ challengeId: challenge.challengeId,
4643
+ nonce: challenge.nonce,
4644
+ requirement: validateCredentialRequirement(input.requirement),
4645
+ credential: envelopeFromVerified(input.credential),
4646
+ presentation
4647
+ })
4648
+ });
4649
+ if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
4650
+ const value = record(await response.json(), "policy delegation");
4651
+ if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string") throw new Error("policy delegation response is invalid");
4652
+ const session = parsePolicySessionUcan(value.authorization, [
4653
+ input.policyRootCid,
4654
+ input.enforcementRootCid
4655
+ ]);
4656
+ const expectedAttenuation = compactAttenuationForPolicyCapabilities(input.requestedCapabilities);
4657
+ validatePolicyCredentialAdmissionV4Authority(session, {
4658
+ nodeAudience: input.expectedNodeAudience,
4659
+ enforcerDid: input.expectedEnforcerDid
4660
+ });
4661
+ 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");
4662
+ const importResponse = await fetchFn(new URL("/delegate", nodeOrigin), {
4663
+ method: "POST",
4664
+ redirect: "error",
4665
+ signal: input.signal,
4666
+ headers: { Authorization: session.authorization }
4667
+ });
4668
+ if (!importResponse.ok) throw new Error(`delegation import rejected (${importResponse.status})`);
4669
+ input.signal?.throwIfAborted();
4670
+ return Object.freeze({ challenge, presentation, session, delegationImported: true });
4671
+ }
4672
+ function policyRuntimeBasePath2(value) {
4673
+ const path = value ?? "/policy/v3";
4674
+ if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
4675
+ throw new Error("policy runtime path is invalid");
4676
+ }
4677
+ return path;
4678
+ }
4359
4679
 
4360
4680
  // src/requester/index.ts
4361
4681
  var REQUESTER_NEAR_EXPIRY_SECONDS = 30;
@@ -7762,13 +8082,13 @@ function canonicalizeRecapCaveats(caveats) {
7762
8082
  }
7763
8083
  if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
7764
8084
  if (typeof value === "object") {
7765
- const object3 = value;
7766
- const prototype = Object.getPrototypeOf(object3);
8085
+ const object4 = value;
8086
+ const prototype = Object.getPrototypeOf(object4);
7767
8087
  if (prototype !== Object.prototype && prototype !== null) {
7768
8088
  throw new Error("ReCap caveats must contain only JSON values.");
7769
8089
  }
7770
- return `{${Object.keys(object3).sort().map(
7771
- (key) => `${JSON.stringify(key)}:${canonicalize(object3[key])}`
8090
+ return `{${Object.keys(object4).sort().map(
8091
+ (key) => `${JSON.stringify(key)}:${canonicalize(object4[key])}`
7772
8092
  ).join(",")}}`;
7773
8093
  }
7774
8094
  throw new Error("ReCap caveats must contain only JSON values.");
@@ -7856,7 +8176,6 @@ import { ed25519 as ed255195 } from "@noble/curves/ed25519";
7856
8176
  import { blake3 as blake33 } from "@noble/hashes/blake3";
7857
8177
  import { sha256 as sha2567 } from "@noble/hashes/sha256";
7858
8178
  var ENVELOPE_SCHEMA = "xyz.tinycloud.share/envelope";
7859
- var INLINE_PREFIX = "tc2:";
7860
8179
  var RAW_CID_CODEC = 85;
7861
8180
  var BLAKE3_256_CODE = 30;
7862
8181
  var DEFAULT_MAX_INLINE_BYTES = 256 * 1024;
@@ -7903,8 +8222,8 @@ function canonicalJson(value) {
7903
8222
  }
7904
8223
  if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
7905
8224
  if (typeof value === "object") {
7906
- const object3 = value;
7907
- return `{${Object.keys(object3).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object3[key])}`).join(",")}}`;
8225
+ const object4 = value;
8226
+ return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object4[key])}`).join(",")}}`;
7908
8227
  }
7909
8228
  throw new ShareEnvelopeError("invalid-envelope", "unsupported JSON value");
7910
8229
  }
@@ -8561,25 +8880,8 @@ function parsePublishedShareLink(link, options2 = {}) {
8561
8880
  }
8562
8881
  const origin2 = normalizeOrigin(url.origin);
8563
8882
  if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
8564
- if (url.search !== "" || url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
8565
- if (url.pathname === "/s/inline" && url.hash.startsWith("#v=2&p=")) {
8566
- let payload;
8567
- try {
8568
- const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
8569
- if (bytes2.length > (options2.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES) * 2) throw new Error("inline payload too large");
8570
- const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
8571
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
8572
- payload = value;
8573
- } catch (error) {
8574
- throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
8575
- }
8576
- 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");
8577
- const sealed = base64UrlToBytes(payload.c);
8578
- if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
8579
- const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
8580
- if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
8581
- return { origin: origin2, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
8582
- }
8883
+ if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
8884
+ if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
8583
8885
  const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
8584
8886
  if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
8585
8887
  const key = base64UrlToBytes(url.hash.slice(3));
@@ -8599,13 +8901,10 @@ function parseShareUrl(link, options2 = {}) {
8599
8901
  } catch {
8600
8902
  throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
8601
8903
  }
8602
- if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
8603
8904
  const origin2 = normalizeOrigin(url.origin);
8604
8905
  if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
8605
- if (url.pathname === "/s/inline" && url.hash.startsWith("#v=2&p=")) {
8606
- const published = parsePublishedShareLink(link, options2);
8607
- return { kind: "inline", origin: origin2, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
8608
- }
8906
+ if (url.pathname === "/viewer") throw new ShareEnvelopeError("invalid-link", "retired viewer links are not accepted");
8907
+ if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
8609
8908
  const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
8610
8909
  if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
8611
8910
  const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
@@ -8628,9 +8927,10 @@ function parseShareUrl(link, options2 = {}) {
8628
8927
  }
8629
8928
  function isV2ShareLink(link) {
8630
8929
  try {
8631
- return new URL(link).pathname.startsWith("/s/");
8930
+ const url = new URL(link);
8931
+ return url.search === "" && url.pathname.startsWith("/s/");
8632
8932
  } catch {
8633
- return link.startsWith(INLINE_PREFIX);
8933
+ return false;
8634
8934
  }
8635
8935
  }
8636
8936
  async function encryptShareBytes(bytes2, key) {
@@ -9086,8 +9386,8 @@ function canonicalJsonForProof(value) {
9086
9386
  }
9087
9387
  if (Array.isArray(value)) return `[${value.map(canonicalJsonForProof).join(",")}]`;
9088
9388
  if (typeof value === "object") {
9089
- const object3 = value;
9090
- return `{${Object.keys(object3).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object3[key])}`).join(",")}}`;
9389
+ const object4 = value;
9390
+ return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object4[key])}`).join(",")}}`;
9091
9391
  }
9092
9392
  throw new ShareAccessError("SHARE_SESSION_INVALID");
9093
9393
  }
@@ -9796,12 +10096,12 @@ var ShareRecipientClient = class {
9796
10096
  this.assertDetachedProof(request.proof);
9797
10097
  }
9798
10098
  parseSessionCapability(value) {
9799
- const object3 = this.requireObject(value, "session capability");
9800
- if (typeof object3.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
9801
- 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");
9802
- const resource = this.requireObject(object3.resource, "session capability resource");
10099
+ const object4 = this.requireObject(value, "session capability");
10100
+ if (typeof object4.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
10101
+ 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");
10102
+ const resource = this.requireObject(object4.resource, "session capability resource");
9803
10103
  if (resource.kind !== "exact" && resource.kind !== "prefix" || typeof resource.path !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability resource is invalid");
9804
- return { spaceId: object3.spaceId, actions: [...new Set(object3.actions)], resource: { kind: resource.kind, path: resource.path } };
10104
+ return { spaceId: object4.spaceId, actions: [...new Set(object4.actions)], resource: { kind: resource.kind, path: resource.path } };
9805
10105
  }
9806
10106
  assertProvidedPolicySession(session, envelope) {
9807
10107
  const sessionExpiry = dateValue(session.expiresAt);
@@ -11441,62 +11741,21 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
11441
11741
  return validateOwnerSharePolicyRegistration(value, expected);
11442
11742
  }
11443
11743
 
11444
- // src/delegations/share-delivery.ts
11744
+ // src/delegations/share-delivery-v3.ts
11445
11745
  import { ed25519 as ed255199 } from "@noble/curves/ed25519";
11446
- var SHARE_DELIVERY_AUTHORIZATION_DOMAIN = "xyz.tinycloud.share/delivery-authorization/v2\0";
11447
- function isCanonicalHttpsOrigin(value) {
11448
- let url;
11449
- try {
11450
- url = new URL(value);
11451
- } catch {
11452
- return false;
11453
- }
11454
- return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
11455
- }
11456
- var AUTHORIZATION_KEYS = [
11457
- "type",
11458
- "version",
11459
- "jti",
11460
- "shareCid",
11461
- "shareId",
11462
- "registrationCid",
11463
- "delegationCid",
11464
- "enforcementDelegationCid",
11465
- "envelopeCid",
11466
- "policyCid",
11467
- "nodeAudience",
11468
- "targetOrigin",
11469
- "openCredentialsAudience",
11470
- "holder",
11471
- "recipientMatcher",
11472
- "deliveryEmail",
11473
- "shareUrl",
11474
- "returnOrigin",
11475
- "documentName",
11476
- "senderDid",
11477
- "senderTrust",
11478
- "contentSource",
11479
- "contentSourceDigest",
11480
- "shareExpiresAt",
11481
- "issuedAt",
11482
- "reportAbuseToken",
11483
- "actions",
11484
- "resource",
11485
- "authorityMaterialHandle",
11486
- "authorityMaterialDigest",
11487
- "requestBodyDigest",
11488
- "idempotencyKey",
11489
- "expiresAt",
11490
- "dataAuthority"
11491
- ];
11492
- function assertObject2(value, keys, label) {
11746
+ import { sha256 as sha25611 } from "@noble/hashes/sha256";
11747
+ var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
11748
+ var DELIVERY_ADMISSION_DOMAIN = "xyz.tinycloud.policy/delivery-admission/v0\0";
11749
+ var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
11750
+ var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
11751
+ function exactObject2(value, keys, label) {
11493
11752
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
11494
11753
  const record2 = value;
11495
11754
  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`);
11496
11755
  return record2;
11497
11756
  }
11498
- function fromB642(value) {
11499
- if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
11757
+ function decodeBase64Url3(value) {
11758
+ if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
11500
11759
  if (typeof atob === "function") {
11501
11760
  const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
11502
11761
  return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
@@ -11504,42 +11763,45 @@ function fromB642(value) {
11504
11763
  if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
11505
11764
  throw new Error("base64url decoding is unavailable");
11506
11765
  }
11507
- function validateShareDeliveryAuthorizationBytes(bytesValue, expected) {
11508
- let value;
11766
+ function object3(value, label) {
11767
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
11768
+ return value;
11769
+ }
11770
+ function didKeyBytes2(did) {
11771
+ if (!did.startsWith("did:key:z") || /[#/?]/.test(did.slice("did:key:".length))) throw new Error("v3 delivery admission signer is invalid");
11772
+ const decoded = base58btc.decode(did.slice("did:key:".length));
11773
+ if (decoded.length !== 34 || decoded[0] !== 237 || decoded[1] !== 1) throw new Error("v3 delivery admission signer is invalid");
11774
+ return decoded.slice(2);
11775
+ }
11776
+ function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
11777
+ let parsed;
11509
11778
  try {
11510
- value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytesValue));
11779
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
11511
11780
  } catch {
11512
- throw new Error("share delivery authorization response is not valid UTF-8 JSON");
11513
- }
11514
- const root = assertObject2(value, ["authorization", "proof"], "share delivery authorization response");
11515
- const authorization = assertObject2(root.authorization, AUTHORIZATION_KEYS, "share delivery authorization");
11516
- const proof = assertObject2(root.proof, ["alg", "kid", "signature"], "share delivery authorization proof");
11517
- const request = expected.request;
11518
- 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) {
11519
- throw new Error("share delivery authorization is not bound to the submitted request");
11520
- }
11521
- if (typeof authorization.issuedAt !== "string" || Date.parse(authorization.issuedAt) > Date.now() + 6e4) throw new Error("share delivery authorization issuedAt is invalid");
11522
- if (new Date(authorization.expiresAt).toISOString() !== authorization.expiresAt) throw new Error("share delivery authorization expiresAt is non-canonical");
11523
- if (typeof authorization.openCredentialsAudience !== "string" || !isCanonicalHttpsOrigin(authorization.openCredentialsAudience) || authorization.openCredentialsAudience !== expected.credentialsAudience || authorization.openCredentialsAudience === authorization.nodeAudience || authorization.openCredentialsAudience === authorization.returnOrigin) {
11524
- throw new Error("share delivery authorization openCredentialsAudience is missing or untrusted");
11525
- }
11526
- if (proof.alg !== "EdDSA" || typeof proof.kid !== "string" || typeof proof.signature !== "string") throw new Error("share delivery authorization proof is invalid");
11527
- if (proof.kid !== expected.nodeProof.kid) throw new Error("share delivery authorization proof key is not trusted");
11528
- const encodedKid = expected.nodeProof.publicKey;
11529
- const publicKey = encodedKid.length === 34 && encodedKid[0] === 237 && encodedKid[1] === 1 ? encodedKid.slice(2) : encodedKid;
11530
- if (publicKey.length !== 32) throw new Error("share delivery authorization proof key is invalid");
11531
- const signatureBytes = fromB642(proof.signature);
11532
- const signedBytes = new TextEncoder().encode(`${SHARE_DELIVERY_AUTHORIZATION_DOMAIN}${canonicalizeSignedObjectUnsigned(authorization)}`);
11533
- if (signatureBytes.length !== 64 || !ed255199.verify(signatureBytes, signedBytes, publicKey)) throw new Error("share delivery authorization proof signature is invalid");
11534
- return { authorization, proof };
11535
- }
11536
- function shareDeliveryTrustedKid(publicKey) {
11537
- if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
11538
- const prefixed = new Uint8Array(34);
11539
- prefixed.set([237, 1]);
11540
- prefixed.set(publicKey, 2);
11541
- const did = `did:key:${base58btc.encode(prefixed)}`;
11542
- return `${did}#${did.slice("did:key:".length)}`;
11781
+ throw new Error("v3 share delivery response is not valid UTF-8 JSON");
11782
+ }
11783
+ const root = exactObject2(parsed, ["request", "admission"], "v3 share delivery response");
11784
+ const request = exactObject2(root.request, REQUEST_KEYS, "credential invitation request");
11785
+ const admission = exactObject2(root.admission, ADMISSION_KEYS, "delivery admission");
11786
+ const signature = exactObject2(admission.signature, ["suite", "signerDid", "value"], "delivery admission signature");
11787
+ const envelope = object3(expected.request.envelope, "v3 share envelope");
11788
+ const policy = object3(envelope.policy, "v3 share policy");
11789
+ const requirement = object3(policy.credentialRequirement, "v3 credential requirement");
11790
+ const credentialType = object3(requirement.credentialType, "v3 credential type");
11791
+ const contentSource = object3(envelope.contentSource, "v3 content source");
11792
+ const envelopeSignature = object3(envelope.signature, "v3 envelope signature");
11793
+ const target = object3(envelope.target, "v3 target");
11794
+ const binding = object3(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
11795
+ const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
11796
+ 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");
11797
+ const issuedAt = Date.parse(String(request.issuedAt));
11798
+ const expiresAt = Date.parse(String(request.expiresAt));
11799
+ if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
11800
+ const admissionSignature = decodeBase64Url3(signature.value);
11801
+ const { signature: _signature, ...unsignedAdmission } = admission;
11802
+ const digest4 = sha25611(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
11803
+ if (admissionSignature.length !== 64 || !ed255199.verify(admissionSignature, digest4, didKeyBytes2(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
11804
+ return { request, admission };
11543
11805
  }
11544
11806
 
11545
11807
  // src/delegations/sender-share-store.ts
@@ -11662,9 +11924,6 @@ import {
11662
11924
  publishPolicyShare,
11663
11925
  claimShare,
11664
11926
  resumeShareAuthorization,
11665
- isLegacyShareLink,
11666
- receiveLegacyShare,
11667
- migrateShare,
11668
11927
  MemoryEncryptedShareHistoryStorage,
11669
11928
  EncryptedSenderShareHistory
11670
11929
  } from "@tinycloud/share-sdk";
@@ -11672,8 +11931,7 @@ import {
11672
11931
  inspectShare,
11673
11932
  receiveShare,
11674
11933
  ShareReceiveError,
11675
- toShareErrorInfo,
11676
- verifyBearerEnvelope
11934
+ toShareErrorInfo
11677
11935
  } from "@tinycloud/share-sdk";
11678
11936
 
11679
11937
  // src/networkId.ts
@@ -14302,8 +14560,8 @@ function stableJson(value) {
14302
14560
  if (Array.isArray(value)) {
14303
14561
  return `[${value.map(stableJson).join(",")}]`;
14304
14562
  }
14305
- const object3 = value;
14306
- return `{${Object.keys(object3).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object3[key])}`).join(",")}}`;
14563
+ const object4 = value;
14564
+ return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object4[key])}`).join(",")}}`;
14307
14565
  }
14308
14566
  function indexedDelegationFromRow(row) {
14309
14567
  const [
@@ -14406,6 +14664,7 @@ import {
14406
14664
  SecretsService,
14407
14665
  SECRET_NAME_RE as SECRET_NAME_RE2,
14408
14666
  canonicalizeSecretScope,
14667
+ parseSecretCatalogKey,
14409
14668
  resolveSecretListPrefix,
14410
14669
  resolveSecretPath as resolveSecretPath2,
14411
14670
  EncryptionService,
@@ -17316,6 +17575,7 @@ var DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
17316
17575
  var LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
17317
17576
  var LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
17318
17577
  var LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
17578
+ var OWNER_NODE_BINDING_TIMEOUT_MS = 5e3;
17319
17579
  function createInMemoryLocalNodeIdentityStore() {
17320
17580
  const pins = /* @__PURE__ */ new Map();
17321
17581
  return {
@@ -17448,21 +17708,80 @@ async function verifyLocationRecord(input) {
17448
17708
  }
17449
17709
  return false;
17450
17710
  }
17451
- async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch) {
17711
+ async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch, signal) {
17452
17712
  const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
17453
- const response = await fetchFn(url);
17713
+ const response = await fetchFn(url, {
17714
+ redirect: "error",
17715
+ headers: { accept: "application/json" },
17716
+ ...signal === void 0 ? {} : { signal }
17717
+ });
17454
17718
  if (response.status === 404) {
17455
17719
  return null;
17456
17720
  }
17457
17721
  if (!response.ok) {
17458
17722
  throw new Error(`location registry returned HTTP ${response.status}`);
17459
17723
  }
17460
- const body = await response.json();
17724
+ const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
17725
+ let body;
17726
+ try {
17727
+ body = JSON.parse(text2);
17728
+ } catch {
17729
+ throw new LocationRecordValidationError("registry response is not JSON");
17730
+ }
17461
17731
  if (body.record === void 0) {
17462
17732
  throw new LocationRecordValidationError("registry response missing record");
17463
17733
  }
17464
17734
  return validateLocationRecord(body.record);
17465
17735
  }
17736
+ async function publishLocationRecord(options2) {
17737
+ validateSubject(options2.subject);
17738
+ const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
17739
+ const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
17740
+ const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
17741
+ const current = await fetchLocationRecord(registryUrl, options2.subject, fetchFn);
17742
+ if (current !== null) {
17743
+ if (current.subject !== options2.subject || !await verifyLocationRecord(current)) {
17744
+ throw new LocationRecordValidationError("existing location record signature is invalid");
17745
+ }
17746
+ if (publishedHttpOrigins(current).includes(nodeOrigin)) {
17747
+ return current;
17748
+ }
17749
+ }
17750
+ const record2 = await signLocationRecord({
17751
+ version: 1,
17752
+ subject: options2.subject,
17753
+ multiaddrs: [...current?.multiaddrs ?? [], httpUrlToMultiaddr(nodeOrigin)],
17754
+ updated_at: (options2.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
17755
+ sequence: (current?.sequence ?? -1) + 1
17756
+ }, options2.signer);
17757
+ const response = await fetchFn(
17758
+ `${registryUrl}/v1/locations/${encodeURIComponent(options2.subject)}`,
17759
+ {
17760
+ method: "PUT",
17761
+ redirect: "error",
17762
+ headers: { accept: "application/json", "content-type": "application/json" },
17763
+ body: JSON.stringify(record2)
17764
+ }
17765
+ );
17766
+ if (!response.ok) {
17767
+ throw new Error(`location registry publish returned HTTP ${response.status}`);
17768
+ }
17769
+ const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
17770
+ let body;
17771
+ try {
17772
+ body = JSON.parse(text2);
17773
+ } catch {
17774
+ throw new LocationRecordValidationError("registry publish response is not JSON");
17775
+ }
17776
+ if (body.record === void 0) {
17777
+ throw new LocationRecordValidationError("registry publish response missing record");
17778
+ }
17779
+ const published = validateLocationRecord(body.record);
17780
+ if (canonicalLocationPayload(locationPayloadForRecord(published)) !== canonicalLocationPayload(locationPayloadForRecord(record2)) || published.signature !== record2.signature || !await verifyLocationRecord(published)) {
17781
+ throw new LocationRecordValidationError("registry publish response does not match the signed record");
17782
+ }
17783
+ return published;
17784
+ }
17466
17785
  async function resolveCloudLocation(subject, options2 = {}) {
17467
17786
  validateSubject(subject);
17468
17787
  const verifyRecords = options2.verifyRecords ?? true;
@@ -17713,6 +18032,123 @@ function multiaddrToHttpUrl(input) {
17713
18032
  }
17714
18033
  return uri;
17715
18034
  }
18035
+ function exactHttpOrigin(value, label) {
18036
+ let url;
18037
+ try {
18038
+ url = new URL(value);
18039
+ } catch {
18040
+ throw new LocationRecordValidationError(`${label} must be an absolute URL origin`);
18041
+ }
18042
+ const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
18043
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
18044
+ throw new LocationRecordValidationError(`${label} must use HTTPS (or loopback HTTP)`);
18045
+ }
18046
+ if (url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
18047
+ throw new LocationRecordValidationError(`${label} must be an origin without credentials, path, query, or fragment`);
18048
+ }
18049
+ return url.origin;
18050
+ }
18051
+ function publishedHttpOrigins(record2) {
18052
+ const origins = [];
18053
+ for (const address of record2.multiaddrs) {
18054
+ try {
18055
+ origins.push(exactHttpOrigin(multiaddrToHttpUrl(address), "published node URL"));
18056
+ } catch {
18057
+ }
18058
+ }
18059
+ return origins;
18060
+ }
18061
+ async function boundedResponseText(response, label, maxBytes) {
18062
+ const declaredLength = response.headers.get("content-length");
18063
+ if (declaredLength !== null && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > maxBytes)) {
18064
+ throw new LocationRecordValidationError(`${label} response is too large`);
18065
+ }
18066
+ if (response.body === null) return "";
18067
+ const reader = response.body.getReader();
18068
+ const chunks = [];
18069
+ let total = 0;
18070
+ try {
18071
+ while (true) {
18072
+ const { done, value } = await reader.read();
18073
+ if (done) break;
18074
+ if (value === void 0) continue;
18075
+ total += value.byteLength;
18076
+ if (total > maxBytes) {
18077
+ await reader.cancel().catch(() => void 0);
18078
+ throw new LocationRecordValidationError(`${label} response is too large`);
18079
+ }
18080
+ chunks.push(value);
18081
+ }
18082
+ } finally {
18083
+ reader.releaseLock();
18084
+ }
18085
+ const bytes2 = new Uint8Array(total);
18086
+ let offset = 0;
18087
+ for (const chunk of chunks) {
18088
+ bytes2.set(chunk, offset);
18089
+ offset += chunk.byteLength;
18090
+ }
18091
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
18092
+ }
18093
+ function boundedSignal(parent, timeoutMs) {
18094
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
18095
+ throw new LocationRecordValidationError("owner-node binding timeout must be positive");
18096
+ }
18097
+ const controller = new AbortController();
18098
+ const abortFromParent = () => controller.abort(parent?.reason);
18099
+ if (parent?.aborted) abortFromParent();
18100
+ else parent?.addEventListener("abort", abortFromParent, { once: true });
18101
+ const timer = setTimeout(() => controller.abort(new DOMException("owner-node binding timed out", "TimeoutError")), timeoutMs);
18102
+ return {
18103
+ signal: controller.signal,
18104
+ dispose() {
18105
+ clearTimeout(timer);
18106
+ parent?.removeEventListener("abort", abortFromParent);
18107
+ }
18108
+ };
18109
+ }
18110
+ async function verifyOwnerNodeBinding(options2) {
18111
+ validateSubject(options2.ownerDid);
18112
+ validateSubject(options2.nodeDid);
18113
+ const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
18114
+ const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
18115
+ const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
18116
+ const lookup = boundedSignal(options2.signal, options2.timeoutMs ?? OWNER_NODE_BINDING_TIMEOUT_MS);
18117
+ try {
18118
+ const record2 = await fetchLocationRecord(registryUrl, options2.ownerDid, fetchFn, lookup.signal);
18119
+ if (record2 === null) {
18120
+ throw new LocationRecordValidationError("owner has no published location record");
18121
+ }
18122
+ if (record2.subject !== options2.ownerDid || !await verifyLocationRecord(record2)) {
18123
+ throw new LocationRecordValidationError("owner location record signature is invalid");
18124
+ }
18125
+ const publishedOrigins = publishedHttpOrigins(record2);
18126
+ if (!publishedOrigins.includes(nodeOrigin)) {
18127
+ throw new LocationRecordValidationError("share target is not in the owner's signed location record");
18128
+ }
18129
+ const response = await fetchFn(new URL("/info", nodeOrigin), {
18130
+ redirect: "error",
18131
+ headers: { accept: "application/json" },
18132
+ signal: lookup.signal
18133
+ });
18134
+ if (!response.ok) {
18135
+ throw new LocationRecordValidationError(`target node /info returned HTTP ${response.status}`);
18136
+ }
18137
+ const text2 = await boundedResponseText(response, "target node /info", 16 * 1024);
18138
+ let body;
18139
+ try {
18140
+ body = JSON.parse(text2);
18141
+ } catch {
18142
+ throw new LocationRecordValidationError("target node /info response is not JSON");
18143
+ }
18144
+ if (body === null || typeof body !== "object" || Array.isArray(body) || body.nodeId !== options2.nodeDid) {
18145
+ throw new LocationRecordValidationError("target node identity does not match the share attestation");
18146
+ }
18147
+ return Object.freeze({ record: record2, nodeOrigin, nodeDid: options2.nodeDid });
18148
+ } finally {
18149
+ lookup.dispose();
18150
+ }
18151
+ }
17716
18152
  function httpUrlToMultiaddr(input) {
17717
18153
  const url = new URL(input);
17718
18154
  if (url.protocol !== "http:" && url.protocol !== "https:") {
@@ -17874,7 +18310,7 @@ async function verifyPkhSignature(did, payload, signature) {
17874
18310
  }
17875
18311
  function verifyDidKeySignature(did, payload, signature) {
17876
18312
  const publicKey = ed25519PublicKeyFromDidKey2(did);
17877
- const signatureBytes = decodeBase64Url3(signature);
18313
+ const signatureBytes = decodeBase64Url4(signature);
17878
18314
  if (signatureBytes.length !== 64) {
17879
18315
  throw new LocationRecordValidationError(
17880
18316
  "did:key signature must be a base64url Ed25519 signature"
@@ -17927,7 +18363,7 @@ function base64UrlEncode3(bytes2) {
17927
18363
  }
17928
18364
  return output;
17929
18365
  }
17930
- function decodeBase64Url3(value) {
18366
+ function decodeBase64Url4(value) {
17931
18367
  const alphabet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
17932
18368
  const bytes2 = [];
17933
18369
  let buffer = 0;
@@ -17974,6 +18410,7 @@ export {
17974
18410
  CREDENTIAL_CONTRACT_VERSION,
17975
18411
  CREDENTIAL_ERROR_CODES,
17976
18412
  CREDENTIAL_FORMAT,
18413
+ CREDENTIAL_INVITATION_REQUEST_DOMAIN,
17977
18414
  CREDENTIAL_ACQUISITION_PROTOCOL as CREDENTIAL_PROTOCOL,
17978
18415
  CREDENTIAL_STEP_REGISTRY_VERSION,
17979
18416
  CapabilityKeyRegistry,
@@ -17997,6 +18434,7 @@ export {
17997
18434
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
17998
18435
  DEFAULT_TINYCLOUD_FALLBACK_HOST,
17999
18436
  DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
18437
+ DELIVERY_ADMISSION_DOMAIN,
18000
18438
  DUCKDB,
18001
18439
  DataVaultService,
18002
18440
  DatabaseHandle,
@@ -18008,6 +18446,7 @@ export {
18008
18446
  DuckDbService2 as DuckDbService,
18009
18447
  ED25519_JCS_SIGNATURE_SUITE,
18010
18448
  EIP191_JCS_SIGNATURE_SUITE,
18449
+ EMAIL_CREDENTIAL_MAX_AGE_SECONDS,
18011
18450
  ENCRYPTION,
18012
18451
  ENCRYPTION_MANIFEST_SPACE,
18013
18452
  ENCRYPTION_NETWORK_URN_PREFIX,
@@ -18046,6 +18485,7 @@ export {
18046
18485
  NETWORK_NAME_PATTERN,
18047
18486
  NetworkIdError,
18048
18487
  OPENKEY_ACTION_ID_SEPARATOR,
18488
+ OWNER_NODE_BINDING_TIMEOUT_MS,
18049
18489
  OpenKeyManageKeyError,
18050
18490
  POLICY_CAPABILITY_V1_DOMAIN,
18051
18491
  POLICY_CAPABILITY_V1_SCHEMA,
@@ -18059,6 +18499,8 @@ export {
18059
18499
  POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA,
18060
18500
  POLICY_PRESENTATION_V3_DOMAIN,
18061
18501
  POLICY_PRESENTATION_V3_SCHEMA,
18502
+ POLICY_PRESENTATION_V4_DOMAIN,
18503
+ POLICY_PRESENTATION_V4_SCHEMA,
18062
18504
  POLICY_SCHEMA,
18063
18505
  POLICY_SESSION_UCAN_V1_PROFILE,
18064
18506
  POLICY_STATUS_SCHEMA,
@@ -18083,7 +18525,6 @@ export {
18083
18525
  SECRET_RECORDS_SCHEMA,
18084
18526
  SERVICE_LONG_TO_SHORT,
18085
18527
  SERVICE_SHORT_TO_LONG,
18086
- SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
18087
18528
  SPACE,
18088
18529
  SQL,
18089
18530
  SQLAction,
@@ -18136,6 +18577,7 @@ export {
18136
18577
  activateSessionWithHost,
18137
18578
  addressStorageKey,
18138
18579
  admitPolicyCredentialV3,
18580
+ admitPolicyCredentialV4,
18139
18581
  applyPrefix,
18140
18582
  bootstrapEncryptionNetworkId,
18141
18583
  bootstrapSpaceId,
@@ -18146,6 +18588,7 @@ export {
18146
18588
  buildDecryptInvocation,
18147
18589
  buildNetworkId2 as buildNetworkId,
18148
18590
  buildPolicyCredentialPresentationV3,
18591
+ buildPolicyCredentialPresentationV4,
18149
18592
  buildSpaceUri,
18150
18593
  canonicalDigest,
18151
18594
  canonicalHashHex,
@@ -18176,6 +18619,7 @@ export {
18176
18619
  createAndSignSignedObject,
18177
18620
  createCapabilityKeyRegistry,
18178
18621
  createDelegatedShareKey,
18622
+ createEmailCredentialRequirement,
18179
18623
  createHolderBinding,
18180
18624
  createInMemoryLocalNodeIdentityStore,
18181
18625
  createLocalStorageLocalNodeIdentityStore,
@@ -18254,6 +18698,7 @@ export {
18254
18698
  isNetworkId,
18255
18699
  isPlausibleOpenKeyActionId,
18256
18700
  isV2ShareLink,
18701
+ issuedCredentialEnvelopeFromVerified,
18257
18702
  jcsCanonicalize,
18258
18703
  loadManifest,
18259
18704
  locationPayloadForRecord,
@@ -18282,6 +18727,7 @@ export {
18282
18727
  parsePolicySessionUcan,
18283
18728
  parsePortableDelegation,
18284
18729
  parseRecapCapabilities,
18730
+ parseSecretCatalogKey,
18285
18731
  parseShareArtifact,
18286
18732
  parseShareUrl,
18287
18733
  parseSpaceUri,
@@ -18294,7 +18740,9 @@ export {
18294
18740
  principalDidEquals,
18295
18741
  projectUnifiedPolicyCapabilities,
18296
18742
  projectUnifiedPolicyCapability,
18743
+ publishLocationRecord,
18297
18744
  recapCaveatsEqual,
18745
+ registerPolicyV3,
18298
18746
  renewPolicyRootStatusV3,
18299
18747
  requestTinyCloudManageKeyScope,
18300
18748
  resolveCloudLocation,
@@ -18312,10 +18760,10 @@ export {
18312
18760
  serviceError5 as serviceError,
18313
18761
  sha256Base64Url,
18314
18762
  shareCapabilityAllows,
18315
- shareDeliveryTrustedKid,
18316
18763
  signCompactUcanAuthorization,
18317
18764
  signCompactUcanRootAuthorization,
18318
18765
  signLocationRecord,
18766
+ signPolicyCredentialPresentationV4,
18319
18767
  signShareEnvelope,
18320
18768
  signedObjectIdFor,
18321
18769
  submitHostDelegation,
@@ -18348,13 +18796,14 @@ export {
18348
18796
  validatePolicyStatusSignedShape,
18349
18797
  validatePolicyStatusUnsigned,
18350
18798
  validatePolicyUnsigned,
18351
- validateShareDeliveryAuthorizationBytes,
18799
+ validateShareDeliveryAuthorizationV3Bytes,
18352
18800
  validateUnifiedPolicyV2,
18353
18801
  verifyDecryptResponse,
18354
18802
  verifyDidKeyEd25519Signature,
18355
18803
  verifyEip191MessageSignature,
18356
18804
  verifyIssuedCredential,
18357
18805
  verifyLocationRecord,
18806
+ verifyOwnerNodeBinding,
18358
18807
  verifyPolicy,
18359
18808
  verifyPolicyEngineRecord,
18360
18809
  verifyPolicyRootRevocationV3,