@tinycloud/node-sdk 3.0.0-beta.9 → 3.0.1-beta.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.
package/dist/index.cjs CHANGED
@@ -17060,6 +17060,7 @@ __export(index_exports, {
17060
17060
  FileSessionStorage: () => FileSessionStorage,
17061
17061
  HooksService: () => import_sdk_core18.HooksService,
17062
17062
  IdentityParseError: () => import_sdk_core11.IdentityParseError,
17063
+ InvalidRestoredSessionError: () => InvalidRestoredSessionError,
17063
17064
  KVService: () => import_sdk_core13.KVService,
17064
17065
  ManifestValidationError: () => import_sdk_core12.ManifestValidationError,
17065
17066
  MemorySessionStorage: () => MemorySessionStorage,
@@ -17139,6 +17140,7 @@ __export(index_exports, {
17139
17140
  expandActionShortNames: () => import_sdk_core12.expandActionShortNames,
17140
17141
  expandPermissionEntries: () => import_sdk_core12.expandPermissionEntries,
17141
17142
  expandPermissionEntry: () => import_sdk_core12.expandPermissionEntry,
17143
+ extractSiweExpiration: () => extractSiweExpiration,
17142
17144
  generateRandomReceiverKey: () => import_sdk_core17.generateRandomReceiverKey,
17143
17145
  getTinyCloudDebugLogs: () => import_sdk_core11.getTinyCloudDebugLogs,
17144
17146
  grantAuthRequest: () => grantAuthRequest,
@@ -19312,6 +19314,22 @@ function cloneRecapCaveats(caveats) {
19312
19314
  throw new Error("Verified persisted session ReCap contains invalid caveats.");
19313
19315
  }
19314
19316
  }
19317
+ function jsonFromVerifier(value) {
19318
+ if (value === void 0) return null;
19319
+ if (Array.isArray(value)) return value.map(jsonFromVerifier);
19320
+ if (value instanceof Map) {
19321
+ const entries = [...value.entries()];
19322
+ if (!entries.every(([key2]) => typeof key2 === "string")) return value;
19323
+ return Object.fromEntries(entries.map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
19324
+ }
19325
+ if (value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
19326
+ return Object.fromEntries(Object.entries(value).map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
19327
+ }
19328
+ return value;
19329
+ }
19330
+ function verifierRecapEntries(entries) {
19331
+ return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
19332
+ }
19315
19333
  var ServiceGraphLifetime = class {
19316
19334
  constructor(invokeFn, invokeAnyFn, fetchFn) {
19317
19335
  this.invokeFn = invokeFn;
@@ -19384,18 +19402,23 @@ var UnsupportedSessionRestoreError = class extends Error {
19384
19402
  this.name = "UnsupportedSessionRestoreError";
19385
19403
  }
19386
19404
  };
19405
+ var InvalidRestoredSessionError = class extends Error {
19406
+ constructor(message = "Persisted session authority is invalid or expired.") {
19407
+ super(message);
19408
+ this.code = "AUTH_EXPIRED";
19409
+ this.name = "InvalidRestoredSessionError";
19410
+ }
19411
+ };
19387
19412
  function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
19388
19413
  if (typeof persistedVerificationMethod !== "string") return void 0;
19389
19414
  const principal = canonicalVerificationMethod.split("#", 1)[0];
19390
19415
  return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
19391
19416
  }
19392
19417
  function persistedExpiry(value) {
19393
- if (typeof value !== "string") {
19394
- throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
19395
- }
19418
+ if (typeof value !== "string") throw new InvalidRestoredSessionError("Persisted session must include expiresAt.");
19396
19419
  const expiry = new Date(value);
19397
19420
  if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
19398
- throw new Error("Persisted session expiry is invalid or expired.");
19421
+ throw new InvalidRestoredSessionError();
19399
19422
  }
19400
19423
  return expiry;
19401
19424
  }
@@ -19938,16 +19961,26 @@ var _TinyCloudNode = class _TinyCloudNode {
19938
19961
  */
19939
19962
  getVerifiedSessionCapabilities() {
19940
19963
  const session = this.currentTinyCloudSession();
19941
- if (!session || !session.siwe) return [];
19942
- return (0, import_sdk_core7.parseRecapCapabilities)(
19943
- (siwe) => this.parseRecapWithCaveats(siwe),
19944
- session.siwe
19945
- ).map((entry) => entry.service === "tinycloud.encryption" ? {
19946
- service: entry.service,
19947
- path: entry.path,
19948
- actions: [...entry.actions],
19949
- ...entry.caveats === void 0 ? {} : { caveats: entry.caveats }
19950
- } : entry);
19964
+ return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
19965
+ }
19966
+ /** Normalize signed capabilities without collapsing nested encryption into raw authority. */
19967
+ projectSignedRecapCapabilities(siwe) {
19968
+ const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
19969
+ const normalized = (0, import_sdk_core7.parseRecapCapabilities)(() => [entry], siwe)[0];
19970
+ if (normalized.service !== "tinycloud.encryption") return normalized;
19971
+ if (!this.isEncryptionNetworkOperation("encryption", entry.path, entry.space)) {
19972
+ return { ...normalized, space: entry.space };
19973
+ }
19974
+ return {
19975
+ service: normalized.service,
19976
+ path: normalized.path,
19977
+ actions: normalized.actions,
19978
+ ...normalized.caveats === void 0 ? {} : { caveats: normalized.caveats }
19979
+ };
19980
+ });
19981
+ return projected.sort(
19982
+ (left, right) => (left.space ?? "").localeCompare(right.space ?? "") || left.service.localeCompare(right.service) || left.path.localeCompare(right.path)
19983
+ );
19951
19984
  }
19952
19985
  /**
19953
19986
  * Get the Ethereum address for this user.
@@ -20402,9 +20435,9 @@ var _TinyCloudNode = class _TinyCloudNode {
20402
20435
  * We must keep the full owner-scoped URI so a synthetic primary grant can
20403
20436
  * never cover an operation on a different owner's space.
20404
20437
  *
20405
- * Mirrors {@link operationsFromDelegation}'s op shape: encryption network
20406
- * entries (`urn:tinycloud:encryption:` paths) become `resource` ops; every
20407
- * other entry becomes a `spaceId` op carrying the raw recap `space` URI.
20438
+ * Only top-level encryption network entries (`space: "encryption"` or
20439
+ * absent) become `resource` ops. A network-shaped path nested under a
20440
+ * data space remains a `spaceId` op, not raw network authority.
20408
20441
  * One op per action.
20409
20442
  *
20410
20443
  * Returns `[]` for session-only / restored-without-siwe modes and for any
@@ -20425,7 +20458,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20425
20458
  operations = entries.flatMap((entry) => {
20426
20459
  const service = this.invocationServiceName(entry.service);
20427
20460
  return entry.actions.map((action) => ({
20428
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
20461
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
20429
20462
  service,
20430
20463
  path: entry.path,
20431
20464
  action,
@@ -20587,7 +20620,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20587
20620
  return;
20588
20621
  } catch (error) {
20589
20622
  const message = error instanceof Error ? error.message : String(error);
20590
- if (/Unauthorized Action|\b401\b/.test(message)) {
20623
+ if (/Unauthorized Action|\b(?:401|403)\b/.test(message)) {
20591
20624
  console.warn(
20592
20625
  "TinyCloud account registry sync stopped: authorization verdict is not retryable",
20593
20626
  error
@@ -20837,9 +20870,14 @@ var _TinyCloudNode = class _TinyCloudNode {
20837
20870
  */
20838
20871
  async restoreSession(sessionData) {
20839
20872
  await this.wasmBindings.ensureInitialized?.();
20840
- const restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
20873
+ let restoredJwk;
20874
+ try {
20875
+ restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
20876
+ } catch {
20877
+ throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
20878
+ }
20841
20879
  if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
20842
- throw new Error("Persisted session chain ID must be a positive safe integer.");
20880
+ throw new InvalidRestoredSessionError("Persisted session chain ID must be a positive safe integer.");
20843
20881
  }
20844
20882
  const stagedManager = this.wasmBindings.createSessionManager();
20845
20883
  const stagedReplace = stagedManager.replaceSessionKey;
@@ -20851,32 +20889,31 @@ var _TinyCloudNode = class _TinyCloudNode {
20851
20889
  let stagedKeyId;
20852
20890
  let stagedJwk;
20853
20891
  let canonicalVerificationMethod;
20892
+ const keyIds = liveKeys.call(this.sessionManager);
20893
+ if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
20894
+ throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
20895
+ }
20896
+ for (const keyId of keyIds) {
20897
+ if (keyId === this.sessionKeyId) continue;
20898
+ const jwk = this.sessionManager.jwk(keyId);
20899
+ if (!jwk) throw new Error("missing live session key");
20900
+ stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
20901
+ }
20854
20902
  try {
20855
- const keyIds = liveKeys.call(this.sessionManager);
20856
- if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
20857
- throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
20858
- }
20859
- for (const keyId of keyIds) {
20860
- if (keyId === this.sessionKeyId) continue;
20861
- const jwk = this.sessionManager.jwk(keyId);
20862
- if (!jwk) throw new Error("missing live session key");
20863
- stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
20864
- }
20865
20903
  stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
20866
- const stagedJwkJson = stagedManager.jwk(stagedKeyId);
20867
- if (!stagedJwkJson) throw new Error("missing restored session key");
20868
- stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
20869
- canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
20870
- } catch (error) {
20871
- if (error instanceof UnsupportedSessionRestoreError) throw error;
20872
- throw new Error("Persisted session has an invalid private Ed25519 session key.");
20904
+ } catch {
20905
+ throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
20873
20906
  }
20907
+ const stagedJwkJson = stagedManager.jwk(stagedKeyId);
20908
+ if (!stagedJwkJson) throw new Error("missing restored session key");
20909
+ stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
20910
+ canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
20874
20911
  const restoredVerificationMethod = canonicalRestoredVerificationMethod(
20875
20912
  canonicalVerificationMethod,
20876
20913
  sessionData.verificationMethod
20877
20914
  );
20878
20915
  if (!restoredVerificationMethod) {
20879
- throw new Error(
20916
+ throw new InvalidRestoredSessionError(
20880
20917
  "Persisted session verification method does not match its private Ed25519 session key."
20881
20918
  );
20882
20919
  }
@@ -20888,26 +20925,37 @@ var _TinyCloudNode = class _TinyCloudNode {
20888
20925
  ];
20889
20926
  const hasPersistedProof = proofValues.every((value) => value !== void 0);
20890
20927
  if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
20891
- throw new Error("Persisted session authority metadata is incomplete.");
20928
+ throw new InvalidRestoredSessionError("Persisted session authority metadata is incomplete.");
20929
+ }
20930
+ let restoredAddress;
20931
+ try {
20932
+ restoredAddress = hasPersistedProof ? (0, import_sdk_core7.canonicalizeAddress)(sessionData.address) : void 0;
20933
+ } catch {
20934
+ throw new InvalidRestoredSessionError("Persisted session has an invalid wallet address.");
20892
20935
  }
20893
- const restoredAddress = hasPersistedProof ? (0, import_sdk_core7.canonicalizeAddress)(sessionData.address) : void 0;
20894
20936
  let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
20895
20937
  let stagedRecap = [];
20896
20938
  if (hasPersistedProof) {
20897
20939
  if (typeof this.wasmBindings.validatePersistedSession !== "function") {
20898
20940
  throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
20899
20941
  }
20900
- const verified = this.wasmBindings.validatePersistedSession({
20901
- delegationHeader: sessionData.delegationHeader,
20902
- delegationCid: sessionData.delegationCid,
20903
- spaceId: sessionData.spaceId,
20904
- jwk: stagedJwk,
20905
- address: restoredAddress,
20906
- chainId: sessionData.chainId,
20907
- siwe: sessionData.siwe,
20908
- signature: sessionData.signature
20909
- });
20910
- const exactRecap = verified.verifiedRecap;
20942
+ const verified = (() => {
20943
+ try {
20944
+ return this.wasmBindings.validatePersistedSession({
20945
+ delegationHeader: sessionData.delegationHeader,
20946
+ delegationCid: sessionData.delegationCid,
20947
+ spaceId: sessionData.spaceId,
20948
+ jwk: stagedJwk,
20949
+ address: restoredAddress,
20950
+ chainId: sessionData.chainId,
20951
+ siwe: sessionData.siwe,
20952
+ signature: sessionData.signature
20953
+ });
20954
+ } catch (error) {
20955
+ throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
20956
+ }
20957
+ })();
20958
+ const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
20911
20959
  if (!Array.isArray(exactRecap) || !exactRecap.every(
20912
20960
  (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(
20913
20961
  (caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
@@ -20927,7 +20975,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20927
20975
  const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
20928
20976
  const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
20929
20977
  if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
20930
- throw new Error("Persisted session expiry does not match its signed SIWE authority.");
20978
+ throw new InvalidRestoredSessionError("Persisted session expiry does not match its signed SIWE authority.");
20931
20979
  }
20932
20980
  stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
20933
20981
  }
@@ -21024,7 +21072,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21024
21072
  operations = recap.flatMap((entry) => {
21025
21073
  const service = this.invocationServiceName(entry.service);
21026
21074
  return entry.actions.map((action) => ({
21027
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
21075
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
21028
21076
  service,
21029
21077
  path: entry.path,
21030
21078
  action,
@@ -21120,7 +21168,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21120
21168
  const delegations = input.recap.map((entry) => ({
21121
21169
  cid: session.delegationCid,
21122
21170
  delegateDID: session.verificationMethod,
21123
- spaceId: entry.space,
21171
+ spaceId: this.spaceIdsEqual(entry.space, session.spaceId) ? session.spaceId : entry.space,
21124
21172
  path: entry.path,
21125
21173
  actions: [...entry.actions],
21126
21174
  caveats: cloneRecapCaveats(entry.caveats),
@@ -21810,16 +21858,11 @@ var _TinyCloudNode = class _TinyCloudNode {
21810
21858
  );
21811
21859
  graph.assertActive();
21812
21860
  if (!response.ok) {
21813
- let permissionHint;
21814
- if (response.status === 401 || response.status === 403) {
21815
- try {
21816
- const body = await response.json();
21817
- const record = typeof body === "object" && body !== null ? body : void 0;
21818
- const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
21819
- permissionHint = (0, import_sdk_services.parsePermissionHint)(record?.permissionHint) ?? (0, import_sdk_services.parsePermissionHint)(nested?.permissionHint);
21820
- } catch {
21821
- }
21822
- }
21861
+ const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services.parsePermissionHint)({
21862
+ service: "tinycloud.encryption",
21863
+ path: networkId,
21864
+ actions: ["tinycloud.encryption/decrypt"]
21865
+ }) : void 0;
21823
21866
  throw new import_sdk_core7.DecryptTransportResponseError(response.status, permissionHint);
21824
21867
  }
21825
21868
  return await response.json();
@@ -22258,13 +22301,20 @@ var _TinyCloudNode = class _TinyCloudNode {
22258
22301
  const session = this.currentTinyCloudSession();
22259
22302
  const serviceSession = this._serviceContext?.session;
22260
22303
  if (!session || !serviceSession) throw new Error("Share delivery requires an authenticated session");
22304
+ const idempotencyBytes = new TextEncoder().encode(input.idempotencyKey);
22305
+ if (idempotencyBytes.byteLength === 0 || idempotencyBytes.byteLength > 512) {
22306
+ throw new Error("Share delivery idempotency key is invalid");
22307
+ }
22308
+ const jtiDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", idempotencyBytes));
22261
22309
  const body = {
22262
22310
  envelope: input.envelope,
22311
+ sealedEnvelope: input.sealedEnvelope,
22312
+ envelopeKey: input.envelopeKey,
22263
22313
  shareCid: input.shareCid,
22264
22314
  recipientEmail: input.recipientEmail,
22265
22315
  shareUrl: input.shareUrl,
22266
22316
  documentName: input.documentName,
22267
- jti: base64UrlEncode(crypto.getRandomValues(new Uint8Array(16))),
22317
+ jti: base64UrlEncode(jtiDigest.slice(0, 16)),
22268
22318
  expiresAt: new Date(input.expiresAt).toISOString().replace(/\.\d{3}Z$/, "Z")
22269
22319
  };
22270
22320
  const requestBodyDigest = base64UrlEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode((0, import_sdk_core7.canonicalizeEncryptionJson)(body)))));
@@ -22747,7 +22797,7 @@ var _TinyCloudNode = class _TinyCloudNode {
22747
22797
  continue;
22748
22798
  }
22749
22799
  let space;
22750
- if (service !== "tinycloud.encryption") {
22800
+ if (service !== "tinycloud.encryption" || operation.resource === void 0) {
22751
22801
  if (typeof operation.spaceId !== "string") continue;
22752
22802
  try {
22753
22803
  space = this.resolvePermissionSpace(operation.spaceId, session);
@@ -23292,11 +23342,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23292
23342
  );
23293
23343
  return { delegation: delegation2, prompted: true };
23294
23344
  }
23295
- const granted = (0, import_sdk_core7.parseRecapCapabilities)(
23296
- (siwe) => this.parseRecapWithCaveats(siwe),
23297
- session.siwe
23298
- );
23299
- const { subset, missing } = (0, import_sdk_core7.isCapabilitySubset)(expandedEntries, granted);
23345
+ const granted = this.projectSignedRecapCapabilities(session.siwe);
23346
+ const { subset, missing } = this.signedCapabilitySubset(expandedEntries, granted);
23300
23347
  if (!subset) {
23301
23348
  const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
23302
23349
  const runtimeGrant = this.findGrantForOperations(
@@ -23616,7 +23663,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23616
23663
  return abilities;
23617
23664
  }
23618
23665
  isEncryptionPermissionEntry(entry) {
23619
- return entry.service === import_sdk_core7.ENCRYPTION_PERMISSION_SERVICE && entry.path.startsWith("urn:tinycloud:encryption:");
23666
+ return entry.service === import_sdk_core7.ENCRYPTION_PERMISSION_SERVICE && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
23620
23667
  }
23621
23668
  permissionsToRawAbilities(entries) {
23622
23669
  const rawAbilities = {};
@@ -23640,7 +23687,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23640
23687
  return entries.flatMap((entry) => {
23641
23688
  const service = this.shortServiceName(entry.service);
23642
23689
  return entry.actions.map((action) => ({
23643
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
23690
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23644
23691
  service,
23645
23692
  path: entry.path,
23646
23693
  action,
@@ -23648,13 +23695,21 @@ var _TinyCloudNode = class _TinyCloudNode {
23648
23695
  }));
23649
23696
  });
23650
23697
  }
23698
+ /** Raw network authority cannot be covered by a nested encryption space. */
23699
+ signedCapabilitySubset(requested, granted) {
23700
+ const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
23701
+ const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
23702
+ const missing = requested.filter((entry) => {
23703
+ const raw = this.isEncryptionPermissionEntry(entry);
23704
+ const comparable = raw ? { ...entry, space: "encryption" } : entry;
23705
+ return !(0, import_sdk_core7.isCapabilitySubset)([comparable], raw ? rawGranted : spaceGranted).subset;
23706
+ });
23707
+ return { subset: missing.length === 0, missing };
23708
+ }
23651
23709
  sessionCoversPermissionEntries(session, entries) {
23652
23710
  try {
23653
- const granted = (0, import_sdk_core7.parseRecapCapabilities)(
23654
- (siwe) => this.parseRecapWithCaveats(siwe),
23655
- session.siwe
23656
- );
23657
- return (0, import_sdk_core7.isCapabilitySubset)(entries, granted).subset;
23711
+ const granted = this.projectSignedRecapCapabilities(session.siwe);
23712
+ return this.signedCapabilitySubset(entries, granted).subset;
23658
23713
  } catch {
23659
23714
  return false;
23660
23715
  }
@@ -23664,7 +23719,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23664
23719
  const spaceId = this.resolvePermissionSpace(entry.space, session);
23665
23720
  const service = this.shortServiceName(entry.service);
23666
23721
  return entry.actions.map((action) => ({
23667
- ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
23722
+ ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23668
23723
  service,
23669
23724
  path: entry.path,
23670
23725
  action,
@@ -23753,7 +23808,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23753
23808
  return resources.flatMap((resource) => {
23754
23809
  const service = this.invocationServiceName(resource.service);
23755
23810
  return resource.actions.map((action) => ({
23756
- ...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
23811
+ ...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
23757
23812
  service,
23758
23813
  path: resource.path,
23759
23814
  action,
@@ -23814,7 +23869,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23814
23869
  };
23815
23870
  for (const resource of resources) {
23816
23871
  const service = this.invocationServiceName(resource.service);
23817
- if (this.isEncryptionNetworkOperation(service, resource.path)) {
23872
+ if (this.isEncryptionNetworkOperation(service, resource.path, resource.space)) {
23818
23873
  rawAbilities[_a = resource.path] ?? (rawAbilities[_a] = []);
23819
23874
  addActions(rawAbilities[resource.path], resource.actions);
23820
23875
  continue;
@@ -23883,21 +23938,15 @@ var _TinyCloudNode = class _TinyCloudNode {
23883
23938
  }
23884
23939
  return granted.spaceId !== void 0 && requested.spaceId !== void 0 && this.spaceIdsEqual(granted.spaceId, requested.spaceId) && this.pathContains(granted.path, requested.path);
23885
23940
  }
23886
- // Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. The embedded
23887
- // EIP-155 address is case-insensitive, but the CLI canonicalizes it to
23888
- // lowercase when building a space URI while stored runtime delegations keep
23889
- // the EIP-55 checksummed form — so a byte-for-byte compare spuriously rejects
23890
- // an otherwise-valid grant. Lowercase ONLY the `eip155:<chain>:0x<addr>`
23891
- // segment and leave everything else (crucially the case-sensitive space NAME)
23892
- // byte-exact. Mirrors the CLI's `normalizeSpaceForCompare` (OPENKEY_SCOPE_MISMATCH fix).
23941
+ // Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. Compare
23942
+ // only the embedded EIP-155 address without case sensitivity; chain, name,
23943
+ // and all non-PKH space identifiers remain byte-exact.
23893
23944
  spaceIdsEqual(a, b) {
23894
23945
  return this.normalizeSpaceAddress(a) === this.normalizeSpaceAddress(b);
23895
23946
  }
23896
23947
  normalizeSpaceAddress(space) {
23897
- return space.replace(
23898
- /(eip155:\d+:)(0x[0-9a-fA-F]{40})/,
23899
- (_match, prefix, addr) => prefix + addr.toLowerCase()
23900
- );
23948
+ const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
23949
+ return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
23901
23950
  }
23902
23951
  actionContains(grantedAction, requestedAction) {
23903
23952
  if (grantedAction === requestedAction) {
@@ -23914,10 +23963,11 @@ var _TinyCloudNode = class _TinyCloudNode {
23914
23963
  }
23915
23964
  /** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
23916
23965
  parseRecapWithCaveats(siwe) {
23917
- return this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe) ?? this.wasmBindings.parseRecapFromSiwe(siwe);
23966
+ const verified = this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe);
23967
+ return verified == null ? this.wasmBindings.parseRecapFromSiwe(siwe) : verifierRecapEntries(verified);
23918
23968
  }
23919
- isEncryptionNetworkOperation(service, path) {
23920
- return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
23969
+ isEncryptionNetworkOperation(service, path, space) {
23970
+ return service === "encryption" && (space === void 0 || space === "encryption") && path.startsWith("urn:tinycloud:encryption:");
23921
23971
  }
23922
23972
  operationFromInvokeAnyEntry(entry) {
23923
23973
  const service = this.invocationServiceName(entry.service);
@@ -23930,7 +23980,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23930
23980
  ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
23931
23981
  };
23932
23982
  }
23933
- if (this.isEncryptionNetworkOperation(service, entry.path)) {
23983
+ if (entry.spaceId === void 0 && this.isEncryptionNetworkOperation(service, entry.path)) {
23934
23984
  return {
23935
23985
  resource: entry.path,
23936
23986
  service,
@@ -24215,8 +24265,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24215
24265
  const jwk = mySession.jwk;
24216
24266
  const { abilities, rawAbilities } = this.buildActivationAbilities(delegation);
24217
24267
  const now = /* @__PURE__ */ new Date();
24218
- const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
24219
- const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
24268
+ const expirationTime = delegation.expiry;
24220
24269
  const prepared = this.wasmBindings.prepareSession({
24221
24270
  abilities,
24222
24271
  address: this.wasmBindings.ensureEip55(mySession.address),
@@ -24320,8 +24369,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24320
24369
  );
24321
24370
  }
24322
24371
  const now = /* @__PURE__ */ new Date();
24323
- const expiryMs = params.expiryMs ?? 60 * 60 * 1e3;
24324
- const requestedExpiry = new Date(now.getTime() + expiryMs);
24372
+ const requestedExpiry = params.expiryMs === void 0 ? parentDelegation.expiry : new Date(now.getTime() + params.expiryMs);
24325
24373
  const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
24326
24374
  const abilities = {};
24327
24375
  const rawAbilities = {};
@@ -24994,6 +25042,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
24994
25042
  FileSessionStorage,
24995
25043
  HooksService,
24996
25044
  IdentityParseError,
25045
+ InvalidRestoredSessionError,
24997
25046
  KVService,
24998
25047
  ManifestValidationError,
24999
25048
  MemorySessionStorage,
@@ -25073,6 +25122,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
25073
25122
  expandActionShortNames,
25074
25123
  expandPermissionEntries,
25075
25124
  expandPermissionEntry,
25125
+ extractSiweExpiration,
25076
25126
  generateRandomReceiverKey,
25077
25127
  getTinyCloudDebugLogs,
25078
25128
  grantAuthRequest,