@tinycloud/node-sdk 3.0.0-beta.2 → 3.0.0-beta.20

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
@@ -17209,11 +17209,13 @@ import {
17209
17209
  canonicalizeAddress as canonicalizeAddress2,
17210
17210
  pkhDid as pkhDid2,
17211
17211
  resolveTinyCloudHosts as resolveTinyCloudHosts2,
17212
+ publishLocationRecord,
17212
17213
  principalDidEquals as principalDidEquals2,
17213
17214
  parseNetworkId as parseNetworkId2,
17214
17215
  resolveSecretPath as resolveSecretPath2,
17215
- validateOwnerSharePolicyRegistrationBytes,
17216
- validateShareDeliveryAuthorizationBytes,
17216
+ registerPolicyV3,
17217
+ CREDENTIAL_INVITATION_REQUEST_DOMAIN,
17218
+ validateShareDeliveryAuthorizationV3Bytes,
17217
17219
  verifyEip191MessageSignature,
17218
17220
  signCompactUcanRootAuthorization
17219
17221
  } from "@tinycloud/sdk-core";
@@ -19094,6 +19096,11 @@ var NodeSecretsService = class {
19094
19096
  if (!permission.ok) return permission;
19095
19097
  return options === void 0 ? this.service.list() : this.service.list(options);
19096
19098
  }
19099
+ async listAll() {
19100
+ const permission = await this.ensurePermission("", void 0, "list");
19101
+ if (!permission.ok) return permission;
19102
+ return this.service.listAll();
19103
+ }
19097
19104
  get service() {
19098
19105
  return this.config.getService();
19099
19106
  }
@@ -19155,7 +19162,11 @@ var NodeSecretsService = class {
19155
19162
  (entry) => manifests.some((candidate) => {
19156
19163
  const resolved = resolveManifest(candidate);
19157
19164
  return resolved.resources.some(
19158
- (resource) => resource.service === entry.service && spaceMatches(resource.space, entry.space, this.config.resolveSpace) && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
19165
+ (resource) => resource.service === entry.service && spaceMatches(
19166
+ resource.space,
19167
+ entry.space,
19168
+ this.config.resolveSpace
19169
+ ) && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
19159
19170
  );
19160
19171
  })
19161
19172
  );
@@ -19288,6 +19299,22 @@ function cloneRecapCaveats(caveats) {
19288
19299
  throw new Error("Verified persisted session ReCap contains invalid caveats.");
19289
19300
  }
19290
19301
  }
19302
+ function jsonFromVerifier(value) {
19303
+ if (value === void 0) return null;
19304
+ if (Array.isArray(value)) return value.map(jsonFromVerifier);
19305
+ if (value instanceof Map) {
19306
+ const entries = [...value.entries()];
19307
+ if (!entries.every(([key2]) => typeof key2 === "string")) return value;
19308
+ return Object.fromEntries(entries.map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
19309
+ }
19310
+ if (value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
19311
+ return Object.fromEntries(Object.entries(value).map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
19312
+ }
19313
+ return value;
19314
+ }
19315
+ function verifierRecapEntries(entries) {
19316
+ return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
19317
+ }
19291
19318
  var ServiceGraphLifetime = class {
19292
19319
  constructor(invokeFn, invokeAnyFn, fetchFn) {
19293
19320
  this.invokeFn = invokeFn;
@@ -19360,18 +19387,23 @@ var UnsupportedSessionRestoreError = class extends Error {
19360
19387
  this.name = "UnsupportedSessionRestoreError";
19361
19388
  }
19362
19389
  };
19390
+ var InvalidRestoredSessionError = class extends Error {
19391
+ constructor(message = "Persisted session authority is invalid or expired.") {
19392
+ super(message);
19393
+ this.code = "AUTH_EXPIRED";
19394
+ this.name = "InvalidRestoredSessionError";
19395
+ }
19396
+ };
19363
19397
  function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
19364
19398
  if (typeof persistedVerificationMethod !== "string") return void 0;
19365
19399
  const principal = canonicalVerificationMethod.split("#", 1)[0];
19366
19400
  return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
19367
19401
  }
19368
19402
  function persistedExpiry(value) {
19369
- if (typeof value !== "string") {
19370
- throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
19371
- }
19403
+ if (typeof value !== "string") throw new InvalidRestoredSessionError("Persisted session must include expiresAt.");
19372
19404
  const expiry = new Date(value);
19373
19405
  if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
19374
- throw new Error("Persisted session expiry is invalid or expired.");
19406
+ throw new InvalidRestoredSessionError();
19375
19407
  }
19376
19408
  return expiry;
19377
19409
  }
@@ -19850,6 +19882,29 @@ var _TinyCloudNode = class _TinyCloudNode {
19850
19882
  const authHosts = this.auth?.hosts ?? [];
19851
19883
  return authHosts.length > 0 ? authHosts : [this.config.host];
19852
19884
  }
19885
+ /** The registry-resolved Node endpoint and the identity returned by that exact Node. */
19886
+ async activeNodeIdentity() {
19887
+ const host = this.hosts[0];
19888
+ if (host === void 0) throw new Error("TinyCloud node host has not been resolved");
19889
+ const parsed = new URL(host);
19890
+ if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost")) || parsed.origin !== host) {
19891
+ throw new Error("TinyCloud node host is not a canonical origin");
19892
+ }
19893
+ const nodeDid = await this.fetchNodeId();
19894
+ if (!nodeDid.startsWith("did:key:z")) throw new Error("TinyCloud node identity is not a did:key");
19895
+ return Object.freeze({ origin: parsed.origin, nodeDid });
19896
+ }
19897
+ /** Publish the active owner Node as a session-signed registry record. */
19898
+ async publishActiveNodeLocation(registryUrl, fetchFn = globalThis.fetch.bind(globalThis)) {
19899
+ const node = await this.activeNodeIdentity();
19900
+ return publishLocationRecord({
19901
+ registryUrl,
19902
+ subject: this.credentialHolderDid,
19903
+ nodeOrigin: node.origin,
19904
+ signer: { type: "did:key", signBytes: (bytes) => this.signSessionBytes(bytes) },
19905
+ fetch: fetchFn
19906
+ });
19907
+ }
19853
19908
  /**
19854
19909
  * Get the primary identity DID for this user.
19855
19910
  * - If wallet connected and signed in: returns PKH DID (did:pkh:eip155:{chainId}:{address})
@@ -19891,16 +19946,26 @@ var _TinyCloudNode = class _TinyCloudNode {
19891
19946
  */
19892
19947
  getVerifiedSessionCapabilities() {
19893
19948
  const session = this.currentTinyCloudSession();
19894
- if (!session || !session.siwe) return [];
19895
- return parseRecapCapabilities(
19896
- (siwe) => this.parseRecapWithCaveats(siwe),
19897
- session.siwe
19898
- ).map((entry) => entry.service === "tinycloud.encryption" ? {
19899
- service: entry.service,
19900
- path: entry.path,
19901
- actions: [...entry.actions],
19902
- ...entry.caveats === void 0 ? {} : { caveats: entry.caveats }
19903
- } : entry);
19949
+ return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
19950
+ }
19951
+ /** Normalize signed capabilities without collapsing nested encryption into raw authority. */
19952
+ projectSignedRecapCapabilities(siwe) {
19953
+ const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
19954
+ const normalized = parseRecapCapabilities(() => [entry], siwe)[0];
19955
+ if (normalized.service !== "tinycloud.encryption") return normalized;
19956
+ if (!this.isEncryptionNetworkOperation("encryption", entry.path, entry.space)) {
19957
+ return { ...normalized, space: entry.space };
19958
+ }
19959
+ return {
19960
+ service: normalized.service,
19961
+ path: normalized.path,
19962
+ actions: normalized.actions,
19963
+ ...normalized.caveats === void 0 ? {} : { caveats: normalized.caveats }
19964
+ };
19965
+ });
19966
+ return projected.sort(
19967
+ (left, right) => (left.space ?? "").localeCompare(right.space ?? "") || left.service.localeCompare(right.service) || left.path.localeCompare(right.path)
19968
+ );
19904
19969
  }
19905
19970
  /**
19906
19971
  * Get the Ethereum address for this user.
@@ -20355,9 +20420,9 @@ var _TinyCloudNode = class _TinyCloudNode {
20355
20420
  * We must keep the full owner-scoped URI so a synthetic primary grant can
20356
20421
  * never cover an operation on a different owner's space.
20357
20422
  *
20358
- * Mirrors {@link operationsFromDelegation}'s op shape: encryption network
20359
- * entries (`urn:tinycloud:encryption:` paths) become `resource` ops; every
20360
- * other entry becomes a `spaceId` op carrying the raw recap `space` URI.
20423
+ * Only top-level encryption network entries (`space: "encryption"` or
20424
+ * absent) become `resource` ops. A network-shaped path nested under a
20425
+ * data space remains a `spaceId` op, not raw network authority.
20361
20426
  * One op per action.
20362
20427
  *
20363
20428
  * Returns `[]` for session-only / restored-without-siwe modes and for any
@@ -20378,7 +20443,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20378
20443
  operations = entries.flatMap((entry) => {
20379
20444
  const service = this.invocationServiceName(entry.service);
20380
20445
  return entry.actions.map((action) => ({
20381
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
20446
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
20382
20447
  service,
20383
20448
  path: entry.path,
20384
20449
  action,
@@ -20540,7 +20605,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20540
20605
  return;
20541
20606
  } catch (error) {
20542
20607
  const message = error instanceof Error ? error.message : String(error);
20543
- if (/Unauthorized Action|\b401\b/.test(message)) {
20608
+ if (/Unauthorized Action|\b(?:401|403)\b/.test(message)) {
20544
20609
  console.warn(
20545
20610
  "TinyCloud account registry sync stopped: authorization verdict is not retryable",
20546
20611
  error
@@ -20790,9 +20855,14 @@ var _TinyCloudNode = class _TinyCloudNode {
20790
20855
  */
20791
20856
  async restoreSession(sessionData) {
20792
20857
  await this.wasmBindings.ensureInitialized?.();
20793
- const restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
20858
+ let restoredJwk;
20859
+ try {
20860
+ restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
20861
+ } catch {
20862
+ throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
20863
+ }
20794
20864
  if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
20795
- throw new Error("Persisted session chain ID must be a positive safe integer.");
20865
+ throw new InvalidRestoredSessionError("Persisted session chain ID must be a positive safe integer.");
20796
20866
  }
20797
20867
  const stagedManager = this.wasmBindings.createSessionManager();
20798
20868
  const stagedReplace = stagedManager.replaceSessionKey;
@@ -20804,32 +20874,31 @@ var _TinyCloudNode = class _TinyCloudNode {
20804
20874
  let stagedKeyId;
20805
20875
  let stagedJwk;
20806
20876
  let canonicalVerificationMethod;
20877
+ const keyIds = liveKeys.call(this.sessionManager);
20878
+ if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
20879
+ throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
20880
+ }
20881
+ for (const keyId of keyIds) {
20882
+ if (keyId === this.sessionKeyId) continue;
20883
+ const jwk = this.sessionManager.jwk(keyId);
20884
+ if (!jwk) throw new Error("missing live session key");
20885
+ stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
20886
+ }
20807
20887
  try {
20808
- const keyIds = liveKeys.call(this.sessionManager);
20809
- if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
20810
- throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
20811
- }
20812
- for (const keyId of keyIds) {
20813
- if (keyId === this.sessionKeyId) continue;
20814
- const jwk = this.sessionManager.jwk(keyId);
20815
- if (!jwk) throw new Error("missing live session key");
20816
- stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
20817
- }
20818
20888
  stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
20819
- const stagedJwkJson = stagedManager.jwk(stagedKeyId);
20820
- if (!stagedJwkJson) throw new Error("missing restored session key");
20821
- stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
20822
- canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
20823
- } catch (error) {
20824
- if (error instanceof UnsupportedSessionRestoreError) throw error;
20825
- throw new Error("Persisted session has an invalid private Ed25519 session key.");
20889
+ } catch {
20890
+ throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
20826
20891
  }
20892
+ const stagedJwkJson = stagedManager.jwk(stagedKeyId);
20893
+ if (!stagedJwkJson) throw new Error("missing restored session key");
20894
+ stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
20895
+ canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
20827
20896
  const restoredVerificationMethod = canonicalRestoredVerificationMethod(
20828
20897
  canonicalVerificationMethod,
20829
20898
  sessionData.verificationMethod
20830
20899
  );
20831
20900
  if (!restoredVerificationMethod) {
20832
- throw new Error(
20901
+ throw new InvalidRestoredSessionError(
20833
20902
  "Persisted session verification method does not match its private Ed25519 session key."
20834
20903
  );
20835
20904
  }
@@ -20841,26 +20910,37 @@ var _TinyCloudNode = class _TinyCloudNode {
20841
20910
  ];
20842
20911
  const hasPersistedProof = proofValues.every((value) => value !== void 0);
20843
20912
  if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
20844
- throw new Error("Persisted session authority metadata is incomplete.");
20913
+ throw new InvalidRestoredSessionError("Persisted session authority metadata is incomplete.");
20914
+ }
20915
+ let restoredAddress;
20916
+ try {
20917
+ restoredAddress = hasPersistedProof ? canonicalizeAddress2(sessionData.address) : void 0;
20918
+ } catch {
20919
+ throw new InvalidRestoredSessionError("Persisted session has an invalid wallet address.");
20845
20920
  }
20846
- const restoredAddress = hasPersistedProof ? canonicalizeAddress2(sessionData.address) : void 0;
20847
20921
  let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
20848
20922
  let stagedRecap = [];
20849
20923
  if (hasPersistedProof) {
20850
20924
  if (typeof this.wasmBindings.validatePersistedSession !== "function") {
20851
20925
  throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
20852
20926
  }
20853
- const verified = this.wasmBindings.validatePersistedSession({
20854
- delegationHeader: sessionData.delegationHeader,
20855
- delegationCid: sessionData.delegationCid,
20856
- spaceId: sessionData.spaceId,
20857
- jwk: stagedJwk,
20858
- address: restoredAddress,
20859
- chainId: sessionData.chainId,
20860
- siwe: sessionData.siwe,
20861
- signature: sessionData.signature
20862
- });
20863
- const exactRecap = verified.verifiedRecap;
20927
+ const verified = (() => {
20928
+ try {
20929
+ return this.wasmBindings.validatePersistedSession({
20930
+ delegationHeader: sessionData.delegationHeader,
20931
+ delegationCid: sessionData.delegationCid,
20932
+ spaceId: sessionData.spaceId,
20933
+ jwk: stagedJwk,
20934
+ address: restoredAddress,
20935
+ chainId: sessionData.chainId,
20936
+ siwe: sessionData.siwe,
20937
+ signature: sessionData.signature
20938
+ });
20939
+ } catch (error) {
20940
+ throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
20941
+ }
20942
+ })();
20943
+ const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
20864
20944
  if (!Array.isArray(exactRecap) || !exactRecap.every(
20865
20945
  (entry) => entry !== null && typeof entry === "object" && typeof entry.service === "string" && typeof entry.space === "string" && typeof entry.path === "string" && Array.isArray(entry.actions) && entry.actions.every((action) => typeof action === "string") && Array.isArray(entry.caveats) && entry.caveats.every(
20866
20946
  (caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
@@ -20880,7 +20960,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20880
20960
  const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
20881
20961
  const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
20882
20962
  if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
20883
- throw new Error("Persisted session expiry does not match its signed SIWE authority.");
20963
+ throw new InvalidRestoredSessionError("Persisted session expiry does not match its signed SIWE authority.");
20884
20964
  }
20885
20965
  stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
20886
20966
  }
@@ -20977,7 +21057,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20977
21057
  operations = recap.flatMap((entry) => {
20978
21058
  const service = this.invocationServiceName(entry.service);
20979
21059
  return entry.actions.map((action) => ({
20980
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
21060
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
20981
21061
  service,
20982
21062
  path: entry.path,
20983
21063
  action,
@@ -21073,7 +21153,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21073
21153
  const delegations = input.recap.map((entry) => ({
21074
21154
  cid: session.delegationCid,
21075
21155
  delegateDID: session.verificationMethod,
21076
- spaceId: entry.space,
21156
+ spaceId: this.spaceIdsEqual(entry.space, session.spaceId) ? session.spaceId : entry.space,
21077
21157
  path: entry.path,
21078
21158
  actions: [...entry.actions],
21079
21159
  caveats: cloneRecapCaveats(entry.caveats),
@@ -21763,16 +21843,11 @@ var _TinyCloudNode = class _TinyCloudNode {
21763
21843
  );
21764
21844
  graph.assertActive();
21765
21845
  if (!response.ok) {
21766
- let permissionHint;
21767
- if (response.status === 401 || response.status === 403) {
21768
- try {
21769
- const body = await response.json();
21770
- const record = typeof body === "object" && body !== null ? body : void 0;
21771
- const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
21772
- permissionHint = parsePermissionHint(record?.permissionHint) ?? parsePermissionHint(nested?.permissionHint);
21773
- } catch {
21774
- }
21775
- }
21846
+ const permissionHint = response.status === 401 || response.status === 403 ? parsePermissionHint({
21847
+ service: "tinycloud.encryption",
21848
+ path: networkId,
21849
+ actions: ["tinycloud.encryption/decrypt"]
21850
+ }) : void 0;
21776
21851
  throw new DecryptTransportResponseError(response.status, permissionHint);
21777
21852
  }
21778
21853
  return await response.json();
@@ -22197,63 +22272,62 @@ var _TinyCloudNode = class _TinyCloudNode {
22197
22272
  }
22198
22273
  };
22199
22274
  }
22200
- /** Register a policy that is already bound to an activated owner delegation. */
22201
- async registerOwnerSharePolicy(params) {
22275
+ /** Register a signed policy and sibling roots with this Node's Policy/v3 runtime. */
22276
+ async registerPolicy(params) {
22202
22277
  this._serviceGraph.assertActive();
22203
- if (params.policy.bytes.byteLength > 2 * 1024 * 1024) throw new Error("Owner share policy is too large");
22204
- const response = await fetch(`${this.config.host}/share/v2/policies`, {
22205
- method: "POST",
22206
- headers: { "content-type": "application/json", accept: "application/json" },
22207
- body: JSON.stringify({
22208
- policy: { bytes: base64UrlEncode(params.policy.bytes), cid: params.policy.cid, proof: params.policy.proof },
22209
- ownerDelegation: { cid: params.ownerDelegation.delegationCid, dagCbor: base64UrlEncode(params.ownerDelegation.signedDagCbor) },
22210
- enforcementDelegation: params.enforcementDelegation,
22211
- contentSourceDigest: params.contentSourceDigest
22212
- })
22278
+ return registerPolicyV3({
22279
+ ...params,
22280
+ nodeOrigin: this.config.host,
22281
+ fetch: globalThis.fetch.bind(globalThis)
22213
22282
  });
22214
- if (!response.ok) {
22215
- let code = "";
22216
- try {
22217
- const body = await response.clone().json();
22218
- if (typeof body.error?.code === "string") code = ` ${body.error.code}`;
22219
- } catch {
22220
- }
22221
- throw new Error(`Owner share policy registration failed: ${response.status}${code}`);
22222
- }
22223
- const responseBytes = new Uint8Array(await response.arrayBuffer());
22224
- return validateOwnerSharePolicyRegistrationBytes(responseBytes, params);
22225
22283
  }
22226
- /** Authorize a short-lived, one-use v2 delivery using the authenticated invocation chain. */
22227
- async authorizeShareDelivery(input) {
22284
+ /** Authorize one short-lived v3 delivery against the signed v3 envelope and registered roots. */
22285
+ async authorizeShareDeliveryV3(input) {
22228
22286
  const session = this.currentTinyCloudSession();
22229
22287
  const serviceSession = this._serviceContext?.session;
22230
22288
  if (!session || !serviceSession) throw new Error("Share delivery requires an authenticated session");
22289
+ const idempotencyBytes = new TextEncoder().encode(input.idempotencyKey);
22290
+ if (idempotencyBytes.byteLength === 0 || idempotencyBytes.byteLength > 512) {
22291
+ throw new Error("Share delivery idempotency key is invalid");
22292
+ }
22293
+ const jtiDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", idempotencyBytes));
22231
22294
  const body = {
22232
- envelopeCid: input.envelopeCid,
22295
+ envelope: input.envelope,
22296
+ sealedEnvelope: input.sealedEnvelope,
22297
+ envelopeKey: input.envelopeKey,
22233
22298
  shareCid: input.shareCid,
22234
- shareId: input.shareId,
22235
- registrationCid: input.registrationCid,
22236
- policyCid: input.policyCid,
22237
- delegationCid: input.delegationCid,
22238
- enforcementDelegationCid: input.enforcementDelegationCid,
22239
22299
  recipientEmail: input.recipientEmail,
22240
22300
  shareUrl: input.shareUrl,
22241
22301
  documentName: input.documentName,
22242
- jti: base64UrlEncode(crypto.getRandomValues(new Uint8Array(16))),
22243
- idempotencyKey: input.idempotencyKey,
22244
- expiresAt: input.expiresAt
22302
+ jti: base64UrlEncode(jtiDigest.slice(0, 16)),
22303
+ expiresAt: new Date(input.expiresAt).toISOString().replace(/\.\d{3}Z$/, "Z")
22245
22304
  };
22246
22305
  const requestBodyDigest = base64UrlEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalizeEncryptionJson(body)))));
22247
22306
  const request = { ...body, requestBodyDigest };
22248
22307
  const authorization = authorizationHeader(this.invokeAnyWithRuntimePermissions(serviceSession, [{ spaceId: session.spaceId, service: "kv", path: input.resourcePath, action: "tinycloud.kv/get" }]));
22249
- const response = await fetch(`${this.config.host}/share/v2/deliveries/authorize`, {
22308
+ const response = await fetch(`${this.config.host}/policy/v3/deliveries/authorize`, {
22250
22309
  method: "POST",
22251
22310
  headers: { accept: "application/json", "content-type": "application/json", authorization },
22252
22311
  body: canonicalizeEncryptionJson(request)
22253
22312
  });
22254
- if (!response.ok) throw new Error(`Share delivery authorization failed: ${response.status}`);
22255
- const responseBytes = new Uint8Array(await response.arrayBuffer());
22256
- return validateShareDeliveryAuthorizationBytes(responseBytes, { request, nodeProof: input.nodeProof, credentialsAudience: input.credentialsAudience });
22313
+ if (!response.ok) throw new Error(`V3 share delivery authorization failed: ${response.status}`);
22314
+ const verified = validateShareDeliveryAuthorizationV3Bytes(new Uint8Array(await response.arrayBuffer()), {
22315
+ request,
22316
+ senderKeyDid: this.credentialHolderDid,
22317
+ deliveryAudience: input.deliveryAudience
22318
+ });
22319
+ const requestDigest = new Uint8Array(await crypto.subtle.digest(
22320
+ "SHA-256",
22321
+ new TextEncoder().encode(`${CREDENTIAL_INVITATION_REQUEST_DOMAIN}${canonicalizeEncryptionJson(verified.request)}`)
22322
+ ));
22323
+ return {
22324
+ ...verified,
22325
+ proof: {
22326
+ alg: "EdDSA",
22327
+ kid: this.credentialHolderDid,
22328
+ signature: base64UrlEncode(await this.signSessionBytes(requestDigest))
22329
+ }
22330
+ };
22257
22331
  }
22258
22332
  async createRootDelegationForSharing(params, assertActive) {
22259
22333
  try {
@@ -22708,7 +22782,7 @@ var _TinyCloudNode = class _TinyCloudNode {
22708
22782
  continue;
22709
22783
  }
22710
22784
  let space;
22711
- if (service !== "tinycloud.encryption") {
22785
+ if (service !== "tinycloud.encryption" || operation.resource === void 0) {
22712
22786
  if (typeof operation.spaceId !== "string") continue;
22713
22787
  try {
22714
22788
  space = this.resolvePermissionSpace(operation.spaceId, session);
@@ -23253,11 +23327,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23253
23327
  );
23254
23328
  return { delegation: delegation2, prompted: true };
23255
23329
  }
23256
- const granted = parseRecapCapabilities(
23257
- (siwe) => this.parseRecapWithCaveats(siwe),
23258
- session.siwe
23259
- );
23260
- const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
23330
+ const granted = this.projectSignedRecapCapabilities(session.siwe);
23331
+ const { subset, missing } = this.signedCapabilitySubset(expandedEntries, granted);
23261
23332
  if (!subset) {
23262
23333
  const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
23263
23334
  const runtimeGrant = this.findGrantForOperations(
@@ -23577,7 +23648,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23577
23648
  return abilities;
23578
23649
  }
23579
23650
  isEncryptionPermissionEntry(entry) {
23580
- return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && entry.path.startsWith("urn:tinycloud:encryption:");
23651
+ return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
23581
23652
  }
23582
23653
  permissionsToRawAbilities(entries) {
23583
23654
  const rawAbilities = {};
@@ -23601,7 +23672,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23601
23672
  return entries.flatMap((entry) => {
23602
23673
  const service = this.shortServiceName(entry.service);
23603
23674
  return entry.actions.map((action) => ({
23604
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
23675
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23605
23676
  service,
23606
23677
  path: entry.path,
23607
23678
  action,
@@ -23609,13 +23680,21 @@ var _TinyCloudNode = class _TinyCloudNode {
23609
23680
  }));
23610
23681
  });
23611
23682
  }
23683
+ /** Raw network authority cannot be covered by a nested encryption space. */
23684
+ signedCapabilitySubset(requested, granted) {
23685
+ const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
23686
+ const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
23687
+ const missing = requested.filter((entry) => {
23688
+ const raw = this.isEncryptionPermissionEntry(entry);
23689
+ const comparable = raw ? { ...entry, space: "encryption" } : entry;
23690
+ return !isCapabilitySubset([comparable], raw ? rawGranted : spaceGranted).subset;
23691
+ });
23692
+ return { subset: missing.length === 0, missing };
23693
+ }
23612
23694
  sessionCoversPermissionEntries(session, entries) {
23613
23695
  try {
23614
- const granted = parseRecapCapabilities(
23615
- (siwe) => this.parseRecapWithCaveats(siwe),
23616
- session.siwe
23617
- );
23618
- return isCapabilitySubset(entries, granted).subset;
23696
+ const granted = this.projectSignedRecapCapabilities(session.siwe);
23697
+ return this.signedCapabilitySubset(entries, granted).subset;
23619
23698
  } catch {
23620
23699
  return false;
23621
23700
  }
@@ -23625,7 +23704,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23625
23704
  const spaceId = this.resolvePermissionSpace(entry.space, session);
23626
23705
  const service = this.shortServiceName(entry.service);
23627
23706
  return entry.actions.map((action) => ({
23628
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
23707
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23629
23708
  service,
23630
23709
  path: entry.path,
23631
23710
  action,
@@ -23714,7 +23793,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23714
23793
  return resources.flatMap((resource) => {
23715
23794
  const service = this.invocationServiceName(resource.service);
23716
23795
  return resource.actions.map((action) => ({
23717
- ...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
23796
+ ...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
23718
23797
  service,
23719
23798
  path: resource.path,
23720
23799
  action,
@@ -23775,7 +23854,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23775
23854
  };
23776
23855
  for (const resource of resources) {
23777
23856
  const service = this.invocationServiceName(resource.service);
23778
- if (this.isEncryptionNetworkOperation(service, resource.path)) {
23857
+ if (this.isEncryptionNetworkOperation(service, resource.path, resource.space)) {
23779
23858
  rawAbilities[_a = resource.path] ?? (rawAbilities[_a] = []);
23780
23859
  addActions(rawAbilities[resource.path], resource.actions);
23781
23860
  continue;
@@ -23844,21 +23923,15 @@ var _TinyCloudNode = class _TinyCloudNode {
23844
23923
  }
23845
23924
  return granted.spaceId !== void 0 && requested.spaceId !== void 0 && this.spaceIdsEqual(granted.spaceId, requested.spaceId) && this.pathContains(granted.path, requested.path);
23846
23925
  }
23847
- // Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. The embedded
23848
- // EIP-155 address is case-insensitive, but the CLI canonicalizes it to
23849
- // lowercase when building a space URI while stored runtime delegations keep
23850
- // the EIP-55 checksummed form — so a byte-for-byte compare spuriously rejects
23851
- // an otherwise-valid grant. Lowercase ONLY the `eip155:<chain>:0x<addr>`
23852
- // segment and leave everything else (crucially the case-sensitive space NAME)
23853
- // byte-exact. Mirrors the CLI's `normalizeSpaceForCompare` (OPENKEY_SCOPE_MISMATCH fix).
23926
+ // Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. Compare
23927
+ // only the embedded EIP-155 address without case sensitivity; chain, name,
23928
+ // and all non-PKH space identifiers remain byte-exact.
23854
23929
  spaceIdsEqual(a, b) {
23855
23930
  return this.normalizeSpaceAddress(a) === this.normalizeSpaceAddress(b);
23856
23931
  }
23857
23932
  normalizeSpaceAddress(space) {
23858
- return space.replace(
23859
- /(eip155:\d+:)(0x[0-9a-fA-F]{40})/,
23860
- (_match, prefix, addr) => prefix + addr.toLowerCase()
23861
- );
23933
+ const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
23934
+ return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
23862
23935
  }
23863
23936
  actionContains(grantedAction, requestedAction) {
23864
23937
  if (grantedAction === requestedAction) {
@@ -23875,10 +23948,11 @@ var _TinyCloudNode = class _TinyCloudNode {
23875
23948
  }
23876
23949
  /** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
23877
23950
  parseRecapWithCaveats(siwe) {
23878
- return this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe) ?? this.wasmBindings.parseRecapFromSiwe(siwe);
23951
+ const verified = this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe);
23952
+ return verified == null ? this.wasmBindings.parseRecapFromSiwe(siwe) : verifierRecapEntries(verified);
23879
23953
  }
23880
- isEncryptionNetworkOperation(service, path) {
23881
- return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
23954
+ isEncryptionNetworkOperation(service, path, space) {
23955
+ return service === "encryption" && (space === void 0 || space === "encryption") && path.startsWith("urn:tinycloud:encryption:");
23882
23956
  }
23883
23957
  operationFromInvokeAnyEntry(entry) {
23884
23958
  const service = this.invocationServiceName(entry.service);
@@ -23891,7 +23965,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23891
23965
  ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
23892
23966
  };
23893
23967
  }
23894
- if (this.isEncryptionNetworkOperation(service, entry.path)) {
23968
+ if (entry.spaceId === void 0 && this.isEncryptionNetworkOperation(service, entry.path)) {
23895
23969
  return {
23896
23970
  resource: entry.path,
23897
23971
  service,
@@ -24176,8 +24250,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24176
24250
  const jwk = mySession.jwk;
24177
24251
  const { abilities, rawAbilities } = this.buildActivationAbilities(delegation);
24178
24252
  const now = /* @__PURE__ */ new Date();
24179
- const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
24180
- const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
24253
+ const expirationTime = delegation.expiry;
24181
24254
  const prepared = this.wasmBindings.prepareSession({
24182
24255
  abilities,
24183
24256
  address: this.wasmBindings.ensureEip55(mySession.address),
@@ -24281,8 +24354,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24281
24354
  );
24282
24355
  }
24283
24356
  const now = /* @__PURE__ */ new Date();
24284
- const expiryMs = params.expiryMs ?? 60 * 60 * 1e3;
24285
- const requestedExpiry = new Date(now.getTime() + expiryMs);
24357
+ const requestedExpiry = params.expiryMs === void 0 ? parentDelegation.expiry : new Date(now.getTime() + params.expiryMs);
24286
24358
  const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
24287
24359
  const abilities = {};
24288
24360
  const rawAbilities = {};
@@ -24976,6 +25048,7 @@ import {
24976
25048
  SecretsService as SecretsService2,
24977
25049
  SECRET_NAME_RE,
24978
25050
  canonicalizeSecretScope,
25051
+ parseSecretCatalogKey,
24979
25052
  resolveSecretListPrefix as resolveSecretListPrefix2,
24980
25053
  resolveSecretPath as resolveSecretPath3
24981
25054
  } from "@tinycloud/sdk-core";
@@ -25086,6 +25159,7 @@ export {
25086
25159
  FileSessionStorage,
25087
25160
  HooksService3 as HooksService,
25088
25161
  IdentityParseError,
25162
+ InvalidRestoredSessionError,
25089
25163
  KVService3 as KVService,
25090
25164
  ManifestValidationError,
25091
25165
  MemorySessionStorage,
@@ -25165,6 +25239,7 @@ export {
25165
25239
  expandActionShortNames,
25166
25240
  expandPermissionEntries2 as expandPermissionEntries,
25167
25241
  expandPermissionEntry,
25242
+ extractSiweExpiration,
25168
25243
  generateRandomReceiverKey,
25169
25244
  getTinyCloudDebugLogs,
25170
25245
  grantAuthRequest,
@@ -25183,6 +25258,7 @@ export {
25183
25258
  parseExpiry2 as parseExpiry,
25184
25259
  parseNetworkId3 as parseNetworkId,
25185
25260
  parsePkhDid,
25261
+ parseSecretCatalogKey,
25186
25262
  parseSpaceUri,
25187
25263
  pkhDid3 as pkhDid,
25188
25264
  principalDid,