@tinycloud/sdk-core 2.11.0-beta.8 → 2.11.0

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.
@@ -1271,6 +1271,7 @@ function validateSharingServiceConfig(data) {
1271
1271
 
1272
1272
  // src/manifest.ts
1273
1273
  import ms from "ms";
1274
+ import { ACCOUNT_MANIFEST_PERMISSIONS } from "@tinycloud/bootstrap";
1274
1275
  import { resolveSecretPath, SECRET_NAME_RE } from "@tinycloud/sdk-services";
1275
1276
  var SERVICE_SHORT_TO_LONG = Object.freeze({
1276
1277
  kv: "tinycloud.kv",
@@ -1288,7 +1289,7 @@ var SERVICE_LONG_TO_SHORT = Object.freeze(
1288
1289
  );
1289
1290
 
1290
1291
  // src/identity.ts
1291
- import { getAddress, isAddress } from "viem";
1292
+ import { getAddress, isAddress, recoverMessageAddress } from "viem";
1292
1293
  var IdentityParseError = class extends Error {
1293
1294
  constructor(message) {
1294
1295
  super(message);
@@ -2153,13 +2154,12 @@ function digest(input, options) {
2153
2154
  }
2154
2155
  var identity2 = { code: code2, name, encode: encode4, digest };
2155
2156
 
2156
- // ../../node_modules/multiformats/dist/src/hashes/sha2.js
2157
- var sha2_exports = {};
2158
- __export(sha2_exports, {
2157
+ // ../../node_modules/multiformats/dist/src/hashes/sha2-browser.js
2158
+ var sha2_browser_exports = {};
2159
+ __export(sha2_browser_exports, {
2159
2160
  sha256: () => sha256,
2160
2161
  sha512: () => sha512
2161
2162
  });
2162
- import crypto2 from "crypto";
2163
2163
 
2164
2164
  // ../../node_modules/multiformats/dist/src/hashes/hasher.js
2165
2165
  var DEFAULT_MIN_DIGEST_LENGTH = 20;
@@ -2209,16 +2209,19 @@ function createDigest(digest4, code3, truncate) {
2209
2209
  return create(code3, digest4);
2210
2210
  }
2211
2211
 
2212
- // ../../node_modules/multiformats/dist/src/hashes/sha2.js
2212
+ // ../../node_modules/multiformats/dist/src/hashes/sha2-browser.js
2213
+ function sha(name2) {
2214
+ return async (data) => new Uint8Array(await crypto.subtle.digest(name2, data));
2215
+ }
2213
2216
  var sha256 = from2({
2214
2217
  name: "sha2-256",
2215
2218
  code: 18,
2216
- encode: (input) => coerce(crypto2.createHash("sha256").update(input).digest())
2219
+ encode: sha("SHA-256")
2217
2220
  });
2218
2221
  var sha512 = from2({
2219
2222
  name: "sha2-512",
2220
2223
  code: 19,
2221
- encode: (input) => coerce(crypto2.createHash("sha512").update(input).digest())
2224
+ encode: sha("SHA-512")
2222
2225
  });
2223
2226
 
2224
2227
  // ../../node_modules/multiformats/dist/src/cid.js
@@ -2560,10 +2563,10 @@ var cidSymbol = /* @__PURE__ */ Symbol.for("@ipld/js-cid/CID");
2560
2563
 
2561
2564
  // ../../node_modules/multiformats/dist/src/basics.js
2562
2565
  var bases = { ...identity_exports, ...base2_exports, ...base8_exports, ...base10_exports, ...base16_exports, ...base32_exports, ...base36_exports, ...base58_exports, ...base64_exports, ...base256emoji_exports };
2563
- var hashes = { ...sha2_exports, ...identity_exports2 };
2566
+ var hashes = { ...sha2_browser_exports, ...identity_exports2 };
2564
2567
 
2565
2568
  // src/delegations/SharingService.ts
2566
- import { ed25519 as ed255193 } from "@noble/curves/ed25519";
2569
+ import { ed25519 as ed255194 } from "@noble/curves/ed25519";
2567
2570
 
2568
2571
  // src/delegations/ShareRecipientClient.ts
2569
2572
  import {
@@ -2571,8 +2574,8 @@ import {
2571
2574
  KVService,
2572
2575
  ServiceContext
2573
2576
  } from "@tinycloud/sdk-services";
2574
- import { ed25519 as ed255192 } from "@noble/curves/ed25519";
2575
- import { sha256 as sha2564 } from "@noble/hashes/sha256";
2577
+ import { ed25519 as ed255193 } from "@noble/curves/ed25519";
2578
+ import { sha256 as sha2565 } from "@noble/hashes/sha256";
2576
2579
 
2577
2580
  // src/delegations/share-envelope.ts
2578
2581
  import { ed25519 } from "@noble/curves/ed25519";
@@ -3663,6 +3666,506 @@ var ShareNativeResponseSchema = z3.discriminatedUnion("action", [
3663
3666
  }).strict()
3664
3667
  ]);
3665
3668
 
3669
+ // src/delegations/portable.ts
3670
+ import { z as z5 } from "zod";
3671
+
3672
+ // src/policy/unified.ts
3673
+ import { sha256 as sha2564 } from "@noble/hashes/sha256";
3674
+ import { blake3 as blake32 } from "@noble/hashes/blake3";
3675
+ import { ed25519 as ed255192 } from "@noble/curves/ed25519";
3676
+ import { z as z4 } from "zod";
3677
+
3678
+ // src/policy/errors.ts
3679
+ var SignedObjectProfileError = class extends Error {
3680
+ constructor(code3, message) {
3681
+ super(message);
3682
+ this.name = "SignedObjectProfileError";
3683
+ this.code = code3;
3684
+ }
3685
+ };
3686
+ var SignedObjectCanonicalizationError = class extends SignedObjectProfileError {
3687
+ constructor(message) {
3688
+ super("canonicalization-mismatch", message);
3689
+ this.name = "SignedObjectCanonicalizationError";
3690
+ }
3691
+ };
3692
+
3693
+ // src/policy/jcs.ts
3694
+ var objectHasOwn = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
3695
+ Object.prototype.hasOwnProperty
3696
+ );
3697
+ function jcsCanonicalize(input) {
3698
+ return serialize(normalizeJson(input, "$"));
3699
+ }
3700
+ function normalizeJson(input, path = "$") {
3701
+ if (input === null) {
3702
+ return null;
3703
+ }
3704
+ switch (typeof input) {
3705
+ case "boolean":
3706
+ return input;
3707
+ case "number":
3708
+ if (!Number.isFinite(input)) {
3709
+ throw new SignedObjectCanonicalizationError(
3710
+ `${path} must be a finite JSON number`
3711
+ );
3712
+ }
3713
+ return input;
3714
+ case "string":
3715
+ assertUnicodeScalarString(input, path);
3716
+ return input;
3717
+ case "object":
3718
+ return normalizeJsonObjectOrArray(input, path);
3719
+ case "bigint":
3720
+ case "function":
3721
+ case "symbol":
3722
+ case "undefined":
3723
+ default:
3724
+ throw new SignedObjectCanonicalizationError(
3725
+ `${path} is not a JSON value`
3726
+ );
3727
+ }
3728
+ }
3729
+ function serialize(value) {
3730
+ if (value === null) {
3731
+ return "null";
3732
+ }
3733
+ switch (typeof value) {
3734
+ case "boolean":
3735
+ return value ? "true" : "false";
3736
+ case "number": {
3737
+ const encoded = JSON.stringify(value);
3738
+ if (encoded === void 0) {
3739
+ throw new SignedObjectCanonicalizationError(
3740
+ "number could not be serialized as JSON"
3741
+ );
3742
+ }
3743
+ return encoded;
3744
+ }
3745
+ case "string":
3746
+ return JSON.stringify(value);
3747
+ case "object":
3748
+ if (Array.isArray(value)) {
3749
+ return `[${value.map((item) => serialize(item)).join(",")}]`;
3750
+ }
3751
+ return serializeObject(value);
3752
+ default:
3753
+ throw new SignedObjectCanonicalizationError(
3754
+ `unsupported JSON value type ${typeof value}`
3755
+ );
3756
+ }
3757
+ }
3758
+ function normalizeJsonObjectOrArray(input, path) {
3759
+ assertNoSymbolKeys(input, path);
3760
+ if (Array.isArray(input)) {
3761
+ for (const key of Object.getOwnPropertyNames(input)) {
3762
+ if (key === "length") {
3763
+ continue;
3764
+ }
3765
+ if (!isArrayIndexKey(key, input.length)) {
3766
+ throw new SignedObjectCanonicalizationError(
3767
+ `${path}.${key} is not allowed on a JSON array`
3768
+ );
3769
+ }
3770
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
3771
+ if (descriptor?.enumerable !== true) {
3772
+ throw new SignedObjectCanonicalizationError(
3773
+ `${path}[${key}] must be an enumerable JSON array item`
3774
+ );
3775
+ }
3776
+ if (!("value" in descriptor)) {
3777
+ throw new SignedObjectCanonicalizationError(
3778
+ `${path}[${key}] must be a JSON data property`
3779
+ );
3780
+ }
3781
+ }
3782
+ const output2 = [];
3783
+ for (let index = 0; index < input.length; index++) {
3784
+ if (!objectHasOwn(input, index)) {
3785
+ throw new SignedObjectCanonicalizationError(
3786
+ `${path}[${index}] must not be a sparse array hole`
3787
+ );
3788
+ }
3789
+ output2.push(normalizeJson(input[index], `${path}[${index}]`));
3790
+ }
3791
+ return output2;
3792
+ }
3793
+ const proto = Object.getPrototypeOf(input);
3794
+ if (proto !== Object.prototype && proto !== null) {
3795
+ throw new SignedObjectCanonicalizationError(
3796
+ `${path} must be a plain JSON object`
3797
+ );
3798
+ }
3799
+ const output = /* @__PURE__ */ Object.create(null);
3800
+ for (const key of Object.getOwnPropertyNames(input)) {
3801
+ assertUnicodeScalarString(key, `${path} key`);
3802
+ if (key === "__proto__" || key === "constructor") {
3803
+ throw new SignedObjectCanonicalizationError(
3804
+ `${path}.${key} is not allowed`
3805
+ );
3806
+ }
3807
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
3808
+ if (descriptor?.enumerable !== true) {
3809
+ throw new SignedObjectCanonicalizationError(
3810
+ `${path}.${key} must be an enumerable JSON object property`
3811
+ );
3812
+ }
3813
+ if (!("value" in descriptor)) {
3814
+ throw new SignedObjectCanonicalizationError(
3815
+ `${path}.${key} must be a JSON data property`
3816
+ );
3817
+ }
3818
+ const value = descriptor.value;
3819
+ output[key] = normalizeJson(value, `${path}.${key}`);
3820
+ }
3821
+ return output;
3822
+ }
3823
+ function assertNoSymbolKeys(input, path) {
3824
+ if (Object.getOwnPropertySymbols(input).length > 0) {
3825
+ throw new SignedObjectCanonicalizationError(
3826
+ `${path} must not have symbol properties`
3827
+ );
3828
+ }
3829
+ }
3830
+ function isArrayIndexKey(key, length2) {
3831
+ if (!/^(0|[1-9]\d*)$/.test(key)) {
3832
+ return false;
3833
+ }
3834
+ const index = Number(key);
3835
+ return Number.isSafeInteger(index) && index >= 0 && index < length2;
3836
+ }
3837
+ function serializeObject(value) {
3838
+ const keys = Object.keys(value).sort(compareUtf16CodeUnits);
3839
+ const parts = keys.map((key) => `${JSON.stringify(key)}:${serialize(value[key])}`);
3840
+ return `{${parts.join(",")}}`;
3841
+ }
3842
+ function compareUtf16CodeUnits(a, b) {
3843
+ const max = Math.min(a.length, b.length);
3844
+ for (let index = 0; index < max; index++) {
3845
+ const leftCodeUnit = a.charCodeAt(index);
3846
+ const rightCodeUnit = b.charCodeAt(index);
3847
+ if (leftCodeUnit !== rightCodeUnit) {
3848
+ return leftCodeUnit - rightCodeUnit;
3849
+ }
3850
+ }
3851
+ return a.length - b.length;
3852
+ }
3853
+ function assertUnicodeScalarString(value, path) {
3854
+ for (let index = 0; index < value.length; index++) {
3855
+ const code3 = value.charCodeAt(index);
3856
+ if (code3 >= 55296 && code3 <= 56319) {
3857
+ const next = value.charCodeAt(index + 1);
3858
+ if (Number.isNaN(next) || next < 56320 || next > 57343) {
3859
+ throw new SignedObjectCanonicalizationError(
3860
+ `${path} contains a lone high surrogate`
3861
+ );
3862
+ }
3863
+ index++;
3864
+ continue;
3865
+ }
3866
+ if (code3 >= 56320 && code3 <= 57343) {
3867
+ throw new SignedObjectCanonicalizationError(
3868
+ `${path} contains a lone low surrogate`
3869
+ );
3870
+ }
3871
+ }
3872
+ }
3873
+
3874
+ // src/policy/unified.ts
3875
+ var POLICY_SESSION_UCAN_V1_PROFILE = "policy-session-ucan/v1";
3876
+ var POLICY_SESSION_FACT_KEYS = [
3877
+ "profile",
3878
+ "ownerDid",
3879
+ "policyId",
3880
+ "policyDigestHex",
3881
+ "policyCid",
3882
+ "policyDelegationCid",
3883
+ "enforcementDelegationCid",
3884
+ "contentSourceDigestHex",
3885
+ "capabilityCeilingHashHex",
3886
+ "nativeProjectionHashHex",
3887
+ "enforcerDid",
3888
+ "nodeAudience",
3889
+ "recipientDid",
3890
+ "challengeId",
3891
+ "claimDigestHex",
3892
+ "claimJti",
3893
+ "vpDigestHex",
3894
+ "credentialEvidenceDigestHex",
3895
+ "decisionContextDigestHex",
3896
+ "issuanceAuditDigestHex",
3897
+ "remainingRedelegationDepth"
3898
+ ];
3899
+ var CompactHeaderSchema = z4.object({
3900
+ alg: z4.literal("EdDSA"),
3901
+ jwk: z4.object({ alg: z4.literal("EdDSA"), crv: z4.literal("Ed25519"), kty: z4.literal("OKP"), x: z4.string().min(1) }).strict(),
3902
+ typ: z4.literal("JWT"),
3903
+ ucv: z4.literal("0.10.0")
3904
+ }).strict();
3905
+ var CompactPayloadSchema = z4.object({
3906
+ att: z4.record(z4.record(z4.array(z4.unknown()).min(1))),
3907
+ aud: z4.string().min(1),
3908
+ exp: z4.number().int(),
3909
+ fct: z4.array(z4.record(z4.unknown())).length(1),
3910
+ iss: z4.string().min(1),
3911
+ nbf: z4.number().int(),
3912
+ nnc: z4.string().min(1),
3913
+ prf: z4.array(z4.string().min(1))
3914
+ }).strict();
3915
+ function createCompactPolicyInvocation(input) {
3916
+ const session = parsePolicySessionUcan(input.sessionAuthorization);
3917
+ if (session.cid !== input.sessionCid || session.aud !== input.recipientDid)
3918
+ throw new Error("policy session does not bind the recipient signer");
3919
+ const principal = input.recipientDid.split("#", 1)[0];
3920
+ const didMaterial = base58btc.decode(principal.slice("did:key:".length));
3921
+ if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1)
3922
+ throw new Error("policy invocation signer must be did:key Ed25519");
3923
+ const publicKey = ed255192.getPublicKey(input.privateKey);
3924
+ if (!equalBytes2(publicKey, didMaterial.slice(2)))
3925
+ throw new Error("policy invocation private key does not bind recipient DID");
3926
+ const now = input.now ?? Math.floor(Date.now() / 1e3);
3927
+ const exp = Math.min(now + 60, session.exp);
3928
+ if (exp <= now) throw new Error("policy session has expired");
3929
+ const header = { alg: "EdDSA", jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: encodeBase64Url(publicKey) }, typ: "JWT", ucv: "0.10.0" };
3930
+ const payload = {
3931
+ att: { [input.resource]: { [input.action]: [input.caveat ?? {}] } },
3932
+ aud: input.audienceDid,
3933
+ exp,
3934
+ fct: [input.facts ?? { type: "tinycloud.policy.invocation/v1", policyCid: session.fact.policyCid, sessionCid: session.cid }],
3935
+ iss: input.recipientDid.includes("#") ? input.recipientDid : `${principal}#${principal.slice("did:key:".length)}`,
3936
+ nbf: now,
3937
+ nnc: input.nonce ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
3938
+ prf: [session.cid]
3939
+ };
3940
+ const protectedSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(header)));
3941
+ const payloadSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(payload)));
3942
+ const signingInput = new TextEncoder().encode(`${protectedSegment}.${payloadSegment}`);
3943
+ const signature = ed255192.sign(signingInput, input.privateKey);
3944
+ return parseCompactUcanAuthorization(`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`);
3945
+ }
3946
+ function createCompactPolicyDescendant(input) {
3947
+ const parent = parseCompactUcanAuthorization(input.parentAuthorization, input.parentCid);
3948
+ if (parent.payload.aud !== input.issuerDid) throw new Error("policy descendant issuer is not the parent audience");
3949
+ const parentFact = parent.payload.fct[0];
3950
+ const depth = parentFact.remainingRedelegationDepth;
3951
+ if (!Number.isInteger(depth) || depth <= 0) throw new Error("policy descendant depth is exhausted");
3952
+ if (!compactAttenuationContains(parent.payload.att, input.attenuation))
3953
+ throw new Error("policy descendant exceeds the parent attenuation");
3954
+ const principal = input.issuerDid.split("#", 1)[0];
3955
+ const didMaterial = base58btc.decode(principal.slice("did:key:".length));
3956
+ const publicKey = ed255192.getPublicKey(input.privateKey);
3957
+ if (didMaterial.length !== 34 || !equalBytes2(publicKey, didMaterial.slice(2))) throw new Error("policy descendant signer does not bind issuer DID");
3958
+ const now = Math.max(input.now ?? Math.floor(Date.now() / 1e3), parent.payload.nbf + 1);
3959
+ const exp = Math.min(input.expiresAt ?? now + 60, now + 60, parent.payload.exp - 1);
3960
+ if (exp <= now) throw new Error("policy parent has no strictly narrower validity window");
3961
+ const header = { alg: "EdDSA", jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: encodeBase64Url(publicKey) }, typ: "JWT", ucv: "0.10.0" };
3962
+ const payload = {
3963
+ att: input.attenuation,
3964
+ aud: input.audienceDid,
3965
+ exp,
3966
+ fct: [{ ...parentFact, remainingRedelegationDepth: depth - 1 }],
3967
+ iss: input.issuerDid.includes("#") ? input.issuerDid : `${principal}#${principal.slice("did:key:".length)}`,
3968
+ nbf: now,
3969
+ nnc: input.nonce ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
3970
+ prf: [parent.cid]
3971
+ };
3972
+ const protectedSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(header)));
3973
+ const payloadSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(payload)));
3974
+ const signature = ed255192.sign(new TextEncoder().encode(`${protectedSegment}.${payloadSegment}`), input.privateKey);
3975
+ return parseCompactUcanAuthorization(`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`);
3976
+ }
3977
+ function compactAttenuationContains(parent, child) {
3978
+ const selector = (value) => {
3979
+ if (value.length !== 1 || !isRecordValue(value[0])) return void 0;
3980
+ const caveat = value[0];
3981
+ if (Object.keys(caveat).length !== 3 || caveat.type !== "xyz.tinycloud.resource/selector" || caveat.kind !== "exact" && caveat.kind !== "prefix" || typeof caveat.value !== "string") return void 0;
3982
+ return { kind: caveat.kind, value: caveat.value };
3983
+ };
3984
+ const equal = (left, right) => jcsCanonicalize(left) === jcsCanonicalize(right);
3985
+ return Object.entries(child).every(
3986
+ ([childResource, childActions]) => Object.entries(childActions).every(
3987
+ ([action, childCaveats]) => Object.entries(parent).some(([parentResource, parentActions]) => {
3988
+ const parentCaveats = parentActions[action];
3989
+ if (parentCaveats === void 0) return false;
3990
+ if (parentResource === childResource && equal(parentCaveats, childCaveats)) return true;
3991
+ const parentSelector = selector(parentCaveats);
3992
+ const childSelector = selector(childCaveats);
3993
+ return parentSelector?.kind === "prefix" && childSelector !== void 0 && parentSelector.value === parentResource && childSelector.value === childResource && (childResource === parentResource || childResource.startsWith(`${parentResource}/`));
3994
+ })
3995
+ )
3996
+ );
3997
+ }
3998
+ function isRecordValue(value) {
3999
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4000
+ }
4001
+ function parseCompactUcanAuthorization(input, expectedCid) {
4002
+ if (typeof input !== "string" || /\s/.test(input))
4003
+ throw new Error("Authorization must be compact UCAN bytes");
4004
+ const segments = input.split(".");
4005
+ if (segments.length !== 3 || segments.some((segment) => segment.length === 0))
4006
+ throw new Error("Authorization must have three compact segments");
4007
+ const [headerSegment, payloadSegment, signatureSegment] = segments;
4008
+ const headerBytes = decodeBase64Url(headerSegment);
4009
+ const payloadBytes = decodeBase64Url(payloadSegment);
4010
+ const signature = decodeBase64Url(signatureSegment);
4011
+ const headerValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(headerBytes));
4012
+ const payloadValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payloadBytes));
4013
+ if (jcsCanonicalize(headerValue) !== new TextDecoder().decode(headerBytes) || jcsCanonicalize(payloadValue) !== new TextDecoder().decode(payloadBytes))
4014
+ throw new Error("compact UCAN segments are not canonical JCS");
4015
+ const header = CompactHeaderSchema.parse(headerValue);
4016
+ const payload = CompactPayloadSchema.parse(payloadValue);
4017
+ if (payload.nbf >= payload.exp) throw new Error("compact UCAN validity is empty");
4018
+ const principal = payload.iss.split("#", 1)[0];
4019
+ const didMaterial = principal.startsWith("did:key:") ? base58btc.decode(principal.slice("did:key:".length)) : new Uint8Array();
4020
+ if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1)
4021
+ throw new Error("compact UCAN issuer must be did:key Ed25519");
4022
+ const publicKey = didMaterial.slice(2);
4023
+ if (!equalBytes2(publicKey, decodeBase64Url(header.jwk.x)))
4024
+ throw new Error("compact UCAN JWK does not bind issuer");
4025
+ if (!ed255192.verify(signature, new TextEncoder().encode(`${headerSegment}.${payloadSegment}`), publicKey))
4026
+ throw new Error("compact UCAN signature is invalid");
4027
+ const cid2 = CID.createV1(85, create(30, blake32(new TextEncoder().encode(input)))).toString();
4028
+ if (expectedCid !== void 0 && cid2 !== expectedCid)
4029
+ throw new Error("compact UCAN CID does not match exact Authorization bytes");
4030
+ return { authorization: input, cid: cid2, header, payload };
4031
+ }
4032
+ function parsePolicySessionUcan(authorization, expectedProofs) {
4033
+ const parsed = parseCompactUcanAuthorization(authorization);
4034
+ const fact = parsed.payload.fct[0];
4035
+ if (parsed.payload.prf.length !== 2 || parsed.payload.prf[0] === parsed.payload.prf[1])
4036
+ throw new Error("policy session UCAN requires two distinct ordered proofs");
4037
+ if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
4038
+ throw new Error("policy session UCAN ordered proofs do not match");
4039
+ const factKeys = Object.keys(fact);
4040
+ 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)))
4041
+ throw new Error("policy session UCAN fact is incomplete");
4042
+ 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)
4043
+ throw new Error("policy session UCAN fact is invalid");
4044
+ return {
4045
+ authorization,
4046
+ cid: parsed.cid,
4047
+ iss: parsed.payload.iss,
4048
+ aud: parsed.payload.aud,
4049
+ prf: parsed.payload.prf,
4050
+ att: parsed.payload.att,
4051
+ nbf: parsed.payload.nbf,
4052
+ exp: parsed.payload.exp,
4053
+ nnc: parsed.payload.nnc,
4054
+ fact
4055
+ };
4056
+ }
4057
+ function decodeBase64Url(value) {
4058
+ if (!/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1)
4059
+ throw new Error("value is not canonical base64url");
4060
+ const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
4061
+ const binary = typeof Buffer !== "undefined" ? Buffer.from(normalized, "base64").toString("binary") : atob(normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "="));
4062
+ const bytes2 = Uint8Array.from(binary, (character) => character.charCodeAt(0));
4063
+ const encoded = typeof Buffer !== "undefined" ? Buffer.from(bytes2).toString("base64url") : btoa(String.fromCharCode(...bytes2)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
4064
+ if (encoded !== value) throw new Error("value is not canonical base64url");
4065
+ return bytes2;
4066
+ }
4067
+ function encodeBase64Url(value) {
4068
+ return typeof Buffer !== "undefined" ? Buffer.from(value).toString("base64url") : btoa(String.fromCharCode(...value)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
4069
+ }
4070
+ function equalBytes2(left, right) {
4071
+ return left.length === right.length && left.every((byte, index) => byte === right[index]);
4072
+ }
4073
+
4074
+ // src/delegations/portable.ts
4075
+ var ResourceSchema = z5.object({
4076
+ service: z5.string().min(1),
4077
+ space: z5.string().min(1),
4078
+ path: z5.string(),
4079
+ actions: z5.array(z5.string().min(1)).min(1),
4080
+ caveats: z5.array(z5.record(z5.unknown())).optional()
4081
+ }).strict();
4082
+ var PortableDelegationSchema = z5.object({
4083
+ cid: z5.string().min(1),
4084
+ delegationHeader: z5.object({ Authorization: z5.string().min(1) }).strict(),
4085
+ ownerAddress: z5.string().min(1),
4086
+ chainId: z5.number().int().nonnegative(),
4087
+ host: z5.string().url().optional(),
4088
+ spaceId: z5.string().min(1),
4089
+ path: z5.string(),
4090
+ actions: z5.array(z5.string().min(1)).min(1),
4091
+ caveats: z5.array(z5.record(z5.unknown())).optional(),
4092
+ expiry: z5.coerce.date(),
4093
+ delegateDID: z5.string().min(1),
4094
+ delegatorDID: z5.string().min(1).optional(),
4095
+ parentCid: z5.string().min(1).optional(),
4096
+ disableSubDelegation: z5.boolean().optional(),
4097
+ resources: z5.array(ResourceSchema).min(1).optional()
4098
+ }).strict();
4099
+ function parsePortableDelegation(input) {
4100
+ const parsed = PortableDelegationSchema.parse(input);
4101
+ if (parsed.resources !== void 0 && parsed.resources.length === 0)
4102
+ throw new Error("portable delegation resources must not be empty");
4103
+ return parsed;
4104
+ }
4105
+ function serializePortableDelegation(delegation) {
4106
+ return JSON.stringify({
4107
+ ...delegation,
4108
+ expiry: delegation.expiry.toISOString()
4109
+ });
4110
+ }
4111
+ async function importPortableDelegation(fetchFn, host, delegation) {
4112
+ const parsed = parsePortableDelegation(delegation);
4113
+ const response = await fetchFn(`${host.replace(/\/+$/, "")}/delegate`, {
4114
+ method: "POST",
4115
+ headers: { Authorization: parsed.delegationHeader.Authorization }
4116
+ });
4117
+ if (!response.ok)
4118
+ throw new Error(`node rejected delegation import (${response.status})`);
4119
+ const body = await response.json().catch(() => ({}));
4120
+ if (body === null || typeof body !== "object" || Array.isArray(body))
4121
+ return {};
4122
+ return body;
4123
+ }
4124
+ async function delegatePortablePolicySession(input) {
4125
+ const parent = parsePortableDelegation(input.parent);
4126
+ const descendant = createCompactPolicyDescendant({
4127
+ parentAuthorization: parent.delegationHeader.Authorization.replace(/^Bearer\s+/i, ""),
4128
+ parentCid: parent.cid,
4129
+ issuerDid: parent.delegateDID,
4130
+ audienceDid: input.delegateDID,
4131
+ attenuation: input.attenuation,
4132
+ privateKey: input.issuerPrivateKey,
4133
+ ...input.now === void 0 ? {} : { now: input.now },
4134
+ ...input.expiresAt === void 0 ? {} : { expiresAt: input.expiresAt },
4135
+ ...input.nonce === void 0 ? {} : { nonce: input.nonce }
4136
+ });
4137
+ const resources = Object.entries(descendant.payload.att).map(([resource, abilities]) => {
4138
+ const actions = Object.keys(abilities);
4139
+ if (resource.startsWith("urn:tinycloud:encryption:")) {
4140
+ return { service: "encryption", space: "encryption", path: resource, actions };
4141
+ }
4142
+ const match = /^tinycloud:\/\/([^/]+)\/kv\/(.+)$/.exec(resource);
4143
+ if (match === null) throw new Error("policy descendant resource is invalid");
4144
+ const caveats = Object.values(abilities).flat().filter((value) => value !== null && typeof value === "object" && !Array.isArray(value));
4145
+ return { service: "kv", space: match[1], path: match[2], actions, ...caveats.length === 0 ? {} : { caveats } };
4146
+ });
4147
+ const primary = resources.find((resource) => resource.service === "kv") ?? resources[0];
4148
+ if (primary === void 0) throw new Error("policy descendant attenuation is empty");
4149
+ const portable = {
4150
+ cid: descendant.cid,
4151
+ delegationHeader: { Authorization: descendant.authorization },
4152
+ ownerAddress: parent.ownerAddress,
4153
+ chainId: parent.chainId,
4154
+ host: input.host,
4155
+ spaceId: primary.space,
4156
+ path: primary.path,
4157
+ actions: primary.actions,
4158
+ expiry: new Date(descendant.payload.exp * 1e3),
4159
+ delegateDID: input.delegateDID,
4160
+ delegatorDID: parent.delegateDID,
4161
+ parentCid: parent.cid,
4162
+ disableSubDelegation: descendant.payload.fct[0]?.remainingRedelegationDepth === 0,
4163
+ resources
4164
+ };
4165
+ await importPortableDelegation(input.fetch, input.host, portable);
4166
+ return portable;
4167
+ }
4168
+
3666
4169
  // src/delegations/ShareRecipientClient.ts
3667
4170
  var DEFAULT_MAX_ARTIFACT_BYTES2 = MAX_SHARE_ARTIFACT_BYTES;
3668
4171
  var DEFAULT_MAX_CONTENT_BYTES = MAX_SHARE_CONTENT_BYTES;
@@ -3738,7 +4241,7 @@ function randomJti() {
3738
4241
  return bytesToBase64Url2(bytes2);
3739
4242
  }
3740
4243
  function digestCanonical(value) {
3741
- return bytesToBase64Url2(sha2564(new TextEncoder().encode(canonicalJsonForProof(value))));
4244
+ return bytesToBase64Url2(sha2565(new TextEncoder().encode(canonicalJsonForProof(value))));
3742
4245
  }
3743
4246
  function actionWire(action) {
3744
4247
  return action === "get" ? "tinycloud.kv/get" : action === "metadata" ? "tinycloud.kv/metadata" : action === "list" ? "tinycloud.kv/list" : "tinycloud.kv/put";
@@ -3850,9 +4353,31 @@ var ShareRecipientClient = class {
3850
4353
  if (policy && this.options.bearerSession !== void 0) throw new ShareAccessError("SHARE_POLICY_BEARER_MISMATCH");
3851
4354
  if (bearer && this.options.presentation !== void 0) throw new ShareAccessError("SHARE_BEARER_PRESENTATION_MISMATCH");
3852
4355
  const session = bearer || legacyBearer ? this.options.bearerSession : recipientDidSession ? recipientDidSession : policy && this.options.establishPolicySession !== void 0 ? await this.options.establishPolicySession({ envelope, presentation: this.options.presentation }) : policy ? await this.establishPolicySession(envelope) : void 0;
3853
- const kv = bearer || legacyBearer ? session === void 0 ? void 0 : this.createKV(envelope.origin, session) : void 0;
3854
4356
  const policySession = policy ? session : void 0;
3855
4357
  if (policySession !== void 0) this.assertProvidedPolicySession(policySession, envelope);
4358
+ const portableDelegation = policySession === void 0 ? void 0 : this.policySessionDelegation(policySession, envelope.origin);
4359
+ const portableInvoke = portableDelegation === void 0 ? void 0 : this.policyInvocationSigner(portableDelegation);
4360
+ if (portableDelegation !== void 0) {
4361
+ if (this.options.importDelegation === void 0) {
4362
+ await importPortableDelegation(this.fetchFn, envelope.origin, portableDelegation);
4363
+ } else {
4364
+ const imported = await this.options.importDelegation(portableDelegation);
4365
+ if (imported !== void 0) {
4366
+ const normalized = parsePortableDelegation(imported);
4367
+ if (normalized.delegationHeader.Authorization !== portableDelegation.delegationHeader.Authorization) {
4368
+ throw new ShareAccessError("SHARE_SESSION_INVALID", "delegation importer changed authorization bytes");
4369
+ }
4370
+ }
4371
+ }
4372
+ }
4373
+ const ordinarySession = portableDelegation === void 0 ? void 0 : {
4374
+ delegationHeader: portableDelegation.delegationHeader,
4375
+ delegationCid: portableDelegation.cid,
4376
+ spaceId: this.policySessionProjection(portableDelegation).spaceId,
4377
+ verificationMethod: portableDelegation.delegateDID,
4378
+ jwk: this.options.bearerSession?.jwk ?? {}
4379
+ };
4380
+ const kv = bearer || legacyBearer || ordinarySession !== void 0 ? session === void 0 && ordinarySession === void 0 ? void 0 : this.createKV(envelope.origin, ordinarySession ?? session, portableInvoke) : void 0;
3856
4381
  const advertisedCapability = { spaceId: envelope.spaceId, resource: envelope.resource, actions: envelope.actions };
3857
4382
  const capability = policySession?.capability === void 0 ? advertisedCapability : intersectShareCapabilities(advertisedCapability, policySession.capability);
3858
4383
  if (capability === void 0) throw new ShareAccessError("SHARE_CAPABILITY_EMPTY");
@@ -3877,6 +4402,11 @@ var ShareRecipientClient = class {
3877
4402
  const scope = normalizePath(input.path ?? envelope.resource.path);
3878
4403
  this.assertAllowed(capability, "list", scope);
3879
4404
  if (kv === void 0 && policySession === void 0) throw new ShareAccessError("SHARE_SESSION_REQUIRED");
4405
+ if (portableDelegation !== void 0 && kv !== void 0) {
4406
+ const result2 = await kv.list({ path: scope, limit: input.limit, cursor: input.cursor });
4407
+ if (!result2.ok) throw result2.error;
4408
+ return { keys: result2.data.keys, truncated: result2.data.truncated, nextCursor: result2.data.nextCursor };
4409
+ }
3880
4410
  if (policySession !== void 0) return this.nativeList(envelope, policySession, scope, input.limit, input.cursor);
3881
4411
  const result = await kv.list({ path: scope, limit: input.limit, cursor: input.cursor });
3882
4412
  if (!result.ok) throw result.error;
@@ -3890,6 +4420,14 @@ var ShareRecipientClient = class {
3890
4420
  const resolvedPath = normalizePath(path);
3891
4421
  this.assertAllowed(capability, "save", resolvedPath);
3892
4422
  if (kv === void 0 && policySession === void 0) throw new ShareAccessError("SHARE_SESSION_REQUIRED");
4423
+ if (portableDelegation !== void 0 && kv !== void 0) {
4424
+ const result2 = await kv.put(resolvedPath, bytes2, { contentType: input.contentType ?? envelope.content.mimeType, ifMatch: input.etag });
4425
+ if (!result2.ok) {
4426
+ if (result2.error.code === ErrorCodes.KV_PRECONDITION_FAILED || result2.error.code === ErrorCodes.KV_CONFLICT) throw new ShareConflict(resolvedPath, result2.error.meta?.etag);
4427
+ throw result2.error;
4428
+ }
4429
+ return { etag: result2.data.headers.etag };
4430
+ }
3893
4431
  if (policySession !== void 0) return this.nativeSave(envelope, policySession, resolvedPath, bytes2, input);
3894
4432
  const result = await kv.put(resolvedPath, bytes2, { contentType: input.contentType ?? envelope.content.mimeType, ifMatch: input.etag });
3895
4433
  if (!result.ok) {
@@ -3905,22 +4443,79 @@ var ShareRecipientClient = class {
3905
4443
  const metadata = async (path = envelope.resource.kind === "exact" ? envelope.resource.path : "") => {
3906
4444
  const resolvedPath = normalizePath(path);
3907
4445
  this.assertAllowed(capability, "metadata", resolvedPath);
4446
+ if (portableDelegation !== void 0 && kv !== void 0) {
4447
+ const result2 = await kv.head(resolvedPath);
4448
+ if (!result2.ok) throw result2.error;
4449
+ return { metadata: { "content-type": result2.data.headers.contentType ?? "application/octet-stream", "content-length": String(result2.data.headers.contentLength ?? ""), ...result2.data.headers.lastModified === void 0 ? {} : { "last-modified": result2.data.headers.lastModified } }, etag: result2.data.headers.etag, contentType: result2.data.headers.contentType, size: result2.data.headers.contentLength };
4450
+ }
3908
4451
  if (policySession !== void 0) return this.nativeMetadata(envelope, policySession, resolvedPath);
3909
4452
  if (kv === void 0) throw new ShareAccessError("SHARE_SESSION_REQUIRED");
3910
4453
  const result = await kv.head(resolvedPath);
3911
4454
  if (!result.ok) throw result.error;
3912
4455
  return { metadata: { "content-type": result.data.headers.contentType ?? "application/octet-stream", "content-length": String(result.data.headers.contentLength ?? ""), ...result.data.headers.lastModified === void 0 ? {} : { "last-modified": result.data.headers.lastModified } }, etag: result.data.headers.etag, contentType: result.data.headers.contentType, size: result.data.headers.contentLength };
3913
4456
  };
3914
- return { kind: "share-v2", envelope, location: { ...location, key: void 0 }, resource: envelope.resource, actions: envelope.actions, expiresAt: new Date(envelope.expiresAt), kv, get, listChildren, save, metadata };
4457
+ return { kind: "share-v2", envelope, location: { ...location, key: void 0 }, resource: envelope.resource, actions: envelope.actions, expiresAt: new Date(envelope.expiresAt), kv, portableDelegation, get, listChildren, save, metadata };
3915
4458
  } catch (error) {
3916
4459
  throw asError(error);
3917
4460
  }
3918
4461
  }
3919
- createKV(origin, session) {
4462
+ policySessionDelegation(session, origin) {
4463
+ if (session.runtimeDelegation === void 0) return void 0;
4464
+ const delegation = parsePortableDelegation(session.runtimeDelegation);
4465
+ if (delegation.host !== void 0 && delegation.host.replace(/\/+$/, "") !== origin.replace(/\/+$/, "")) throw new ShareAccessError("SHARE_ORIGIN_MISMATCH", "delegation host does not match the trusted share origin");
4466
+ return delegation;
4467
+ }
4468
+ policyInvocationSigner(delegation) {
4469
+ const privateKey = this.options.policySessionPrivateKey;
4470
+ if (privateKey === void 0) throw new ShareAccessError("SHARE_SESSION_SIGNER_REQUIRED");
4471
+ const session = parsePolicySessionUcan(delegation.delegationHeader.Authorization);
4472
+ if (session.cid !== delegation.cid || session.aud !== delegation.delegateDID)
4473
+ throw new ShareAccessError("SHARE_SESSION_INVALID");
4474
+ return (_serviceSession, service, path, action, facts) => {
4475
+ const serviceName = service.replace(/^tinycloud\./, "");
4476
+ const cleanPath = path.replace(/^\//, "").replace(/\/$/, "");
4477
+ const authority = Object.entries(session.att).find(([parentResource, abilities]) => {
4478
+ if (!(action in abilities)) return false;
4479
+ if (serviceName === "encryption") return parentResource.startsWith("urn:tinycloud:encryption:") && parentResource === cleanPath;
4480
+ const match = /^tinycloud:\/\/[^/]+\/kv\/(.+)$/.exec(parentResource);
4481
+ if (match === null) return false;
4482
+ const parentPath = match[1];
4483
+ const caveats = abilities[action] ?? [];
4484
+ const prefix = caveats.some((value) => isRecord(value) && value.kind === "prefix");
4485
+ return cleanPath === parentPath || prefix && cleanPath.startsWith(`${parentPath}/`);
4486
+ });
4487
+ if (authority === void 0) throw new ShareAccessError("SHARE_OUT_OF_SCOPE");
4488
+ const resource = serviceName === "encryption" ? authority[0] : `${authority[0].slice(0, authority[0].indexOf("/kv/") + 4)}${cleanPath}`;
4489
+ const caveat = serviceName === "encryption" ? {} : { type: "xyz.tinycloud.resource/selector", kind: "exact", value: resource };
4490
+ const invocation = createCompactPolicyInvocation({
4491
+ sessionAuthorization: delegation.delegationHeader.Authorization,
4492
+ sessionCid: delegation.cid,
4493
+ recipientDid: delegation.delegateDID,
4494
+ audienceDid: String(session.fact.nodeAudience),
4495
+ resource,
4496
+ action,
4497
+ caveat,
4498
+ ...facts?.length === 1 ? { facts: facts[0] } : {},
4499
+ privateKey,
4500
+ now: Math.floor(this.now().getTime() / 1e3)
4501
+ });
4502
+ return { Authorization: invocation.authorization };
4503
+ };
4504
+ }
4505
+ policySessionProjection(delegation) {
4506
+ const session = parsePolicySessionUcan(delegation.delegationHeader.Authorization);
4507
+ if (session.cid !== delegation.cid || session.aud !== delegation.delegateDID) throw new ShareAccessError("SHARE_SESSION_INVALID");
4508
+ const kvResource = Object.keys(session.att).find((resource) => /^tinycloud:\/\/[^/]+\/kv\/.+$/.test(resource));
4509
+ const match = kvResource === void 0 ? void 0 : /^tinycloud:\/\/([^/]+)\/kv\/(.+)$/.exec(kvResource);
4510
+ if (match === void 0 || match === null) throw new ShareAccessError("SHARE_SESSION_INVALID");
4511
+ return { spaceId: match[1] };
4512
+ }
4513
+ createKV(origin, session, invokeOverride) {
4514
+ const invoke = invokeOverride ?? this.options.invoke ?? nativeInvoke;
3920
4515
  if (this.options.createKVService !== void 0) {
3921
- return this.options.createKVService({ hosts: [origin], session, invoke: this.options.invoke ?? nativeInvoke, fetch: this.fetchFn });
4516
+ return this.options.createKVService({ hosts: [origin], session, invoke, fetch: this.fetchFn });
3922
4517
  }
3923
- const context = new ServiceContext({ hosts: [origin], session, invoke: this.options.invoke ?? nativeInvoke, fetch: this.fetchFn });
4518
+ const context = new ServiceContext({ hosts: [origin], session, invoke, fetch: this.fetchFn });
3924
4519
  const service = new KVService({ prefix: "" });
3925
4520
  context.registerService("kv", service);
3926
4521
  service.initialize(context);
@@ -3963,7 +4558,7 @@ var ShareRecipientClient = class {
3963
4558
  if (bytes2.byteLength > (this.options.maxContentBytes ?? DEFAULT_MAX_CONTENT_BYTES)) throw new ShareAccessError("SHARE_TOO_LARGE");
3964
4559
  const contentType = input.contentType ?? envelope.content.mimeType;
3965
4560
  if (contentType !== envelope.content.mimeType && !/^text\/(?:plain|markdown)(?:;|$)/i.test(contentType)) throw new ShareAccessError("SHARE_CONTENT_TYPE_INVALID");
3966
- const result = await this.invokeNative({ envelope, session, action: "put", resource: path, body: bytes2, bodyDigest: bytesToBase64Url2(sha2564(bytes2)), contentType, ifMatch: input.etag, mediaType: "application/vnd.tinycloud.share+json" });
4561
+ const result = await this.invokeNative({ envelope, session, action: "put", resource: path, body: bytes2, bodyDigest: bytesToBase64Url2(sha2565(bytes2)), contentType, ifMatch: input.etag, mediaType: "application/vnd.tinycloud.share+json" });
3967
4562
  if (result.status === 412 || result.status === 409) throw new ShareConflict(path, result.headers?.etag);
3968
4563
  if (result.status === 401 || result.status === 403) throw new ShareAccessError("SHARE_DENIED");
3969
4564
  if (result.status < 200 || result.status >= 300) throw new ShareAccessError("SHARE_WRITE_FAILED");
@@ -4085,7 +4680,7 @@ var ShareRecipientClient = class {
4085
4680
  };
4086
4681
  const signed = this.options.signNativeInvoke === void 0 ? void 0 : await this.options.signNativeInvoke({ envelope: input.envelope, session: input.session, request: body });
4087
4682
  const transport = this.options.invoke !== void 0 || session.authorization !== void 0 || session.delegationHeader !== void 0 ? this.defaultNativeHeaders(session, input.action, input.resource, body) : void 0;
4088
- const response = await this.fetchFn(v2 ? `${base3}/share/v2/invoke` : `${base3}/invoke`, {
4683
+ const response = await this.fetchFn(`${base3}/invoke`, {
4089
4684
  method: "POST",
4090
4685
  headers: { ...headersRecord(transport), ...headersRecord(signed), "content-type": input.mediaType, accept: input.mediaType },
4091
4686
  body: JSON.stringify(body),
@@ -4209,7 +4804,7 @@ var ShareRecipientClient = class {
4209
4804
  }]);
4210
4805
  }
4211
4806
  digestMatches(value, bytes2) {
4212
- const digest4 = bytesToBase64Url2(sha2564(bytes2));
4807
+ const digest4 = bytesToBase64Url2(sha2565(bytes2));
4213
4808
  return value === digest4 || value === `sha-256=:${digest4}:`;
4214
4809
  }
4215
4810
  assertNativeRequestBounds(envelope, session, operation, path, limit, cursor) {
@@ -4427,7 +5022,7 @@ var ShareRecipientClient = class {
4427
5022
  nodeAudience: binding.nodeAudience,
4428
5023
  ...isV2 ? {
4429
5024
  enforcerDid: challenge.enforcerDid,
4430
- credentialDigest: typeof presentation.credentialDigest === "string" ? presentation.credentialDigest : bytesToBase64Url2(sha2564(new TextEncoder().encode(credential)))
5025
+ credentialDigest: typeof presentation.credentialDigest === "string" ? presentation.credentialDigest : bytesToBase64Url2(sha2565(new TextEncoder().encode(credential)))
4431
5026
  } : {},
4432
5027
  action,
4433
5028
  ...binding.actions === void 0 ? {} : { actions: [...binding.actions] },
@@ -4493,7 +5088,7 @@ var ShareRecipientClient = class {
4493
5088
  }
4494
5089
  if (isJwsProof && detached.kid !== `${signerDid}#${encoded}` || isEd25519Proof && detached.signerDid !== signerDid || signature.length !== 64) throw new ShareAccessError("SHARE_SESSION_INVALID", "Node proof binding is invalid");
4495
5090
  const bytes2 = new TextEncoder().encode(`${domain}${canonicalJsonForProof(message)}`);
4496
- if (!ed255192.verify(signature, bytes2, key)) throw new ShareAccessError("SHARE_SESSION_INVALID", "Node proof signature is invalid");
5091
+ if (!ed255193.verify(signature, bytes2, key)) throw new ShareAccessError("SHARE_SESSION_INVALID", "Node proof signature is invalid");
4497
5092
  }
4498
5093
  assertChallengeRequestBinding(request, challenge) {
4499
5094
  const source = isRecord(request.body) ? request.body : request;
@@ -5697,7 +6292,7 @@ function shareKeyMatchesDid(key, didUrl) {
5697
6292
  if (!publicKey) return false;
5698
6293
  const jwkBytes = decodeBase64UrlBytes(key.x);
5699
6294
  const privateKey = decodeBase64UrlBytes(key.d);
5700
- const derivedPublicKey = ed255193.getPublicKey(privateKey);
6295
+ const derivedPublicKey = ed255194.getPublicKey(privateKey);
5701
6296
  return privateKey.length === 32 && publicKey.length === jwkBytes.length && publicKey.every((byte, index) => byte === jwkBytes[index]) && derivedPublicKey.length === jwkBytes.length && derivedPublicKey.every((byte, index) => byte === jwkBytes[index]);
5702
6297
  } catch {
5703
6298
  return false;
@@ -5747,210 +6342,12 @@ function verifySignedChild(result, expected, computeCid) {
5747
6342
  }
5748
6343
 
5749
6344
  // src/delegations/owner-policy.ts
5750
- import { ed25519 as ed255195 } from "@noble/curves/ed25519";
5751
- import { sha256 as sha2566 } from "@noble/hashes/sha256";
5752
-
5753
- // src/policy/signed-object.ts
5754
- import { ed25519 as ed255194 } from "@noble/curves/ed25519";
5755
- import { bytesToHex, sha256 as sha2565, verifyMessage } from "viem";
5756
-
5757
- // src/policy/errors.ts
5758
- var SignedObjectProfileError = class extends Error {
5759
- constructor(code3, message) {
5760
- super(message);
5761
- this.name = "SignedObjectProfileError";
5762
- this.code = code3;
5763
- }
5764
- };
5765
- var SignedObjectCanonicalizationError = class extends SignedObjectProfileError {
5766
- constructor(message) {
5767
- super("canonicalization-mismatch", message);
5768
- this.name = "SignedObjectCanonicalizationError";
5769
- }
5770
- };
5771
-
5772
- // src/policy/jcs.ts
5773
- var objectHasOwn = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
5774
- Object.prototype.hasOwnProperty
5775
- );
5776
- function jcsCanonicalize(input) {
5777
- return serialize(normalizeJson(input, "$"));
5778
- }
5779
- function normalizeJson(input, path = "$") {
5780
- if (input === null) {
5781
- return null;
5782
- }
5783
- switch (typeof input) {
5784
- case "boolean":
5785
- return input;
5786
- case "number":
5787
- if (!Number.isFinite(input)) {
5788
- throw new SignedObjectCanonicalizationError(
5789
- `${path} must be a finite JSON number`
5790
- );
5791
- }
5792
- return input;
5793
- case "string":
5794
- assertUnicodeScalarString(input, path);
5795
- return input;
5796
- case "object":
5797
- return normalizeJsonObjectOrArray(input, path);
5798
- case "bigint":
5799
- case "function":
5800
- case "symbol":
5801
- case "undefined":
5802
- default:
5803
- throw new SignedObjectCanonicalizationError(
5804
- `${path} is not a JSON value`
5805
- );
5806
- }
5807
- }
5808
- function serialize(value) {
5809
- if (value === null) {
5810
- return "null";
5811
- }
5812
- switch (typeof value) {
5813
- case "boolean":
5814
- return value ? "true" : "false";
5815
- case "number": {
5816
- const encoded = JSON.stringify(value);
5817
- if (encoded === void 0) {
5818
- throw new SignedObjectCanonicalizationError(
5819
- "number could not be serialized as JSON"
5820
- );
5821
- }
5822
- return encoded;
5823
- }
5824
- case "string":
5825
- return JSON.stringify(value);
5826
- case "object":
5827
- if (Array.isArray(value)) {
5828
- return `[${value.map((item) => serialize(item)).join(",")}]`;
5829
- }
5830
- return serializeObject(value);
5831
- default:
5832
- throw new SignedObjectCanonicalizationError(
5833
- `unsupported JSON value type ${typeof value}`
5834
- );
5835
- }
5836
- }
5837
- function normalizeJsonObjectOrArray(input, path) {
5838
- assertNoSymbolKeys(input, path);
5839
- if (Array.isArray(input)) {
5840
- for (const key of Object.getOwnPropertyNames(input)) {
5841
- if (key === "length") {
5842
- continue;
5843
- }
5844
- if (!isArrayIndexKey(key, input.length)) {
5845
- throw new SignedObjectCanonicalizationError(
5846
- `${path}.${key} is not allowed on a JSON array`
5847
- );
5848
- }
5849
- const descriptor = Object.getOwnPropertyDescriptor(input, key);
5850
- if (descriptor?.enumerable !== true) {
5851
- throw new SignedObjectCanonicalizationError(
5852
- `${path}[${key}] must be an enumerable JSON array item`
5853
- );
5854
- }
5855
- if (!("value" in descriptor)) {
5856
- throw new SignedObjectCanonicalizationError(
5857
- `${path}[${key}] must be a JSON data property`
5858
- );
5859
- }
5860
- }
5861
- const output2 = [];
5862
- for (let index = 0; index < input.length; index++) {
5863
- if (!objectHasOwn(input, index)) {
5864
- throw new SignedObjectCanonicalizationError(
5865
- `${path}[${index}] must not be a sparse array hole`
5866
- );
5867
- }
5868
- output2.push(normalizeJson(input[index], `${path}[${index}]`));
5869
- }
5870
- return output2;
5871
- }
5872
- const proto = Object.getPrototypeOf(input);
5873
- if (proto !== Object.prototype && proto !== null) {
5874
- throw new SignedObjectCanonicalizationError(
5875
- `${path} must be a plain JSON object`
5876
- );
5877
- }
5878
- const output = /* @__PURE__ */ Object.create(null);
5879
- for (const key of Object.getOwnPropertyNames(input)) {
5880
- assertUnicodeScalarString(key, `${path} key`);
5881
- if (key === "__proto__" || key === "constructor") {
5882
- throw new SignedObjectCanonicalizationError(
5883
- `${path}.${key} is not allowed`
5884
- );
5885
- }
5886
- const descriptor = Object.getOwnPropertyDescriptor(input, key);
5887
- if (descriptor?.enumerable !== true) {
5888
- throw new SignedObjectCanonicalizationError(
5889
- `${path}.${key} must be an enumerable JSON object property`
5890
- );
5891
- }
5892
- if (!("value" in descriptor)) {
5893
- throw new SignedObjectCanonicalizationError(
5894
- `${path}.${key} must be a JSON data property`
5895
- );
5896
- }
5897
- const value = descriptor.value;
5898
- output[key] = normalizeJson(value, `${path}.${key}`);
5899
- }
5900
- return output;
5901
- }
5902
- function assertNoSymbolKeys(input, path) {
5903
- if (Object.getOwnPropertySymbols(input).length > 0) {
5904
- throw new SignedObjectCanonicalizationError(
5905
- `${path} must not have symbol properties`
5906
- );
5907
- }
5908
- }
5909
- function isArrayIndexKey(key, length2) {
5910
- if (!/^(0|[1-9]\d*)$/.test(key)) {
5911
- return false;
5912
- }
5913
- const index = Number(key);
5914
- return Number.isSafeInteger(index) && index >= 0 && index < length2;
5915
- }
5916
- function serializeObject(value) {
5917
- const keys = Object.keys(value).sort(compareUtf16CodeUnits);
5918
- const parts = keys.map((key) => `${JSON.stringify(key)}:${serialize(value[key])}`);
5919
- return `{${parts.join(",")}}`;
5920
- }
5921
- function compareUtf16CodeUnits(a, b) {
5922
- const max = Math.min(a.length, b.length);
5923
- for (let index = 0; index < max; index++) {
5924
- const leftCodeUnit = a.charCodeAt(index);
5925
- const rightCodeUnit = b.charCodeAt(index);
5926
- if (leftCodeUnit !== rightCodeUnit) {
5927
- return leftCodeUnit - rightCodeUnit;
5928
- }
5929
- }
5930
- return a.length - b.length;
5931
- }
5932
- function assertUnicodeScalarString(value, path) {
5933
- for (let index = 0; index < value.length; index++) {
5934
- const code3 = value.charCodeAt(index);
5935
- if (code3 >= 55296 && code3 <= 56319) {
5936
- const next = value.charCodeAt(index + 1);
5937
- if (Number.isNaN(next) || next < 56320 || next > 57343) {
5938
- throw new SignedObjectCanonicalizationError(
5939
- `${path} contains a lone high surrogate`
5940
- );
5941
- }
5942
- index++;
5943
- continue;
5944
- }
5945
- if (code3 >= 56320 && code3 <= 57343) {
5946
- throw new SignedObjectCanonicalizationError(
5947
- `${path} contains a lone low surrogate`
5948
- );
5949
- }
5950
- }
5951
- }
6345
+ import { ed25519 as ed255196 } from "@noble/curves/ed25519";
6346
+ import { sha256 as sha2567 } from "@noble/hashes/sha256";
5952
6347
 
5953
6348
  // src/policy/signed-object.ts
6349
+ import { ed25519 as ed255195 } from "@noble/curves/ed25519";
6350
+ import { bytesToHex, sha256 as sha2566, verifyMessage } from "viem";
5954
6351
  var textEncoder2 = new TextEncoder();
5955
6352
  var objectHasOwn2 = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
5956
6353
  Object.prototype.hasOwnProperty
@@ -5986,10 +6383,10 @@ function fromB64(value) {
5986
6383
  throw new Error("base64url decoding is unavailable");
5987
6384
  }
5988
6385
  function cid(bytesValue) {
5989
- return CID.createV1(code, create(sha256.code, sha2566(bytesValue))).toString(base32.encoder);
6386
+ return CID.createV1(code, create(sha256.code, sha2567(bytesValue))).toString(base32.encoder);
5990
6387
  }
5991
6388
  function digest3(bytesValue) {
5992
- return b64(sha2566(bytesValue));
6389
+ return b64(sha2567(bytesValue));
5993
6390
  }
5994
6391
  function didKeyFromEd25519PublicKey(publicKey) {
5995
6392
  if (publicKey.length !== 32) throw new Error("share key public key must be 32 bytes");
@@ -6174,7 +6571,7 @@ function validateOwnerSharePolicyRegistration(value, expected) {
6174
6571
  const publicKey = encodedKid.length === 34 ? encodedKid.slice(2) : encodedKid;
6175
6572
  const signatureBytes = fromB64(proof.signature);
6176
6573
  const signedBytes = new TextEncoder().encode(`${OWNER_SHARE_REGISTRATION_DOMAIN}${canonicalizeSignedObjectUnsigned(registrationCore)}`);
6177
- if (signatureBytes.length !== 64 || !ed255195.verify(signatureBytes, signedBytes, publicKey)) throw new Error("owner-share registration proof signature is invalid");
6574
+ if (signatureBytes.length !== 64 || !ed255196.verify(signatureBytes, signedBytes, publicKey)) throw new Error("owner-share registration proof signature is invalid");
6178
6575
  return { registration, proof };
6179
6576
  }
6180
6577
  function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
@@ -6188,7 +6585,7 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
6188
6585
  }
6189
6586
 
6190
6587
  // src/delegations/share-delivery.ts
6191
- import { ed25519 as ed255196 } from "@noble/curves/ed25519";
6588
+ import { ed25519 as ed255197 } from "@noble/curves/ed25519";
6192
6589
  var SHARE_DELIVERY_AUTHORIZATION_DOMAIN = "xyz.tinycloud.share/delivery-authorization/v2\0";
6193
6590
  function isCanonicalHttpsOrigin(value) {
6194
6591
  let url;
@@ -6276,7 +6673,7 @@ function validateShareDeliveryAuthorizationBytes(bytesValue, expected) {
6276
6673
  if (publicKey.length !== 32) throw new Error("share delivery authorization proof key is invalid");
6277
6674
  const signatureBytes = fromB642(proof.signature);
6278
6675
  const signedBytes = new TextEncoder().encode(`${SHARE_DELIVERY_AUTHORIZATION_DOMAIN}${canonicalizeSignedObjectUnsigned(authorization)}`);
6279
- if (signatureBytes.length !== 64 || !ed255196.verify(signatureBytes, signedBytes, publicKey)) throw new Error("share delivery authorization proof signature is invalid");
6676
+ if (signatureBytes.length !== 64 || !ed255197.verify(signatureBytes, signedBytes, publicKey)) throw new Error("share delivery authorization proof signature is invalid");
6280
6677
  return { authorization, proof };
6281
6678
  }
6282
6679
  function shareDeliveryTrustedKid(publicKey) {
@@ -6466,11 +6863,13 @@ export {
6466
6863
  createSharePolicy,
6467
6864
  createSharingService,
6468
6865
  decryptShareBytesAsync,
6866
+ delegatePortablePolicySession,
6469
6867
  encodeInlineShareUrl,
6470
6868
  encodeShareArtifact,
6471
6869
  encodeShareUrl,
6472
6870
  encryptShareBytes,
6473
6871
  encryptShareBytesAsync,
6872
+ importPortableDelegation,
6474
6873
  inspectShare,
6475
6874
  intersectShareCapabilities,
6476
6875
  isLegacyShareLink,
@@ -6481,6 +6880,7 @@ export {
6481
6880
  normalizeShareRecipientTarget,
6482
6881
  normalizeShareResource,
6483
6882
  notifyShare,
6883
+ parsePortableDelegation,
6484
6884
  parseShareArtifact,
6485
6885
  parseShareUrl,
6486
6886
  publishPolicyShare,
@@ -6490,6 +6890,7 @@ export {
6490
6890
  restoreDelegatedShareKey,
6491
6891
  resumeShareAuthorization,
6492
6892
  revokeShare,
6893
+ serializePortableDelegation,
6493
6894
  shareCapabilityAllows,
6494
6895
  shareDeliveryTrustedKid,
6495
6896
  showShare,